/** * Look for SSL session ID if required. Only look for SSL Session ID if it is the only tracking * method enabled. */ protected void parseSessionSslId(Request request) { if (request.getRequestedSessionId() == null && SSL_ONLY.equals(request.getServletContext().getEffectiveSessionTrackingModes()) && request.connector.secure) { // TODO Is there a better way to map SSL sessions to our sesison ID? // TODO The request.getAttribute() will cause a number of other SSL // attribute to be populated. Is this a performance concern? request.setRequestedSessionId(request.getAttribute(SSLSupport.SESSION_ID_KEY).toString()); request.setRequestedSessionSSL(true); } }
@Override public boolean asyncDispatch( org.apache.coyote.Request req, org.apache.coyote.Response res, SocketStatus status) throws Exception { Request request = (Request) req.getNote(ADAPTER_NOTES); Response response = (Response) res.getNote(ADAPTER_NOTES); if (request == null) { throw new IllegalStateException("Dispatch may only happen on an existing request."); } boolean comet = false; boolean success = true; AsyncContextImpl asyncConImpl = (AsyncContextImpl) request.getAsyncContext(); req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName()); try { if (!request.isAsync() && !comet) { // Error or timeout - need to tell listeners the request is over // Have to test this first since state may change while in this // method and this is only required if entering this method in // this state Context ctxt = (Context) request.getMappingData().context; if (ctxt != null) { ctxt.fireRequestDestroyEvent(request); } // Lift any suspension (e.g. if sendError() was used by an async // request) to allow the response to be written to the client response.setSuspended(false); } if (status == SocketStatus.TIMEOUT) { if (!asyncConImpl.timeout()) { asyncConImpl.setErrorState(null, false); } } // Has an error occurred during async processing that needs to be // processed by the application's error page mechanism (or Tomcat's // if the application doesn't define one)? if (!request.isAsyncDispatching() && request.isAsync() && response.isErrorReportRequired()) { connector.getService().getContainer().getPipeline().getFirst().invoke(request, response); } if (request.isAsyncDispatching()) { connector.getService().getContainer().getPipeline().getFirst().invoke(request, response); Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); if (t != null) { asyncConImpl.setErrorState(t, true); } } if (request.isComet()) { if (!response.isClosed() && !response.isError()) { if (request.getAvailable() || (request.getContentLength() > 0 && (!request.isParametersParsed()))) { // Invoke a read event right away if there are available bytes if (event(req, res, SocketStatus.OPEN_READ)) { comet = true; res.action(ActionCode.COMET_BEGIN, null); } } else { comet = true; res.action(ActionCode.COMET_BEGIN, null); } } else { // Clear the filter chain, as otherwise it will not be reset elsewhere // since this is a Comet request request.setFilterChain(null); } } if (!request.isAsync() && !comet) { request.finishRequest(); response.finishResponse(); req.action(ActionCode.POST_REQUEST, null); ((Context) request.getMappingData().context) .logAccess(request, response, System.currentTimeMillis() - req.getStartTime(), false); } // Check to see if the processor is in an error state. If it is, // bail out now. AtomicBoolean error = new AtomicBoolean(false); res.action(ActionCode.IS_ERROR, error); if (error.get()) { success = false; } } catch (IOException e) { success = false; // Ignore } catch (Throwable t) { ExceptionUtils.handleThrowable(t); success = false; log.error(sm.getString("coyoteAdapter.service"), t); } finally { req.getRequestProcessor().setWorkerThreadName(null); // Recycle the wrapper request and response if (!success || (!comet && !request.isAsync())) { request.recycle(); response.recycle(); } else { // Clear converters so that the minimum amount of memory // is used by this processor request.clearEncoders(); response.clearEncoders(); } } return success; }
/** * Event method. * * @return false to indicate an error, expected or not */ @Override public boolean event( org.apache.coyote.Request req, org.apache.coyote.Response res, SocketStatus status) { Request request = (Request) req.getNote(ADAPTER_NOTES); Response response = (Response) res.getNote(ADAPTER_NOTES); if (request.getWrapper() == null) { return false; } boolean error = false; boolean read = false; try { if (status == SocketStatus.OPEN_READ) { if (response.isClosed()) { // The event has been closed asynchronously, so call end instead of // read to cleanup the pipeline request.getEvent().setEventType(CometEvent.EventType.END); request.getEvent().setEventSubType(null); } else { try { // Fill the read buffer of the servlet layer if (request.read()) { read = true; } } catch (IOException e) { error = true; } if (read) { request.getEvent().setEventType(CometEvent.EventType.READ); request.getEvent().setEventSubType(null); } else if (error) { request.getEvent().setEventType(CometEvent.EventType.ERROR); request.getEvent().setEventSubType(CometEvent.EventSubType.CLIENT_DISCONNECT); } else { request.getEvent().setEventType(CometEvent.EventType.END); request.getEvent().setEventSubType(null); } } } else if (status == SocketStatus.DISCONNECT) { request.getEvent().setEventType(CometEvent.EventType.ERROR); request.getEvent().setEventSubType(CometEvent.EventSubType.CLIENT_DISCONNECT); error = true; } else if (status == SocketStatus.ERROR) { request.getEvent().setEventType(CometEvent.EventType.ERROR); request.getEvent().setEventSubType(CometEvent.EventSubType.IOEXCEPTION); error = true; } else if (status == SocketStatus.STOP) { request.getEvent().setEventType(CometEvent.EventType.END); request.getEvent().setEventSubType(CometEvent.EventSubType.SERVER_SHUTDOWN); } else if (status == SocketStatus.TIMEOUT) { if (response.isClosed()) { // The event has been closed asynchronously, so call end instead of // read to cleanup the pipeline request.getEvent().setEventType(CometEvent.EventType.END); request.getEvent().setEventSubType(null); } else { request.getEvent().setEventType(CometEvent.EventType.ERROR); request.getEvent().setEventSubType(CometEvent.EventSubType.TIMEOUT); } } req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName()); // Calling the container connector .getService() .getContainer() .getPipeline() .getFirst() .event(request, response, request.getEvent()); if (!error && !response.isClosed() && (request.getAttribute(RequestDispatcher.ERROR_EXCEPTION) != null)) { // An unexpected exception occurred while processing the event, so // error should be called request.getEvent().setEventType(CometEvent.EventType.ERROR); request.getEvent().setEventSubType(null); error = true; connector .getService() .getContainer() .getPipeline() .getFirst() .event(request, response, request.getEvent()); } if (response.isClosed() || !request.isComet()) { if (status == SocketStatus.OPEN_READ && request.getEvent().getEventType() != EventType.END) { // CometEvent.close was called during an event other than END request.getEvent().setEventType(CometEvent.EventType.END); request.getEvent().setEventSubType(null); error = true; connector .getService() .getContainer() .getPipeline() .getFirst() .event(request, response, request.getEvent()); } res.action(ActionCode.COMET_END, null); } else if (!error && read && request.getAvailable()) { // If this was a read and not all bytes have been read, or if no data // was read from the connector, then it is an error request.getEvent().setEventType(CometEvent.EventType.ERROR); request.getEvent().setEventSubType(CometEvent.EventSubType.IOEXCEPTION); error = true; connector .getService() .getContainer() .getPipeline() .getFirst() .event(request, response, request.getEvent()); } return (!error); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); if (!(t instanceof IOException)) { log.error(sm.getString("coyoteAdapter.service"), t); } error = true; return false; } finally { req.getRequestProcessor().setWorkerThreadName(null); // Recycle the wrapper request and response if (error || response.isClosed() || !request.isComet()) { ((Context) request.getMappingData().context) .logAccess(request, response, System.currentTimeMillis() - req.getStartTime(), false); request.recycle(); request.setFilterChain(null); response.recycle(); } } }
@Override public void sendError(int code, String message) throws IOException { if (isIncluding()) return; if (isCommitted()) LOG.warn("Committed before " + code + " " + message); resetBuffer(); _characterEncoding = null; setHeader(HttpHeader.EXPIRES, null); setHeader(HttpHeader.LAST_MODIFIED, null); setHeader(HttpHeader.CACHE_CONTROL, null); setHeader(HttpHeader.CONTENT_TYPE, null); setHeader(HttpHeader.CONTENT_LENGTH, null); _outputType = OutputType.NONE; setStatus(code); _reason = message; Request request = _channel.getRequest(); Throwable cause = (Throwable) request.getAttribute(Dispatcher.ERROR_EXCEPTION); if (message == null) message = cause == null ? HttpStatus.getMessage(code) : cause.toString(); // If we are allowed to have a body if (code != SC_NO_CONTENT && code != SC_NOT_MODIFIED && code != SC_PARTIAL_CONTENT && code >= SC_OK) { ErrorHandler error_handler = null; ContextHandler.Context context = request.getContext(); if (context != null) error_handler = context.getContextHandler().getErrorHandler(); if (error_handler == null) error_handler = _channel.getServer().getBean(ErrorHandler.class); if (error_handler != null) { request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, new Integer(code)); request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message); request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI()); request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME, request.getServletName()); error_handler.handle(null, _channel.getRequest(), _channel.getRequest(), this); } else { setHeader(HttpHeader.CACHE_CONTROL, "must-revalidate,no-cache,no-store"); setContentType(MimeTypes.Type.TEXT_HTML_8859_1.toString()); ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer(2048); if (message != null) { message = StringUtil.replace(message, "&", "&"); message = StringUtil.replace(message, "<", "<"); message = StringUtil.replace(message, ">", ">"); } String uri = request.getRequestURI(); if (uri != null) { uri = StringUtil.replace(uri, "&", "&"); uri = StringUtil.replace(uri, "<", "<"); uri = StringUtil.replace(uri, ">", ">"); } writer.write( "<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html;charset=ISO-8859-1\"/>\n"); writer.write("<title>Error "); writer.write(Integer.toString(code)); writer.write(' '); if (message == null) writer.write(message); writer.write("</title>\n</head>\n<body>\n<h2>HTTP ERROR: "); writer.write(Integer.toString(code)); writer.write("</h2>\n<p>Problem accessing "); writer.write(uri); writer.write(". Reason:\n<pre> "); writer.write(message); writer.write("</pre>"); writer.write("</p>\n<hr /><i><small>Powered by Jetty://</small></i>"); writer.write("\n</body>\n</html>\n"); writer.flush(); setContentLength(writer.size()); writer.writeTo(getOutputStream()); writer.destroy(); } } else if (code != SC_PARTIAL_CONTENT) { // TODO work out why this is required? _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_TYPE); _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_LENGTH); _characterEncoding = null; _mimeType = null; } complete(); }
/** * Notifies this <tt>ResponseCollector</tt> that a STUN response described by the specified * <tt>StunResponseEvent</tt> has been received. * * @param event the <tt>StunResponseEvent</tt> which describes the received STUN response * @see ResponseCollector#processResponse(StunResponseEvent) */ @Override public void processResponse(StunResponseEvent event) { TransactionID transactionID = event.getTransactionID(); logger.finest("Received a message: tranid= " + transactionID); logger.finest("localCand= " + hostCandidate); /* * Clean up for the purposes of the workaround which determines the STUN * Request to which a STUN Response responds. */ synchronized (requests) { requests.remove(transactionID); } // At long last, do start handling the received STUN Response. Response response = event.getResponse(); Request request = event.getRequest(); boolean completedResolvingCandidate = true; try { if (response.isSuccessResponse()) { // Authentication and Message-Integrity Mechanisms if (request.containsAttribute(Attribute.MESSAGE_INTEGRITY)) { MessageIntegrityAttribute messageIntegrityAttribute = (MessageIntegrityAttribute) response.getAttribute(Attribute.MESSAGE_INTEGRITY); /* * RFC 5389: If MESSAGE-INTEGRITY was absent, the response * MUST be discarded, as if it was never received. */ if (messageIntegrityAttribute == null) return; UsernameAttribute usernameAttribute = (UsernameAttribute) request.getAttribute(Attribute.USERNAME); /* * For a request or indication message, the agent MUST * include the USERNAME and MESSAGE-INTEGRITY attributes in * the message. */ if (usernameAttribute == null) return; if (!harvester .getStunStack() .validateMessageIntegrity( messageIntegrityAttribute, LongTermCredential.toString(usernameAttribute.getUsername()), !request.containsAttribute(Attribute.REALM) && !request.containsAttribute(Attribute.NONCE), event.getRawMessage())) return; } processSuccess(response, request, transactionID); } else { ErrorCodeAttribute errorCodeAttr = (ErrorCodeAttribute) response.getAttribute(Attribute.ERROR_CODE); if ((errorCodeAttr != null) && (errorCodeAttr.getErrorClass() == 4)) { try { switch (errorCodeAttr.getErrorNumber()) { case 1: // 401 Unauthorized if (processUnauthorized(response, request, transactionID)) completedResolvingCandidate = false; break; case 38: // 438 Stale Nonce if (processStaleNonce(response, request, transactionID)) completedResolvingCandidate = false; break; } } catch (StunException sex) { completedResolvingCandidate = true; } } if (completedResolvingCandidate && processErrorOrFailure(response, request, transactionID)) completedResolvingCandidate = false; } } finally { if (completedResolvingCandidate) completedResolvingCandidate(request, response); } }
/** * Notifies this <tt>StunCandidateHarvest</tt> that a specific STUN <tt>Request</tt> has been * challenged for a long-term credential (as the short-term credential mechanism does not utilize * challenging) in a specific <tt>realm</tt> and with a specific <tt>nonce</tt>. * * @param realm the realm in which the specified STUN <tt>Request</tt> has been challenged for a * long-term credential * @param nonce the nonce with which the specified STUN <tt>Request</tt> has been challenged for a * long-term credential * @param request the STUN <tt>Request</tt> which has been challenged for a long-term credential * @param requestTransactionID the <tt>TransactionID</tt> of <tt>request</tt> because * <tt>request</tt> only has it as a <tt>byte</tt> array and <tt>TransactionID</tt> is * required for the <tt>applicationData</tt> property value * @return <tt>true</tt> if the challenge has been processed and this * <tt>StunCandidateHarvest</tt> is to continue processing STUN <tt>Response</tt>s; otherwise, * <tt>false</tt> * @throws StunException if anything goes wrong while processing the challenge */ private boolean processChallenge( byte[] realm, byte[] nonce, Request request, TransactionID requestTransactionID) throws StunException { UsernameAttribute usernameAttribute = (UsernameAttribute) request.getAttribute(Attribute.USERNAME); if (usernameAttribute == null) { if (longTermCredentialSession == null) { LongTermCredential longTermCredential = harvester.createLongTermCredential(this, realm); if (longTermCredential == null) { // The long-term credential mechanism is not being utilized. return false; } else { longTermCredentialSession = new LongTermCredentialSession(longTermCredential, realm); harvester .getStunStack() .getCredentialsManager() .registerAuthority(longTermCredentialSession); } } else { /* * If we're going to use the long-term credential to retry the * request, the long-term credential should be for the request * in terms of realm. */ if (!longTermCredentialSession.realmEquals(realm)) return false; } } else { /* * If we sent a USERNAME in our request, then we had the long-term * credential at the time we sent the request in question. */ if (longTermCredentialSession == null) return false; else { /* * If we're going to use the long-term credential to retry the * request, the long-term credential should be for the request * in terms of username. */ if (!longTermCredentialSession.usernameEquals(usernameAttribute.getUsername())) return false; else { // And it terms of realm, of course. if (!longTermCredentialSession.realmEquals(realm)) return false; } } } /* * The nonce is either becoming known for the first time or being * updated after the old one has gone stale. */ longTermCredentialSession.setNonce(nonce); Request retryRequest = createRequestToRetry(request); TransactionID retryRequestTransactionID = null; if (retryRequest != null) { if (requestTransactionID != null) { Object applicationData = requestTransactionID.getApplicationData(); if (applicationData != null) { byte[] retryRequestTransactionIDAsBytes = retryRequest.getTransactionID(); retryRequestTransactionID = (retryRequestTransactionIDAsBytes == null) ? TransactionID.createNewTransactionID() : TransactionID.createTransactionID( harvester.getStunStack(), retryRequestTransactionIDAsBytes); retryRequestTransactionID.setApplicationData(applicationData); } } retryRequestTransactionID = sendRequest(retryRequest, false, retryRequestTransactionID); } return (retryRequestTransactionID != null); }
protected void onError(Throwable failure) { final List<AsyncListener> listeners; final AsyncContextEvent event; final Request baseRequest = _channel.getRequest(); int code = HttpStatus.INTERNAL_SERVER_ERROR_500; String reason = null; if (failure instanceof BadMessageException) { BadMessageException bme = (BadMessageException) failure; code = bme.getCode(); reason = bme.getReason(); } else if (failure instanceof UnavailableException) { if (((UnavailableException) failure).isPermanent()) code = HttpStatus.NOT_FOUND_404; else code = HttpStatus.SERVICE_UNAVAILABLE_503; } try (Locker.Lock lock = _locker.lock()) { if (DEBUG) LOG.debug("onError {} {}", toStringLocked(), failure); // Set error on request. if (_event != null) { if (_event.getThrowable() != null) throw new IllegalStateException("Error already set", _event.getThrowable()); _event.addThrowable(failure); _event.getSuppliedRequest().setAttribute(ERROR_STATUS_CODE, code); _event.getSuppliedRequest().setAttribute(ERROR_EXCEPTION, failure); _event .getSuppliedRequest() .setAttribute( RequestDispatcher.ERROR_EXCEPTION_TYPE, failure == null ? null : failure.getClass()); _event.getSuppliedRequest().setAttribute(ERROR_MESSAGE, reason != null ? reason : null); } else { Throwable error = (Throwable) baseRequest.getAttribute(ERROR_EXCEPTION); if (error != null) throw new IllegalStateException("Error already set", error); baseRequest.setAttribute(ERROR_STATUS_CODE, code); baseRequest.setAttribute(ERROR_EXCEPTION, failure); baseRequest.setAttribute( RequestDispatcher.ERROR_EXCEPTION_TYPE, failure == null ? null : failure.getClass()); baseRequest.setAttribute(ERROR_MESSAGE, reason != null ? reason : null); } // Are we blocking? if (_async == null) { // Only called from within HttpChannel Handling, so much be dispatched, let's stay // dispatched! if (_state == State.DISPATCHED) { _state = State.THROWN; return; } throw new IllegalStateException(this.getStatusStringLocked()); } // We are Async _async = Async.ERRORING; listeners = _asyncListeners; event = _event; } if (listeners != null) { Runnable task = new Runnable() { @Override public void run() { for (AsyncListener listener : listeners) { try { listener.onError(event); } catch (Throwable x) { LOG.warn(x + " while invoking onError listener " + listener); LOG.debug(x); } } } @Override public String toString() { return "onError"; } }; runInContext(event, task); } boolean dispatch = false; try (Locker.Lock lock = _locker.lock()) { switch (_async) { case ERRORING: { // Still in this state ? The listeners did not invoke API methods // and the container must provide a default error dispatch. _async = Async.ERRORED; break; } case DISPATCH: case COMPLETE: { // The listeners called dispatch() or complete(). break; } default: { throw new IllegalStateException(toString()); } } if (_state == State.ASYNC_WAIT) { _state = State.ASYNC_WOKEN; dispatch = true; } } if (dispatch) { if (LOG.isDebugEnabled()) LOG.debug("Dispatch after error {}", this); scheduleDispatch(); } }