⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 client.java

📁 很棒的web服务器源代码
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
// Client.java// $Id: Client.java,v 1.77 2003/02/25 16:53:53 ylafon Exp $// (c) COPYRIGHT MIT and INRIA, 1996.// Please first read the full copyright statement in file COPYRIGHT.htmlpackage org.w3c.jigsaw.http;import java.io.DataOutputStream;import java.io.InputStream;import java.io.IOException;import java.io.PipedInputStream;import java.net.InetAddress;import org.w3c.www.http.HTTP;import org.w3c.www.http.HttpParserException;import org.w3c.www.mime.MimeParser;import org.w3c.www.mime.MimeParserException;import org.w3c.www.mime.MimeParserFactory;import org.w3c.tools.resources.ProtocolException;import org.w3c.tools.resources.ResourceException;import org.w3c.tools.timers.EventHandler;import org.w3c.jigsaw.servlet.JigsawHttpServletResponse;import org.w3c.jigsaw.servlet.ServletWrapper;/** * The request timeout event, to be delivered by the timer package. * Handling timers is expensive, and can be a bottleneck, this is the only * event Jigsaw will set during processing a request. * No other timeing events are used (ie closing a persistent connection is * not triggered by some timing, but rather by the load of the server). */class RequestTimeout {    Client client = null;    RequestTimeout (Client client) {	this.client = client;    }}/** * Client instances keep track of a specific connection with a browser. * This abstract class is responsible for handling an HTTP connection, as * described by an input and output stream, from right after the connection * is accepted, until the connection has to be shutdown. It provides * all the methods to run the HTTP dialog, and leave it to subclasses to * implement the accept connection and the persistent connections strategy. * <p>Customizing this class is done by subclassing it, and implementing * the abstract methods. * <p>For sample implementations, you can check the socket and mux sub * packages. * @see ClientFactory * @see org.w3c.jigsaw.http.socket.SocketClient * @see org.w3c.jigsaw.http.mux.MuxClient */public abstract class Client implements EventHandler {     private static final boolean debuglog = false;    private final static byte hexaTable[] = { 	(byte) '0', (byte) '1', (byte) '2', (byte) '3',	(byte) '4', (byte) '5', (byte) '6', (byte) '7',	 	(byte) '8', (byte) '9', (byte) 'A', (byte) 'B',	(byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F' };    /**     * The continue reply, if ever needed is created only once.     */    private Reply contreply = null;    /**     * The Mime factory instance we use to create requests.     */    private MimeParserFactory factory     = null;    /**     * The uniq integer identifier for that client.     */    protected int identifier = -1;    /**     * The server context responsible for that client.     */    protected httpd server = null;    /**     * Is this client in debug mode ?     */    protected boolean debug = false;    /**     * The buffer used to emit copy data back to the client.     */    protected byte buffer[] = null ;     /**     * Is this client currently <em>running</em>.     * A client starts <em>running</em> when its <code>startConnection</code>     * method is invoked, with the HTTP transport streams. It stops running     * either because of an interruption, triggered by a call to      * <code>interruptConnection</code> or by the <code>idleConnection</code>,     * returning <strong>true</strong>, or because the connection has to be      * closed.     * <p>In all these cases, the <code>stopConnection</code> method is      * invoked.     */    private boolean running = false;    /**     * When runnin, the HTTP major version used to discuss with that client.     */    private short major = -1;     /**     * WHen running, the HTTP minor version used to discuss with that client.     */    private short minor = -1;     /**     * When running, the input stream from which this client gets HTTP.     */    private InputStream input  = null ;    /**     * When running, the output stream to which this client emits HTTP.     */    private DataOutputStream output = null ;    /**     * When processing a request, the handle to the pending timer.     */    private Object timer = null;    /**     * When running, the Mime parser instance to parse current input stream.     */    private MimeParser parser      = null;    /**     * When running, the interrupt flag.     */    private boolean interrupted = false;    /**     * Number of requests handled within this client context.     */    protected int reqcount = 0;    /**      * flags to avoid multiple 100-Continue during multiple stages of a       * request       */    protected boolean cont = false;    /**     * The number of bytes in the body of the previously handled request     */    protected long prev_body_count = 0;    /**     * HTTP lenient mode?     */    private boolean lenient = true;    /**     * Sets this client next timer.      * Timers are used only to limit the duration of a request processing.     * Only one timer can be pending at any time for a given client,      * setting some new timer if one is already pending will kill     * the previous one.     * @param ms The number of milliseconds after which the timer should     *    expire.     * @param data The call data for the tevent timer handler.     */    private synchronized void setTimeout (int ms, Object data) {	if ( timer != null ) {	    server.timer.recallTimer (timer) ;	    timer = null ;	}	timer = server.timer.registerTimer (ms, this, data) ;    }    /**     * Remove any pending timer.     */	    private synchronized void removeTimeout() {	if ( timer != null )	    server.timer.recallTimer (timer) ;    }    /**     * Handle timer events.      * For the time being, timer events are only used     * to detect an overrunning request, so this handler just kills the      * correponding client.     * @param data The timer closure.     * @param time The absolute time at which the event was triggered.     * @see org.w3c.tools.timers.EventManager     * @see org.w3c.tools.timers.EventHandler     */    public synchronized void handleTimerEvent (Object data, long time) {	timer = null ;	// This request has taken to long to fullfill, abort it	Reply abort = new Reply (this) ;	abort.setStatus (HTTP.REQUEST_TIMEOUT) ;	interruptConnection(true);    }    /**     * Terminate the connection that is currently handled.     * This method closes the currently handled input and output stream,      * removes any pending timers and cleanup the client state, so it is ready     * to handle any new connection.     * If the client is bound to a thread, the thread is throw an exception at     */    private synchronized void terminate() {	if ( ! running )	    return ;	removeTimeout();	try {	    if ( output != null ) {		output.flush();		output.close();	    }	} catch (IOException ex) {	}	try {	    if ( input != null )		input.close();	} catch (IOException ex) {	}	input       = null;	output      = null;	parser      = null;	major       = -1;	minor       = -1;	interrupted = false;	running     = false;    }    /**     * Request has been processed into Reply, should we keep connection alive ?     * Test wether we can keep the connection alive, after the given     * reply has been emited.      * @param request The request to examine.     * @param reply Its computed reply.     */    protected boolean tryKeepConnection (Request request, Reply reply) {	// The server doesn't want any keep connection	if ( ! server.getClientKeepConnection() )	    return false ;	if (!request.canKeepConnection()) {	    if (reply.tryKeepConnection()) {		reply.addConnection("close");	    }	    return false;	}	return reply.tryKeepConnection();    }    /**     * Read the next request from our current input stream.     * @return a Request instance, or <strong>null</strong> if interrupted.     * @exception IOException If some IO error occured.     * @exception ClientException If either an IO error happened or bad     * HTTP was received. In both cases the connection needs to be closed.     */    protected Request getNextRequest () 	throws ClientException, IOException    {	Request request = null ;	cont = false; // reinit the continue	try {	    request = (Request) parser.parse(lenient);	} catch (IOException ex) {	    // The connection has probably been closed prematurely:	    return null;	} catch (HttpParserException ex) {	    if ( debug ) {		System.out.println("+++ "+this+" got exception:");		ex.printStackTrace();	    }	    throw new ClientException(this, ex);	} catch (MimeParserException ex) {	    if ( debug ) {		System.out.println("+++ "+this+" got exception:");		ex.printStackTrace();	    }	    throw new ClientException (this, ex);	}	if ( debug )	    request.dump(System.out) ;	return request ;    }    /**     * Run chunk encoding on the provided stream to emit reply's body.     * @param is The reply's body that has to be chunk encoded.     * @exception IOException If IO error occurs.     */    protected int chunkTransfer(InputStream is, Reply reply)	throws IOException    {	byte   zeroChunk[] = { ((byte) 48), ((byte) 13), ((byte) 10) };//0\r\n	byte   crlf[]      = { ((byte) 13), ((byte) 10) } ; 	byte   bheader[]   = new byte[32] ;	int    blen        = 0 ;	int    written     = 0 ;	int    got         = 0 ;	int    sgot;	String header      = null ;	try {	    // Emit the reply stream:	    while (got >= 0) {		if (got == 0) {		    try {			got = is.read(buffer);		    } catch (IOException ioex) {			if (reply.hasState(ServletWrapper.RUNNER) &&			    (is instanceof PipedInputStream) ) {			    // here, the problem may be that multiple			    // threads are writing to the PipedOutputStream			    // and the IOError may exists			    if (!reply.hasState(ServletWrapper.ENDED)) {				got = 0;				continue;			    }			}			throw ioex;		    }		    continue;		}		// Emit a full chunk: header first, followed by the body		// we dump the hexa size of the header backward		sgot = got;		blen = 3;		bheader[30] = ((byte) 13); // \r		bheader[31] = ((byte) 10); // \n		while (sgot > 15) {		    bheader[32-blen] = hexaTable[sgot % 16];		    sgot >>= 4;		    blen++;		}		bheader[32-blen] = hexaTable[sgot];		output.write(bheader, 32-blen, blen) ;		output.write(buffer, 0, got) ;		output.write(crlf, 0, 2) ;		output.flush() ;		written += (blen+got) ;		try {		    got = is.read(buffer);		} catch (IOException ioex) {		    if (reply.hasState(ServletWrapper.RUNNER) &&			(is instanceof PipedInputStream) ) {			// here, the problem may be that multiple			// threads are writing to the PipedOutputStream			// and the IOError may exists			if (!reply.hasState(ServletWrapper.ENDED)) {			    got = 0;			    continue;			}		    }		    throw ioex;		}	    }	} catch (IOException ex) {	    // To cope with Java's exec bug 	    // Anyway, if this really fails, the output will fail below too	    if (debug) {		ex.printStackTrace();	    }	}	// Emit the 0 chunk:	output.write(zeroChunk, 0, 3) ;	// FIXME trailers should be sent here	output.write(crlf, 0, 2) ;	output.flush() ;	return written + blen ;    }    /**     * Emit the given reply to the client.     * @param reply The reply to be emited.     * @return The number of body bytes emited, or <strong>-1</strong> if     * no bytes needed to be emitted.     * @exception IOException If some IO error occurs.     */    protected int emitReply (Reply reply) 	throws IOException    {	boolean chunkable = false ;	// Emit the reply if needed:	if ( reply.getStatus() == HTTP.DONE ) 	    return -1;	InputStream is = reply.openStream() ;	if ( is == null ) {	    if ( debug )		reply.dump(System.out);	    reply.emit(output);	    return -1;	} else {	    chunkable = reply.canChunkTransfer() ;	    if ( debug )		reply.dump(System.out);	    if ( reply.getStatus() != HTTP.NOHEADER )		reply.emit(output) ;	}	// No shuffler available, perform the job ourselves.	if ( buffer == null )	    buffer = new byte[getServer().getClientBufferSize()] ;	// Check if we can chunk the reply back:	int written = 0 ;	try {	    if ( chunkable ) {		written = chunkTransfer(is, reply) ;	    } else {		int got = 0 ;		while (got >= 0) {		    output.write (buffer, 0, got) ;		    written += got ;		    try {			got = is.read(buffer, 0, buffer.length);		    } catch (IOException ioex) {			if (reply.hasState(ServletWrapper.RUNNER) &&			    (is instanceof PipedInputStream) ) {			    // here, the problem may be that multiple			    // threads are writing to the PipedOutputStream			    // and the IOError may exists			    if (!reply.hasState(ServletWrapper.ENDED)) {				got = 0;				continue;			    }			}			throw ioex;		    }		}	    }	} finally {	    is.close() ;	}	return written ;    }    /**     * Process a request.     * This methods processs the request to the point that a reply is      * available. This methods sets a timeout, to limit the duration of this      * request processing.      * @param request The request to process.     * @exception ClientException If either the timeout expires or the entity     *     was unable to handle the request.     */    protected Reply processRequest (Request request) 	throws ClientException    {	Reply        reply    = null ;	setTimeout(server.getRequestTimeOut(), new RequestTimeout(this)) ;	try {	    reply = (Reply)server.perform(request);	} catch (ProtocolException ex) {	    if ( debug ) { 		System.out.println("+++ "+this+" got exception:");		ex.printStackTrace();	    }	    if ((reply != null) && reply.hasStream()) {		try {		    reply.openStream().close();		} catch (Exception cex) {}	    }	    if ( ex.hasReply () ) {		return (Reply) ex.getReply() ;	    } else {		throw new ClientException (this, ex) ;	    }	} catch (ResourceException ex2) {	    throw new ClientException(this, ex2);	}	if ( reply == null ) {	    String errmsg = "target resource emited a null Reply.";	    throw new ClientException (this, errmsg);	}	reqcount++;	return reply ;    }    /**     * Start processing the given connection.     * This is the entry point for sub classes, in order to make the client     * start processing the HTTP protocol on the given input and output     * streams.     * <p>Before this method returns, both provided streams are <em>always     * </em> closed, and the <code>stopConnection</code> method invoked.     * @param in The input stream to receive HTTP requests.     * @param out The output stream to send HTTP replies.     * @return A boolean <strong>true</strong> if this method returns because     * of an interruption, <strong>false</strong> otherwsie (ie the connection     * was gracefully shutdown).     * @exception ClientException If some severe error has occured and the     * current connection needs to be terminated.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -