Ejemplo n.º 1
0
  @Override
  public void reset() {
    resetForForward();
    _status = 200;
    _reason = null;
    _contentLength = -1;
    _fields.clear();

    String connection = _channel.getRequest().getHttpFields().getStringField(HttpHeader.CONNECTION);
    if (connection != null) {
      String[] values = connection.split(",");
      for (int i = 0; values != null && i < values.length; i++) {
        HttpHeaderValue cb = HttpHeaderValue.CACHE.get(values[0].trim());

        if (cb != null) {
          switch (cb) {
            case CLOSE:
              _fields.put(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.toString());
              break;

            case KEEP_ALIVE:
              if (HttpVersion.HTTP_1_0.is(_channel.getRequest().getProtocol()))
                _fields.put(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.toString());
              break;
            case TE:
              _fields.put(HttpHeader.CONNECTION, HttpHeaderValue.TE.toString());
              break;
          }
        }
      }
    }
  }
Ejemplo n.º 2
0
  /**
   * Asynchronous send of HTTP content.
   *
   * @param httpContent The HTTP content to send
   * @param callback The callback to use to notify success or failure
   */
  public void sendContent(HttpContent httpContent, Callback callback) {
    if (LOG.isDebugEnabled()) LOG.debug("sendContent(http={},{})", httpContent, callback);

    if (BufferUtil.hasContent(_aggregate)) {
      callback.failed(new IOException("cannot sendContent() after write()"));
      return;
    }
    if (_channel.isCommitted()) {
      callback.failed(new IOException("committed"));
      return;
    }

    while (true) {
      switch (_state.get()) {
        case OPEN:
          if (!_state.compareAndSet(OutputState.OPEN, OutputState.PENDING)) continue;
          break;

        case ERROR:
          callback.failed(new EofException(_onError));
          return;

        case CLOSED:
          callback.failed(new EofException("Closed"));
          return;

        default:
          throw new IllegalStateException();
      }
      break;
    }

    ByteBuffer buffer = _channel.useDirectBuffers() ? httpContent.getDirectBuffer() : null;
    if (buffer == null) buffer = httpContent.getIndirectBuffer();

    if (buffer != null) {
      sendContent(buffer, callback);
      return;
    }

    try {
      ReadableByteChannel rbc = httpContent.getReadableByteChannel();
      if (rbc != null) {
        // Close of the rbc is done by the async sendContent
        sendContent(rbc, callback);
        return;
      }

      InputStream in = httpContent.getInputStream();
      if (in != null) {
        sendContent(in, callback);
        return;
      }

      throw new IllegalArgumentException("unknown content for " + httpContent);
    } catch (Throwable th) {
      abort(th);
      callback.failed(th);
    }
  }
Ejemplo n.º 3
0
 /**
  * Blocking send of content.
  *
  * @param content The content to send.
  * @throws IOException
  */
 public void sendContent(ByteBuffer content) throws IOException {
   final BlockingCallback callback = _channel.getWriteBlockingCallback();
   if (content.hasArray() && content.limit() < content.capacity())
     content = content.asReadOnlyBuffer();
   _channel.write(content, true, callback);
   callback.block();
 }
Ejemplo n.º 4
0
  @Override
  public void flush() throws IOException {
    while (true) {
      switch (_state.get()) {
        case OPEN:
          if (BufferUtil.hasContent(_aggregate)) _channel.write(_aggregate, false);
          else _channel.write(BufferUtil.EMPTY_BUFFER, false);
          return;

        case ASYNC:
          throw new IllegalStateException("isReady() not called");

        case READY:
          if (!_state.compareAndSet(State.READY, State.PENDING)) continue;
          new AsyncFlush().process();
          return;

        case PENDING:
        case UNREADY:
          throw new WritePendingException();

        case CLOSED:
          return;
      }
      break;
    }
  }
