📄 client.java
字号:
*/ protected boolean startConnection(InputStream in, DataOutputStream out) throws ClientException { boolean keep = true ; long tstart = 0 ; long tend = 0 ; Request request = null ; Reply reply = null ; int sent = 0 ; int processed = 0; ClientException err = null; this.input = in; this.output = out; this.parser = new MimeParser(input, factory); try { running = true; alive_loop: while ( (! interrupted) && keep ) { // Get the next available request, and mark client as used: try { // mark the stream, if some bytes are to be eaten if (prev_body_count > 0) { input.mark(2048); } if ( processed == 0 ) { // Always run for the first request, // update client's infos if ((request = getNextRequest()) == null ) break alive_loop; major = request.getMajorVersion(); minor = request.getMinorVersion(); } else { if ( interrupted = idleConnection() ) break alive_loop; if ((request = getNextRequest()) == null) break alive_loop; // the gateway case, the major/minor _may_ change major = request.getMajorVersion(); minor = request.getMinorVersion(); usedConnection(); } } catch (ClientException cex) { if (cex.ex != null && (cex.ex instanceof HttpParserException)) { HttpParserException hex = (HttpParserException) cex.ex; if (!hex.hasRequest()) { throw (cex); } request = (Request) hex.getRequest(); if ( processed == 0 ) { major = request.getMajorVersion(); minor = request.getMinorVersion(); } usedConnection(); reply = (Reply) request.makeBadRequestReply(); reply.setContentLength(0); reply.addConnection("close"); // Inital keep alive check, emit the reply: keep = false; sent = emitReply(reply) ; // Semi-fake log entry log(request, reply, 0, 0) ; processed++; // a bad request is still a request... reqcount++; break alive_loop; } else if (cex.ex != null && (cex.ex instanceof MimeParserException)) { reply = new Reply(this); reply.setStatus(HTTP.BAD_REQUEST); reply.setContentLength(0); reply.addConnection("close"); keep = false; sent = emitReply(reply) ; // Semi-fake log entry log(request, reply, 0, 0) ; processed++; // a bad request is still a request... reqcount++; break alive_loop; } throw (cex); } catch (Exception genex) { if (debug) genex.printStackTrace(); // some bytes may have to be eaten // abomination, very ugly hack! if (genex.getMessage().startsWith("Bad request") && (prev_body_count > 0)) { // check if the error is due to a usedConnection(); if (debug) System.out.println("Error after a body "+ "request! (Skipping: " + prev_body_count+" bytes)"); // now try to eat some data... input.reset(); byte b[] = new byte[(int)prev_body_count]; prev_body_count -= input.read(b); prev_body_count -= input.skip(prev_body_count); break alive_loop; } throw (genex); } // Some traces if required: if ( debuglog ) System.out.println(this+": request "+request.getURL()); // Process request, and time it: tstart = System.currentTimeMillis() ; reply = processRequest (request) ; tend = System.currentTimeMillis() ; // Inital keep alive check, emit the reply: if ( keep ) keep = tryKeepConnection(request, reply) ; sent = emitReply(reply) ; // Second keep alive check: if (reply.hasContentLength() && (sent >= 0 ) && (reply.getContentLength() != sent)) keep = false; // Log and/or traces: if ( debuglog ) System.out.println(this + ", cl="+reply.getContentLength() + ", size="+sent); // will be -1 if there is no body prev_body_count = request.getContentLength(); log(request, reply, sent, tend-tstart) ; processed++; // If we can keep alive, and if the client doesn't do // pipelining, be clever: // We should compare against 0 here, we use 2 because of a // well-known client bug, that emits an extra CRLF at the end // of forms POSTing // Note we're doing that very last, so that if the socket is // closed by peer, we really have *already* done everything if ( keep /*&& (in.available() <= 2)*/) output.flush() ; // be clever again... in case of protocol switching, // we must free everything and let the other client use the // streams if (reply.getStatus() == HTTP.SWITCHING) { input = null; output = null; throw new Exception ("Switching"); } // hack for servlets if (request.hasState(JigsawHttpServletResponse.STREAM)) { try { PipedInputStream pis; pis = (PipedInputStream) request.getState(JigsawHttpServletResponse.STREAM); pis.close(); } catch (ClassCastException ccex) { // do nothing } catch (IOException pisioex) { // fail silently also } } } } catch (IOException ex) { if ( debug ) { System.out.println("+++ "+this+" got IOException:"); ex.printStackTrace(); } // Close any stream pending try { InputStream i = null; if ( reply != null ) { if ((i = reply.openStream()) != null) i.close(); } } catch (IOException ioex) { } err = new ClientException(this, ex); } catch (ClientException ex) { if ( debug ) { System.out.println("+++ "+this+" got ClientException:"); ex.printStackTrace(); } try { InputStream i = null; if ( reply != null ) { if ((i = reply.openStream()) != null) i.close(); } } catch (IOException ioex) { } err = ex; } catch (Exception ex) { if ( debug ) { System.out.println("+++ "+this+" got exception:"); ex.printStackTrace(); } err = new ClientException(this, ex); } finally { // Absorb incoming data to avoid RST TCP packets: if ((err == null) && (request != null) && (request.getContentLength() > 0) && (reply != null) && (reply.getStatus() != HTTP.SWITCHING) && (reply.getStatus() / 100 != 2)) { // The request may have failed... try { InputStream i = request.getInputStream(); if ( i != null ) { while (i.available() > 0) i.read(buffer, 0, buffer.length); } } catch (Exception ex) { } } if ((request != null) && ( request.hasState(JigsawHttpServletResponse.STREAM))) { try { PipedInputStream pis; pis = (PipedInputStream) request.getState(JigsawHttpServletResponse.STREAM); pis.close(); } catch (ClassCastException ccex) { // do nothing } catch (IOException pisioex) { // fail silently also } catch (Exception ex) { // be sure not to cause any trouble :) } } terminate(); if (reply == null || (reply.getStatus() != HTTP.SWITCHING)) { stopConnection(); if (reply != null) { try { reply.openStream().close(); } catch (Exception roex) {}; } } if ( err != null ) throw err; } return interrupted; } /** * Interrupt the currently handled connection. * This method will make best effort to interrupt any thread currently * processing the connection. * @param now Make sure the thread is interrupted right now if * <strong>true</strong>, otherwise, just schedule an interruption * after the current request (if any) has been processed. */ protected synchronized void interruptConnection(boolean now) { if ( running ) { interrupted = true; if ( now ) terminate(); } } /** * Send a 100 HTTP continue message on the currently handled connection. * This method will take care of creating the appropriate HTTP * continue reply, and will emit that reply only if the spoken HTTP * version allows for it. * @exception IOException If some IO error occured. */ public int sendContinue() throws IOException { if (cont) return -1; if ((major > 1) || ((major == 1) && (minor >= 1))) { if ( contreply == null ) contreply = new Reply(this, null, major, minor, HTTP.CONTINUE); int len = emitReply(contreply); output.flush(); cont = true; return len; } return -1; } /** * Send a 100 HTTP continue message on the currently handled connection. * This method will take care of creating the appropriate HTTP * continue reply, and will emit that reply only if the spoken HTTP * version allows for it. * @exception IOException If some IO error occured. */ public int sendContinue(Reply contReply) throws IOException { if (contReply == null) { return sendContinue(); } if ((major > 1) || ((major == 1) && (minor >= 1))) { int len = emitReply(contReply); output.flush(); cont = true; return len; } return -1; } /** * Get this client identifier. * @return An integer identifying uniquely this client's context. */ public final int getIdentifier() { return identifier; } /** * Is this client currently <em>running</em> for a connection. * @return A boolean, <strong>true</strong> if it is, * <strong>false</strong> otherwise. */ public final synchronized boolean isRunning() { return running; } /** * Get the HTTP major version number spoken on the current connection. * @return The HTTP major version number, or <strong>-1</strong> if that * client is not running. */ public final short getMajorVersion() { return major; } /** * Get the HTTP minor version number spoken on the current connection. * @return The HTTP minor version number, or <strong>-1</strong> if * that client is not running. */ public final short getMinorVersion() { return minor; } /** * Does this client has an interrupt pending ? * @return A boolean, <strong>true</strong> if an interrupt is pending, * <strong>false</strong> otherwise. */ public final boolean isInterrupted() { return interrupted; } /** * Get the total number of requests handled within this client context. * @return An integer giving the number of requests handled. */ public final int getRequestCount() { return reqcount; } /** * Get the server context responsible for this client context. * @return An httpd instance. */ public final httpd getServer() { return server; } /** * Emit an error message on behalf of that client. * @param msg The error message to output. */ public final void error(String msg) { server.errlog(this, msg); } /** * Emit a trace on behalf of the given client. * @param msg The trace to output. */ public final void trace(String msg) { server.trace(this, msg); } /** * Log the given HTTP transaction. * @param request The request that has been processed. * @param reply The generated reply. * @param nbytes Number of content bytes sent along with the reply. * @param duration The processing time for that request in milliseconds. */ public void log(Request request, Reply reply, int nbytes, long duration) { server.log (this, request, reply, nbytes, duration) ; } /** * Get this client input stream. * @return An instance of InputStream, or <strong>null</strong> if the * client is not handling any connection at that time. */ public InputStream getInputStream() { return input; } /** * Get this client output stream. * @return An instance of OutputStream, or <strong>null</strong> if the * client is not handling any connection at that time. */ public DataOutputStream getOutputStream() { return output; } /** * Get the IP address of the host that runs the client described by this * context. * @return An InetAddress instance, or <strong>null</strong> if that client * is not handling any connection at that given time. */ abstract public InetAddress getInetAddress(); /** * Client callback - The client is about to block, getting next request. * This method is triggered by the client instance itself, before * reading next request from the input stream provided at * <code>startConnection</code> time. * @return A boolean, if <strong>true</strong>, the client will consider * itself interrupted, and terminate the connection it is current handling. */ abstract protected boolean idleConnection(); /** * Client callback - A full request has been received on input stream. * This method is called by the client itself, before starting processing * the newly received request. The purpose of this callback is typically * to mark that client <em>buzy</em>. */ abstract protected void usedConnection(); /** * Client callback - The current connection has been terminated. * This client has finished processing the connection provided * at <code>startConnection</code> time, it is now stopped. */ abstract protected void stopConnection(); /** * Get the thread powering that client, if any. * This method is called to kill the client (by interrupting the thread * used to run it). * @return A Thread instance, or <strong>null</strong>. */ abstract protected Thread getThread(); /** * Initialize this client. * It is up to this method to initialize: * <dl> * <dt>parser<dd>The MimeParser to be used to parse incomminf requests. * </dl> * @param server The server responsible for that client. * @param factory The factory that created this client. * @param identifier The uniq identifier for this client. */ protected void initialize(httpd server, int identifier) { this.server = server; this.lenient = server.isLenient(); this.identifier = identifier; this.debug = server.getClientDebug() ; this.factory = server.getMimeClientFactory(this); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -