KickJava   Java API By Example, From Geeks To Geeks.

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


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.InputStream JavaDoc;
23 import java.io.EOFException JavaDoc;
24
25 import org.apache.tomcat.util.buf.ByteChunk;
26 import org.apache.tomcat.util.buf.MessageBytes;
27 import org.apache.tomcat.util.http.MimeHeaders;
28 import org.apache.tomcat.util.res.StringManager;
29
30 import org.apache.coyote.InputBuffer;
31 import org.apache.coyote.Request;
32
33 /**
34  * Implementation of InputBuffer which provides HTTP request header parsing as
35  * well as transfer decoding.
36  *
37  * @author <a HREF="mailto:remm@apache.org">Remy Maucherat</a>
38  */

39 public class InternalInputBuffer implements InputBuffer {
40
41
42     // -------------------------------------------------------------- Constants
43

44
45     // ----------------------------------------------------------- Constructors
46

47
48     /**
49      * Default constructor.
50      */

51     public InternalInputBuffer(Request request) {
52         this(request, Constants.DEFAULT_HTTP_HEADER_BUFFER_SIZE);
53     }
54
55
56     /**
57      * Alternate constructor.
58      */

59     public InternalInputBuffer(Request request, int headerBufferSize) {
60
61         this.request = request;
62         headers = request.getMimeHeaders();
63
64         buf = new byte[headerBufferSize];
65
66         inputStreamInputBuffer = new InputStreamInputBuffer();
67
68         filterLibrary = new InputFilter[0];
69         activeFilters = new InputFilter[0];
70         lastActiveFilter = -1;
71
72         parsingHeader = true;
73         swallowInput = true;
74
75     }
76
77
78     // -------------------------------------------------------------- Variables
79

80
81     /**
82      * The string manager for this package.
83      */

84     protected static StringManager sm =
85         StringManager.getManager(Constants.Package);
86
87
88     // ----------------------------------------------------- Instance Variables
89

90
91     /**
92      * Associated Coyote request.
93      */

94     protected Request request;
95
96
97     /**
98      * Headers of the associated request.
99      */

100     protected MimeHeaders headers;
101
102
103     /**
104      * State.
105      */

106     protected boolean parsingHeader;
107
108
109     /**
110      * Swallow input ? (in the case of an expectation)
111      */

112     protected boolean swallowInput;
113
114
115     /**
116      * Pointer to the current read buffer.
117      */

118     protected byte[] buf;
119
120
121     /**
122      * Last valid byte.
123      */

124     protected int lastValid;
125
126
127     /**
128      * Position in the buffer.
129      */

130     protected int pos;
131
132
133     /**
134      * Pos of the end of the header in the buffer, which is also the
135      * start of the body.
136      */

137     protected int end;
138
139
140     /**
141      * Underlying input stream.
142      */

143     protected InputStream JavaDoc inputStream;
144
145
146     /**
147      * Underlying input buffer.
148      */

149     protected InputBuffer inputStreamInputBuffer;
150
151
152     /**
153      * Filter library.
154      * Note: Filter[0] is always the "chunked" filter.
155      */

156     protected InputFilter[] filterLibrary;
157
158
159     /**
160      * Active filters (in order).
161      */

162     protected InputFilter[] activeFilters;
163
164
165     /**
166      * Index of the last active filter.
167      */

168     protected int lastActiveFilter;
169
170
171     // ------------------------------------------------------------- Properties
172

173
174     /**
175      * Set the underlying socket input stream.
176      */

177     public void setInputStream(InputStream JavaDoc inputStream) {
178
179         // FIXME: Check for null ?
180

181         this.inputStream = inputStream;
182
183     }
184
185
186     /**
187      * Get the underlying socket input stream.
188      */

189     public InputStream JavaDoc getInputStream() {
190
191         return inputStream;
192
193     }
194
195
196     /**
197      * Add an input filter to the filter library.
198      */

199     public void addFilter(InputFilter filter) {
200
201         // FIXME: Check for null ?
202

203         InputFilter[] newFilterLibrary =
204             new InputFilter[filterLibrary.length + 1];
205         for (int i = 0; i < filterLibrary.length; i++) {
206             newFilterLibrary[i] = filterLibrary[i];
207         }
208         newFilterLibrary[filterLibrary.length] = filter;
209         filterLibrary = newFilterLibrary;
210
211         activeFilters = new InputFilter[filterLibrary.length];
212
213     }
214
215
216     /**
217      * Get filters.
218      */

219     public InputFilter[] getFilters() {
220
221         return filterLibrary;
222
223     }
224
225
226     /**
227      * Clear filters.
228      */

229     public void clearFilters() {
230
231         filterLibrary = new InputFilter[0];
232         lastActiveFilter = -1;
233
234     }
235
236
237     /**
238      * Add an input filter to the filter library.
239      */

240     public void addActiveFilter(InputFilter filter) {
241
242         if (lastActiveFilter == -1) {
243             filter.setBuffer(inputStreamInputBuffer);
244         } else {
245             for (int i = 0; i <= lastActiveFilter; i++) {
246                 if (activeFilters[i] == filter)
247                     return;
248             }
249             filter.setBuffer(activeFilters[lastActiveFilter]);
250         }
251
252         activeFilters[++lastActiveFilter] = filter;
253
254         filter.setRequest(request);
255
256     }
257
258
259     /**
260      * Set the swallow input flag.
261      */

262     public void setSwallowInput(boolean swallowInput) {
263         this.swallowInput = swallowInput;
264     }
265
266
267     // --------------------------------------------------------- Public Methods
268

269
270     /**
271      * Recycle the input buffer. This should be called when closing the
272      * connection.
273      */

274     public void recycle() {
275
276         // Recycle Request object
277
request.recycle();
278
279         inputStream = null;
280         lastValid = 0;
281         pos = 0;
282         lastActiveFilter = -1;
283         parsingHeader = true;
284         swallowInput = true;
285
286     }
287
288
289     /**
290      * End processing of current HTTP request.
291      * Note: All bytes of the current request should have been already
292      * consumed. This method only resets all the pointers so that we are ready
293      * to parse the next HTTP request.
294      */

295     public void nextRequest() {
296
297         // Recycle Request object
298
request.recycle();
299
300         // Copy leftover bytes to the beginning of the buffer
301
if (lastValid - pos > 0) {
302             int npos = 0;
303             int opos = pos;
304             while (lastValid - opos > opos - npos) {
305                 System.arraycopy(buf, opos, buf, npos, opos - npos);
306                 npos += pos;
307                 opos += pos;
308             }
309             System.arraycopy(buf, opos, buf, npos, lastValid - opos);
310         }
311
312         // Recycle filters
313
for (int i = 0; i <= lastActiveFilter; i++) {
314             activeFilters[i].recycle();
315         }
316
317         // Reset pointers
318
lastValid = lastValid - pos;
319         pos = 0;
320         lastActiveFilter = -1;
321         parsingHeader = true;
322         swallowInput = true;
323
324     }
325
326
327     /**
328      * End request (consumes leftover bytes).
329      *
330      * @throws IOException an undelying I/O error occured
331      */

332     public void endRequest()
333         throws IOException JavaDoc {
334
335         if (swallowInput && (lastActiveFilter != -1)) {
336             int extraBytes = (int) activeFilters[lastActiveFilter].end();
337             pos = pos - extraBytes;
338         }
339
340     }
341
342
343     /**
344      * Read the request line. This function is meant to be used during the
345      * HTTP request header parsing. Do NOT attempt to read the request body
346      * using it.
347      *
348      * @throws IOException If an exception occurs during the underlying socket
349      * read operations, or if the given buffer is not big enough to accomodate
350      * the whole line.
351      */

352     public void parseRequestLine()
353         throws IOException JavaDoc {
354
355         int start = 0;
356
357         //
358
// Skipping blank lines
359
//
360

361         byte chr = 0;
362         do {
363
364             // Read new bytes if needed
365
if (pos >= lastValid) {
366                 if (!fill())
367                     throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
368             }
369
370             chr = buf[pos++];
371
372         } while ((chr == Constants.CR) || (chr == Constants.LF));
373
374         pos--;
375
376         // Mark the current buffer position
377
start = pos;
378
379         //
380
// Reading the method name
381
// Method name is always US-ASCII
382
//
383

384         boolean space = false;
385
386         while (!space) {
387
388             // Read new bytes if needed
389
if (pos >= lastValid) {
390                 if (!fill())
391                     throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
392             }
393
394             if (buf[pos] == Constants.SP) {
395                 space = true;
396                 request.method().setBytes(buf, start, pos - start);
397             }
398
399             pos++;
400
401         }
402
403         // Mark the current buffer position
404
start = pos;
405         int end = 0;
406         int questionPos = -1;
407
408         //
409
// Reading the URI
410
//
411

412         space = false;
413         boolean eol = false;
414
415         while (!space) {
416
417             // Read new bytes if needed
418
if (pos >= lastValid) {
419                 if (!fill())
420                     throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
421             }
422
423             if (buf[pos] == Constants.SP) {
424                 space = true;
425                 end = pos;
426             } else if ((buf[pos] == Constants.CR)
427                        || (buf[pos] == Constants.LF)) {
428                 // HTTP/0.9 style request
429
eol = true;
430                 space = true;
431                 end = pos;
432             } else if ((buf[pos] == Constants.QUESTION)
433                        && (questionPos == -1)) {
434                 questionPos = pos;
435             }
436
437             pos++;
438
439         }
440
441         request.unparsedURI().setBytes(buf, start, end - start);
442         if (questionPos >= 0) {
443             request.queryString().setBytes(buf, questionPos + 1,
444                                            end - questionPos - 1);
445             request.requestURI().setBytes(buf, start, questionPos - start);
446         } else {
447             request.requestURI().setBytes(buf, start, end - start);
448         }
449
450         // Mark the current buffer position
451
start = pos;
452         end = 0;
453
454         //
455
// Reading the protocol
456
// Protocol is always US-ASCII
457
//
458

459         while (!eol) {
460
461             // Read new bytes if needed
462
if (pos >= lastValid) {
463                 if (!fill())
464                     throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
465             }
466
467             if (buf[pos] == Constants.CR) {
468                 end = pos;
469             } else if (buf[pos] == Constants.LF) {
470                 if (end == 0)
471                     end = pos;
472                 eol = true;
473             }
474
475             pos++;
476
477         }
478
479         if ((end - start) > 0) {
480             request.protocol().setBytes(buf, start, end - start);
481         } else {
482             request.protocol().setString("");
483         }
484
485     }
486
487
488     /**
489      * Parse the HTTP headers.
490      */

491     public void parseHeaders()
492         throws IOException JavaDoc {
493
494         while (parseHeader()) {
495         }
496
497         parsingHeader = false;
498         end = pos;
499
500     }
501
502
503     /**
504      * Parse an HTTP header.
505      *
506      * @return false after reading a blank line (which indicates that the
507      * HTTP header parsing is done
508      */

509     public boolean parseHeader()
510         throws IOException JavaDoc {
511
512         //
513
// Check for blank line
514
//
515

516         byte chr = 0;
517         while (true) {
518
519             // Read new bytes if needed
520
if (pos >= lastValid) {
521                 if (!fill())
522                     throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
523             }
524
525             chr = buf[pos];
526
527             if ((chr == Constants.CR) || (chr == Constants.LF)) {
528                 if (chr == Constants.LF) {
529                     pos++;
530                     return false;
531                 }
532             } else {
533                 break;
534             }
535
536             pos++;
537
538         }
539
540         // Mark the current buffer position
541
int start = pos;
542
543         //
544
// Reading the header name
545
// Header name is always US-ASCII
546
//
547

548         boolean colon = false;
549         MessageBytes headerValue = null;
550
551         while (!colon) {
552
553             // Read new bytes if needed
554
if (pos >= lastValid) {
555                 if (!fill())
556                     throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
557             }
558
559             if (buf[pos] == Constants.COLON) {
560                 colon = true;
561                 headerValue = headers.addValue(buf, start, pos - start);
562             }
563             chr = buf[pos];
564             if ((chr >= Constants.A) && (chr <= Constants.Z)) {
565                 buf[pos] = (byte) (chr - Constants.LC_OFFSET);
566             }
567
568             pos++;
569
570         }
571
572         // Mark the current buffer position
573
start = pos;
574         int realPos = pos;
575
576         //
577
// Reading the header value (which can be spanned over multiple lines)
578
//
579

580         boolean eol = false;
581         boolean validLine = true;
582
583         while (validLine) {
584
585             boolean space = true;
586
587             // Skipping spaces
588
while (space) {
589
590                 // Read new bytes if needed
591
if (pos >= lastValid) {
592                     if (!fill())
593                         throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
594                 }
595
596                 if ((buf[pos] == Constants.SP) || (buf[pos] == Constants.HT)) {
597                     pos++;
598                 } else {
599                     space = false;
600                 }
601
602             }
603
604             int lastSignificantChar = realPos;
605
606             // Reading bytes until the end of the line
607
while (!eol) {
608
609                 // Read new bytes if needed
610
if (pos >= lastValid) {
611                     if (!fill())
612                         throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
613                 }
614
615                 if (buf[pos] == Constants.CR) {
616                 } else if (buf[pos] == Constants.LF) {
617                     eol = true;
618                 } else if (buf[pos] == Constants.SP) {
619                     buf[realPos] = buf[pos];
620                     realPos++;
621                 } else {
622                     buf[realPos] = buf[pos];
623                     realPos++;
624                     lastSignificantChar = realPos;
625                 }
626
627                 pos++;
628
629             }
630
631             realPos = lastSignificantChar;
632
633             // Checking the first character of the new line. If the character
634
// is a LWS, then it's a multiline header
635

636             // Read new bytes if needed
637
if (pos >= lastValid) {
638                 if (!fill())
639                     throw new EOFException JavaDoc(sm.getString("iib.eof.error"));
640             }
641
642             chr = buf[pos];
643             if ((chr != Constants.SP) && (chr != Constants.HT)) {
644                 validLine = false;
645             } else {
646                 eol = false;
647                 // Copying one extra space in the buffer (since there must
648
// be at least one space inserted between the lines)
649
buf[realPos] = chr;
650                 realPos++;
651             }
652
653         }
654
655         // Set the header value
656
headerValue.setBytes(buf, start, realPos - start);
657
658         return true;
659
660     }
661
662
663     // ---------------------------------------------------- InputBuffer Methods
664

665
666     /**
667      * Read some bytes.
668      */

669     public int doRead(ByteChunk chunk, Request req)
670         throws IOException JavaDoc {
671
672         if (lastActiveFilter == -1)
673             return inputStreamInputBuffer.doRead(chunk, req);
674         else
675             return activeFilters[lastActiveFilter].doRead(chunk,req);
676
677     }
678
679
680     // ------------------------------------------------------ Protected Methods
681

682
683     /**
684      * Fill the internal buffer using data from the undelying input stream.
685      *
686      * @return false if at end of stream
687      */

688     protected boolean fill()
689         throws IOException JavaDoc {
690
691         int nRead = 0;
692
693         if (parsingHeader) {
694
695             if (lastValid == buf.length) {
696                 throw new IOException JavaDoc
697                     (sm.getString("iib.requestheadertoolarge.error"));
698             }
699
700             nRead = inputStream.read(buf, pos, buf.length - lastValid);
701             if (nRead > 0) {
702                 lastValid = pos + nRead;
703             }
704
705         } else {
706
707             if (buf.length - end < 4500) {
708                 // In this case, the request header was really large, so we allocate a
709
// brand new one; the old one will get GCed when subsequent requests
710
// clear all references
711
buf = new byte[buf.length];
712                 end = 0;
713             }
714             pos = end;
715             lastValid = pos;
716             nRead = inputStream.read(buf, pos, buf.length - lastValid);
717             if (nRead > 0) {
718                 lastValid = pos + nRead;
719             }
720
721         }
722
723         return (nRead > 0);
724
725     }
726
727
728     // ------------------------------------- InputStreamInputBuffer Inner Class
729

730
731     /**
732      * This class is an input buffer which will read its data from an input
733      * stream.
734      */

735     protected class InputStreamInputBuffer
736         implements InputBuffer {
737
738
739         /**
740          * Read bytes into the specified chunk.
741          */

742         public int doRead(ByteChunk chunk, Request req )
743             throws IOException JavaDoc {
744
745             if (pos >= lastValid) {
746                 if (!fill())
747                     return -1;
748             }
749
750             int length = lastValid - pos;
751             chunk.setBytes(buf, pos, length);
752             pos = lastValid;
753
754             return (length);
755
756         }
757
758
759     }
760
761
762 }
763
Popular Tags