Ejemplo n.º 5
0
  @Override
  public void setWriteListener(WriteListener writeListener) {
    if (!_channel.getState().isAsync()) throw new IllegalStateException("!ASYNC");

    if (_state.compareAndSet(OutputState.OPEN, OutputState.READY)) {
      _writeListener = writeListener;
      if (_channel.getState().onWritePossible()) _channel.execute(_channel);
    } else throw new IllegalStateException();
  }
Ejemplo n.º 6
0
 protected ResponseInfo newResponseInfo() {
   if (_status == HttpStatus.NOT_SET_000) _status = HttpStatus.OK_200;
   return new ResponseInfo(
       _channel.getRequest().getHttpVersion(),
       _fields,
       getLongContentLength(),
       getStatus(),
       getReason(),
       _channel.getRequest().isHead());
 }
Ejemplo n.º 7
0
  @Override
  public void write(int b) throws IOException {
    _written += 1;
    boolean complete = _channel.getResponse().isAllContentWritten(_written);

    // Async or Blocking ?
    while (true) {
      switch (_state.get()) {
        case OPEN:
          if (_aggregate == null)
            _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), false);
          BufferUtil.append(_aggregate, (byte) b);

          // Check if all written or full
          if (complete || BufferUtil.isFull(_aggregate)) {
            BlockingCallback callback = _channel.getWriteBlockingCallback();
            _channel.write(_aggregate, complete, callback);
            callback.block();
            if (complete) closed();
          }
          break;

        case ASYNC:
          throw new IllegalStateException("isReady() not called");

        case READY:
          if (!_state.compareAndSet(State.READY, State.PENDING)) continue;

          if (_aggregate == null)
            _aggregate = _channel.getByteBufferPool().acquire(getBufferSize(), false);
          BufferUtil.append(_aggregate, (byte) b);

          // Check if all written or full
          if (!complete && !BufferUtil.isFull(_aggregate)) {
            if (!_state.compareAndSet(State.PENDING, State.ASYNC))
              throw new IllegalStateException();
            return;
          }

          // Do the asynchronous writing from the callback
          new AsyncFlush().process();
          return;

        case PENDING:
        case UNREADY:
          throw new WritePendingException();

        case CLOSED:
          throw new EofException("Closed");
      }
      break;
    }
  }
Ejemplo n.º 8
0
  @Override
  public void sendRedirect(String location) throws IOException {
    if (isIncluding()) return;

    if (location == null) throw new IllegalArgumentException();

    if (!URIUtil.hasScheme(location)) {
      StringBuilder buf = _channel.getRequest().getRootURL();
      if (location.startsWith("/")) buf.append(location);
      else {
        String path = _channel.getRequest().getRequestURI();
        String parent = (path.endsWith("/")) ? path : URIUtil.parentPath(path);
        location = URIUtil.addPaths(parent, location);
        if (location == null) throw new IllegalStateException("path cannot be above root");
        if (!location.startsWith("/")) buf.append('/');
        buf.append(location);
      }

      location = buf.toString();
      HttpURI uri = new HttpURI(location);
      String path = uri.getDecodedPath();
      String canonical = URIUtil.canonicalPath(path);
      if (canonical == null) throw new IllegalArgumentException();
      if (!canonical.equals(path)) {
        buf = _channel.getRequest().getRootURL();
        buf.append(URIUtil.encodePath(canonical));
        String param = uri.getParam();
        if (param != null) {
          buf.append(';');
          buf.append(param);
        }
        String query = uri.getQuery();
        if (query != null) {
          buf.append('?');
          buf.append(query);
        }
        String fragment = uri.getFragment();
        if (fragment != null) {
          buf.append('#');
          buf.append(fragment);
        }
        location = buf.toString();
      }
    }

    resetBuffer();
    setHeader(HttpHeader.LOCATION, location);
    setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
    complete();
  }
