mirror of https://github.com/lukechilds/node.git
Ryan Dahl
14 years ago
35 changed files with 1953 additions and 1109 deletions
@ -0,0 +1,171 @@ |
|||
Warning: this is not actual API but desired API. |
|||
|
|||
# `uv_handle_t` |
|||
|
|||
This is the abstract base class of all types of handles. All handles have in |
|||
common: |
|||
|
|||
* When handles are initialized, the reference count to the event loop is |
|||
increased by one. |
|||
|
|||
* The user owns the `uv_handle_t` memory and is in charge of freeing it. |
|||
|
|||
* In order to free resources associated with a handle, one must `uv_close()` |
|||
and wait for the `uv_close_cb` callback. After the close callback has been |
|||
made, the user is allowed to the `uv_handle_t` object. |
|||
|
|||
* The `uv_close_cb` is always made directly off the event loop. That is, it |
|||
is not called from `uv_close()`. |
|||
|
|||
|
|||
|
|||
# `uv_tcp_server_t` |
|||
|
|||
A TCP server class that is a subclass of `uv_handle_t`. This can be bound to |
|||
an address and begin accepting new TCP sockets. |
|||
|
|||
int uv_bind4(uv_tcp_server_t* tcp_server, struct sockaddr_in* address); |
|||
int uv_bind6(uv_tcp_server_t* tcp_server, struct sockaddr_in6* address); |
|||
|
|||
Binds the TCP server to an address. The `address` can be created with |
|||
`uv_ip4_addr()`. Call this before `uv_listen()` |
|||
|
|||
Returns zero on success, -1 on failure. Errors in order of least-seriousness: |
|||
|
|||
* `UV_EADDRINUSE` There is already another socket bound to the specified |
|||
address. |
|||
|
|||
* `UV_EADDRNOTAVAIL` The `address` parameter is an IP address that is not |
|||
|
|||
* `UV_EINVAL` The server is already bound to an address. |
|||
|
|||
* `UV_EFAULT` Memory of `address` parameter is unintelligible. |
|||
|
|||
|
|||
int uv_listen(uv_tcp_server_t*, int backlog, uv_connection_cb cb); |
|||
|
|||
Begins listening for connections. The accept callback is level-triggered. |
|||
|
|||
|
|||
int uv_accept(uv_tcp_server_t* server, |
|||
uv_tcp_t* client, |
|||
uv_close_cb close_cb, |
|||
void* data); |
|||
|
|||
Accepts a connection. This should be called after the accept callback is |
|||
made. The `client` parameter should be uninitialized memory; `uv_accept` is |
|||
used instead of `uv_tcp_init` for server-side `uv_tcp_t` initialization. |
|||
|
|||
Return value 0 indicates success, -1 failure. Possible errors: |
|||
|
|||
* `UV_EAGAIN` There are no connections. Wait for the `uv_connection_cb` callback |
|||
to be called again. |
|||
|
|||
* `UV_EFAULT` The memory of either `server` is unintelligible. |
|||
|
|||
|
|||
|
|||
# `uv_stream_t` |
|||
|
|||
An abstract subclass of `uv_handle_t`. Streams represent something that |
|||
reads and/or writes data. Streams can be half or full-duplex. TCP sockets |
|||
are streams, files are streams with offsets. |
|||
|
|||
int uv_read_start(uv_stream_t* stream, |
|||
uv_alloc_cb alloc_cb, |
|||
uv_read_cb read_cb); |
|||
|
|||
Starts the stream reading continuously. The `alloc_cb` is used to allow the |
|||
user to implement various means of supplying the stream with buffers to |
|||
fill. The `read_cb` returns buffers to the user filled with data. |
|||
|
|||
Sometimes the buffers returned to the user do not contain data. This does |
|||
not indicate EOF as in other systems. EOF is made via the `uv_eof_cb` which |
|||
can be set like this `uv_set_eof_cb(stream, eof_cb);` |
|||
|
|||
|
|||
int uv_read_stop(uv_stream_t* stream); |
|||
|
|||
Stops reading from the stream. |
|||
|
|||
int uv_write_req_init(uv_write_req_t*, |
|||
uv_stream_t*, |
|||
uv_buf_t bufs[], |
|||
int butcnf, |
|||
uv_close_cb close_cb, |
|||
void* data); |
|||
|
|||
Initiates a write request on a stream. |
|||
|
|||
int uv_shutdown_req_init(uv_shutdown_req_t*, uv_stream_t*) |
|||
|
|||
Initiates a shutdown of outgoing data once the write queue drains. |
|||
|
|||
|
|||
|
|||
# `uv_tcp_t` |
|||
|
|||
The TCP handle class represents one endpoint of a duplex TCP stream. |
|||
`uv_tcp_t` is a subclass of `uv_stream_t`. A TCP handle can represent a |
|||
client side connection (one that has been used with uv_connect_req_init`) |
|||
or a server-side connection (one that was initialized with `uv_accept`) |
|||
|
|||
int uv_connect_req_init(uv_connect_req_t* req, |
|||
uv_tcp_t* socket, |
|||
struct sockaddr* addr, |
|||
uv_close_cb close_cb, |
|||
void* data); |
|||
|
|||
Initiates a request to open a connection. |
|||
|
|||
|
|||
|
|||
# `uv_req_t` |
|||
|
|||
Abstract class represents an asynchronous request. This is a subclass of `uv_handle_t`. |
|||
|
|||
|
|||
# `uv_connect_req_t` |
|||
|
|||
Subclass of `uv_req_t`. Represents a request for a TCP connection. Operates |
|||
on `uv_tcp_t` handles. Like other types of requests the `close_cb` indicates |
|||
completion of the request. |
|||
|
|||
int uv_connect_req_init(uv_connect_req_t* req, |
|||
uv_tcp_t* socket, |
|||
struct sockaddr* addr, |
|||
uv_close_cb close_cb, |
|||
void* data); |
|||
|
|||
Initializes the connection request. Returning 0 indicates success, -1 if |
|||
there was an error. The following values can be retrieved from |
|||
`uv_last_error` in the case of an error: |
|||
|
|||
* ??? |
|||
|
|||
|
|||
# `uv_shutdown_req_t` |
|||
|
|||
Subclass of `uv_req_t`. Represents an ongoing shutdown request. Once the |
|||
write queue of the parent `uv_stream_t` is drained, the outbound data |
|||
channel is shutdown. Once a shutdown request is initiated on a stream, the |
|||
stream will allow no more writes. |
|||
|
|||
int uv_shutdown_req_init(uv_shutdown_req_t*, |
|||
uv_stream_t* parent, |
|||
uv_close_cb close_cb, |
|||
void* data); |
|||
|
|||
Initializes the shutdown request. |
|||
|
|||
|
|||
# `uv_write_req_t` |
|||
|
|||
int uv_write_req_init(uv_write_req_t*, |
|||
uv_stream_t*, |
|||
uv_buf_t bufs[], |
|||
int butcnf, |
|||
uv_close_cb close_cb, |
|||
void* data); |
|||
|
|||
Initiates a write request on a stream. |
@ -0,0 +1,53 @@ |
|||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|||
* |
|||
* 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. |
|||
*/ |
|||
|
|||
#include "../uv.h" |
|||
#include "task.h" |
|||
#include <string.h> |
|||
|
|||
#define PATHMAX 1024 |
|||
extern char executable_path[]; |
|||
|
|||
TEST_IMPL(get_currentexe) { |
|||
char buffer[PATHMAX]; |
|||
size_t size; |
|||
char* match; |
|||
int r; |
|||
|
|||
size = sizeof(buffer) / sizeof(buffer[0]); |
|||
r = uv_get_exepath(buffer, &size); |
|||
ASSERT(!r); |
|||
|
|||
match = strstr(buffer, executable_path); |
|||
/* Verify that the path returned from uv_get_exepath is a subdirectory of executable_path */ |
|||
ASSERT(match && !strcmp(match, executable_path)); |
|||
ASSERT(size == strlen(buffer)); |
|||
|
|||
/* Negative tests */ |
|||
size = sizeof(buffer) / sizeof(buffer[0]); |
|||
r = uv_get_exepath(NULL, &size); |
|||
ASSERT(r == -1); |
|||
|
|||
r = uv_get_exepath(buffer, NULL); |
|||
ASSERT(r == -1); |
|||
|
|||
return 0; |
|||
} |
@ -0,0 +1,176 @@ |
|||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|||
* |
|||
* 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. |
|||
*/ |
|||
|
|||
#include "../uv.h" |
|||
#include "task.h" |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
|
|||
static uv_timer_t timer; |
|||
static uv_tcp_t tcp; |
|||
static uv_req_t connect_req, write_req, shutdown_req; |
|||
static uv_buf_t qbuf; |
|||
static int got_q; |
|||
static int got_eof; |
|||
static int called_connect_cb; |
|||
static int called_shutdown_cb; |
|||
static int called_tcp_close_cb; |
|||
static int called_timer_close_cb; |
|||
static int called_timer_cb; |
|||
|
|||
|
|||
static uv_buf_t alloc_cb(uv_tcp_t* tcp, size_t size) { |
|||
uv_buf_t buf; |
|||
buf.base = (char*)malloc(size); |
|||
buf.len = size; |
|||
return buf; |
|||
} |
|||
|
|||
|
|||
static void read_cb(uv_tcp_t* t, int nread, uv_buf_t buf) { |
|||
ASSERT(t == &tcp); |
|||
|
|||
if (!got_q) { |
|||
ASSERT(nread == 1); |
|||
ASSERT(!got_eof); |
|||
ASSERT(buf.base[0] == 'Q'); |
|||
free(buf.base); |
|||
got_q = 1; |
|||
puts("got Q"); |
|||
} else { |
|||
ASSERT(uv_last_error().code == UV_EOF); |
|||
if (buf.base) { |
|||
free(buf.base); |
|||
} |
|||
got_eof = 1; |
|||
puts("got EOF"); |
|||
} |
|||
} |
|||
|
|||
|
|||
static void shutdown_cb(uv_req_t *req, int status) { |
|||
ASSERT(req == &shutdown_req); |
|||
|
|||
ASSERT(called_connect_cb == 1); |
|||
ASSERT(!got_eof); |
|||
ASSERT(called_tcp_close_cb == 0); |
|||
ASSERT(called_timer_close_cb == 0); |
|||
ASSERT(called_timer_cb == 0); |
|||
|
|||
called_shutdown_cb++; |
|||
} |
|||
|
|||
|
|||
static void connect_cb(uv_req_t *req, int status) { |
|||
ASSERT(status == 0); |
|||
ASSERT(req == &connect_req); |
|||
|
|||
/* Start reading from our connection so we can receive the EOF. */ |
|||
uv_read_start(&tcp, alloc_cb, read_cb); |
|||
|
|||
/*
|
|||
* Write the letter 'Q' to gracefully kill the echo-server. This will not |
|||
* effect our connection. |
|||
*/ |
|||
uv_req_init(&write_req, (uv_handle_t*)&tcp, NULL); |
|||
uv_write(&write_req, &qbuf, 1); |
|||
|
|||
/* Shutdown our end of the connection. */ |
|||
uv_req_init(&shutdown_req, (uv_handle_t*)&tcp, shutdown_cb); |
|||
uv_shutdown(&shutdown_req); |
|||
|
|||
called_connect_cb++; |
|||
ASSERT(called_shutdown_cb == 0); |
|||
} |
|||
|
|||
|
|||
void tcp_close_cb(uv_handle_t* handle, int status) { |
|||
ASSERT(handle == (uv_handle_t*) &tcp); |
|||
|
|||
ASSERT(called_connect_cb == 1); |
|||
ASSERT(got_q); |
|||
ASSERT(got_eof); |
|||
ASSERT(called_timer_cb == 1); |
|||
|
|||
called_tcp_close_cb++; |
|||
} |
|||
|
|||
|
|||
void timer_close_cb(uv_handle_t* handle, int status) { |
|||
ASSERT(handle == (uv_handle_t*) &timer); |
|||
called_timer_close_cb++; |
|||
} |
|||
|
|||
|
|||
void timer_cb(uv_handle_t* handle, int status) { |
|||
ASSERT(handle == (uv_handle_t*) &timer); |
|||
uv_close(handle); |
|||
|
|||
/*
|
|||
* The most important assert of the test: we have not received |
|||
* tcp_close_cb yet. |
|||
*/ |
|||
ASSERT(called_tcp_close_cb == 0); |
|||
uv_close((uv_handle_t*) &tcp); |
|||
|
|||
called_timer_cb++; |
|||
} |
|||
|
|||
|
|||
/*
|
|||
* This test has a client which connects to the echo_server and immediately |
|||
* issues a shutdown. The echo-server, in response, will also shutdown their |
|||
* connection. We check, with a timer, that libuv is not automatically |
|||
* calling uv_close when the client receives the EOF from echo-server. |
|||
*/ |
|||
TEST_IMPL(shutdown_eof) { |
|||
struct sockaddr_in server_addr; |
|||
int r; |
|||
|
|||
uv_init(); |
|||
|
|||
qbuf.base = "Q"; |
|||
qbuf.len = 1; |
|||
|
|||
uv_timer_init(&timer, timer_close_cb, NULL); |
|||
uv_timer_start(&timer, timer_cb, 100, 0); |
|||
|
|||
server_addr = uv_ip4_addr("127.0.0.1", TEST_PORT); |
|||
r = uv_tcp_init(&tcp, tcp_close_cb, NULL); |
|||
ASSERT(!r); |
|||
|
|||
uv_req_init(&connect_req, (uv_handle_t*) &tcp, connect_cb); |
|||
r = uv_connect(&connect_req, server_addr); |
|||
ASSERT(!r); |
|||
|
|||
uv_run(); |
|||
|
|||
ASSERT(called_connect_cb == 1); |
|||
ASSERT(called_shutdown_cb == 1); |
|||
ASSERT(got_eof); |
|||
ASSERT(got_q); |
|||
ASSERT(called_tcp_close_cb == 1); |
|||
ASSERT(called_timer_close_cb == 1); |
|||
ASSERT(called_timer_cb == 1); |
|||
|
|||
return 0; |
|||
} |
|||
|
File diff suppressed because it is too large
File diff suppressed because it is too large
Loading…
Reference in new issue