- Visual C++源码
- Visual Basic源码
- C++ Builder源码
- Java源码
- Delphi源码
- C/C++源码
- PHP源码
- Perl源码
- Python源码
- Asm源码
- Pascal源码
- Borland C++源码
- Others源码
- SQL源码
- VBScript源码
- JavaScript源码
- ASP/ASPX源码
- C#源码
- Flash/ActionScript源码
- matlab源码
- PowerBuilder源码
- LabView源码
- Flex源码
- MathCAD源码
- VBA源码
- IDL源码
- Lisp/Scheme源码
- VHDL源码
- Objective-C源码
- Fortran源码
- tcl/tk源码
- QT源码
RedirectionModule.java
资源名称:HTTP客户端.zip [点击查看]
上传用户:demmber
上传日期:2007-12-22
资源大小:717k
文件大小:14k
源码类别:
Java编程
开发平台:
Java
- /*
- * @(#)RedirectionModule.java 0.3-3 06/05/2001
- *
- * This file is part of the HTTPClient package
- * Copyright (C) 1996-2001 Ronald Tschal鋜
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307, USA
- *
- * For questions, suggestions, bug-reports, enhancement-requests etc.
- * I may be contacted at:
- *
- * ronald@innovation.ch
- *
- * The HTTPClient's home page is located at:
- *
- * http://www.innovation.ch/java/HTTPClient/
- *
- */
- package HTTPClient;
- import java.net.ProtocolException;
- import java.io.IOException;
- import java.util.Hashtable;
- /**
- * This module handles the redirection status codes 301, 302, 303, 305, 306
- * and 307.
- *
- * @version 0.3-3 06/05/2001
- * @author Ronald Tschal鋜
- */
- class RedirectionModule implements HTTPClientModule
- {
- /** a list of permanent redirections (301) */
- private static Hashtable perm_redir_cntxt_list = new Hashtable();
- /** a list of deferred redirections (used with Response.retryRequest()) */
- private static Hashtable deferred_redir_list = new Hashtable();
- /** the level of redirection */
- private int level;
- /** the url used in the last redirection */
- private URI lastURI;
- /** used for deferred redirection retries */
- private boolean new_con;
- /** used for deferred redirection retries */
- private Request saved_req;
- // Constructors
- /**
- * Start with level 0.
- */
- RedirectionModule()
- {
- level = 0;
- lastURI = null;
- saved_req = null;
- }
- // Methods
- /**
- * 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 + "'");
- }
- // 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 fucked 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)
- { }
- Log.write(Log.MODS, "RdirM: matched request in permanent " +
- "redirection list - redoing request to " +
- lastURI.toExternalForm());
- if (!con.isCompatibleWith(new_loc))
- {
- try
- { con = new HTTPConnection(new_loc); }
- catch (Exception e)
- {
- throw new Error("HTTPClient Internal Error: unexpected " +
- "exception '" + 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 void responsePhase1Handler(Response resp, RoRequest req)
- throws IOException
- {
- int sts = resp.getStatusCode();
- if (sts < 301 || sts > 307 || sts == 304)
- {
- if (lastURI != null) // it's been redirected
- resp.setEffectiveURI(lastURI);
- }
- }
- /**
- * 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"))
- {
- Log.write(Log.MODS, "RdirM: 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!)
- Log.write(Log.MODS, "RdirM: 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)
- {
- Log.write(Log.MODS, "RdirM: 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)
- Log.write(Log.MODS, "RdirM: Handling status: " + sts +
- " " + resp.getReasonLine());
- // Don't accept 305 from a proxy
- if (sts == 305 && req.getConnection().getProxyHost() != null)
- {
- Log.write(Log.MODS, "RdirM: 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 (level >= 15)
- Log.write(Log.MODS, "RdirM: not redirected because "+
- "of too many levels of redirection");
- else
- Log.write(Log.MODS, "RdirM: 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 (Exception 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)
- {
- Log.write(Log.MODS, "RdirM: 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 +
- "'");
- }
- }
- }
- // 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 (sts != 305 && sts != 306)
- {
- try
- { lastURI = new URI(loc, nres); }
- catch (ParseException pe)
- { /* ??? */ }
- Log.write(Log.MODS, "RdirM: request redirected to " +
- lastURI.toExternalForm() +
- " using method " + req.getMethod());
- }
- else
- {
- Log.write(Log.MODS, "RdirM: 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;
- }
- }
- /**
- * Invoked by the HTTPClient.
- */
- public void responsePhase3Handler(Response resp, RoRequest req)
- {
- }
- /**
- * Invoked by the HTTPClient.
- */
- public void trailerHandler(Response resp, RoRequest req)
- {
- }
- /**
- * Update the permanent redirection list.
- *
- * @param the original request
- * @param the new location
- */
- private static void update_perm_redir_list(RoRequest req, URI new_loc)
- {
- HTTPConnection con = req.getConnection();
- URI cur_loc = null;
- try
- {
- cur_loc = new URI(new URI(con.getProtocol(), con.getHost(), con.getPort(), null),
- req.getRequestURI());
- }
- catch (ParseException pe)
- { }
- if (!cur_loc.equals(new_loc))
- {
- Hashtable perm_redir_list =
- Util.getList(perm_redir_cntxt_list, con.getContext());
- perm_redir_list.put(cur_loc, new_loc);
- }
- }
- /**
- * The Location header field must be an absolute URI, but too many broken
- * servers use relative URIs. So, we always resolve relative to the
- * full request URI.
- *
- * @param loc the Location header field
- * @param req the Request to resolve relative URI's relative to
- * @return an absolute URI corresponding to the Location header field
- * @exception ProtocolException if the Location header field is completely
- * unparseable
- */
- private URI resLocHdr(String loc, RoRequest req) throws ProtocolException
- {
- try
- {
- URI base = new URI(req.getConnection().getProtocol(),
- req.getConnection().getHost(),
- req.getConnection().getPort(), null);
- base = new URI(base, req.getRequestURI());
- URI res = new URI(base, loc);
- if (res.getHost() == null)
- throw new ProtocolException("Malformed URL in Location header: `" + loc +
- "' - missing host field");
- return res;
- }
- catch (ParseException pe)
- {
- throw new ProtocolException("Malformed URL in Location header: `" + loc +
- "' - exception was: " + pe.getMessage());
- }
- }
- private void copyFrom(RedirectionModule other)
- {
- this.level = other.level;
- this.lastURI = other.lastURI;
- this.saved_req = other.saved_req;
- }
- }