Ejemplo n.º 9
0
  /**
   * Called to indicate that the last write has been performed. It updates the state and performs
   * cleanup operations.
   */
  void closed() {
    while (true) {
      OutputState state = _state.get();
      switch (state) {
        case CLOSED:
          {
            return;
          }
        case UNREADY:
          {
            if (_state.compareAndSet(state, OutputState.ERROR))
              _writeListener.onError(
                  _onError == null ? new EofException("Async closed") : _onError);
            break;
          }
        default:
          {
            if (!_state.compareAndSet(state, OutputState.CLOSED)) break;

            try {
              _channel.getResponse().closeOutput();
            } catch (Throwable x) {
              if (LOG.isDebugEnabled()) LOG.debug(x);
              abort(x);
            } finally {
              releaseBuffer();
            }
            // Return even if an exception is thrown by closeOutput().
            return;
          }
      }
    }
  }
Ejemplo n.º 10
0
 /* Called to indicated that the output is already closed and the state needs to be updated to match */
 void closed() {
   State state = _state.get();
   while (state != State.CLOSED) {
     if (_state.compareAndSet(state, State.CLOSED)) {
       try {
         _channel.getResponse().closeOutput();
       } catch (IOException e) {
         LOG.debug(e);
         _channel.failed();
       }
       releaseBuffer();
       return;
     }
     state = _state.get();
   }
 }
Ejemplo n.º 11
0
  @Override
  public void setLocale(Locale locale) {
    if (locale == null || isCommitted() || isIncluding()) return;

    _locale = locale;
    _fields.put(HttpHeader.CONTENT_LANGUAGE, locale.toString().replace('_', '-'));

    if (_outputType != OutputType.NONE) return;

    if (_channel.getRequest().getContext() == null) return;

    String charset =
        _channel.getRequest().getContext().getContextHandler().getLocaleEncoding(locale);

    if (charset != null && charset.length() > 0 && _characterEncoding == null)
      setCharacterEncoding(charset);
  }
Ejemplo n.º 12
0
 @Override
 public void close() {
   State state = _state.get();
   while (state != State.CLOSED) {
     if (_state.compareAndSet(state, State.CLOSED)) {
       try {
         if (BufferUtil.hasContent(_aggregate))
           _channel.write(_aggregate, !_channel.getResponse().isIncluding());
         else _channel.write(BufferUtil.EMPTY_BUFFER, !_channel.getResponse().isIncluding());
       } catch (IOException e) {
         LOG.debug(e);
         _channel.failed();
       }
       releaseBuffer();
       return;
     }
     state = _state.get();
   }
 }
Ejemplo n.º 13
0
  public void write(ByteBuffer buffer) throws IOException {
    _written += buffer.remaining();
    boolean complete = _channel.getResponse().isAllContentWritten(_written);

    // Async or Blocking ?
    while (true) {
      switch (_state.get()) {
        case OPEN:
          // process blocking below
          break;

        case ASYNC:
          throw new IllegalStateException("isReady() not called");

        case READY:
          if (!_state.compareAndSet(State.READY, State.PENDING)) continue;

          // Do the asynchronous writing from the callback
          new AsyncWrite(buffer, complete).process();
          return;

        case PENDING:
        case UNREADY:
          throw new WritePendingException();

        case CLOSED:
          throw new EofException("Closed");
      }
      break;
    }

    // handle blocking write
    int len = BufferUtil.length(buffer);

    // flush any content from the aggregate
    if (BufferUtil.hasContent(_aggregate)) _channel.write(_aggregate, complete && len == 0);

    // write any remaining content in the buffer directly
    if (len > 0) _channel.write(buffer, complete);
    else if (complete) _channel.write(BufferUtil.EMPTY_BUFFER, complete);

    if (complete) closed();
  }
 public GzipHttpOutputInterceptor(
     GzipFactory factory, HttpChannel channel, HttpOutput.Interceptor next, boolean syncFlush) {
   this(
       factory,
       VARY_ACCEPT_ENCODING_USER_AGENT,
       channel.getHttpConfiguration().getOutputBufferSize(),
       channel,
       next,
       syncFlush);
 }
