Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cleanup http-client module, remove HttpClient, add support for follow…
…ing redirects (on by default)
- Loading branch information
Tom Robinson
committed
Nov 3, 2010
1 parent
d6d9bde
commit 0d12310
Showing
5 changed files
with
83 additions
and
330 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -1,99 +1,80 @@ | |||
|
|
||
// -- isaacs Isaac Schlueter | |||
// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License | // -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License | ||
// -- tlrobinson Tom Robinson | |||
|
|
||
var IO = require("io").IO, | var IO = require("io").IO; | ||
UTIL = require("narwhal/util"); | |||
|
|
||
exports.IO = function (url) { | exports.open = function(url, mode, options) { | ||
return new IO( | var connection, output, input; | ||
new java.net.URL(url).openStream(), | |||
null | |||
); | |||
}; | |||
|
|
||
exports.connect = function HttpClient_engine_connect (tx) { | function startRequest() { | ||
if (tx._isConnected) return; | connection = new java.net.URL(url).openConnection(); | ||
|
connection.setDoInput(true); | ||
var con = java.net.HttpURLConnection(new java.net.URL(tx.url).openConnection()); | connection.setDoOutput(true); | ||
con.setRequestMethod(tx.method.toUpperCase()); | connection.setRequestMethod(options.method); | ||
|
connection.setInstanceFollowRedirects(!!options.followRedirects); | ||
UTIL.forEach(tx.headers, function (h, v) { |
|
||
con.setRequestProperty(h, v); | for (var name in options.headers) { | ||
}); | if (options.headers.hasOwnProperty(name)) { | ||
if (!tx.headers) tx.headers = {}; | connection.addRequestProperty(String(name), String(options.headers[name])); | ||
var cl = UTIL.get(tx.headers, "Content-Length") || 0; | } | ||
if (cl > 0) { | |||
con.setDoOutput(true); | |||
var os = null; | |||
try { | |||
os = con.getOutputStream(); | |||
} catch (ex) {} | |||
if (os) { | |||
var writer = new IO(null, con.getOutputStream()); | |||
tx.body.forEach(function (piece) { | |||
writer.write(piece); | |||
}); | |||
writer.close(); | |||
} | |||
} | |||
|
|||
try { | |||
con.connect(); | |||
} catch (ex) { | |||
// It would be nice to do something clever and special here, | |||
// but I'm not feeling it at the moment. | |||
ex.message = [ | |||
"Could not connect to "+tx.url+". Probably a bad hostname.", | |||
ex.message | |||
].join("\n"); | |||
throw ex; | |||
} | |||
|
|||
tx._isConnected = true; | |||
var resp = tx._response = {status:200, headers:{}, body:[]}; | |||
|
|||
// Call this now to trigger the fetch asynchronously. | |||
// This way, if you set up multiple HttpClients, and then call connect() | |||
// on all of them, you'll only wait as long as the slowest one, since | |||
// the streams will start filling up right away. | |||
|
|||
|
|||
// now pull everything out. | |||
var fields = con.getHeaderFields(); | |||
var fieldKeys = fields.keySet().toArray(); | |||
for (var i = 0, l = fieldKeys.length; i < l; i ++ ) { | |||
var fieldValue = fields.get(fieldKeys[i]).toArray().join(''); | |||
var fieldName = fieldKeys[i]; | |||
if (fieldName === null) { | |||
// Something like: HTTP/1.1 200 OK | |||
UTIL.set(resp, "status", +(/HTTP\/1\.[01] ([0-9]{3})/.exec(fieldValue)[1])); | |||
// fieldName = "Status"; | |||
resp.statusText = fieldValue; | |||
continue; | |||
} | } | ||
UTIL.set(resp.headers, fieldName, fieldValue); |
|
||
connection.connect(); | |||
|
|||
output = new IO(null, connection.getOutputStream()); | |||
input = null; | |||
} | } | ||
|
|
||
// TODO: Restructure using non-blocking IO to support asynchronous interactions. | startRequest(); | ||
// In that case, you could just have a callback that gets each bytestring. |
|
||
var is = null; | var request = { | ||
try { | status : null, | ||
var is = con.getInputStream(); | headers : {}, | ||
} catch (ex) { | read : function() { | ||
return resp; | if (!input) { | ||
output.close(); | |||
input = new IO(connection.getInputStream(), null); | |||
this.status = Number(connection.getResponseCode()); | |||
this.statusText = String(connection.getResponseMessage() || ""); | |||
for (var i = 0; ; i++) { | |||
var key = connection.getHeaderFieldKey(i), value = connection.getHeaderField(i); | |||
if (!key && !value) { | |||
break; | |||
} | |||
if (key) { | |||
key = String(key); | |||
value = String(value); | |||
this.headers[key] = value; | |||
if (key.toUpperCase() === "LOCATION") | |||
url = value; | |||
} | |||
} | |||
|
|||
// Manually follow cross-protocol redirects because Java doesn't: | |||
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4620571 | |||
if (options.followRedirects && this.status >= 300 && this.status < 400) { | |||
// TODO: should we change the method to GET if it was not a GET like curl does? | |||
startRequest(); | |||
return this.read.apply(this, arguments); | |||
} | |||
} | |||
return input.read.apply(input, arguments); | |||
}, | |||
write : function() { | |||
output.write.apply(output, arguments); | |||
return this; | |||
}, | |||
flush : function() { | |||
output.flush.apply(output, arguments); | |||
return this; | |||
}, | |||
close : function() { | |||
output && output.close(); | |||
input && input.close(); | |||
return this; | |||
}, | |||
copy : IO.prototype.copy | |||
} | } | ||
|
return request; | ||
// TODO: Should the input stream be rewindable? | |||
var reader = new IO(con.getInputStream(), null); | |||
resp.body = {forEach : function (block) { | |||
var buflen = 1024; | |||
for ( | |||
var bytes = reader.read(buflen); | |||
bytes.length > 0; | |||
bytes = reader.read(buflen) | |||
) block(bytes); | |||
}}; | |||
|
|||
return resp; | |||
}; | }; |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.