alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*   
 *  Copyright 1999-2004 The Apache Sofware Foundation.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.apache.tomcat.modules.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.io.RecycleBufferedInputStream;
import org.apache.tomcat.util.log.Log;

public class Http10 {
    private Socket socket;
    private boolean moreRequests = false;
    RecycleBufferedInputStream sin;
    protected OutputStream sout;

    byte[] buf;
    int bufSize=2048; // default
    int off=0;
    int count=0;

    protected static final int DEFAULT_HEAD_BUFFER_SIZE = 1024;
    protected byte[] oBuffer = new byte[DEFAULT_HEAD_BUFFER_SIZE];
    protected int oBufferCount = 0;

    static final byte CRLF[]= { (byte)'\r', (byte)'\n' };
    Log loghelper = Log.getLog("tc_log", this);
    
    public Http10() {
        super();
	buf=new byte[bufSize];
    }

    public void setSocket(Socket socket) throws IOException {
	if( sin==null)
	    sin = new RecycleBufferedInputStream ( socket.getInputStream());
	else
	    sin.setInputStream( socket.getInputStream());
        this.socket = socket;
    	moreRequests = true;	
	sout=socket.getOutputStream();
    }

    public void recycle() {
	off=0;
	count=0;
	oBufferCount=0;
	if( sin!=null )  sin.recycle();
    }

    // -------------------- HTTP input methods --------------------
    
    public int doRead() throws IOException {
	return sin.read();
    }

    public int doRead(byte[] b, int off, int len) throws IOException {
	return sin.read(b, off, len);
    }

    static class Location {
	int off;
	int end;
	Location(  int start , int end ) {
	    this.off=start;
	    this.end=end;
	}
    }

    private int readLine( Location line )
	throws IOException
    {
	while (true) {
	    line.end=line.off;
	    
	    int len = buf.length - line.off;
	    
	    if (len > 0) {
		len = readLine(sin, buf, line.off, len);

		if (len == -1) {
		    return 400;
		}
	    
		line.end += len;
	    
		if (len == 0 || buf[line.end-1] == '\n') {
		    // strip \n
		    if( line.end> line.off && buf[line.end-1]=='\n' )
			--line.end;
		
		    // strip off trailing "\r\n"
		    if (line.end > line.off && buf[line.end-1] == '\r') {
			--line.end;
		    }
		    return 0; // Empty line || end of line
		}
	    }
	    
	    // overflowed buffer, so temporarily expand and continue
	    
	    // XXX DOS - if the length is too big - stop and throw exception
	    byte[] tmp = new byte[buf.length * 2];
	    
	    System.arraycopy(buf, 0, tmp, 0, buf.length);
	    buf = tmp;
	    // read more ( the buffer was resized )
	}
    }

    Location headerArea=new Location(  off, -1 );

    Location line=new Location(  off, -1 );
    Location nextLine=new Location( off, -1);
    
    /**
     * Reads header fields from the specified servlet input stream until
     * a blank line is encountered.
     * @param in the servlet input stream
     * @exception IllegalArgumentException if the header format was invalid 
     * @exception IOException if an I/O error has occurred
     */
    public int readHeaders( MimeHeaders headers )  throws IOException {
	// use pre-allocated buffer if possible
	off = count; // where the request line ended
	headerArea.off=off;
	boolean firstLine=true;
			
	while (true) {
	    if( firstLine ) {
		// first line
		line.off=off;
		
		int status=readLine( line );
		if( status != 0 ) return status;
		
		off=line.end;
		
		if( d > 0 ) debug( "Read1: " + new String( buf, line.off,
							   line.end-line.off));
		firstLine=false;
	    } else {
		line.off=nextLine.off;
		line.end=nextLine.end;
	    }

	    if (line.off == line.end) {
		// Empty line, end of headers
		break;
	    }
	    
	    // Read next lines, maybe we have a multi-line header
	    while( true ) {
		nextLine.off=line.end;
		int status=readLine( nextLine );
		if( status!= 0 ) return status;
		if( d > 0 ) debug( "Read2: " +
				   new String( buf, nextLine.off,
					       nextLine.end-nextLine.off));

		off=nextLine.end;

		// if continuation, concat with line, and continue
		// if a not - break, the current line will be parsed and
		// then nextLine will take it's place
		if (nextLine.off == nextLine.end) {
		    // Empty line, end of headers
		    break;
		}
		char firstB=(char)buf[nextLine.off];
		if( firstB != ' ' && firstB != '\t' )
		    break; // normal line
		line.end=nextLine.end;
		if(d>0) debug("Continuation: " + firstB  + " " +
			      new String( buf, line.off, line.end-line.off));
		// continue reading the next line
	    }
	    
	    // XXX this does not currently handle headers which
	    // are folded to take more than one line.
	    if( ! parseHeaderField(headers, buf, line.off, line.end - line.off) ) {
		// error parsing header
		return 200;
	    }
	}
	return 200;
    }

