KickJava   Java API By Example, From Geeks To Geeks.

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


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.EOFException JavaDoc;
22 import java.io.IOException JavaDoc;
23 import java.nio.channels.Selector JavaDoc;
24
25 import org.apache.coyote.InputBuffer;
26 import org.apache.coyote.Request;
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.net.NioChannel;
31 import org.apache.tomcat.util.net.NioSelectorPool;
32 import org.apache.tomcat.util.res.StringManager;
33
34 /**
35  * Implementation of InputBuffer which provides HTTP request header parsing as
36  * well as transfer decoding.
37  *
38  * @author <a HREF="mailto:remm@apache.org">Remy Maucherat</a>
39  * @author Filip Hanik
40  */

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

46     enum HeaderParseStatus {DONE, HAVE_MORE_HEADERS, NEED_MORE_DATA}
47     enum HeaderParsePosition {HEADER_START, HEADER_NAME, HEADER_VALUE, HEADER_MULTI_LINE}
48     // ----------------------------------------------------------- Constructors
49

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

54     public InternalNioInputBuffer(Request request, int headerBufferSize,
55                                   long readTimeout) {
56
57         this.request = request;
58         headers = request.getMimeHeaders();
59
60         buf = new byte[headerBufferSize];
61 // if (headerBufferSize < (8 * 1024)) {
62
// bbuf = ByteBuffer.allocateDirect(6 * 1500);
63
// } else {
64
// bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
65
// }
66

67         inputStreamInputBuffer = new SocketInputBuffer();
68
69         filterLibrary = new InputFilter[0];
70         activeFilters = new InputFilter[0];
71         lastActiveFilter = -1;
72
73         parsingHeader = true;
74         parsingRequestLine = true;
75         headerParsePos = HeaderParsePosition.HEADER_START;
76         headerData.recycle();
77         swallowInput = true;
78
79         if (readTimeout < 0) {
80             this.readTimeout = -1;
81         } else {
82             this.readTimeout = readTimeout;
83         }
84
85     }
86
87
88     // -------------------------------------------------------------- Variables
89

90
91     /**
92      * The string manager for this package.
93      */

94     protected static StringManager sm =
95         StringManager.getManager(Constants.Package);
96
97
98     // ----------------------------------------------------- Instance Variables
99

100
101     /**
102      * Associated Coyote request.
103      */

104     protected Request request;
105
106
107     /**
108      * Headers of the associated request.
109      */

110     protected MimeHeaders headers;
111
112
113     /**
114      * State.
115      */

116     protected boolean parsingHeader;
117     protected boolean parsingRequestLine;
118     protected HeaderParsePosition headerParsePos;
119
120
121     /**
122      * Swallow input ? (in the case of an expectation)
123      */

124     protected boolean swallowInput;
125
126
127     /**
128      * Pointer to the current read buffer.
129      */

130     protected byte[] buf;
131
132
133     /**
134      * Last valid byte.
135      */

136     protected int lastValid;
137
138
139     /**
140      * Position in the buffer.
141      */

142     protected int pos;
143
144
145     /**
146      * Pos of the end of the header in the buffer, which is also the
147      * start of the body.
148      */

149     protected int end;
150
151
152
153     /**
154      * Underlying socket.
155      */

156     protected NioChannel socket;
157     
158     /**
159      * Selector pool, for blocking reads and blocking writes
160      */

161     protected NioSelectorPool pool;
162     
163
164     /**
165      * Underlying input buffer.
166      */

167     protected InputBuffer inputStreamInputBuffer;
168
169
170     /**
171      * Filter library.
172      * Note: Filter[0] is always the "chunked" filter.
173      */

174     protected InputFilter[] filterLibrary;
175
176
177     /**
178      * Active filters (in order).
179      */

180     protected InputFilter[] activeFilters;
181
182
183     /**
184      * Index of the last active filter.
185      */

186     protected int lastActiveFilter;
187
188
189     /**
190      * The socket timeout used when reading the first block of the request
191      * header.
192      */

193     protected long readTimeout;
194
195     // ------------------------------------------------------------- Properties
196

197
198     /**
199      * Set the underlying socket.
200      */

201     public void setSocket(NioChannel socket) {
202         this.socket = socket;
203     }
204     
205     /**
206      * Get the underlying socket input stream.
207      */

208     public NioChannel getSocket() {
209         return socket;
210     }
211
212     public void setSelectorPool(NioSelectorPool pool) {
213         this.pool = pool;
214     }
215     
216     public NioSelectorPool getSelectorPool() {
217         return pool;
218     }
219
220
221     /**
222      * Add an input filter to the filter library.
223      */

