Example #1
0
 /**
  * Sends an error 500, performing a special logic to detect whether the request is suspended, to
  * avoid concurrent writes from the application.
  *
  * <p>It may happen that the application suspends, and then throws an exception, while an
  * application spawned thread writes the response content; in such case, we attempt to commit the
  * error directly bypassing the {@link ErrorHandler} mechanisms and the response OutputStream.
  *
  * @param x the Throwable that caused the problem
  */
 protected void handleException(Throwable x) {
   try {
     _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, x);
     _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE, x.getClass());
     if (_state.isSuspended()) {
       HttpFields fields = new HttpFields();
       fields.add(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE);
       ResponseInfo info =
           new ResponseInfo(
               _request.getHttpVersion(),
               fields,
               0,
               HttpStatus.INTERNAL_SERVER_ERROR_500,
               null,
               _request.isHead());
       boolean committed = sendResponse(info, null, true);
       if (!committed) LOG.warn("Could not send response error 500: " + x);
       _request.getAsyncContext().complete();
     } else if (isCommitted()) {
       if (!(x instanceof EofException)) LOG.warn("Could not send response error 500: " + x);
     } else {
       _response.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
       _response.sendError(500, x.getMessage());
     }
   } catch (IOException e) {
     // We tried our best, just log
     LOG.debug("Could not commit response error 500", e);
   }
 }
Example #2
0
  @Override
  public boolean headerComplete() {
    _requests.incrementAndGet();
    switch (_version) {
      case HTTP_0_9:
        break;

      case HTTP_1_0:
        if (_configuration.getSendDateHeader())
          _response.getHttpFields().put(_connector.getServer().getDateField());
        break;

      case HTTP_1_1:
        if (_configuration.getSendDateHeader())
          _response.getHttpFields().put(_connector.getServer().getDateField());

        if (_expect) {
          badMessage(HttpStatus.EXPECTATION_FAILED_417, null);
          return true;
        }

        break;

      default:
        throw new IllegalStateException();
    }

    return true;
  }
Example #3
0
 @Override
 public void log(Request request, Response response) {
   if (_ignorePathMap != null && _ignorePathMap.getMatch(request.getRequestURI()) != null) return;
   int status = response.getStatus();
   long written = response.getContentLength();
   transmitter.queue(
       new AccessLogJettyAdapter(request, status, written, _preferProxiedForAddress));
 }
Example #4
0
  protected boolean sendResponse(
      ResponseInfo info, ByteBuffer content, boolean complete, final Callback callback) {
    // TODO check that complete only set true once by changing _committed to AtomicRef<Enum>
    boolean committing = _committed.compareAndSet(false, true);
    if (committing) {
      // We need an info to commit
      if (info == null) info = _response.newResponseInfo();

      // wrap callback to process 100 or 500 responses
      final int status = info.getStatus();
      final Callback committed =
          (status < 200 && status >= 100)
              ? new Commit100Callback(callback)
              : new CommitCallback(callback);

      // committing write
      _transport.send(info, content, complete, committed);
    } else if (info == null) {
      // This is a normal write
      _transport.send(content, complete, callback);
    } else {
      callback.failed(new IllegalStateException("committed"));
    }
    return committing;
  }
Example #5
0
 public void reset() {
   _committed.set(false);
   _expect = false;
   _expect100Continue = false;
   _expect102Processing = false;
   _request.recycle();
   _response.recycle();
   _uri.clear();
 }
  /* ------------------------------------------------------------ */
  public void reset(boolean returnBuffers) {
    _parser.reset();
    if (returnBuffers) _parser.returnBuffers();
    _requestFields.clear();
    _request.recycle();

    _generator.reset(returnBuffers); // TODO maybe only release when low on resources
    _responseFields.clear();
    _response.recycle();

    _uri.clear();
  }
  /* ------------------------------------------------------------ */
  public void completeResponse() throws IOException {
    if (!_generator.isCommitted()) {
      _generator.setResponse(_response.getStatus(), _response.getReason());
      try {
        _generator.completeHeader(_responseFields, Generator.LAST);
      } catch (IOException io) {
        throw io;
      } catch (RuntimeException e) {
        LOG.warn("header full: " + e);
        LOG.debug(e);

        _response.reset();
        _generator.reset(true);
        _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500, null);
        _generator.completeHeader(_responseFields, Generator.LAST);
        _generator.complete();
        throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR_500);
      }
    }

    _generator.complete();
  }
  /* ------------------------------------------------------------ */
  public void commitResponse(boolean last) throws IOException {
    if (!_generator.isCommitted()) {
      _generator.setResponse(_response.getStatus(), _response.getReason());
      try {
        // If the client was expecting 100 continues, but we sent something
        // else, then we need to close the connection
        if (_expect100Continue && _response.getStatus() != 100) _generator.setPersistent(false);
        _generator.completeHeader(_responseFields, last);
      } catch (IOException io) {
        throw io;
      } catch (RuntimeException e) {
        LOG.warn("header full: " + e);

        _response.reset();
        _generator.reset(true);
        _generator.setResponse(HttpStatus.INTERNAL_SERVER_ERROR_500, null);
        _generator.completeHeader(_responseFields, Generator.LAST);
        _generator.complete();
        throw new HttpException(HttpStatus.INTERNAL_SERVER_ERROR_500);
      }
    }
    if (last) _generator.complete();
  }
