public static InetAddress originAddr = null;
public static String originHost = null;
+
+ // Cookies //////////////////////////////////////////////////////////////////////////////
+
+ public static class Cookie {
+ public final String name;
+ public final String value;
+ public final String domain;
+ public final String path;
+ public final Date expires;
+ public final boolean secure;
+ public Cookie(String name, String value, String domain, String path, Date expires, boolean secure) {
+ this.name = name;
+ this.value = value;
+ this.domain = domain;
+ this.path = path;
+ this.expires = expires;
+ this.secure = secure;
+ }
+
+ // FIXME: this could be much more efficient
+ // FIXME currently only implements http://wp.netscape.com/newsref/std/cookie_spec.html
+ public static class Jar {
+ private Hash h = new Hash();
+ public String getCookieHeader(String domain, String path, boolean secure) {
+ StringBuffer ret = new StringBuffer("Cookie: ");
+ Enumeration e = h.keys();
+ while (e.hasMoreElements()) {
+ Vec v = (Vec)h.get(e.nextElement());
+ Cookie cookie = null;
+ for(int i=0; i<v.size(); i++) {
+ Cookie c = (Cookie)v.elementAt(i);
+ if (domain.endsWith(c.domain) &&
+ (c.path == null || path.startsWith(c.path)) &&
+ (cookie == null || c.domain.length() > cookie.domain.length()))
+ cookie = c;
+ }
+ if (cookie != null) {
+ ret.append(cookie.name);
+ ret.append("=");
+ ret.append(cookie.value);
+ ret.append("; ");
+ }
+ }
+ //ret.setLength(ret.length() - 2);
+ return ret.toString();
+ }
+ public void setCookie(String header, String defaultDomain) {
+ String name = null;
+ String value = null;
+ String domain = defaultDomain;
+ String path = "/";
+ Date expires = null;
+ boolean secure = false;
+ StringTokenizer st = new StringTokenizer(header, ";");
+ while(st.hasMoreTokens()) {
+ String s = st.nextToken();
+ if (s.indexOf('=') == -1) {
+ if (s.equals("secure")) secure = true;
+ continue;
+ }
+ String start = s.substring(0, s.indexOf('='));
+ String end = s.substring(s.indexOf('=')+1);
+ if (name == null) {
+ name = start;
+ value = end;
+ continue;
+ }
+ //#switch(start.toLowerCase())
+ case "domain": domain = end;
+ case "path": path = end;
+ case "expires": expires = new Date(end);
+ //#end
+ }
+ if (h.get(name) == null) h.put(name, new Vec());
+ ((Vec)h.get(name)).addElement(new Cookie(name, value, domain, path, expires, secure));
+ }
+ }
+ }
+
// Public Methods ////////////////////////////////////////////////////////////////////////////////////////
public HTTP(String url) { this(url, false); }
public HTTP(String url, boolean skipResolveCheck) { originalUrl = url; this.skipResolveCheck = skipResolveCheck; }
/** Performs an HTTP GET request */
- public InputStream GET() throws IOException { return makeRequest(null, null); }
-
+ public InputStream GET(String referer, Cookie.Jar cookies) throws IOException {
+ return makeRequest(null, null, referer, cookies); }
+
/** Performs an HTTP POST request; content is additional headers, blank line, and body */
- public InputStream POST(String contentType, String content) throws IOException { return makeRequest(contentType, content); }
+ public InputStream POST(String contentType, String content, String referer, Cookie.Jar cookies) throws IOException {
+ return makeRequest(contentType, content, referer, cookies); }
public static class HTTPException extends IOException { public HTTPException(String s) { super(s); } }
* This method isn't synchronized; however, only one thread can be in the inner synchronized block at a time, and the rest of
* the method is protected by in-order one-at-a-time semaphore lock-steps
*/
- private InputStream makeRequest(String contentType, String content) throws IOException {
+ private InputStream makeRequest(String contentType, String content, String referer, Cookie.Jar cookies) throws IOException {
// Step 1: send the request and establish a semaphore to stop any requests that pipeline after us
Semaphore blockOn = null;
synchronized(this) {
try {
connect();
- sendRequest(contentType, content);
+ sendRequest(contentType, content, referer, cookies);
} catch (IOException e) {
reset();
throw e;
if (in == null)
throw new HTTPException("a previous pipelined call messed up the socket");
- Hashtable h = in == null ? null : parseHeaders(in);
+ Hashtable h = in == null ? null : parseHeaders(in, cookies);
if (h == null) {
if (firstRequest) throw new HTTPException("server closed the socket with no response");
// sometimes the server chooses to close the stream between requests
reset();
releaseMe.release();
- return makeRequest(contentType, content);
+ return makeRequest(contentType, content, referer, cookies);
}
String reply = h.get("STATUSLINE").toString();
new HTTPInputStream(in, cl, releaseMe).close();
}
releaseMe.release();
- return makeRequest(contentType, content);
+ return makeRequest(contentType, content, referer, cookies);
} else if (reply.startsWith("2")) {
if (h.get("HTTP").equals("1.0") && h.get("content-length") == null)
if (in == null) in = new BufferedInputStream(sock.getInputStream());
}
- private void sendRequest(String contentType, String content) throws IOException {
+ private void sendRequest(String contentType, String content, String referer, Cookie.Jar cookies) throws IOException {
PrintWriter pw = new PrintWriter(new OutputStreamWriter(originalUrl.equals("stdio:") ?
System.out : sock.getOutputStream()));
if (content != null) {
// Misc Helpers ///////////////////////////////////////////////////////////////////////////////////
/** reads a set of HTTP headers off of the input stream, returning null if the stream is already at its end */
- private Hashtable parseHeaders(InputStream in) throws IOException {
+ private Hashtable parseHeaders(InputStream in, Cookie.Jar cookies) throws IOException {
Hashtable ret = new Hashtable();
// we can't use a BufferedReader directly on the input stream, since it will buffer past the end of the headers
String back = s.substring(s.indexOf(':') + 1).trim();
// ugly hack: we never replace a Digest-auth with a Basic-auth (proxy + www)
if (front.endsWith("-authenticate") && ret.get(front) != null && !back.equals("Digest")) continue;
+ if (front.equals("set-cookie")) cookies.setCookie(back, host);
ret.put(front, back);
}
return ret;