224     public void addFilter(InputFilter filter) {
225
226         InputFilter[] newFilterLibrary =
227             new InputFilter[filterLibrary.length + 1];
228         for (int i = 0; i < filterLibrary.length; i++) {
229             newFilterLibrary[i] = filterLibrary[i];
230         }
231         newFilterLibrary[filterLibrary.length] = filter;
232         filterLibrary = newFilterLibrary;
233
234         activeFilters = new InputFilter[filterLibrary.length];
235
236     }
237
238
239     /**
240      * Get filters.
241      */

242     public InputFilter[] getFilters() {
243
244         return filterLibrary;
245
246     }
247
248
249     /**
250      * Clear filters.
251      */

252     public void clearFilters() {
253
254         filterLibrary = new InputFilter[0];
255         lastActiveFilter = -1;
256
257     }
258
259
260     /**
261      * Add an input filter to the filter library.
262      */

263     public void addActiveFilter(InputFilter filter) {
264
265         if (lastActiveFilter == -1) {
266             filter.setBuffer(inputStreamInputBuffer);
267         } else {
268             for (int i = 0; i <= lastActiveFilter; i++) {
269                 if (activeFilters[i] == filter)
270                     return;
271             }
272             filter.setBuffer(activeFilters[lastActiveFilter]);
273         }
274
275         activeFilters[++lastActiveFilter] = filter;
276
277         filter.setRequest(request);
278
279     }
280
281
282     /**
283      * Set the swallow input flag.
284      */

285     public void setSwallowInput(boolean swallowInput) {
286         this.swallowInput = swallowInput;
287     }
288
289     // --------------------------------------------------------- Public Methods
290

291
292     /**
293      * Recycle the input buffer. This should be called when closing the
294      * connection.
295      */

296     public void recycle() {
297
298         // Recycle Request object
299
request.recycle();
300
301         socket = null;
302         lastValid = 0;
303         pos = 0;
304         lastActiveFilter = -1;
305         parsingHeader = true;
306         headerParsePos = HeaderParsePosition.HEADER_START;
307         parsingRequestLine = true;
308         headerData.recycle();
309         swallowInput = true;
310
311     }
312
313
314     /**
315      * End processing of current HTTP request.
316      * Note: All bytes of the current request should have been already
317      * consumed. This method only resets all the pointers so that we are ready
318      * to parse the next HTTP request.
319      */

320     public void nextRequest() {
321
322         // Recycle Request object
323
request.recycle();
324
325         //System.out.println("LV-pos: " + (lastValid - pos));
326
// Copy leftover bytes to the beginning of the buffer
327
if (lastValid - pos > 0) {
328             int npos = 0;
329             int opos = pos;
330             while (lastValid - opos > opos - npos) {
331                 System.arraycopy(buf, opos, buf, npos, opos - npos);
332                 npos += pos;
333                 opos += pos;
334             }
335             System.arraycopy(buf, opos, buf, npos, lastValid - opos);
336         }
337
338         // Recycle filters
339
for (int i = 0; i <= lastActiveFilter; i++) {
340             activeFilters[i].recycle();
341         }
342
343         // Reset pointers
344
lastValid = lastValid - pos;
345         pos = 0;
346         lastActiveFilter = -1;
347         parsingHeader = true;
348         headerParsePos = HeaderParsePosition.HEADER_START;
349         parsingRequestLine = true;
350         headerData.recycle();
351         swallowInput = true;
352
353     }
354
355
356     /**
357      * End request (consumes leftover bytes).
358      *
359      * @throws IOException an undelying I/O error occured
360      */

361     public void endRequest()
362         throws IOException JavaDoc {
363
364         if (swallowInput && (lastActiveFilter != -1)) {
365             int extraBytes = (int) activeFilters[lastActiveFilter].end();
366             pos = pos - extraBytes;
367         }
368
369     }
370
371
372     /**
373      * Read the request line. This function is meant to be used during the
374      * HTTP request header parsing. Do NOT attempt to read the request body
375      * using it.
376      *
377      * @throws IOException If an exception occurs during the underlying socket
378      * read operations, or if the given buffer is not big enough to accomodate
379      * the whole line.
380      * @return true if data is properly fed; false if no data is available
381      * immediately and thread should be freed
382      */