Example #9
0
  /**
   * If the associated response has the Expect header set to 100 Continue, then accessing the input
   * stream indicates that the handler/servlet is ready for the request body and thus a 100 Continue
   * response is sent.
   *
   * @throws IOException if the InputStream cannot be created
   */
  public void continue100(int available) throws IOException {
    // If the client is expecting 100 CONTINUE, then send it now.
    // TODO: consider using an AtomicBoolean ?
    if (isExpecting100Continue()) {
      _expect100Continue = false;

      // is content missing?
      if (available == 0) {
        if (_response.isCommitted()) throw new IOException("Committed before 100 Continues");

        // TODO: break this dependency with HttpGenerator
        boolean committed = sendResponse(HttpGenerator.CONTINUE_100_INFO, null, false);
        if (!committed)
          throw new IOException("Concurrent commit while trying to send 100-Continue");
      }
    }
  }
  /*
   * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
   */
  protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch)
      throws ServletException, IOException {
    Request baseRequest =
        (request instanceof Request)
            ? ((Request) request)
            : HttpChannel.getCurrentHttpChannel().getRequest();
    Response base_response = baseRequest.getResponse();
    base_response.resetForForward();

    if (!(request instanceof HttpServletRequest)) request = new ServletRequestHttpWrapper(request);
    if (!(response instanceof HttpServletResponse))
      response = new ServletResponseHttpWrapper(response);

    final boolean old_handled = baseRequest.isHandled();
    final String old_uri = baseRequest.getRequestURI();
    final String old_context_path = baseRequest.getContextPath();
    final String old_servlet_path = baseRequest.getServletPath();
    final String old_path_info = baseRequest.getPathInfo();
    final String old_query = baseRequest.getQueryString();
    final Attributes old_attr = baseRequest.getAttributes();
    final DispatcherType old_type = baseRequest.getDispatcherType();
    MultiMap<String> old_params = baseRequest.getParameters();

    try {
      baseRequest.setHandled(false);
      baseRequest.setDispatcherType(dispatch);

      if (_named != null)
        _contextHandler.handle(
            _named, baseRequest, (HttpServletRequest) request, (HttpServletResponse) response);
      else {

        // process any query string from the dispatch URL
        String query = _dQuery;
        if (query != null) {
          // force parameter extraction
          if (old_params == null) {
            baseRequest.extractParameters();
            old_params = baseRequest.getParameters();
          }

          baseRequest.mergeQueryString(query);
        }

        ForwardAttributes attr = new ForwardAttributes(old_attr);

        // If we have already been forwarded previously, then keep using the established
        // original value. Otherwise, this is the first forward and we need to establish the values.
        // Note: the established value on the original request for pathInfo and
        // for queryString is allowed to be null, but cannot be null for the other values.
        if (old_attr.getAttribute(FORWARD_REQUEST_URI) != null) {
          attr._pathInfo = (String) old_attr.getAttribute(FORWARD_PATH_INFO);
          attr._query = (String) old_attr.getAttribute(FORWARD_QUERY_STRING);
          attr._requestURI = (String) old_attr.getAttribute(FORWARD_REQUEST_URI);
          attr._contextPath = (String) old_attr.getAttribute(FORWARD_CONTEXT_PATH);
          attr._servletPath = (String) old_attr.getAttribute(FORWARD_SERVLET_PATH);
        } else {
          attr._pathInfo = old_path_info;
          attr._query = old_query;
          attr._requestURI = old_uri;
          attr._contextPath = old_context_path;
          attr._servletPath = old_servlet_path;
        }

        baseRequest.setRequestURI(_uri);
        baseRequest.setContextPath(_contextHandler.getContextPath());
        baseRequest.setServletPath(null);
        baseRequest.setPathInfo(_uri);
        baseRequest.setAttributes(attr);

        _contextHandler.handle(
            _path, baseRequest, (HttpServletRequest) request, (HttpServletResponse) response);

        if (!baseRequest.getHttpChannelState().isAsync()) commitResponse(response, baseRequest);
      }
    } finally {
      baseRequest.setHandled(old_handled);
      baseRequest.setRequestURI(old_uri);
      baseRequest.setContextPath(old_context_path);
      baseRequest.setServletPath(old_servlet_path);
      baseRequest.setPathInfo(old_path_info);
      baseRequest.setAttributes(old_attr);
      baseRequest.setParameters(old_params);
      baseRequest.setQueryString(old_query);
      baseRequest.setDispatcherType(old_type);
    }
  }
  /* ------------------------------------------------------------ */
  protected void handleRequest() throws IOException {
    boolean error = false;

    String threadName = null;
    try {
      if (LOG.isDebugEnabled()) {
        threadName = Thread.currentThread().getName();
        Thread.currentThread().setName(threadName + " - " + _uri);
      }

      // Loop here to handle async request redispatches.
      // The loop is controlled by the call to async.unhandle in the
      // finally block below.  If call is from a non-blocking connector,
      // then the unhandle will return false only if an async dispatch has
      // already happened when unhandle is called.   For a blocking connector,
      // the wait for the asynchronous dispatch or timeout actually happens
      // within the call to unhandle().

      final Server server = _server;
      boolean handling = _request._async.handling() && server != null && server.isRunning();
      while (handling) {
        _request.setHandled(false);

        String info = null;
        try {
          _uri.getPort();
          info = URIUtil.canonicalPath(_uri.getDecodedPath());
          if (info == null && !_request.getMethod().equals(HttpMethods.CONNECT))
            throw new HttpException(400);
          _request.setPathInfo(info);

          if (_out != null) _out.reopen();

          if (_request._async.isInitial()) {
            _request.setDispatcherType(DispatcherType.REQUEST);
            _connector.customize(_endp, _request);
            server.handle(this);
          } else {
            _request.setDispatcherType(DispatcherType.ASYNC);
            server.handleAsync(this);
          }
        } catch (ContinuationThrowable e) {
          LOG.ignore(e);
        } catch (EofException e) {
          LOG.debug(e);
          error = true;
          _request.setHandled(true);
        } catch (RuntimeIOException e) {
          LOG.debug(e);
          error = true;
          _request.setHandled(true);
        } catch (HttpException e) {
          LOG.debug(e);
          error = true;
          _request.setHandled(true);
          _response.sendError(e.getStatus(), e.getReason());
        } catch (Throwable e) {
          if (e instanceof ThreadDeath) throw (ThreadDeath) e;

          LOG.warn(String.valueOf(_uri), e);
          error = true;
          _request.setHandled(true);
          _generator.sendError(info == null ? 400 : 500, null, null, true);
        } finally {
          handling = !_request._async.unhandle() && server.isRunning() && _server != null;
        }
      }
    } finally {
      if (threadName != null) Thread.currentThread().setName(threadName);

      if (_request._async.isUncompleted()) {
        _request._async.doComplete();

        if (_expect100Continue) {
          LOG.debug("100 continues not sent");
          // We didn't send 100 continues, but the latest interpretation
          // of the spec (see httpbis) is that the client will either
          // send the body anyway, or close.  So we no longer need to
          // do anything special here other than make the connection not persistent
          _expect100Continue = false;
          if (!_response.isCommitted()) _generator.setPersistent(false);
        }

        if (_endp.isOpen()) {
          if (error) {
            _endp.shutdownOutput();
            _generator.setPersistent(false);
            if (!_generator.isComplete()) _response.complete();
          } else {
            if (!_response.isCommitted() && !_request.isHandled())
              _response.sendError(HttpServletResponse.SC_NOT_FOUND);
            _response.complete();
            if (_generator.isPersistent()) _connector.persist(_endp);
          }
        } else {
          _response.complete();
        }

        _request.setHandled(true);
      }
    }
  }
