public void handleEvent(final ConnectedStreamChannel channel) { if (UndertowLogger.REQUEST_LOGGER.isTraceEnabled()) { UndertowLogger.REQUEST_LOGGER.tracef("Opened connection with %s", channel.getPeerAddress()); } final PushBackStreamChannel pushBackStreamChannel = new PushBackStreamChannel(channel); HttpServerConnection connection = new HttpServerConnection( channel, bufferPool, rootHandler, maxConcurrentRequestsPerConnection); HttpReadListener readListener = new HttpReadListener(channel, connection); pushBackStreamChannel.getReadSetter().set(readListener); readListener.handleEvent(pushBackStreamChannel); channel.resumeReads(); }
public static void executeRootHandler( final HttpHandler handler, final HttpServerExchange exchange) { try { exchange.setInCall(true); handler.handleRequest(exchange); exchange.setInCall(false); if (exchange.isDispatched()) { final Runnable dispatchTask = exchange.getDispatchTask(); Executor executor = exchange.getDispatchExecutor(); exchange.unDispatch(); if (dispatchTask != null) { executor = executor == null ? exchange.getConnection().getWorker() : executor; executor.execute(dispatchTask); } } else { exchange.endExchange(); } } catch (Throwable t) { exchange.setInCall(false); if (!exchange.isResponseStarted()) { exchange.setResponseCode(500); } UndertowLogger.REQUEST_LOGGER.errorf(t, "Blocking request failed %s", exchange); exchange.endExchange(); } }
@Override public void run() { handle = null; if (expireTime == -1) { return; } long current = System.currentTimeMillis(); if (current < expireTime) { // timeout has been bumped, re-schedule handle = WorkerUtils.executeAfter( connection.getIoThread(), timeoutCommand, (expireTime - current) + FUZZ_FACTOR, TimeUnit.MILLISECONDS); return; } UndertowLogger.REQUEST_LOGGER.tracef( "Timing out channel %s due to inactivity", connection.getSourceChannel()); IoUtils.safeClose(connection); if (connection.getSourceChannel().isReadResumed()) { ChannelListeners.invokeChannelListener( connection.getSourceChannel(), connection.getSourceChannel().getReadListener()); } if (connection.getSinkChannel().isWriteResumed()) { ChannelListeners.invokeChannelListener( connection.getSinkChannel(), connection.getSinkChannel().getWriteListener()); } }
@Override public void emitHeader(HttpString name, String value, boolean neverIndex) { headerMap.add(name, value); for (int i = 0; i < name.length(); ++i) { byte c = name.byteAt(i); if (c >= 'A' && c <= 'Z') { invalid = true; UndertowLogger.REQUEST_LOGGER.debugf( "Malformed request, header %s contains uppercase characters", name); } } }
@Override public void handleEvent(final StreamSourceChannel channel) { try { doParse(channel); if (state == 4) { exchange.dispatch(SameThreadExecutor.INSTANCE, handler); } } catch (IOException e) { IoUtils.safeClose(channel); UndertowLogger.REQUEST_LOGGER.ioExceptionReadingFromChannel(e); exchange.endExchange(); } }
@Override public void run() { UndertowLogger.REQUEST_LOGGER.tracef("Timing out channel %s due to inactivity"); IoUtils.safeClose(connection); if (connection.getSourceChannel().isReadResumed()) { ChannelListeners.invokeChannelListener( connection.getSourceChannel(), connection.getSourceChannel().getReadListener()); } if (connection.getSinkChannel().isWriteResumed()) { ChannelListeners.invokeChannelListener( connection.getSinkChannel(), connection.getSinkChannel().getWriteListener()); } }
@Override public void handleEvent(SpdyChannel channel) { try { SpdyStreamSourceChannel result = channel.receive(); if (result instanceof SpdySynReplyStreamSourceChannel) { final int streamId = ((SpdySynReplyStreamSourceChannel) result).getStreamId(); SpdyClientExchange request = currentExchanges.get(streamId); result.addCloseTask( new ChannelListener<SpdyStreamSourceChannel>() { @Override public void handleEvent(SpdyStreamSourceChannel channel) { currentExchanges.remove(streamId); } }); if (request == null) { // server side initiated stream, we can't deal with that at the moment // just fail // TODO: either handle this properly or at the very least send RST_STREAM channel.sendGoAway(SpdyChannel.CLOSE_PROTOCOL_ERROR); IoUtils.safeClose(SpdyClientConnection.this); return; } request.responseReady((SpdySynReplyStreamSourceChannel) result); } else if (result instanceof SpdyPingStreamSourceChannel) { handlePing((SpdyPingStreamSourceChannel) result); } else if (result instanceof SpdyRstStreamStreamSourceChannel) { int stream = ((SpdyRstStreamStreamSourceChannel) result).getStreamId(); UndertowLogger.REQUEST_LOGGER.debugf("Client received RST_STREAM for stream %s", stream); SpdyClientExchange exchange = currentExchanges.get(stream); if (exchange != null) { exchange.failed(UndertowMessages.MESSAGES.spdyStreamWasReset()); } } else if (!channel.isOpen()) { throw UndertowMessages.MESSAGES.channelIsClosed(); } } catch (IOException e) { UndertowLogger.REQUEST_IO_LOGGER.ioException(e); IoUtils.safeClose(SpdyClientConnection.this); for (Map.Entry<Integer, SpdyClientExchange> entry : currentExchanges.entrySet()) { try { entry.getValue().failed(e); } catch (Exception ex) { UndertowLogger.REQUEST_IO_LOGGER.ioException(new IOException(ex)); } } } }
@Override public void requestDone(HttpServerExchange exchange) { // Batch may no longer be active if session was invalidated if (this.batch.isActive()) { try (BatchContext context = this.manager.getSessionManager().getBatcher().resumeBatch(this.batch)) { this.entry.getKey().close(); this.batch.close(); } catch (Throwable e) { // Don't propagate exceptions at the stage, since response was alread committed UndertowLogger.REQUEST_LOGGER.warn(e.getLocalizedMessage(), e); } } }
@Override public void handleRequest(HttpServerExchange exchange) throws Exception { HeaderMap requestHeaders = exchange.getRequestHeaders(); final String sessionId = requestHeaders.getFirst(SSL_SESSION_ID); if (sessionId != null) { final String cipher = requestHeaders.getFirst(SSL_CIPHER); String clientCert = requestHeaders.getFirst(SSL_CLIENT_CERT); // the proxy client replaces \n with ' ' if (clientCert != null && clientCert.length() > 28) { StringBuilder sb = new StringBuilder(clientCert.length() + 1); sb.append(Certificates.BEGIN_CERT); sb.append('\n'); sb.append( clientCert .replace(' ', '\n') .substring(28, clientCert.length() - 26)); // core certificate data sb.append('\n'); sb.append(Certificates.END_CERT); clientCert = sb.toString(); } try { SSLSessionInfo info = new BasicSSLSessionInfo(sessionId, cipher, clientCert); exchange.setRequestScheme(HTTPS); exchange.getConnection().setSslSessionInfo(info); exchange.addExchangeCompleteListener(CLEAR_SSL_LISTENER); } catch (java.security.cert.CertificateException e) { UndertowLogger.REQUEST_LOGGER.debugf( e, "Could not create certificate from header %s", clientCert); } catch (CertificateException e) { UndertowLogger.REQUEST_LOGGER.debugf( e, "Could not create certificate from header %s", clientCert); } } next.handleRequest(exchange); }
@Override public void handleRequest(final HttpServerExchange exchange) { final Deque<String> origin = exchange.getRequestHeaders().get(Headers.ORIGIN); if (origin == null) { if (requireOriginHeader) { // TODO: Is 403 (Forbidden) the best response code if (UndertowLogger.REQUEST_LOGGER.isDebugEnabled()) { UndertowLogger.REQUEST_LOGGER.debugf( "Refusing request for %s due to lack of Origin: header", exchange.getRequestPath()); } HttpHandlers.executeHandler(originFailedHandler, exchange); return; } } else { boolean found = false; final boolean requireAllOrigins = this.requireAllOrigins; for (final String header : origin) { if (allowedOrigins.contains(header)) { found = true; if (!requireAllOrigins) { break; } } else if (requireAllOrigins) { if (UndertowLogger.REQUEST_LOGGER.isDebugEnabled()) { UndertowLogger.REQUEST_LOGGER.debugf( "Refusing request for %s due to Origin %s not being in the allowed origins list", exchange.getRequestPath(), header); } HttpHandlers.executeHandler(originFailedHandler, exchange); return; } } if (!found) { if (UndertowLogger.REQUEST_LOGGER.isDebugEnabled()) { UndertowLogger.REQUEST_LOGGER.debugf( "Refusing request for %s as none of the specified origins %s were in the allowed origins list", exchange.getRequestPath(), origin); } HttpHandlers.executeHandler(originFailedHandler, exchange); return; } } HttpHandlers.executeHandler(next, exchange); }
public void handleEvent(final StreamSourceChannel channel) { Pooled<ByteBuffer> existing = connection.getExtraBytes(); final Pooled<ByteBuffer> pooled = existing == null ? connection.getBufferPool().allocate() : existing; final ByteBuffer buffer = pooled.getResource(); boolean free = true; try { int res; do { if (existing == null) { buffer.clear(); try { res = channel.read(buffer); } catch (IOException e) { UndertowLogger.REQUEST_IO_LOGGER.debug("Error reading request", e); IoUtils.safeClose(connection); return; } } else { res = buffer.remaining(); } if (res == 0) { if (!channel.isReadResumed()) { channel.getReadSetter().set(this); channel.resumeReads(); } return; } else if (res == -1) { try { channel.suspendReads(); channel.shutdownReads(); final StreamSinkChannel responseChannel = this.connection.getChannel().getSinkChannel(); responseChannel.shutdownWrites(); // will return false if there's a response queued ahead of this one, so we'll set up a // listener then if (!responseChannel.flush()) { responseChannel .getWriteSetter() .set(ChannelListeners.flushingChannelListener(null, null)); responseChannel.resumeWrites(); } } catch (IOException e) { UndertowLogger.REQUEST_IO_LOGGER.debug("Error reading request", e); // f**k it, it's all ruined IoUtils.safeClose(channel); return; } return; } if (existing != null) { existing = null; connection.setExtraBytes(null); } else { buffer.flip(); } parser.handle(buffer, state, httpServerExchange); if (buffer.hasRemaining()) { free = false; connection.setExtraBytes(pooled); } int total = read + res; read = total; if (read > maxRequestSize) { UndertowLogger.REQUEST_LOGGER.requestHeaderWasTooLarge( connection.getPeerAddress(), maxRequestSize); IoUtils.safeClose(connection); return; } } while (!state.isComplete()); // we remove ourselves as the read listener from the channel; // if the http handler doesn't set any then reads will suspend, which is the right thing to do channel.getReadSetter().set(null); channel.suspendReads(); final HttpServerExchange httpServerExchange = this.httpServerExchange; httpServerExchange.putAttachment( UndertowOptions.ATTACHMENT_KEY, connection.getUndertowOptions()); httpServerExchange.setRequestScheme(connection.getSslSession() != null ? "https" : "http"); this.httpServerExchange = null; HttpTransferEncoding.setupRequest(httpServerExchange); HttpHandlers.executeRootHandler( connection.getRootHandler(), httpServerExchange, Thread.currentThread() instanceof XnioExecutor); } catch (Exception e) { sendBadRequestAndClose(connection.getChannel(), e); return; } finally { if (free) pooled.free(); } }
@Override public Session createSession( final HttpServerExchange serverExchange, final SessionConfig config) { if (evictionQueue != null) { if (expireOldestUnusedSessionOnMax) { while (sessions.size() >= maxSize && !evictionQueue.isEmpty()) { String key = evictionQueue.poll(); UndertowLogger.REQUEST_LOGGER.debugf("Removing session %s as max size has been hit", key); SessionImpl toRemove = sessions.get(key); if (toRemove != null) { toRemove.invalidate( null, SessionListener.SessionDestroyedReason.TIMEOUT); // todo: better reason } } } else if (sessions.size() >= maxSize) { if (statisticsEnabled) { rejectedSessionCount.incrementAndGet(); } throw UndertowMessages.MESSAGES.tooManySessions(maxSize); } } if (config == null) { throw UndertowMessages.MESSAGES.couldNotFindSessionCookieConfig(); } String sessionID = config.findSessionId(serverExchange); int count = 0; while (sessionID == null) { sessionID = sessionIdGenerator.createSessionId(); if (sessions.containsKey(sessionID)) { sessionID = null; } if (count++ == 100) { // this should never happen // but we guard against pathalogical session id generators to prevent an infinite loop throw UndertowMessages.MESSAGES.couldNotGenerateUniqueSessionId(); } } Object evictionToken; if (evictionQueue != null) { evictionToken = evictionQueue.offerLastAndReturnToken(sessionID); } else { evictionToken = null; } if (statisticsEnabled) { createdSessionCount.incrementAndGet(); } final SessionImpl session = new SessionImpl( this, sessionID, config, serverExchange.getIoThread(), serverExchange.getConnection().getWorker(), evictionToken, defaultSessionTimeout); sessions.put(sessionID, session); config.setSessionId(serverExchange, session.getId()); session.lastAccessed = System.currentTimeMillis(); session.bumpTimeout(); sessionListeners.sessionCreated(session, serverExchange); serverExchange.putAttachment(NEW_SESSION, session); return session; }