Merge branch 'master' into net2

This commit is contained in:
Ryan Dahl 2010-01-12 16:59:14 -08:00
Родитель a876df6c71 c4397b801a
Коммит 02e52ef8e9
53 изменённых файлов: 2373 добавлений и 916 удалений

Просмотреть файл

@ -1,4 +1,43 @@
2009.12.22, Version 0.1.23
2010.01.09, Version 0.1.25
* sys.inspect() improvements (Tim Caswell)
* path module improvements (isaacs, Benjamin Thomas)
* API: request.uri -> request.url
It is no longer an object, but a string. The 'url' module
was addded to parse that string. That is, node no longer
parses the request URL automatically.
require('url').parse(request.url)
is roughly equivlent to the old request.uri object.
(isaacs)
* Bugfix: Several libeio related race conditions.
* Better errors for multipart library (Felix Geisendörfer)
* Bugfix: Update node-waf version to 1.5.10
* getmem for freebsd (Vanilla Hsu)
2009.12.31, Version 0.1.24, 642c2773a7eb2034f597af1cd404b9e086b59632
* Bugfix: don't chunk responses to HTTP/1.0 clients, even if
they send Connection: Keep-Alive (e.g. wget)
* Bugfix: libeio race condition
* Bugfix: Don't segfault on unknown http method
* Simplify exception reporting
* Upgrade V8 to 2.0.5.4
2009.12.22, Version 0.1.23, f91e347eeeeac1a8bd6a7b462df0321b60f3affc
* Bugfix: require("../blah") issues (isaacs)

Просмотреть файл

