Browse Source

Clean up outgoing encoding API. Generally: send(chunk, encoding).

v0.7.4-release
Ryan 16 years ago
parent
commit
e8f177aa2d
  1. 7
      src/file.js
  2. 148
      src/http.js
  3. 53
      src/net.cc
  4. 19
      src/node.cc
  5. 3
      src/node.h
  6. 2
      src/node.js
  7. 45
      website/index.html

7
src/file.js

@ -41,10 +41,11 @@ node.fs.File = function (options) {
var self = this; var self = this;
options = options || {}; options = options || {};
if (options.encoding === undefined) if (options.encoding === "utf8") {
self.encoding = node.fs.UTF8;
} else {
self.encoding = node.fs.RAW; self.encoding = node.fs.RAW;
else }
self.encoding = options.encoding
//node.debug("encoding: opts=" + options.encoding + " self=" + self.encoding); //node.debug("encoding: opts=" + options.encoding + " self=" + self.encoding);
self.fd = options.fd || null; self.fd = options.fd || null;

148
src/http.js

@ -102,55 +102,54 @@ function toRaw(string) {
return a; return a;
} }
node.http.ServerResponse = function (connection, responses) { // The send method appends data onto the output array. The deal is,
responses.push(this); // the data is either an array of integer, representing binary or it
this.connection = connection; // is a string in which case it's UTF8 encoded.
this.closeOnFinish = false; // Two things to be considered:
var output = []; // - 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, var li = output.length-1;
// the data is either an array of integer, representing binary or it var last_encoding = output[li][1];
// 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;
}
if (output.length == 0) { if (data.constructor === String) {
output.push(data); if ( last_encoding === encoding
|| (last_encoding === "utf8" && encoding === "ascii")
)
{
output[li][0] += data;
return; return;
} }
}
var li = output.length-1; if (data.constructor === Array && last_encoding === encoding) {
output[li][0] = output[li][0].concat(data);
if (data.constructor == String && output[li].constructor == String) { return;
output[li] += data; }
return;
}
if (data.constructor == Array && output[li].constructor == Array) { output.push([data, encoding]);
output[li] = output[li].concat(data); };
return;
}
// If the string is small enough, just convert it to binary node.http.ServerResponse = function (connection, responses) {
if (data.constructor == String responses.push(this);
&& data.length < 128 this.connection = connection;
&& output[li].constructor == Array) this.closeOnFinish = false;
{ var output = [];
output[li] = output[li].concat(toRaw(data));
return;
}
output.push(data);
};
var chunked_encoding = false; var chunked_encoding = false;
@ -206,34 +205,39 @@ node.http.ServerResponse = function (connection, responses) {
header += CRLF; header += CRLF;
send(header); send(output, header);
}; };
this.sendBody = function (chunk) { this.sendBody = function (chunk, encoding) {
if (chunked_encoding) { if (chunked_encoding) {
send(chunk.length.toString(16)); send(output, chunk.length.toString(16));
send(CRLF); send(output, CRLF);
send(chunk); send(output, chunk, encoding);
send(CRLF); send(output, CRLF);
} else { } else {
send(chunk); send(output, chunk, encoding);
} }
this.flush(); this.flush();
}; };
this.flush = function () { this.flush = function () {
if (connection.readyState === "closed" || connection.readyState === "readOnly")
{
responses = [];
return;
}
if (responses.length > 0 && responses[0] === this) if (responses.length > 0 && responses[0] === this)
while (output.length > 0) { while (output.length > 0) {
var out = output.shift(); var out = output.shift();
connection.send(out); connection.send(out[0], out[1]);
} }
}; };
this.finished = false; this.finished = false;
this.finish = function () { this.finish = function () {
if (chunked_encoding) if (chunked_encoding)
send("0\r\n\r\n"); // last chunk send(output, "0\r\n\r\n"); // last chunk
this.finished = true; this.finished = true;
@ -386,44 +390,14 @@ node.http.Client = function (port, host) {
var output = [header]; var output = [header];
function send (data) { this.sendBody = function (chunk, encoding) {
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) { if (chunked_encoding) {
send(chunk.length.toString(16)); send(output, chunk.length.toString(16));
send(CRLF); send(output, CRLF);
send(chunk); send(output, chunk, encoding);
send(CRLF); send(output, CRLF);
} else { } else {
send(chunk); send(output, chunk, encoding);
} }
this.flush(); this.flush();
@ -443,7 +417,7 @@ node.http.Client = function (port, host) {
this.finish = function (responseHandler) { this.finish = function (responseHandler) {
this.responseHandler = responseHandler; this.responseHandler = responseHandler;
if (chunked_encoding) if (chunked_encoding)
send("0\r\n\r\n"); // last chunk send(output, "0\r\n\r\n"); // last chunk
this.flush(); this.flush();
}; };

53
src/net.cc

@ -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, "connect", Connect);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "send", Send); 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, "close", Close);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "fullClose", FullClose); NODE_SET_PROTOTYPE_METHOD(constructor_template, "fullClose", FullClose);
NODE_SET_PROTOTYPE_METHOD(constructor_template, "forceClose", ForceClose); NODE_SET_PROTOTYPE_METHOD(constructor_template, "forceClose", ForceClose);
@ -326,31 +325,6 @@ new_buf (size_t size)
return b; return b;
} }
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> Handle<Value>
Connection::Send (const Arguments& args) Connection::Send (const Arguments& args)
{ {
@ -372,19 +346,30 @@ Connection::Send (const Arguments& args)
// addressed. // addressed.
if (args[0]->IsString()) { if (args[0]->IsString()) {
// ASCII encoding enum encoding enc = ParseEncoding(args[1]);
Local<String> s = args[0]->ToString(); Local<String> s = args[0]->ToString();
size_t length = s->Utf8Length(); size_t len = s->Utf8Length();
oi_buf *buf = new_buf(length); oi_buf *buf = new_buf(len);
s->WriteAscii(buf->base, 0, length); 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); connection->Send(buf);
} else if (args[0]->IsArray()) { } else if (args[0]->IsArray()) {
// raw encoding
Handle<Array> array = Handle<Array>::Cast(args[0]); Handle<Array> array = Handle<Array>::Cast(args[0]);
size_t length = array->Length(); size_t len = array->Length();
oi_buf *buf = new_buf(length); oi_buf *buf = new_buf(len);
for (size_t i = 0; i < length; i++) { for (size_t i = 0; i < len; i++) {
Local<Value> int_value = array->Get(Integer::New(i)); Local<Value> int_value = array->Get(Integer::New(i));
buf->base[i] = int_value->IntegerValue(); buf->base[i] = int_value->IntegerValue();
} }

19
src/node.cc

@ -243,6 +243,25 @@ node::eio_warmup (void)
ev_async_start(EV_DEFAULT_UC_ &eio_watcher); 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 int
main (int argc, char *argv[]) main (int argc, char *argv[])
{ {

3
src/node.h

@ -23,7 +23,8 @@ do { \
templ->PrototypeTemplate()->Set(NODE_SYMBOL(name), __callback##_TEM); \ templ->PrototypeTemplate()->Set(NODE_SYMBOL(name), __callback##_TEM); \
} while(0) } 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 fatal_exception (v8::TryCatch &try_catch);
void eio_warmup (void); // call this before creating a new eio event. void eio_warmup (void); // call this before creating a new eio event.

2
src/node.js

@ -134,7 +134,7 @@ clearInterval = clearTimeout;
} }
function loadScript (filename, target, callback) { 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) { if (status != 0) {
stderr.puts("Error reading " + filename + ": " + node.fs.strerror(status)); stderr.puts("Error reading " + filename + ": " + node.fs.strerror(status));
node.exit(1); node.exit(1);

45
website/index.html

@ -37,7 +37,8 @@ h1, h2, h3, h4 {
margin: 2em 0; 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 { pre, code {
@ -165,11 +166,15 @@ make install</pre>
<code>on</code>. All methods and members are camel cased. Constructors <code>on</code>. All methods and members are camel cased. Constructors
always have a capital first letter. always have a capital first letter.
<p>Node uses strings to represent ASCII or UTF-8 encoded data. For the <p>
moment, arrays of integers are used to represent raw binary data&mdash;this Node supports 3 byte-string encodings:
representation is rather inefficient. In the future, <a ASCII (<code>"ascii"</code>),
href="http://code.google.com/p/v8/issues/detail?id=270">when V8 natively supports binary UTF-8 (<code>"utf8"</code>), and
Blob objects</a>, Node will use them. 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> <p>The following are global functions:</p>
@ -463,18 +468,27 @@ server.listen(7000, "localhost");
<dd>Creates a new connection object. <dd>Creates a new connection object.
</dd> </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> <dt><code>connection.setEncoding(encoding)</code></dt>
<dd>Sets the encoding (either <code>"utf8"</code> or <code>"raw"</code>) <dd>Sets the encoding (either <code>"utf8"</code> or <code>"raw"</code>)
for data that is received. for data that is received.
</dd> </dd>
<dt><code>connection.send(data)</code></dt> <dt><code>connection.send(data, encoding="ascii")</code></dt>
<dd>sends data on the connection <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> </dd>
<dt><code>connection.close()</code></dt> <dt><code>connection.close()</code></dt>
<dd>Half-closes the connection. I.E. sends a FIN packet. It is possible <dd>Half-closes the connection. I.E. sends a FIN packet. It is possible
the server will still send some data. the server will still send some data.
After calling this <code>readyState</code> will be <code>"readOnly"</code>.
</dd> </dd>
<dt><code>connection.fullClose()</code></dt> <dt><code>connection.fullClose()</code></dt>
@ -500,8 +514,9 @@ server.listen(7000, "localhost");
<dt><code>conneciton.onEOF = function () { };</code></dt> <dt><code>conneciton.onEOF = function () { };</code></dt>
<dd>Called when the other end of the connection sends a FIN packet. <dd>Called when the other end of the connection sends a FIN packet.
<code>onReceive</code> will not be called after this. <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 You should probably just call <code>connection.close()</code> in this
callback. callback.
<dt><code>conneciton.onDisconnect = function () { };</code></dt> <dt><code>conneciton.onDisconnect = function () { };</code></dt>
<dd>Called once the connection is fully disconnected.</dd> <dd>Called once the connection is fully disconnected.</dd>
@ -611,7 +626,7 @@ req.onBody = function (chunk) {
}; };
</pre> </pre>
A chunk of the body is given as the single argument. The transfer-encoding 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 <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 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. before <code>res.finish()</code> is called.
</dd> </dd>
<dt><code>res.sendBody(chunk)</code></dt> <dt><code>res.sendBody(chunk, encoding="ascii")</code></dt>
<dd> <dd>
This method must be called after <code>sendHeader</code> was called. It 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 sends a chunk of the response body. This method may be called multiple
times to provide successive parts of the body. 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> </dd>
<dt><code>res.finish()</code></dt> <dt><code>res.finish()</code></dt>
@ -730,7 +749,7 @@ it, so neither do we.</i>
whose header has already been sent. whose header has already been sent.
<dl> <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, <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 the user can stream a request body to a server&mdash;in that case it is
suggested to use the <code>["Transfer-Encoding", 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 <code>"utf8"</code> or <code>"ascii"</code>. By default the body uses ASCII
encoding, as it is faster. encoding, as it is faster.
<p> TODO
<dt><code>req.finish(response_handler)</code></dt> <dt><code>req.finish(response_handler)</code></dt>
<dd> Finishes sending the request. If any parts of the body are <dd> Finishes sending the request. If any parts of the body are

Loading…
Cancel
Save