@Override public io.undertow.server.session.Session createSession( HttpServerExchange exchange, SessionConfig config) { if (config == null) { throw UndertowMessages.MESSAGES.couldNotFindSessionCookieConfig(); } String id = config.findSessionId(exchange); if (id == null) { int attempts = 0; do { if (++attempts > MAX_SESSION_ID_GENERATION_ATTEMPTS) { throw UndertowMessages.MESSAGES.couldNotGenerateUniqueSessionId(); } id = this.manager.createIdentifier(); } while (this.manager.containsSession(id)); config.setSessionId(exchange, id); } Batch batch = this.manager.getBatcher().createBatch(); try { Session<LocalSessionContext> session = this.manager.createSession(id); io.undertow.server.session.Session adapter = new DistributableSession(this, session, config, batch); this.sessionListeners.sessionCreated(adapter, exchange); return adapter; } catch (RuntimeException | Error e) { batch.discard(); throw e; } }
@Override public io.undertow.server.session.Session createSession( HttpServerExchange exchange, SessionConfig config) { if (config == null) { throw UndertowMessages.MESSAGES.couldNotFindSessionCookieConfig(); } String id = this.findSessionId(exchange, config); if (id != null) { if (this.manager.containsSession(id)) { throw UndertowMessages.MESSAGES.sessionAlreadyExists(id); } } else { id = this.manager.createSessionId(); } Batcher batcher = this.manager.getBatcher(); boolean started = batcher.startBatch(); try { Session<Void> session = this.manager.createSession(id); io.undertow.server.session.Session facade = this.getSession(session, exchange, config); this.sessionListeners.sessionCreated(facade, exchange); return facade; } catch (RuntimeException | Error e) { if (started) { batcher.endBatch(false); } throw e; } }
@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 setUndertowOptions(OptionMap undertowOptions) { if (undertowOptions == null) { throw UndertowMessages.MESSAGES.argumentCannotBeNull("undertowOptions"); } this.undertowOptions = undertowOptions; }
public synchronized PathMatcher addExactPath(final String path, final T handler) { if (path.isEmpty()) { throw UndertowMessages.MESSAGES.pathMustBeSpecified(); } exactPathMatches.put(URLUtils.normalizeSlashes(path), handler); return this; }
@Override public int write(ByteBuffer src) throws IOException { if (anyAreSet(state, FLAG_WRITES_TERMINATED)) { throw UndertowMessages.MESSAGES.channelIsClosed(); } return (int) doWrite(new ByteBuffer[] {src}, 0, 1); }
@Override public long write(ByteBuffer[] srcs, int offs, int len) throws IOException { if (anyAreSet(state, FLAG_WRITES_TERMINATED)) { throw UndertowMessages.MESSAGES.channelIsClosed(); } return doWrite(srcs, offs, len); }
@Override public int getMaxInactiveInterval() { if (invalid) { throw UndertowMessages.MESSAGES.sessionNotFound(sessionId); } return maxInactiveInterval; }
@Override public long getLastAccessedTime() { if (invalid) { throw UndertowMessages.MESSAGES.sessionNotFound(sessionId); } return lastAccessed; }
@Override public long getCreationTime() { if (invalid) { throw UndertowMessages.MESSAGES.sessionNotFound(sessionId); } return creationTime; }
@Override public HttpServerExchange sendOutOfBandResponse(HttpServerExchange exchange) { if (exchange == null || !HttpContinue.requiresContinueResponse(exchange)) { throw UndertowMessages.MESSAGES.outOfBandResponseOnlyAllowedFor100Continue(); } final ConduitState state = resetChannel(); HttpServerExchange newExchange = new HttpServerExchange(this); for (HttpString header : exchange.getRequestHeaders().getHeaderNames()) { newExchange.getRequestHeaders().putAll(header, exchange.getRequestHeaders().get(header)); } newExchange.setProtocol(exchange.getProtocol()); newExchange.setRequestMethod(exchange.getRequestMethod()); newExchange.setRequestPath(exchange.getRequestPath()); newExchange.getRequestHeaders().put(Headers.CONNECTION, Headers.KEEP_ALIVE.toString()); newExchange.getRequestHeaders().put(Headers.CONTENT_LENGTH, 0); newExchange.addExchangeCompleteListener( new ExchangeCompletionListener() { @Override public void exchangeEvent(HttpServerExchange exchange, NextListener nextListener) { restoreChannel(state); } }); return newExchange; }
@Override public Set<String> getAttributeNames() { if (invalid) { throw UndertowMessages.MESSAGES.sessionNotFound(sessionId); } bumpTimeout(); return attributes.keySet(); }
@Override public Object getAttribute(final String name) { if (invalid) { throw UndertowMessages.MESSAGES.sessionNotFound(sessionId); } bumpTimeout(); return attributes.get(name); }
@Override public void setMaxInactiveInterval(final int interval) { if (invalid) { throw UndertowMessages.MESSAGES.sessionNotFound(sessionId); } maxInactiveInterval = interval; bumpTimeout(); }
private void queue(final FileChannel source, final IoCallback ioCallback) { // if data is sent from withing the callback we queue it, to prevent the stack growing // indefinitely if (pendingFile != null) { throw UndertowMessages.MESSAGES.dataAlreadyQueued(); } pendingFile = source; queuedCallback = ioCallback; }
public synchronized PathMatcher removeExactPath(final String path) { if (path == null || path.isEmpty()) { throw UndertowMessages.MESSAGES.pathMustBeSpecified(); } exactPathMatches.remove(URLUtils.normalizeSlashes(path)); return this; }
private void queue(final ByteBuffer[] byteBuffers, final IoCallback ioCallback) { // if data is sent from withing the callback we queue it, to prevent the stack growing // indefinitely if (next != null) { throw UndertowMessages.MESSAGES.dataAlreadyQueued(); } next = byteBuffers; queuedCallback = ioCallback; }
@Override public void truncateWrites() throws IOException { for (Frame frame : frameQueue) { FrameCallBack cb = frame.callback; if (cb != null) { cb.failed(UndertowMessages.MESSAGES.channelIsClosed()); } } }
@Override public Object removeAttribute(final String name) { if (invalid) { throw UndertowMessages.MESSAGES.sessionNotFound(sessionId); } final Object existing = attributes.remove(name); sessionManager.sessionListeners.attributeRemoved(this, name, existing); bumpTimeout(); return existing; }
/** * Change the response code for this response. If not specified, the code will be a {@code 200}. * Setting the response code after the response headers have been transmitted has no effect. * * @param responseCode the new code * @throws IllegalStateException if a response or upgrade was already sent */ public void setResponseCode(final int responseCode) { if (responseCode < 0 || responseCode > 999) { throw new IllegalArgumentException("Invalid response code"); } int oldVal, newVal; do { oldVal = state; if (allAreSet(oldVal, FLAG_RESPONSE_SENT)) { throw UndertowMessages.MESSAGES.responseAlreadyStarted(); } newVal = oldVal & ~MASK_RESPONSE_CODE | responseCode & MASK_RESPONSE_CODE; } while (!stateUpdater.compareAndSet(this, oldVal, newVal)); }
/** * Adds a {@link ChannelWrapper} to the response wrapper chain. * * @param wrapper the wrapper */ public void addResponseWrapper(final ChannelWrapper<StreamSinkChannel> wrapper) { ChannelWrapper[] oldVal; ChannelWrapper[] newVal; int oldLen; do { oldVal = responseWrappers; if (oldVal == null) { throw UndertowMessages.MESSAGES.responseChannelAlreadyProvided(); } oldLen = oldVal.length; newVal = Arrays.copyOf(oldVal, oldLen + 1); newVal[oldLen] = wrapper; } while (!responseWrappersUpdater.compareAndSet(this, oldVal, newVal)); }
/** * Transmit the response headers. After this method successfully returns, the response channel may * become writable. * * <p>If this method fails the request and response channels will be closed. * * <p>This method runs asynchronously. If the channel is writable it will attempt to write as much * of the response header as possible, and then queue the rest in a listener and return. * * <p>If future handlers in the chain attempt to write before this is finished XNIO will just * magically sort it out so it works. This is not actually implemented yet, so we just terminate * the connection straight away at the moment. * * <p>TODO: make this work properly * * @throws IllegalStateException if the response headers were already sent */ void startResponse() throws IllegalStateException { int oldVal, newVal; do { oldVal = state; if (allAreSet(oldVal, FLAG_RESPONSE_SENT)) { throw UndertowMessages.MESSAGES.responseAlreadyStarted(); } newVal = oldVal | FLAG_RESPONSE_SENT; } while (!stateUpdater.compareAndSet(this, oldVal, newVal)); log.tracef( "Starting to write response for %s using channel %s", this, underlyingResponseChannel); final HeaderMap responseHeaders = this.responseHeaders; responseHeaders.lock(); }
@Override public Object setAttribute(final String name, final Object value) { if (value == null) { return removeAttribute(name); } if (invalid) { throw UndertowMessages.MESSAGES.sessionNotFound(sessionId); } final Object existing = attributes.put(name, value); if (existing == null) { sessionManager.sessionListeners.attributeAdded(this, name, value); } else { sessionManager.sessionListeners.attributeUpdated(this, name, value, existing); } bumpTimeout(); return existing; }
void invalidate( final HttpServerExchange exchange, SessionListener.SessionDestroyedReason reason) { synchronized (SessionImpl.this) { if (timerCancelKey != null) { timerCancelKey.remove(); } SessionImpl sess = sessionManager.sessions.remove(sessionId); if (sess == null) { if (reason == SessionListener.SessionDestroyedReason.INVALIDATED) { throw UndertowMessages.MESSAGES.sessionAlreadyInvalidated(); } return; } invalidationStarted = true; } sessionManager.sessionListeners.sessionDestroyed(this, exchange, reason); invalid = true; if (sessionManager.statisticsEnabled) { long avg, newAvg; do { avg = sessionManager.averageSessionLifetime.get(); BigDecimal bd = new BigDecimal(avg); bd.multiply(new BigDecimal(sessionManager.expiredSessionCount.get())).add(bd); newAvg = bd.divide( new BigDecimal(sessionManager.expiredSessionCount.get() + 1), MathContext.DECIMAL64) .longValue(); } while (!sessionManager.averageSessionLifetime.compareAndSet(avg, newAvg)); sessionManager.expiredSessionCount.incrementAndGet(); long life = System.currentTimeMillis() - creationTime; long existing = sessionManager.longestSessionLifetime.get(); while (life > existing) { if (sessionManager.longestSessionLifetime.compareAndSet(existing, life)) { break; } existing = sessionManager.longestSessionLifetime.get(); } } if (exchange != null) { sessionCookieConfig.clearSession(exchange, this.getId()); } }
public synchronized PathMatcher removePrefixPath(final String path) { if (path == null || path.isEmpty()) { throw UndertowMessages.MESSAGES.pathMustBeSpecified(); } final String normalizedPath = URLUtils.normalizeSlashes(path); if (PathMatcher.STRING_PATH_SEPARATOR.equals(normalizedPath)) { defaultHandler = null; return this; } paths.remove(normalizedPath); buildLengths(); return this; }
@Override public FormData parseBlocking() throws IOException { final FormData existing = exchange.getAttachment(FORM_DATA); if (existing != null) { return existing; } StreamSourceChannel channel = exchange.getRequestChannel(); if (channel == null) { throw new IOException(UndertowMessages.MESSAGES.requestChannelAlreadyProvided()); } else { while (state != 4) { doParse(channel); if (state != 4) { channel.awaitReadable(); } } } return data; }
@Override public void parse(HttpHandler handler) throws Exception { if (exchange.getAttachment(FORM_DATA) != null) { handler.handleRequest(exchange); return; } this.handler = handler; StreamSourceChannel channel = exchange.getRequestChannel(); if (channel == null) { throw new IOException(UndertowMessages.MESSAGES.requestChannelAlreadyProvided()); } else { doParse(channel); if (state != 4) { channel.getReadSetter().set(this); channel.resumeReads(); } else { exchange.dispatch(SameThreadExecutor.INSTANCE, handler); } } }
public void setMaxConcurrentRequestsPerConnection(final int maxConcurrentRequestsPerConnection) { if (maxConcurrentRequestsPerConnection <= 0) { throw UndertowMessages.MESSAGES.maximumConcurrentRequestsMustBeLargerThanZero(); } this.maxConcurrentRequestsPerConnection = maxConcurrentRequestsPerConnection; }
/** * Initiate a low-copy transfer between two stream channels. The pool should be a direct buffer * pool for best performance. * * @param source the source channel * @param sink the target channel * @param sourceListener the source listener to set and call when the transfer is complete, or * {@code null} to clear the listener at that time * @param sinkListener the target listener to set and call when the transfer is complete, or * {@code null} to clear the listener at that time * @param readExceptionHandler the read exception handler to call if an error occurs during a read * operation * @param writeExceptionHandler the write exception handler to call if an error occurs during a * write operation * @param pool the pool from which the transfer buffer should be allocated */ public static <I extends StreamSourceChannel, O extends StreamSinkChannel> void initiateTransfer( final I source, final O sink, final ChannelListener<? super I> sourceListener, final ChannelListener<? super O> sinkListener, final ChannelExceptionHandler<? super I> readExceptionHandler, final ChannelExceptionHandler<? super O> writeExceptionHandler, Pool<ByteBuffer> pool) { if (pool == null) { throw UndertowMessages.MESSAGES.argumentCannotBeNull("pool"); } final Pooled<ByteBuffer> allocated = pool.allocate(); boolean free = true; try { final ByteBuffer buffer = allocated.getResource(); long read; for (; ; ) { try { read = source.read(buffer); buffer.flip(); } catch (IOException e) { ChannelListeners.invokeChannelExceptionHandler(source, readExceptionHandler, e); return; } if (read == 0 && !buffer.hasRemaining()) { break; } if (read == -1 && !buffer.hasRemaining()) { done(source, sink, sourceListener, sinkListener); return; } while (buffer.hasRemaining()) { final int res; try { res = sink.write(buffer); } catch (IOException e) { ChannelListeners.invokeChannelExceptionHandler(sink, writeExceptionHandler, e); return; } if (res == 0) { break; } } if (buffer.hasRemaining()) { break; } buffer.clear(); } Pooled<ByteBuffer> current = null; if (buffer.hasRemaining()) { current = allocated; free = false; } final TransferListener<I, O> listener = new TransferListener<I, O>( pool, current, source, sink, sourceListener, sinkListener, writeExceptionHandler, readExceptionHandler, read == -1); sink.getWriteSetter().set(listener); source.getReadSetter().set(listener); // we resume both reads and writes, as we want to keep trying to fill the buffer if (current == null || buffer.capacity() != buffer.remaining()) { // we don't resume if the buffer is 100% full source.resumeReads(); } if (current != null) { // we don't resume writes if we have nothing to write sink.resumeWrites(); } } finally { if (free) { allocated.free(); } } }
@Override public StreamConnection performUpgrade() throws IOException { throw UndertowMessages.MESSAGES.upgradeNotSupported(); }