Example #12
0
  /** @return True if the channel is ready to continue handling (ie it is not suspended) */
  public boolean handle() {
    LOG.debug("{} handle enter", this);

    setCurrentHttpChannel(this);

    String threadName = null;
    if (LOG.isDebugEnabled()) {
      threadName = Thread.currentThread().getName();
      Thread.currentThread().setName(threadName + " - " + _uri);
    }

    // Loop here to handle async request redispatches.
    // The loop is controlled by the call to async.unhandle in the
    // finally block below.  Unhandle will return false only if an async dispatch has
    // already happened when unhandle is called.
    HttpChannelState.Next next = _state.handling();
    while (next == Next.CONTINUE && getServer().isRunning()) {
      boolean error = false;
      try {
        _request.setHandled(false);
        _response.getHttpOutput().reopen();

        if (_state.isInitial()) {
          _request.setTimeStamp(System.currentTimeMillis());
          _request.setDispatcherType(DispatcherType.REQUEST);

          for (HttpConfiguration.Customizer customizer : _configuration.getCustomizers())
            customizer.customize(getConnector(), _configuration, _request);
          getServer().handle(this);
        } else {
          if (_request.getHttpChannelState().isExpired()) {
            _request.setDispatcherType(DispatcherType.ERROR);

            Throwable ex = _state.getAsyncContextEvent().getThrowable();
            String reason = "Async Timeout";
            if (ex != null) {
              reason = "Async Exception";
              _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ex);
            }
            _request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, new Integer(500));
            _request.setAttribute(RequestDispatcher.ERROR_MESSAGE, reason);
            _request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, _request.getRequestURI());

            _response.setStatusWithReason(500, reason);

            ErrorHandler eh = _state.getContextHandler().getErrorHandler();
            if (eh instanceof ErrorHandler.ErrorPageMapper) {
              String error_page =
                  ((ErrorHandler.ErrorPageMapper) eh)
                      .getErrorPage(
                          (HttpServletRequest) _state.getAsyncContextEvent().getSuppliedRequest());
              if (error_page != null) _state.getAsyncContextEvent().setDispatchPath(error_page);
            }
          } else _request.setDispatcherType(DispatcherType.ASYNC);
          getServer().handleAsync(this);
        }
      } catch (Error e) {
        if ("ContinuationThrowable".equals(e.getClass().getSimpleName())) LOG.ignore(e);
        else {
          error = true;
          throw e;
        }
      } catch (Exception e) {
        error = true;
        if (e instanceof EofException) LOG.debug(e);
        else LOG.warn(String.valueOf(_uri), e);
        _state.error(e);
        _request.setHandled(true);
        handleException(e);
      } finally {
        if (error && _state.isAsyncStarted()) _state.errorComplete();
        next = _state.unhandle();
      }
    }

    if (threadName != null && LOG.isDebugEnabled()) Thread.currentThread().setName(threadName);
    setCurrentHttpChannel(null);

    if (next == Next.COMPLETE) {
      try {
        _state.completed();

        if (!_response.isCommitted() && !_request.isHandled()) _response.sendError(404);
        else
          // Complete generating the response
          _response.closeOutput();
      } catch (EofException | ClosedChannelException e) {
        LOG.debug(e);
      } catch (Exception e) {
        LOG.warn("complete failed", e);
      } finally {
        _request.setHandled(true);
        _transport.completed();
      }
    }

    LOG.debug("{} handle exit, result {}", this, next);

    return next != Next.WAIT;
  }
  protected void commit(ByteBuffer content, boolean complete, Callback callback) {
    // Are we excluding because of status?
    Response response = _channel.getResponse();
    int sc = response.getStatus();
    if (sc > 0 && (sc < 200 || sc == 204 || sc == 205 || sc >= 300)) {
      LOG.debug("{} exclude by status {}", this, sc);
      noCompression();

      if (sc == 304) {
        String request_etags =
            (String) _channel.getRequest().getAttribute("o.e.j.s.h.gzip.GzipHandler.etag");
        String response_etag = response.getHttpFields().get(HttpHeader.ETAG);
        if (request_etags != null && response_etag != null) {
          String response_etag_gzip = etagGzip(response_etag);
          if (request_etags.contains(response_etag_gzip))
            response.getHttpFields().put(HttpHeader.ETAG, response_etag_gzip);
        }
      }

      _interceptor.write(content, complete, callback);
      return;
    }

    // Are we excluding because of mime-type?
    String ct = response.getContentType();
    if (ct != null) {
      ct = MimeTypes.getContentTypeWithoutCharset(ct);
      if (!_factory.isMimeTypeGzipable(StringUtil.asciiToLowerCase(ct))) {
        LOG.debug("{} exclude by mimeType {}", this, ct);
        noCompression();
        _interceptor.write(content, complete, callback);
        return;
      }
    }

    // Has the Content-Encoding header already been set?
    HttpFields fields = response.getHttpFields();
    String ce = fields.get(HttpHeader.CONTENT_ENCODING);
    if (ce != null) {
      LOG.debug("{} exclude by content-encoding {}", this, ce);
      noCompression();
      _interceptor.write(content, complete, callback);
      return;
    }

    // Are we the thread that commits?
    if (_state.compareAndSet(GZState.MIGHT_COMPRESS, GZState.COMMITTING)) {
      // We are varying the response due to accept encoding header.
      if (_vary != null) {
        if (fields.contains(HttpHeader.VARY)) fields.addCSV(HttpHeader.VARY, _vary.getValues());
        else fields.add(_vary);
      }

      long content_length = response.getContentLength();
      if (content_length < 0 && complete) content_length = content.remaining();

      _deflater = _factory.getDeflater(_channel.getRequest(), content_length);

      if (_deflater == null) {
        LOG.debug("{} exclude no deflater", this);
        _state.set(GZState.NOT_COMPRESSING);
        _interceptor.write(content, complete, callback);
        return;
      }

      fields.put(GZIP._contentEncoding);
      _crc.reset();
      _buffer = _channel.getByteBufferPool().acquire(_bufferSize, false);
      BufferUtil.fill(_buffer, GZIP_HEADER, 0, GZIP_HEADER.length);

      // Adjust headers
      response.setContentLength(-1);
      String etag = fields.get(HttpHeader.ETAG);
      if (etag != null) fields.put(HttpHeader.ETAG, etagGzip(etag));

      LOG.debug("{} compressing {}", this, _deflater);
      _state.set(GZState.COMPRESSING);

      gzip(content, complete, callback);
    } else callback.failed(new WritePendingException());
  }
