mirror of
https://github.com/nodejs/node.git
synced 2024-11-29 15:06:33 +01:00
Clean up outgoing encoding API. Generally: send(chunk, encoding).
This commit is contained in:
parent
cf0eebf685
commit
e8f177aa2d
@ -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;
|
||||
|
152
src/http.js
152
src/http.js
@ -102,55 +102,54 @@ function toRaw(string) {
|
||||
return a;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
var li = output.length-1;
|
||||
var last_encoding = output[li][1];
|
||||
|
||||
if (data.constructor === String) {
|
||||
if ( last_encoding === encoding
|
||||
|| (last_encoding === "utf8" && encoding === "ascii")
|
||||
)
|
||||
{
|
||||
output[li][0] += data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.constructor === Array && last_encoding === encoding) {
|
||||
output[li][0] = output[li][0].concat(data);
|
||||
return;
|
||||
}
|
||||
|
||||
output.push([data, encoding]);
|
||||
};
|
||||
|
||||
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 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) {
|
||||
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);
|
||||
};
|
||||
|
||||
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();
|
||||
};
|
||||
|
53
src/net.cc
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, "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);
|
||||
@ -326,31 +325,6 @@ new_buf (size_t size)
|
||||
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>
|
||||
Connection::Send (const Arguments& args)
|
||||
{
|
||||
@ -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();
|
||||
}
|
||||
|
19
src/node.cc
19
src/node.cc
@ -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[])
|
||||
{
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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—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—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—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—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
|
||||
|
Loading…
Reference in New Issue
Block a user