383     public boolean parseRequestLine(boolean useAvailableData)
384         throws IOException JavaDoc {
385
386         //check state
387
if ( !parsingRequestLine ) return true;
388         
389         int start = 0;
390
391         //
392
// Skipping blank lines
393
//
394

395         byte chr = 0;
396         do {
397
398             // Read new bytes if needed
399
if (pos >= lastValid) {
400                 if (useAvailableData) {
401                     return false;
402                 }
403                 if (readTimeout == -1) {
404                     if (!fill(false,true)) //request line parsing
405
throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
406                 } else {
407                     // Do a simple read with a short timeout
408
if ( !readSocket(true, false) ) return false;
409                 }
410             }
411
412             chr = buf[pos++];
413
414         } while ((chr == Constants.CR) || (chr == Constants.LF));
415
416         pos--;
417
418         // Mark the current buffer position
419
start = pos;
420
421         if (pos >= lastValid) {
422             if (useAvailableData) {
423                 return false;
424             }
425             if (readTimeout == -1) {
426                 if (!fill(false,false)) //request line parsing
427
return false;
428             } else {
429                 // Do a simple read with a short timeout
430
if ( !readSocket(true, false) ) return false;
431             }
432         }
433
434         //
435
// Reading the method name
436
// Method name is always US-ASCII
437
//
438

439         boolean space = false;
440
441         while (!space) {
442
443             // Read new bytes if needed
444
if (pos >= lastValid) {
445                 if (!fill(true,false)) //request line parsing
446
return false;
447             }
448
449             if (buf[pos] == Constants.SP) {
450                 space = true;
451                 request.method().setBytes(buf, start, pos - start);
452             }
453
454             pos++;
455
456         }
457
458         // Mark the current buffer position
459
start = pos;
460         int end = 0;
461         int questionPos = -1;
462
463         //
464
// Reading the URI
465
//
466

467         space = false;
468         boolean eol = false;
469
470         while (!space) {
471
472             // Read new bytes if needed
473
if (pos >= lastValid) {
474                 if (!fill(true,false)) //request line parsing
475
return false;
476             }
477
478             if (buf[pos] == Constants.SP) {
479                 space = true;
480                 end = pos;
481             } else if ((buf[pos] == Constants.CR)
482                        || (buf[pos] == Constants.LF)) {
483                 // HTTP/0.9 style request
484
eol = true;
485                 space = true;
486                 end = pos;
487             } else if ((buf[pos] == Constants.QUESTION)
488                        && (questionPos == -1)) {
489                 questionPos = pos;
490             }
491
492             pos++;
493
494         }
495
496         request.unparsedURI().setBytes(buf, start, end - start);
497         if (questionPos >= 0) {
498             request.queryString().setBytes(buf, questionPos + 1,
499                                            end - questionPos - 1);
500             request.requestURI().setBytes(buf, start, questionPos - start);
501         } else {
502             request.requestURI().setBytes(buf, start, end - start);
503         }
504
505         // Mark the current buffer position
506
start = pos;
507         end = 0;
508
509         //
510
// Reading the protocol
511
// Protocol is always US-ASCII
512
//
513

514         while (!eol) {
515
516             // Read new bytes if needed
517
if (pos >= lastValid) {
518                 if (!fill(true,false)) //reques line parsing
519
return false;
520             }
521
522             if (buf[pos] == Constants.CR) {
523                 end = pos;
524             } else if (buf[pos] == Constants.LF) {
525                 if (end == 0)
526                     end = pos;
527                 eol = true;
528             }
529
530             pos++;
531
532         }
533
534         if ((end - start) > 0) {
535             request.protocol().setBytes(buf, start, end - start);
536         } else {
537             request.protocol().setString("");
538         }
539         parsingRequestLine = false;
540         return true;
541
542     }
543     
544     private void expand(int newsize) {
545         if ( newsize > buf.length ) {
546             byte[] tmp = new byte[newsize];
547             System.arraycopy(buf,0,tmp,0,buf.length);
548             buf = tmp;
549             tmp = null;
550         }
551     }
552     /**
553      * Perform blocking read with a timeout if desired
554      * @param timeout boolean - if we want to use the timeout data
555      * @param block - true if the system should perform a blocking read, false otherwise
556      * @return boolean - true if data was read, false is no data read, EOFException if EOF is reached
557      * @throws IOException if a socket exception occurs
558      * @throws EOFException if end of stream is reached
559      */

