|
What this is
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
|
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 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.