Ejemplo n.º 15
0
  @Override
  public void close() {
    while (true) {
      OutputState state = _state.get();
      switch (state) {
        case CLOSED:
          {
            return;
          }

        case ASYNC:
        case UNREADY:
        case PENDING:
          {
            if (!_state.compareAndSet(state, OutputState.CLOSED)) break;
            IOException ex = new IOException("Closed while Pending/Unready");
            LOG.warn(ex.toString());
            LOG.debug(ex);
            _channel.abort(ex);
          }
        default:
          {
            if (!_state.compareAndSet(state, OutputState.CLOSED)) break;

            try {
              write(
                  BufferUtil.hasContent(_aggregate) ? _aggregate : BufferUtil.EMPTY_BUFFER,
                  !_channel.getResponse().isIncluding());
            } catch (IOException x) {
              // Ignore it, it's been already logged in write().
            } finally {
              releaseBuffer();
            }
            // Return even if an exception is thrown by write().
            return;
          }
      }
    }
  }
Ejemplo n.º 16
0
  /**
   * Asynchronous send of content.
   *
   * @param httpContent The content to send
   * @param callback The callback to use to notify success or failure
   */
  public void sendContent(HttpContent httpContent, Callback callback) throws IOException {
    if (BufferUtil.hasContent(_aggregate)) throw new IOException("written");
    if (_channel.isCommitted()) throw new IOException("committed");

    while (true) {
      switch (_state.get()) {
        case OPEN:
          if (!_state.compareAndSet(State.OPEN, State.PENDING)) continue;
          break;
        case CLOSED:
          throw new EofException("Closed");
        default:
          throw new IllegalStateException();
      }
      break;
    }
    ByteBuffer buffer = _channel.useDirectBuffers() ? httpContent.getDirectBuffer() : null;
    if (buffer == null) buffer = httpContent.getIndirectBuffer();

    if (buffer != null) {
      sendContent(buffer, callback);
      return;
    }

    ReadableByteChannel rbc = httpContent.getReadableByteChannel();
    if (rbc != null) {
      // Close of the rbc is done by the async sendContent
      sendContent(rbc, callback);
      return;
    }

    InputStream in = httpContent.getInputStream();
    if (in != null) {
      sendContent(in, callback);
      return;
    }

    callback.failed(new IllegalArgumentException("unknown content for " + httpContent));
  }
 public GzipHttpOutputInterceptor(
     GzipFactory factory,
     HttpField vary,
     HttpChannel channel,
     HttpOutput.Interceptor next,
     boolean syncFlush) {
   this(
       factory,
       vary,
       channel.getHttpConfiguration().getOutputBufferSize(),
       channel,
       next,
       syncFlush);
 }
Ejemplo n.º 18
0
  /**
   * Called to signal async read isReady() has returned false. This indicates that there is no
   * content available to be consumed and that once the channel enteres the ASYNC_WAIT state it will
   * register for read interest by calling {@link HttpChannel#asyncReadFillInterested()} either from
   * this method or from a subsequent call to {@link #unhandle()}.
   */
  public void onReadUnready() {
    boolean interested = false;
    try (Locker.Lock lock = _locker.lock()) {
      if (DEBUG) LOG.debug("onReadUnready {}", toStringLocked());

      // We were already unready, this is not a state change, so do nothing
      if (!_asyncReadUnready) {
        _asyncReadUnready = true;
        _asyncReadPossible = false; // Assumes this has been checked in isReady() with lock held
        if (_state == State.ASYNC_WAIT) interested = true;
      }
    }

    if (interested) _channel.asyncReadFillInterested();
  }
