/* Handle a request from a connection.
   * Called to handle a request on the connection when either the header has been received,
   * or after the entire request has been received (for short requests of known length), or
   * on the dispatch of an async request.
   */
  public void handleAsync(AbstractHttpConnection connection) throws IOException, ServletException {
    final AsyncContinuation async = connection.getRequest().getAsyncContinuation();
    final AsyncContinuation.AsyncEventState state = async.getAsyncEventState();

    final Request baseRequest = connection.getRequest();
    final String path = state.getPath();

    if (path != null) {
      // this is a dispatch with a path
      final String contextPath = state.getServletContext().getContextPath();
      HttpURI uri = new HttpURI(URIUtil.addPaths(contextPath, path));
      baseRequest.setUri(uri);
      baseRequest.setRequestURI(null);
      baseRequest.setPathInfo(baseRequest.getRequestURI());
      if (uri.getQuery() != null) baseRequest.mergeQueryString(uri.getQuery());
    }

    final String target = baseRequest.getPathInfo();
    final HttpServletRequest request = (HttpServletRequest) async.getRequest();
    final HttpServletResponse response = (HttpServletResponse) async.getResponse();

    if (LOG.isDebugEnabled()) {
      LOG.debug("REQUEST " + target + " on " + connection);
      handle(target, baseRequest, request, response);
      LOG.debug("RESPONSE " + target + "  " + connection.getResponse().getStatus());
    } else handle(target, baseRequest, request, response);
  }
示例#2
0
  @Override
  public boolean startRequest(
      HttpMethod httpMethod, String method, ByteBuffer uri, HttpVersion version) {
    _expect = false;
    _expect100Continue = false;
    _expect102Processing = false;

    if (_request.getTimeStamp() == 0) _request.setTimeStamp(System.currentTimeMillis());
    _request.setMethod(httpMethod, method);

    if (httpMethod == HttpMethod.CONNECT)
      _uri.parseConnect(uri.array(), uri.arrayOffset() + uri.position(), uri.remaining());
    else _uri.parse(uri.array(), uri.arrayOffset() + uri.position(), uri.remaining());
    _request.setUri(_uri);

    String path;
    try {
      path = _uri.getDecodedPath();
    } catch (Exception e) {
      LOG.warn("Failed UTF-8 decode for request path, trying ISO-8859-1");
      LOG.ignore(e);
      path = _uri.getDecodedPath(StandardCharsets.ISO_8859_1);
    }

    String info = URIUtil.canonicalPath(path);

    if (info == null) {
      if (path == null && _uri.getScheme() != null && _uri.getHost() != null) {
        info = "/";
        _request.setRequestURI("");
      } else {
        badMessage(400, null);
        return true;
      }
    }
    _request.setPathInfo(info);
    _version = version == null ? HttpVersion.HTTP_0_9 : version;
    _request.setHttpVersion(_version);

    return false;
  }
示例#3
0
  /** Invoked by the HTTPClient. */
  public int requestHandler(Request req, Response[] resp) {
    HTTPConnection con = req.getConnection();
    URI new_loc, cur_loc;

    // check for retries

    HttpOutputStream out = req.getStream();
    if (out != null && deferred_redir_list.get(out) != null) {
      copyFrom((RedirectionModule) deferred_redir_list.remove(out));
      req.copyFrom(saved_req);

      if (new_con) return REQ_NEWCON_RST;
      else return REQ_RESTART;
    }

    // handle permanent redirections

    try {
      cur_loc =
          new URI(
              new URI(con.getProtocol(), con.getHost(), con.getPort(), null), req.getRequestURI());
    } catch (ParseException pe) {
      throw new Error("HTTPClient Internal Error: unexpected exception '" + pe + "'", pe);
    }

    // handle permanent redirections

    Hashtable perm_redir_list =
        Util.getList(perm_redir_cntxt_list, req.getConnection().getContext());
    if ((new_loc = (URI) perm_redir_list.get(cur_loc)) != null) {
      /*
       * copy query if present in old url but not in new url. This isn't
       * strictly conforming, but some scripts fail to properly propagate the
       * query string to the Location header. Unfortunately it looks like we're
       * f****d either way: some scripts fail if you don't propagate the query
       * string, some fail if you do... God, don't you just love it when people
       * can't read a spec? Anway, since we can't get it right for all scripts
       * we opt to follow the spec. String nres = new_loc.getPathAndQuery(),
       * oquery = Util.getQuery(req.getRequestURI()), nquery =
       * Util.getQuery(nres); if (nquery == null && oquery != null) nres += "?"
       * + oquery;
       */
      String nres = new_loc.getPathAndQuery();
      req.setRequestURI(nres);

      try {
        lastURI = new URI(new_loc, nres);
      } catch (ParseException pe) {
        if (LOG.isTraceEnabled()) {
          LOG.trace("An exception occurred: " + pe.getMessage());
        }
      }

      if (LOG.isDebugEnabled())
        LOG.debug(
            "Matched request in permanent redirection list - redoing request to "
                + lastURI.toExternalForm());

      if (!con.isCompatibleWith(new_loc)) {
        try {
          con = new HTTPConnection(new_loc);
        } catch (ProtocolNotSuppException e) {
          throw new Error("HTTPClient Internal Error: unexpected " + "exception '" + e + "'", e);
        }

        con.setContext(req.getConnection().getContext());
        req.setConnection(con);
        return REQ_NEWCON_RST;
      } else {
        return REQ_RESTART;
      }
    }

    return REQ_CONTINUE;
  }
