001: /*
002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/connector/http/SocketInputStream.java,v 1.10 2002/03/18 07:15:40 remm Exp $
003: * $Revision: 1.10 $
004: * $Date: 2002/03/18 07:15:40 $
005: *
006: * ====================================================================
007: *
008: * The Apache Software License, Version 1.1
009: *
010: * Copyright (c) 1999 The Apache Software Foundation. All rights
011: * reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions
015: * are met:
016: *
017: * 1. Redistributions of source code must retain the above copyright
018: * notice, this list of conditions and the following disclaimer.
019: *
020: * 2. Redistributions in binary form must reproduce the above copyright
021: * notice, this list of conditions and the following disclaimer in
022: * the documentation and/or other materials provided with the
023: * distribution.
024: *
025: * 3. The end-user documentation included with the redistribution, if
026: * any, must include the following acknowlegement:
027: * "This product includes software developed by the
028: * Apache Software Foundation (http://www.apache.org/)."
029: * Alternately, this acknowlegement may appear in the software itself,
030: * if and wherever such third-party acknowlegements normally appear.
031: *
032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
033: * Foundation" must not be used to endorse or promote products derived
034: * from this software without prior written permission. For written
035: * permission, please contact apache@apache.org.
036: *
037: * 5. Products derived from this software may not be called "Apache"
038: * nor may "Apache" appear in their names without prior written
039: * permission of the Apache Group.
040: *
041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
052: * SUCH DAMAGE.
053: * ====================================================================
054: *
055: * This software consists of voluntary contributions made by many
056: * individuals on behalf of the Apache Software Foundation. For more
057: * information on the Apache Software Foundation, please see
058: * <http://www.apache.org/>.
059: *
060: * [Additional notices, if required by prior licensing conditions]
061: *
062: */
063:
064: package org.apache.catalina.connector.http;
065:
066: import java.io.IOException;
067: import java.io.InputStream;
068: import java.io.EOFException;
069: import org.apache.catalina.util.StringManager;
070:
071: /**
072: * Extends InputStream to be more efficient reading lines during HTTP
073: * header processing.
074: *
075: * @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
076: * @deprecated
077: */
078: public class SocketInputStream extends InputStream {
079:
080: // -------------------------------------------------------------- Constants
081:
082: /**
083: * CR.
084: */
085: private static final byte CR = (byte) '\r';
086:
087: /**
088: * LF.
089: */
090: private static final byte LF = (byte) '\n';
091:
092: /**
093: * SP.
094: */
095: private static final byte SP = (byte) ' ';
096:
097: /**
098: * HT.
099: */
100: private static final byte HT = (byte) '\t';
101:
102: /**
103: * COLON.
104: */
105: private static final byte COLON = (byte) ':';
106:
107: /**
108: * Lower case offset.
109: */
110: private static final int LC_OFFSET = 'A' - 'a';
111:
112: /**
113: * Internal buffer.
114: */
115: protected byte buf[];
116:
117: /**
118: * Last valid byte.
119: */
120: protected int count;
121:
122: /**
123: * Position in the buffer.
124: */
125: protected int pos;
126:
127: /**
128: * Underlying input stream.
129: */
130: protected InputStream is;
131:
132: // ----------------------------------------------------------- Constructors
133:
134: /**
135: * Construct a servlet input stream associated with the specified socket
136: * input.
137: *
138: * @param is socket input stream
139: * @param bufferSize size of the internal buffer
140: */
141: public SocketInputStream(InputStream is, int bufferSize) {
142:
143: this .is = is;
144: buf = new byte[bufferSize];
145:
146: }
147:
148: // -------------------------------------------------------------- Variables
149:
150: /**
151: * The string manager for this package.
152: */
153: protected static StringManager sm = StringManager
154: .getManager(Constants.Package);
155:
156: // ----------------------------------------------------- Instance Variables
157:
158: // --------------------------------------------------------- Public Methods
159:
160: /**
161: * Read the request line, and copies it to the given buffer. This
162: * function is meant to be used during the HTTP request header parsing.
163: * Do NOT attempt to read the request body using it.
164: *
165: * @param requestLine Request line object
166: * @throws IOException If an exception occurs during the underlying socket
167: * read operations, or if the given buffer is not big enough to accomodate
168: * the whole line.
169: */
170: public void readRequestLine(HttpRequestLine requestLine)
171: throws IOException {
172:
173: // Recycling check
174: if (requestLine.methodEnd != 0)
175: requestLine.recycle();
176:
177: // Checking for a blank line
178: int chr = 0;
179: do { // Skipping CR or LF
180: try {
181: chr = read();
182: } catch (IOException e) {
183: chr = -1;
184: }
185: } while ((chr == CR) || (chr == LF));
186: if (chr == -1)
187: throw new EOFException(sm
188: .getString("requestStream.readline.error"));
189: pos--;
190:
191: // Reading the method name
192:
193: int maxRead = requestLine.method.length;
194: int readStart = pos;
195: int readCount = 0;
196:
197: boolean space = false;
198:
199: while (!space) {
200: // if the buffer is full, extend it
201: if (readCount >= maxRead) {
202: if ((2 * maxRead) <= HttpRequestLine.MAX_METHOD_SIZE) {
203: char[] newBuffer = new char[2 * maxRead];
204: System.arraycopy(requestLine.method, 0, newBuffer,
205: 0, maxRead);
206: requestLine.method = newBuffer;
207: maxRead = requestLine.method.length;
208: } else {
209: throw new IOException(
210: sm
211: .getString("requestStream.readline.toolong"));
212: }
213: }
214: // We're at the end of the internal buffer
215: if (pos >= count) {
216: int val = read();
217: if (val == -1) {
218: throw new IOException(sm
219: .getString("requestStream.readline.error"));
220: }
221: pos = 0;
222: readStart = 0;
223: }
224: if (buf[pos] == SP) {
225: space = true;
226: }
227: requestLine.method[readCount] = (char) buf[pos];
228: readCount++;
229: pos++;
230: }
231:
232: requestLine.methodEnd = readCount - 1;
233:
234: // Reading URI
235:
236: maxRead = requestLine.uri.length;
237: readStart = pos;
238: readCount = 0;
239:
240: space = false;
241:
242: boolean eol = false;
243:
244: while (!space) {
245: // if the buffer is full, extend it
246: if (readCount >= maxRead) {
247: if ((2 * maxRead) <= HttpRequestLine.MAX_URI_SIZE) {
248: char[] newBuffer = new char[2 * maxRead];
249: System.arraycopy(requestLine.uri, 0, newBuffer, 0,
250: maxRead);
251: requestLine.uri = newBuffer;
252: maxRead = requestLine.uri.length;
253: } else {
254: throw new IOException(
255: sm
256: .getString("requestStream.readline.toolong"));
257: }
258: }
259: // We're at the end of the internal buffer
260: if (pos >= count) {
261: int val = read();
262: if (val == -1)
263: throw new IOException(sm
264: .getString("requestStream.readline.error"));
265: pos = 0;
266: readStart = 0;
267: }
268: if (buf[pos] == SP) {
269: space = true;
270: } else if ((buf[pos] == CR) || (buf[pos] == LF)) {
271: // HTTP/0.9 style request
272: eol = true;
273: space = true;
274: }
275: requestLine.uri[readCount] = (char) buf[pos];
276: readCount++;
277: pos++;
278: }
279:
280: requestLine.uriEnd = readCount - 1;
281:
282: // Reading protocol
283:
284: maxRead = requestLine.protocol.length;
285: readStart = pos;
286: readCount = 0;
287:
288: while (!eol) {
289: // if the buffer is full, extend it
290: if (readCount >= maxRead) {
291: if ((2 * maxRead) <= HttpRequestLine.MAX_PROTOCOL_SIZE) {
292: char[] newBuffer = new char[2 * maxRead];
293: System.arraycopy(requestLine.protocol, 0,
294: newBuffer, 0, maxRead);
295: requestLine.protocol = newBuffer;
296: maxRead = requestLine.protocol.length;
297: } else {
298: throw new IOException(
299: sm
300: .getString("requestStream.readline.toolong"));
301: }
302: }
303: // We're at the end of the internal buffer
304: if (pos >= count) {
305: // Copying part (or all) of the internal buffer to the line
306: // buffer
307: int val = read();
308: if (val == -1)
309: throw new IOException(sm
310: .getString("requestStream.readline.error"));
311: pos = 0;
312: readStart = 0;
313: }
314: if (buf[pos] == CR) {
315: // Skip CR.
316: } else if (buf[pos] == LF) {
317: eol = true;
318: } else {
319: requestLine.protocol[readCount] = (char) buf[pos];
320: readCount++;
321: }
322: pos++;
323: }
324:
325: requestLine.protocolEnd = readCount;
326:
327: }
328:
329: /**
330: * Read a header, and copies it to the given buffer. This
331: * function is meant to be used during the HTTP request header parsing.
332: * Do NOT attempt to read the request body using it.
333: *
334: * @param requestLine Request line object
335: * @throws IOException If an exception occurs during the underlying socket
336: * read operations, or if the given buffer is not big enough to accomodate
337: * the whole line.
338: */
339: public void readHeader(HttpHeader header) throws IOException {
340:
341: // Recycling check
342: if (header.nameEnd != 0)
343: header.recycle();
344:
345: // Checking for a blank line
346: int chr = read();
347: if ((chr == CR) || (chr == LF)) { // Skipping CR
348: if (chr == CR)
349: read(); // Skipping LF
350: header.nameEnd = 0;
351: header.valueEnd = 0;
352: return;
353: } else {
354: pos--;
355: }
356:
357: // Reading the header name
358:
359: int maxRead = header.name.length;
360: int readStart = pos;
361: int readCount = 0;
362:
363: boolean colon = false;
364:
365: while (!colon) {
366: // if the buffer is full, extend it
367: if (readCount >= maxRead) {
368: if ((2 * maxRead) <= HttpHeader.MAX_NAME_SIZE) {
369: char[] newBuffer = new char[2 * maxRead];
370: System.arraycopy(header.name, 0, newBuffer, 0,
371: maxRead);
372: header.name = newBuffer;
373: maxRead = header.name.length;
374: } else {
375: throw new IOException(
376: sm
377: .getString("requestStream.readline.toolong"));
378: }
379: }
380: // We're at the end of the internal buffer
381: if (pos >= count) {
382: int val = read();
383: if (val == -1) {
384: throw new IOException(sm
385: .getString("requestStream.readline.error"));
386: }
387: pos = 0;
388: readStart = 0;
389: }
390: if (buf[pos] == COLON) {
391: colon = true;
392: }
393: char val = (char) buf[pos];
394: if ((val >= 'A') && (val <= 'Z')) {
395: val = (char) (val - LC_OFFSET);
396: }
397: header.name[readCount] = val;
398: readCount++;
399: pos++;
400: }
401:
402: header.nameEnd = readCount - 1;
403:
404: // Reading the header value (which can be spanned over multiple lines)
405:
406: maxRead = header.value.length;
407: readStart = pos;
408: readCount = 0;
409:
410: int crPos = -2;
411:
412: boolean eol = false;
413: boolean validLine = true;
414:
415: while (validLine) {
416:
417: boolean space = true;
418:
419: // Skipping spaces
420: // Note : Only leading white spaces are removed. Trailing white
421: // spaces are not.
422: while (space) {
423: // We're at the end of the internal buffer
424: if (pos >= count) {
425: // Copying part (or all) of the internal buffer to the line
426: // buffer
427: int val = read();
428: if (val == -1)
429: throw new IOException(
430: sm
431: .getString("requestStream.readline.error"));
432: pos = 0;
433: readStart = 0;
434: }
435: if ((buf[pos] == SP) || (buf[pos] == HT)) {
436: pos++;
437: } else {
438: space = false;
439: }
440: }
441:
442: while (!eol) {
443: // if the buffer is full, extend it
444: if (readCount >= maxRead) {
445: if ((2 * maxRead) <= HttpHeader.MAX_VALUE_SIZE) {
446: char[] newBuffer = new char[2 * maxRead];
447: System.arraycopy(header.value, 0, newBuffer, 0,
448: maxRead);
449: header.value = newBuffer;
450: maxRead = header.value.length;
451: } else {
452: throw new IOException(
453: sm
454: .getString("requestStream.readline.toolong"));
455: }
456: }
457: // We're at the end of the internal buffer
458: if (pos >= count) {
459: // Copying part (or all) of the internal buffer to the line
460: // buffer
461: int val = read();
462: if (val == -1)
463: throw new IOException(
464: sm
465: .getString("requestStream.readline.error"));
466: pos = 0;
467: readStart = 0;
468: }
469: if (buf[pos] == CR) {
470: } else if (buf[pos] == LF) {
471: eol = true;
472: } else {
473: // FIXME : Check if binary conversion is working fine
474: int ch = buf[pos] & 0xff;
475: header.value[readCount] = (char) ch;
476: readCount++;
477: }
478: pos++;
479: }
480:
481: int nextChr = read();
482:
483: if ((nextChr != SP) && (nextChr != HT)) {
484: pos--;
485: validLine = false;
486: } else {
487: eol = false;
488: // if the buffer is full, extend it
489: if (readCount >= maxRead) {
490: if ((2 * maxRead) <= HttpHeader.MAX_VALUE_SIZE) {
491: char[] newBuffer = new char[2 * maxRead];
492: System.arraycopy(header.value, 0, newBuffer, 0,
493: maxRead);
494: header.value = newBuffer;
495: maxRead = header.value.length;
496: } else {
497: throw new IOException(
498: sm
499: .getString("requestStream.readline.toolong"));
500: }
501: }
502: header.value[readCount] = ' ';
503: readCount++;
504: }
505:
506: }
507:
508: header.valueEnd = readCount;
509:
510: }
511:
512: /**
513: * Read byte.
514: */
515: public int read() throws IOException {
516: if (pos >= count) {
517: fill();
518: if (pos >= count)
519: return -1;
520: }
521: return buf[pos++] & 0xff;
522: }
523:
524: /**
525: *
526: */
527: /*
528: public int read(byte b[], int off, int len)
529: throws IOException {
530:
531: }
532: */
533:
534: /**
535: *
536: */
537: /*
538: public long skip(long n)
539: throws IOException {
540:
541: }
542: */
543:
544: /**
545: * Returns the number of bytes that can be read from this input
546: * stream without blocking.
547: */
548: public int available() throws IOException {
549: return (count - pos) + is.available();
550: }
551:
552: /**
553: * Close the input stream.
554: */
555: public void close() throws IOException {
556: if (is == null)
557: return;
558: is.close();
559: is = null;
560: buf = null;
561: }
562:
563: // ------------------------------------------------------ Protected Methods
564:
565: /**
566: * Fill the internal buffer using data from the undelying input stream.
567: */
568: protected void fill() throws IOException {
569: pos = 0;
570: count = 0;
571: int nRead = is.read(buf, 0, buf.length);
572: if (nRead > 0) {
573: count = nRead;
574: }
575: }
576:
577: }
|