Ejemplo n.º 19
0
  /**
   * Asynchronous send of content.
   *
   * @param content The content to send
   * @param callback The callback to use to notify success or failure
   */
  public void sendContent(ByteBuffer content, final Callback callback) {
    if (content.hasArray() && content.limit() < content.capacity())
      content = content.asReadOnlyBuffer();
    _channel.write(
        content,
        true,
        new Callback() {
          @Override
          public void succeeded() {
            closed();
            callback.succeeded();
          }

          @Override
          public void failed(Throwable x) {
            callback.failed(x);
          }
        });
  }
Ejemplo n.º 20
0
 public HttpOutput(HttpChannel channel) {
   _channel = channel;
   _interceptor = channel;
   _writeBlock =
       new SharedBlockingCallback() {
         @Override
         protected long getIdleTimeout() {
           long bto = getHttpChannel().getHttpConfiguration().getBlockingTimeout();
           if (bto > 0) return bto;
           if (bto < 0) return -1;
           return _channel.getIdleTimeout();
         }
       };
   HttpConfiguration config = channel.getHttpConfiguration();
   _bufferSize = config.getOutputBufferSize();
   _commitSize = config.getOutputAggregationSize();
   if (_commitSize > _bufferSize) {
     LOG.warn("OutputAggregationSize {} exceeds bufferSize {}", _commitSize, _bufferSize);
     _commitSize = _bufferSize;
   }
 }
Ejemplo n.º 21
0
 @Override
 public boolean isCommitted() {
   return _channel.isCommitted();
 }
Ejemplo n.º 22
0
  @Override
  public void print(String s) throws IOException {
    if (isClosed()) throw new IOException("Closed");

    write(s.getBytes(_channel.getResponse().getCharacterEncoding()));
  }
Ejemplo n.º 23
0
  @Override
  public void write(byte[] b, int off, int len) throws IOException {
    _written += len;
    boolean complete = _channel.getResponse().isAllContentWritten(_written);

    // Async or Blocking ?
    while (true) {
      switch (_state.get()) {
        case OPEN:
          // process blocking below
          break;

        case ASYNC:
          throw new IllegalStateException("isReady() not called");

        case READY:
          if (!_state.compareAndSet(OutputState.READY, OutputState.PENDING)) continue;

          // Should we aggregate?
          if (!complete && len <= _commitSize) {
            if (_aggregate == null)
              _aggregate =
                  _channel
                      .getByteBufferPool()
                      .acquire(getBufferSize(), _interceptor.isOptimizedForDirectBuffers());

            // YES - fill the aggregate with content from the buffer
            int filled = BufferUtil.fill(_aggregate, b, off, len);

            // return if we are not complete, not full and filled all the content
            if (filled == len && !BufferUtil.isFull(_aggregate)) {
              if (!_state.compareAndSet(OutputState.PENDING, OutputState.ASYNC))
                throw new IllegalStateException();
              return;
            }

            // adjust offset/length
            off += filled;
            len -= filled;
          }

          // Do the asynchronous writing from the callback
          new AsyncWrite(b, off, len, complete).iterate();
          return;

        case PENDING:
        case UNREADY:
          throw new WritePendingException();

        case ERROR:
          throw new EofException(_onError);

        case CLOSED:
          throw new EofException("Closed");

        default:
          throw new IllegalStateException();
      }
      break;
    }

    // handle blocking write

    // Should we aggregate?
    int capacity = getBufferSize();
    if (!complete && len <= _commitSize) {
      if (_aggregate == null)
        _aggregate =
            _channel
                .getByteBufferPool()
                .acquire(capacity, _interceptor.isOptimizedForDirectBuffers());

      // YES - fill the aggregate with content from the buffer
      int filled = BufferUtil.fill(_aggregate, b, off, len);

      // return if we are not complete, not full and filled all the content
      if (filled == len && !BufferUtil.isFull(_aggregate)) return;

      // adjust offset/length
      off += filled;
      len -= filled;
    }

    // flush any content from the aggregate
    if (BufferUtil.hasContent(_aggregate)) {
      write(_aggregate, complete && len == 0);

      // should we fill aggregate again from the buffer?
      if (len > 0 && !complete && len <= _commitSize && len <= BufferUtil.space(_aggregate)) {
        BufferUtil.append(_aggregate, b, off, len);
        return;
      }
    }

    // write any remaining content in the buffer directly
    if (len > 0) {
      ByteBuffer wrap = ByteBuffer.wrap(b, off, len);
      ByteBuffer view = wrap.duplicate();

      // write a buffer capacity at a time to avoid JVM pooling large direct buffers
      // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6210541
      while (len > getBufferSize()) {
        int p = view.position();
        int l = p + getBufferSize();
        view.limit(p + getBufferSize());
        write(view, false);
        len -= getBufferSize();
        view.limit(l + Math.min(len, getBufferSize()));
        view.position(l);
      }
      write(view, complete);
    } else if (complete) {
      write(BufferUtil.EMPTY_BUFFER, true);
    }

    if (complete) closed();
  }
