KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > coyote > http11 > InternalAprInputBuffer


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18
19 package org.apache.coyote.http11;
20
21 import java.io.IOException JavaDoc;
22 import java.io.EOFException JavaDoc;
23 import java.nio.ByteBuffer JavaDoc;
24
25 import org.apache.tomcat.jni.Socket;
26 import org.apache.tomcat.jni.Status;
27 import org.apache.tomcat.util.buf.ByteChunk;
28 import org.apache.tomcat.util.buf.MessageBytes;
29 import org.apache.tomcat.util.http.MimeHeaders;
30 import org.apache.tomcat.util.res.StringManager;
31
32 import org.apache.coyote.InputBuffer;
33 import org.apache.coyote.Request;
34
35 /**
36  * Implementation of InputBuffer which provides HTTP request header parsing as
37  * well as transfer decoding.
38  *
39  * @author <a HREF="mailto:remm@apache.org">Remy Maucherat</a>
40  */

41 public class InternalAprInputBuffer implements InputBuffer {
42
43
44     // -------------------------------------------------------------- Constants
45

46
47     // ----------------------------------------------------------- Constructors
48

49
50     /**
51      * Alternate constructor.
52      */

53     public InternalAprInputBuffer(Request request, int headerBufferSize,
54                                   long readTimeout) {
55
56         this.request = request;
57         headers = request.getMimeHeaders();
58
59         buf = new byte[headerBufferSize];
60         if (headerBufferSize < (8 * 1024)) {
61             bbuf = ByteBuffer.allocateDirect(6 * 1500);
62         } else {
63             bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
64         }
65
66         inputStreamInputBuffer = new SocketInputBuffer();
67
68         filterLibrary = new InputFilter[0];
69         activeFilters = new InputFilter[0];
70         lastActiveFilter = -1;
71
72         parsingHeader = true;
73         swallowInput = true;
74         
75         if (readTimeout < 0) {
76             this.readTimeout = -1;
77         } else {
78             this.readTimeout = readTimeout * 1000;
79         }
80
81     }
82
83
84     // -------------------------------------------------------------- Variables
85

86
87     /**
88      * The string manager for this package.
89      */

90     protected static StringManager sm =
91         StringManager.getManager(Constants.Package);
92
93
94     // ----------------------------------------------------- Instance Variables
95

96
97     /**
98      * Associated Coyote request.
99      */

100     protected Request request;
101
102
103     /**
104      * Headers of the associated request.
105      */

106     protected MimeHeaders headers;
107
108
109     /**
110      * State.
111      */

112     protected boolean parsingHeader;
113
114
115     /**
116      * Swallow input ? (in the case of an expectation)
117      */

118     protected boolean swallowInput;
119
120
121     /**
122      * Pointer to the current read buffer.
123      */

124     protected byte[] buf;
125
126
127     /**
128      * Last valid byte.
129      */

130     protected int lastValid;
131
132
133     /**
134      * Position in the buffer.
135      */

136     protected int pos;
137
138
139     /**
140      * Pos of the end of the header in the buffer, which is also the
141      * start of the body.
142      */

143     protected int end;
144
145
146     /**
147      * Direct byte buffer used to perform actual reading.
148      */

149     protected ByteBuffer JavaDoc bbuf;
150
151
152     /**
153      * Underlying socket.
154      */

155     protected long socket;
156
157
158     /**
159      * Underlying input buffer.
160      */

161     protected InputBuffer inputStreamInputBuffer;
162
163
164     /**
165      * Filter library.
166      * Note: Filter[0] is always the "chunked" filter.
167      */

168     protected InputFilter[] filterLibrary;
169
170
171     /**
172      * Active filters (in order).
173      */

174     protected InputFilter[] activeFilters;
175
176
177     /**
178      * Index of the last active filter.
179      */

180     protected int lastActiveFilter;
181
182
183     /**
184      * The socket timeout used when reading the first block of the request
185      * header.
186      */

187     protected long readTimeout;
188     
189     
190     // ------------------------------------------------------------- Properties
191

192
193     /**
194      * Set the underlying socket.
195      */

196     public void setSocket(long socket) {
197         this.socket = socket;
198         Socket.setrbb(this.socket, bbuf);
199     }
200
201
202     /**
203      * Get the underlying socket input stream.
204      */

205     public long getSocket() {
206         return socket;
207     }
208
209
210     /**
211      * Add an input filter to the filter library.
212      */

213     public void addFilter(InputFilter filter) {
214
215         InputFilter[] newFilterLibrary =
216             new InputFilter[filterLibrary.length + 1];
217         for (int i = 0; i < filterLibrary.length; i++) {
218             newFilterLibrary[i] = filterLibrary[i];
219         }
220         newFilterLibrary[filterLibrary.length] = filter;
221         filterLibrary = newFilterLibrary;
222
223         activeFilters = new InputFilter[filterLibrary.length];
224
225     }
226
227
228     /**
229      * Get filters.
230      */

231     public InputFilter[] getFilters() {
232
233         return filterLibrary;
234
235     }
236
237
238     /**
239      * Clear filters.
240      */

241     public void clearFilters() {
242
243         filterLibrary = new InputFilter[0];
244         lastActiveFilter = -1;
245
246     }
247
248
249     /**
250      * Add an input filter to the filter library.
251      */

252     public void addActiveFilter(InputFilter filter) {
253
254         if (lastActiveFilter == -1) {
255             filter.setBuffer(inputStreamInputBuffer);
256         } else {
257             for (int i = 0; i <= lastActiveFilter; i++) {
258                 if (activeFilters[i] == filter)
259                     return;
260             }
261             filter.setBuffer(activeFilters[lastActiveFilter]);
262         }
263
264         activeFilters[++lastActiveFilter] = filter;
265
266         filter.setRequest(request);
267
268     }
269
270
271     /**
272      * Set the swallow input flag.
273      */

274     public void setSwallowInput(boolean swallowInput) {
275         this.swallowInput = swallowInput;
276     }
277
278
279     // --------------------------------------------------------- Public Methods
280

281
282     /**
283      * Recycle the input buffer. This should be called when closing the
284      * connection.
285      */

286     public void recycle() {
287
288         // Recycle Request object
289
request.recycle();
290
291         socket = 0;
292         lastValid = 0;
293         pos = 0;
294         lastActiveFilter = -1;
295         parsingHeader = true;
296         swallowInput = true;
297
298     }
299
300
301     /**
302      * End processing of current HTTP request.
303      * Note: All bytes of the current request should have been already
304      * consumed. This method only resets all the pointers so that we are ready
305      * to parse the next HTTP request.
306      */

307     public void nextRequest() {
308
309         // Recycle Request object
310
request.recycle();
311
312         //System.out.println("LV-pos: " + (lastValid - pos));
313
// Copy leftover bytes to the beginning of the buffer
314
if (lastValid - pos > 0) {
315             int npos = 0;
316             int opos = pos;
317             while (lastValid - opos > opos - npos) {
318                 System.arraycopy(buf, opos, buf, npos, opos - npos);
319                 npos += pos;
320                 opos += pos;
321             }
322             System.arraycopy(buf, opos, buf, npos, lastValid - opos);
323         }
324         
325         // Recycle filters
326
for (int i = 0; i <= lastActiveFilter; i++) {
327             activeFilters[i].recycle();
328         }
329
330         // Reset pointers
331
lastValid = lastValid - pos;
332         pos = 0;
333         lastActiveFilter = -1;
334         parsingHeader = true;
335         swallowInput = true;
336
337     }
338
339
340     /**
341      * End request (consumes leftover bytes).
342      *
343      * @throws IOException an undelying I/O error occured
344      */

345     public void endRequest()
346         throws IOException JavaDoc {
347
348         if (swallowInput && (lastActiveFilter != -1)) {
349             int extraBytes = (int) activeFilters[lastActiveFilter].end();
350             pos = pos - extraBytes;
351         }
352
353     }
354
355
356     /**
357      * Read the request line. This function is meant to be used during the
358      * HTTP request header parsing. Do NOT attempt to read the request body
359      * using it.
360      *
361      * @throws IOException If an exception occurs during the underlying socket
362      * read operations, or if the given buffer is not big enough to accomodate
363      * the whole line.
364      * @return true if data is properly fed; false if no data is available
365      * immediately and thread should be freed
366      */

367     public boolean parseRequestLine(boolean useAvailableData)
368         throws IOException JavaDoc {
369
370         int start = 0;
371
372         //
373
// Skipping blank lines
374
//
375

376         byte chr = 0;
377         do {
378
379             // Read new bytes if needed
380
if (pos >= lastValid) {
381                 if (useAvailableData) {
382                     return false;
383                 }
384                 if (readTimeout == -1) {
385                     if (!fill())
386                         throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
387                 } else {
388                     // Do a simple read with a short timeout
389
bbuf.clear();
390                     int nRead = Socket.recvbbt
391                     (socket, 0, buf.length - lastValid, readTimeout);
392                     if (nRead > 0) {
393                         bbuf.limit(nRead);
394                         bbuf.get(buf, pos, nRead);
395                         lastValid = pos + nRead;
396                     } else {
397                         if ((-nRead) == Status.ETIMEDOUT || (-nRead) == Status.TIMEUP) {
398                             return false;
399                         } else {
400                             throw new IOException JavaDoc(sm.getString("iib.failedread"));
401                         }
402                     }
403                 }
404             }
405
406             chr = buf[pos++];
407
408         } while ((chr == Constants.CR) || (chr == Constants.LF));
409
410         pos--;
411
412         // Mark the current buffer position
413
start = pos;
414
415         if (pos >= lastValid) {
416             if (useAvailableData) {
417                 return false;
418             }
419             if (readTimeout == -1) {
420                 if (!fill())
421                     throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
422             } else {
423                 // Do a simple read with a short timeout
424
bbuf.clear();
425                 int nRead = Socket.recvbbt
426                     (socket, 0, buf.length - lastValid, readTimeout);
427                 if (nRead > 0) {
428                     bbuf.limit(nRead);
429                     bbuf.get(buf, pos, nRead);
430                     lastValid = pos + nRead;
431                 } else {
432                     if ((-nRead) == Status.ETIMEDOUT || (-nRead) == Status.TIMEUP) {
433                         return false;
434                     } else {
435                         throw new IOException JavaDoc(sm.getString("iib.failedread"));
436                     }
437                 }
438             }
439         }
440
441         //
442
// Reading the method name
443
// Method name is always US-ASCII
444
//
445

446         boolean space = false;
447
448         while (!space) {
449
450             // Read new bytes if needed
451
if (pos >= lastValid) {
452                 if (!fill())
453                     throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
454             }
455
456             if (buf[pos] == Constants.SP) {
457                 space = true;
458                 request.method().setBytes(buf, start, pos - start);
459             }
460
461             pos++;
462
463         }
464
465         // Mark the current buffer position
466
start = pos;
467         int end = 0;
468         int questionPos = -1;
469
470         //
471
// Reading the URI
472
//
473

474         space = false;
475         boolean eol = false;
476
477         while (!space) {
478
479             // Read new bytes if needed
480
if (pos >= lastValid) {
481                 if (!fill())
482                     throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
483             }
484
485             if (buf[pos] == Constants.SP) {
486                 space = true;
487                 end = pos;
488             } else if ((buf[pos] == Constants.CR)
489                        || (buf[pos] == Constants.LF)) {
490                 // HTTP/0.9 style request
491
eol = true;
492                 space = true;
493                 end = pos;
494             } else if ((buf[pos] == Constants.QUESTION)
495                        && (questionPos == -1)) {
496                 questionPos = pos;
497             }
498
499             pos++;
500
501         }
502
503         request.unparsedURI().setBytes(buf, start, end - start);
504         if (questionPos >= 0) {
505             request.queryString().setBytes(buf, questionPos + 1,
506                                            end - questionPos - 1);
507             request.requestURI().setBytes(buf, start, questionPos - start);
508         } else {
509             request.requestURI().setBytes(buf, start, end - start);
510         }
511
512         // Mark the current buffer position
513
start = pos;
514         end = 0;
515
516         //
517
// Reading the protocol
518
// Protocol is always US-ASCII
519
//
520

521         while (!eol) {
522
523             // Read new bytes if needed
524
if (pos >= lastValid) {
525                 if (!fill())
526                     throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
527             }
528
529             if (buf[pos] == Constants.CR) {
530                 end = pos;
531             } else if (buf[pos] == Constants.LF) {
532                 if (end == 0)
533                     end = pos;
534                 eol = true;
535             }
536
537             pos++;
538
539         }
540
541         if ((end - start) > 0) {
542             request.protocol().setBytes(buf, start, end - start);
543         } else {
544             request.protocol().setString("");
545         }
546         
547         return true;
548
549     }
550
551
552     /**
553      * Parse the HTTP headers.
554      */

555     public void parseHeaders()
556         throws IOException JavaDoc {
557
558         while (parseHeader()) {
559         }
560
561         parsingHeader = false;
562         end = pos;
563
564     }
565
566
567     /**
568      * Parse an HTTP header.
569      *
570      * @return false after reading a blank line (which indicates that the
571      * HTTP header parsing is done
572      */

573     public boolean parseHeader()
574         throws IOException JavaDoc {
575
576         //
577
// Check for blank line
578
//
579

580         byte chr = 0;
581         while (true) {
582
583             // Read new bytes if needed
584
if (pos >= lastValid) {
585                 if (!fill())
586                     throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
587             }
588
589             chr = buf[pos];
590
591             if ((chr == Constants.CR) || (chr == Constants.LF)) {
592                 if (chr == Constants.LF) {
593                     pos++;
594                     return false;
595                 }
596             } else {
597                 break;
598             }
599
600             pos++;
601
602         }
603
604         // Mark the current buffer position
605
int start = pos;
606
607         //
608
// Reading the header name
609
// Header name is always US-ASCII
610
//
611

612         boolean colon = false;
613         MessageBytes headerValue = null;
614
615         while (!colon) {
616
617             // Read new bytes if needed
618
if (pos >= lastValid) {
619                 if (!fill())
620                     throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
621             }
622
623             if (buf[pos] == Constants.COLON) {
624                 colon = true;
625                 headerValue = headers.addValue(buf, start, pos - start);
626             }
627             chr = buf[pos];
628             if ((chr >= Constants.A) && (chr <= Constants.Z)) {
629                 buf[pos] = (byte) (chr - Constants.LC_OFFSET);
630             }
631
632             pos++;
633
634         }
635
636         // Mark the current buffer position
637
start = pos;
638         int realPos = pos;
639
640         //
641
// Reading the header value (which can be spanned over multiple lines)
642
//
643

644         boolean eol = false;
645         boolean validLine = true;
646
647         while (validLine) {
648
649             boolean space = true;
650
651             // Skipping spaces
652
while (space) {
653
654                 // Read new bytes if needed
655
if (pos >= lastValid) {
656                     if (!fill())
657                         throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
658                 }
659
660                 if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {
661                     pos++;
662                 } else {
663                     space = false;
664                 }
665
666             }
667
668             int lastSignificantChar = realPos;
669
670             // Reading bytes until the end of the line
671
while (!eol) {
672
673                 // Read new bytes if needed
674
if (pos >= lastValid) {
675                     if (!fill())
676                         throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
677                 }
678
679                 if (buf[pos] == Constants.CR) {
680                 } else if (buf[pos] == Constants.LF) {
681                     eol = true;
682                 } else if (buf[pos] == Constants.SP) {
683                     buf[realPos] = buf[pos];
684                     realPos++;
685                 } else {
686                     buf[realPos] = buf[pos];
687                     realPos++;
688                     lastSignificantChar = realPos;
689                 }
690
691                 pos++;
692
693             }
694
695             realPos = lastSignificantChar;
696
697             // Checking the first character of the new line. If the character
698
// is a LWS, then it's a multiline header
699

700             // Read new bytes if needed
701
if (pos >= lastValid) {
702                 if (!fill())
703                     throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
704             }
705
706             chr = buf[pos];
707             if ((chr != Constants.SP) && (chr != Constants.HT)) {
708                 validLine = false;
709             } else {
710                 eol = false;
711                 // Copying one extra space in the buffer (since there must
712
// be at least one space inserted between the lines)
713
buf[realPos] = chr;
714                 realPos++;
715             }
716
717         }
718
719         // Set the header value
720
headerValue.setBytes(buf, start, realPos - start);
721
722         return true;
723
724     }
725
726
727     // ---------------------------------------------------- InputBuffer Methods
728

729
730     /**
731      * Read some bytes.
732      */

733     public int doRead(ByteChunk chunk, Request req)
734         throws IOException JavaDoc {
735
736         if (lastActiveFilter == -1)
737             return inputStreamInputBuffer.doRead(chunk, req);
738         else
739             return activeFilters[lastActiveFilter].doRead(chunk,req);
740
741     }
742
743
744     // ------------------------------------------------------ Protected Methods
745

746
747     /**
748      * Fill the internal buffer using data from the undelying input stream.
749      *
750      * @return false if at end of stream
751      */

752     protected boolean fill()
753         throws IOException JavaDoc {
754
755         int nRead = 0;
756
757         if (parsingHeader) {
758
759             if (lastValid == buf.length) {
760                 throw new IOException JavaDoc
761                     (sm.getString("iib.requestheadertoolarge.error"));
762             }
763
764             bbuf.clear();
765             nRead = Socket.recvbb(socket, 0, buf.length - lastValid);
766             if (nRead > 0) {
767                 bbuf.limit(nRead);
768                 bbuf.get(buf, pos, nRead);
769                 lastValid = pos + nRead;
770             } else {
771                 if ((-nRead) == Status.EAGAIN) {
772                     return false;
773                 } else {
774                     throw new IOException JavaDoc(sm.getString("iib.failedread"));
775                 }
776             }
777
778         } else {
779
780             if (buf.length - end < 4500) {
781                 // In this case, the request header was really large, so we allocate a
782
// brand new one; the old one will get GCed when subsequent requests
783
// clear all references
784
buf = new byte[buf.length];
785                 end = 0;
786             }
787             pos = end;
788             lastValid = pos;
789             bbuf.clear();
790             nRead = Socket.recvbb(socket, 0, buf.length - lastValid);
791             if (nRead > 0) {
792                 bbuf.limit(nRead);
793                 bbuf.get(buf, pos, nRead);
794                 lastValid = pos + nRead;
795             } else {
796                 throw new IOException JavaDoc(sm.getString("iib.failedread"));
797             }
798
799         }
800
801         return (nRead > 0);
802
803     }
804
805
806     // ------------------------------------- InputStreamInputBuffer Inner Class
807

808
809     /**
810      * This class is an input buffer which will read its data from an input
811      * stream.
812      */

813     protected class SocketInputBuffer
814         implements InputBuffer {
815
816
817         /**
818          * Read bytes into the specified chunk.
819          */

820         public int doRead(ByteChunk chunk, Request req )
821             throws IOException JavaDoc {
822
823             if (pos >= lastValid) {
824                 if (!fill())
825                     return -1;
826             }
827
828             int length = lastValid - pos;
829             chunk.setBytes(buf, pos, length);
830             pos = lastValid;
831
832             return (length);
833
834         }
835
836
837     }
838
839
840 }
841
Popular Tags