Example #14
0
  @Override
  public void log(Request request, Response response) {
    // copied almost entirely from NCSARequestLog
    final StringBuilder buf = new StringBuilder(256);
    String address = request.getHeader(HttpHeaders.X_FORWARDED_FOR);
    if (address == null) {
      address = request.getRemoteAddr();
    }

    buf.append(address);
    buf.append(" - ");
    final Authentication authentication = request.getAuthentication();
    if (authentication instanceof Authentication.User) {
      buf.append(
          ((Authentication.User) authentication).getUserIdentity().getUserPrincipal().getName());
    } else {
      buf.append('-');
    }

    buf.append(" [");
    buf.append(dateCache.format(request.getTimeStamp()));

    buf.append("] \"");
    buf.append(request.getMethod());
    buf.append(' ');
    buf.append(request.getUri().toString());
    buf.append(' ');
    buf.append(request.getProtocol());
    buf.append("\" ");

    // TODO: Handle async requests?
    // e.g. if (request.getAsyncContinuation().isInitial())
    assert !request.isAsyncStarted();

    int status = response.getStatus();
    if (status <= 0) {
      if (request.isHandled()) {
        status = 200;
      } else {
        status = 404;
      }
    }
    buf.append((char) ('0' + ((status / 100) % 10)));
    buf.append((char) ('0' + ((status / 10) % 10)));
    buf.append((char) ('0' + (status % 10)));

    final long responseLength = response.getContentCount();
    if (responseLength >= 0) {
      buf.append(' ');
      if (responseLength > 99999) {
        buf.append(responseLength);
      } else {
        if (responseLength > 9999) {
          buf.append((char) ('0' + ((responseLength / 10000) % 10)));
        }
        if (responseLength > 999) {
          buf.append((char) ('0' + ((responseLength / 1000) % 10)));
        }
        if (responseLength > 99) {
          buf.append((char) ('0' + ((responseLength / 100) % 10)));
        }
        if (responseLength > 9) {
          buf.append((char) ('0' + ((responseLength / 10) % 10)));
        }
        buf.append((char) ('0' + (responseLength % 10)));
      }
    } else {
      buf.append(" -");
    }

    final long now = System.currentTimeMillis();
    final long dispatchTime = request.getDispatchTime();

    buf.append(' ');
    buf.append(now - ((dispatchTime == 0) ? request.getTimeStamp() : dispatchTime));

    buf.append(' ');
    buf.append(now - request.getTimeStamp());

    System.out.println(buf.toString());
  }