/* 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); }
@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; }
/** 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; }
/** 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); } }