You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@locus.apache.org on 2000/05/24 06:41:59 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/util MimeHeaderField.java MimeHeaders.java

costin      00/05/23 21:41:59

  Modified:    src/etc  test-tomcat.xml
               src/share/org/apache/tomcat/core RequestImpl.java
               src/share/org/apache/tomcat/request AccessInterceptor.java
               src/share/org/apache/tomcat/service/http
                        HttpRequestAdapter.java
               src/share/org/apache/tomcat/util MimeHeaderField.java
                        MimeHeaders.java
  Removed:     src/share/org/apache/tomcat/service/http Constants.java
  Log:
  Moved the HTTP-specific code from MimeHeaders and MHField to Http adapter. This also
  allow better buffer usage, and is a better code organization ( MimeHeaders are used
  by all adapters, shouldn't be http specific ).
  
  I also made a big change in Http adapter - optimized it and cleaned up request
  parsing. ( well, I still think you shouldn't use tomcat in standalone mode
  for production sites, only for development or where performance doesn't matter, but
  that doesn't mean it have to be slow !). The new parser generates only 4 strings,
  and uses only one byte[] buffer per thread for request processing.
  
  It seems all tomcat-tests are passing, I'll run watchdog after I finish all other changes.
  
  Small changes:
  
  - fixed test case ( Max-Age instead of bad MaxAge )
  - fixed Request - get facade from default context if error happens and no Context is set.
  It's a workaround, will go away when we finish decoupling Request/Facades.
  
  Revision  Changes    Path
  1.20      +1 -1      jakarta-tomcat/src/etc/test-tomcat.xml
  
  Index: test-tomcat.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/etc/test-tomcat.xml,v
  retrieving revision 1.19
  retrieving revision 1.20
  diff -u -r1.19 -r1.20
  --- test-tomcat.xml	2000/05/24 01:58:10	1.19
  +++ test-tomcat.xml	2000/05/24 04:41:49	1.20
  @@ -427,7 +427,7 @@
   
      <target name="get">
         <gtest host="${host}" port="${port}"  request="GET /test/servlet/Cookie25 HTTP/1.0"
  -             expectHeaders="Set-Cookie2:foo=bar;Version=1;MaxAge=8640000"
  +             expectHeaders="Set-Cookie2:foo=bar;Version=1;Max-Age=8640000"
         />
   
         <gtest host="${host}" port="${port}"  request="GET /test/servlet/ResponseError HTTP/1.0"
  
  
  
  1.40      +8 -1      jakarta-tomcat/src/share/org/apache/tomcat/core/RequestImpl.java
  
  Index: RequestImpl.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/core/RequestImpl.java,v
  retrieving revision 1.39
  retrieving revision 1.40
  diff -u -r1.39 -r1.40
  --- RequestImpl.java	2000/05/24 01:58:14	1.39
  +++ RequestImpl.java	2000/05/24 04:41:50	1.40
  @@ -346,8 +346,15 @@
       public HttpServletRequest getFacade() {
   	// some requests are internal, and will never need a
   	// facade - no need to create a new object unless needed.
  -        if( requestFacade==null )
  +        if( requestFacade==null ) {
  +	    if( context==null ) {
  +		// wrong request
  +		// XXX the will go away after we remove the one-one relation between
  +		// request and facades ( security, etc) 
  +		requestFacade = contextM.getContext("/" ).getFacadeManager().createHttpServletRequestFacade(this );
  +	    }
   	    requestFacade = context.getFacadeManager().createHttpServletRequestFacade(this);
  +	}
   	return requestFacade;
       }
   
  
  
  
  1.4       +1 -1      jakarta-tomcat/src/share/org/apache/tomcat/request/AccessInterceptor.java
  
  Index: AccessInterceptor.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/request/AccessInterceptor.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- AccessInterceptor.java	2000/05/09 17:56:33	1.3
  +++ AccessInterceptor.java	2000/05/24 04:41:55	1.4
  @@ -183,7 +183,7 @@
   	String path=reqURI.substring( ctxPath.length());
   	String method=req.getMethod();
   
  -	log( "ACCESS: checking " + path );
  +	if( ctx.getDebug() > 0 ) log( "ACCESS: checking " + path );
   	
   	for( int i=0; i< ctxSec.patterns ; i++ ) {
   	    Container ct=ctxSec.securityPatterns[i];
  
  
  
  1.12      +188 -93   jakarta-tomcat/src/share/org/apache/tomcat/service/http/HttpRequestAdapter.java
  
  Index: HttpRequestAdapter.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/service/http/HttpRequestAdapter.java,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- HttpRequestAdapter.java	2000/04/25 17:54:26	1.11
  +++ HttpRequestAdapter.java	2000/05/24 04:41:55	1.12
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/service/http/HttpRequestAdapter.java,v 1.11 2000/04/25 17:54:26 costin Exp $
  - * $Revision: 1.11 $
  - * $Date: 2000/04/25 17:54:26 $
  + * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/service/http/HttpRequestAdapter.java,v 1.12 2000/05/24 04:41:55 costin Exp $
  + * $Revision: 1.12 $
  + * $Date: 2000/05/24 04:41:55 $
    *
    * ====================================================================
    *
  @@ -77,10 +77,15 @@
       private boolean moreRequests = false;
       InputStream sin;
       byte[] buf;
  +    int bufSize=2048; // default
  +    int off=0;
  +    int count=0;
  +    public static final String DEFAULT_CHARACTER_ENCODING = "8859_1";
  +
       
       public HttpRequestAdapter() {
           super();
  -	buf=new byte[Constants.RequestBufferSize];
  +	buf=new byte[bufSize];
       }
   
       public void setSocket(Socket socket) throws IOException {
  @@ -92,6 +97,8 @@
   
       public void recycle() {
   	super.recycle();
  +	off=0;
  +	count=0;
       }
       
       public Socket getSocket() {
  @@ -111,36 +118,126 @@
       }
   
       public void readNextRequest(Response response) throws IOException {
  -	int count = in.readLine(buf, 0, buf.length);
  +	count = in.readLine(buf, 0, buf.length);
  +
   	if (count < 0 ) {
   	    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
   	    return;
   	}
   	
  -	processRequestLine(response, buf, 0, count );
  -
  -	// XXX
  -	//    return if an error was detected in processing the
  -	//    request line
  -
  -	// read headers if at least we have a protocol >=1.0, or the
  -	// error will be reported to early
  -	//         if (response.getStatus() >=
  -	// 	    HttpServletResponse.SC_BAD_REQUEST) {
  -	//             return;
  -	// 	}
  +	processRequestLine(response  );
   
   	// for 0.9, we don't have headers!
   	if ((protocol!=null) &&
  -            !protocol.toLowerCase().startsWith("http/0."))
  -	    headers.read(in);
  +            !protocol.toLowerCase().startsWith("http/0.")) {
  +	    readHeaders( headers, in  );
  +	}
   
   	// XXX
   	// detect for real whether or not we have more requests
   	// coming
   	moreRequests = false;	
  -    }    
  -    
  +    }
  +
  +
  +    /**
  +     * 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 void readHeaders( MimeHeaders headers, ServletInputStream in )  throws IOException {
  +	// use pre-allocated buffer if possible
  +	off = count; // where the request line ended
  +	
  +	while (true) {
  +	    int start = off;
  +
  +	    while (true) {
  +		int len = buf.length - off;
  +
  +		if (len > 0) {
  +		    len = in.readLine(buf, off, len);
  +
  +		    if (len == -1) {
  +                        String msg =
  +                            sm.getString("mimeHeader.connection.ioe");
  +
  +			throw new IOException (msg);
  +		    }
  +		}
  +
  +		off += len;
  +
  +		if (len == 0 || buf[off-1] == '\n') {
  +		    break;
  +		}
  +
  +		// 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;
  +	    }
  +
  +	    // strip off trailing "\r\n"
  +	    if (--off > start && buf[off-1] == '\r') {
  +		--off;
  +	    }
  +
  +	    if (off == start) {
  +		break;
  +	    }
  +	    
  +	    // XXX this does not currently handle headers which
  +	    // are folded to take more than one line.
  +	    MimeHeaderField mhf=headers.putHeader();
  +	    if( ! parseHeaderFiled(mhf, buf, start, off - start) ) {
  +		// error parsing header
  +		return;
  +	    }
  +	}
  +    }
  +
  +    /**
  +     * 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 boolean parseHeaderFiled(MimeHeaderField mhf, byte[] b, int off, int len)
  +    {
  +	int start = off;
  +	byte c;
  +
  +	while ((c = b[off++]) != ':' && c != ' ') {
  +	    if (c == '\n') {
  +		System.out.println("Parse error, empty line: " + new String( b, off, len ));
  +		return false;
  +	    }
  +	}
  +
  +	mhf.setName(b, start, off - start - 1);
  +
  +	while (c == ' ') {
  +	    c = b[off++];
  +	}
  +
  +	if (c != ':') {
  +	    System.out.println("Parse error, missing : in  " + new String( b, off, len ));
  +	    return false;
  +	}
  +
  +	while ((c = b[off++]) == ' ');
  +
  +	mhf.setValue(b, off - 1, len - (off - start - 1));
  +	return true;
  +    }
  +
       public int getServerPort() {
           return socket.getLocalPort();
       }
  @@ -176,98 +273,96 @@
       public String getRemoteHost() {
   	return socket.getInetAddress().getHostName();
       }    
  -    
  -    public void processRequestLine(Response response, byte buf[], int start, int count)
  -	throws IOException
  -    {
  -
  -	String line=new String(buf, 0, count, Constants.CharacterEncoding.Default);
  -        String buffer = line.trim();
   
  -	int firstDelim = buffer.indexOf(' ');
  -	int lastDelim = buffer.lastIndexOf(' ');
  -	// default - set it to HTTP/0.9 or null if we can parse the request
  -	//protocol = "HTTP/1.0";
  -
  -	if (firstDelim == -1 && lastDelim == -1) {
  -	    if (buffer.trim().length() > 0) {
  -	        firstDelim = buffer.trim().length();
  -		lastDelim = buffer.trim().length();
  +    /** Advance to first non-space
  +     */
  +    private  final int skipSpaces() {
  +	while (off < count) {
  +	    if ((buf[off] != (byte) ' ') 
  +		&& (buf[off] != (byte) '\t')) {
  +		return off;
   	    }
  +	    off++;
   	}
  -
  -	if (firstDelim != lastDelim) {
  -	    String s = buffer.substring(firstDelim, lastDelim);
  +	return -1;
  +    }
   
  -	    if (s.trim().length() == 0) {
  -	        firstDelim = lastDelim;
  +    /** Advance to the first space
  +     */
  +    private  int findSpace() {
  +	while (off < count) {
  +	    if ((buf[off] == (byte) ' ') 
  +		|| (buf[off] == (byte) '\t')) {
  +		return off;
   	    }
  +	    off++;
   	}
  +	return -1;
  +    }
   
  -	String requestString=null;
  -	
  -	if (firstDelim != lastDelim) {
  -	    method = buffer.substring(0, firstDelim).trim();
  -	    protocol = buffer.substring(lastDelim + 1).trim();
  -	    requestString = buffer.substring(firstDelim + 1, lastDelim).trim();
  -	} else if (firstDelim != -1 && lastDelim != -1) {
  -	    method = buffer.substring(0, firstDelim).trim();
  -	    protocol = null;
  -	    if (lastDelim < buffer.length()) {
  -	        requestString = buffer.substring(lastDelim + 1).trim();
  +    /** Find a character, no side effects
  +     */
  +    private  int findChar( char c, int start, int end ) {
  +	byte b=(byte)c;
  +	int offset = start;
  +	while (offset < end) {
  +	    if (buf[offset] == b) {
  +		return offset;
   	    }
  -	}
  -
  -	if (protocol != null &&
  -	    ! protocol.toLowerCase().startsWith("http/")) {
  -	    requestString += " " + protocol;
  -	    protocol = null;
  +	    offset++;
   	}
  +	return -1;
  +    }
   
  -        int requestErrorCode = 0; 
  +    public void processRequestLine(Response response)
  +	throws IOException
  +    {
  +	off=0;
   
  -	// see if request looks right
  +	// 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' ) {
  +	    response.setStatus(HttpServletResponse.SC_REQUEST_URI_TOO_LONG);
  +	    return;
  +	}	    
  +	
  +	int startMethod=skipSpaces();
  +	int endMethod=findSpace();
   
  -	try {
  -	    int len = line.length();
  +	int startReq=skipSpaces();
  +	int endReq=findSpace();
   
  -	    if (len < 2) {
  -	        requestErrorCode = HttpServletResponse.SC_BAD_REQUEST;
  -	    } else if (/* line.charAt(len - 2) != '\r' || Correct, but will break C clients */
  -                line.charAt(len - 1) != '\n') {
  -	        requestErrorCode =
  -		    HttpServletResponse.SC_REQUEST_URI_TOO_LONG;
  -		// XXX
  -		// For simplicity we assume there's an HTTP/1.0 on the end
  -		// We should check to be sure.
  -		protocol = "HTTP/1.0";
  -	    }
  -	} catch (StringIndexOutOfBoundsException siobe) {
  -	}
  +	int startProto=skipSpaces();
  +	int endProto=findSpace();
   
  -	// see if uri is well formed
  -
  -	String msg="";
  -	if (requestErrorCode == 0 &&
  -	    (requestString == null || requestString.indexOf(' ') > -1 ||
  -	        requestString.indexOf('/') != 0)) {
  -	    requestErrorCode = HttpServletResponse.SC_BAD_REQUEST;
  -	    msg="Bad request: " + requestString + " " + requestErrorCode;
  +	if( startReq < 0   ) {
  +	    // we don't have 2 "words", probably only method
  +	    // startReq>0 => method is fine, request has at least one char
  +	    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
  +	    return;
   	}
  +	
  +	method= new String( buf, startMethod, endMethod - startMethod );
   
  -	if (requestErrorCode != 0) {
  -	    response.setStatus(requestErrorCode);
  -	    return;
  +	if( endReq < 0 ) {
  +	    protocol=null;
  +	    endReq=count;
  +	} else {
  +	    if( endProto < 0 ) endProto = count;
  +	    protocol=new String( buf, startProto, endProto-startProto );
   	}
   
  -	int indexQ=requestString.indexOf("?");
  -	int rLen=requestString.length();
  -	if ( (indexQ >-1) && ( indexQ  < rLen) ) {
  -	    queryString = requestString.substring(indexQ + 1, requestString.length());
  -	    requestURI = requestString.substring(0, indexQ);
  +	int qryIdx= findChar( '?', startReq, endReq );
  +	if( qryIdx <0 ) {
  +	    requestURI = new String( buf, startReq, endReq - startReq );
   	} else {
  -	    requestURI= requestString;
  +	    requestURI = new String( buf, startReq, qryIdx - startReq );
  +	    queryString = new String( buf, qryIdx+1, endReq - qryIdx -1 );
   	}
  +
  +	System.out.println("XXX " + method + " " + requestURI + " " + queryString + " " + protocol );
  +
       }
   
       
  
  
  
  1.7       +7 -40     jakarta-tomcat/src/share/org/apache/tomcat/util/MimeHeaderField.java
  
  Index: MimeHeaderField.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/MimeHeaderField.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- MimeHeaderField.java	2000/05/24 01:58:17	1.6
  +++ MimeHeaderField.java	2000/05/24 04:41:56	1.7
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/MimeHeaderField.java,v 1.6 2000/05/24 01:58:17 costin Exp $
  - * $Revision: 1.6 $
  - * $Date: 2000/05/24 01:58:17 $
  + * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/MimeHeaderField.java,v 1.7 2000/05/24 04:41:56 costin Exp $
  + * $Revision: 1.7 $
  + * $Date: 2000/05/24 04:41:56 $
    *
    * ====================================================================
    *
  @@ -134,6 +134,10 @@
   	type = T_NULL;
       }
   
  +    public int getType() {
  +	return type;
  +    }
  +    
       /**
        * Sets the header field name to the specified string.
        * @param s the header field name String
  @@ -342,43 +346,6 @@
   	System.arraycopy(b, 0, buf, off, DATELEN);
   	return DATELEN;
       }    
  -
  -    
  -    /**
  -     * 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 boolean parse(byte[] b, int off, int len)
  -    {
  -	int start = off;
  -	byte c;
  -
  -	while ((c = b[off++]) != ':' && c != ' ') {
  -	    if (c == '\n') {
  -		System.out.println("Parse error, empty line: " + new String( b, off, len ));
  -		return false;
  -	    }
  -	}
  -
  -	setName(b, start, off - start - 1);
  -
  -	while (c == ' ') {
  -	    c = b[off++];
  -	}
  -
  -	if (c != ':') {
  -	    System.out.println("Parse error, missing : in  " + new String( b, off, len ));
  -	    return false;
  -	}
  -
  -	while ((c = b[off++]) == ' ');
  -
  -	setValue(b, off - 1, len - (off - start - 1));
  -	return true;
  -    }
   
       /**
        * Writes this header field to the specified servlet output stream.
  
  
  
  1.7       +3 -72     jakarta-tomcat/src/share/org/apache/tomcat/util/MimeHeaders.java
  
  Index: MimeHeaders.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/MimeHeaders.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- MimeHeaders.java	2000/05/23 20:58:27	1.6
  +++ MimeHeaders.java	2000/05/24 04:41:57	1.7
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/MimeHeaders.java,v 1.6 2000/05/23 20:58:27 costin Exp $
  - * $Revision: 1.6 $
  - * $Date: 2000/05/23 20:58:27 $
  + * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/MimeHeaders.java,v 1.7 2000/05/24 04:41:57 costin Exp $
  + * $Revision: 1.7 $
  + * $Date: 2000/05/24 04:41:57 $
    *
    * ====================================================================
    *
  @@ -494,75 +494,6 @@
       }
   
       
  -    /**
  -     * 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 void read(ServletInputStream in) throws IOException {
  -	// use pre-allocated buffer if possible
  -	byte[] b;
  -
  -	if (count == 0) {
  -	    if( buf==null ) buf=new byte[bufSize];
  -	    b = buf;
  -	} else {
  -	    b = new byte[buf.length];
  -	}
  -
  -	int off = 0;
  -
  -	while (true) {
  -	    int start = off;
  -
  -	    while (true) {
  -		int len = b.length - off;
  -
  -		if (len > 0) {
  -		    len = in.readLine(b, off, len);
  -
  -		    if (len == -1) {
  -                        String msg =
  -                            sm.getString("mimeHeader.connection.ioe");
  -
  -			throw new IOException (msg);
  -		    }
  -		}
  -
  -		off += len;
  -
  -		if (len == 0 || b[off-1] == '\n') {
  -		    break;
  -		}
  -
  -		// overflowed buffer, so temporarily expand and continue
  -		byte[] tmp = new byte[b.length * 2];
  -
  -		System.arraycopy(b, 0, tmp, 0, b.length);
  -		b = tmp;
  -	    }
  -
  -	    // strip off trailing "\r\n"
  -	    if (--off > start && b[off-1] == '\r') {
  -		--off;
  -	    }
  -
  -	    if (off == start) {
  -		break;
  -	    }
  -	    
  -	    // XXX this does not currently handle headers which
  -	    // are folded to take more than one line.
  -	    MimeHeaderField mhf=putHeader();
  -	    if( ! mhf.parse(b, start, off - start) ) {
  -		// error parsing header
  -		return;
  -	    }
  -	}
  -    }
  -
       /**
        * Returns a lengthly string representation of the current header fields.
        */