001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.coyote.http11;
019:
020: import java.io.IOException;
021: import java.io.EOFException;
022: import java.net.SocketTimeoutException;
023: import java.nio.ByteBuffer;
024:
025: import org.apache.tomcat.jni.Socket;
026: import org.apache.tomcat.jni.Status;
027: import org.apache.tomcat.util.buf.ByteChunk;
028: import org.apache.tomcat.util.buf.MessageBytes;
029: import org.apache.tomcat.util.http.MimeHeaders;
030: import org.apache.tomcat.util.res.StringManager;
031:
032: import org.apache.coyote.InputBuffer;
033: import org.apache.coyote.Request;
034:
035: /**
036: * Implementation of InputBuffer which provides HTTP request header parsing as
037: * well as transfer decoding.
038: *
039: * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
040: */
041: public class InternalAprInputBuffer implements InputBuffer {
042:
043: // -------------------------------------------------------------- Constants
044:
045: // ----------------------------------------------------------- Constructors
046:
047: /**
048: * Alternate constructor.
049: */
050: public InternalAprInputBuffer(Request request, int headerBufferSize) {
051:
052: this .request = request;
053: headers = request.getMimeHeaders();
054:
055: buf = new byte[headerBufferSize];
056: if (headerBufferSize < (8 * 1024)) {
057: bbuf = ByteBuffer.allocateDirect(6 * 1500);
058: } else {
059: bbuf = ByteBuffer
060: .allocateDirect((headerBufferSize / 1500 + 1) * 1500);
061: }
062:
063: inputStreamInputBuffer = new SocketInputBuffer();
064:
065: filterLibrary = new InputFilter[0];
066: activeFilters = new InputFilter[0];
067: lastActiveFilter = -1;
068:
069: parsingHeader = true;
070: swallowInput = true;
071:
072: }
073:
074: // -------------------------------------------------------------- Variables
075:
076: /**
077: * The string manager for this package.
078: */
079: protected static StringManager sm = StringManager
080: .getManager(Constants.Package);
081:
082: // ----------------------------------------------------- Instance Variables
083:
084: /**
085: * Associated Coyote request.
086: */
087: protected Request request;
088:
089: /**
090: * Headers of the associated request.
091: */
092: protected MimeHeaders headers;
093:
094: /**
095: * State.
096: */
097: protected boolean parsingHeader;
098:
099: /**
100: * Swallow input ? (in the case of an expectation)
101: */
102: protected boolean swallowInput;
103:
104: /**
105: * Pointer to the current read buffer.
106: */
107: protected byte[] buf;
108:
109: /**
110: * Last valid byte.
111: */
112: protected int lastValid;
113:
114: /**
115: * Position in the buffer.
116: */
117: protected int pos;
118:
119: /**
120: * Pos of the end of the header in the buffer, which is also the
121: * start of the body.
122: */
123: protected int end;
124:
125: /**
126: * Direct byte buffer used to perform actual reading.
127: */
128: protected ByteBuffer bbuf;
129:
130: /**
131: * Underlying socket.
132: */
133: protected long socket;
134:
135: /**
136: * Underlying input buffer.
137: */
138: protected InputBuffer inputStreamInputBuffer;
139:
140: /**
141: * Filter library.
142: * Note: Filter[0] is always the "chunked" filter.
143: */
144: protected InputFilter[] filterLibrary;
145:
146: /**
147: * Active filters (in order).
148: */
149: protected InputFilter[] activeFilters;
150:
151: /**
152: * Index of the last active filter.
153: */
154: protected int lastActiveFilter;
155:
156: // ------------------------------------------------------------- Properties
157:
158: /**
159: * Set the underlying socket.
160: */
161: public void setSocket(long socket) {
162: this .socket = socket;
163: Socket.setrbb(this .socket, bbuf);
164: }
165:
166: /**
167: * Get the underlying socket input stream.
168: */
169: public long getSocket() {
170: return socket;
171: }
172:
173: /**
174: * Add an input filter to the filter library.
175: */
176: public void addFilter(InputFilter filter) {
177:
178: InputFilter[] newFilterLibrary = new InputFilter[filterLibrary.length + 1];
179: for (int i = 0; i < filterLibrary.length; i++) {
180: newFilterLibrary[i] = filterLibrary[i];
181: }
182: newFilterLibrary[filterLibrary.length] = filter;
183: filterLibrary = newFilterLibrary;
184:
185: activeFilters = new InputFilter[filterLibrary.length];
186:
187: }
188:
189: /**
190: * Get filters.
191: */
192: public InputFilter[] getFilters() {
193:
194: return filterLibrary;
195:
196: }
197:
198: /**
199: * Clear filters.
200: */
201: public void clearFilters() {
202:
203: filterLibrary = new InputFilter[0];
204: lastActiveFilter = -1;
205:
206: }
207:
208: /**
209: * Add an input filter to the filter library.
210: */
211: public void addActiveFilter(InputFilter filter) {
212:
213: if (lastActiveFilter == -1) {
214: filter.setBuffer(inputStreamInputBuffer);
215: } else {
216: for (int i = 0; i <= lastActiveFilter; i++) {
217: if (activeFilters[i] == filter)
218: return;
219: }
220: filter.setBuffer(activeFilters[lastActiveFilter]);
221: }
222:
223: activeFilters[++lastActiveFilter] = filter;
224:
225: filter.setRequest(request);
226:
227: }
228:
229: /**
230: * Set the swallow input flag.
231: */
232: public void setSwallowInput(boolean swallowInput) {
233: this .swallowInput = swallowInput;
234: }
235:
236: // --------------------------------------------------------- Public Methods
237:
238: /**
239: * Recycle the input buffer. This should be called when closing the
240: * connection.
241: */
242: public void recycle() {
243:
244: // Recycle Request object
245: request.recycle();
246:
247: socket = 0;
248: lastValid = 0;
249: pos = 0;
250: lastActiveFilter = -1;
251: parsingHeader = true;
252: swallowInput = true;
253:
254: }
255:
256: /**
257: * End processing of current HTTP request.
258: * Note: All bytes of the current request should have been already
259: * consumed. This method only resets all the pointers so that we are ready
260: * to parse the next HTTP request.
261: */
262: public void nextRequest() {
263:
264: // Recycle Request object
265: request.recycle();
266:
267: // Copy leftover bytes to the beginning of the buffer
268: if (lastValid - pos > 0) {
269: int npos = 0;
270: int opos = pos;
271: while (lastValid - opos > opos - npos) {
272: System.arraycopy(buf, opos, buf, npos, opos - npos);
273: npos += pos;
274: opos += pos;
275: }
276: System.arraycopy(buf, opos, buf, npos, lastValid - opos);
277: }
278:
279: // Recycle filters
280: for (int i = 0; i <= lastActiveFilter; i++) {
281: activeFilters[i].recycle();
282: }
283:
284: // Reset pointers
285: lastValid = lastValid - pos;
286: pos = 0;
287: lastActiveFilter = -1;
288: parsingHeader = true;
289: swallowInput = true;
290:
291: }
292:
293: /**
294: * End request (consumes leftover bytes).
295: *
296: * @throws IOException an undelying I/O error occured
297: */
298: public void endRequest() throws IOException {
299:
300: if (swallowInput && (lastActiveFilter != -1)) {
301: int extraBytes = (int) activeFilters[lastActiveFilter]
302: .end();
303: pos = pos - extraBytes;
304: }
305:
306: }
307:
308: /**
309: * Read the request line. This function is meant to be used during the
310: * HTTP request header parsing. Do NOT attempt to read the request body
311: * using it.
312: *
313: * @throws IOException If an exception occurs during the underlying socket
314: * read operations, or if the given buffer is not big enough to accomodate
315: * the whole line.
316: * @return true if data is properly fed; false if no data is available
317: * immediately and thread should be freed
318: */
319: public boolean parseRequestLine(boolean useAvailableData)
320: throws IOException {
321:
322: int start = 0;
323:
324: //
325: // Skipping blank lines
326: //
327:
328: byte chr = 0;
329: do {
330:
331: // Read new bytes if needed
332: if (pos >= lastValid) {
333: if (useAvailableData) {
334: return false;
335: }
336: if (!fill())
337: throw new EOFException(sm
338: .getString("iib.eof.error"));
339: }
340:
341: chr = buf[pos++];
342:
343: } while ((chr == Constants.CR) || (chr == Constants.LF));
344:
345: pos--;
346:
347: // Mark the current buffer position
348: start = pos;
349:
350: if (pos >= lastValid) {
351: if (useAvailableData) {
352: return false;
353: }
354: if (!fill())
355: throw new EOFException(sm.getString("iib.eof.error"));
356: }
357:
358: //
359: // Reading the method name
360: // Method name is always US-ASCII
361: //
362:
363: boolean space = false;
364:
365: while (!space) {
366:
367: // Read new bytes if needed
368: if (pos >= lastValid) {
369: if (!fill())
370: throw new EOFException(sm
371: .getString("iib.eof.error"));
372: }
373:
374: if (buf[pos] == Constants.SP) {
375: space = true;
376: request.method().setBytes(buf, start, pos - start);
377: }
378:
379: pos++;
380:
381: }
382:
383: // Mark the current buffer position
384: start = pos;
385: int end = 0;
386: int questionPos = -1;
387:
388: //
389: // Reading the URI
390: //
391:
392: space = false;
393: boolean eol = false;
394:
395: while (!space) {
396:
397: // Read new bytes if needed
398: if (pos >= lastValid) {
399: if (!fill())
400: throw new EOFException(sm
401: .getString("iib.eof.error"));
402: }
403:
404: if (buf[pos] == Constants.SP) {
405: space = true;
406: end = pos;
407: } else if ((buf[pos] == Constants.CR)
408: || (buf[pos] == Constants.LF)) {
409: // HTTP/0.9 style request
410: eol = true;
411: space = true;
412: end = pos;
413: } else if ((buf[pos] == Constants.QUESTION)
414: && (questionPos == -1)) {
415: questionPos = pos;
416: }
417:
418: pos++;
419:
420: }
421:
422: request.unparsedURI().setBytes(buf, start, end - start);
423: if (questionPos >= 0) {
424: request.queryString().setBytes(buf, questionPos + 1,
425: end - questionPos - 1);
426: request.requestURI().setBytes(buf, start,
427: questionPos - start);
428: } else {
429: request.requestURI().setBytes(buf, start, end - start);
430: }
431:
432: // Mark the current buffer position
433: start = pos;
434: end = 0;
435:
436: //
437: // Reading the protocol
438: // Protocol is always US-ASCII
439: //
440:
441: while (!eol) {
442:
443: // Read new bytes if needed
444: if (pos >= lastValid) {
445: if (!fill())
446: throw new EOFException(sm
447: .getString("iib.eof.error"));
448: }
449:
450: if (buf[pos] == Constants.CR) {
451: end = pos;
452: } else if (buf[pos] == Constants.LF) {
453: if (end == 0)
454: end = pos;
455: eol = true;
456: }
457:
458: pos++;
459:
460: }
461:
462: if ((end - start) > 0) {
463: request.protocol().setBytes(buf, start, end - start);
464: } else {
465: request.protocol().setString("");
466: }
467:
468: return true;
469:
470: }
471:
472: /**
473: * Parse the HTTP headers.
474: */
475: public void parseHeaders() throws IOException {
476:
477: while (parseHeader()) {
478: }
479:
480: parsingHeader = false;
481: end = pos;
482:
483: }
484:
485: /**
486: * Parse an HTTP header.
487: *
488: * @return false after reading a blank line (which indicates that the
489: * HTTP header parsing is done
490: */
491: public boolean parseHeader() throws IOException {
492:
493: //
494: // Check for blank line
495: //
496:
497: byte chr = 0;
498: while (true) {
499:
500: // Read new bytes if needed
501: if (pos >= lastValid) {
502: if (!fill())
503: throw new EOFException(sm
504: .getString("iib.eof.error"));
505: }
506:
507: chr = buf[pos];
508:
509: if ((chr == Constants.CR) || (chr == Constants.LF)) {
510: if (chr == Constants.LF) {
511: pos++;
512: return false;
513: }
514: } else {
515: break;
516: }
517:
518: pos++;
519:
520: }
521:
522: // Mark the current buffer position
523: int start = pos;
524:
525: //
526: // Reading the header name
527: // Header name is always US-ASCII
528: //
529:
530: boolean colon = false;
531: MessageBytes headerValue = null;
532:
533: while (!colon) {
534:
535: // Read new bytes if needed
536: if (pos >= lastValid) {
537: if (!fill())
538: throw new EOFException(sm
539: .getString("iib.eof.error"));
540: }
541:
542: if (buf[pos] == Constants.COLON) {
543: colon = true;
544: headerValue = headers.addValue(buf, start, pos - start);
545: }
546: chr = buf[pos];
547: if ((chr >= Constants.A) && (chr <= Constants.Z)) {
548: buf[pos] = (byte) (chr - Constants.LC_OFFSET);
549: }
550:
551: pos++;
552:
553: }
554:
555: // Mark the current buffer position
556: start = pos;
557: int realPos = pos;
558:
559: //
560: // Reading the header value (which can be spanned over multiple lines)
561: //
562:
563: boolean eol = false;
564: boolean validLine = true;
565:
566: while (validLine) {
567:
568: boolean space = true;
569:
570: // Skipping spaces
571: while (space) {
572:
573: // Read new bytes if needed
574: if (pos >= lastValid) {
575: if (!fill())
576: throw new EOFException(sm
577: .getString("iib.eof.error"));
578: }
579:
580: if ((buf[pos] == Constants.SP)
581: || (buf[pos] == Constants.HT)) {
582: pos++;
583: } else {
584: space = false;
585: }
586:
587: }
588:
589: int lastSignificantChar = realPos;
590:
591: // Reading bytes until the end of the line
592: while (!eol) {
593:
594: // Read new bytes if needed
595: if (pos >= lastValid) {
596: if (!fill())
597: throw new EOFException(sm
598: .getString("iib.eof.error"));
599: }
600:
601: if (buf[pos] == Constants.CR) {
602: } else if (buf[pos] == Constants.LF) {
603: eol = true;
604: } else if (buf[pos] == Constants.SP) {
605: buf[realPos] = buf[pos];
606: realPos++;
607: } else {
608: buf[realPos] = buf[pos];
609: realPos++;
610: lastSignificantChar = realPos;
611: }
612:
613: pos++;
614:
615: }
616:
617: realPos = lastSignificantChar;
618:
619: // Checking the first character of the new line. If the character
620: // is a LWS, then it's a multiline header
621:
622: // Read new bytes if needed
623: if (pos >= lastValid) {
624: if (!fill())
625: throw new EOFException(sm
626: .getString("iib.eof.error"));
627: }
628:
629: chr = buf[pos];
630: if ((chr != Constants.SP) && (chr != Constants.HT)) {
631: validLine = false;
632: } else {
633: eol = false;
634: // Copying one extra space in the buffer (since there must
635: // be at least one space inserted between the lines)
636: buf[realPos] = chr;
637: realPos++;
638: }
639:
640: }
641:
642: // Set the header value
643: headerValue.setBytes(buf, start, realPos - start);
644:
645: return true;
646:
647: }
648:
649: /**
650: * Available bytes (note that due to encoding, this may not correspond )
651: */
652: public int available() {
653: int result = (lastValid - pos);
654: if ((result == 0) && (lastActiveFilter >= 0)) {
655: for (int i = 0; (result == 0) && (i <= lastActiveFilter); i++) {
656: result = activeFilters[i].available();
657: }
658: }
659: return result;
660: }
661:
662: // ---------------------------------------------------- InputBuffer Methods
663:
664: /**
665: * Read some bytes.
666: */
667: public int doRead(ByteChunk chunk, Request req) throws IOException {
668:
669: if (lastActiveFilter == -1)
670: return inputStreamInputBuffer.doRead(chunk, req);
671: else
672: return activeFilters[lastActiveFilter].doRead(chunk, req);
673:
674: }
675:
676: // ------------------------------------------------------ Protected Methods
677:
678: /**
679: * Fill the internal buffer using data from the undelying input stream.
680: *
681: * @return false if at end of stream
682: */
683: protected boolean fill() throws IOException {
684:
685: int nRead = 0;
686:
687: if (parsingHeader) {
688:
689: if (lastValid == buf.length) {
690: throw new IOException(sm
691: .getString("iib.requestheadertoolarge.error"));
692: }
693:
694: bbuf.clear();
695: nRead = Socket.recvbb(socket, 0, buf.length - lastValid);
696: if (nRead > 0) {
697: bbuf.limit(nRead);
698: bbuf.get(buf, pos, nRead);
699: lastValid = pos + nRead;
700: } else {
701: if ((-nRead) == Status.EAGAIN) {
702: return false;
703: } else {
704: throw new IOException(sm
705: .getString("iib.failedread"));
706: }
707: }
708:
709: } else {
710:
711: if (buf.length - end < 4500) {
712: // In this case, the request header was really large, so we allocate a
713: // brand new one; the old one will get GCed when subsequent requests
714: // clear all references
715: buf = new byte[buf.length];
716: end = 0;
717: }
718: pos = end;
719: lastValid = pos;
720: bbuf.clear();
721: nRead = Socket.recvbb(socket, 0, buf.length - lastValid);
722: if (nRead > 0) {
723: bbuf.limit(nRead);
724: bbuf.get(buf, pos, nRead);
725: lastValid = pos + nRead;
726: } else {
727: if ((-nRead) == Status.ETIMEDOUT
728: || (-nRead) == Status.TIMEUP) {
729: throw new SocketTimeoutException(sm
730: .getString("iib.failedread"));
731: } else {
732: throw new IOException(sm
733: .getString("iib.failedread"));
734: }
735: }
736:
737: }
738:
739: return (nRead > 0);
740:
741: }
742:
743: // ------------------------------------- InputStreamInputBuffer Inner Class
744:
745: /**
746: * This class is an input buffer which will read its data from an input
747: * stream.
748: */
749: protected class SocketInputBuffer implements InputBuffer {
750:
751: /**
752: * Read bytes into the specified chunk.
753: */
754: public int doRead(ByteChunk chunk, Request req)
755: throws IOException {
756:
757: if (pos >= lastValid) {
758: if (!fill())
759: return -1;
760: }
761:
762: int length = lastValid - pos;
763: chunk.setBytes(buf, pos, length);
764: pos = lastValid;
765:
766: return (length);
767:
768: }
769:
770: }
771:
772: }
|