Ejemplo n.º 24
0
 private void releaseBuffer() {
   if (_aggregate != null) {
     _channel.getConnector().getByteBufferPool().release(_aggregate);
     _aggregate = null;
   }
 }
Ejemplo n.º 25
0
 @Override
 public String toString() {
   return String.format(
       "%s %d %s%n%s",
       _channel.getRequest().getHttpVersion(), _status, _reason == null ? "" : _reason, _fields);
 }
Ejemplo n.º 26
0
 private void abort(Throwable failure) {
   closed();
   _channel.abort(failure);
 }
Ejemplo n.º 27
0
 public boolean isAllContentWritten() {
   return _channel.getResponse().isAllContentWritten(_written);
 }
Ejemplo n.º 28
0
 @Override
 public boolean hasOriginalRequestAndResponse() {
   HttpChannel channel = state().getHttpChannel();
   return channel.getRequest() == getRequest() && channel.getResponse() == getResponse();
 }
Ejemplo n.º 29
0
  /*
   * @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);
    }
  }
Ejemplo n.º 30
0
  /*
   * @see javax.servlet.RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
   */
  @Override
  public void include(ServletRequest request, ServletResponse response)
      throws ServletException, IOException {
    Request baseRequest =
        (request instanceof Request)
            ? ((Request) request)
            : HttpChannel.getCurrentHttpChannel().getRequest();

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

    final DispatcherType old_type = baseRequest.getDispatcherType();
    final Attributes old_attr = baseRequest.getAttributes();
    MultiMap<String> old_params = baseRequest.getParameters();
    try {
      baseRequest.setDispatcherType(DispatcherType.INCLUDE);
      baseRequest.getResponse().include();
      if (_named != null)
        _contextHandler.handle(
            _named, baseRequest, (HttpServletRequest) request, (HttpServletResponse) response);
      else {
        String query = _dQuery;

        if (query != null) {
          // force parameter extraction
          if (old_params == null) {
            baseRequest.extractParameters();
            old_params = baseRequest.getParameters();
          }

          MultiMap<String> parameters = new MultiMap<>();
          UrlEncoded.decodeTo(query, parameters, baseRequest.getCharacterEncoding(), -1);

          if (old_params != null) {
            // Merge parameters.
            parameters.addAllValues(old_params);
          }
          baseRequest.setParameters(parameters);
        }

        IncludeAttributes attr = new IncludeAttributes(old_attr);

        attr._requestURI = _uri;
        attr._contextPath = _contextHandler.getContextPath();
        attr._servletPath = null; // set by ServletHandler
        attr._pathInfo = _path;
        attr._query = query;

        baseRequest.setAttributes(attr);

        _contextHandler.handle(
            _path, baseRequest, (HttpServletRequest) request, (HttpServletResponse) response);
      }
    } finally {
      baseRequest.setAttributes(old_attr);
      baseRequest.getResponse().included();
      baseRequest.setParameters(old_params);
      baseRequest.setDispatcherType(old_type);
    }
  }