*/
Semaphore okToRecieve = null;
+ /** true iff this is the first request to be made on this socket */
+ boolean firstRequest = true;
+
/** cache for resolveAndCheckIfFirewalled() */
static Hashtable resolvedHosts = new Hashtable();
- /** if any request encounters an IOException, the entire HTTP connection is invalidated */
- boolean invalid = false;
-
/** true iff we are allowed to skip the resolve check (only allowed when we're downloading the PAC script) */
boolean skipResolveCheck = false;
}
/** Performs an HTTP GET request */
- public HTTPInputStream GET() throws IOException { return makeRequest(null, null); }
+ public InputStream GET() throws IOException { return makeRequest(null, null); }
/** Performs an HTTP POST request; content is appended to the headers (so it should include a blank line to delimit the beginning of the body) */
- public HTTPInputStream POST(String contentType, String content) throws IOException { return makeRequest(contentType, content); }
+ public InputStream POST(String contentType, String content) throws IOException { return makeRequest(contentType, content); }
/**
* 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 HTTPInputStream makeRequest(String contentType, String content) throws IOException {
+ private InputStream makeRequest(String contentType, String content) throws IOException {
// Step 1: send the request and establish a semaphore to stop any requests that pipeline after us
Semaphore blockOn = null;
Semaphore releaseMe = null;
synchronized(this) {
- if (invalid) throw new HTTPException("connection failed on a previous pipelined call");
try {
connect();
sendRequest(contentType, content);
} catch (IOException e) {
- invalid = true;
+ reset();
throw e;
}
blockOn = okToRecieve;
boolean doRelease = true;
try {
if (blockOn != null) blockOn.block();
- if (invalid) throw new HTTPException("connection failed on a previous pipelined call");
+
+ // previous call wrecked the socket connection, but we already sent our request, so we can't just retry --
+ // this could cause the server to receive the request twice, which could be bad (think of the case where the
+ // server call causes Amazon.com to ship you an item with one-click purchasing).
+ if (sock == null)
+ throw new HTTPException("a previous pipelined call messed up the socket");
Hashtable h = in == null ? null : parseHeaders(in);
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
- in = null; sock = null;
+ reset();
releaseMe.release();
return makeRequest(contentType, content);
}
if (h.get("HTTP").equals("1.0") && h.get("content-length") == null) {
if (Log.on) Log.log(this, "proxy returned an HTTP/1.0 reply with no content-length...");
- in = null; sock = null;
+ reset();
} else {
int cl = h.get("content-length") == null ? -1 : Integer.parseInt(h.get("content-length").toString());
new HTTPInputStream(in, cl, releaseMe).close();
if (h.get("HTTP").equals("1.0") && h.get("content-length") == null)
throw new HTTPException("XWT does not support HTTP/1.0 servers which fail to return the Content-Length header");
int cl = h.get("content-length") == null ? -1 : Integer.parseInt(h.get("content-length").toString());
- HTTPInputStream ret = new HTTPInputStream(in, cl, releaseMe);
+ InputStream ret = new HTTPInputStream(in, cl, releaseMe);
+ if ("gzip".equals(h.get("content-encoding"))) ret = new java.util.zip.GZIPInputStream(ret);
doRelease = false;
return ret;
}
- } catch (IOException e) { invalid = true; throw e;
+ } catch (IOException e) { reset(); throw e;
} finally { if (doRelease) releaseMe.release();
}
}
} catch (UnknownHostException uhe) { }
if (Platform.detectProxy() == null) throw new HTTPException("could not resolve hostname \"" + host + "\" and no proxy configured");
+ if (Log.on) Log.log(this, " could not resolve host " + host + "; using xmlrpc.xwt.org to ensure security");
+ try {
+ JS.Array args = new JS.Array();
+ args.addElement(host);
+ Object ret = new XMLRPC("http://xmlrpc.xwt.org/RPC2/", "dns.resolve").call(args);
+ if (ret == null || !(ret instanceof String)) throw new Exception(" xmlrpc.xwt.org returned non-String: " + ret);
+ resolvedHosts.put(host, ret);
+ return;
+ } catch (Throwable e) {
+ throw new HTTPException("exception while attempting to use xmlrpc.xwt.org to resolve " + host + ": " + e);
+ }
}
// Everything Else ////////////////////////////////////////////////////////////////////////////
private synchronized void connect() throws IOException {
+ if (originalUrl.equals("stdio:")) {
+ in = new BufferedInputStream(System.in);
+ return;
+ }
if (sock != null) {
if (in == null) in = new BufferedInputStream(sock.getInputStream());
return;
if (Log.verbose) Log.log(this, "creating HTTP object for connection to " + host + ":" + port);
Proxy pi = Platform.detectProxy();
- if (sock == null && pi != null && pi.proxyAutoConfigFunction != null) sock = attemptPAC(pi.proxyAutoConfigFunction);
- if (sock == null && pi != null && ssl && pi.httpsProxyHost != null) sock = attemptHttpProxy(pi.httpsProxyHost, pi.httpsProxyPort);
- if (sock == null && pi != null && pi.httpProxyHost != null) sock = attemptHttpProxy(pi.httpProxyHost, pi.httpProxyPort);
- if (sock == null && pi != null && pi.socksProxyHost != null) sock = attemptSocksProxy(pi.socksProxyHost, pi.socksProxyPort);
+ OUTER: do {
+ if (pi != null) {
+ for(int i=0; i<pi.excluded.length; i++) if (host.equals(pi.excluded[i])) break OUTER;
+ if (sock == null && pi.proxyAutoConfigFunction != null) sock = attemptPAC(pi.proxyAutoConfigFunction);
+ if (sock == null && ssl && pi.httpsProxyHost != null) sock = attemptHttpProxy(pi.httpsProxyHost, pi.httpsProxyPort);
+ if (sock == null && pi.httpProxyHost != null) sock = attemptHttpProxy(pi.httpProxyHost, pi.httpProxyPort);
+ 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);
public void sendRequest(String contentType, String content) throws IOException {
- PrintWriter pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream()));
+ PrintWriter pw = new PrintWriter(new OutputStreamWriter(originalUrl.equals("stdio:") ? System.out : sock.getOutputStream()));
if (content != null) {
pw.print("POST " + path + " HTTP/1.1\r\n");
int contentLength = content.substring(0, 2).equals("\r\n") ?
}
pw.print("User-Agent: XWT\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");
private int contentLength = 0;
public int getContentLength() { return contentLength; }
- HTTPInputStream(InputStream in, int length, Semaphore releaseMe) {
+ HTTPInputStream(InputStream in, int length, Semaphore releaseMe) throws IOException {
super(in);
this.releaseMe = releaseMe;
this.contentLength = length;
int i = super.read();
if (i == -1) throw new HTTPException("encountered end of stream while reading chunk length");
- // FIXME: handle chunking extensions
+ // FEATURE: handle chunking extensions
if (i == '\r') {
super.read(); // LF
break;
}
return ret;
} finally {
- if (!good) invalid = true;
+ if (!good) reset();
}
}
}
}
+ void reset() {
+ firstRequest = true;
+ in = null;
+ sock = null;
+ }
+
// Misc Helpers ///////////////////////////////////////////////////////////////////////////////////
try {
org.xwt.js.JS.Array arr = new org.xwt.js.JS.Array();
arr.addElement(((JS.Exn)e).getObject());
- // FIXME
- //XWT.recursivePrintObject.call();
} catch (Exception e2) {
Log.log(Platform.class, e);
}
if (authorization != oldAuth) return;
if (Log.on) Log.log(Authorization.class, "displaying proxy authorization dialog");
- MessageQueue.add(new Message() {
+ Message.Q.add(new Message() {
public void perform() {
Box b = new Box();
- Template.getTemplate("org.xwt.builtin.proxy_authorization", null).apply(b, null, null, null, 0, 0, null);
+ Template t = Template.getTemplate((Res)Main.builtin.get("org/xwt/builtin/proxy_authorization.xwt"));
+ t.apply(b, null, null);
b.put("realm", realm);
b.put("proxyIP", proxyIP);
}
// ProxyAutoConfigRootScope ////////////////////////////////////////////////////////////////////
- public static class ProxyAutoConfigRootScope extends JS.Scope {
+ public static class ProxyAutoConfigRootScope extends JS.GlobalScope {
public ProxyAutoConfigRootScope() { super(null); }
- // FIXME: needs "standard objects"
-
public Object get(Object name) {
if (name.equals("isPlainHostName")) return isPlainHostName;
else if (name.equals("dnsDomainIs")) return dnsDomainIs;