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