Merge branch 'master' into net2
This commit is contained in:
Коммит
02e52ef8e9
41
ChangeLog
41
ChangeLog
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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 */
|
||||
|
|
225
doc/api.txt
225
doc/api.txt
|
@ -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>
|
||||
|
|
68
lib/http.js
68
lib/http.js
|
@ -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 || '');
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
19
lib/sys.js
19
lib/sys.js
|
@ -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);
|
||||
|
|
244
lib/uri.js
244
lib/uri.js
|
@ -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));
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
155
src/node.cc
155
src/node.cc
|
@ -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;
|
||||
|
|
55
src/node.js
55
src/node.js
|
@ -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);
|
||||
});
|
|
@ -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);
|
||||
});
|
||||
|
|
@ -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
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'
|
||||
|
|
Загрузка…
Ссылка в новой задаче