560     private boolean readSocket(boolean timeout, boolean block) throws IOException JavaDoc {
561         int nRead = 0;
562         long rto = timeout?this.readTimeout:-1;
563         socket.getBufHandler().getReadBuffer().clear();
564         if ( block ) {
565             Selector JavaDoc selector = null;
566             try { selector = getSelectorPool().get(); }catch ( IOException JavaDoc x ) {}
567             try {
568                 nRead = getSelectorPool().read(socket.getBufHandler().getReadBuffer(),socket,selector,rto);
569             } catch ( EOFException JavaDoc eof ) {
570                 nRead = -1;
571             } finally {
572                 if ( selector != null ) getSelectorPool().put(selector);
573             }
574         } else {
575             nRead = socket.read(socket.getBufHandler().getReadBuffer());
576         }
577         if (nRead > 0) {
578             socket.getBufHandler().getReadBuffer().flip();
579             socket.getBufHandler().getReadBuffer().limit(nRead);
580             expand(nRead + pos);
581             socket.getBufHandler().getReadBuffer().get(buf, pos, nRead);
582             lastValid = pos + nRead;
583             return true;
584         } else if (nRead == -1) {
585             //return false;
586
throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
587         } else {
588             return false;
589         }
590     }
591
592     /**
593      * Parse the HTTP headers.
594      */

595     public boolean parseHeaders()
596         throws IOException JavaDoc {
597         HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS;
598         
599         do {
600             status = parseHeader();
601         } while ( status == HeaderParseStatus.HAVE_MORE_HEADERS );
602         if (status == HeaderParseStatus.DONE) {
603             parsingHeader = false;
604             end = pos;
605             return true;
606         } else {
607             return false;
608         }
609     }
610
611
612     /**
613      * Parse an HTTP header.
614      *
615      * @return false after reading a blank line (which indicates that the
616      * HTTP header parsing is done
617      */

618     public HeaderParseStatus parseHeader()
619         throws IOException JavaDoc {
620
621         //
622
// Check for blank line
623
//
624

625         byte chr = 0;
626         while (headerParsePos == HeaderParsePosition.HEADER_START) {
627
628             // Read new bytes if needed
629
if (pos >= lastValid) {
630                 if (!fill(true,false)) {//parse header
631
headerParsePos = HeaderParsePosition.HEADER_START;
632                     return HeaderParseStatus.NEED_MORE_DATA;
633                 }
634             }
635
636             chr = buf[pos];
637
638             if ((chr == Constants.CR) || (chr == Constants.LF)) {
639                 if (chr == Constants.LF) {
640                     pos++;
641                     return HeaderParseStatus.DONE;
642                 }
643             } else {
644                 break;
645             }
646
647             pos++;
648
649         }
650
651         if ( headerParsePos == HeaderParsePosition.HEADER_START ) {
652             // Mark the current buffer position
653
headerData.start = pos;
654             headerParsePos = HeaderParsePosition.HEADER_NAME;
655         }
656
657         //
658
// Reading the header name
659
// Header name is always US-ASCII
660
//
661

662         
663
664         while (headerParsePos == HeaderParsePosition.HEADER_NAME) {
665
666             // Read new bytes if needed
667
if (pos >= lastValid) {
668                 if (!fill(true,false)) { //parse header
669
return HeaderParseStatus.NEED_MORE_DATA;
670                 }
671             }
672
673             if (buf[pos] == Constants.COLON) {
674                 headerParsePos = HeaderParsePosition.HEADER_VALUE;
675                 headerData.headerValue = headers.addValue(buf, headerData.start, pos - headerData.start);
676             }
677             chr = buf[pos];
678             if ((chr >= Constants.A) && (chr <= Constants.Z)) {
679                 buf[pos] = (byte) (chr - Constants.LC_OFFSET);
680             }
681
682             pos++;
683             if ( headerParsePos == HeaderParsePosition.HEADER_VALUE ) {
684                 // Mark the current buffer position
685
headerData.start = pos;
686                 headerData.realPos = pos;
687             }
688         }
689
690         
691         //
692
// Reading the header value (which can be spanned over multiple lines)
693
//
694

695         boolean eol = false;
696
697         while (headerParsePos == HeaderParsePosition.HEADER_VALUE ||
698                headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE) {
699             if ( headerParsePos == HeaderParsePosition.HEADER_VALUE ) {
700             
701                 boolean space = true;
702
703                 // Skipping spaces
704
while (space) {
705
706                     // Read new bytes if needed
707
if (pos >= lastValid) {
708                         if (!fill(true,false)) {//parse header
709
//HEADER_VALUE, should already be set
710
return HeaderParseStatus.NEED_MORE_DATA;
711                         }
712                     }
713
714                     if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {
715                         pos++;
716                     } else {
717                         space = false;
718                     }
719
720                 }
721
722                 headerData.lastSignificantChar = headerData.realPos;
723
724                 // Reading bytes until the end of the line
725
while (!eol) {
726
727                     // Read new bytes if needed
728
if (pos >= lastValid) {
729                         if (!fill(true,false)) {//parse header
730
//HEADER_VALUE
731
return HeaderParseStatus.NEED_MORE_DATA;
732                         }
733
734                     }
735
736                     if (buf[pos] == Constants.CR) {
737                     } else if (buf[pos] == Constants.LF) {
738                         eol = true;
739                     } else if (buf[pos] == Constants.SP) {
740                         buf[headerData.realPos] = buf[pos];
741                         headerData.realPos++;
742                     } else {
743                         buf[headerData.realPos] = buf[pos];
744                         headerData.realPos++;
745                         headerData.lastSignificantChar = headerData.realPos;
746                     }
747
748                     pos++;
749
750                 }
751
752                 headerData.realPos = headerData.lastSignificantChar;
753
754                 // Checking the first character of the new line. If the character
755
// is a LWS, then it's a multiline header
756
headerParsePos = HeaderParsePosition.HEADER_MULTI_LINE;
757             }
758             // Read new bytes if needed
759
if (pos >= lastValid) {
760                 if (!fill(true,false)) {//parse header
761

762                     //HEADER_MULTI_LINE
763
return HeaderParseStatus.NEED_MORE_DATA;
764                 }
765             }
766
767             chr = buf[pos];
768             if ( headerParsePos == HeaderParsePosition.HEADER_MULTI_LINE ) {
769                 if ( (chr != Constants.SP) && (chr != Constants.HT)) {
770                     headerParsePos = HeaderParsePosition.HEADER_START;
771                 } else {
772                     eol = false;
773                     // Copying one extra space in the buffer (since there must
774
// be at least one space inserted between the lines)
775
buf[headerData.realPos] = chr;
776                     headerData.realPos++;
777                 }
778             }
779         }
780         // Set the header value
781
headerData.headerValue.setBytes(buf, headerData.start, headerData.realPos - headerData.start);
782         headerData.recycle();
783         return HeaderParseStatus.HAVE_MORE_HEADERS;
784     }
785     
786     protected HeaderParseData headerData = new HeaderParseData();
787     public static class HeaderParseData {
788         int start = 0;
789         int realPos = 0;
790         int lastSignificantChar = 0;
791         MessageBytes headerValue = null;
792         public void recycle() {
793             start = 0;
794             realPos = 0;
795             lastSignificantChar = 0;
796             headerValue = null;
797         }
798     }
799
800
801     // ---------------------------------------------------- InputBuffer Methods
802