@ -14,7 +14,7 @@ for (var i = 0; i < 20*1024; i++) {
stored = {};
http.createServer(function (req, res) {
var commands = req.uri.path.split("/");
var commands = req.url.split("/");
var command = commands[1];
var body = "";
var arg = commands[2];

Просмотреть файл

@ -5,6 +5,7 @@ puts("Welcome to the Node.js REPL.");
puts("Enter ECMAScript at the prompt.");
puts("Tip 1: Use 'rlwrap node-repl' for a better interface");
puts("Tip 2: Type Control-D to exit.");
puts("Type '.help' for options.");
require('repl').start();

Просмотреть файл

@ -12,6 +12,6 @@ t = join(w, 'Tools')
sys.path = [w, t] + sys.path
import Scripting
VERSION="1.5.9"
VERSION="1.5.10"
Scripting.prepare(t, os.getcwd(), VERSION, wafdir)
sys.exit(0)

2
deps/http_parser/LICENSE-MIT поставляемый
Просмотреть файл

@ -1,4 +1,4 @@
Copyright 2009 Ryan Dahl <ry@tinyclouds.org>
Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to

10
deps/http_parser/README.md поставляемый
Просмотреть файл

@ -29,10 +29,10 @@ Usage
One `http_parser` object is used per TCP connection. Initialize the struct
using `http_parser_init()` and set the callbacks. That might look something
like this:
like this for a request parser:
http_parser *parser = malloc(sizeof(http_parser));
http_parser_init(parser);
http_parser_init(parser, HTTP_REQUEST);
parser->on_path = my_path_callback;
parser->on_header_field = my_header_field_callback;
/* ... */
@ -54,7 +54,7 @@ When data is received on the socket execute the parser and check for errors.
* Note we pass the recved==0 to http_parse_requests to signal
* that EOF has been recieved.
*/
nparsed = http_parse_requests(parser, buf, recved);
nparsed = http_parser_execute(parser, buf, recved);
if (nparsed != recved) {
/* Handle error. Usually just close the connection. */
@ -63,7 +63,7 @@ When data is received on the socket execute the parser and check for errors.
HTTP needs to know where the end of the stream is. For example, sometimes
servers send responses without Content-Length and expect the client to
consume input (for the body) until EOF. To tell http_parser about EOF, give
`0` as the third parameter to `http_parse_requests()`. Callbacks and errors
`0` as the third parameter to `http_parser_execute()`. Callbacks and errors
can still be encountered during an EOF, so one must still be prepared
to receive them.
@ -85,7 +85,7 @@ parser, for example, would not want such a feature.
Callbacks
---------
During the `http_parse_requests()` call, the callbacks set in `http_parser`
During the `http_parser_execute()` call, the callbacks set in `http_parser`
will be executed. The parser maintains state and never looks behind, so
buffering the data is not necessary. If you need to save certain data for
later usage, you can do that from the callbacks.

106
deps/http_parser/http_parser.c поставляемый
Просмотреть файл

@ -1,25 +1,25 @@
/* Copyright 2009 Ryan Dahl <ry@tinyclouds.org>
/* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
*
* Some parts of this source file were taken from NGINX
* (src/http/ngx_http_parser.c) copyright (C) 2002-2009 Igor Sysoev.
*
* (src/http/ngx_http_parser.c) copyright (C) 2002-2009 Igor Sysoev.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
* IN THE SOFTWARE.
*/
#include <http_parser.h>
#include <stdint.h>
@ -97,6 +97,7 @@ static inline int message_complete_callback (http_parser *parser)
return parser->on_message_complete(parser);
}
#define PROXY_CONNECTION "proxy-connection"
#define CONNECTION "connection"
#define CONTENT_LENGTH "content-length"
#define TRANSFER_ENCODING "transfer-encoding"
@ -148,7 +149,7 @@ static const uint32_t usual[] = {
#define USUAL(c) (usual[c >> 5] & (1 << (c & 0x1f)))
enum state
enum state
{ s_dead = 1 /* important that this is > 0 */
, s_start_res
@ -198,7 +199,6 @@ enum state
, s_header_almost_done
, s_headers_almost_done
, s_headers_done
, s_chunk_size_start
, s_chunk_size
@ -212,13 +212,14 @@ enum state
, s_body_identity_eof
};
enum header_states
enum header_states
{ h_general = 0
, h_C
, h_CO
, h_CON
, h_matching_connection
, h_matching_proxy_connection
, h_matching_content_length
, h_matching_transfer_encoding
@ -236,28 +237,31 @@ enum header_states
};
enum flags
{ F_CHUNKED = 0x0001
, F_CONNECTION_KEEP_ALIVE = 0x0002
, F_CONNECTION_CLOSE = 0x0004
, F_TRAILING = 0x0010
{ F_CHUNKED = 1 << 0
, F_CONNECTION_KEEP_ALIVE = 1 << 1
, F_CONNECTION_CLOSE = 1 << 2
, F_TRAILING = 1 << 3
};
#define CR '\r'
#define LF '\n'
#define LOWER(c) (unsigned char)(c | 0x20)
#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
#if HTTP_PARSER_STRICT
# define STRICT_CHECK(cond) if (cond) goto error
# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
#else
# define STRICT_CHECK(cond)
#else
# define STRICT_CHECK(cond)
# define NEW_MESSAGE() start_state
#endif
static inline
size_t parse (http_parser *parser, const char *data, size_t len, int start_state)
size_t http_parser_execute (http_parser *parser,
const char *data,
size_t len)
{
char c, ch;
char c, ch;
const char *p, *pe;
ssize_t to_read;
@ -298,7 +302,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
switch (ch) {
case 'H':
state = s_res_H;
state = s_res_H;
break;
case CR:
@ -321,7 +325,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
state = s_res_HTT;
break;
case s_res_HTT:
case s_res_HTT:
STRICT_CHECK(ch != 'P');
state = s_res_HTTP;
break;
@ -353,7 +357,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
if (parser->http_major > 999) goto error;
break;
}
/* first digit of minor HTTP version */
case s_res_first_http_minor:
if (ch < '0' || ch > '9') goto error;
@ -464,7 +468,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
if (strncmp(parser->buffer, "GET", 3) == 0) {
parser->method = HTTP_GET;
break;
}
}
if (strncmp(parser->buffer, "PUT", 3) == 0) {
parser->method = HTTP_PUT;
@ -706,7 +710,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
switch (ch) {
case '?':
break; // XXX ignore extra '?' ... is this right?
break; // XXX ignore extra '?' ... is this right?
case ' ':
CALLBACK(url);
state = s_req_http_start;
@ -931,8 +935,10 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
}
if (ch == LF) {
state = s_headers_done;
break;
/* they might be just sending \n instead of \r\n so this would be
* the second \n to denote the end of headers*/
state = s_headers_almost_done;
goto headers_almost_done;
}
c = LOWER(ch);
@ -949,6 +955,10 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
header_state = h_C;
break;
case 'p':
header_state = h_matching_proxy_connection;
break;
case 't':
header_state = h_matching_transfer_encoding;
break;
@ -1006,6 +1016,18 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
}
break;
/* proxy-connection */
case h_matching_proxy_connection:
index++;
if (index > sizeof(PROXY_CONNECTION)-1
|| c != PROXY_CONNECTION[index]) {
header_state = h_general;
} else if (index == sizeof(PROXY_CONNECTION)-2) {
header_state = h_connection;
}
break;
/* content-length */
case h_matching_content_length:
@ -1089,7 +1111,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
header_state = h_general;
break;
}
}
switch (header_state) {
case h_transfer_encoding:
@ -1138,8 +1160,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
if (ch == LF) {
CALLBACK(header_value);
state = s_header_field_start;
break;
goto header_almost_done;
}
break;
}
@ -1162,7 +1183,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
/* Transfer-Encoding: chunked */
case h_matching_transfer_encoding_chunked:
index++;
if (index > sizeof(CHUNKED)-1
if (index > sizeof(CHUNKED)-1
|| c != CHUNKED[index]) {
header_state = h_general;
} else if (index == sizeof(CHUNKED)-2) {
@ -1206,6 +1227,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
}
case s_header_almost_done:
header_almost_done:
{
STRICT_CHECK(ch != LF);
@ -1228,6 +1250,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
}
case s_headers_almost_done:
headers_almost_done:
{
STRICT_CHECK(ch != LF);
@ -1241,7 +1264,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
parser->body_read = 0;
CALLBACK2(headers_complete);
if (parser->flags & F_CHUNKED) {
/* chunked encoding - ignore Content-Length header */
state = s_chunk_size_start;
@ -1254,7 +1277,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
/* Content-Length header given and non-zero */
state = s_body_identity;
} else {
if (start_state == s_start_req || http_should_keep_alive(parser)) {
if (parser->type == HTTP_REQUEST || http_should_keep_alive(parser)) {
/* Assume content-length 0 - read the next */
CALLBACK2(message_complete);
state = NEW_MESSAGE();
@ -1406,22 +1429,6 @@ error:
}
size_t
http_parse_requests (http_parser *parser, const char *data, size_t len)
{
if (!parser->state) parser->state = s_start_req;
return parse(parser, data, len, s_start_req);
}
size_t
http_parse_responses (http_parser *parser, const char *data, size_t len)
{
if (!parser->state) parser->state = s_start_res;
return parse(parser, data, len, s_start_res);
}
int
http_should_keep_alive (http_parser *parser)
{
@ -1444,9 +1451,10 @@ http_should_keep_alive (http_parser *parser)
void
http_parser_init (http_parser *parser)
http_parser_init (http_parser *parser, enum http_parser_type t)
{
parser->state = 0;
parser->type = t;
parser->state = (t == HTTP_REQUEST ? s_start_req : s_start_res);
parser->on_message_begin = NULL;
parser->on_path = NULL;
parser->on_query_string = NULL;

46
deps/http_parser/http_parser.h поставляемый
Просмотреть файл

@ -1,22 +1,22 @@
/* Copyright 2009 Ryan Dahl <ry@tinyclouds.org>
*
/* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
* IN THE SOFTWARE.
*/
#ifndef http_parser_h
#define http_parser_h
@ -30,8 +30,8 @@ extern "C" {
#include <sys/types.h>
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
* faster
*/
* faster
*/
#ifndef HTTP_PARSER_STRICT
# define HTTP_PARSER_STRICT 1
#else
@ -74,8 +74,11 @@ enum http_method
, HTTP_UNLOCK = 0x4000
};
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE };
struct http_parser {
/** PRIVATE **/
enum http_parser_type type;
unsigned short state;
unsigned short header_state;
size_t index;
@ -125,38 +128,15 @@ struct http_parser {
http_cb on_message_complete;
};
void http_parser_init(http_parser *parser);
size_t http_parse_requests(http_parser *parser, const char *data, size_t len);
size_t http_parse_responses(http_parser *parser, const char *data, size_t len);
void http_parser_init(http_parser *parser, enum http_parser_type type);
size_t http_parser_execute(http_parser *parser, const char *data, size_t len);
/* Call this in the on_headers_complete or on_message_complete callback to
* determine if this will be the last message on the connection.
* determine if this will be the last message on the connection.
* If you are the server, respond with the "Connection: close" header
* if you are the client, close the connection.
*/
int http_should_keep_alive(http_parser *parser);
static inline const char * http_method_str (enum http_method method)
{
switch (method) {
case HTTP_DELETE: return "DELETE";
case HTTP_GET: return "GET";
case HTTP_HEAD: return "HEAD";
case HTTP_POST: return "POST";
case HTTP_PUT: return "PUT";
case HTTP_CONNECT: return "CONNECT";
case HTTP_OPTIONS: return "OPTIONS";
case HTTP_TRACE: return "TRACE";
case HTTP_COPY: return "COPY";
case HTTP_LOCK: return "LOCK";
case HTTP_MKCOL: return "MKCOL";
case HTTP_MOVE: return "MOVE";
case HTTP_PROPFIND: return "PROPFIND";
case HTTP_PROPPATCH: return "PROPPATCH";
case HTTP_UNLOCK: return "UNLOCK";
default: return (const char*)0;
}
}
#ifdef __cplusplus
}
#endif

190
deps/http_parser/test.c поставляемый
Просмотреть файл

@ -1,22 +1,22 @@
/* Copyright 2009 Ryan Dahl <ry@tinyclouds.org>
*
/* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
* IN THE SOFTWARE.
*/
#include "http_parser.h"
#include <stdlib.h>
@ -34,14 +34,12 @@
#define MAX_HEADERS 10
#define MAX_ELEMENT_SIZE 500
enum message_type { REQUEST, RESPONSE };
static http_parser *parser;
struct message {
const char *name; // for debugging purposes
const char *raw;
enum message_type type;
enum http_parser_type type;
enum http_method method;
int status_code;
char request_path[MAX_ELEMENT_SIZE];
@ -60,17 +58,16 @@ struct message {
int message_begin_cb_called;
int headers_complete_cb_called;
int message_complete_cb_called;
int message_complete_on_eof;
int eof_indicates_message_end;
};
static int currently_parsing_eof;
inline size_t parse (enum message_type t, const char *buf, size_t len)
inline size_t parse (const char *buf, size_t len)
{
size_t nparsed;
currently_parsing_eof = (len == 0);
nparsed = (t == REQUEST ? http_parse_requests(parser, buf, len)
: http_parse_responses(parser, buf, len));
nparsed = http_parser_execute(parser, buf, len);
return nparsed;
}
@ -81,14 +78,14 @@ static int num_messages;
const struct message requests[] =
#define CURL_GET 0
{ {.name= "curl get"
,.type= REQUEST
,.type= HTTP_REQUEST
,.raw= "GET /test HTTP/1.1\r\n"
"User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\n"
"Host: 0.0.0.0=5000\r\n"
"Accept: */*\r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
@ -107,7 +104,7 @@ const struct message requests[] =
#define FIREFOX_GET 1
, {.name= "firefox get"
,.type= REQUEST
,.type= HTTP_REQUEST
,.raw= "GET /favicon.ico HTTP/1.1\r\n"
"Host: 0.0.0.0=5000\r\n"
"User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n"
@ -119,7 +116,7 @@ const struct message requests[] =
"Connection: keep-alive\r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
@ -143,12 +140,12 @@ const struct message requests[] =
#define DUMBFUCK 2
, {.name= "dumbfuck"
,.type= REQUEST
,.type= HTTP_REQUEST
,.raw= "GET /dumbfuck HTTP/1.1\r\n"
"aaaaaaaaaaaaa:++++++++++\r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
@ -165,11 +162,11 @@ const struct message requests[] =
#define FRAGMENT_IN_URI 3
, {.name= "fragment in url"
,.type= REQUEST
,.type= HTTP_REQUEST
,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
@ -184,11 +181,11 @@ const struct message requests[] =
#define GET_NO_HEADERS_NO_BODY 4
, {.name= "get no headers no body"
,.type= REQUEST
,.type= HTTP_REQUEST
,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE /* would need Connection: close */
,.eof_indicates_message_end= FALSE /* would need Connection: close */
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
@ -202,12 +199,12 @@ const struct message requests[] =
#define GET_ONE_HEADER_NO_BODY 5
, {.name= "get one header no body"
,.type= REQUEST
,.type= HTTP_REQUEST
,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n"
"Accept: */*\r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE /* would need Connection: close */
,.eof_indicates_message_end= FALSE /* would need Connection: close */
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
@ -224,13 +221,13 @@ const struct message requests[] =
#define GET_FUNKY_CONTENT_LENGTH 6
, {.name= "get funky content length body hello"
,.type= REQUEST
,.type= HTTP_REQUEST
,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n"
"conTENT-Length: 5\r\n"
"\r\n"
"HELLO"
,.should_keep_alive= FALSE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 0
,.method= HTTP_GET
@ -247,7 +244,7 @@ const struct message requests[] =
#define POST_IDENTITY_BODY_WORLD 7
, {.name= "post identity body world"
,.type= REQUEST
,.type= HTTP_REQUEST
,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
"Accept: */*\r\n"
"Transfer-Encoding: identity\r\n"
@ -255,7 +252,7 @@ const struct message requests[] =
"\r\n"
"World"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.method= HTTP_POST
@ -274,7 +271,7 @@ const struct message requests[] =
#define POST_CHUNKED_ALL_YOUR_BASE 8
, {.name= "post - chunked body: all your base are belong to us"
,.type= REQUEST
,.type= HTTP_REQUEST
,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
@ -282,7 +279,7 @@ const struct message requests[] =
"0\r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.method= HTTP_POST
@ -299,7 +296,7 @@ const struct message requests[] =
#define TWO_CHUNKS_MULT_ZERO_END 9
, {.name= "two chunks ; triple zero ending"
,.type= REQUEST
,.type= HTTP_REQUEST
,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
@ -308,7 +305,7 @@ const struct message requests[] =
"000\r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.method= HTTP_POST
@ -325,7 +322,7 @@ const struct message requests[] =
#define CHUNKED_W_TRAILING_HEADERS 10
, {.name= "chunked with trailing headers. blech."
,.type= REQUEST
,.type= HTTP_REQUEST
,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
@ -336,7 +333,7 @@ const struct message requests[] =
"Content-Type: text/plain\r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.method= HTTP_POST
@ -355,7 +352,7 @@ const struct message requests[] =
#define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
, {.name= "with bullshit after the length"
,.type= REQUEST
,.type= HTTP_REQUEST
,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
@ -364,7 +361,7 @@ const struct message requests[] =
"0\r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.method= HTTP_POST
@ -381,10 +378,10 @@ const struct message requests[] =
#define WITH_QUOTES 12
, {.name= "with quotes"
,.type= REQUEST
,.type= HTTP_REQUEST
,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
@ -400,17 +397,17 @@ const struct message requests[] =
#define APACHEBENCH_GET 13
/* The server receiving this request SHOULD NOT wait for EOF
* to know that content-length == 0.
* How to represent this in a unit test? message_complete_on_eof
* How to represent this in a unit test? eof_indicates_message_end
* Compare with NO_CONTENT_LENGTH_RESPONSE.
*/
, {.name = "apachebench get"
,.type= REQUEST
,.type= HTTP_REQUEST
,.raw= "GET /test HTTP/1.0\r\n"
"Host: 0.0.0.0:5000\r\n"
"User-Agent: ApacheBench/2.3\r\n"
"Accept: */*\r\n\r\n"
,.should_keep_alive= FALSE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 0
,.method= HTTP_GET
@ -433,7 +430,7 @@ const struct message requests[] =
const struct message responses[] =
#define GOOGLE_301 0
{ {.name= "google 301"
,.type= RESPONSE
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 301 Moved Permanently\r\n"
"Location: http://www.google.com/\r\n"
"Content-Type: text/html; charset=UTF-8\r\n"
@ -450,7 +447,7 @@ const struct message responses[] =
"<A HREF=\"http://www.google.com/\">here</A>.\r\n"
"</BODY></HTML>\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.status_code= 301
@ -479,7 +476,7 @@ const struct message responses[] =
* Compare with APACHEBENCH_GET
*/
, {.name= "no content-length response"
,.type= RESPONSE
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\r\n"
"Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n"
"Server: Apache\r\n"
@ -497,7 +494,7 @@ const struct message responses[] =
" </SOAP-ENV:Body>\n"
"</SOAP-ENV:Envelope>"
,.should_keep_alive= FALSE
,.message_complete_on_eof= TRUE
,.eof_indicates_message_end= TRUE
,.http_major= 1
,.http_minor= 1
,.status_code= 200
@ -522,10 +519,10 @@ const struct message responses[] =
#define NO_HEADERS_NO_BODY_404 2
, {.name= "404 no headers no body"
,.type= RESPONSE
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.status_code= 404
@ -536,10 +533,10 @@ const struct message responses[] =
#define NO_REASON_PHRASE 3
, {.name= "301 no response phrase"
,.type= RESPONSE
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 301\r\n\r\n"
,.should_keep_alive = TRUE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.status_code= 301
@ -550,7 +547,7 @@ const struct message responses[] =
#define TRAILING_SPACE_ON_CHUNKED_BODY 4
, {.name="200 trailing space on chunked body"
,.type= RESPONSE
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Transfer-Encoding: chunked\r\n"
@ -564,7 +561,7 @@ const struct message responses[] =
"0 \r\n"
"\r\n"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.status_code= 200
@ -579,6 +576,52 @@ const struct message responses[] =
}
#define NO_CARRIAGE_RET 5
, {.name="no carriage ret"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\n"
"Content-Type: text/html; charset=utf-8\n"
"Connection: close\n"
"\n"
"these headers are from http://news.ycombinator.com/"
,.should_keep_alive= FALSE
,.eof_indicates_message_end= TRUE
,.http_major= 1
,.http_minor= 1
,.status_code= 200
,.num_headers= 2
,.headers=
{ {"Content-Type", "text/html; charset=utf-8" }
, {"Connection", "close" }
}
,.body= "these headers are from http://news.ycombinator.com/"
}
#define PROXY_CONNECTION 6
, {.name="proxy connection"
,.type= HTTP_RESPONSE
,.raw= "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n"
"Content-Length: 11\r\n"
"Proxy-Connection: close\r\n"
"Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n"
"\r\n"
"hello world"
,.should_keep_alive= FALSE
,.eof_indicates_message_end= FALSE
,.http_major= 1
,.http_minor= 1
,.status_code= 200
,.num_headers= 4
,.headers=
{ {"Content-Type", "text/html; charset=UTF-8" }
, {"Content-Length", "11" }
, {"Proxy-Connection", "close" }
, {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"}
}
,.body= "hello world"
}
, {.name= NULL } /* sentinel */
};
@ -687,14 +730,14 @@ message_complete_cb (http_parser *p)
}
messages[num_messages].message_complete_cb_called = TRUE;
messages[num_messages].message_complete_on_eof = currently_parsing_eof;
messages[num_messages].eof_indicates_message_end = currently_parsing_eof;
num_messages++;
return 0;
}
void
parser_init ()
parser_init (enum http_parser_type type)
{
num_messages = 0;
@ -702,7 +745,7 @@ parser_init ()
parser = malloc(sizeof(http_parser));
http_parser_init(parser);
http_parser_init(parser, type);
memset(&messages, 0, sizeof messages);
@ -770,14 +813,14 @@ message_eq (int index, const struct message *expected)
MESSAGE_CHECK_NUM_EQ(expected, m, http_major);
MESSAGE_CHECK_NUM_EQ(expected, m, http_minor);
if (expected->type == REQUEST) {
if (expected->type == HTTP_REQUEST) {
MESSAGE_CHECK_NUM_EQ(expected, m, method);
} else {
MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
}
MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);
MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof);
MESSAGE_CHECK_NUM_EQ(expected, m, eof_indicates_message_end);
assert(m->message_begin_cb_called);
assert(m->headers_complete_cb_called);
@ -807,7 +850,7 @@ static void
print_error (const char *raw, size_t error_location)
{
fprintf(stderr, "\n*** parse error ***\n\n");
int this_line = 0, char_len = 0;
size_t i, j, len = strlen(raw), error_location_line = 0;
for (i = 0; i < len; i++) {
@ -848,17 +891,17 @@ print_error (const char *raw, size_t error_location)
void
test_message (const struct message *message)
{
parser_init();
parser_init(message->type);
size_t read;
read = parse(message->type, message->raw, strlen(message->raw));
read = parse(message->raw, strlen(message->raw));
if (read != strlen(message->raw)) {
print_error(message->raw, read);
exit(1);
}
read = parse(message->type, NULL, 0);
read = parse(NULL, 0);
if (read != 0) {
print_error(message->raw, read);
exit(1);
@ -877,13 +920,13 @@ test_message (const struct message *message)
void
test_error (const char *buf)
{
parser_init();
parser_init(HTTP_REQUEST);
size_t parsed;
parsed = parse(REQUEST, buf, strlen(buf));
parsed = parse(buf, strlen(buf));
if (parsed != strlen(buf)) goto out;
parsed = parse(REQUEST, NULL, 0);
parsed = parse(NULL, 0);
if (parsed != 0) goto out;
fprintf(stderr, "\n*** Error expected but none found ***\n\n%s", buf);
@ -908,17 +951,17 @@ test_multiple3 (const struct message *r1, const struct message *r2, const struct
strcat(total, r2->raw);
strcat(total, r3->raw);
parser_init();
parser_init(r1->type);
size_t read;
read = parse(r1->type, total, strlen(total));
read = parse(total, strlen(total));
if (read != strlen(total)) {
print_error(total, read);
exit(1);
}
read = parse(REQUEST, NULL, 0);
read = parse(NULL, 0);
if (read != 0) {
print_error(total, read);
exit(1);
@ -971,7 +1014,7 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
}
ops += 1;
parser_init();
parser_init(r1->type);
buf1_len = i;
strncpy(buf1, total, buf1_len);
@ -985,25 +1028,25 @@ test_scan (const struct message *r1, const struct message *r2, const struct mess
strncpy(buf3, total+j, buf3_len);
buf3[buf3_len] = 0;
read = parse(r1->type, buf1, buf1_len);
read = parse(buf1, buf1_len);
if (read != buf1_len) {
print_error(buf1, read);
goto error;
}
read = parse(r1->type, buf2, buf2_len);
read = parse(buf2, buf2_len);
if (read != buf2_len) {
print_error(buf2, read);
goto error;
}
read = parse(r1->type, buf3, buf3_len);
read = parse(buf3, buf3_len);
if (read != buf3_len) {
print_error(buf3, read);
goto error;
}
parse(r1->type, NULL, 0);
parse(NULL, 0);
if (3 != num_messages) {
fprintf(stderr, "\n\nParser didn't see 3 messages only %d\n", num_messages);
@ -1049,9 +1092,6 @@ main (void)
printf("sizeof(http_parser) = %d\n", sizeof(http_parser));
assert(strcmp(http_method_str(HTTP_GET), "GET") == 0);
assert(strcmp(http_method_str(HTTP_CONNECT), "CONNECT") == 0);
for (request_count = 0; requests[request_count].name; request_count++);
for (response_count = 0; responses[response_count].name; response_count++);

7
deps/libeio/Changes поставляемый
Просмотреть файл

@ -1,6 +1,6 @@
Revision history for libeio
TODO: maybe add mincore support? available on at leats darwin, solaris, linux, freebsd
TODO: maybe add mincore support? available on at least darwin, solaris, linux, freebsd
1.0
- readdir: correctly handle malloc failures.
@ -16,4 +16,9 @@ TODO: maybe add mincore support? available on at leats darwin, solaris, linux, f
- fixed custom support.
- use a more robust feed-add detection method.
- "outbundled" from IO::AIO.
- eio_set_max_polltime did not properly convert time to ticks.
- tentatively support darwin in sendfile.
- also use sendfile emulation for ENOTSUP and EOPNOTSUPP
error codes.
- add OS-independent EIO_MT_* and EIO_MS_* flag enums.

65
deps/libeio/eio.c поставляемый
Просмотреть файл

@ -1,7 +1,7 @@
/*
* libeio implementation
*
* Copyright (c) 2007,2008,2009 Marc Alexander Lehmann <libeio@schmorp.de>
* Copyright (c) 2007,2008,2009,2010 Marc Alexander Lehmann <libeio@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
@ -82,11 +82,11 @@
# include <dirent.h>
/* POSIX_SOURCE is useless on bsd's, and XOPEN_SOURCE is unreliable there, too */
# if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
# if __freebsd || defined __NetBSD__ || defined __OpenBSD__
# define _DIRENT_HAVE_D_TYPE /* sigh */
# define D_INO(de) (de)->d_fileno
# define D_NAMLEN(de) (de)->d_namlen
# elif defined(__linux) || defined(d_ino) || _XOPEN_SOURCE >= 600
# elif __linux || defined d_ino || _XOPEN_SOURCE >= 600
# define D_INO(de) (de)->d_ino
# endif
@ -108,12 +108,12 @@
#if HAVE_SENDFILE
# if __linux
# include <sys/sendfile.h>
# elif __freebsd
# elif __freebsd || defined __APPLE__
# include <sys/socket.h>
# include <sys/uio.h>
# elif __hpux
# include <sys/socket.h>
# elif __solaris /* not yet */
# elif __solaris
# include <sys/sendfile.h>
# else
# error sendfile support requested but not available
@ -198,7 +198,7 @@ static void eio_execute (struct etp_worker *self, eio_req *req);
#define ETP_NUM_PRI (ETP_PRI_MAX - ETP_PRI_MIN + 1)
/* calculcate time difference in ~1/EIO_TICKS of a second */
/* calculate time difference in ~1/EIO_TICKS of a second */
static int tvdiff (struct timeval *tv1, struct timeval *tv2)
{
return (tv2->tv_sec - tv1->tv_sec ) * EIO_TICKS
@ -600,7 +600,7 @@ static void etp_submit (ETP_REQ *req)
static void etp_set_max_poll_time (double nseconds)
{
if (WORDACCESS_UNSAFE) X_LOCK (reslock);
max_poll_time = nseconds;
max_poll_time = nseconds * EIO_TICKS;
if (WORDACCESS_UNSAFE) X_UNLOCK (reslock);
}
@ -925,6 +925,16 @@ eio__sendfile (int ofd, int ifd, off_t offset, size_t count, etp_worker *self)
res = sbytes;
}
# elif defined (__APPLE__)
{
off_t sbytes = count;
res = sendfile (ifd, ofd, offset, &sbytes, 0, 0);
if (res < 0 && errno == EAGAIN && sbytes)
res = sbytes;
}
# elif __hpux
res = sendfile (ofd, ifd, offset, count, 0, 0);
@ -945,6 +955,16 @@ eio__sendfile (int ofd, int ifd, off_t offset, size_t count, etp_worker *self)
}
# endif
#elif defined (_WIN32)
/* does not work, just for documentation of what would need to be done */
{
HANDLE h = TO_SOCKET (ifd);
SetFilePointer (h, offset, 0, FILE_BEGIN);
res = TransmitFile (TO_SOCKET (ofd), h, count, 0, 0, 0, 0);
}
#else
res = -1;
errno = ENOSYS;
@ -952,6 +972,11 @@ eio__sendfile (int ofd, int ifd, off_t offset, size_t count, etp_worker *self)
if (res < 0
&& (errno == ENOSYS || errno == EINVAL || errno == ENOTSOCK
/* BSDs */
#ifdef ENOTSUP /* sigh, if the steenking pile called openbsd would only try to at least compile posix code... */
|| errno == ENOTSUP
#endif
|| errno == EOPNOTSUPP /* BSDs */
#if __solaris
|| errno == EAFNOSUPPORT || errno == EPROTOTYPE
#endif
@ -1355,8 +1380,25 @@ eio__scandir (eio_req *req, etp_worker *self)
}
#if !(_POSIX_MAPPED_FILES && _POSIX_SYNCHRONIZED_IO)
# undef msync
# define msync(a,b,c) ((errno = ENOSYS), -1)
# define eio__msync(a,b,c) ((errno = ENOSYS), -1)
#else
int
eio__msync (void *mem, size_t len, int flags)
{
if (EIO_MS_ASYNC != MS_SYNC
|| EIO_MS_INVALIDATE != MS_INVALIDATE
|| EIO_MS_SYNC != MS_SYNC)
{
flags = 0
| (flags & EIO_MS_ASYNC ? MS_ASYNC : 0)
| (flags & EIO_MS_INVALIDATE ? MS_INVALIDATE : 0)
| (flags & EIO_MS_SYNC ? MS_SYNC : 0);
}
return msync (mem, len, flags);
}
#endif
int
@ -1373,10 +1415,11 @@ eio__mtouch (void *mem, size_t len, int flags)
page = sysconf (_SC_PAGESIZE);
#endif
/* round down to start of page, although this is probably useless */
addr &= ~(page - 1); /* assume page size is always a power of two */
if (addr < end)
if (flags) /* modify */
if (flags & EIO_MT_MODIFY) /* modify */
do { *((volatile sig_atomic_t *)addr) |= 0; } while ((addr += page) < len);
else
do { *((volatile sig_atomic_t *)addr) ; } while ((addr += page) < len);
@ -1558,7 +1601,7 @@ static void eio_execute (etp_worker *self, eio_req *req)
case EIO_SYNC: req->result = 0; sync (); break;
case EIO_FSYNC: req->result = fsync (req->int1); break;
case EIO_FDATASYNC: req->result = fdatasync (req->int1); break;
case EIO_MSYNC: req->result = msync (req->ptr2, req->size, req->int1); break;
case EIO_MSYNC: req->result = eio__msync (req->ptr2, req->size, req->int1); break;
case EIO_MTOUCH: req->result = eio__mtouch (req->ptr2, req->size, req->int1); break;
case EIO_SYNC_FILE_RANGE: req->result = eio__sync_file_range (req->int1, req->offs, req->size, req->int2); break;

15
deps/libeio/eio.h поставляемый
Просмотреть файл

@ -1,7 +1,7 @@
/*
* libeio API header
*
* Copyright (c) 2007,2008,2009 Marc Alexander Lehmann <libeio@schmorp.de>
* Copyright (c) 2007,2008,2009,2010 Marc Alexander Lehmann <libeio@schmorp.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modifica-
@ -102,6 +102,19 @@ struct eio_dirent {
ino_t inode; /* the inode number, if available, otherwise unspecified */
};
/* eio_msync flags */
enum {
EIO_MS_ASYNC = 1,
EIO_MS_INVALIDATE = 2,
EIO_MS_SYNC = 4
};
/* eio_mtouch flags */
enum {
EIO_MT_MODIFY = 1
};
/* eio_sync_file_range flags */
enum {

2
deps/libeio/eio.pod поставляемый
Просмотреть файл

@ -29,7 +29,7 @@ It also offers wrappers around C<sendfile> (Solaris, Linux, HP-UX and
FreeBSD, with emulation on other platforms) and C<readahead> (Linux, with
emulation elsewhere>).
The goal is to enbale you to write fully non-blocking programs. For
The goal is to enable you to write fully non-blocking programs. For
example, in a game server, you would not want to freeze for a few seconds
just because the server is running a backup and you happen to call
C<readdir>.

2
deps/libeio/libeio.m4 поставляемый
Просмотреть файл

@ -64,7 +64,7 @@ AC_CACHE_CHECK(for sendfile, ac_cv_sendfile, [AC_LINK_IFELSE([
# include <sys/types.h>
#if __linux
# include <sys/sendfile.h>
#elif __freebsd
#elif __freebsd || defined __APPLE__
# include <sys/socket.h>
# include <sys/uio.h>
#elif __hpux

13
deps/libev/Changes поставляемый
Просмотреть файл

@ -1,6 +1,11 @@
Revision history for libev, a high-performance and full-featured event loop.
TODO: somehow unblock procmask?
3.9 Thu Dec 31 07:59:59 CET 2009
- signalfd is no longer used by default and has to be requested
explicitly - this means that easy to catch bugs become hard to
catch race conditions, but the users have spoken.
- point out the unspecified signal mask in the documentation, and
that this is a race condition regardless of EV_SIGNALFD.
- backport inotify code to C89.
- inotify file descriptors could leak into child processes.
- ev_stat watchers could keep an errornous extra ref on the loop,
@ -10,13 +15,17 @@ TODO: somehow unblock procmask?
symbols to make it easier for apps to do their own fd management.
- support EV_IDLE_ENABLE being disabled in ev++.h
(patch by Didier Spezia).
- point out the unspecified signal mask in the documentation.
- take advantage of inotify_init1, if available, to set cloexec/nonblock
on fd creation, to avoid races.
- the signal handling pipe wasn't always initialised under windows
(analysed by lekma).
- changed minimum glibc requirement from glibc 2.9 to 2.7, for
signalfd.
- add missing string.h include (Denis F. Latypoff).
- only replace ev_stat.prev when we detect an actual difference,
so prev is (almost) always different to attr. this might
have caused the probems with 04_stat.t.
- add ev::timer->remaining () method to C++ API.
3.8 Sun Aug 9 14:30:45 CEST 2009
- incompatible change: do not necessarily reset signal handler

2
deps/libev/configure.ac поставляемый
Просмотреть файл

@ -1,7 +1,7 @@
AC_INIT
AC_CONFIG_SRCDIR([ev_epoll.c])
AM_INIT_AUTOMAKE(libev,3.8) dnl also update ev.h!
AM_INIT_AUTOMAKE(libev,3.9) dnl also update ev.h!
AC_CONFIG_HEADERS([config.h])
AM_MAINTAINER_MODE

5
deps/libev/ev++.h поставляемый
Просмотреть файл

@ -644,6 +644,11 @@ namespace ev {
{
ev_timer_again (EV_A_ static_cast<ev_timer *>(this));
}
ev_tstamp remaining ()
{
return ev_timer_remaining (EV_A_ static_cast<ev_timer *>(this));
}
EV_END_WATCHER (timer, timer)
#if EV_PERIODIC_ENABLE

157
deps/libev/ev.3 поставляемый
Просмотреть файл

@ -124,7 +124,7 @@
.\" ========================================================================
.\"
.IX Title "LIBEV 3"
.TH LIBEV 3 "2009-07-27" "libev-3.8" "libev - high performance full featured event loop"
.TH LIBEV 3 "2009-12-31" "libev-3.9" "libev - high performance full featured event loop"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
@ -248,7 +248,7 @@ configuration will be described, which supports multiple event loops. For
more info about various configuration options please have a look at
\&\fB\s-1EMBED\s0\fR section in this manual. If libev was configured without support
for multiple event loops, then all functions taking an initial argument of
name \f(CW\*(C`loop\*(C'\fR (which is always of type \f(CW\*(C`ev_loop *\*(C'\fR) will not have
name \f(CW\*(C`loop\*(C'\fR (which is always of type \f(CW\*(C`struct ev_loop *\*(C'\fR) will not have
this argument.
.SS "\s-1TIME\s0 \s-1REPRESENTATION\s0"
.IX Subsection "TIME REPRESENTATION"
@ -488,14 +488,19 @@ When this flag is specified, then libev will not attempt to use the
\&\fIinotify\fR \s-1API\s0 for it's \f(CW\*(C`ev_stat\*(C'\fR watchers. Apart from debugging and
testing, this flag can be useful to conserve inotify file descriptors, as
otherwise each loop using \f(CW\*(C`ev_stat\*(C'\fR watchers consumes one inotify handle.
.ie n .IP """EVFLAG_NOSIGNALFD""" 4
.el .IP "\f(CWEVFLAG_NOSIGNALFD\fR" 4
.IX Item "EVFLAG_NOSIGNALFD"
When this flag is specified, then libev will not attempt to use the
\&\fIsignalfd\fR \s-1API\s0 for it's \f(CW\*(C`ev_signal\*(C'\fR (and \f(CW\*(C`ev_child\*(C'\fR) watchers. This is
probably only useful to work around any bugs in libev. Consequently, this
flag might go away once the signalfd functionality is considered stable,
so it's useful mostly in environment variables and not in program code.
.ie n .IP """EVFLAG_SIGNALFD""" 4
.el .IP "\f(CWEVFLAG_SIGNALFD\fR" 4
.IX Item "EVFLAG_SIGNALFD"
When this flag is specified, then libev will attempt to use the
\&\fIsignalfd\fR \s-1API\s0 for it's \f(CW\*(C`ev_signal\*(C'\fR (and \f(CW\*(C`ev_child\*(C'\fR) watchers. This \s-1API\s0
delivers signals synchronously, which makes it both faster and might make
it possible to get the queued signal data. It can also simplify signal
handling with threads, as long as you properly block signals in your
threads that are not interested in handling them.
.Sp
Signalfd will not be used by default as this changes your signal mask, and
there are a lot of shoddy libraries and programs (glib's threadpool for
example) that can't properly initialise their signal masks.
.ie n .IP """EVBACKEND_SELECT"" (value 1, portable select backend)" 4
.el .IP "\f(CWEVBACKEND_SELECT\fR (value 1, portable select backend)" 4
.IX Item "EVBACKEND_SELECT (value 1, portable select backend)"
@ -530,6 +535,9 @@ This backend maps \f(CW\*(C`EV_READ\*(C'\fR to \f(CW\*(C`POLLIN | POLLERR | POLL
.ie n .IP """EVBACKEND_EPOLL"" (value 4, Linux)" 4
.el .IP "\f(CWEVBACKEND_EPOLL\fR (value 4, Linux)" 4
.IX Item "EVBACKEND_EPOLL (value 4, Linux)"
Use the linux-specific \fIepoll\fR\|(7) interface (for both pre\- and post\-2.6.9
kernels).
.Sp
For few fds, this backend is a bit little slower than poll and select,
but it scales phenomenally better. While poll and select usually scale
like O(total_fds) where n is the total number of fds (or the highest fd),
@ -716,7 +724,7 @@ as signal and child watchers) would need to be stopped manually.
In general it is not advisable to call this function except in the
rare occasion where you really need to free e.g. the signal handling
pipe fds. If you need dynamically allocated loops it is better to use
\&\f(CW\*(C`ev_loop_new\*(C'\fR and \f(CW\*(C`ev_loop_destroy\*(C'\fR).
\&\f(CW\*(C`ev_loop_new\*(C'\fR and \f(CW\*(C`ev_loop_destroy\*(C'\fR.
.IP "ev_loop_destroy (loop)" 4
.IX Item "ev_loop_destroy (loop)"
Like \f(CW\*(C`ev_default_destroy\*(C'\fR, but destroys an event loop created by an
@ -823,8 +831,8 @@ event loop time (see \f(CW\*(C`ev_now_update\*(C'\fR).
.IP "ev_loop (loop, int flags)" 4
.IX Item "ev_loop (loop, int flags)"
Finally, this is it, the event handler. This function usually is called
after you initialised all your watchers and you want to start handling
events.
after you have initialised all your watchers and you want to start
handling events.
.Sp
If the flags argument is specified as \f(CW0\fR, it will not return until
either no event watchers are active anymore or \f(CW\*(C`ev_unloop\*(C'\fR was called.
@ -912,9 +920,10 @@ Ref/unref can be used to add or remove a reference count on the event
loop: Every watcher keeps one reference, and as long as the reference
count is nonzero, \f(CW\*(C`ev_loop\*(C'\fR will not return on its own.
.Sp
If you have a watcher you never unregister that should not keep \f(CW\*(C`ev_loop\*(C'\fR
from returning, call \fIev_unref()\fR after starting, and \fIev_ref()\fR before
stopping it.
This is useful when you have a watcher that you never intend to
unregister, but that nevertheless should not keep \f(CW\*(C`ev_loop\*(C'\fR from
returning. In such a case, call \f(CW\*(C`ev_unref\*(C'\fR after starting, and \f(CW\*(C`ev_ref\*(C'\fR
before stopping it.
.Sp
As an example, libev itself uses this for its internal signal pipe: It
is not visible to the libev user and should not keep \f(CW\*(C`ev_loop\*(C'\fR from
@ -1042,7 +1051,7 @@ While event loop modifications are allowed between invocations of
\&\f(CW\*(C`release\*(C'\fR and \f(CW\*(C`acquire\*(C'\fR (that's their only purpose after all), no
modifications done will affect the event loop, i.e. adding watchers will
have no effect on the set of file descriptors being watched, or the time
waited. USe an \f(CW\*(C`ev_async\*(C'\fR watcher to wake up \f(CW\*(C`ev_loop\*(C'\fR when you want it
waited. Use an \f(CW\*(C`ev_async\*(C'\fR watcher to wake up \f(CW\*(C`ev_loop\*(C'\fR when you want it
to take note of any changes you made.
.Sp
In theory, threads executing \f(CW\*(C`ev_loop\*(C'\fR will be async-cancel safe between
@ -1247,9 +1256,9 @@ Example: Initialise an \f(CW\*(C`ev_io\*(C'\fR watcher in two steps.
\& ev_init (&w, my_cb);
\& ev_io_set (&w, STDIN_FILENO, EV_READ);
.Ve
.ie n .IP """ev_TYPE_set"" (ev_TYPE *, [args])" 4
.el .IP "\f(CWev_TYPE_set\fR (ev_TYPE *, [args])" 4
.IX Item "ev_TYPE_set (ev_TYPE *, [args])"
.ie n .IP """ev_TYPE_set"" (ev_TYPE *watcher, [args])" 4
.el .IP "\f(CWev_TYPE_set\fR (ev_TYPE *watcher, [args])" 4
.IX Item "ev_TYPE_set (ev_TYPE *watcher, [args])"
This macro initialises the type-specific parts of a watcher. You need to
call \f(CW\*(C`ev_init\*(C'\fR at least once before you call this macro, but you can
call \f(CW\*(C`ev_TYPE_set\*(C'\fR any number of times. You must not, however, call this
@ -1272,9 +1281,9 @@ Example: Initialise and set an \f(CW\*(C`ev_io\*(C'\fR watcher in one step.
.Vb 1
\& ev_io_init (&w, my_cb, STDIN_FILENO, EV_READ);
.Ve
.ie n .IP """ev_TYPE_start"" (loop *, ev_TYPE *watcher)" 4
.el .IP "\f(CWev_TYPE_start\fR (loop *, ev_TYPE *watcher)" 4
.IX Item "ev_TYPE_start (loop *, ev_TYPE *watcher)"
.ie n .IP """ev_TYPE_start"" (loop, ev_TYPE *watcher)" 4
.el .IP "\f(CWev_TYPE_start\fR (loop, ev_TYPE *watcher)" 4
.IX Item "ev_TYPE_start (loop, ev_TYPE *watcher)"
Starts (activates) the given watcher. Only active watchers will receive
events. If the watcher is already active nothing will happen.
.Sp
@ -1284,9 +1293,9 @@ whole section.
.Vb 1
\& ev_io_start (EV_DEFAULT_UC, &w);
.Ve
.ie n .IP """ev_TYPE_stop"" (loop *, ev_TYPE *watcher)" 4
.el .IP "\f(CWev_TYPE_stop\fR (loop *, ev_TYPE *watcher)" 4
.IX Item "ev_TYPE_stop (loop *, ev_TYPE *watcher)"
.ie n .IP """ev_TYPE_stop"" (loop, ev_TYPE *watcher)" 4
.el .IP "\f(CWev_TYPE_stop\fR (loop, ev_TYPE *watcher)" 4
.IX Item "ev_TYPE_stop (loop, ev_TYPE *watcher)"
Stops the given watcher if active, and clears the pending status (whether
the watcher was active or not).
.Sp
@ -1315,8 +1324,8 @@ Returns the callback currently set on the watcher.
.IX Item "ev_cb_set (ev_TYPE *watcher, callback)"
Change the callback. You can change the callback at virtually any time
(modulo threads).
.IP "ev_set_priority (ev_TYPE *watcher, priority)" 4
.IX Item "ev_set_priority (ev_TYPE *watcher, priority)"
.IP "ev_set_priority (ev_TYPE *watcher, int priority)" 4
.IX Item "ev_set_priority (ev_TYPE *watcher, int priority)"
.PD 0
.IP "int ev_priority (ev_TYPE *watcher)" 4
.IX Item "int ev_priority (ev_TYPE *watcher)"
@ -1356,6 +1365,19 @@ watcher isn't pending it does nothing and returns \f(CW0\fR.
.Sp
Sometimes it can be useful to \*(L"poll\*(R" a watcher instead of waiting for its
callback to be invoked, which can be accomplished with this function.
.IP "ev_feed_event (loop, ev_TYPE *watcher, int revents)" 4
.IX Item "ev_feed_event (loop, ev_TYPE *watcher, int revents)"
Feeds the given event set into the event loop, as if the specified event
had happened for the specified watcher (which must be a pointer to an
initialised but not necessarily started event watcher). Obviously you must
not free the watcher as long as it has pending events.
.Sp
Stopping the watcher, letting libev invoke it, or calling
\&\f(CW\*(C`ev_clear_pending\*(C'\fR will clear the pending event, even if the watcher was
not started in the first place.
.Sp
See also \f(CW\*(C`ev_feed_fd_event\*(C'\fR and \f(CW\*(C`ev_feed_signal_event\*(C'\fR for related
functions that do not need a watcher.
.SS "\s-1ASSOCIATING\s0 \s-1CUSTOM\s0 \s-1DATA\s0 \s-1WITH\s0 A \s-1WATCHER\s0"
.IX Subsection "ASSOCIATING CUSTOM DATA WITH A WATCHER"
Each watcher has, by default, a member \f(CW\*(C`void *data\*(C'\fR that you can change
@ -1976,8 +1998,8 @@ If the timer is repeating, either start it if necessary (with the
.Sp
This sounds a bit complicated, see \*(L"Be smart about timeouts\*(R", above, for a
usage example.
.IP "ev_timer_remaining (loop, ev_timer *)" 4
.IX Item "ev_timer_remaining (loop, ev_timer *)"
.IP "ev_tstamp ev_timer_remaining (loop, ev_timer *)" 4
.IX Item "ev_tstamp ev_timer_remaining (loop, ev_timer *)"
Returns the remaining time until a timer fires. If the timer is active,
then this time is relative to the current event loop time, otherwise it's
the timeout value currently configured.
@ -2251,17 +2273,42 @@ When the first watcher gets started will libev actually register something
with the kernel (thus it coexists with your own signal handlers as long as
you don't register any with libev for the same signal).
.PP
Both the signal mask state (\f(CW\*(C`sigprocmask\*(C'\fR) and the signal handler state
(\f(CW\*(C`sigaction\*(C'\fR) are unspecified after starting a signal watcher (and after
sotpping it again), that is, libev might or might not block the signal,
and might or might not set or restore the installed signal handler.
.PP
If possible and supported, libev will install its handlers with
\&\f(CW\*(C`SA_RESTART\*(C'\fR (or equivalent) behaviour enabled, so system calls should
not be unduly interrupted. If you have a problem with system calls getting
interrupted by signals you can block all signals in an \f(CW\*(C`ev_check\*(C'\fR watcher
and unblock them in an \f(CW\*(C`ev_prepare\*(C'\fR watcher.
.PP
\fIThe special problem of inheritance over fork/execve/pthread_create\fR
.IX Subsection "The special problem of inheritance over fork/execve/pthread_create"
.PP
Both the signal mask (\f(CW\*(C`sigprocmask\*(C'\fR) and the signal disposition
(\f(CW\*(C`sigaction\*(C'\fR) are unspecified after starting a signal watcher (and after
stopping it again), that is, libev might or might not block the signal,
and might or might not set or restore the installed signal handler.
.PP
While this does not matter for the signal disposition (libev never
sets signals to \f(CW\*(C`SIG_IGN\*(C'\fR, so handlers will be reset to \f(CW\*(C`SIG_DFL\*(C'\fR on
\&\f(CW\*(C`execve\*(C'\fR), this matters for the signal mask: many programs do not expect
certain signals to be blocked.
.PP
This means that before calling \f(CW\*(C`exec\*(C'\fR (from the child) you should reset
the signal mask to whatever \*(L"default\*(R" you expect (all clear is a good
choice usually).
.PP
The simplest way to ensure that the signal mask is reset in the child is
to install a fork handler with \f(CW\*(C`pthread_atfork\*(C'\fR that resets it. That will
catch fork calls done by libraries (such as the libc) as well.
.PP
In current versions of libev, the signal will not be blocked indefinitely
unless you use the \f(CW\*(C`signalfd\*(C'\fR \s-1API\s0 (\f(CW\*(C`EV_SIGNALFD\*(C'\fR). While this reduces
the window of opportunity for problems, it will not go away, as libev
\&\fIhas\fR to modify the signal mask, at least temporarily.
.PP
So I can't stress this enough: \fIIf you do not reset your signal mask when
you expect it to be empty, you have a race condition in your code\fR. This
is not a libev-specific thing, this is true for most event libraries.
.PP
\fIWatcher-Specific Functions and Data Members\fR
.IX Subsection "Watcher-Specific Functions and Data Members"
.IP "ev_signal_init (ev_signal *, callback, int signum)" 4
@ -3087,7 +3134,8 @@ just the default loop.
\&\f(CW\*(C`ev_async\*(C'\fR does not support queueing of data in any way. The reason
is that the author does not know of a simple (or any) algorithm for a
multiple-writer-single-reader queue that works in all cases and doesn't
need elaborate support such as pthreads.
need elaborate support such as pthreads or unportable memory access
semantics.
.PP
That means that if you want to queue data, you have to provide your own
queue. But at least I can tell you how to implement locking around your
@ -3242,17 +3290,12 @@ Example: wait up to ten seconds for data to appear on \s-1STDIN_FILENO\s0.
\&
\& ev_once (STDIN_FILENO, EV_READ, 10., stdin_ready, 0);
.Ve
.IP "ev_feed_event (struct ev_loop *, watcher *, int revents)" 4
.IX Item "ev_feed_event (struct ev_loop *, watcher *, int revents)"
Feeds the given event set into the event loop, as if the specified event
had happened for the specified watcher (which must be a pointer to an
initialised but not necessarily started event watcher).
.IP "ev_feed_fd_event (struct ev_loop *, int fd, int revents)" 4
.IX Item "ev_feed_fd_event (struct ev_loop *, int fd, int revents)"
.IP "ev_feed_fd_event (loop, int fd, int revents)" 4
.IX Item "ev_feed_fd_event (loop, int fd, int revents)"
Feed an event on the given fd, as if a file descriptor backend detected
the given events it.
.IP "ev_feed_signal_event (struct ev_loop *loop, int signum)" 4
.IX Item "ev_feed_signal_event (struct ev_loop *loop, int signum)"
.IP "ev_feed_signal_event (loop, int signum)" 4
.IX Item "ev_feed_signal_event (loop, int signum)"
Feed an event as if the given signal occurred (\f(CW\*(C`loop\*(C'\fR must be the default
loop!).
.SH "LIBEVENT EMULATION"
@ -3331,8 +3374,8 @@ All of those classes have these methods:
.IP "ev::TYPE::TYPE ()" 4
.IX Item "ev::TYPE::TYPE ()"
.PD 0
.IP "ev::TYPE::TYPE (struct ev_loop *)" 4
.IX Item "ev::TYPE::TYPE (struct ev_loop *)"
.IP "ev::TYPE::TYPE (loop)" 4
.IX Item "ev::TYPE::TYPE (loop)"
.IP "ev::TYPE::~TYPE" 4
.IX Item "ev::TYPE::~TYPE"
.PD
@ -3421,8 +3464,8 @@ Example: Use a plain function as callback.
\& static void io_cb (ev::io &w, int revents) { }
\& iow.set <io_cb> ();
.Ve
.IP "w\->set (struct ev_loop *)" 4
.IX Item "w->set (struct ev_loop *)"
.IP "w\->set (loop)" 4
.IX Item "w->set (loop)"
Associates a different \f(CW\*(C`struct ev_loop\*(C'\fR with this watcher. You can only
do this when the watcher is inactive (and not pending either).
.IP "w\->set ([arguments])" 4
@ -3771,13 +3814,25 @@ be used is the winsock select). This means that it will call
\&\f(CW\*(C`_get_osfhandle\*(C'\fR on the fd to convert it to an \s-1OS\s0 handle. Otherwise,
it is assumed that all these functions actually work on fds, even
on win32. Should not be defined on non\-win32 platforms.
.IP "\s-1EV_FD_TO_WIN32_HANDLE\s0" 4
.IX Item "EV_FD_TO_WIN32_HANDLE"
.IP "\s-1EV_FD_TO_WIN32_HANDLE\s0(fd)" 4
.IX Item "EV_FD_TO_WIN32_HANDLE(fd)"
If \f(CW\*(C`EV_SELECT_IS_WINSOCKET\*(C'\fR is enabled, then libev needs a way to map
file descriptors to socket handles. When not defining this symbol (the
default), then libev will call \f(CW\*(C`_get_osfhandle\*(C'\fR, which is usually
correct. In some cases, programs use their own file descriptor management,
in which case they can provide this function to map fds to socket handles.
.IP "\s-1EV_WIN32_HANDLE_TO_FD\s0(handle)" 4
.IX Item "EV_WIN32_HANDLE_TO_FD(handle)"
If \f(CW\*(C`EV_SELECT_IS_WINSOCKET\*(C'\fR then libev maps handles to file descriptors
using the standard \f(CW\*(C`_open_osfhandle\*(C'\fR function. For programs implementing
their own fd to handle mapping, overwriting this function makes it easier
to do so. This can be done by defining this macro to an appropriate value.
.IP "\s-1EV_WIN32_CLOSE_FD\s0(fd)" 4
.IX Item "EV_WIN32_CLOSE_FD(fd)"
If programs implement their own fd to handle mapping on win32, then this
macro can be used to override the \f(CW\*(C`close\*(C'\fR function, useful to unregister
file descriptors again. Note that the replacement function has to close
the underlying \s-1OS\s0 handle.
.IP "\s-1EV_USE_POLL\s0" 4
.IX Item "EV_USE_POLL"
If defined to be \f(CW1\fR, libev will compile in support for the \f(CW\*(C`poll\*(C'\fR(2)

43
deps/libev/ev.c поставляемый
Просмотреть файл

@ -155,6 +155,7 @@ extern "C" {
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stddef.h>
@ -1606,7 +1607,7 @@ loop_init (EV_P_ unsigned int flags)
fs_fd = flags & EVFLAG_NOINOTIFY ? -1 : -2;
#endif
#if EV_USE_SIGNALFD
sigfd = flags & EVFLAG_NOSIGFD ? -1 : -2;
sigfd = flags & EVFLAG_SIGNALFD ? -2 : -1;
#endif
if (!(flags & 0x0000ffffU))
@ -2791,11 +2792,14 @@ ev_signal_stop (EV_P_ ev_signal *w)
#if EV_USE_SIGNALFD
if (sigfd >= 0)
{
sigprocmask (SIG_UNBLOCK, &sigfd_set, 0);//D
sigset_t ss;
sigemptyset (&ss);
sigaddset (&ss, w->signum);
sigdelset (&sigfd_set, w->signum);
signalfd (sigfd, &sigfd_set, 0);
sigprocmask (SIG_BLOCK, &sigfd_set, 0);//D
/*TODO: maybe unblock signal? */
sigprocmask (SIG_UNBLOCK, &ss, 0);
}
else
#endif
@ -3102,25 +3106,28 @@ stat_timer_cb (EV_P_ ev_timer *w_, int revents)
{
ev_stat *w = (ev_stat *)(((char *)w_) - offsetof (ev_stat, timer));
/* we copy this here each the time so that */
/* prev has the old value when the callback gets invoked */
w->prev = w->attr;
ev_statdata prev = w->attr;
ev_stat_stat (EV_A_ w);
/* memcmp doesn't work on netbsd, they.... do stuff to their struct stat */
if (
w->prev.st_dev != w->attr.st_dev
|| w->prev.st_ino != w->attr.st_ino
|| w->prev.st_mode != w->attr.st_mode
|| w->prev.st_nlink != w->attr.st_nlink
|| w->prev.st_uid != w->attr.st_uid
|| w->prev.st_gid != w->attr.st_gid
|| w->prev.st_rdev != w->attr.st_rdev
|| w->prev.st_size != w->attr.st_size
|| w->prev.st_atime != w->attr.st_atime
|| w->prev.st_mtime != w->attr.st_mtime
|| w->prev.st_ctime != w->attr.st_ctime
prev.st_dev != w->attr.st_dev
|| prev.st_ino != w->attr.st_ino
|| prev.st_mode != w->attr.st_mode
|| prev.st_nlink != w->attr.st_nlink
|| prev.st_uid != w->attr.st_uid
|| prev.st_gid != w->attr.st_gid
|| prev.st_rdev != w->attr.st_rdev
|| prev.st_size != w->attr.st_size
|| prev.st_atime != w->attr.st_atime
|| prev.st_mtime != w->attr.st_mtime
|| prev.st_ctime != w->attr.st_ctime
) {
/* we only update w->prev on actual differences */
/* in case we test more often than invoke the callback, */
/* to ensure that prev is always different to attr */
w->prev = prev;
#if EV_USE_INOTIFY
if (fs_fd >= 0)
{

5
deps/libev/ev.h поставляемый
Просмотреть файл

@ -163,7 +163,7 @@ struct ev_loop;
#endif
#define EV_VERSION_MAJOR 3
#define EV_VERSION_MINOR 8
#define EV_VERSION_MINOR 9
#ifndef EV_CB_DECLARE
# define EV_CB_DECLARE(type) void (*cb)(EV_P_ struct type *w, int revents);
@ -406,7 +406,8 @@ union ev_any_watcher
#define EVFLAG_FORKCHECK 0x02000000U /* check for a fork in each iteration */
/* debugging/feature disable */
#define EVFLAG_NOINOTIFY 0x00100000U /* do not attempt to use inotify */
#define EVFLAG_NOSIGFD 0x00200000U /* do not attempt to use signalfd */
#define EVFLAG_NOSIGFD 0 /* compatibility to pre-3.9 */
#define EVFLAG_SIGNALFD 0x00200000U /* attempt to use signalfd */
/* method bits to be ored together */
#define EVBACKEND_SELECT 0x00000001U /* about anywhere */
#define EVBACKEND_POLL 0x00000002U /* !win */

86
deps/libev/ev.pod поставляемый
Просмотреть файл

@ -120,7 +120,7 @@ configuration will be described, which supports multiple event loops. For
more info about various configuration options please have a look at
B<EMBED> section in this manual. If libev was configured without support
for multiple event loops, then all functions taking an initial argument of
name C<loop> (which is always of type C<ev_loop *>) will not have
name C<loop> (which is always of type C<struct ev_loop *>) will not have
this argument.
=head2 TIME REPRESENTATION
@ -372,13 +372,18 @@ I<inotify> API for it's C<ev_stat> watchers. Apart from debugging and
testing, this flag can be useful to conserve inotify file descriptors, as
otherwise each loop using C<ev_stat> watchers consumes one inotify handle.
=item C<EVFLAG_NOSIGFD>
=item C<EVFLAG_SIGNALFD>
When this flag is specified, then libev will not attempt to use the
I<signalfd> API for it's C<ev_signal> (and C<ev_child>) watchers. This is
probably only useful to work around any bugs in libev. Consequently, this
flag might go away once the signalfd functionality is considered stable,
so it's useful mostly in environment variables and not in program code.
When this flag is specified, then libev will attempt to use the
I<signalfd> API for it's C<ev_signal> (and C<ev_child>) watchers. This API
delivers signals synchronously, which makes it both faster and might make
it possible to get the queued signal data. It can also simplify signal
handling with threads, as long as you properly block signals in your
threads that are not interested in handling them.
Signalfd will not be used by default as this changes your signal mask, and
there are a lot of shoddy libraries and programs (glib's threadpool for
example) that can't properly initialise their signal masks.
=item C<EVBACKEND_SELECT> (value 1, portable select backend)
@ -413,6 +418,9 @@ C<EV_WRITE> to C<POLLOUT | POLLERR | POLLHUP>.
=item C<EVBACKEND_EPOLL> (value 4, Linux)
Use the linux-specific epoll(7) interface (for both pre- and post-2.6.9
kernels).
For few fds, this backend is a bit little slower than poll and select,
but it scales phenomenally better. While poll and select usually scale
like O(total_fds) where n is the total number of fds (or the highest fd),
@ -791,9 +799,10 @@ Ref/unref can be used to add or remove a reference count on the event
loop: Every watcher keeps one reference, and as long as the reference
count is nonzero, C<ev_loop> will not return on its own.
If you have a watcher you never unregister that should not keep C<ev_loop>
from returning, call ev_unref() after starting, and ev_ref() before
stopping it.
This is useful when you have a watcher that you never intend to
unregister, but that nevertheless should not keep C<ev_loop> from
returning. In such a case, call C<ev_unref> after starting, and C<ev_ref>
before stopping it.
As an example, libev itself uses this for its internal signal pipe: It
is not visible to the libev user and should not keep C<ev_loop> from
@ -918,7 +927,7 @@ While event loop modifications are allowed between invocations of
C<release> and C<acquire> (that's their only purpose after all), no
modifications done will affect the event loop, i.e. adding watchers will
have no effect on the set of file descriptors being watched, or the time
waited. USe an C<ev_async> watcher to wake up C<ev_loop> when you want it
waited. Use an C<ev_async> watcher to wake up C<ev_loop> when you want it
to take note of any changes you made.
In theory, threads executing C<ev_loop> will be async-cancel safe between
@ -1125,7 +1134,7 @@ Example: Initialise an C<ev_io> watcher in two steps.
ev_init (&w, my_cb);
ev_io_set (&w, STDIN_FILENO, EV_READ);
=item C<ev_TYPE_set> (ev_TYPE *, [args])
=item C<ev_TYPE_set> (ev_TYPE *watcher, [args])
This macro initialises the type-specific parts of a watcher. You need to
call C<ev_init> at least once before you call this macro, but you can
@ -1148,7 +1157,7 @@ Example: Initialise and set an C<ev_io> watcher in one step.
ev_io_init (&w, my_cb, STDIN_FILENO, EV_READ);
=item C<ev_TYPE_start> (loop *, ev_TYPE *watcher)
=item C<ev_TYPE_start> (loop, ev_TYPE *watcher)
Starts (activates) the given watcher. Only active watchers will receive
events. If the watcher is already active nothing will happen.
@ -1158,7 +1167,7 @@ whole section.
ev_io_start (EV_DEFAULT_UC, &w);
=item C<ev_TYPE_stop> (loop *, ev_TYPE *watcher)
=item C<ev_TYPE_stop> (loop, ev_TYPE *watcher)
Stops the given watcher if active, and clears the pending status (whether
the watcher was active or not).
@ -1193,7 +1202,7 @@ Returns the callback currently set on the watcher.
Change the callback. You can change the callback at virtually any time
(modulo threads).
=item ev_set_priority (ev_TYPE *watcher, priority)
=item ev_set_priority (ev_TYPE *watcher, int priority)
=item int ev_priority (ev_TYPE *watcher)
@ -1235,6 +1244,20 @@ watcher isn't pending it does nothing and returns C<0>.
Sometimes it can be useful to "poll" a watcher instead of waiting for its
callback to be invoked, which can be accomplished with this function.
=item ev_feed_event (loop, ev_TYPE *watcher, int revents)
Feeds the given event set into the event loop, as if the specified event
had happened for the specified watcher (which must be a pointer to an
initialised but not necessarily started event watcher). Obviously you must
not free the watcher as long as it has pending events.
Stopping the watcher, letting libev invoke it, or calling
C<ev_clear_pending> will clear the pending event, even if the watcher was
not started in the first place.
See also C<ev_feed_fd_event> and C<ev_feed_signal_event> for related
functions that do not need a watcher.
=back
@ -1839,7 +1862,7 @@ C<repeat> value), or reset the running timer to the C<repeat> value.
This sounds a bit complicated, see L<Be smart about timeouts>, above, for a
usage example.
=item ev_timer_remaining (loop, ev_timer *)
=item ev_tstamp ev_timer_remaining (loop, ev_timer *)
Returns the remaining time until a timer fires. If the timer is active,
then this time is relative to the current event loop time, otherwise it's
@ -2116,7 +2139,7 @@ not be unduly interrupted. If you have a problem with system calls getting
interrupted by signals you can block all signals in an C<ev_check> watcher
and unblock them in an C<ev_prepare> watcher.
=head3 The special problem of inheritance over execve
=head3 The special problem of inheritance over fork/execve/pthread_create
Both the signal mask (C<sigprocmask>) and the signal disposition
(C<sigaction>) are unspecified after starting a signal watcher (and after
@ -2136,10 +2159,14 @@ The simplest way to ensure that the signal mask is reset in the child is
to install a fork handler with C<pthread_atfork> that resets it. That will
catch fork calls done by libraries (such as the libc) as well.
In current versions of libev, you can also ensure that the signal mask is
not blocking any signals (except temporarily, so thread users watch out)
by specifying the C<EVFLAG_NOSIGFD> when creating the event loop. This
is not guaranteed for future versions, however.
In current versions of libev, the signal will not be blocked indefinitely
unless you use the C<signalfd> API (C<EV_SIGNALFD>). While this reduces
the window of opportunity for problems, it will not go away, as libev
I<has> to modify the signal mask, at least temporarily.
So I can't stress this enough: I<If you do not reset your signal mask when
you expect it to be empty, you have a race condition in your code>. This
is not a libev-specific thing, this is true for most event libraries.
=head3 Watcher-Specific Functions and Data Members
@ -2966,7 +2993,8 @@ just the default loop.
C<ev_async> does not support queueing of data in any way. The reason
is that the author does not know of a simple (or any) algorithm for a
multiple-writer-single-reader queue that works in all cases and doesn't
need elaborate support such as pthreads.
need elaborate support such as pthreads or unportable memory access
semantics.
That means that if you want to queue data, you have to provide your own
queue. But at least I can tell you how to implement locking around your
@ -3134,18 +3162,12 @@ Example: wait up to ten seconds for data to appear on STDIN_FILENO.
ev_once (STDIN_FILENO, EV_READ, 10., stdin_ready, 0);
=item ev_feed_event (struct ev_loop *, watcher *, int revents)
Feeds the given event set into the event loop, as if the specified event
had happened for the specified watcher (which must be a pointer to an
initialised but not necessarily started event watcher).
=item ev_feed_fd_event (struct ev_loop *, int fd, int revents)
=item ev_feed_fd_event (loop, int fd, int revents)
Feed an event on the given fd, as if a file descriptor backend detected
the given events it.
=item ev_feed_signal_event (struct ev_loop *loop, int signum)
=item ev_feed_signal_event (loop, int signum)
Feed an event as if the given signal occurred (C<loop> must be the default
loop!).
@ -3235,7 +3257,7 @@ All of those classes have these methods:
=item ev::TYPE::TYPE ()
=item ev::TYPE::TYPE (struct ev_loop *)
=item ev::TYPE::TYPE (loop)
=item ev::TYPE::~TYPE
@ -3322,7 +3344,7 @@ Example: Use a plain function as callback.
static void io_cb (ev::io &w, int revents) { }
iow.set <io_cb> ();
=item w->set (struct ev_loop *)
=item w->set (loop)
Associates a different C<struct ev_loop> with this watcher. You can only
do this when the watcher is inactive (and not pending either).

2
deps/libev/ev_epoll.c поставляемый
Просмотреть файл

@ -161,6 +161,8 @@ epoll_poll (EV_P_ ev_tstamp timeout)
ev->events = (want & EV_READ ? EPOLLIN : 0)
| (want & EV_WRITE ? EPOLLOUT : 0);
/* pre-2.6.9 kernels require a non-null pointer with EPOLL_CTL_DEL, */
/* which is fortunately easy to do for us. */
if (epoll_ctl (backend_fd, want ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, fd, ev))
{
postfork = 1; /* an error occured, recreate kernel state */

Просмотреть файл

@ -1,7 +1,7 @@
NODE(1)
=======
Ryan Dahl <ry@tinyclouds.org>
Version, 0.1.23, 2009.12.22
Version, 0.1.25, 2010.01.09
== NAME
@ -818,8 +818,8 @@ The request method as a string. Read only. Example:
+"GET"+, +"DELETE"+.
+request.uri+ ::
Request URI Object. This contains only the parameters that are
+request.url+ ::
Request URL string. This contains only the URL that is
present in the actual HTTP request. If the request is
+
----------------------------------------
@ -828,16 +828,41 @@ Accept: text/plain\r\n
\r\n
----------------------------------------
+
Then +request.uri+ will be
Then +request.url+ will be
+
----------------------------------------
{ full: "/status?name=ryan",
path: "/status",
queryString: "name=ryan",
params: { "name": "ryan" },
fragment: ""
"/status?name=ryan"
----------------------------------------
+
If you would like to parse the URL into its parts, you can use
+require("url").parse(request.url)+. Example:
+
----------------------------------------
node> require("url").parse("/status?name=ryan")
{
"href": "/status?name=ryan",
"search": "?name=ryan",
"query": "name=ryan",
"pathname": "/status"
}
----------------------------------------
+
If you would like to extract the params from the query string,
you can use the +require("querystring").parse+ function, or pass
+true+ as the second argument to +require("url").parse+. Example:
+
----------------------------------------
node> require("url").parse("/status?name=ryan", true)
{
"href": "/status?name=ryan",
"search": "?name=ryan",
"query": {
"name": "ryan"
},
"pathname": "/status"
}
----------------------------------------
+
+request.headers+ ::
@ -1109,9 +1134,11 @@ Node. To use it, +require("multipart")+.
+multipart.parse(options)+ ::
- on success: Returns an object where each key holds the value of one part of
the stream. +options+ can either be an instance of
+http.ServerRequest+ or an object containing a "boundary" and a
"data" key.
- on error: no parameters.
+http.ServerRequest+ or an object containing a "boundary" and
an optional "data" key.
- on error: Returns an instanceof Error object. Right now only the request
content-type / boundary option is checked. The stream data itself
is not validated.
==== +multipart.Stream+
@ -1464,6 +1491,180 @@ Expects +block+ to throw an error.
+assert.doesNotThrow(block, error, message)+::
Expects +block+ not to throw an error.
=== Path Module
This module contains utilities for dealing with file paths. Use
+require('path')+ to use it. It provides the following methods:
+path.join(/* path1, path2, ... */)+::
Join all arguments together and resolve the resulting path. Example:
+
------------------------------------
node> require("path").join("/foo", "bar", "baz/asdf", "quux", "..")
"/foo/bar/baz/asdf"
------------------------------------
+
+path.normalizeArray(arr)+::
Normalize an array of path parts, taking care of +".."+ and +"."+ parts. Example:
+
------------------------------------
node> require("path").normalizeArray(["", "foo", "bar", "baz", "asdf", "quux", ".."])
[
"",
"foo",
"bar",
"baz",
"asdf"
]
------------------------------------
+
+path.normalize(p)+::
Normalize a string path, taking care of +".."+ and +"."+ parts. Example:
+
------------------------------------
node> require("path").normalize("/foo/bar/baz/asdf/quux/..")
"/foo/bar/baz/asdf"
------------------------------------
+
+path.dirname(p)+::
Return the directory name of a path. Similar to the Unix +dirname+ command. Example:
+
------------------------------------
node> require("path").dirname("/foo/bar/baz/asdf/quux")
"/foo/bar/baz/asdf"
------------------------------------
+
+path.basename(p, ext)+::
Return the last portion of a path. Similar to the Unix +basename+ command. Example:
+
------------------------------------
node> require("path").filename("/foo/bar/baz/asdf/quux.html")
"quux.html"
node> require("path").filename("/foo/bar/baz/asdf/quux.html", ".html")
"quux"
------------------------------------
+
+path.extname(p)+::
Return the extension of the path. Everything after the last '.', if there
is no '.' then it returns an empty string. Examples:
+
------------------------------------
node> require("path").extname("index.html")
".html"
node> require("path").extname("index")
""
------------------------------------
+
+path.exists(p, callback)+::
Test whether or not the given path exists. Then, call the +callback+ argument with either true or false. Example:
+
------------------------------------
require("path").exists("/etc/passwd", function (exists) {
require("sys").debug( exists ? "it's there" : "no passwd!" );
});
------------------------------------
=== URL Module
This module has utilities for URL resolution and parsing.
Parsed URL objects have some or all of the following fields, depending on whether or not
they exist in the URL string. Any parts that are not in the URL string will not be in the
parsed object. Examples are shown for the URL +"http://user:pass@host.com:8080/p/a/t/h?query=string#hash"+
+href+::
The full URL that was originally parsed. Example: +"http://user:pass@host.com:8080/p/a/t/h?query=string#hash"+
+protocol+::
The request protocol. Example: +"http:"+
+host+::
The full host portion of the URL, including port and authentication information. Example:
+"user:pass@host.com:8080"+
+auth+::
The authentication information portion of a URL. Example: +"user:pass"+
+hostname+::
Just the hostname portion of the host. Example: +"host.com"+
+port+::
The port number portion of the host. Example: +"8080"+
+pathname+::
The path section of the URL, that comes after the host and before the query, including the
initial slash if present. Example: +"/p/a/t/h"+
+search+::
The "query string" portion of the URL, including the leading question mark. Example:
+"?query=string"+
+query+::
Either the "params" portion of the query string, or a querystring-parsed object. Example:
+"query=string"+ or +{"query":"string"}+
+hash+::
The portion of the URL after the pound-sign. Example: +"#hash"+
The following methods are provided by the URL module:
+url.parse(urlStr, parseQueryString=false)+::
Take a URL string, and return an object. Pass +true+ as the second argument to also parse
the query string using the +querystring+ module.
+url.format(urlObj)+::
Take a parsed URL object, and return a formatted URL string.
+url.resolve(from, to)+::
Take a base URL, and a href URL, and resolve them as a browser would for an anchor tag.
=== Query String Module
This module provides utilities for dealing with query strings. It provides the following methods:
+querystring.stringify(obj, sep="&", eq="=")+::
Serialize an object to a query string. Optionally override the default separator and assignment characters.
Example:
+
------------------------------------
node> require("querystring").stringify({foo:"bar", baz : {quux:"asdf", oof : "rab"}, boo:[1,2,3]})
"foo=bar&baz%5Bquux%5D=asdf&baz%5Boof%5D=rab&boo%5B%5D=1&boo%5B%5D=2&boo%5B%5D=3"
------------------------------------
+
+querystring.parse(str, sep="&", eq="=")+::
Deserialize a query string to an object. Optionally override the default separator and assignment characters.
+
------------------------------------
node> require("querystring").parse("foo=bar&baz%5Bquux%5D=asdf&baz%5Boof%5D=rab&boo%5B%5D=1")
{
"foo": "bar",
"baz": {
"quux": "asdf",
"oof": "rab"
},
"boo": [
1
]
}
------------------------------------
+
+querystring.escape+
The escape function used by +querystring.stringify+, provided so that it could be overridden if necessary.
+querystring.unescape+
The unescape function used by +querystring.parse+, provided so that it could be overridden if necessary.
== REPL
A Read-Eval-Print-Loop is available both as a standalone program and easily

Просмотреть файл

@ -97,9 +97,9 @@ server.listen(7000, "localhost");</pre>
<a href="http://github.com/ry/node/tree/master">git repo</a>
</p>
<p>
2009.12.19
2010.01.09
<a
href="http://s3.amazonaws.com/four.livejournal/20091222/node-v0.1.23.tar.gz">node-v0.1.23.tar.gz</a>
href="http://s3.amazonaws.com/four.livejournal/20100109/node-v0.1.25.tar.gz">node-v0.1.25.tar.gz</a>
</p>
<h2 id="build">Build</h2>

Просмотреть файл

@ -57,13 +57,7 @@ function IncomingMessage (connection) {
this.headers = {};
// request (server) only
this.uri = {
full: "",
queryString: "",
fragment: "",
path: "",
params: {}
};
this.url = "";
this.method = null;
@ -74,20 +68,8 @@ function IncomingMessage (connection) {
sys.inherits(IncomingMessage, process.EventEmitter);
exports.IncomingMessage = IncomingMessage;
var decode = require("uri").decode;
IncomingMessage.prototype._parseQueryString = function () {
var parts = this.uri.queryString.split('&');
for (var j = 0; j < parts.length; j++) {
var i = parts[j].indexOf('=');
if (i < 0) continue;
try {
var key = decode(parts[j].slice(0,i))
var value = decode(parts[j].slice(i+1));
this.uri.params[key] = value;
} catch (e) {
continue;
}
}
throw new Error("_parseQueryString is deprecated. Use require(\"querystring\") to parse query strings.\n");
};
IncomingMessage.prototype.setBodyEncoding = function (enc) {
@ -267,7 +249,7 @@ ServerResponse.prototype.sendHeader = function (statusCode, headers) {
};
function ClientRequest (method, uri, headers) {
function ClientRequest (method, url, headers) {
OutgoingMessage.call(this);
this.should_keep_alive = false;
@ -278,7 +260,7 @@ function ClientRequest (method, uri, headers) {
}
this.closeOnFinish = true;
this.sendHeaderLines(method + " " + uri + " HTTP/1.1\r\n", headers);
this.sendHeaderLines(method + " " + url + " HTTP/1.1\r\n", headers);
}
sys.inherits(ClientRequest, OutgoingMessage);
exports.ClientRequest = ClientRequest;
@ -301,22 +283,10 @@ function createIncomingMessageStream (connection, incoming_listener) {
field = null;
value = null;
});
// Only servers will get URI events.
// Only servers will get URL events.
connection.addListener("url", function (data) {
incoming.uri.full += data;
});
connection.addListener("path", function (data) {
incoming.uri.path += data;
});
connection.addListener("fragment", function (data) {
incoming.uri.fragment += data;
});
connection.addListener("queryString", function (data) {
incoming.uri.queryString += data;
incoming.url += data;
});
connection.addListener("headerField", function (data) {
@ -352,10 +322,6 @@ function createIncomingMessageStream (connection, incoming_listener) {
if (info.method) {
// server only
incoming.method = info.method;
if (incoming.uri.queryString.length > 0) {
incoming._parseQueryString();
}
} else {
// client only
incoming.statusCode = info.statusCode;
@ -548,13 +514,13 @@ process.http.Client.prototype.put = function () {
throw new Error("client.put(...) is now client.request('PUT', ...)");
};
process.http.Client.prototype.request = function (method, uri, headers) {
if (typeof(uri) != "string") { // assume method was omitted, shift arguments
headers = uri;
uri = method;
process.http.Client.prototype.request = function (method, url, headers) {
if (typeof(url) != "string") { // assume method was omitted, shift arguments
headers = url;
url = method;
method = null;
}
var req = new ClientRequest(method || "GET", uri, headers);
var req = new ClientRequest(method || "GET", url, headers);
this._pushRequest(req);
return req;
};
@ -565,7 +531,7 @@ exports.cat = function (url, encoding, headers) {
encoding = encoding || "utf8";
var uri = require("uri").parse(url);
var url = require("url").parse(url);
headers = headers || {};
var hasHost = false;
@ -574,11 +540,11 @@ exports.cat = function (url, encoding, headers) {
break;
}
if (!hasHost) {
headers["Host"] = uri.domain;
headers["Host"] = url.hostname;
}
var client = exports.createClient(uri.port || 80, uri.domain);
var req = client.request(uri.path || "/", headers);
var client = exports.createClient(url.port || 80, url.hostname);
var req = client.request((url.pathname || "/")+(url.search || "")+(url.hash || ""), headers);
client.addListener("error", function () {
promise.emitError();

Просмотреть файл

@ -1,9 +1,17 @@
var sys = require("sys");
exports.parse = function(options) {
var stream = new exports.Stream(options);
var promise = new process.Promise();
try {
var stream = new exports.Stream(options);
} catch (e) {
setTimeout(function() {
promise.emitError(e);
}, 0);
return promise;
}
var parts = {};
stream.addListener('part', function(part) {
var name = part.name;
@ -43,11 +51,16 @@ proto.init = function(options) {
if ('headers' in options) {
var req = options, contentType = req.headers['content-type'];
if (contentType) {
contentType = contentType.split(/; ?boundary=/)
this.boundary = '--'+contentType[1];
if (!contentType) {
throw new Error('Content-Type header not set');
}
var boundary = contentType.match(/^multipart\/form-data; ?boundary=(.+)$/i)
if (!boundary) {
throw new Error('Content-Type is not multipart: "'+contentType+'"');
}
this.boundary = '--'+boundary[1];
this.bytesTotal = req.headers['content-length'];
var self = this;
@ -59,6 +72,10 @@ proto.init = function(options) {
self.emit('complete');
});
} else {
if (!options.boundary) {
throw new Error('No boundary option given');
}
this.boundary = options.boundary;
this.write(options.data || '');
}

177
lib/querystring.js Normal file
Просмотреть файл

@ -0,0 +1,177 @@
// Query String Utilities
var QueryString = exports;
QueryString.unescape = function (str, decodeSpaces) {
return decodeURIComponent(decodeSpaces ? str.replace(/\+/g, " ") : str);
};
QueryString.escape = function (str) {
return encodeURIComponent(str);
};
var stack = [];
/**
* <p>Converts an arbitrary value to a Query String representation.</p>
*
* <p>Objects with cyclical references will trigger an exception.</p>
*
* @method stringify
* @param obj {Variant} any arbitrary value to convert to query string
* @param sep {String} (optional) Character that should join param k=v pairs together. Default: "&"
* @param eq {String} (optional) Character that should join keys to their values. Default: "="
* @param name {String} (optional) Name of the current key, for handling children recursively.
* @static
*/
QueryString.stringify = function (obj, sep, eq, name) {
sep = sep || "&";
eq = eq || "=";
if (isA(obj, null) || isA(obj, undefined) || typeof(obj) === 'function') {
return name ? encodeURIComponent(name) + eq : '';
}
if (isBool(obj)) obj = +obj;
if (isNumber(obj) || isString(obj)) {
return encodeURIComponent(name) + eq + encodeURIComponent(obj);
}
if (isA(obj, [])) {
var s = [];
name = name+'[]';
for (var i = 0, l = obj.length; i < l; i ++) {
s.push( QueryString.stringify(obj[i], sep, eq, name) );
}
return s.join(sep);
}
// now we know it's an object.
// Check for cyclical references in nested objects
for (var i = stack.length - 1; i >= 0; --i) if (stack[i] === obj) {
throw new Error("querystring.stringify. Cyclical reference");
}
stack.push(obj);
var s = [];
var begin = name ? name + '[' : '';
var end = name ? ']' : '';
for (var i in obj) if (obj.hasOwnProperty(i)) {
var n = begin + i + end;
s.push(QueryString.stringify(obj[i], sep, eq, n));
}
stack.pop();
s = s.join(sep);
if (!s && name) return name + "=";
return s;
};
QueryString.parseQuery = QueryString.parse = function (qs, sep, eq) {
return qs
.split(sep||"&")
.map(pieceParser(eq||"="))
.reduce(mergeParams);
};
// Parse a key=val string.
// These can get pretty hairy
// example flow:
// parse(foo[bar][][bla]=baz)
// return parse(foo[bar][][bla],"baz")
// return parse(foo[bar][], {bla : "baz"})
// return parse(foo[bar], [{bla:"baz"}])
// return parse(foo, {bar:[{bla:"baz"}]})
// return {foo:{bar:[{bla:"baz"}]}}
var trimmerPattern = /^\s+|\s+$/g,
slicerPattern = /(.*)\[([^\]]*)\]$/;
var pieceParser = function (eq) {
return function parsePiece (key, val) {
if (arguments.length !== 2) {
// key=val, called from the map/reduce
key = key.split(eq);
return parsePiece(
QueryString.unescape(key.shift(), true),
QueryString.unescape(key.join(eq), true)
);
}
key = key.replace(trimmerPattern, '');
if (isString(val)) {
val = val.replace(trimmerPattern, '');
// convert numerals to numbers
if (!isNaN(val)) {
var numVal = +val;
if (val === numVal.toString(10)) val = numVal;
}
}
var sliced = slicerPattern.exec(key);
if (!sliced) {
var ret = {};
if (key) ret[key] = val;
return ret;
}
// ["foo[][bar][][baz]", "foo[][bar][]", "baz"]
var tail = sliced[2], head = sliced[1];
// array: key[]=val
if (!tail) return parsePiece(head, [val]);
// obj: key[subkey]=val
var ret = {};
ret[tail] = val;
return parsePiece(head, ret);
};
};
// the reducer function that merges each query piece together into one set of params
function mergeParams (params, addition) {
return (
// if it's uncontested, then just return the addition.
(!params) ? addition
// if the existing value is an array, then concat it.
: (isA(params, [])) ? params.concat(addition)
// if the existing value is not an array, and either are not objects, arrayify it.
: (!isA(params, {}) || !isA(addition, {})) ? [params].concat(addition)
// else merge them as objects, which is a little more complex
: mergeObjects(params, addition)
);
};
// Merge two *objects* together. If this is called, we've already ruled
// out the simple cases, and need to do the for-in business.
function mergeObjects (params, addition) {
for (var i in addition) if (i && addition.hasOwnProperty(i)) {
params[i] = mergeParams(params[i], addition[i]);
}
return params;
};
// duck typing
function isA (thing, canon) {
return (
// truthiness. you can feel it in your gut.
(!thing === !canon)
// typeof is usually "object"
&& typeof(thing) === typeof(canon)
// check the constructor
&& Object.prototype.toString.call(thing) === Object.prototype.toString.call(canon)
);
};
function isBool (thing) {
return (
typeof(thing) === "boolean"
|| isA(thing, new Boolean(thing))
);
};
function isNumber (thing) {
return (
typeof(thing) === "number"
|| isA(thing, new Number(thing))
) && isFinite(thing);
};
function isString (thing) {
return (
typeof(thing) === "string"
|| isA(thing, new String(thing))
);
};

Просмотреть файл

@ -3,9 +3,6 @@
var sys = require('sys');
sys.puts("Type '.help' for options.");
var buffered_cmd = '';
var trimmer = /^\s*(.+)\s*$/m;
var scopedVar = /^\s*var\s*([_\w\$]+)(.*)$/m;
@ -13,6 +10,8 @@ var scopeFunc = /^\s*function\s*([_\w\$]+)/;
exports.scope = {};
exports.prompt = "node> ";
// Can overridden with custom print functions, such as `probe` or `eyes.js`
exports.writer = sys.p;
exports.start = function (prompt) {
if (prompt !== undefined) {
exports.prompt = prompt;
@ -49,7 +48,7 @@ function readline (cmd) {
var ret = eval(buffered_cmd);
if (ret !== undefined) {
exports.scope['_'] = ret;
sys.p(ret);
exports.writer(ret);
}
}

Просмотреть файл

@ -92,13 +92,27 @@ var formatter = function(value, indent, parents) {
if (parents.indexOf(value) >= 0) return '[Circular]';
parents.push(value);
if (value instanceof Array) {
if (value instanceof Array && Object.keys(value).length === value.length) {
return formatObject(value, indent, parents, '[]', function(x, f) {
return f(value[x]);
});
} else {
return formatObject(value, indent, parents, '{}', function(x, f) {
return f(x) + ': ' + f(value[x]);
var child;
if (value.__lookupGetter__(x)) {
if (value.__lookupSetter__(x)) {
child = "[Getter/Setter]";
} else {
child = "[Getter]";
}
} else {
if (value.__lookupSetter__(x)) {
child = "[Setter]";
} else {
child = f(value[x]);
}
}
return f(x) + ': ' + child;
});
}
return buffer;
@ -113,6 +127,7 @@ var formatter = function(value, indent, parents) {
var formatObject = function(obj, indent, parents, parenthesis, entryFormatter) {
var buffer = parenthesis[0];
var values = [];
var x;
var localFormatter = function(value) {
return formatter(value, indent + ' ', parents);

Просмотреть файл

@ -1,243 +1,5 @@
/**
* uri.js
* A URI parser, compliant with assorted RFCs, providing parsing and resolution utilities.
**/
process.stdio.writeError("\nWARNING: uri module is deprecated. Please use require(\"url\") instead.\n");
/*
Blatantly stolen with permission from Narwhal, which did the same from Chiron,
and bits taken from parseUri 1.2.1 (c) 2007 Steven Levithan <stevenlevithan.com> MIT License,
and probably also plagiarizing http://code.google.com/p/js-uri/ in some ways.
Most lines have been changed, please don't blame any of the above persons for
any errors in this file.
*/
exports.parse = uri_parse;
exports.format = uri_format;
exports.resolve = uri_resolve;
exports.resolveObject = uri_resolveObject;
exports.decode = uri_decode;
/****
Decode a URI, replacing + with space
*/
function uri_decode (s) {
return decodeURIComponent(s.replace(/\+/g, ' '));
}
/**** expressionKeys
members of a parsed URI object that you get
from evaluting the strict regular expression.
*/
var expressionKeys = [
"url",
"protocol",
"authorityRoot",
"authority",
"userInfo",
"user",
"password",
"domain",
"port",
"path",
"root",
"directory",
"file",
"query",
"anchor"
],
strictExpression = new RegExp( /* url */
"^" +
"(?:" +
"([^:/?#]+):" + /* protocol */
")?" +
"(?:" +
"(//)" + /* authorityRoot */
"(" + /* authority */
"(?:" +
"(" + /* userInfo */
"([^:@/]*)" + /* user */
":?" +
"([^@/]*)" + /* password */
")?" +
"@" +
")?" +
"([^:/?#]*)" + /* domain */
"(?::(\\d*))?" + /* port */
")" +
")?" +
"(" + /* path */
"(/?)" + /* root */
"((?:[^?#/]*/)*)" +
"([^?#]*)" + /* file */
")" +
"(?:\\?([^#]*))?" + /* query */
"(?:#(.*))?" /*anchor */
);
/**** parse
a URI parser function that uses the `strictExpression`
and `expressionKeys` and returns an `Object`
mapping all `keys` to values.
*/
function uri_parse (url) {
var items = {},
parts = strictExpression.exec(url);
for (var i = 0; i < parts.length; i++) {
items[expressionKeys[i]] = parts[i] ? parts[i] : "";
}
items.root = (items.root || items.authorityRoot) ? '/' : '';
items.directories = items.directory.split("/");
if (items.directories[items.directories.length - 1] == "") {
items.directories.pop();
}
/* normalize */
items.directories = require("path").normalizeArray(items.directories);
items.domains = items.domain.split(".");
return items;
exports.decode = exports.parse = exports.format = exports.resolveObject = exports.resolve = function () {
throw new Error("uri module is deprecated. Please use require(\"url\") instead.");
};
/**** format
accepts a parsed URI object and returns
the corresponding string.
*/
function uri_format (object) {
if (typeof(object) == 'undefined')
throw new Error("UrlError: URL undefined for urls#format");
if (object instanceof String || typeof(object) === 'string')
return object;
var domain =
object.domains ?
object.domains.join(".") :
object.domain;
var userInfo = (
object.user ||
object.password
) ? (
(object.user || "") +
(object.password ? ":" + object.password : "")
) :
object.userInfo;
var authority = object.authority || ((
userInfo ||
domain ||
object.port
) ? (
(userInfo ? userInfo + "@" : "") +
(domain || "") +
(object.port ? ":" + object.port : "")
) : "");
var directory =
object.directories ?
object.directories.join("/") :
object.directory;
var path =
object.path ? object.path.substr(1) : (
(directory || object.file) ? (
(directory ? directory + "/" : "") +
(object.file || "")
) : "");
var authorityRoot =
object.authorityRoot
|| authority ? "//" : "";
return object.url = ((
(object.protocol ? object.protocol + ":" : "") +
(authorityRoot) +
(authority) +
(object.root || (authority && path) ? "/" : "") +
(path ? path : "") +
(object.query ? "?" + object.query : "") +
(object.anchor ? "#" + object.anchor : "")
) || object.url || "");
};
function uri_resolveObject (source, relative) {
if (!source) return relative;
// parse a string, or get new objects
source = uri_parse(uri_format(source));
relative = uri_parse(uri_format(relative));
if (relative.url === "") return source;
// links to xyz:... from abc:... are always absolute.
if (relative.protocol && source.protocol && relative.protocol !== source.protocol) {
return relative;
}
// if there's an authority root, but no protocol, then keep the current protocol
if (relative.authorityRoot && !relative.protocol) {
relative.protocol = source.protocol;
}
// if we have an authority root, then it's absolute
if (relative.authorityRoot) return relative;
// at this point, we start doing the tricky stuff
// we know that relative doesn't have an authority, but might have root,
// path, file, query, etc.
// also, the directory segments might contain .. or .
// start mutating "source", and then return that.
// relative urls that start with / are absolute to the authority/protocol
if (relative.root) {
[
"path", "root", "directory", "directories", "file", "query", "anchor"
].forEach(function (part) { source[part] = relative[part] });
} else {
// if you have /a/b/c and you're going to x/y/z, then that's /a/b/x/y/z
// if you have /a/b/c/ and you're going ot x/y/z, then that's /a/b/c/x/y/z
// if you have /a/b/c and you're going to ?foo, then that's /a/b/c?foo
if (relative.file || relative.directory) {
source.file = relative.file;
source.query = relative.query;
source.anchor = relative.anchor;
}
if (relative.query) source.query = relative.query;
if (relative.query || relative.anchor) source.anchor = relative.anchor;
// just append the dirs. we'll sort out .. and . later
source.directories = source.directories.concat(relative.directories);
}
// back up "file" to the first non-dot
// one step for ., two for ..
var file = source.file;
while (file === ".." || file === ".") {
if (file === "..") source.directories.pop();
file = source.directories.pop();
}
source.file = file || "";
// walk over the dirs, replacing a/b/c/.. with a/b/ and a/b/c/. with a/b/c
var dirs = [];
source.directories.forEach(function (dir, i) {
if (dir === "..") dirs.pop();
else if (dir !== "." && dir !== "") dirs.push(dir);
});
// now construct path/etc.
source.directories = dirs;
source.directory = dirs.concat("").join("/");
source.path = source.root + source.directory + source.file;
source.url = uri_format(source);
return source;
};
/**** resolve
returns a URL resovled to a relative URL from a source URL.
*/
function uri_resolve (source, relative) {
return uri_format(uri_resolveObject(source, relative));
};

304
lib/url.js Normal file
Просмотреть файл

@ -0,0 +1,304 @@
exports.parse = url_parse;
exports.resolve = url_resolve;
exports.resolveObject = url_resolveObject;
exports.format = url_format;
// define these here so at least they only have to be compiled once on the first module load.
var protocolPattern = /^([a-z0-9]+:)/,
portPattern = /:[0-9]+$/,
nonHostChars = ["/", "?", ";", "#"],
hostlessProtocol = {
"file":true,
"file:":true
},
slashedProtocol = {
"http":true, "https":true, "ftp":true, "gopher":true, "file":true,
"http:":true, "https:":true, "ftp:":true, "gopher:":true, "file:":true
},
path = require("path"), // internal module, guaranteed to be loaded already.
querystring; // don't load unless necessary.
function url_parse (url, parseQueryString) {
if (url && typeof(url) === "object" && url.href) return url;
var out = { href : url },
rest = url;
var proto = protocolPattern.exec(rest);
if (proto) {
proto = proto[0];
out.protocol = proto;
rest = rest.substr(proto.length);
}
// figure out if it's got a host
var slashes = rest.substr(0, 2) === "//";
if (slashes && !(proto && hostlessProtocol[proto])) {
rest = rest.substr(2);
out.slashes = true;
}
if (!hostlessProtocol[proto] && (slashes || (proto && !slashedProtocol[proto]))) {
// there's a hostname.
// the first instance of /, ?, ;, or # ends the host.
// don't enforce full RFC correctness, just be unstupid about it.
var firstNonHost = -1;
for (var i = 0, l = nonHostChars.length; i < l; i ++) {
var index = rest.indexOf(nonHostChars[i]);
if (index !== -1 && (firstNonHost < 0 || index < firstNonHost)) firstNonHost = index;
}
if (firstNonHost !== -1) {
out.host = rest.substr(0, firstNonHost);
rest = rest.substr(firstNonHost);
} else {
out.host = rest;
rest = "";
}
// pull out the auth and port.
var p = parseHost(out.host);
for (var i in p) out[i] = p[i];
// we've indicated that there is a hostname, so even if it's empty, it has to be present.
out.hostname = out.hostname || "";
}
// now rest is set to the post-host stuff.
// chop off from the tail first.
var hash = rest.indexOf("#");
if (hash !== -1) {
// got a fragment string.
out.hash = rest.substr(hash);
rest = rest.slice(0, hash);
}
var qm = rest.indexOf("?");
if (qm !== -1) {
out.search = rest.substr(qm);
out.query = rest.substr(qm+1);
if (parseQueryString) {
if (!querystring) {
querystring = require("querystring");
}
out.query = querystring.parse(out.query);
}
rest = rest.slice(0, qm);
}
if (rest) out.pathname = rest;
return out;
};
// format a parsed object into a url string
function url_format (obj) {
// ensure it's an object, and not a string url. If it's an obj, this is a no-op.
// this way, you can call url_format() on strings to clean up potentially wonky urls.
if (typeof(obj) === "string") obj = url_parse(obj);
var protocol = obj.protocol || "",
host = (obj.host !== undefined) ? obj.host
: obj.hostname !== undefined ? (
(obj.auth ? obj.auth + "@" : "")
+ obj.hostname
+ (obj.port ? ":" + obj.port : "")
)
: false,
pathname = obj.pathname || "",
search = obj.search || (
obj.query && ( "?" + (
typeof(obj.query) === "object"
? require("querystring").stringify(obj.query)
: String(obj.query)
))
) || "",
hash = obj.hash || "";
if (protocol && protocol.substr(-1) !== ":") protocol += ":";
// only the slashedProtocols get the //. Not mailto:, xmpp:, etc.
// unless they had them to begin with.
if (obj.slashes || (!protocol || slashedProtocol[protocol]) && host !== false) {
host = "//" + (host || "");
if (pathname && pathname.charAt(0) !== "/") pathname = "/" + pathname;
} else if (!host) host = "";
if (hash && hash.charAt(0) !== "#") hash = "#" + hash;
if (search && search.charAt(0) !== "?") search = "?" + search;
return protocol + host + pathname + search + hash;
};
function url_resolve (source, relative) {
return url_format(url_resolveObject(source, relative));
};
function url_resolveObject (source, relative) {
if (!source) return relative;
source = url_parse(url_format(source));
relative = url_parse(url_format(relative));
// hash is always overridden, no matter what.
source.hash = relative.hash;
if (relative.href === "") return source;
// hrefs like //foo/bar always cut to the protocol.
if (relative.slashes && !relative.protocol) {
relative.protocol = source.protocol;
return relative;
}
if (relative.protocol && relative.protocol !== source.protocol) {
// if it's a known url protocol, then changing the protocol does weird things
// first, if it's not file:, then we MUST have a host, and if there was a path
// to begin with, then we MUST have a path.
// if it is file:, then the host is dropped, because that's known to be hostless.
// anything else is assumed to be absolute.
if (!slashedProtocol[relative.protocol]) return relative;
source.protocol = relative.protocol;
if (!relative.host && !hostlessProtocol[relative.protocol]) {
var relPath = (relative.pathname || "").split("/");
while (relPath.length && !(relative.host = relPath.shift()));
if (!relative.host) relative.host = "";
if (relPath[0] !== "") relPath.unshift("");
if (relPath.length < 2) relPath.unshift("");
relative.pathname = relPath.join("/");
}
source.pathname = relative.pathname;
source.search = relative.search;
source.query = relative.query;
source.host = relative.host || "";
delete source.auth;
delete source.hostname;
source.port = relative.port;
return source;
}
var isSourceAbs = (source.pathname && source.pathname.charAt(0) === "/"),
isRelAbs = (
relative.host !== undefined
|| relative.pathname && relative.pathname.charAt(0) === "/"
),
mustEndAbs = (isRelAbs || isSourceAbs || (source.host && relative.pathname)),
removeAllDots = mustEndAbs,
srcPath = source.pathname && source.pathname.split("/") || [],
relPath = relative.pathname && relative.pathname.split("/") || [],
psychotic = source.protocol && !slashedProtocol[source.protocol] && source.host !== undefined;
// if the url is a non-slashed url, then relative links like ../.. should be able
// to crawl up to the hostname, as well. This is strange.
// source.protocol has already been set by now.
// Later on, put the first path part into the host field.
if ( psychotic ) {
delete source.hostname;
delete source.auth;
delete source.port;
if (source.host) {
if (srcPath[0] === "") srcPath[0] = source.host;
else srcPath.unshift(source.host);
}
delete source.host;
if (relative.protocol) {
delete relative.hostname;
delete relative.auth;
delete relative.port;
if (relative.host) {
if (relPath[0] === "") relPath[0] = relative.host;
else relPath.unshift(relative.host);
}
delete relative.host;
}
mustEndAbs = mustEndAbs && (relPath[0] === "" || srcPath[0] === "");
}
if (isRelAbs) {
// it's absolute.
source.host = (relative.host || relative.host === "") ? relative.host : source.host;
source.search = relative.search;
source.query = relative.query;
srcPath = relPath;
// fall through to the dot-handling below.
} else if (relPath.length) {
// it's relative
// throw away the existing file, and take the new path instead.
if (!srcPath) srcPath = [];
srcPath.pop();
srcPath = srcPath.concat(relPath);
source.search = relative.search;
source.query = relative.query;
} else if ("search" in relative) {
// just pull out the search.
// like href="?foo".
// Put this after the other two cases because it simplifies the booleans
if (psychotic) {
source.host = srcPath.shift();
}
source.search = relative.search;
source.query = relative.query;
return source;
}
if (!srcPath.length) {
// no path at all. easy.
// we've already handled the other stuff above.
delete source.pathname;
return source;
}
// resolve dots.
// if a url ENDs in . or .., then it must get a trailing slash.
// however, if it ends in anything else non-slashy, then it must NOT get a trailing slash.
var last = srcPath.slice(-1)[0];
var hasTrailingSlash = (
(source.host || relative.host) && (last === "." || last === "..")
|| last === ""
);
// Figure out if this has to end up as an absolute url, or should continue to be relative.
srcPath = path.normalizeArray(srcPath, true);
if (srcPath.length === 1 && srcPath[0] === ".") srcPath = [];
if (mustEndAbs || removeAllDots) {
// all dots must go.
var dirs = [];
srcPath.forEach(function (dir, i) {
if (dir === "..") dirs.pop();
else if (dir !== ".") dirs.push(dir);
});
if (mustEndAbs && dirs[0] !== "") {
dirs.unshift("");
}
srcPath = dirs;
}
if (hasTrailingSlash && (srcPath.length < 2 || srcPath.slice(-1)[0] !== "")) srcPath.push("");
// put the host back
if ( psychotic ) source.host = srcPath[0] === "" ? "" : srcPath.shift();
mustEndAbs = mustEndAbs || (source.host && srcPath.length);
if (mustEndAbs && srcPath[0] !== "") srcPath.unshift("")
source.pathname = srcPath.join("/");
return source;
};
function parseHost (host) {
var out = {};
var at = host.indexOf("@");
if (at !== -1) {
out.auth = host.substr(0, at);
host = host.substr(at+1); // drop the @
}
var port = portPattern.exec(host);
if (port) {
port = port[0];
out.port = port.substr(1);
host = host.substr(0, host.length - port.length);
}
if (host) out.hostname = host;
return out;
}

Просмотреть файл

@ -63,6 +63,67 @@ static Persistent<String> emit_symbol;
static int dash_dash_index = 0;
static bool use_debug_agent = false;
static ev_async eio_want_poll_notifier;
static ev_async eio_done_poll_notifier;
static ev_idle eio_poller;
static void DoPoll(EV_P_ ev_idle *watcher, int revents) {
assert(watcher == &eio_poller);
assert(revents == EV_IDLE);
//printf("eio_poller\n");
if (eio_poll() != -1) {
//printf("eio_poller stop\n");
ev_idle_stop(EV_DEFAULT_UC_ watcher);
}
}
// Called from the main thread.
static void WantPollNotifier(EV_P_ ev_async *watcher, int revents) {
assert(watcher == &eio_want_poll_notifier);
assert(revents == EV_ASYNC);
//printf("want poll notifier\n");
if (eio_poll() == -1) {
//printf("eio_poller start\n");
ev_idle_start(EV_DEFAULT_UC_ &eio_poller);
}
}
static void DonePollNotifier(EV_P_ ev_async *watcher, int revents) {
assert(watcher == &eio_done_poll_notifier);
assert(revents == EV_ASYNC);
//printf("done poll notifier\n");
if (eio_poll() != -1) {
//printf("eio_poller stop\n");
ev_idle_stop(EV_DEFAULT_UC_ &eio_poller);
}
}
// EIOWantPoll() is called from the EIO thread pool each time an EIO
// request (that is, one of the node.fs.* functions) has completed.
static void EIOWantPoll(void) {
// Signal the main thread that eio_poll need to be processed.
ev_async_send(EV_DEFAULT_UC_ &eio_want_poll_notifier);
}
static void EIODonePoll(void) {
// Signal the main thread that we should stop calling eio_poll().
// from the idle watcher.
ev_async_send(EV_DEFAULT_UC_ &eio_done_poll_notifier);
}
enum encoding ParseEncoding(Handle<Value> encoding_v, enum encoding _default) {
HandleScope scope;
@ -339,6 +400,11 @@ static Handle<Value> ByteLength(const Arguments& args) {
static Handle<Value> Loop(const Arguments& args) {
HandleScope scope;
// TODO Probably don't need to start this each time.
// Avoids failing on test/mjsunit/test-eio-race3.js though
ev_idle_start(EV_DEFAULT_UC_ &eio_poller);
ev_loop(EV_DEFAULT_UC_ 0);
return Undefined();
}
@ -409,6 +475,44 @@ v8::Handle<v8::Value> Exit(const v8::Arguments& args) {
return Undefined();
}
#ifdef __FreeBSD__
#define HAVE_GETMEM 1
#include <kvm.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <fcntl.h>
#include <unistd.h>
int getmem(size_t *rss, size_t *vsize) {
kvm_t *kd = NULL;
struct kinfo_proc *kinfo = NULL;
pid_t pid;
int nprocs;
pid = getpid();
kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open");
if (kd == NULL) goto error;
kinfo = kvm_getprocs(kd, KERN_PROC_PID, pid, &nprocs);
if (kinfo == NULL) goto error;
*rss = kinfo->ki_rssize * PAGE_SIZE;
*vsize = kinfo->ki_size;
kvm_close(kd);
return 0;
error:
if (kd) kvm_close(kd);
return -1;
}
#endif // __FreeBSD__
#ifdef __APPLE__
#define HAVE_GETMEM 1
/* Researched by Tim Becker and Michael Knight
@ -716,25 +820,6 @@ void FatalException(TryCatch &try_catch) {
uncaught_exception_counter--;
}
static ev_async eio_watcher;
// Called from the main thread.
static void EIOCallback(EV_P_ ev_async *watcher, int revents) {
assert(watcher == &eio_watcher);
assert(revents == EV_ASYNC);
// Give control to EIO to process responses. In nearly every case
// EIOPromise::After() (file.cc) is called once EIO receives the response.
eio_poll();
}
// EIOWantPoll() is called from the EIO thread pool each time an EIO
// request (that is, one of the node.fs.* functions) has completed.
static void EIOWantPoll(void) {
// Signal the main thread that EIO callbacks need to be processed.
ev_async_send(EV_DEFAULT_UC_ &eio_watcher);
// EIOCallback() will be called from the main thread in the next event
// loop.
}
static ev_async debug_watcher;
@ -950,21 +1035,23 @@ int main(int argc, char *argv[]) {
// Initialize the default ev loop.
ev_default_loop(EVFLAG_AUTO);
// Start the EIO thread pool:
// 1. Initialize the ev_async watcher which allows for notification from
// the thread pool (in node::EIOWantPoll) to poll for updates (in
// node::EIOCallback).
ev_async_init(&node::eio_watcher, node::EIOCallback);
// 2. Actaully start the thread pool.
eio_init(node::EIOWantPoll, NULL);
// Don't handle more than 10 reqs on each eio_poll(). This is to avoid
// race conditions. See test/mjsunit/test-eio-race.js
eio_set_max_poll_reqs(10);
// 3. Start watcher.
ev_async_start(EV_DEFAULT_UC_ &node::eio_watcher);
// 4. Remove a reference to the async watcher. This means we'll drop out
// of the ev_loop even though eio_watcher is active.
ev_unref(EV_DEFAULT_UC);
// Setup the EIO thread pool
{ // It requires 3, yes 3, watchers.
ev_idle_init(&node::eio_poller, node::DoPoll);
ev_async_init(&node::eio_want_poll_notifier, node::WantPollNotifier);
ev_async_start(EV_DEFAULT_UC_ &node::eio_want_poll_notifier);
ev_unref(EV_DEFAULT_UC);
ev_async_init(&node::eio_done_poll_notifier, node::DonePollNotifier);
ev_async_start(EV_DEFAULT_UC_ &node::eio_done_poll_notifier);
ev_unref(EV_DEFAULT_UC);
eio_init(node::EIOWantPoll, node::EIODonePoll);
// Don't handle more than 10 reqs on each eio_poll(). This is to avoid
// race conditions. See test/mjsunit/test-eio-race.js
eio_set_max_poll_reqs(10);
}
V8::Initialize();
HandleScope handle_scope;

Просмотреть файл

@ -443,7 +443,9 @@ GLOBAL.setInterval = function (callback, repeat) {
};
GLOBAL.clearTimeout = function (timer) {
timer.stop();
if (timer instanceof process.Timer) {
timer.stop();
}
};
GLOBAL.clearInterval = GLOBAL.clearTimeout;
@ -670,40 +672,57 @@ var pathModule = createInternalModule("path", function (exports) {
return exports.normalize(Array.prototype.join.call(arguments, "/"));
};
exports.normalizeArray = function (parts) {
var directories = [];
for (var i = 0; i < parts.length; i++) {
exports.normalizeArray = function (parts, keepBlanks) {
var directories = [], prev;
for (var i = 0, l = parts.length - 1; i <= l; i++) {
var directory = parts[i];
if (directory === "." || (directory === "" && directories.length)) {
continue;
}
// if it's blank, but it's not the first thing, and not the last thing, skip it.
if (directory === "" && i !== 0 && i !== l && !keepBlanks) continue;
// if it's a dot, and there was some previous dir already, then skip it.
if (directory === "." && prev !== undefined) continue;
if (
directory === ".."
&& directories.length
&& directories[directories.length - 1] != '..'
&& prev !== ".."
&& prev !== undefined
&& (prev !== "" || keepBlanks)
) {
directories.pop();
prev = directories.slice(-1)[0]
} else {
if (prev === ".") directories.pop();
directories.push(directory);
prev = directory;
}
}
return directories;
};
exports.normalize = function (path) {
return exports.normalizeArray(path.split("/")).join("/");
exports.normalize = function (path, keepBlanks) {
return exports.normalizeArray(path.split("/"), keepBlanks).join("/");
};
exports.dirname = function (path) {
if (path.charAt(0) !== "/") path = "./" + path;
var parts = path.split("/");
return parts.slice(0, parts.length-1).join("/");
return path.substr(0, path.lastIndexOf("/")) || ".";
};
exports.filename = function (path) {
if (path.charAt(0) !== "/") path = "./" + path;
var parts = path.split("/");
return parts[parts.length-1];
exports.filename = function () {
throw new Error("path.filename is deprecated. Please use path.basename instead.");
};
exports.basename = function (path, ext) {
var f = path.substr(path.lastIndexOf("/") + 1);
if (ext && f.substr(-1 * ext.length) === ext) {
f = f.substr(0, f.length - ext.length);
}
return f;
};
exports.extname = function (path) {
var index = path.lastIndexOf('.');
return index < 0 ? '' : path.substring(index);
};
exports.exists = function (path, callback) {
@ -788,7 +807,7 @@ function loadModule (request, parent) {
if (request.charAt(0) == "." && (request.charAt(1) == "/" || request.charAt(1) == ".")) {
// Relative request
var parentIdPath = path.dirname(parent.id +
(path.filename(parent.filename).match(/^index\.(js|addon)$/) ? "/" : ""));
(path.basename(parent.filename).match(/^index\.(js|addon)$/) ? "/" : ""));
id = path.join(parentIdPath, request);
// debug("RELATIVE: requested:"+request+" set ID to: "+id+" from "+parent.id+"("+parentIdPath+")");
paths = [path.dirname(parent.filename)];

Просмотреть файл

@ -29,6 +29,23 @@ static Persistent<String> header_complete_symbol;
static Persistent<String> body_symbol;
static Persistent<String> eof_symbol;
static Persistent<String> delete_sym;
static Persistent<String> get_sym;
static Persistent<String> head_sym;
static Persistent<String> post_sym;
static Persistent<String> put_sym;
static Persistent<String> connect_sym;
static Persistent<String> options_sym;
static Persistent<String> trace_sym;
static Persistent<String> copy_sym;
static Persistent<String> lock_sym;
static Persistent<String> mkcol_sym;
static Persistent<String> move_sym;
static Persistent<String> propfind_sym;
static Persistent<String> proppatch_sym;
static Persistent<String> unlock_sym;
static Persistent<String> unknown_method_sym;
void
HTTPConnection::Initialize (Handle<Object> target)
{
@ -49,10 +66,8 @@ HTTPConnection::Initialize (Handle<Object> target)
NODE_SET_PROTOTYPE_METHOD(server_constructor_template, "resetParser", ResetParser);
server_constructor_template->SetClassName(String::NewSymbol("ServerSideConnection"));
method_symbol = NODE_PSYMBOL("method");
status_code_symbol = NODE_PSYMBOL("statusCode");
http_version_symbol = NODE_PSYMBOL("httpVersion");
should_keep_alive_symbol = NODE_PSYMBOL("should_keep_alive");
eof_symbol = NODE_PSYMBOL("eof");
}
Handle<Value>
@ -92,13 +107,9 @@ HTTPConnection::OnReceive (const void *buf, size_t len)
HandleScope scope;
assert(refs_);
size_t nparsed;
size_t nparsed;
if (type_ == HTTP_REQUEST) {
nparsed = http_parse_requests(&parser_, static_cast<const char*>(buf), len);
} else {
nparsed = http_parse_responses(&parser_, static_cast<const char*>(buf), len);
}
nparsed = http_parser_execute(&parser_, static_cast<const char*>(buf), len);
if (nparsed != len) {
ForceClose();
@ -110,11 +121,8 @@ HTTPConnection::OnEOF ()
{
HandleScope scope;
assert(refs_);
if (type_ == HTTP_REQUEST) {
http_parse_requests(&parser_, NULL, 0);
} else {
http_parse_responses(&parser_, NULL, 0);
}
size_t nparsed;
nparsed = http_parser_execute(&parser_, NULL, 0);
Emit(eof_symbol, 0, NULL);
}
@ -141,7 +149,23 @@ HTTPConnection::on_message_begin (http_parser *parser)
header_value_symbol = NODE_PSYMBOL("headerValue");
header_complete_symbol = NODE_PSYMBOL("headerComplete");
body_symbol = NODE_PSYMBOL("body");
eof_symbol = NODE_PSYMBOL("eof");
delete_sym = NODE_PSYMBOL("DELETE");
get_sym = NODE_PSYMBOL("GET");
head_sym = NODE_PSYMBOL("HEAD");
post_sym = NODE_PSYMBOL("POST");
put_sym = NODE_PSYMBOL("PUT");
connect_sym = NODE_PSYMBOL("CONNECT");
options_sym = NODE_PSYMBOL("OPTIONS");
trace_sym = NODE_PSYMBOL("TRACE");
copy_sym = NODE_PSYMBOL("COPY");
lock_sym = NODE_PSYMBOL("LOCK");
mkcol_sym = NODE_PSYMBOL("MKCOL");
move_sym = NODE_PSYMBOL("MOVE");
propfind_sym = NODE_PSYMBOL("PROPFIND");
proppatch_sym = NODE_PSYMBOL("PROPPATCH");
unlock_sym = NODE_PSYMBOL("UNLOCK");
unknown_method_sym = NODE_PSYMBOL("UNKNOWN_METHOD");
}
HTTPConnection *connection = static_cast<HTTPConnection*> (parser->data);
@ -242,6 +266,28 @@ HTTPConnection::on_header_value (http_parser *parser, const char *buf, size_t le
return 0;
}
static inline Persistent<String>
method_to_str(enum http_method m) {
switch (m) {
case HTTP_DELETE: return delete_sym;
case HTTP_GET: return get_sym;
case HTTP_HEAD: return head_sym;
case HTTP_POST: return post_sym;
case HTTP_PUT: return put_sym;
case HTTP_CONNECT: return connect_sym;
case HTTP_OPTIONS: return options_sym;
case HTTP_TRACE: return trace_sym;
case HTTP_COPY: return copy_sym;
case HTTP_LOCK: return lock_sym;
case HTTP_MKCOL: return mkcol_sym;
case HTTP_MOVE: return move_sym;
case HTTP_PROPFIND: return propfind_sym;
case HTTP_PROPPATCH: return proppatch_sym;
case HTTP_UNLOCK: return unlock_sym;
default: return unknown_method_sym;
}
}
int
HTTPConnection::on_headers_complete (http_parser *parser)
{
@ -253,8 +299,7 @@ HTTPConnection::on_headers_complete (http_parser *parser)
// METHOD
if (connection->type_ == HTTP_REQUEST) {
message_info->Set(method_symbol, String::NewSymbol(
http_method_str(connection->parser_.method)));
message_info->Set(method_symbol, method_to_str(connection->parser_.method));
}
// STATUS

Просмотреть файл

@ -7,8 +7,6 @@
namespace node {
enum http_connection_type { HTTP_RESPONSE, HTTP_REQUEST };
class HTTPConnection : public Connection {
public:
static void Initialize (v8::Handle<v8::Object> target);
@ -21,7 +19,7 @@ protected:
static v8::Handle<v8::Value> NewServer (const v8::Arguments& args);
static v8::Handle<v8::Value> ResetParser(const v8::Arguments& args);
HTTPConnection (enum http_connection_type t)
HTTPConnection (enum http_parser_type t)
: Connection()
{
type_ = t;
@ -29,7 +27,7 @@ protected:
}
void ResetParser() {
http_parser_init (&parser_);
http_parser_init (&parser_, type_);
parser_.on_message_begin = on_message_begin;
parser_.on_url = on_url;
parser_.on_path = on_path;
@ -57,10 +55,8 @@ protected:
static int on_body (http_parser *parser, const char *buf, size_t len);
static int on_message_complete (http_parser *parser);
enum http_parser_type type_;
http_parser parser_;
enum http_connection_type type_; // should probably use subclass
// but going to refactor this all soon
// so won't worry about it.
friend class HTTPServer;
};

Просмотреть файл

@ -0,0 +1,20 @@
process.mixin(require("./common"));
var testTxt = path.join(fixturesDir, "x.txt");
var posix = require('posix');
setTimeout(function () {
// put this in a timeout, just so it doesn't get bunched up with the
// require() calls..
N = 30;
for (var i=0; i < N; i++) {
puts("start " + i);
posix.cat(testTxt).addCallback(function(data) {
puts("finish");
}).addErrback(function (e) {
puts("error! " + e);
process.exit(1);
});
}
}, 100);

Просмотреть файл

@ -0,0 +1,16 @@
process.mixin(require("./common"));
puts('first stat ...');
posix.stat(__filename)
.addCallback( function(stats) {
puts('second stat ...');
posix.stat(__filename)
.timeout(1000)
.wait();
puts('test passed');
})
.addErrback(function() {
throw new Exception();
});

Просмотреть файл

@ -0,0 +1,19 @@
process.mixin(require("./common"));
var N = 100;
var j = 0;
for (var i = 0; i < N; i++) {
posix.stat("does-not-exist-" + i) // these files don't exist
.addErrback(function (e) {
j++; // only makes it to about 17
puts("finish " + j);
})
.addCallback(function () {
puts("won't be called");
});
}
process.addListener("exit", function () {
assert.equal(N, j);
});

Просмотреть файл

@ -1,12 +1,13 @@
process.mixin(require("./common"));
http = require("http");
url = require("url");
PORT = 8888;
var body1_s = "1111111111111111";
var body2_s = "22222";
var server = http.createServer(function (req, res) {
var body = req.uri.path === "/1" ? body1_s : body2_s;
var body = url.parse(req.url).pathname === "/1" ? body1_s : body2_s;
res.sendHeader(200, { "Content-Type": "text/plain"
, "Content-Length": body.length
});

Просмотреть файл

@ -0,0 +1,16 @@
tcp = require("tcp");
http = require("http");
// This is a regression test for http://github.com/ry/node/issues/#issue/44
// It is separate from test-http-malformed-request.js because it is only
// reproduceable on the first packet on the first connection to a server.
port = 9999;
server = http.createServer(function (req, res) {});
server.listen(port);
tcp.createConnection(port).addListener("connect", function () {
this.close();
}).addListener("close", function () {
server.close();
});

Просмотреть файл

@ -1,6 +1,7 @@
process.mixin(require("./common"));
tcp = require("tcp");
http = require("http");
url = require("url");
// Make sure no exceptions are thrown when receiving malformed HTTP
// requests.
@ -10,7 +11,7 @@ nrequests_completed = 0;
nrequests_expected = 1;
var s = http.createServer(function (req, res) {
puts("req: " + JSON.stringify(req.uri));
puts("req: " + JSON.stringify(url.parse(req.url)));
res.sendHeader(200, {"Content-Type": "text/plain"});
res.sendBody("Hello World");

Просмотреть файл

@ -1,5 +1,6 @@
process.mixin(require("./common"));
http = require("http");
url = require("url");
var PROXY_PORT = 8869;
var BACKEND_PORT = 8870;
@ -16,7 +17,7 @@ backend.listen(BACKEND_PORT);
var proxy_client = http.createClient(BACKEND_PORT);
var proxy = http.createServer(function (req, res) {
debug("proxy req headers: " + JSON.stringify(req.headers));
var proxy_req = proxy_client.request(req.uri.path);
var proxy_req = proxy_client.request(url.parse(req.url).pathname);
proxy_req.finish(function(proxy_res) {
res.sendHeader(proxy_res.statusCode, proxy_res.headers);
proxy_res.addListener("body", function(chunk) {

Просмотреть файл

@ -1,6 +1,8 @@
process.mixin(require("./common"));
tcp = require("tcp");
http = require("http");
url = require("url");
qs = require("querystring");
var port = 8222;
@ -15,14 +17,14 @@ http.createServer(function (req, res) {
if (req.id == 0) {
assert.equal("GET", req.method);
assert.equal("/hello", req.uri.path);
assert.equal("world", req.uri.params["hello"]);
assert.equal("b==ar", req.uri.params["foo"]);
assert.equal("/hello", url.parse(req.url).pathname);
assert.equal("world", qs.parse(url.parse(req.url).query).hello);
assert.equal("b==ar", qs.parse(url.parse(req.url).query).foo);
}
if (req.id == 1) {
assert.equal("POST", req.method);
assert.equal("/quit", req.uri.path);
assert.equal("/quit", url.parse(req.url).pathname);
}
if (req.id == 2) {
@ -37,7 +39,7 @@ http.createServer(function (req, res) {
setTimeout(function () {
res.sendHeader(200, {"Content-Type": "text/plain"});
res.sendBody(req.uri.path);
res.sendBody(url.parse(req.url).pathname);
res.finish();
}, 1);

Просмотреть файл

@ -1,5 +1,6 @@
process.mixin(require("./common"));
http = require("http");
url = require("url");
PORT = 8888;
HOST = "localhost";
@ -34,7 +35,7 @@ var http_server=http.createServer(function (req, res) {
if (responses_sent == 0) {
assert.equal("GET", req.method);
assert.equal("/hello", req.uri.path);
assert.equal("/hello", url.parse(req.url).pathname);
p(req.headers);
assert.equal(true, "accept" in req.headers);
@ -46,13 +47,13 @@ var http_server=http.createServer(function (req, res) {
if (responses_sent == 1) {
assert.equal("POST", req.method);
assert.equal("/world", req.uri.path);
assert.equal("/world", url.parse(req.url).pathname);
this.close();
}
req.addListener("complete", function () {
res.sendHeader(200, {"Content-Type": "text/plain"});
res.sendBody("The path was " + req.uri.path);
res.sendBody("The path was " + url.parse(req.url).pathname);
res.finish();
responses_sent += 1;
});

Просмотреть файл

@ -1,5 +1,6 @@
process.mixin(require("./common"));
http = require("http");
url = require("url");
PORT = 8888;
var responses_sent = 0;
@ -10,7 +11,7 @@ var body1 = "";
http.createServer(function (req, res) {
if (responses_sent == 0) {
assert.equal("GET", req.method);
assert.equal("/hello", req.uri.path);
assert.equal("/hello", url.parse(req.url).pathname);
p(req.headers);
assert.equal(true, "accept" in req.headers);
@ -22,13 +23,13 @@ http.createServer(function (req, res) {
if (responses_sent == 1) {
assert.equal("POST", req.method);
assert.equal("/world", req.uri.path);
assert.equal("/world", url.parse(req.url).pathname);
this.close();
}
req.addListener("complete", function () {
res.sendHeader(200, {"Content-Type": "text/plain"});
res.sendBody("The path was " + req.uri.path);
res.sendBody("The path was " + url.parse(req.url).pathname);
res.finish();
responses_sent += 1;
});

Просмотреть файл

@ -1,25 +1,59 @@
process.mixin(require("./common"));
http = require("http");
var multipart = require('multipart');
var fixture = require('./fixtures/multipart');
var
PORT = 8222,
var port = 8222;
var parts_reveived = 0;
var parts_complete = 0;
var parts = {};
multipart = require('multipart'),
fixture = require('./fixtures/multipart'),
requests = 0,
badRequests = 0,
partsReceived = 0,
partsComplete = 0,
respond = function(res, text) {
requests++;
if (requests == 4) {
server.close();
}
res.sendHeader(200, {"Content-Type": "text/plain"});
res.sendBody(text);
res.finish();
};
var server = http.createServer(function(req, res) {
var stream = new multipart.Stream(req);
if (req.headers['x-use-simple-api']) {
multipart.parse(req)
.addCallback(function() {
respond(res, 'thanks');
})
.addErrback(function() {
badRequests++;
respond(res, 'no thanks');
});
return;
}
try {
var stream = new multipart.Stream(req);
} catch (e) {
badRequests++;
respond(res, 'no thanks');
return;
}
var parts = {};
stream.addListener('part', function(part) {
parts_reveived++;
partsReceived++;
var name = part.name;
if (parts_reveived == 1) {
if (partsReceived == 1) {
assert.equal('reply', name);
} else if (parts_reveived == 2) {
} else if (partsReceived == 2) {
assert.equal('fileupload', name);
}
@ -29,31 +63,59 @@ var server = http.createServer(function(req, res) {
});
part.addListener('complete', function(chunk) {
assert.equal(0, part.buffer.length);
if (parts_reveived == 1) {
if (partsReceived == 1) {
assert.equal('yes', parts[name]);
} else if (parts_reveived == 2) {
assert.equal('/9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg', parts[name]);
} else if (partsReceived == 2) {
assert.equal(
'/9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg',
parts[name]
);
}
parts_complete++;
partsComplete++;
});
});
stream.addListener('complete', function() {
res.sendHeader(200, {"Content-Type": "text/plain"});
res.sendBody('thanks');
res.finish();
server.close();
respond(res, 'thanks');
});
});
server.listen(port);
server.listen(PORT);
var client = http.createClient(port);
var request = client.request('POST', '/', {'Content-Type': 'multipart/form-data; boundary=AaB03x', 'Content-Length': fixture.reply.length});
var client = http.createClient(PORT);
var request = client.request('POST', '/', {
'Content-Type': 'multipart/form-data; boundary=AaB03x',
'Content-Length': fixture.reply.length
});
request.sendBody(fixture.reply, 'binary');
request.finish();
var simpleRequest = client.request('POST', '/', {
'X-Use-Simple-Api': 'yes',
'Content-Type': 'multipart/form-data; boundary=AaB03x',
'Content-Length': fixture.reply.length
});
simpleRequest.sendBody(fixture.reply, 'binary');
simpleRequest.finish();
var badRequest = client.request('POST', '/', {
'Content-Type': 'invalid!',
'Content-Length': fixture.reply.length
});
badRequest.sendBody(fixture.reply, 'binary');
badRequest.finish();
var simpleBadRequest = client.request('POST', '/', {
'X-Use-Simple-Api': 'yes',
'Content-Type': 'something',
'Content-Length': fixture.reply.length
});
simpleBadRequest.sendBody(fixture.reply, 'binary');
simpleBadRequest.finish();
process.addListener('exit', function() {
puts("done");
assert.equal(2, parts_complete);
assert.equal(2, parts_reveived);
});
assert.equal(2, partsComplete);
assert.equal(2, partsReceived);
assert.equal(2, badRequests);
});

27
test/mjsunit/test-path.js Normal file
Просмотреть файл

@ -0,0 +1,27 @@
var path = require("path");
process.mixin(require("./common"));
var f = __filename;
assert.equal(path.basename(f), "test-path.js");
assert.equal(path.basename(f, ".js"), "test-path");
assert.equal(path.extname(f), ".js");
assert.equal(path.dirname(f).substr(-13), "/test/mjsunit");
path.exists(f, function (y) { assert.equal(y, true) });
assert.equal(path.join(".", "fixtures/b", "..", "/b/c.js"), "fixtures/b/c.js");
assert.equal(path.normalize("./fixtures///b/../b/c.js"), "fixtures/b/c.js");
assert.equal(path.normalize("./fixtures///b/../b/c.js",true), "fixtures///b/c.js");
assert.deepEqual(path.normalizeArray(["fixtures","b","","..","b","c.js"]), ["fixtures","b","c.js"]);
assert.deepEqual(path.normalizeArray(["fixtures","","b","..","b","c.js"], true), ["fixtures","","b","c.js"]);
assert.equal(path.normalize("a//b//../b", true), "a//b/b");
assert.equal(path.normalize("a//b//../b"), "a/b");
assert.equal(path.normalize("a//b//./c", true), "a//b//c");
assert.equal(path.normalize("a//b//./c"), "a/b/c");
assert.equal(path.normalize("a//b//.", true), "a//b/");
assert.equal(path.normalize("a//b//."), "a/b");

Просмотреть файл

@ -0,0 +1,125 @@
process.mixin(require("./common"));
// test using assert
var qs = require("querystring");
// folding block.
{
// [ wonkyQS, canonicalQS, obj ]
var qsTestCases = [
["foo=bar", "foo=bar", {"foo" : "bar"}],
["foo=bar&foo=quux", "foo%5B%5D=bar&foo%5B%5D=quux", {"foo" : ["bar", "quux"]}],
["foo=1&bar=2", "foo=1&bar=2", {"foo" : 1, "bar" : 2}],
["my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F", "my%20weird%20field=q1!2%22'w%245%267%2Fz8)%3F", {"my weird field" : "q1!2\"'w$5&7/z8)?" }],
["foo%3Dbaz=bar", "foo%3Dbaz=bar", {"foo=baz" : "bar"}],
["foo=baz=bar", "foo=baz%3Dbar", {"foo" : "baz=bar"}],
[ "str=foo&arr[]=1&arr[]=2&arr[]=3&obj[a]=bar&obj[b][]=4&obj[b][]=5&obj[b][]=6&obj[b][]=&obj[c][]=4&obj[c][]=5&obj[c][][somestr]=baz&obj[objobj][objobjstr]=blerg&somenull=&undef=", "str=foo&arr%5B%5D=1&arr%5B%5D=2&arr%5B%5D=3&obj%5Ba%5D=bar&obj%5Bb%5D%5B%5D=4&obj%5Bb%5D%5B%5D=5&obj%5Bb%5D%5B%5D=6&obj%5Bb%5D%5B%5D=&obj%5Bc%5D%5B%5D=4&obj%5Bc%5D%5B%5D=5&obj%5Bc%5D%5B%5D%5Bsomestr%5D=baz&obj%5Bobjobj%5D%5Bobjobjstr%5D=blerg&somenull=&undef=", {
"str":"foo",
"arr":[1,2,3],
"obj":{
"a":"bar",
"b":[4,5,6,""],
"c":[4,5,{"somestr":"baz"}],
"objobj":{"objobjstr":"blerg"}
},
"somenull":"",
"undef":""
}],
["foo[bar][bla]=baz&foo[bar][bla]=blo", "foo%5Bbar%5D%5Bbla%5D%5B%5D=baz&foo%5Bbar%5D%5Bbla%5D%5B%5D=blo", {"foo":{"bar":{"bla":["baz","blo"]}}}],
["foo[bar][][bla]=baz&foo[bar][][bla]=blo", "foo%5Bbar%5D%5B%5D%5Bbla%5D=baz&foo%5Bbar%5D%5B%5D%5Bbla%5D=blo", {"foo":{"bar":[{"bla":"baz"},{"bla":"blo"}]}}],
["foo[bar][bla][]=baz&foo[bar][bla][]=blo", "foo%5Bbar%5D%5Bbla%5D%5B%5D=baz&foo%5Bbar%5D%5Bbla%5D%5B%5D=blo", {"foo":{"bar":{"bla":["baz","blo"]}}}],
[" foo = bar ", "foo=bar", {"foo":"bar"}]
];
// [ wonkyQS, canonicalQS, obj ]
var qsColonTestCases = [
["foo:bar", "foo:bar", {"foo":"bar"}],
["foo:bar;foo:quux", "foo%5B%5D:bar;foo%5B%5D:quux", {"foo" : ["bar", "quux"]}],
["foo:1&bar:2;baz:quux", "foo:1%26bar%3A2;baz:quux", {"foo":"1&bar:2", "baz":"quux"}],
["foo%3Abaz:bar", "foo%3Abaz:bar", {"foo:baz":"bar"}],
["foo:baz:bar", "foo:baz%3Abar", {"foo":"baz:bar"}]
];
// [ wonkyObj, qs, canonicalObj ]
var extendedFunction = function () {};
extendedFunction.prototype = {a:"b"};
var qsWeirdObjects = [
[ {regexp:/./g}, "regexp=", {"regexp":""} ],
[ {regexp: new RegExp(".", "g")}, "regexp=", {"regexp":""} ],
[ {fn:function () {}}, "fn=", {"fn":""}],
[ {fn:new Function("")}, "fn=", {"fn":""} ],
[ {math:Math}, "math=", {"math":""} ],
[ {e:extendedFunction}, "e=", {"e":""} ],
[ {d:new Date()}, "d=", {"d":""} ],
[ {d:Date}, "d=", {"d":""} ],
[ {f:new Boolean(false), t:new Boolean(true)}, "f=0&t=1", {"f":0, "t":1} ],
[ {f:false, t:true}, "f=0&t=1", {"f":0, "t":1} ],
];
}
// test that the canonical qs is parsed properly.
qsTestCases.forEach(function (testCase) {
assert.deepEqual(testCase[2], qs.parse(testCase[0]));
});
// test that the colon test cases can do the same
qsColonTestCases.forEach(function (testCase) {
assert.deepEqual(testCase[2], qs.parse(testCase[0], ";", ":"));
});
// test the weird objects, that they get parsed properly
qsWeirdObjects.forEach(function (testCase) {
assert.deepEqual(testCase[2], qs.parse(testCase[1]));
});
// test the nested qs-in-qs case
var f = qs.parse("a=b&q=x%3Dy%26y%3Dz");
f.q = qs.parse(f.q);
assert.deepEqual(f, { a : "b", q : { x : "y", y : "z" } });
// nested in colon
var f = qs.parse("a:b;q:x%3Ay%3By%3Az", ";", ":");
f.q = qs.parse(f.q, ";", ":");
assert.deepEqual(f, { a : "b", q : { x : "y", y : "z" } });
// now test stringifying
assert.throws(function () {
var f = {};
f.f = f;
qs.stringify(f);
});
// basic
qsTestCases.forEach(function (testCase) {
assert.equal(testCase[1], qs.stringify(testCase[2]));
});
qsColonTestCases.forEach(function (testCase) {
assert.equal(testCase[1], qs.stringify(testCase[2], ";", ":"));
});
qsWeirdObjects.forEach(function (testCase) {
assert.equal(testCase[1], qs.stringify(testCase[0]));
});
// nested
var f = qs.stringify({
a : "b",
q : qs.stringify({
x : "y",
y : "z"
})
});
assert.equal(f, "a=b&q=x%3Dy%26y%3Dz");
// nested in colon
var f = qs.stringify({
a : "b",
q : qs.stringify({
x : "y",
y : "z"
}, ";", ":")
}, ";", ":");
assert.equal(f, "a:b;q:x%3Ay%3By%3Az");

Просмотреть файл

@ -3,11 +3,12 @@ process.mixin(require("./common"));
var PORT = 8889;
var http = require('http');
var sys = require('sys');
var url = require("url");
var modulesLoaded = 0;
var server = http.createServer(function(req, res) {
var body = 'exports.httpPath = function() {'+
'return '+JSON.stringify(req.uri.path)+';'+
'return '+JSON.stringify(url.parse(req.url).pathname)+';'+
'};';
res.sendHeader(200, {'Content-Type': 'text/javascript'});

Просмотреть файл

@ -13,6 +13,7 @@ assert.equal('null', inspect(null));
assert.equal("\"\\n\\u0001\"", inspect("\n\u0001"));
assert.equal('[]', inspect([]));
assert.equal('[]', inspect(Object.create([])));
assert.equal('[\n 1,\n 2\n]', inspect([1, 2]));
assert.equal('[\n 1,\n [\n 2,\n 3\n ]\n]', inspect([1, [2, 3]]));
@ -23,6 +24,23 @@ assert.equal('{\n "a": 1,\n "b": 2\n}', inspect({a: 1, b: 2}));
assert.equal('{\n "a": {}\n}', inspect({'a': {}}));
assert.equal('{\n "a": {\n "b": 2\n }\n}', inspect({'a': {'b': 2}}));
var value = {}
// Dynamic properties
assert.equal(
"{\n \"readonly\": [Getter],\n \"readwrite\": [Getter/Setter],\n \"writeonly\": [Setter]\n}",
inspect({get readonly(){},get readwrite(){},set readwrite(val){},set writeonly(val){}})
);
var value = {};
value['a'] = value;
assert.equal('{\n "a": [Circular]\n}', inspect(value));
value = Object.create([]);
value.push(1);
assert.equal('{\n "0": 1,\n "length": 1\n}', inspect(value));
// Array with dynamic properties
value = [1,2,3];
value.__defineGetter__('growingLength', function () { this.push(true); return this.length; });
assert.equal(
"{\n \"0\": 1,\n \"1\": 2,\n \"2\": 3,\n \"growingLength\": [Getter]\n}",
inspect(value)
);

Просмотреть файл

@ -1,193 +0,0 @@
process.mixin(require("./common"));
var uri = require("uri"),
sys = require("sys");
// URIs to parse, and expected data
// { url : parsed }
var parseTests = {
"http://www.narwhaljs.org/blog/categories?id=news" : {
"url": "http://www.narwhaljs.org/blog/categories?id=news",
"protocol": "http",
"authorityRoot": "//",
"authority": "www.narwhaljs.org",
"userInfo": "",
"user": "",
"password": "",
"domain": "www.narwhaljs.org",
"port": "",
"path": "/blog/categories",
"root": "/",
"directory": "blog/",
"file": "categories",
"query": "id=news",
"anchor": "",
"directories": [
"blog"
],
"domains": [
"www",
"narwhaljs",
"org"
]
},
"http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=" : {
"url": "http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=",
"protocol": "http",
"authorityRoot": "//",
"authority": "mt0.google.com",
"userInfo": "",
"user": "",
"password": "",
"domain": "mt0.google.com",
"port": "",
"path": "/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=",
"root": "/",
"directory": "vt/",
"file": "lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=",
"query": "",
"anchor": "",
"directories": [
"vt"
],
"domains": [
"mt0",
"google",
"com"
]
},
"http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=" : {
"url": "http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=",
"protocol": "http",
"authorityRoot": "//",
"authority": "mt0.google.com",
"userInfo": "",
"user": "",
"password": "",
"domain": "mt0.google.com",
"port": "",
"path": "/vt/lyrs=m@114",
"root": "/",
"directory": "vt/",
"file": "lyrs=m@114",
"query": "??&hl=en&src=api&x=2&y=2&z=3&s=",
"anchor": "",
"directories": [
"vt"
],
"domains": [
"mt0",
"google",
"com"
]
},
"http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=" : {
"url": "http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=",
"protocol": "http",
"authorityRoot": "//",
"authority": "user:pass@mt0.google.com",
"userInfo": "user:pass",
"user": "user",
"password": "pass",
"domain": "mt0.google.com",
"port": "",
"path": "/vt/lyrs=m@114",
"root": "/",
"directory": "vt/",
"file": "lyrs=m@114",
"query": "??&hl=en&src=api&x=2&y=2&z=3&s=",
"anchor": "",
"directories": [
"vt"
],
"domains": [
"mt0",
"google",
"com"
]
},
"file:///etc/passwd" : {
"url": "file:///etc/passwd",
"protocol": "file",
"authorityRoot": "//",
"authority": "",
"userInfo": "",
"user": "",
"password": "",
"domain": "",
"port": "",
"path": "/etc/passwd",
"root": "/",
"directory": "etc/",
"file": "passwd",
"query": "",
"anchor": "",
"directories": [
"etc"
],
"domains": [
""
]
},
"file:///etc/node/" : {
"url": "file:///etc/node/",
"protocol": "file",
"authorityRoot": "//",
"authority": "",
"userInfo": "",
"user": "",
"password": "",
"domain": "",
"port": "",
"path": "/etc/node/",
"root": "/",
"directory": "etc/node/",
"file": "",
"query": "",
"anchor": "",
"directories": [
"etc",
"node"
],
"domains": [
""
]
}
};
for (var url in parseTests) {
var actual = uri.parse(url),
expected = parseTests[url];
for (var i in expected) {
// sys.debug(i);
// sys.debug("expect: " + JSON.stringify(expected[i]));
// sys.debug("actual: " + JSON.stringify(actual[i]));
var e = JSON.stringify(expected[i]),
a = JSON.stringify(actual[i]);
assert.equal(e, a, "parse(" + url + ")."+i+" == "+e+"\nactual: "+a);
}
var expected = url,
actual = uri.format(parseTests[url]);
assert.equal(expected, actual, "format("+url+") == "+url+"\nactual:"+actual);
}
[
// [from, path, expected]
["/foo/bar/baz", "quux", "/foo/bar/quux"],
["/foo/bar/baz", "quux/asdf", "/foo/bar/quux/asdf"],
["/foo/bar/baz", "quux/baz", "/foo/bar/quux/baz"],
["/foo/bar/baz", "../quux/baz", "/foo/quux/baz"],
["/foo/bar/baz", "/bar", "/bar"],
["/foo/bar/baz/", "quux", "/foo/bar/baz/quux"],
["/foo/bar/baz/", "quux/baz", "/foo/bar/baz/quux/baz"],
["/foo/bar/baz", "../../../../../../../../quux/baz", "/quux/baz"],
["/foo/bar/baz", "../../../../../../../quux/baz", "/quux/baz"]
].forEach(function (relativeTest) {
var a = uri.resolve(relativeTest[0], relativeTest[1]),
e = relativeTest[2];
assert.equal(e, a,
"resolve("+[relativeTest[0], relativeTest[1]]+") == "+e+
"\n actual="+a);
});

516
test/mjsunit/test-url.js Normal file
Просмотреть файл

@ -0,0 +1,516 @@
process.mixin(require("./common"));
var url = require("url"),
sys = require("sys");
// URLs to parse, and expected data
// { url : parsed }
var parseTests = {
"http://www.narwhaljs.org/blog/categories?id=news" : {
"href": "http://www.narwhaljs.org/blog/categories?id=news",
"protocol": "http:",
"host": "www.narwhaljs.org",
"hostname": "www.narwhaljs.org",
"search": "?id=news",
"query": "id=news",
"pathname": "/blog/categories"
},
"http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=" : {
"href": "http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=",
"protocol": "http:",
"host": "mt0.google.com",
"hostname": "mt0.google.com",
"pathname": "/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s="
},
"http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=" : {
"href": "http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=",
"protocol": "http:",
"host": "mt0.google.com",
"hostname": "mt0.google.com",
"search": "???&hl=en&src=api&x=2&y=2&z=3&s=",
"query": "??&hl=en&src=api&x=2&y=2&z=3&s=",
"pathname": "/vt/lyrs=m@114"
},
"http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=" : {
"href": "http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=",
"protocol": "http:",
"host": "user:pass@mt0.google.com",
"auth": "user:pass",
"hostname": "mt0.google.com",
"search": "???&hl=en&src=api&x=2&y=2&z=3&s=",
"query": "??&hl=en&src=api&x=2&y=2&z=3&s=",
"pathname": "/vt/lyrs=m@114"
},
"file:///etc/passwd" : {
"href": "file:///etc/passwd",
"protocol": "file:",
"pathname": "///etc/passwd"
},
"file:///etc/node/" : {
"href": "file:///etc/node/",
"protocol": "file:",
"pathname": "///etc/node/"
},
"http:/baz/../foo/bar" : {
"href": "http:/baz/../foo/bar",
"protocol": "http:",
"pathname": "/baz/../foo/bar"
},
"http://user:pass@example.com:8000/foo/bar?baz=quux#frag" : {
"href": "http://user:pass@example.com:8000/foo/bar?baz=quux#frag",
"protocol": "http:",
"host": "user:pass@example.com:8000",
"auth": "user:pass",
"port": "8000",
"hostname": "example.com",
"hash": "#frag",
"search": "?baz=quux",
"query": "baz=quux",
"pathname": "/foo/bar"
},
"//user:pass@example.com:8000/foo/bar?baz=quux#frag" : {
"href": "//user:pass@example.com:8000/foo/bar?baz=quux#frag",
"host": "user:pass@example.com:8000",
"auth": "user:pass",
"port": "8000",
"hostname": "example.com",
"hash": "#frag",
"search": "?baz=quux",
"query": "baz=quux",
"pathname": "/foo/bar"
},
"http://example.com?foo=bar#frag" : {
"href": "http://example.com?foo=bar#frag",
"protocol": "http:",
"host": "example.com",
"hostname": "example.com",
"hash": "#frag",
"search": "?foo=bar",
"query": "foo=bar"
},
"http://example.com?foo=@bar#frag" : {
"href": "http://example.com?foo=@bar#frag",
"protocol": "http:",
"host": "example.com",
"hostname": "example.com",
"hash": "#frag",
"search": "?foo=@bar",
"query": "foo=@bar"
},
"http://example.com?foo=/bar/#frag" : {
"href": "http://example.com?foo=/bar/#frag",
"protocol": "http:",
"host": "example.com",
"hostname": "example.com",
"hash": "#frag",
"search": "?foo=/bar/",
"query": "foo=/bar/"
},
"http://example.com?foo=?bar/#frag" : {
"href": "http://example.com?foo=?bar/#frag",
"protocol": "http:",
"host": "example.com",
"hostname": "example.com",
"hash": "#frag",
"search": "?foo=?bar/",
"query": "foo=?bar/"
},
"http://example.com#frag=?bar/#frag" : {
"href": "http://example.com#frag=?bar/#frag",
"protocol": "http:",
"host": "example.com",
"hostname": "example.com",
"hash": "#frag=?bar/#frag"
},
"/foo/bar?baz=quux#frag" : {
"href": "/foo/bar?baz=quux#frag",
"hash": "#frag",
"search": "?baz=quux",
"query": "baz=quux",
"pathname": "/foo/bar"
},
"http:/foo/bar?baz=quux#frag" : {
"href": "http:/foo/bar?baz=quux#frag",
"protocol": "http:",
"hash": "#frag",
"search": "?baz=quux",
"query": "baz=quux",
"pathname": "/foo/bar"
},
"mailto:foo@bar.com?subject=hello" : {
"href": "mailto:foo@bar.com?subject=hello",
"protocol": "mailto:",
"host": "foo@bar.com",
"auth" : "foo",
"hostname" : "bar.com",
"search": "?subject=hello",
"query": "subject=hello"
},
"javascript:alert('hello');" : {
"href": "javascript:alert('hello');",
"protocol": "javascript:",
"host": "alert('hello')",
"hostname": "alert('hello')",
"pathname" : ";"
},
"xmpp:isaacschlueter@jabber.org" : {
"href": "xmpp:isaacschlueter@jabber.org",
"protocol": "xmpp:",
"host": "isaacschlueter@jabber.org",
"auth": "isaacschlueter",
"hostname": "jabber.org"
}
};
for (var u in parseTests) {
var actual = url.parse(u),
expected = parseTests[u];
for (var i in expected) {
var e = JSON.stringify(expected[i]),
a = JSON.stringify(actual[i]);
assert.equal(e, a, "parse(" + u + ")."+i+" == "+e+"\nactual: "+a);
}
var expected = u,
actual = url.format(parseTests[u]);
assert.equal(expected, actual, "format("+u+") == "+u+"\nactual:"+actual);
}
var parseTestsWithQueryString = {
"/foo/bar?baz=quux#frag" : {
"href": "/foo/bar?baz=quux#frag",
"hash": "#frag",
"search": "?baz=quux",
"query": {
"baz": "quux"
},
"pathname": "/foo/bar"
},
};
for (var u in parseTestsWithQueryString) {
var actual = url.parse(u,true);
var expected = parseTestsWithQueryString[u];
for (var i in expected) {
var e = JSON.stringify(expected[i]),
a = JSON.stringify(actual[i]);
assert.equal(e, a, "parse(" + u + ")."+i+" == "+e+"\nactual: "+a);
}
}
// some extra formatting tests, just to verify that it'll format slightly wonky content to a valid url.
var formatTests = {
"http://a.com/a/b/c?s#h" : {
"protocol": "http",
"host": "a.com",
"pathname": "a/b/c",
"hash": "h",
"search": "s"
},
"xmpp:isaacschlueter@jabber.org" : {
"href": "xmpp://isaacschlueter@jabber.org",
"protocol": "xmpp:",
"host": "isaacschlueter@jabber.org",
"auth": "isaacschlueter",
"hostname": "jabber.org"
}
};
for (var u in formatTests) {
var actual = url.format(formatTests[u]);
assert.equal(actual, u, "wonky format("+u+") == "+u+"\nactual:"+actual);
}
[
// [from, path, expected]
["/foo/bar/baz", "quux", "/foo/bar/quux"],
["/foo/bar/baz", "quux/asdf", "/foo/bar/quux/asdf"],
["/foo/bar/baz", "quux/baz", "/foo/bar/quux/baz"],
["/foo/bar/baz", "../quux/baz", "/foo/quux/baz"],
["/foo/bar/baz", "/bar", "/bar"],
["/foo/bar/baz/", "quux", "/foo/bar/baz/quux"],
["/foo/bar/baz/", "quux/baz", "/foo/bar/baz/quux/baz"],
["/foo/bar/baz", "../../../../../../../../quux/baz", "/quux/baz"],
["/foo/bar/baz", "../../../../../../../quux/baz", "/quux/baz"],
["foo/bar", "../../../baz", "../../baz"],
["foo/bar/", "../../../baz", "../baz"],
["http://example.com/b//c//d;p?q#blarg","https:#hash2","https:///#hash2" ],
["http://example.com/b//c//d;p?q#blarg","https:/p/a/t/h?s#hash2","https://p/a/t/h?s#hash2" ],
["http://example.com/b//c//d;p?q#blarg","https://u:p@h.com/p/a/t/h?s#hash2","https://u:p@h.com/p/a/t/h?s#hash2"],
["http://example.com/b//c//d;p?q#blarg","https:/a/b/c/d","https://a/b/c/d"],
["http://example.com/b//c//d;p?q#blarg","http:#hash2","http://example.com/b//c//d;p?q#hash2" ],
["http://example.com/b//c//d;p?q#blarg","http:/p/a/t/h?s#hash2","http://example.com/p/a/t/h?s#hash2" ],
["http://example.com/b//c//d;p?q#blarg","http://u:p@h.com/p/a/t/h?s#hash2","http://u:p@h.com/p/a/t/h?s#hash2" ],
["http://example.com/b//c//d;p?q#blarg","http:/a/b/c/d","http://example.com/a/b/c/d"],
["/foo/bar/baz", "/../etc/passwd", "/etc/passwd"]
].forEach(function (relativeTest) {
var a = url.resolve(relativeTest[0], relativeTest[1]),
e = relativeTest[2];
assert.equal(e, a,
"resolve("+[relativeTest[0], relativeTest[1]]+") == "+e+
"\n actual="+a);
});
//
// Tests below taken from Chiron
// http://code.google.com/p/chironjs/source/browse/trunk/src/test/http/url.js
//
// Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
// used with permission under MIT License
//
// Changes marked with @isaacs
var bases = [
'http://a/b/c/d;p?q',
'http://a/b/c/d;p?q=1/2',
'http://a/b/c/d;p=1/2?q',
'fred:///s//a/b/c',
'http:///s//a/b/c'
];
//[to, from, result]
[
// http://lists.w3.org/Archives/Public/uri/2004Feb/0114.html
['../c', 'foo:a/b', 'foo:c'],
['foo:.', 'foo:a', 'foo:'],
['/foo/../../../bar', 'zz:abc', 'zz:/bar'],
['/foo/../bar', 'zz:abc', 'zz:/bar'],
['foo/../../../bar', 'zz:abc', 'zz:bar'], // @isaacs Disagree. Not how web browsers resolve this.
// ['foo/../../../bar', 'zz:abc', 'zz:../../bar'], // @isaacs Added
['foo/../bar', 'zz:abc', 'zz:bar'],
['zz:.', 'zz:abc', 'zz:'],
['/.' , bases[0], 'http://a/'],
['/.foo' , bases[0], 'http://a/.foo'],
['.foo' , bases[0], 'http://a/b/c/.foo'],
// http://gbiv.com/protocols/uri/test/rel_examples1.html
// examples from RFC 2396
['g:h' , bases[0], 'g:h'],
['g' , bases[0], 'http://a/b/c/g'],
['./g' , bases[0], 'http://a/b/c/g'],
['g/' , bases[0], 'http://a/b/c/g/'],
['/g' , bases[0], 'http://a/g'],
['//g' , bases[0], 'http://g'],
// changed with RFC 2396bis
//('?y' , bases[0], 'http://a/b/c/d;p?y'],
['?y' , bases[0], 'http://a/b/c/d;p?y'],
['g?y' , bases[0], 'http://a/b/c/g?y'],
// changed with RFC 2396bis
//('#s' , bases[0], CURRENT_DOC_URI + '#s'],
['#s' , bases[0], 'http://a/b/c/d;p?q#s'],
['g#s' , bases[0], 'http://a/b/c/g#s'],
['g?y#s' , bases[0], 'http://a/b/c/g?y#s'],
[';x' , bases[0], 'http://a/b/c/;x'],
['g;x' , bases[0], 'http://a/b/c/g;x'],
['g;x?y#s' , bases[0], 'http://a/b/c/g;x?y#s'],
// changed with RFC 2396bis
//('' , bases[0], CURRENT_DOC_URI],
['' , bases[0], 'http://a/b/c/d;p?q'],
['.' , bases[0], 'http://a/b/c/'],
['./' , bases[0], 'http://a/b/c/'],
['..' , bases[0], 'http://a/b/'],
['../' , bases[0], 'http://a/b/'],
['../g' , bases[0], 'http://a/b/g'],
['../..' , bases[0], 'http://a/'],
['../../' , bases[0], 'http://a/'],
['../../g' , bases[0], 'http://a/g'],
['../../../g', bases[0], ('http://a/../g', 'http://a/g')],
['../../../../g', bases[0], ('http://a/../../g', 'http://a/g')],
// changed with RFC 2396bis
//('/./g', bases[0], 'http://a/./g'],
['/./g', bases[0], 'http://a/g'],
// changed with RFC 2396bis
//('/../g', bases[0], 'http://a/../g'],
['/../g', bases[0], 'http://a/g'],
['g.', bases[0], 'http://a/b/c/g.'],
['.g', bases[0], 'http://a/b/c/.g'],
['g..', bases[0], 'http://a/b/c/g..'],
['..g', bases[0], 'http://a/b/c/..g'],
['./../g', bases[0], 'http://a/b/g'],
['./g/.', bases[0], 'http://a/b/c/g/'],
['g/./h', bases[0], 'http://a/b/c/g/h'],
['g/../h', bases[0], 'http://a/b/c/h'],
['g;x=1/./y', bases[0], 'http://a/b/c/g;x=1/y'],
['g;x=1/../y', bases[0], 'http://a/b/c/y'],
['g?y/./x', bases[0], 'http://a/b/c/g?y/./x'],
['g?y/../x', bases[0], 'http://a/b/c/g?y/../x'],
['g#s/./x', bases[0], 'http://a/b/c/g#s/./x'],
['g#s/../x', bases[0], 'http://a/b/c/g#s/../x'],
['http:g', bases[0], ('http:g', 'http://a/b/c/g')],
['http:', bases[0], ('http:', bases[0])],
// not sure where this one originated
['/a/b/c/./../../g', bases[0], 'http://a/a/g'],
// http://gbiv.com/protocols/uri/test/rel_examples2.html
// slashes in base URI's query args
['g' , bases[1], 'http://a/b/c/g'],
['./g' , bases[1], 'http://a/b/c/g'],
['g/' , bases[1], 'http://a/b/c/g/'],
['/g' , bases[1], 'http://a/g'],
['//g' , bases[1], 'http://g'],
// changed in RFC 2396bis
//('?y' , bases[1], 'http://a/b/c/?y'],
['?y' , bases[1], 'http://a/b/c/d;p?y'],
['g?y' , bases[1], 'http://a/b/c/g?y'],
['g?y/./x' , bases[1], 'http://a/b/c/g?y/./x'],
['g?y/../x', bases[1], 'http://a/b/c/g?y/../x'],
['g#s' , bases[1], 'http://a/b/c/g#s'],
['g#s/./x' , bases[1], 'http://a/b/c/g#s/./x'],
['g#s/../x', bases[1], 'http://a/b/c/g#s/../x'],
['./' , bases[1], 'http://a/b/c/'],
['../' , bases[1], 'http://a/b/'],
['../g' , bases[1], 'http://a/b/g'],
['../../' , bases[1], 'http://a/'],
['../../g' , bases[1], 'http://a/g'],
// http://gbiv.com/protocols/uri/test/rel_examples3.html
// slashes in path params
// all of these changed in RFC 2396bis
['g' , bases[2], 'http://a/b/c/d;p=1/g'],
['./g' , bases[2], 'http://a/b/c/d;p=1/g'],
['g/' , bases[2], 'http://a/b/c/d;p=1/g/'],
['g?y' , bases[2], 'http://a/b/c/d;p=1/g?y'],
[';x' , bases[2], 'http://a/b/c/d;p=1/;x'],
['g;x' , bases[2], 'http://a/b/c/d;p=1/g;x'],
['g;x=1/./y', bases[2], 'http://a/b/c/d;p=1/g;x=1/y'],
['g;x=1/../y', bases[2], 'http://a/b/c/d;p=1/y'],
['./' , bases[2], 'http://a/b/c/d;p=1/'],
['../' , bases[2], 'http://a/b/c/'],
['../g' , bases[2], 'http://a/b/c/g'],
['../../' , bases[2], 'http://a/b/'],
['../../g' , bases[2], 'http://a/b/g'],
// http://gbiv.com/protocols/uri/test/rel_examples4.html
// double and triple slash, unknown scheme
['g:h' , bases[3], 'g:h'],
['g' , bases[3], 'fred:///s//a/b/g'],
['./g' , bases[3], 'fred:///s//a/b/g'],
['g/' , bases[3], 'fred:///s//a/b/g/'],
['/g' , bases[3], 'fred:///g'], // may change to fred:///s//a/g
['//g' , bases[3], 'fred://g'], // may change to fred:///s//g
['//g/x' , bases[3], 'fred://g/x'], // may change to fred:///s//g/x
['///g' , bases[3], 'fred:///g'],
['./' , bases[3], 'fred:///s//a/b/'],
['../' , bases[3], 'fred:///s//a/'],
['../g' , bases[3], 'fred:///s//a/g'],
['../../' , bases[3], 'fred:///s//'],
['../../g' , bases[3], 'fred:///s//g'],
['../../../g', bases[3], 'fred:///s/g'],
['../../../../g', bases[3], 'fred:///g'], // may change to fred:///s//a/../../../g
// http://gbiv.com/protocols/uri/test/rel_examples5.html
// double and triple slash, well-known scheme
['g:h' , bases[4], 'g:h'],
['g' , bases[4], 'http:///s//a/b/g'],
['./g' , bases[4], 'http:///s//a/b/g'],
['g/' , bases[4], 'http:///s//a/b/g/'],
['/g' , bases[4], 'http:///g'], // may change to http:///s//a/g
['//g' , bases[4], 'http://g'], // may change to http:///s//g
['//g/x' , bases[4], 'http://g/x'], // may change to http:///s//g/x
['///g' , bases[4], 'http:///g'],
['./' , bases[4], 'http:///s//a/b/'],
['../' , bases[4], 'http:///s//a/'],
['../g' , bases[4], 'http:///s//a/g'],
['../../' , bases[4], 'http:///s//'],
['../../g' , bases[4], 'http:///s//g'],
['../../../g', bases[4], 'http:///s/g'], // may change to http:///s//a/../../g
['../../../../g', bases[4], 'http:///g'], // may change to http:///s//a/../../../g
// from Dan Connelly's tests in http://www.w3.org/2000/10/swap/uripath.py
["bar:abc", "foo:xyz", "bar:abc"],
['../abc', 'http://example/x/y/z', 'http://example/x/abc'],
['http://example/x/abc', 'http://example2/x/y/z', 'http://example/x/abc'],
['../r', 'http://ex/x/y/z', 'http://ex/x/r'],
['q/r', 'http://ex/x/y', 'http://ex/x/q/r'],
['q/r#s', 'http://ex/x/y', 'http://ex/x/q/r#s'],
['q/r#s/t', 'http://ex/x/y', 'http://ex/x/q/r#s/t'],
['ftp://ex/x/q/r', 'http://ex/x/y', 'ftp://ex/x/q/r'],
['', 'http://ex/x/y', 'http://ex/x/y'],
['', 'http://ex/x/y/', 'http://ex/x/y/'],
['', 'http://ex/x/y/pdq', 'http://ex/x/y/pdq'],
['z/', 'http://ex/x/y/', 'http://ex/x/y/z/'],
['#Animal', 'file:/swap/test/animal.rdf', 'file:/swap/test/animal.rdf#Animal'],
['../abc', 'file:/e/x/y/z', 'file:/e/x/abc'],
['/example/x/abc', 'file:/example2/x/y/z', 'file:/example/x/abc'],
['../r', 'file:/ex/x/y/z', 'file:/ex/x/r'],
['/r', 'file:/ex/x/y/z', 'file:/r'],
['q/r', 'file:/ex/x/y', 'file:/ex/x/q/r'],
['q/r#s', 'file:/ex/x/y', 'file:/ex/x/q/r#s'],
['q/r#', 'file:/ex/x/y', 'file:/ex/x/q/r#'],
['q/r#s/t', 'file:/ex/x/y', 'file:/ex/x/q/r#s/t'],
['ftp://ex/x/q/r', 'file:/ex/x/y', 'ftp://ex/x/q/r'],
['', 'file:/ex/x/y', 'file:/ex/x/y'],
['', 'file:/ex/x/y/', 'file:/ex/x/y/'],
['', 'file:/ex/x/y/pdq', 'file:/ex/x/y/pdq'],
['z/', 'file:/ex/x/y/', 'file:/ex/x/y/z/'],
['file://meetings.example.com/cal#m1', 'file:/devel/WWW/2000/10/swap/test/reluri-1.n3', 'file://meetings.example.com/cal#m1'],
['file://meetings.example.com/cal#m1', 'file:/home/connolly/w3ccvs/WWW/2000/10/swap/test/reluri-1.n3', 'file://meetings.example.com/cal#m1'],
['./#blort', 'file:/some/dir/foo', 'file:/some/dir/#blort'],
['./#', 'file:/some/dir/foo', 'file:/some/dir/#'],
// Ryan Lee
["./", "http://example/x/abc.efg", "http://example/x/"],
// Graham Klyne's tests
// http://www.ninebynine.org/Software/HaskellUtils/Network/UriTest.xls
// 01-31 are from Connelly's cases
// 32-49
['./q:r', 'http://ex/x/y', 'http://ex/x/q:r'],
['./p=q:r', 'http://ex/x/y', 'http://ex/x/p=q:r'],
['?pp/rr', 'http://ex/x/y?pp/qq', 'http://ex/x/y?pp/rr'],
['y/z', 'http://ex/x/y?pp/qq', 'http://ex/x/y/z'],
['local/qual@domain.org#frag', 'mailto:local', 'mailto:local/qual@domain.org#frag'],
['more/qual2@domain2.org#frag', 'mailto:local/qual1@domain1.org', 'mailto:local/more/qual2@domain2.org#frag'],
['y?q', 'http://ex/x/y?q', 'http://ex/x/y?q'],
['/x/y?q', 'http://ex?p', 'http://ex/x/y?q'],
['c/d', 'foo:a/b', 'foo:a/c/d'],
['/c/d', 'foo:a/b', 'foo:/c/d'],
['', 'foo:a/b?c#d', 'foo:a/b?c'],
['b/c', 'foo:a', 'foo:b/c'],
['../b/c', 'foo:/a/y/z', 'foo:/a/b/c'],
['./b/c', 'foo:a', 'foo:b/c'],
['/./b/c', 'foo:a', 'foo:/b/c'],
['../../d', 'foo://a//b/c', 'foo://a/d'],
['.', 'foo:a', 'foo:'],
['..', 'foo:a', 'foo:'],
// 50-57[cf. TimBL comments --
// http://lists.w3.org/Archives/Public/uri/2003Feb/0028.html,
// http://lists.w3.org/Archives/Public/uri/2003Jan/0008.html)
['abc', 'http://example/x/y%2Fz', 'http://example/x/abc'],
['../../x%2Fabc', 'http://example/a/x/y/z', 'http://example/a/x%2Fabc'],
['../x%2Fabc', 'http://example/a/x/y%2Fz', 'http://example/a/x%2Fabc'],
['abc', 'http://example/x%2Fy/z', 'http://example/x%2Fy/abc'],
['q%3Ar', 'http://ex/x/y', 'http://ex/x/q%3Ar'],
['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'],
['/x%2Fabc', 'http://example/x/y/z', 'http://example/x%2Fabc'],
['/x%2Fabc', 'http://example/x/y%2Fz', 'http://example/x%2Fabc'],
// 70-77
['local2@domain2', 'mailto:local1@domain1?query1', 'mailto:local2@domain2'],
['local2@domain2?query2', 'mailto:local1@domain1', 'mailto:local2@domain2?query2'],
['local2@domain2?query2', 'mailto:local1@domain1?query1', 'mailto:local2@domain2?query2'],
['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'],
['local@domain?query2', 'mailto:?query1', 'mailto:local@domain?query2'],
['?query2', 'mailto:local@domain?query1', 'mailto:local@domain?query2'],
['http://example/a/b?c/../d', 'foo:bar', 'http://example/a/b?c/../d'],
['http://example/a/b#c/../d', 'foo:bar', 'http://example/a/b#c/../d'],
// 82-88
// ['http:this', 'http://example.org/base/uri', 'http:this'], // @isaacs Disagree. Not how browsers do it.
['http:this', 'http://example.org/base/uri', "http://example.org/base/this"], // @isaacs Added
['http:this', 'http:base', 'http:this'],
['.//g', 'f:/a', 'f://g'],
['b/c//d/e', 'f://example.org/base/a', 'f://example.org/base/b/c//d/e'],
['m2@example.ord/c2@example.org', 'mid:m@example.ord/c@example.org', 'mid:m@example.ord/m2@example.ord/c2@example.org'],
['mini1.xml', 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/', 'file:///C:/DEV/Haskell/lib/HXmlToolbox-3.01/examples/mini1.xml'],
['../b/c', 'foo:a/y/z', 'foo:a/b/c']
].forEach(function (relativeTest) {
var a = url.resolve(relativeTest[1], relativeTest[0]),
e = relativeTest[2];
assert.equal(e, a,
"resolve("+[relativeTest[1], relativeTest[0]]+") == "+e+
"\n actual="+a);
});

11
wscript
Просмотреть файл

@ -7,7 +7,7 @@ from os.path import join, dirname, abspath
from logging import fatal
cwd = os.getcwd()
VERSION="0.1.23"
VERSION="0.1.25"
APPNAME="node.js"
import js2c
@ -101,7 +101,9 @@ def conf_subproject (conf, subdir, command=None):
def configure(conf):
conf.check_tool('compiler_cxx')
if not conf.env.CXX: conf.fatal('c++ compiler not found')
conf.check_tool('compiler_cc')
if not conf.env.CC: conf.fatal('c compiler not found')
conf.env["USE_DEBUG"] = Options.options.debug
@ -109,6 +111,9 @@ def configure(conf):
conf.env.append_value("CCFLAGS", "-rdynamic")
conf.env.append_value("LINKFLAGS_DL", "-rdynamic")
if sys.platform.startswith("freebsd"):
conf.check(lib='kvm', uselib_store='KVM')
#if Options.options.debug:
# conf.check(lib='profiler', uselib_store='PROFILER')
@ -127,7 +132,7 @@ def configure(conf):
#libpath=['/usr/lib', '/usr/local/lib'],
uselib_store='GNUTLS'):
if conf.check(lib='gpg-error',
#libpath=['/usr/lib', '/usr/local/lib'],
libpath=['/usr/lib', '/usr/local/lib'],
uselib_store='GPGERROR'):
conf.env.append_value("CCFLAGS", "-DEVCOM_HAVE_GNUTLS=1")
conf.env.append_value("CXXFLAGS", "-DEVCOM_HAVE_GNUTLS=1")
@ -349,7 +354,7 @@ def build(bld):
"""
node.add_objects = 'ev eio evcom http_parser coupling'
node.uselib_local = ''
node.uselib = 'UDNS V8 EXECINFO DL GPGERROR GNUTLS'
node.uselib = 'GNUTLS GPGERROR UDNS V8 EXECINFO DL KVM'
node.install_path = '${PREFIX}/lib'
node.install_path = '${PREFIX}/bin'