Simple HTTP client is working a bit.
authorRyan <ry@tinyclouds.org>
Tue, 19 May 2009 11:12:46 +0000 (13:12 +0200)
committerRyan <ry@tinyclouds.org>
Tue, 19 May 2009 11:12:46 +0000 (13:12 +0200)
src/http.js
test-http_simple.js
test_client.js [new file with mode: 0644]
test_http.js

index babdfa6..869d725 100644 (file)
@@ -1,4 +1,5 @@
 (function () {
+CRLF = "\r\n";
 node.http.STATUS_CODES = { 100 : 'Continue'
                          , 101 : 'Switching Protocols' 
                          , 200 : 'OK' 
@@ -169,14 +170,14 @@ node.http.ServerResponse = function (connection, responses) {
                + status_code.toString() 
                + " " 
                + reason 
-               + "\r\n"
+               + CRLF
                ;
 
     for (var i = 0; i < headers.length; i++) {
       var field = headers[i][0];
       var value = headers[i][1];
 
-      header += field + ": " + value + "\r\n";
+      header += field + ": " + value + CRLF;
       
       if (connection_expression.exec(field)) {
         sent_connection_header = true;
@@ -206,7 +207,7 @@ node.http.ServerResponse = function (connection, responses) {
       chunked_encoding = true;
     }
 
-    header += "\r\n";
+    header += CRLF;
 
     send(header);
   };
@@ -214,9 +215,9 @@ node.http.ServerResponse = function (connection, responses) {
   this.sendBody = function (chunk) {
     if (chunked_encoding) {
       send(chunk.length.toString(16));
-      send("\r\n");
+      send(CRLF);
       send(chunk);
-      send("\r\n");
+      send(CRLF);
     } else {
       send(chunk);
     }
@@ -296,7 +297,7 @@ node.http.Server = function (RequestHandler, options) {
       this.onHeadersComplete = function () {
         req.http_version = this.http_version;
         req.method       = this.method;
-        req.uri          = parseUri(req.uri); // TODO parse the URI lazily
+        req.uri          = node.http.parseUri(req.uri); // TODO parse the URI lazily
 
         res.should_keep_alive = this.should_keep_alive;
 
@@ -328,17 +329,11 @@ node.http.Server = function (RequestHandler, options) {
     new node.http.LowLevelServer(ConnectionHandler, options);
 };
 
-node.http.Client = function (port, host) {
-  var port = port;
-  var host = host;
+node.http.Client = function (port, host, options) {
+  var connection = new node.http.LowLevelClient(options);
+  var requests  = [];
 
-  var pending_responses = [];
-  function Message (method, uri, header_lines) {
-    pending_responses.push(this);
-
-    this.method   = method;
-    this.path     = path;
-    this.headers  = headers;
+  function ClientRequest (method, uri, header_lines) {
 
     var chunked_encoding = false;
     var connection_close = false;
@@ -349,11 +344,12 @@ node.http.Client = function (port, host) {
 
     var header = method + " " + uri + " HTTP/1.1\r\n";
 
+    header_lines = header_lines || [];
     for (var i = 0; i < header_lines.length; i++) {
       var field = header_lines[i][0];
       var value = header_lines[i][1];
 
-      header += field + ": " + value + "\r\n";
+      header += field + ": " + value + CRLF;
       
       if (connection_expression.exec(field)) {
         sent_connection_header = true;
@@ -376,50 +372,165 @@ node.http.Client = function (port, host) {
       header += "Transfer-Encoding: chunked\r\n";
       chunked_encoding = true;
     }
-    header += "\r\n";
+    header += CRLF;
             
     var output = [header];
 
+    function send (data) {
+      if (output.length == 0) {
+        output.push(data);
+        return;
+      }
+
+      var li = output.length-1;
+
+      if (data.constructor == String && output[li].constructor == String) {
+        output[li] += data;
+        return;
+      }
+
+      if (data.constructor == Array && output[li].constructor == Array) {
+        output[li] = output[li].concat(data);
+        return;
+      }
+
+      // If the string is small enough, just convert it to binary
+      if (data.constructor == String 
+          && data.length < 128
+          && output[li].constructor == Array) 
+      {
+        output[li] = output[li].concat(toRaw(data));
+        return;
+      }
+
+      output.push(data);
+    };
+
     this.sendBody = function (chunk) {
+      if (chunked_encoding) {
+        send(chunk.length.toString(16));
+        send(CRLF);
+        send(chunk);
+        send(CRLF);
+      } else {
+        send(chunk);
+      }
+
+      this.flush();
     };
 
-    this.finish = function () {
+    this.flush = function ( ) {
+      if (connection.readyState !== "open") {
+        connection.connect(port, host);
+        return;
+      }
+      while (this === requests[0] && output.length > 0) {
+        connection.send(output.shift());
+      }
+    };
+
+    this.finished = false;
+    this.finish = function (responseHandler) {
+      this.responseHandler = responseHandler;
+      if (chunked_encoding)
+        send("0\r\n\r\n"); // last chunk
+
+      this.flush();
     };
   }
   
-  function spawn_client ( ) {
-    var c = new node.http.LowLevelClient();
+  connection.onConnect = function () {
+    puts("HTTP CLIENT: connected");
+    requests[0].flush();
+  };
+
+  connection.onDisconnect = function () {
+    // If there are more requests to handle, reconnect.
+    if (requests.length > 0) {
+      puts("HTTP CLIENT: reconnecting");
+      connection.connect(port, host);
+    }
+  };
 
-    c.onConnect = function () {
+  // On response
+  connection.onMessage = function () {
+    var req = requests.shift();
+    var res = { status_code   : null  // set in onHeadersComplete
+              , http_version  : null  // set in onHeadersComplete
+              , headers       : []    // set in onHeaderField/Value
+              };
+
+    var headers = res.headers;
+    var last_was_value = false;
+
+    this.onHeaderField = function (data) {
+      if (headers.length > 0 && last_was_value == false)
+        headers[headers.length-1][0] += data; 
+      else
+        headers.push([data]);
+      last_was_value = false;
+      return true;
     };
 
-    // On response
-    c.onMessage = function () {
+    this.onHeaderValue = function (data) {
+      var last_pair = headers[headers.length-1];
+      if (last_pair.length == 1)
+        last_pair[1] = data;
+      else 
+        last_pair[1] += data;
+      last_was_value = true;
+      return true;
     };
+
+    this.onHeadersComplete = function () {
+      res.status_code = this.status_code;
+      res.http_version = this.http_version;
+      res.headers = headers;
+
+      req.responseHandler(res);
+    };
+
+    this.onBody = function (chunk) {
+      if (res.onBody)
+        return res.onBody(chunk);
+      else
+        return true;
+    };
+
+    this.onMessageComplete = function () {
+      connection.close();
+
+      if (res.onBodyComplete)
+        return res.onBodyComplete();
+      else
+        return true;
+    };
+  };
+
+  function newRequest (method, uri, headers) {
+    var req = new ClientRequest(method, uri, headers);
+    requests.push(req);
+    return req;
   }
   
-  this.get = function (path, headers) {
-    var m = new Message("GET", path, headers);
-    m.finish();
-    return m;
+  this.get = function (uri, headers) {
+    return newRequest("GET", uri, headers);
   };
 
-  this.head = function (path, headers) {
-    var m = new Message("HEAD", path, headers);
-    m.finish();
-    return m;
+  this.head = function (uri, headers) {
+    return newRequest("HEAD", uri, headers);
   };
 
-  this.post = function (path, headers) {
-    return new Message("POST", path, headers);
+  this.post = function (uri, headers) {
+    return newRequest("POST", uri, headers);
   };
 
-  this.del = function (path, headers) {
-    return new Message("DELETE", path, headers);
+  this.del = function (uri, headers) {
+    return newRequest("DELETE", uri, headers);
   };
 
-  this.put = function (path, headers) {
-    return new Message("PUT", path, headers);
+  this.put = function (uri, headers) {
+    return newRequest("PUT", uri, headers);
   };
 };
 
index 61f21f0..30dfd40 100644 (file)
@@ -4,7 +4,7 @@ for (var i = 0; i < 20*1024; i++) {
 }
 stored = {};
 new node.http.Server(function (req, res) {
-  var commands = req.uri.split("/");
+  var commands = req.uri.path.split("/");
   var command = commands[1];
   var body = "";
   var arg = commands[2];
diff --git a/test_client.js b/test_client.js
new file mode 100644 (file)
index 0000000..8e37f68
--- /dev/null
@@ -0,0 +1,16 @@
+var c = new node.http.Client(8000, "localhost")
+
+var req = c.get("/hello/world", [["Accept", "*/*"]]);
+req.finish(function (res) {
+  puts("got response: " + res.status_code.toString());
+
+  res.onBody = function (chunk) {
+    puts("got response body <" + chunk.encodeUtf8() + ">");
+    return true;
+  };
+
+  res.onBodyComplete = function () {
+    puts("response complete!");
+    return true;
+  };
+});
index 4cb2239..5e5da57 100644 (file)
@@ -1,4 +1,5 @@
 new node.http.Server(function (req, res) {
+  puts("got req to " + req.uri.path);
   setTimeout(function () {
     res.sendHeader(200, [["Content-Type", "text/plain"]]);
     res.sendBody(JSON.stringify(req.uri));