示例#4
0
  /** Invoked by the HTTPClient. */
  public int responsePhase2Handler(Response resp, Request req) throws IOException {
    /* handle various response status codes until satisfied */

    int sts = resp.getStatusCode();
    switch (sts) {
      case 302: // General (temporary) Redirection (handle like 303)

        /*
         * Note we only do this munging for POST and PUT. For GET it's not
         * necessary; for HEAD we probably want to do another HEAD. For all
         * others (i.e. methods from WebDAV, IPP, etc) it's somewhat unclear -
         * servers supporting those should really return a 307 or 303, but some
         * don't (guess who...), so we just don't touch those.
         */
        if (req.getMethod().equals("POST") || req.getMethod().equals("PUT")) {
          if (LOG.isDebugEnabled())
            LOG.debug(
                "Received status: " + sts + " " + resp.getReasonLine() + " - treating as 303");

          sts = 303;
        }

      case 301: // Moved Permanently
      case 303: // See Other (use GET)
      case 307: // Moved Temporarily (we mean it!)
        if (LOG.isDebugEnabled()) LOG.debug("Handling status: " + sts + " " + resp.getReasonLine());

        // the spec says automatic redirection may only be done if
        // the second request is a HEAD or GET.
        if (!req.getMethod().equals("GET") && !req.getMethod().equals("HEAD") && sts != 303) {
          if (LOG.isDebugEnabled())
            LOG.debug("Not redirected because method is neither HEAD nor GET");

          if (sts == 301 && resp.getHeader("Location") != null)
            update_perm_redir_list(req, resLocHdr(resp.getHeader("Location"), req));

          resp.setEffectiveURI(lastURI);
          return RSP_CONTINUE;
        }

      case 305: // Use Proxy
      case 306: // Switch Proxy
        if (sts == 305 || sts == 306) {
          if (LOG.isDebugEnabled())
            LOG.debug("Handling status: " + sts + " " + resp.getReasonLine());
        }

        // Don't accept 305 from a proxy
        if (sts == 305 && req.getConnection().getProxyHost() != null) {
          if (LOG.isDebugEnabled()) LOG.debug("305 ignored because a proxy is already in use");

          resp.setEffectiveURI(lastURI);
          return RSP_CONTINUE;
        }

        /*
         * the level is a primitive way of preventing infinite redirections.
         * RFC-2068 set the max to 5, but RFC-2616 has loosened this. Since some
         * sites (notably M$) need more levels, this is now set to the
         * (arbitrary) value of 15 (god only knows why they need to do even 5
         * redirections...).
         */
        if (level >= 15 || resp.getHeader("Location") == null) {
          if (LOG.isDebugEnabled()) {
            if (level >= 15) LOG.debug("Not redirected because of too many levels of redirection");
            else LOG.debug("Not redirected because no Location header was present");
          }

          resp.setEffectiveURI(lastURI);
          return RSP_CONTINUE;
        }
        level++;

        URI loc = resLocHdr(resp.getHeader("Location"), req);

        HTTPConnection mvd;
        String nres;
        new_con = false;

        if (sts == 305) {
          mvd =
              new HTTPConnection(
                  req.getConnection().getProtocol(),
                  req.getConnection().getHost(),
                  req.getConnection().getPort());
          mvd.setCurrentProxy(loc.getHost(), loc.getPort());
          mvd.setContext(req.getConnection().getContext());
          new_con = true;

          nres = req.getRequestURI();

          /*
           * There was some discussion about this, and especially Foteos
           * Macrides (Lynx) said a 305 should also imply a change to GET (for
           * security reasons) - see the thread starting at
           * http://www.ics.uci.edu/pub/ietf/http/hypermail/1997q4/0351.html
           * However, this is not in the latest draft, but since I agree with
           * Foteos we do it anyway...
           */
          req.setMethod("GET");
          req.setData(null);
          req.setStream(null);
        } else if (sts == 306) {
          // We'll have to wait for Josh to create a new spec here.
          return RSP_CONTINUE;
        } else {
          if (req.getConnection().isCompatibleWith(loc)) {
            mvd = req.getConnection();
            nres = loc.getPathAndQuery();
          } else {
            try {
              mvd = new HTTPConnection(loc);
              nres = loc.getPathAndQuery();
            } catch (ProtocolNotSuppException e) {
              if (req.getConnection().getProxyHost() == null
                  || !loc.getScheme().equalsIgnoreCase("ftp")) return RSP_CONTINUE;

              // We're using a proxy and the protocol is ftp -
              // maybe the proxy will also proxy ftp...
              mvd =
                  new HTTPConnection(
                      "http",
                      req.getConnection().getProxyHost(),
                      req.getConnection().getProxyPort());
              mvd.setCurrentProxy(null, 0);
              nres = loc.toExternalForm();
            }

            mvd.setContext(req.getConnection().getContext());
            new_con = true;
          }

          /*
           * copy query if present in old url but not in new url. This isn't
           * strictly conforming, but some scripts fail to propagate the query
           * properly to the Location header. See comment on line 126. String
           * oquery = Util.getQuery(req.getRequestURI()), nquery =
           * Util.getQuery(nres); if (nquery == null && oquery != null) nres +=
           * "?" + oquery;
           */

          if (sts == 303) {
            // 303 means "use GET"

            if (!req.getMethod().equals("HEAD")) req.setMethod("GET");
            req.setData(null);
            req.setStream(null);
          } else {
            // If they used an output stream then they'll have
            // to do the resend themselves
            if (req.getStream() != null) {
              if (!HTTPConnection.deferStreamed) {
                if (LOG.isDebugEnabled())
                  LOG.debug("Status " + sts + " not handled - request has an output stream");

                return RSP_CONTINUE;
              }

              saved_req = (Request) req.clone();
              deferred_redir_list.put(req.getStream(), this);
              req.getStream().reset();
              resp.setRetryRequest(true);
            }

            if (sts == 301) {
              // update permanent redirection list
              try {
                update_perm_redir_list(req, new URI(loc, nres));
              } catch (ParseException pe) {
                throw new Error(
                    "HTTPClient Internal Error: " + "unexpected exception '" + pe + "'", pe);
              }
            }
          }

          // Adjust Referer, if present
          NVPair[] hdrs = req.getHeaders();
          for (int idx = 0; idx < hdrs.length; idx++)
            if (hdrs[idx].getName().equalsIgnoreCase("Referer")) {
              HTTPConnection con = req.getConnection();
              hdrs[idx] = new NVPair("Referer", con + req.getRequestURI());
              break;
            }
        }

        req.setConnection(mvd);
        req.setRequestURI(nres);

        try {
          resp.getInputStream().close();
        } catch (IOException ioe) {
          if (LOG.isTraceEnabled()) {
            LOG.trace("An exception occurred: " + ioe.getMessage());
          }
        }

        if (sts != 305 && sts != 306) {
          try {
            lastURI = new URI(loc, nres);
          } catch (ParseException pe) {
            if (LOG.isTraceEnabled()) {
              LOG.trace("An exception occurred: " + pe.getMessage());
            }
          }

          if (LOG.isDebugEnabled())
            LOG.debug(
                "Request redirected to "
                    + lastURI.toExternalForm()
                    + " using method "
                    + req.getMethod());
        } else {
          if (LOG.isDebugEnabled())
            LOG.debug(
                "Resending request using "
                    + "proxy "
                    + mvd.getProxyHost()
                    + ":"
                    + mvd.getProxyPort());
        }

        if (req.getStream() != null) return RSP_CONTINUE;
        else if (new_con) return RSP_NEWCON_REQ;
        else return RSP_REQUEST;

      default:
        return RSP_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);
    }
  }