    /**
     * Parses a header field from a subarray of bytes.
     * @param b the bytes to parse
     * @param off the start offset of the bytes
     * @param len the length of the bytes
     * @exception IllegalArgumentException if the header format was invalid
     */
    public final boolean parseHeaderField(MimeHeaders headers, byte[] b,
					  int off, int len)
    {
	int start = off;
	byte c;

	while ((c = b[off++]) != ':' && c != ' ') {
	    if (c == '\n') {
		loghelper.log("Parse error, empty line: " +
			      new String( b, off, len ), Log.ERROR);
		return false;
	    }
	}

	int nS=start;
	int nE=off - start - 1;

	while (c == ' ') {
	    c = b[off++];
	}

	if (c != ':') {
	    loghelper.log("Parse error, missing : in  " +
			  new String( b, off, len ), Log.ERROR);
	    loghelper.log("Full  " + new String( b, 0, b.length ),
			  Log.ERROR);
	    return false;
	}

	while ((c = b[off++]) == ' ');

	headers.addValue( b, nS, nE).
	    setBytes(b, off-1, len - (off - start - 1));
	return true;
    }

    /** Parse a request line
     */
    public final int processRequestLine(MessageBytes methodMB,
					MessageBytes uriMB,
					MessageBytes queryMB,
					MessageBytes protocolMB)
	throws IOException
    {
	count = readLine(sin,buf, 0, buf.length);

	if (count < 0 ) {
	    return 400;
	}
	
	off=0;

	// if end of line is reached before we scan all 3 components -
	// we're fine, off=count and remain unchanged
	if( buf[count-1]!= '\r' && buf[count-1]!= '\n' ) {
	    return 414; //HttpServletResponse.SC_REQUEST_URI_TOO_LONG;
	}	    
	
	int startMethod=skipSpaces();
	int endMethod=findSpace();

	int startReq=skipSpaces();
	int endReq=findSpace();

	int startProto=skipSpaces();
	int endProto=findSpace();

	if( startReq < 0   ) {
	    // we don't have 2 "words", probably only method
	    // startReq>0 => method is fine, request has at least one char
	    return 400;
	}

	methodMB.setBytes( buf, startMethod, endMethod - startMethod );
	// optimization - detect common strings, no allocations
	// buf[startMethod] == 'g' ||, ignoreCase

	// the idea is that we don't allocate new strings - but set
	// to constants. ( probably not needed, it's has a tiny impact )
	if( buf[startMethod] == 'G') {
	    if( methodMB.equals( "GET" ))
		methodMB.setString("GET");
	}
	if( buf[startMethod] == 'P' ) {
	    if( methodMB.equals( "POST" ))
		methodMB.setString("POST");
	    if( methodMB.equals( "PUT" ))
		methodMB.setString("PUT");
	}

	if( endReq < 0 ) {
	    endReq=count;
	} else {
	    if( startProto > 0 ) {
		if( endProto < 0 ) endProto = count;
		protocolMB.setBytes( buf, startProto, endProto-startProto);
		if( protocolMB.equalsIgnoreCase( "http/1.0" ))
		    protocolMB.setString("HTTP/1.0");
		if( protocolMB.equalsIgnoreCase( "http/1.1" ))
		    protocolMB.setString("HTTP/1.1");
	    } else {
		protocolMB.setString("");
	    }
	}

	int qryIdx= findChar( '?', startReq, endReq );
	if( qryIdx <0 ) {
	    uriMB.setBytes(buf, startReq, endReq - startReq );
	    //= new String( buf, startReq, endReq - startReq );
	} else {
	    uriMB.setBytes( buf, startReq, qryIdx - startReq );
	    queryMB.setBytes( buf, qryIdx+1, endReq - qryIdx -1 );
	}

	// Perform URL decoding only if necessary
	// 	if ((uriMB.indexOf('%') >= 0) || (uriMB.indexOf('+') >= 0)) {
	// 	    try {
	// 		uriMB.unescapeURL();
	// 	    } catch (Exception e) {
	// 		return 400;
	// 	    }
	// 	}

	// XXX what about query strings ?
	
	return 200;
    }

    // -------------------- Output methods --------------------

    /** Format and send the output headers
     */
    public void sendHeaders(MimeHeaders headers)  throws IOException 
    {
	int count=headers.size();
	for( int i=0; i 0 ? count : -1;
    }

    // From BufferedServletOutputStream
    // XXX will be moved in a new in/out system, temp. code
    // Right now it's not worse than BOS
    public void printHead( String s ) {
	if (s==null) s="null";

	int len = s.length();
	for (int i = 0; i < len; i++) {
	    char c = s.charAt (i);
	    
	    //
	    // XXX NOTE:  This is clearly incorrect for many strings,
	    // but is the only consistent approach within the current
	    // servlet framework.  It must suffice until servlet output
	    // streams properly encode their output.
	    //
	    if ((c & 0xff00) != 0) {	// high order byte must be zero
		// XXX will go away after we change the I/O system
		loghelper.log("Header character is not iso8859_1, " +
			      "not supported yet: " + c, Log.ERROR ) ;
	    }
	    if( oBufferCount >= oBuffer.length ) {
		byte bufferNew[]=new byte[ oBuffer.length * 2 ];
		System.arraycopy( oBuffer,0, bufferNew, 0, oBuffer.length );
		oBuffer=bufferNew;
	    }
	    oBuffer[oBufferCount] = (byte)c;
	    oBufferCount++;
	}
    }    

    private static final int d=0;
    private static void debug(String s ) {
	System.out.println("Http10: " +s );
    }

}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.