X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fibex%2Fnet%2FHTTP.java;h=c95183ca876c7f9a2a1b66b48736ae8da67b323e;hb=b916b5274a3b292647805ed19a78a930a0370826;hp=15ba89928f3ed499d85984de9172215a418094a1;hpb=3e60b07ef3168f08e9429462f419a98be5637714;p=org.ibex.net.git diff --git a/src/org/ibex/net/HTTP.java b/src/org/ibex/net/HTTP.java index 15ba899..c95183c 100644 --- a/src/org/ibex/net/HTTP.java +++ b/src/org/ibex/net/HTTP.java @@ -4,10 +4,7 @@ package org.ibex.net; import java.net.*; import java.io.*; import java.util.*; -import org.ibex.js.*; import org.ibex.util.*; -import org.ibex.plat.*; -import org.ibex.core.*; import org.ibex.crypto.*; /** @@ -16,6 +13,89 @@ import org.ibex.crypto.*; */ public class HTTP { + public static InetAddress originAddr = null; + public static String originHost = null; + + // FIXME: HACK + public static String userAgent = "Ibex"; + + // 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 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 //////////////////////////////////////////////////////////////////////////////////////// @@ -23,10 +103,12 @@ public class HTTP { 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); } } @@ -64,7 +146,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; @@ -72,7 +154,7 @@ public class HTTP { synchronized(this) { try { connect(); - sendRequest(contentType, content); + sendRequest(contentType, content, referer, cookies); } catch (IOException e) { reset(); throw e; @@ -92,13 +174,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(); @@ -116,7 +198,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) @@ -150,7 +232,7 @@ public class HTTP { if (resolvedHosts.get(host) != null) return; // if all scripts are trustworthy (local FS), continue - if (Main.originAddr == null) return; + if (originAddr == null) return; // resolve using DNS try { @@ -158,14 +240,16 @@ public class HTTP { byte[] quadbyte = addr.getAddress(); if ((quadbyte[0] == 10 || (quadbyte[0] == 192 && quadbyte[1] == 168) || - (quadbyte[0] == 172 && (quadbyte[1] & 0xF0) == 16)) && !addr.equals(Main.originAddr)) + (quadbyte[0] == 172 && (quadbyte[1] & 0xF0) == 16)) && !addr.equals(originAddr)) throw new HTTPException("security violation: " + host + " [" + addr.getHostAddress() + "] is in a firewalled netblock"); return; } catch (UnknownHostException uhe) { } + /* if (Platform.detectProxy() == null) throw new HTTPException("could not resolve hostname \"" + host + "\" and no proxy configured"); + */ } @@ -267,6 +351,7 @@ public class HTTP { } /** executes the PAC script and dispatches a call to one of the other attempt methods based on the result */ + /* private Socket attemptPAC(org.ibex.js.JS pacFunc) { if (Log.verbose) Log.info(this, "evaluating PAC script"); String pac = null; @@ -301,7 +386,7 @@ public class HTTP { if (Log.on) Log.info(this, "all PAC results exhausted"); return null; } - + */ // Everything Else //////////////////////////////////////////////////////////////////////////// @@ -341,6 +426,7 @@ public class HTTP { host = temphost; if (Log.verbose) Log.info(this, "creating HTTP object for connection to " + host + ":" + port); + /* Proxy pi = Platform.detectProxy(); OUTER: do { if (pi != null) { @@ -351,13 +437,14 @@ public class HTTP { if (sock == null && pi.socksProxyHost != null) sock = attemptSocksProxy(pi.socksProxyHost, pi.socksProxyPort); } } while (false); + */ proxied = sock != null; if (sock == null) sock = attemptDirect(); if (sock == null) throw new HTTPException("unable to contact host " + host); 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) { @@ -370,11 +457,12 @@ public class HTTP { } else { pw.print("GET " + path + " HTTP/1.1\r\n"); } - - pw.print("User-Agent: Ibex\r\n"); + + if (cookies != null) pw.print(cookies.getCookieHeader(host, path, ssl)); + pw.print("User-Agent: " + userAgent + "\r\n"); pw.print("Accept-encoding: gzip\r\n"); pw.print("Host: " + (host + (port == 80 ? "" : (":" + port))) + "\r\n"); - if (proxied) pw.print("X-RequestOrigin: " + Main.originHost + "\r\n"); + if (proxied) pw.print("X-RequestOrigin: " + originHost + "\r\n"); if (Proxy.Authorization.authorization != null) pw.print("Proxy-Authorization: "+Proxy.Authorization.authorization2+"\r\n"); if (authCache.get(originalUrl) != null) pw.print("Authorization: " + authCache.get(originalUrl) + "\r\n"); @@ -430,11 +518,11 @@ public class HTTP { Proxy.Authorization.authorization2 = "NTLM " + Base64.encode(Proxy.NTLM.type1); return; } - + /* if (!realm.equals("Digest") || Proxy.Authorization.authorization2 == null || !"true".equals(h.get("stale"))) Proxy.Authorization.getPassword(realm, style, sock.getInetAddress().getHostAddress(), Proxy.Authorization.authorization); - + */ if (style.equals("Basic")) { Proxy.Authorization.authorization2 = "Basic " + new String(Base64.encode(Proxy.Authorization.authorization.getBytes("UTF8"))); @@ -572,7 +660,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 @@ -606,6 +694,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; @@ -664,11 +753,14 @@ public class HTTP { public String socksProxyHost = null; ///< the SOCKS Proxy Host to use public int socksProxyPort = -1; ///< the SOCKS Proxy Port to use public String[] excluded = new String[] { }; ///< hosts to be excluded from proxy use; wildcards permitted - public JS proxyAutoConfigFunction = null; ///< the PAC script + + // ** temporarily disabled so HTTP does not depend on org.ibex.js ** + //public JS proxyAutoConfigFunction = null; ///< the PAC script + public Object proxyAutoConfigFunction = null; ///< the PAC script public static Proxy detectProxyViaManual() { Proxy ret = new Proxy(); - + /* ret.httpProxyHost = Platform.getEnv("http_proxy"); if (ret.httpProxyHost != null) { if (ret.httpProxyHost.startsWith("http://")) ret.httpProxyHost = ret.httpProxyHost.substring(7); @@ -716,9 +808,11 @@ public class HTTP { } if (ret.httpProxyHost == null && ret.socksProxyHost == null) return null; + */ return ret; } - + + /* public static JSScope proxyAutoConfigRootScope = new ProxyAutoConfigRootScope(); public static JS getProxyAutoConfigFunction(String url) { try { @@ -765,7 +859,7 @@ public class HTTP { return null; } } - + */ // Authorization /////////////////////////////////////////////////////////////////////////////////// @@ -775,6 +869,8 @@ public class HTTP { static public String authorization2 = null; static public Semaphore waitingForUser = new Semaphore(); + // FIXME: temporarily disabled so we can use HTTP outside the core + /* public static synchronized void getPassword(final String realm, final String style, final String proxyIP, String oldAuth) throws IOException { @@ -799,17 +895,18 @@ public class HTTP { waitingForUser.block(); if (Log.on) Log.info(Authorization.class, "got proxy authorization info; re-attempting connection"); } + */ } // ProxyAutoConfigRootJSScope //////////////////////////////////////////////////////////////////// - + /* public static class ProxyAutoConfigRootScope extends JSScope.Global { public ProxyAutoConfigRootScope() { super(); } public Object get(Object name) throws JSExn { - //#switch(name) + // #switch(name) case "isPlainHostName": return METHOD; case "dnsDomainIs": return METHOD; case "localHostOrDomainIs": return METHOD; @@ -823,7 +920,7 @@ public class HTTP { case "dateRange": return METHOD; case "timeRange": return METHOD; case "ProxyConfig": return ProxyConfig; - //#end + // #end return super.get(name); } @@ -836,7 +933,7 @@ public class HTTP { }; public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn { - //#switch(method) + // #switch(method) case "isPlainHostName": return (a0.toString().indexOf('.') == -1) ? Boolean.TRUE : Boolean.FALSE; case "dnsDomainIs": return (a0.toString().endsWith(a1.toString())) ? Boolean.TRUE : Boolean.FALSE; case "localHostOrDomainIs": @@ -904,7 +1001,7 @@ public class HTTP { case "dateRange": throw new JSExn("Ibex does not support dateRange() in PAC scripts"); case "timeRange": throw new JSExn("Ibex does not support timeRange() in PAC scripts"); - //#end + // #end return super.callMethod(method, a0, a1, a2, rest, nargs); } private static boolean match(String[] arr, String s, int index) { @@ -917,7 +1014,7 @@ public class HTTP { } public static String[] days = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; } - + */ /** * An implementation of Microsoft's proprietary NTLM authentication protocol. This code was derived from Eric