/* Exception reporting policy method. * @param e the Throwable to report. */ private void exception(Throwable e) { try { _persistent = false; int error_code = HttpResponse.__500_Internal_Server_Error; if (e instanceof HttpException) { error_code = ((HttpException) e).getCode(); if (_request == null) log.warn(e.toString()); else log.warn(_request.getRequestLine() + " " + e.toString()); log.debug(LogSupport.EXCEPTION, e); } else if (e instanceof EOFException) { LogSupport.ignore(log, e); return; } else { _request.setAttribute("javax.servlet.error.exception_type", e.getClass()); _request.setAttribute("javax.servlet.error.exception", e); if (_request == null) log.warn(LogSupport.EXCEPTION, e); else log.warn(_request.getRequestLine(), e); } if (_response != null && !_response.isCommitted()) { _response.reset(); _response.removeField(HttpFields.__TransferEncoding); _response.setField(HttpFields.__Connection, HttpFields.__Close); _response.sendError(error_code); } } catch (Exception ex) { LogSupport.ignore(log, ex); } }
/** * Handle next request off the connection. The service(request,response) method is called by * handle to service each request received on the connection. If the thread is a PoolThread, the * thread is set as inactive when waiting for a request. * * <p>If a HttpTunnel has been set on this connection, it's handle method is called and when that * completes, false is return from this method. * * <p>The Connection is set as a ThreadLocal of the calling thread and is available via the * getHttpConnection() method. * * @return true if the connection is still open and may provide more requests. */ public boolean handleNext() { // Handle a HTTP tunnel if (_tunnel != null) { if (log.isDebugEnabled()) log.debug("Tunnel: " + _tunnel); _outputStream.resetObservers(); _tunnel.handle(_inputStream.getInputStream(), _outputStream.getOutputStream()); return false; } // Normal handling. HttpContext context = null; boolean stats = false; try { // Assume the connection is not persistent, // unless told otherwise. _persistent = false; _close = false; _keepAlive = false; _firstWrite = false; _completing = false; _dotVersion = 0; // Read requests readRequest(); if (_listener == null || !_listener.isStarted()) { // dead connection _response.destroy(); _response = null; _persistent = false; return false; } _listener.customizeRequest(this, _request); if (_request.getState() != HttpMessage.__MSG_RECEIVED) throw new HttpException(HttpResponse.__400_Bad_Request); // We have a valid request! statsRequestStart(); stats = true; // Pick response version, we assume that _request.getVersion() == 1 _dotVersion = _request.getDotVersion(); if (_dotVersion > 1) { _dotVersion = 1; } // Common fields on the response _response.setVersion(HttpMessage.__HTTP_1_1); _response.setField(HttpFields.__Date, _request.getTimeStampStr()); if (!Version.isParanoid()) _response.setField(HttpFields.__Server, Version.getDetail()); // Handle Connection header field Enumeration connectionValues = _request.getFieldValues(HttpFields.__Connection, HttpFields.__separators); if (connectionValues != null) { while (connectionValues.hasMoreElements()) { String token = connectionValues.nextElement().toString(); // handle close token if (token.equalsIgnoreCase(HttpFields.__Close)) { _close = true; _response.setField(HttpFields.__Connection, HttpFields.__Close); } else if (token.equalsIgnoreCase(HttpFields.__KeepAlive) && _dotVersion == 0) _keepAlive = true; // Remove headers for HTTP/1.0 requests if (_dotVersion == 0) _request.forceRemoveField(token); } } // Handle version specifics if (_dotVersion == 1) verifyHTTP_1_1(); else if (_dotVersion == 0) verifyHTTP_1_0(); else if (_dotVersion != -1) throw new HttpException(HttpResponse.__505_HTTP_Version_Not_Supported); if (log.isDebugEnabled()) log.debug("REQUEST from " + _listener + ":\n" + _request); // handle HttpListener handlers if (!_request.isHandled() && _listener.getHttpHandler() != null) _listener.getHttpHandler().handle("", null, _request, _response); // service the request if (!_request.isHandled()) context = service(_request, _response); } catch (HttpException e) { exception(e); } catch (IOException e) { if (_request.getState() != HttpMessage.__MSG_RECEIVED) { if (log.isDebugEnabled()) { if (log.isTraceEnabled()) log.trace(LogSupport.EXCEPTION, e); else if (log.isDebugEnabled()) log.debug(e.toString()); } _response.destroy(); _response = null; } else exception(e); } catch (Exception e) { exception(e); } catch (Error e) { exception(e); } finally { int bytes_written = 0; int content_length = _response == null ? -1 : _response.getIntField(HttpFields.__ContentLength); // Complete the request if (_persistent) { boolean no_continue_sent = false; try { if (_inputStream.getExpectContinues() != null) { _inputStream.setExpectContinues(null); no_continue_sent = true; } else { int remaining = _inputStream.getContentLength(); if (remaining != 0) // Read remaining input while (_inputStream.skip(4096) > 0 || _inputStream.read() >= 0) ; } } catch (IOException e) { if (_inputStream.getContentLength() > 0) _inputStream.setContentLength(0); _persistent = false; LogSupport.ignore(log, e); exception(new HttpException(HttpResponse.__400_Bad_Request, "Missing Content")); } // Check for no more content if (!no_continue_sent && _inputStream.getContentLength() > 0) { _inputStream.setContentLength(0); _persistent = false; exception(new HttpException(HttpResponse.__400_Bad_Request, "Missing Content")); } // Commit the response try { _outputStream.close(); bytes_written = _outputStream.getBytesWritten(); _outputStream.resetStream(); _outputStream.addObserver(this); _inputStream.resetStream(); } catch (IOException e) { exception(e); } } else if (_response != null) // There was a request { // half hearted attempt to eat any remaining input try { if (_inputStream.getContentLength() > 0) while (_inputStream.skip(4096) > 0 || _inputStream.read() >= 0) ; _inputStream.resetStream(); } catch (IOException e) { LogSupport.ignore(log, e); } // commit non persistent try { _outputStream.flush(); _response.commit(); bytes_written = _outputStream.getBytesWritten(); _outputStream.close(); _outputStream.resetStream(); } catch (IOException e) { exception(e); } } // Check response length if (_response != null) { if (log.isDebugEnabled()) log.debug("RESPONSE:\n" + _response); if (_persistent && content_length >= 0 && bytes_written > 0 && content_length != bytes_written) { log.warn( "Invalid length: Content-Length=" + content_length + " written=" + bytes_written + " for " + _request.getRequestURL()); _persistent = false; try { _outputStream.close(); } catch (IOException e) { log.warn(LogSupport.EXCEPTION, e); } } } // stats & logging if (stats) statsRequestEnd(); if (context != null) context.log(_request, _response, bytes_written); } return (_tunnel != null) || _persistent; }
/* ------------------------------------------------------------ */ protected void commit() throws IOException { if (_response.isCommitted()) return; int status = _response.getStatus(); int length = -1; // Check if there is missing content expectations if (_inputStream.getExpectContinues() != null) { // No input read yet - so assume it never will be _inputStream.setExpectContinues(null); _inputStream.unsafeSetContentLength(0); } // Handler forced close, listener stopped or no idle threads left. boolean has_close = HttpFields.__Close.equals(_response.getField(HttpFields.__Connection)); if (!_persistent || _close || _listener != null && (!_listener.isStarted() || _listener.isOutOfResources())) { _close = true; if (!has_close) _response.setField(HttpFields.__Connection, HttpFields.__Close); has_close = true; } if (_close) _persistent = false; // Determine how to limit content length if (_persistent) { switch (_dotVersion) { case 1: { String transfer_coding = _response.getField(HttpFields.__TransferEncoding); if (transfer_coding == null || transfer_coding.length() == 0 || HttpFields.__Identity.equalsIgnoreCase(transfer_coding)) { // if (can have content and no content length) if (status != HttpResponse.__304_Not_Modified && status != HttpResponse.__204_No_Content && _response.getField(HttpFields.__ContentLength) == null) { if (_completing) { length = _outputStream.getBytesWritten(); _response.setContentLength(length); } else { // Chunk it! _response.setField(HttpFields.__TransferEncoding, HttpFields.__Chunked); _outputStream.setChunking(); } } } else { // Use transfer encodings to determine length _response.removeField(HttpFields.__ContentLength); _outputStream.setChunking(); if (!HttpFields.__Chunked.equalsIgnoreCase(transfer_coding)) { // Check against any TE field List te = _request.getAcceptableTransferCodings(); Enumeration enm = _response.getFieldValues( HttpFields.__TransferEncoding, HttpFields.__separators); while (enm.hasMoreElements()) { String coding = (String) enm.nextElement(); if (HttpFields.__Identity.equalsIgnoreCase(coding) || HttpFields.__Chunked.equalsIgnoreCase(coding)) continue; if (te == null || !te.contains(coding)) throw new HttpException(HttpResponse.__501_Not_Implemented, coding); } } } } break; case 0: { // if (can have content and no content length) _response.removeField(HttpFields.__TransferEncoding); if (_keepAlive) { if (status != HttpResponse.__304_Not_Modified && status != HttpResponse.__204_No_Content && _response.getField(HttpFields.__ContentLength) == null) { if (_completing) { length = _outputStream.getBytesWritten(); _response.setContentLength(length); _response.setField(HttpFields.__Connection, HttpFields.__KeepAlive); } else { _response.setField(HttpFields.__Connection, HttpFields.__Close); has_close = _close = true; _persistent = false; } } else _response.setField(HttpFields.__Connection, HttpFields.__KeepAlive); } else if (!has_close) _response.setField(HttpFields.__Connection, HttpFields.__Close); break; } default: { _close = true; _persistent = false; _keepAlive = false; } } } // Mark request as handled. _request.setHandled(true); _outputStream.writeHeader(_response); _outputStream.flush(); }