Clean up outgoing encoding API. Generally: send(chunk, encoding).
authorRyan <ry@tinyclouds.org>
Tue, 26 May 2009 15:46:56 +0000 (17:46 +0200)
committerRyan <ry@tinyclouds.org>
Tue, 26 May 2009 15:48:59 +0000 (17:48 +0200)
src/file.js
src/http.js
src/net.cc
src/node.cc
src/node.h
src/node.js
website/index.html

index 6e0cead..12c95fa 100644 (file)
@@ -41,10 +41,11 @@ node.fs.File = function (options) {
   var self = this;
   options = options || {};
 
-  if (options.encoding === undefined)
+  if (options.encoding === "utf8") {
+    self.encoding = node.fs.UTF8;
+  } else {
     self.encoding = node.fs.RAW;
-  else
-    self.encoding = options.encoding
+  }
 
   //node.debug("encoding: opts=" + options.encoding + " self=" + self.encoding);
   self.fd = options.fd || null;
index 05aee5d..fad9535 100644 (file)
@@ -102,55 +102,54 @@ function toRaw(string) {
   return a;
 }
 
-node.http.ServerResponse = function (connection, responses) {
-  responses.push(this);
-  this.connection = connection;
-  this.closeOnFinish = false;
-  var output = [];
+// The send method appends data onto the output array. The deal is,
+// the data is either an array of integer, representing binary or it
+// is a string in which case it's UTF8 encoded. 
+// Two things to be considered:
+// - we should be able to send mixed encodings.
+// - we don't want to call connection.send("smallstring") because that
+//   is wasteful. *I think* its rather faster to concat inside of JS
+// Thus I attempt to concat as much as possible.  
+//
+// XXX this function is extremely ugly
+function send (output, data, encoding) {
+  if (data.constructor === String)
+    encoding = encoding || "ascii";
+  else
+    encoding = "raw";
+
+  if (output.length == 0) {
+    output.push([data, encoding]);
+    return;
+  }
 
-  // The send method appends data onto the output array. The deal is,
-  // the data is either an array of integer, representing binary or it
-  // is a string in which case it's UTF8 encoded. 
-  // Two things to considered:
-  // - we should be able to send mixed encodings.
-  // - we don't want to call connection.send("smallstring") because that
-  //   is wasteful. *I think* its rather faster to concat inside of JS
-  // Thus I attempt to concat as much as possible.  
-  function send (data) {
-    if (connection.readyState === "closed" || connection.readyState === "readOnly")
-    {
-      responses = [];
-      return;
-    }
+  var li = output.length-1;
+  var last_encoding = output[li][1];
 
-    if (output.length == 0) {
-      output.push(data);
+  if (data.constructor === String)  {
+    if ( last_encoding === encoding
+      || (last_encoding === "utf8" && encoding === "ascii")
+       ) 
+    {
+      output[li][0] += data;
       return;
     }
+  }
 
-    var li = output.length-1;
-
-    if (data.constructor == String && output[li].constructor == String) {
-      output[li] += data;
-      return;
-    }
+  if (data.constructor === Array && last_encoding === encoding) {
+    output[li][0] = output[li][0].concat(data);
+    return;
+  }
 
-    if (data.constructor == Array && output[li].constructor == Array) {
-      output[li] = output[li].concat(data);
-      return;
-    }
+  output.push([data, encoding]);
+};
 
-    // 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;
-    }
+node.http.ServerResponse = function (connection, responses) {
+  responses.push(this);
+  this.connection = connection;
+  this.closeOnFinish = false;
+  var output = [];
 
-    output.push(data);
-  };
 
   var chunked_encoding = false;
 
@@ -206,34 +205,39 @@ node.http.ServerResponse = function (connection, responses) {
 
     header += CRLF;
 
-    send(header);
+    send(output, header);
   };
 
-  this.sendBody = function (chunk) {
+  this.sendBody = function (chunk, encoding) {
     if (chunked_encoding) {
-      send(chunk.length.toString(16));
-      send(CRLF);
-      send(chunk);
-      send(CRLF);
+      send(output, chunk.length.toString(16));
+      send(output, CRLF);
+      send(output, chunk, encoding);
+      send(output, CRLF);
     } else {
-      send(chunk);
+      send(output, chunk, encoding);
     }
 
     this.flush();
   };
 
   this.flush = function () {
+    if (connection.readyState === "closed" || connection.readyState === "readOnly")
+    {
+      responses = [];
+      return;
+    }
     if (responses.length > 0 && responses[0] === this)
       while (output.length > 0) {
         var out = output.shift();
-        connection.send(out);
+        connection.send(out[0], out[1]);
       }
   };
 
   this.finished = false;
   this.finish = function () {
     if (chunked_encoding)
-      send("0\r\n\r\n"); // last chunk
+      send(output, "0\r\n\r\n"); // last chunk
 
     this.finished = true;
 
@@ -386,44 +390,14 @@ node.http.Client = function (port, host) {
      
     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) {
+    this.sendBody = function (chunk, encoding) {
       if (chunked_encoding) {
-        send(chunk.length.toString(16));
-        send(CRLF);
-        send(chunk);
-        send(CRLF);
+        send(output, chunk.length.toString(16));
+        send(output, CRLF);
+        send(output, chunk, encoding);
+        send(output, CRLF);
       } else {
-        send(chunk);
+        send(output, chunk, encoding);
       }
 
       this.flush();
@@ -443,7 +417,7 @@ node.http.Client = function (port, host) {
     this.finish = function (responseHandler) {
       this.responseHandler = responseHandler;
       if (chunked_encoding)
-        send("0\r\n\r\n"); // last chunk
+        send(output, "0\r\n\r\n"); // last chunk
 
       this.flush();
     };
index b2695e9..8a216a8 100644 (file)
@@ -62,7 +62,6 @@ Connection::Initialize (v8::Handle<v8::Object> target)
 
   NODE_SET_PROTOTYPE_METHOD(constructor_template, "connect", Connect);
   NODE_SET_PROTOTYPE_METHOD(constructor_template, "send", Send);
-  NODE_SET_PROTOTYPE_METHOD(constructor_template, "sendUtf8", SendUtf8);
   NODE_SET_PROTOTYPE_METHOD(constructor_template, "close", Close);
   NODE_SET_PROTOTYPE_METHOD(constructor_template, "fullClose", FullClose);
   NODE_SET_PROTOTYPE_METHOD(constructor_template, "forceClose", ForceClose);
@@ -327,31 +326,6 @@ new_buf (size_t size)
 }
 
 Handle<Value>
-Connection::SendUtf8 (const Arguments& args)
-{
-  HandleScope scope;
-  Connection *connection = NODE_UNWRAP(Connection, args.Holder());
-  if (!connection) return Handle<Value>();
-
-  if ( connection->ReadyState() != OPEN 
-    && connection->ReadyState() != WRITE_ONLY
-     ) 
-    return ThrowException(String::New("Socket is not open for writing"));
-
-  if (!args[0]->IsString())
-    return ThrowException(String::New("Must have string argument"));
-
-  // utf8 encoding
-  Local<String> s = args[0]->ToString();
-  size_t length = s->Utf8Length();
-  oi_buf *buf = new_buf(length);
-  s->WriteUtf8(buf->base, length);
-  connection->Send(buf);
-
-  return Undefined();  
-}
-
-Handle<Value>
 Connection::Send (const Arguments& args)
 {
   HandleScope scope;
@@ -372,19 +346,30 @@ Connection::Send (const Arguments& args)
   // addressed. 
 
   if (args[0]->IsString()) {
-    // ASCII encoding
+    enum encoding enc = ParseEncoding(args[1]);
     Local<String> s = args[0]->ToString();
-    size_t length = s->Utf8Length();
-    oi_buf *buf = new_buf(length);
-    s->WriteAscii(buf->base, 0, length);
+    size_t len = s->Utf8Length();
+    oi_buf *buf = new_buf(len);
+    switch (enc) {
+      case RAW:
+      case ASCII:
+        s->WriteAscii(buf->base, 0, len);
+        break;
+
+      case UTF8:
+        s->WriteUtf8(buf->base, len);
+        break;
+
+      default:
+        assert(0 && "unhandled string encoding");
+    }
     connection->Send(buf);
 
   } else if (args[0]->IsArray()) {
-    // raw encoding
     Handle<Array> array = Handle<Array>::Cast(args[0]);
-    size_t length = array->Length();
-    oi_buf *buf = new_buf(length);
-    for (size_t i = 0; i < length; i++) {
+    size_t len = array->Length();
+    oi_buf *buf = new_buf(len);
+    for (size_t i = 0; i < len; i++) {
       Local<Value> int_value = array->Get(Integer::New(i));
       buf->base[i] = int_value->IntegerValue();
     }
index f189ea3..9df8655 100644 (file)
@@ -243,6 +243,25 @@ node::eio_warmup (void)
   ev_async_start(EV_DEFAULT_UC_ &eio_watcher);
 }
 
+enum encoding
+node::ParseEncoding (Handle<Value> encoding_v)
+{
+  HandleScope scope;
+
+  if (!encoding_v->IsString())
+    return RAW;
+
+  String::Utf8Value encoding(encoding_v->ToString());
+
+  if(strcasecmp(*encoding, "utf8") == 0) {
+    return UTF8;
+  } else if (strcasecmp(*encoding, "ascii") == 0) {
+    return ASCII;
+  } else {
+    return RAW;
+  }
+}
+
 int
 main (int argc, char *argv[]) 
 {
index 7d1d296..d207d11 100644 (file)
@@ -23,7 +23,8 @@ do {                                                                      \
   templ->PrototypeTemplate()->Set(NODE_SYMBOL(name), __callback##_TEM);   \
 } while(0)
 
-enum encoding {UTF8, RAW};
+enum encoding {ASCII, UTF8, RAW};
+enum encoding ParseEncoding (v8::Handle<v8::Value> encoding_v);
 void fatal_exception (v8::TryCatch &try_catch); 
 void eio_warmup (void); // call this before creating a new eio event.
 
index 6bdef37..e3d9d16 100644 (file)
@@ -134,7 +134,7 @@ clearInterval = clearTimeout;
   }
 
   function loadScript (filename, target, callback) {
-    node.fs.cat(filename, node.fs.UTF8, function (status, content) {
+    node.fs.cat(filename, "utf8", function (status, content) {
       if (status != 0) {
         stderr.puts("Error reading " + filename + ": " + node.fs.strerror(status));
         node.exit(1);
index 2833245..9332647 100644 (file)
@@ -37,7 +37,8 @@ h1, h2, h3, h4 {
   margin: 2em 0; 
 }
 
-h1 a { color: inherit; }
+h1 code, h2 code, h3 code, h4 code { color: inherit; }
+h1 a, h2 a, h3 a, h4 a { color: inherit; }
 
 
 pre, code {
@@ -165,11 +166,15 @@ make install</pre>
 <code>on</code>. All methods and members are camel cased. Constructors
 always have a capital first letter.
 
-<p>Node uses strings to represent ASCII or UTF-8 encoded data. For the
-moment, arrays of integers are used to represent raw binary data&mdash;this
-representation is rather inefficient. In the future, <a
-href="http://code.google.com/p/v8/issues/detail?id=270">when V8 natively supports binary
-Blob objects</a>, Node will use them.
+<p>
+Node supports 3 byte-string encodings: 
+ASCII (<code>"ascii"</code>), 
+UTF-8 (<code>"utf8"</code>), and 
+raw binary (<code>"raw"</code>).
+It uses strings to represent ASCII and UTF-8 encoded data. For the moment,
+arrays of integers are used to represent raw binary  data&mdash;this
+representation is rather inefficient. This will change in the future, when <a
+  href="http://code.google.com/p/v8/issues/detail?id=270">V8 supports Blob objects</a>.
 
 <p>The following are global functions:</p>
 
@@ -463,18 +468,27 @@ server.listen(7000, "localhost");
   <dd>Creates a new connection object.
   </dd>
 
+  <dt><code>connection.readyState</code></dt>
+  <dd>Either <code>"closed"</code>, <code>"open"</code>,
+  <code>"readOnly"</code>, or <code>"writeOnly"</code>.
+  </dd>
+
   <dt><code>connection.setEncoding(encoding)</code></dt>
   <dd>Sets the encoding (either <code>"utf8"</code> or <code>"raw"</code>)
   for data that is received. 
   </dd>
 
-  <dt><code>connection.send(data)</code></dt>
-  <dd>sends data on the connection
+  <dt><code>connection.send(data, encoding="ascii")</code></dt>
+  <dd>Sends data on the connection. The data should be eithre an array of
+  integers (for raw binary) or a string (for utf8 or ascii). The second
+  parameter specifies the encoding in the case of a string&mdash;it defaults
+  to ASCII because encoding to UTF8 is rather slow.
   </dd>
 
   <dt><code>connection.close()</code></dt>
   <dd>Half-closes the connection. I.E. sends a FIN packet. It is possible
   the server will still send some data.
+     After calling this <code>readyState</code> will be <code>"readOnly"</code>.
   </dd>
 
   <dt><code>connection.fullClose()</code></dt>
@@ -500,8 +514,9 @@ server.listen(7000, "localhost");
   <dt><code>conneciton.onEOF = function () { };</code></dt>
   <dd>Called when the other end of the connection sends a FIN packet.
      <code>onReceive</code> will not be called after this.
+     After receiving this <code>readyState</code> will be <code>"writeOnly"</code>.
      You should probably just call <code>connection.close()</code> in this
-     callback.
+     callback. 
 
   <dt><code>conneciton.onDisconnect = function () { };</code></dt>
   <dd>Called once the connection is fully disconnected.</dd>
@@ -611,7 +626,7 @@ req.onBody = function (chunk) {
 };
 </pre>
   A chunk of the body is given as the single argument. The transfer-encoding
-  has been removed. 
+  has been decoded. 
 
   <p>The body chunk is either a String in the case of UTF-8 encoding or an
   array of numbers in the case of raw encoding. The body encoding is set with
@@ -654,11 +669,15 @@ res.sendHeader(200, [ ["Content-Length", body.length]
     before <code>res.finish()</code> is called.
   </dd>
 
-  <dt><code>res.sendBody(chunk)</code></dt>
+  <dt><code>res.sendBody(chunk, encoding="ascii")</code></dt>
   <dd>
   This method must be called after <code>sendHeader</code> was called. It
   sends a chunk of the response body. This method may be called multiple
   times to provide successive parts of the body.
+
+  <p>If <code>chunk</code> is a string, the second parameter specifies how
+  to encode it into a byte stream. By default the <code>encoding</code> is
+  <code>"ascii"</code>. 
   </dd>
 
   <dt><code>res.finish()</code></dt>
@@ -730,7 +749,7 @@ it, so neither do we.</i>
 whose header has already been sent. 
 
 <dl>
-  <dt><code>req.sendBody(chunk, encoding)</code></dt>
+  <dt><code>req.sendBody(chunk, encoding="ascii")</code></dt>
   <dd> Sends a sucessive peice of the body.  By calling this method many times,
 the user can stream a request body to a server&mdash;in that case it is
 suggested to use the <code>["Transfer-Encoding",
@@ -743,8 +762,6 @@ suggested to use the <code>["Transfer-Encoding",
     <code>"utf8"</code> or <code>"ascii"</code>. By default the body uses ASCII
     encoding, as it is faster.
 
-  <p> TODO
-
   <dt><code>req.finish(response_handler)</code></dt>
 
   <dd> Finishes sending the request. If any parts of the body are