803
804     /**
805      * Read some bytes.
806      */

807     public int doRead(ByteChunk chunk, Request req)
808         throws IOException JavaDoc {
809
810         if (lastActiveFilter == -1)
811             return inputStreamInputBuffer.doRead(chunk, req);
812         else
813             return activeFilters[lastActiveFilter].doRead(chunk,req);
814
815     }
816
817
818     // ------------------------------------------------------ Protected Methods
819

820     /**
821      * Fill the internal buffer using data from the undelying input stream.
822      *
823      * @return false if at end of stream
824      */

825     protected boolean fill(boolean timeout, boolean block)
826         throws IOException JavaDoc, EOFException JavaDoc {
827
828         boolean read = false;
829
830         if (parsingHeader) {
831
832             if (lastValid == buf.length) {
833                 throw new IOException JavaDoc
834                     (sm.getString("iib.requestheadertoolarge.error"));
835             }
836
837             // Do a simple read with a short timeout
838
read = readSocket(timeout,block);
839         } else {
840
841             if (buf.length - end < 4500) {
842                 // In this case, the request header was really large, so we allocate a
843
// brand new one; the old one will get GCed when subsequent requests
844
// clear all references
845
buf = new byte[buf.length];
846                 end = 0;
847             }
848             pos = end;
849             lastValid = pos;
850             // Do a simple read with a short timeout
851
read = readSocket(timeout, block);
852         }
853         return read;
854     }
855
856
857     // ------------------------------------- InputStreamInputBuffer Inner Class
858

859
860     /**
861      * This class is an input buffer which will read its data from an input
862      * stream.
863      */

864     protected class SocketInputBuffer
865         implements InputBuffer {
866
867
868         /**
869          * Read bytes into the specified chunk.
870          */

871         public int doRead(ByteChunk chunk, Request req )
872             throws IOException JavaDoc {
873
874             if (pos >= lastValid) {
875                 if (!fill(true,true)) //read body, must be blocking, as the thread is inside the app
876
return -1;
877             }
878
879             int length = lastValid - pos;
880             chunk.setBytes(buf, pos, length);
881             pos = lastValid;
882
883             return (length);
884
885         }
886
887
888     }
889
890
891 }
892
Popular Tags