preliminary (buggy) support for cookies
authoradam <adam@megacz.com>
Fri, 3 Sep 2004 00:46:40 +0000 (00:46 +0000)
committeradam <adam@megacz.com>
Fri, 3 Sep 2004 00:46:40 +0000 (00:46 +0000)
darcs-hash:20040903004640-5007d-309d872baf225670174cfa1e24b20e9e6ef437d8.gz

src/org/ibex/net/HTTP.java

index 18c4069..30de701 100644 (file)
@@ -16,16 +16,97 @@ public class HTTP {
     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); } }
 
@@ -63,7 +144,7 @@ public class HTTP {
      *  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;
@@ -71,7 +152,7 @@ public class HTTP {
         synchronized(this) {
             try {
                 connect();
-                sendRequest(contentType, content);
+                sendRequest(contentType, content, referer, cookies);
             } catch (IOException e) {
                 reset();
                 throw e;
@@ -91,13 +172,13 @@ public class HTTP {
             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();
@@ -115,7 +196,7 @@ public class HTTP {
                     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)
@@ -361,7 +442,7 @@ public class HTTP {
         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) {
@@ -576,7 +657,7 @@ public class HTTP {
     // 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
@@ -610,6 +691,7 @@ public class HTTP {
             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;