@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;
 }
Beispiel #5
0
 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;
 }
Beispiel #16
0
  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());
      }
    }
Beispiel #25
0
  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);
     }
   }
 }
Beispiel #28
0
 public void setMaxConcurrentRequestsPerConnection(final int maxConcurrentRequestsPerConnection) {
   if (maxConcurrentRequestsPerConnection <= 0) {
     throw UndertowMessages.MESSAGES.maximumConcurrentRequestsMustBeLargerThanZero();
   }
   this.maxConcurrentRequestsPerConnection = maxConcurrentRequestsPerConnection;
 }
Beispiel #29
0
  /**
   * 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();
 }