mirror of https://github.com/lukechilds/node.git
Ryan Dahl
15 years ago
19 changed files with 8 additions and 5703 deletions
@ -1,75 +0,0 @@ |
|||
# Define EVDIR=/foo/bar if your libev header and library files are in
|
|||
# /foo/bar/include and /foo/bar/lib directories.
|
|||
EVDIR=$(HOME)/local/libev |
|||
|
|||
# Define GNUTLSDIR=/foo/bar if your gnutls header and library files are in
|
|||
# /foo/bar/include and /foo/bar/lib directories.
|
|||
#
|
|||
# Comment out the following line to disable TLS
|
|||
GNUTLSDIR=/usr |
|||
|
|||
# CFLAGS and LDFLAGS are for the users to override from the command line.
|
|||
CFLAGS = -g -I. -Wall -Werror -Wextra #-DNDEBUG=1 |
|||
LDFLAGS = |
|||
|
|||
CC = gcc |
|||
AR = ar |
|||
RANLIB = ranlib |
|||
|
|||
ifdef EVDIR |
|||
CFLAGS += -I$(EVDIR)/include |
|||
LDFLAGS += -L$(EVDIR)/lib |
|||
endif |
|||
|
|||
LDFLAGS += -lev |
|||
|
|||
ifdef GNUTLSDIR |
|||
CFLAGS += -I$(GNUTLSDIR)/include -DEVCOM_HAVE_GNUTLS=1 |
|||
LDFLAGS += -L$(GNUTLSDIR)/lib |
|||
LDFLAGS += -lgnutls |
|||
endif |
|||
|
|||
DEP = evcom.h |
|||
SRC = evcom.c |
|||
OBJ = ${SRC:.c=.o} |
|||
|
|||
NAME=libevcom |
|||
OUTPUT_A=$(NAME).a |
|||
|
|||
all: $(OUTPUT_A) test/test test/echo |
|||
|
|||
$(OUTPUT_A): $(OBJ) |
|||
$(AR) cru $(OUTPUT_A) $(OBJ) |
|||
$(RANLIB) $(OUTPUT_A) |
|||
|
|||
.c.o: |
|||
$(CC) -c ${CFLAGS} $< |
|||
|
|||
${OBJ}: ${DEP} |
|||
|
|||
FAIL=ruby -e 'puts "\033[1;31m FAIL\033[m"' |
|||
PASS=ruby -e 'puts "\033[1;32m PASS\033[m"' |
|||
|
|||
test: test/test test/echo test/timeout.rb |
|||
@echo test.c |
|||
@test/test > /dev/null && $(PASS) || $(FAIL) |
|||
@echo timeout.rb |
|||
@test/timeout.rb |
|||
|
|||
test/test: test/test.c $(OUTPUT_A) |
|||
$(CC) $(LDFLAGS) $(CFLAGS) -o $@ test/test.c $(OUTPUT_A) |
|||
|
|||
test/echo: test/echo.c $(OUTPUT_A) |
|||
$(CC) $(LDFLAGS) $(CFLAGS) -o $@ test/echo.c $(OUTPUT_A) |
|||
|
|||
send_states.png: send_states.dot |
|||
dot -Tpng -o send_states.png send_states.dot |
|||
|
|||
recv_states.png: recv_states.dot |
|||
dot -Tpng -o recv_states.png recv_states.dot |
|||
|
|||
clean: |
|||
rm -rf test/test test/echo |
|||
rm -f $(OUTPUT_A) *.o |
|||
|
|||
.PHONY: all clean test |
File diff suppressed because it is too large
@ -1,260 +0,0 @@ |
|||
/* Copyright (c) 2008,2009 Ryan Dahl
|
|||
* |
|||
* evcom_queue comes from Nginx, ngx_queue.h |
|||
* Copyright (C) 2002-2009 Igor Sysoev |
|||
* |
|||
* Redistribution and use in source and binary forms, with or without |
|||
* modification, are permitted provided that the following conditions |
|||
* are met: |
|||
* 1. Redistributions of source code must retain the above copyright |
|||
* notice, this list of conditions and the following disclaimer. |
|||
* 2. Redistributions in binary form must reproduce the above copyright |
|||
* notice, this list of conditions and the following disclaimer in the |
|||
* documentation and/or other materials provided with the distribution. |
|||
* |
|||
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
|||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|||
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
|||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
|||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
|||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|||
* SUCH DAMAGE. |
|||
*/ |
|||
#include <netdb.h> |
|||
#include <ev.h> |
|||
#include <stddef.h> /* offsetof() */ |
|||
|
|||
#ifndef evcom_h |
|||
#define evcom_h |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" { |
|||
#endif |
|||
|
|||
#ifndef EVCOM_HAVE_GNUTLS |
|||
# define EVCOM_HAVE_GNUTLS 0 |
|||
#endif |
|||
#if EVCOM_HAVE_GNUTLS |
|||
# include <gnutls/gnutls.h> |
|||
#endif |
|||
|
|||
/* The maximum evcom_stream will try to read in one callback */ |
|||
#ifndef EVCOM_CHUNKSIZE |
|||
# define EVCOM_CHUNKSIZE (8*1024) |
|||
#endif |
|||
|
|||
/* flags for stream and server */ |
|||
#define EVCOM_ATTACHED 0x0001 |
|||
#define EVCOM_LISTENING 0x0002 |
|||
#define EVCOM_CONNECTED 0x0004 |
|||
#define EVCOM_SECURE 0x0008 |
|||
#define EVCOM_DUPLEX 0x0010 |
|||
#define EVCOM_GOT_CLOSE 0x0020 |
|||
#define EVCOM_PAUSED 0x0040 |
|||
#define EVCOM_READABLE 0x0080 |
|||
#define EVCOM_WRITABLE 0x0100 |
|||
#define EVCOM_TOO_MANY_CONN 0x0200 |
|||
|
|||
enum evcom_stream_state { EVCOM_INITIALIZED |
|||
, EVCOM_CONNECTING |
|||
, EVCOM_CONNECTED_RW /* read write */ |
|||
, EVCOM_CONNECTED_RO /* read only */ |
|||
, EVCOM_CONNECTED_WO /* write only */ |
|||
, EVCOM_CLOSING |
|||
, EVCOM_CLOSED |
|||
}; |
|||
|
|||
typedef struct evcom_queue { |
|||
struct evcom_queue *prev; |
|||
struct evcom_queue *next; |
|||
} evcom_queue; |
|||
|
|||
typedef struct evcom_buf { |
|||
/* public */ |
|||
char *base; |
|||
size_t len; |
|||
void (*release) (struct evcom_buf *); /* called when oi is done with the object */ |
|||
void *data; |
|||
|
|||
/* private */ |
|||
size_t written; |
|||
evcom_queue queue; |
|||
} evcom_buf; |
|||
|
|||
#if EV_MULTIPLICITY |
|||
# define EVCOM_LOOP struct ev_loop *loop; |
|||
#else |
|||
# define EVCOM_LOOP |
|||
#endif |
|||
|
|||
#define EVCOM_DESCRIPTOR(type) \ |
|||
/* private */ unsigned int flags; \ |
|||
/* private */ int (*action) (struct evcom_descriptor*); \ |
|||
/* read-only */ int errorno; \ |
|||
/* read-only */ int fd; \ |
|||
/* read-only */ EVCOM_LOOP \ |
|||
/* public */ void *data; \ |
|||
/* public */ void (*on_close) (struct type*); |
|||
|
|||
/* abstract base class */ |
|||
typedef struct evcom_descriptor { |
|||
EVCOM_DESCRIPTOR(evcom_descriptor) |
|||
} evcom_descriptor; |
|||
|
|||
typedef struct evcom_reader { |
|||
EVCOM_DESCRIPTOR(evcom_reader) |
|||
ev_io read_watcher; /* private */ |
|||
void (*on_read) (struct evcom_reader*, const void* buf, size_t len); /* public */ |
|||
} evcom_reader; |
|||
|
|||
typedef struct evcom_writer { |
|||
EVCOM_DESCRIPTOR(evcom_writer) |
|||
ev_io write_watcher; /* private */ |
|||
evcom_queue out; /* private */ |
|||
} evcom_writer; |
|||
|
|||
typedef struct evcom_stream { |
|||
/* PRIVATE */ |
|||
EVCOM_LOOP |
|||
int errorno; |
|||
unsigned int flags; |
|||
evcom_queue out; |
|||
ev_io read_watcher; |
|||
ev_io write_watcher; |
|||
int (*send_action) (struct evcom_stream*); |
|||
int (*recv_action) (struct evcom_stream*); |
|||
ev_timer timeout_watcher; |
|||
#if EVCOM_HAVE_GNUTLS |
|||
gnutls_session_t session; |
|||
#endif |
|||
|
|||
/* READ-ONLY */ |
|||
int recvfd; |
|||
int sendfd; |
|||
struct evcom_server *server; |
|||
#if EVCOM_HAVE_GNUTLS |
|||
int gnutls_errorno; |
|||
#endif |
|||
|
|||
/* PUBLIC */ |
|||
void (*on_connect) (struct evcom_stream *); |
|||
void (*on_timeout) (struct evcom_stream *); |
|||
void (*on_read) (struct evcom_stream *, const void* buf, size_t len); |
|||
void (*on_drain) (struct evcom_stream *); |
|||
void (*on_close) (struct evcom_stream *); |
|||
void *data; |
|||
} evcom_stream; |
|||
|
|||
typedef struct evcom_server { |
|||
EVCOM_DESCRIPTOR(evcom_server) |
|||
|
|||
/* PRIVATE */ |
|||
ev_io watcher; |
|||
|
|||
/* PUBLIC */ |
|||
struct evcom_stream* |
|||
(*on_connection)(struct evcom_server *, struct sockaddr *remote_addr); |
|||
} evcom_server; |
|||
|
|||
/* Highly recommended to ignore SIGPIPE! */ |
|||
void evcom_ignore_sigpipe (void); |
|||
|
|||
void evcom_reader_init (evcom_reader*); |
|||
void evcom_reader_set (evcom_reader*, int fd); |
|||
void evcom_reader_attach (EV_P_ evcom_reader*); |
|||
void evcom_reader_detach (evcom_reader*); |
|||
void evcom_reader_close (evcom_reader*); |
|||
|
|||
void evcom_writer_init (evcom_writer*); |
|||
void evcom_writer_set (evcom_writer*, int fd); |
|||
void evcom_writer_attach (EV_P_ evcom_writer*); |
|||
void evcom_writer_detach (evcom_writer*); |
|||
void evcom_writer_write (evcom_writer*, const char *str, size_t len); |
|||
void evcom_writer_close (evcom_writer*); |
|||
|
|||
void evcom_server_init (evcom_server *); |
|||
int evcom_server_listen (evcom_server *, struct sockaddr *address, int backlog); |
|||
void evcom_server_attach (EV_P_ evcom_server *); |
|||
void evcom_server_detach (evcom_server *); |
|||
void evcom_server_close (evcom_server *); |
|||
|
|||
void evcom_stream_init (evcom_stream *); |
|||
|
|||
int evcom_stream_pair (evcom_stream *a, evcom_stream *b); |
|||
int evcom_stream_connect (evcom_stream *, struct sockaddr *address); |
|||
void evcom_stream_assign_fds (evcom_stream *, int recvfd, int sendfd); |
|||
|
|||
void evcom_stream_attach (EV_P_ evcom_stream *); |
|||
void evcom_stream_detach (evcom_stream *); |
|||
void evcom_stream_read_resume (evcom_stream *); |
|||
void evcom_stream_read_pause (evcom_stream *); |
|||
void evcom_stream_reset_timeout (evcom_stream *, float timeout); |
|||
void evcom_stream_set_no_delay (evcom_stream *, int no_delay); |
|||
ssize_t evcom_stream_write (evcom_stream *, const char *str, size_t len); |
|||
/* Once the write buffer is drained, evcom_stream_close will shutdown the
|
|||
* writing end of the stream and will close the read end once the server |
|||
* replies with an EOF. |
|||
*/ |
|||
void evcom_stream_close (evcom_stream *); |
|||
|
|||
/* Will not wait for the write queue to complete. Closes both directions */ |
|||
void evcom_stream_force_close (evcom_stream *); |
|||
|
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
/* Tells the stream to use transport layer security (SSL). evcom_stream does
|
|||
* not want to make any decisions about security requirements, so the |
|||
* majoirty of GnuTLS configuration is left to the user. Only the transport |
|||
* layer of GnuTLS is controlled by evcom_stream. That is, do not use |
|||
* gnutls_transport_* functions. Do use the rest of GnuTLS's API. |
|||
*/ |
|||
void evcom_stream_set_secure_session (evcom_stream *, gnutls_session_t); |
|||
#endif |
|||
|
|||
enum evcom_stream_state evcom_stream_state (evcom_stream *stream); |
|||
|
|||
evcom_buf* evcom_buf_new (const char* base, size_t len); |
|||
evcom_buf* evcom_buf_new2 (size_t len); |
|||
|
|||
EV_INLINE void |
|||
evcom_queue_init (evcom_queue *q) |
|||
{ |
|||
q->prev = q; |
|||
q->next = q; |
|||
} |
|||
|
|||
EV_INLINE void |
|||
evcom_queue_insert_head (evcom_queue *h, evcom_queue *x) |
|||
{ |
|||
(x)->next = (h)->next; |
|||
(x)->next->prev = x; |
|||
(x)->prev = h; |
|||
(h)->next = x; |
|||
} |
|||
|
|||
EV_INLINE void |
|||
evcom_queue_remove (evcom_queue *x) |
|||
{ |
|||
(x)->next->prev = (x)->prev; |
|||
(x)->prev->next = (x)->next; |
|||
#ifndef NDEBUG |
|||
(x)->prev = NULL; |
|||
(x)->next = NULL; |
|||
#endif |
|||
} |
|||
|
|||
#define evcom_queue_empty(h) (h == (h)->prev) |
|||
#define evcom_queue_head(h) (h)->next |
|||
#define evcom_queue_last(h) (h)->prev |
|||
#define evcom_queue_data(q, type, link) \ |
|||
(type *) ((unsigned char *) q - offsetof(type, link)) |
|||
|
|||
|
|||
#ifdef __cplusplus |
|||
} |
|||
#endif |
|||
#endif /* evcom_h */ |
@ -1,37 +0,0 @@ |
|||
strict digraph recv_states { |
|||
start [peripheries=2]; |
|||
end [peripheries=2]; |
|||
handshake; |
|||
recv_data; |
|||
wait_for_resume; |
|||
wait_for_close; |
|||
close_one; |
|||
close_both; |
|||
|
|||
node [label="", shape="box", height=0.1, width=0.1]; |
|||
close; |
|||
|
|||
|
|||
|
|||
start -> handshake [label="tls"]; |
|||
start -> recv_data; |
|||
|
|||
handshake -> close [label="error"]; |
|||
handshake -> recv_data; |
|||
|
|||
recv_data -> handshake [label="rehandshake"]; |
|||
recv_data -> wait_for_resume [label="pause"]; |
|||
recv_data -> wait_for_close [label="eof"]; |
|||
recv_data -> close [label="error"]; |
|||
|
|||
wait_for_resume -> recv_data; |
|||
|
|||
wait_for_close -> close; |
|||
|
|||
close -> close_one [label="duplex"]; |
|||
close -> close_both; |
|||
|
|||
close_one -> end; |
|||
close_both -> end; |
|||
|
|||
} |
@ -1,65 +0,0 @@ |
|||
strict digraph send_states { |
|||
start [peripheries=2]; |
|||
end [peripheries=2]; |
|||
connection_established; |
|||
handshake; |
|||
send_data; |
|||
shutdown; |
|||
gnutls_bye; |
|||
close_one; |
|||
close_both; |
|||
|
|||
wait_for_connect; |
|||
wait_for_buf; |
|||
wait_for_eof; |
|||
|
|||
node [label="", shape="box", height=0.1, width=0.1]; |
|||
close; |
|||
drain; |
|||
hangup; |
|||
hangup_unsecure; |
|||
|
|||
|
|||
|
|||
start -> wait_for_connect [label="duplex"]; |
|||
start -> connection_established; |
|||
|
|||
wait_for_connect -> connection_established; |
|||
wait_for_connect -> close [label="error"]; |
|||
|
|||
connection_established -> handshake [label="tls"]; |
|||
connection_established -> send_data; |
|||
|
|||
handshake -> close [label="error"]; |
|||
handshake -> send_data; |
|||
|
|||
send_data -> close [label="error"]; |
|||
send_data -> drain [label="drain"]; |
|||
|
|||
drain -> wait_for_buf; |
|||
drain -> hangup [label="got_close"]; |
|||
|
|||
wait_for_buf -> send_data; |
|||
wait_for_buf -> drain [label="empty_buf"]; |
|||
|
|||
hangup -> gnutls_bye [label="tls"]; |
|||
hangup -> hangup_unsecure; |
|||
|
|||
gnutls_bye -> wait_for_eof; |
|||
gnutls_bye -> close [label="error"]; |
|||
|
|||
hangup_unsecure -> shutdown [label="duplex"]; |
|||
hangup_unsecure -> close_one; |
|||
|
|||
shutdown -> wait_for_eof; |
|||
shutdown -> close [label="error"]; |
|||
|
|||
wait_for_eof -> close_one; |
|||
close_one -> wait_for_eof [label="readable"]; |
|||
|
|||
close -> close_both; |
|||
close -> close_one [label="duplex"]; |
|||
|
|||
close_both -> end; |
|||
close_one -> end; |
|||
} |
@ -1,100 +0,0 @@ |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <unistd.h> |
|||
#include <assert.h> |
|||
|
|||
#include <sys/types.h> |
|||
#include <sys/socket.h> |
|||
#include <sys/un.h> |
|||
#include <netinet/in.h> |
|||
|
|||
|
|||
#include <ev.h> |
|||
#include <evcom.h> |
|||
#if EVCOM_HAVE_GNUTLS |
|||
# include <gnutls/gnutls.h> |
|||
#endif |
|||
|
|||
#define HOST "127.0.0.1" |
|||
#define SOCKFILE "/tmp/oi.sock" |
|||
#define PORT 5000 |
|||
|
|||
static int nconnections; |
|||
|
|||
static void |
|||
on_peer_close (evcom_stream *stream) |
|||
{ |
|||
assert(stream->errorno == 0); |
|||
//printf("server connection closed\n");
|
|||
free(stream); |
|||
} |
|||
|
|||
static void |
|||
on_peer_timeout (evcom_stream *stream) |
|||
{ |
|||
assert(stream); |
|||
fprintf(stderr, "peer connection timeout\n"); |
|||
assert(0); |
|||
} |
|||
|
|||
|
|||
|
|||
// timeout must match the timeout in timeout.rb
|
|||
#define TIMEOUT 5.0 |
|||
|
|||
static void |
|||
on_peer_read (evcom_stream *stream, const void *base, size_t len) |
|||
{ |
|||
if(len == 0) return; |
|||
|
|||
evcom_stream_write(stream, base, len); |
|||
} |
|||
|
|||
static evcom_stream* |
|||
on_server_connection (evcom_server *server, struct sockaddr *addr) |
|||
{ |
|||
assert(server); |
|||
assert(addr); |
|||
|
|||
evcom_stream *stream = malloc(sizeof(evcom_stream)); |
|||
evcom_stream_init(stream); |
|||
stream->on_read = on_peer_read; |
|||
stream->on_close = on_peer_close; |
|||
stream->on_timeout = on_peer_timeout; |
|||
evcom_stream_reset_timeout(stream, TIMEOUT); |
|||
|
|||
nconnections++; |
|||
|
|||
|
|||
//printf("on server connection\n");
|
|||
|
|||
return stream; |
|||
} |
|||
|
|||
int |
|||
main (void) |
|||
{ |
|||
int r; |
|||
evcom_server server; |
|||
|
|||
//printf("sizeof(evcom_server): %d\n", sizeof(evcom_server));
|
|||
//printf("sizeof(evcom_stream): %d\n", sizeof(evcom_stream));
|
|||
|
|||
evcom_server_init(&server); |
|||
server.on_connection = on_server_connection; |
|||
|
|||
struct sockaddr_in address; |
|||
memset(&address, 0, sizeof(struct sockaddr_in)); |
|||
address.sin_family = AF_INET; |
|||
address.sin_port = htons(PORT); |
|||
address.sin_addr.s_addr = INADDR_ANY; |
|||
|
|||
r = evcom_server_listen(&server, (struct sockaddr*)&address, 10); |
|||
assert(r == 0); |
|||
evcom_server_attach(EV_DEFAULT_ &server); |
|||
|
|||
ev_loop(EV_DEFAULT_ 0); |
|||
|
|||
return 0; |
|||
} |
@ -1,892 +0,0 @@ |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <unistd.h> |
|||
#include <assert.h> |
|||
|
|||
#include <sys/types.h> |
|||
#include <sys/socket.h> |
|||
#include <sys/un.h> |
|||
#include <netinet/in.h> |
|||
|
|||
#include <ev.h> |
|||
#include <evcom.h> |
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
# include <gnutls/gnutls.h> |
|||
#endif |
|||
|
|||
#undef MAX |
|||
#define MAX(a,b) ((a) > (b) ? (a) : (b)) |
|||
|
|||
#undef MIN |
|||
#define MIN(a,b) ((a) < (b) ? (a) : (b)) |
|||
|
|||
#define MARK_PROGRESS(c,cur,max) \ |
|||
if (cur % (MAX(max,50)/50) == 0) write(STDERR_FILENO, c, 1) |
|||
|
|||
#define SOCKFILE "/tmp/oi.sock" |
|||
#define PORT 5000 |
|||
|
|||
static evcom_server server; |
|||
static int nconnections; |
|||
static int use_tls; |
|||
static int got_server_close; |
|||
|
|||
static void |
|||
common_on_server_close (evcom_server *s) |
|||
{ |
|||
printf("server on_close\n"); |
|||
assert(s == &server); |
|||
assert(s->errorno == 0); |
|||
got_server_close = 1; |
|||
evcom_server_detach(s); |
|||
} |
|||
|
|||
static void |
|||
common_on_peer_close (evcom_stream *stream) |
|||
{ |
|||
assert(EVCOM_CLOSED == evcom_stream_state(stream)); |
|||
assert(stream->errorno == 0); |
|||
printf("server connection closed\n"); |
|||
#if EVCOM_HAVE_GNUTLS |
|||
assert(stream->gnutls_errorno == 0); |
|||
if (use_tls) gnutls_deinit(stream->session); |
|||
#endif |
|||
evcom_stream_detach(stream); |
|||
free(stream); |
|||
} |
|||
|
|||
static void |
|||
common_on_client_timeout (evcom_stream *stream) |
|||
{ |
|||
assert(stream); |
|||
printf("client connection timeout\n"); |
|||
} |
|||
|
|||
static void |
|||
common_on_peer_timeout (evcom_stream *stream) |
|||
{ |
|||
assert(stream); |
|||
fprintf(stderr, "peer connection timeout\n"); |
|||
assert(0); |
|||
} |
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
#define DH_BITS 768 |
|||
static gnutls_anon_server_credentials_t server_credentials; |
|||
const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 }; |
|||
static gnutls_dh_params_t dh_params; |
|||
|
|||
void anon_tls_server (evcom_stream *stream) |
|||
{ |
|||
gnutls_session_t session; |
|||
stream->data = session; |
|||
|
|||
int r = gnutls_init(&session, GNUTLS_SERVER); |
|||
assert(r == 0); |
|||
gnutls_set_default_priority(session); |
|||
gnutls_kx_set_priority (session, kx_prio); |
|||
gnutls_credentials_set(session, GNUTLS_CRD_ANON, server_credentials); |
|||
gnutls_dh_set_prime_bits(session, DH_BITS); |
|||
|
|||
evcom_stream_set_secure_session(stream, session); |
|||
} |
|||
|
|||
void anon_tls_client (evcom_stream *stream) |
|||
{ |
|||
gnutls_session_t client_session; |
|||
gnutls_anon_client_credentials_t client_credentials; |
|||
|
|||
gnutls_anon_allocate_client_credentials (&client_credentials); |
|||
gnutls_init(&client_session, GNUTLS_CLIENT); |
|||
gnutls_set_default_priority(client_session); |
|||
gnutls_kx_set_priority(client_session, kx_prio); |
|||
/* Need to enable anonymous KX specifically. */ |
|||
gnutls_credentials_set(client_session, GNUTLS_CRD_ANON, client_credentials); |
|||
|
|||
evcom_stream_set_secure_session(stream, client_session); |
|||
assert(stream->flags & EVCOM_SECURE); |
|||
} |
|||
|
|||
#endif // EVCOM_HAVE_GNUTLS
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
#define PING "PING" |
|||
#define PONG "PONG" |
|||
#define EXCHANGES 500 |
|||
#define PINGPONG_TIMEOUT 5.0 |
|||
|
|||
static int successful_ping_count; |
|||
|
|||
static void |
|||
pingpong_on_peer_read (evcom_stream *stream, const void *base, size_t len) |
|||
{ |
|||
if (len == 0) { |
|||
evcom_stream_close(stream); |
|||
return; |
|||
} |
|||
|
|||
char buf[2000]; |
|||
strncpy(buf, base, len); |
|||
buf[len] = 0; |
|||
printf("server got message: %s\n", buf); |
|||
|
|||
evcom_stream_write(stream, PONG, sizeof PONG); |
|||
} |
|||
|
|||
static void |
|||
pingpong_on_client_close (evcom_stream *stream) |
|||
{ |
|||
assert(EVCOM_CLOSED == evcom_stream_state(stream)); |
|||
assert(stream); |
|||
printf("client connection closed\n"); |
|||
evcom_server_close(&server); |
|||
evcom_stream_detach(stream); |
|||
} |
|||
|
|||
static evcom_stream* |
|||
pingpong_on_server_connection (evcom_server *_server, struct sockaddr *addr) |
|||
{ |
|||
assert(_server == &server); |
|||
assert(addr); |
|||
|
|||
evcom_stream *stream = malloc(sizeof(evcom_stream)); |
|||
evcom_stream_init(stream); |
|||
stream->on_read = pingpong_on_peer_read; |
|||
stream->on_close = common_on_peer_close; |
|||
stream->on_timeout = common_on_peer_timeout; |
|||
evcom_stream_reset_timeout(stream, PINGPONG_TIMEOUT); |
|||
|
|||
assert(EVCOM_INITIALIZED == evcom_stream_state(stream)); |
|||
|
|||
nconnections++; |
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
if (use_tls) anon_tls_server(stream); |
|||
#endif |
|||
|
|||
printf("on server connection\n"); |
|||
|
|||
return stream; |
|||
} |
|||
|
|||
static void |
|||
pingpong_on_client_connect (evcom_stream *stream) |
|||
{ |
|||
printf("client connected. sending ping\n"); |
|||
evcom_stream_write(stream, PING, sizeof PING); |
|||
assert(EVCOM_CONNECTED_RW == evcom_stream_state(stream)); |
|||
} |
|||
|
|||
static void |
|||
pingpong_on_client_read (evcom_stream *stream, const void *base, size_t len) |
|||
{ |
|||
if(len == 0) { |
|||
evcom_stream_close(stream); |
|||
return; |
|||
} |
|||
|
|||
assert(len = strlen(PONG)); |
|||
|
|||
char buf[len+1]; |
|||
strncpy(buf, base, len); |
|||
buf[len] = 0; |
|||
printf("client got message: %s\n", buf); |
|||
|
|||
assert(strcmp(buf, PONG) == 0); |
|||
|
|||
if (++successful_ping_count > EXCHANGES) { |
|||
evcom_stream_close(stream); |
|||
return; |
|||
} |
|||
|
|||
MARK_PROGRESS(".", successful_ping_count, EXCHANGES); |
|||
|
|||
evcom_stream_write(stream, PING, sizeof PING); |
|||
} |
|||
|
|||
int |
|||
pingpong (struct sockaddr *address) |
|||
{ |
|||
int r; |
|||
evcom_stream client; |
|||
|
|||
successful_ping_count = 0; |
|||
nconnections = 0; |
|||
got_server_close = 0; |
|||
|
|||
evcom_server_init(&server); |
|||
server.on_connection = pingpong_on_server_connection; |
|||
server.on_close = common_on_server_close; |
|||
|
|||
r = evcom_server_listen(&server, address, 10); |
|||
assert(r == 0); |
|||
evcom_server_attach(EV_DEFAULT_ &server); |
|||
|
|||
evcom_stream_init(&client); |
|||
client.on_read = pingpong_on_client_read; |
|||
client.on_connect = pingpong_on_client_connect; |
|||
client.on_close = pingpong_on_client_close; |
|||
client.on_timeout = common_on_client_timeout; |
|||
evcom_stream_reset_timeout(&client, PINGPONG_TIMEOUT); |
|||
|
|||
assert(EVCOM_INITIALIZED == evcom_stream_state(&client)); |
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
if (use_tls) anon_tls_client(&client); |
|||
#endif |
|||
|
|||
r = evcom_stream_connect(&client, address); |
|||
assert(r == 0 && "problem connecting"); |
|||
evcom_stream_attach(EV_DEFAULT_ &client); |
|||
|
|||
ev_loop(EV_DEFAULT_ 0); |
|||
|
|||
printf("successful_ping_count = %d\n", successful_ping_count); |
|||
assert(successful_ping_count == EXCHANGES + 1); |
|||
assert(nconnections == 1); |
|||
assert(got_server_close); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
|
|||
#define NCONN 50 |
|||
#define CONNINT_TIMEOUT 10.0 |
|||
|
|||
static void |
|||
send_bye_and_close(evcom_stream *stream, const void *base, size_t len) |
|||
{ |
|||
assert(base); |
|||
assert(len == 0); |
|||
evcom_stream_write(stream, "BYE", 3); |
|||
printf("server wrote bye\n"); |
|||
evcom_stream_close(stream); |
|||
} |
|||
|
|||
static evcom_stream* |
|||
connint_on_connection(evcom_server *_server, struct sockaddr *addr) |
|||
{ |
|||
assert(_server == &server); |
|||
assert(addr); |
|||
|
|||
evcom_stream *stream = malloc(sizeof(evcom_stream)); |
|||
evcom_stream_init(stream); |
|||
stream->on_read = send_bye_and_close; |
|||
stream->on_close = common_on_peer_close; |
|||
stream->on_timeout = common_on_peer_timeout; |
|||
evcom_stream_reset_timeout(stream, CONNINT_TIMEOUT); |
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
if (use_tls) anon_tls_server(stream); |
|||
#endif |
|||
|
|||
printf("on server connection\n"); |
|||
|
|||
return stream; |
|||
} |
|||
|
|||
static void |
|||
connint_on_client_connect (evcom_stream *stream) |
|||
{ |
|||
printf("on client connection\n"); |
|||
evcom_stream_close(stream); |
|||
} |
|||
|
|||
static void |
|||
connint_on_client_close (evcom_stream *stream) |
|||
{ |
|||
evcom_stream_close(stream); // already closed, but it shouldn't crash if we try to do it again
|
|||
|
|||
printf("client connection closed\n"); |
|||
|
|||
MARK_PROGRESS(".", nconnections, NCONN); |
|||
|
|||
if(++nconnections == NCONN) { |
|||
evcom_server_close(&server); |
|||
printf("closing server\n"); |
|||
} |
|||
|
|||
evcom_stream_detach(stream); |
|||
} |
|||
|
|||
static void |
|||
connint_on_client_read (evcom_stream *stream, const void *base, size_t len) |
|||
{ |
|||
if (len == 0) { |
|||
evcom_stream_close(stream); |
|||
return; |
|||
} |
|||
|
|||
char buf[200000]; |
|||
strncpy(buf, base, len); |
|||
buf[len] = 0; |
|||
|
|||
printf("client got message: %s\n", buf); |
|||
|
|||
assert(strcmp(buf, "BYE") == 0); |
|||
evcom_stream_close(stream); |
|||
} |
|||
|
|||
int |
|||
connint (struct sockaddr *address) |
|||
{ |
|||
int r; |
|||
|
|||
nconnections = 0; |
|||
got_server_close = 0; |
|||
|
|||
evcom_server_init(&server); |
|||
server.on_connection = connint_on_connection; |
|||
server.on_close = common_on_server_close; |
|||
|
|||
evcom_server_listen(&server, address, 1000); |
|||
evcom_server_attach(EV_DEFAULT_ &server); |
|||
|
|||
evcom_stream clients[NCONN]; |
|||
int i; |
|||
for (i = 0; i < NCONN; i++) { |
|||
evcom_stream *client = &clients[i]; |
|||
evcom_stream_init(client); |
|||
client->on_read = connint_on_client_read; |
|||
client->on_connect = connint_on_client_connect; |
|||
client->on_close = connint_on_client_close; |
|||
client->on_timeout = common_on_client_timeout; |
|||
evcom_stream_reset_timeout(client, CONNINT_TIMEOUT); |
|||
#if EVCOM_HAVE_GNUTLS |
|||
if (use_tls) anon_tls_client(client); |
|||
#endif |
|||
r = evcom_stream_connect(client, address); |
|||
assert(r == 0 && "problem connecting"); |
|||
evcom_stream_attach(EV_DEFAULT_ client); |
|||
} |
|||
|
|||
ev_loop(EV_DEFAULT_ 0); |
|||
|
|||
assert(nconnections == NCONN); |
|||
assert(got_server_close); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
static evcom_reader reader; |
|||
static evcom_writer writer; |
|||
static int reader_got_close = 0; |
|||
static int reader_got_eof = 0; |
|||
static int reader_got_hello = 0; |
|||
static int reader_cnt = 0; |
|||
static int writer_got_close = 0; |
|||
#define PIPE_MSG "hello world" |
|||
#define PIPE_CNT 5000 |
|||
|
|||
static void |
|||
reader_read (evcom_reader *r, const void *str, size_t len) |
|||
{ |
|||
assert(r == &reader); |
|||
|
|||
if (len == 0) { |
|||
reader_got_eof = 1; |
|||
return; |
|||
} |
|||
|
|||
assert(len == strlen(PIPE_MSG)); |
|||
|
|||
if (strncmp(str, PIPE_MSG, strlen(PIPE_MSG)) == 0) { |
|||
reader_got_hello = 1; |
|||
} |
|||
|
|||
if (++reader_cnt < PIPE_CNT) { |
|||
MARK_PROGRESS(".", reader_cnt, PIPE_CNT); |
|||
evcom_writer_write(&writer, PIPE_MSG, strlen(PIPE_MSG)); |
|||
} else { |
|||
evcom_writer_close(&writer); |
|||
} |
|||
} |
|||
|
|||
static void |
|||
reader_close (evcom_reader *r) |
|||
{ |
|||
assert(r == &reader); |
|||
reader_got_close = 1; |
|||
evcom_reader_detach(r); |
|||
} |
|||
|
|||
static void |
|||
writer_close (evcom_writer *w) |
|||
{ |
|||
assert(w == &writer); |
|||
writer_got_close = 1; |
|||
evcom_writer_detach(w); |
|||
} |
|||
|
|||
int |
|||
pipe_stream (void) |
|||
{ |
|||
reader_cnt = 0; |
|||
reader_got_close = 0; |
|||
reader_got_hello = 0; |
|||
reader_got_eof = 0; |
|||
writer_got_close = 0; |
|||
|
|||
int pipefd[2]; |
|||
int r = pipe(pipefd); |
|||
if (r < 0) { |
|||
perror("pipe()"); |
|||
return -1; |
|||
} |
|||
|
|||
evcom_reader_init(&reader); |
|||
reader.on_read = reader_read; |
|||
reader.on_close = reader_close; |
|||
evcom_reader_set(&reader, pipefd[0]); |
|||
evcom_reader_attach(EV_DEFAULT_ &reader); |
|||
|
|||
evcom_writer_init(&writer); |
|||
writer.on_close = writer_close; |
|||
evcom_writer_set(&writer, pipefd[1]); |
|||
evcom_writer_attach(EV_DEFAULT_ &writer); |
|||
|
|||
evcom_writer_write(&writer, PIPE_MSG, strlen(PIPE_MSG)); |
|||
|
|||
ev_loop(EV_DEFAULT_ 0); |
|||
|
|||
assert(reader_got_close); |
|||
assert(reader_got_hello); |
|||
assert(reader_got_eof); |
|||
assert(writer_got_close); |
|||
assert(reader_cnt == PIPE_CNT); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
#define PAIR_PINGPONG_TIMEOUT 5000.0 |
|||
#define PAIR_PINGPONG_EXCHANGES 50 |
|||
static int a_got_close; |
|||
static int a_got_connect; |
|||
static int b_got_close; |
|||
static int b_got_connect; |
|||
static int pair_pingpong_cnt; |
|||
static evcom_stream a, b; |
|||
|
|||
void a_connect (evcom_stream *stream) |
|||
{ |
|||
assert(stream == &a); |
|||
a_got_connect = 1; |
|||
} |
|||
|
|||
void a_close (evcom_stream *stream) |
|||
{ |
|||
evcom_stream_detach(stream); |
|||
assert(stream == &a); |
|||
a_got_close = 1; |
|||
|
|||
assert(stream->errorno == 0); |
|||
#if EVCOM_HAVE_GNUTLS |
|||
if (stream->gnutls_errorno) { |
|||
fprintf(stderr, "\nGNUTLS ERROR: %s\n", gnutls_strerror(stream->gnutls_errorno)); |
|||
} |
|||
assert(stream->gnutls_errorno == 0); |
|||
if (use_tls) gnutls_deinit(stream->session); |
|||
#endif |
|||
} |
|||
|
|||
void a_read (evcom_stream *stream, const void *buf, size_t len) |
|||
{ |
|||
assert(stream == &a); |
|||
if (len == 0) return; |
|||
|
|||
assert(len == strlen(PONG)); |
|||
assert(strncmp(buf, PONG, strlen(PONG)) == 0); |
|||
|
|||
if (++pair_pingpong_cnt < PAIR_PINGPONG_EXCHANGES) { |
|||
evcom_stream_write(&a, PING, strlen(PING)); |
|||
} else if (pair_pingpong_cnt == PAIR_PINGPONG_EXCHANGES) { |
|||
evcom_stream_close(stream); |
|||
} |
|||
|
|||
MARK_PROGRESS(".", pair_pingpong_cnt, PAIR_PINGPONG_EXCHANGES); |
|||
} |
|||
|
|||
void b_connect (evcom_stream *stream) |
|||
{ |
|||
assert(stream == &b); |
|||
b_got_connect = 1; |
|||
} |
|||
|
|||
void b_close (evcom_stream *stream) |
|||
{ |
|||
evcom_stream_detach(stream); |
|||
assert(stream == &b); |
|||
b_got_close = 1; |
|||
|
|||
assert(stream->errorno == 0); |
|||
#if EVCOM_HAVE_GNUTLS |
|||
if (stream->gnutls_errorno) { |
|||
fprintf(stderr, "\nGNUTLS ERROR: %s\n", gnutls_strerror(stream->gnutls_errorno)); |
|||
} |
|||
assert(stream->gnutls_errorno == 0); |
|||
if (use_tls) gnutls_deinit(stream->session); |
|||
#endif |
|||
} |
|||
|
|||
void b_read (evcom_stream *stream, const void *buf, size_t len) |
|||
{ |
|||
assert(stream == &b); |
|||
if (len == 0) { |
|||
evcom_stream_close(stream); |
|||
return; |
|||
} |
|||
|
|||
assert(len == strlen(PING)); |
|||
assert(strncmp(buf, PING, strlen(PING)) == 0); |
|||
|
|||
evcom_stream_write(&b, PONG, strlen(PONG)); |
|||
} |
|||
|
|||
int |
|||
pair_pingpong (int use_pipe) |
|||
{ |
|||
a_got_close = 0; |
|||
a_got_connect = 0; |
|||
b_got_close = 0; |
|||
b_got_connect = 0; |
|||
pair_pingpong_cnt = 0; |
|||
|
|||
evcom_stream_init(&a); |
|||
a.on_close = a_close; |
|||
a.on_connect = a_connect; |
|||
a.on_read = a_read; |
|||
evcom_stream_reset_timeout(&a, PAIR_PINGPONG_TIMEOUT); |
|||
#if EVCOM_HAVE_GNUTLS |
|||
if (use_tls) anon_tls_client(&a); |
|||
#endif |
|||
|
|||
evcom_stream_init(&b); |
|||
b.on_close = b_close; |
|||
b.on_connect = b_connect; |
|||
b.on_read = b_read; |
|||
evcom_stream_reset_timeout(&b, PAIR_PINGPONG_TIMEOUT); |
|||
#if EVCOM_HAVE_GNUTLS |
|||
if (use_tls) anon_tls_server(&b); |
|||
#endif |
|||
|
|||
if (use_pipe) { |
|||
int pipeA[2], pipeB[2]; |
|||
assert(0 == pipe(pipeA)); |
|||
assert(0 == pipe(pipeB)); |
|||
|
|||
evcom_stream_assign_fds(&a, pipeA[0], pipeB[1]); |
|||
evcom_stream_assign_fds(&b, pipeB[0], pipeA[1]); |
|||
|
|||
} else { |
|||
int r = evcom_stream_pair(&a, &b); |
|||
assert(r == 0); |
|||
} |
|||
|
|||
evcom_stream_attach(EV_DEFAULT_ &a); |
|||
evcom_stream_attach(EV_DEFAULT_ &b); |
|||
|
|||
evcom_stream_write(&a, PING, strlen(PING)); |
|||
|
|||
ev_loop(EV_DEFAULT_ 0); |
|||
|
|||
assert(a_got_close); |
|||
assert(a_got_connect); |
|||
assert(b_got_close); |
|||
assert(b_got_connect); |
|||
assert(pair_pingpong_cnt == PAIR_PINGPONG_EXCHANGES); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
static void |
|||
free_stream (evcom_stream *stream) |
|||
{ |
|||
assert(stream->errorno == 0); |
|||
free(stream); |
|||
} |
|||
|
|||
#define ZERO_TIMEOUT 50.0 |
|||
static size_t zero_to_write = 0; |
|||
static size_t zero_written = 0; |
|||
static size_t zero_read = 0; |
|||
static size_t zero_client_closed = 0; |
|||
|
|||
static void |
|||
error_out (evcom_stream *stream) |
|||
{ |
|||
assert(stream); |
|||
fprintf(stderr, "peer connection timeout\n"); |
|||
assert(0); |
|||
} |
|||
|
|||
static void |
|||
echo (evcom_stream *stream, const void *base, size_t len) |
|||
{ |
|||
if(len == 0) { |
|||
fprintf(stderr, "close"); |
|||
evcom_stream_close(stream); |
|||
} else { |
|||
evcom_stream_write(stream, base, len); |
|||
} |
|||
} |
|||
|
|||
static evcom_stream* |
|||
make_echo_connection (evcom_server *server, struct sockaddr *addr) |
|||
{ |
|||
assert(server); |
|||
assert(addr); |
|||
|
|||
evcom_stream *stream = malloc(sizeof(evcom_stream)); |
|||
evcom_stream_init(stream); |
|||
stream->on_read = echo; |
|||
stream->on_close = free_stream; |
|||
stream->on_timeout = error_out; |
|||
evcom_stream_reset_timeout(stream, ZERO_TIMEOUT); |
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
if (use_tls) anon_tls_server(stream); |
|||
#endif |
|||
|
|||
return stream; |
|||
} |
|||
|
|||
|
|||
static void |
|||
zero_start (evcom_stream *stream) |
|||
{ |
|||
evcom_stream_write(stream, "0", 1); |
|||
zero_written++; |
|||
} |
|||
|
|||
static void |
|||
zero_close (evcom_stream *stream) |
|||
{ |
|||
assert(stream); |
|||
zero_client_closed = 1; |
|||
} |
|||
|
|||
static void |
|||
zero_recv (evcom_stream *stream, const void *buf, size_t len) |
|||
{ |
|||
MARK_PROGRESS("-", zero_read, zero_to_write); |
|||
zero_read += len; |
|||
|
|||
size_t i; |
|||
|
|||
for (i = 0; i < len; i++) { |
|||
assert(((char*)buf)[i] == '0'); |
|||
} |
|||
|
|||
for (i = 0; i < MIN(zero_to_write - zero_written, 90000); i++) { |
|||
evcom_stream_write(stream, "0", 1); |
|||
zero_written++; |
|||
|
|||
MARK_PROGRESS(".", zero_written, zero_to_write); |
|||
|
|||
if (zero_written == zero_to_write) { |
|||
|
|||
fprintf(stderr, "CLOSE"); |
|||
evcom_stream_close(stream); |
|||
} |
|||
} |
|||
|
|||
if (len == 0) { |
|||
fprintf(stderr, "finish"); |
|||
evcom_server_close(&server); |
|||
} |
|||
} |
|||
|
|||
int |
|||
zero_stream (struct sockaddr *address, size_t to_write) |
|||
{ |
|||
int r; |
|||
|
|||
assert(to_write >= 1024); // should be kind of big at least.
|
|||
zero_to_write = to_write; |
|||
got_server_close = 0; |
|||
zero_written = 0; |
|||
zero_read = 0; |
|||
zero_client_closed = 0; |
|||
|
|||
evcom_server_init(&server); |
|||
server.on_connection = make_echo_connection; |
|||
server.on_close = common_on_server_close; |
|||
|
|||
evcom_server_listen(&server, address, 1000); |
|||
evcom_server_attach(EV_DEFAULT_ &server); |
|||
|
|||
evcom_stream client; |
|||
evcom_stream_init(&client); |
|||
client.on_read = zero_recv; |
|||
client.on_connect = zero_start; |
|||
client.on_close = zero_close; |
|||
client.on_timeout = error_out; |
|||
evcom_stream_reset_timeout(&client, ZERO_TIMEOUT); |
|||
#if EVCOM_HAVE_GNUTLS |
|||
if (use_tls) anon_tls_client(&client); |
|||
#endif |
|||
r = evcom_stream_connect(&client, address); |
|||
assert(r == 0 && "problem connecting"); |
|||
evcom_stream_attach(EV_DEFAULT_ &client); |
|||
|
|||
ev_loop(EV_DEFAULT_ 0); |
|||
|
|||
assert(got_server_close); |
|||
assert(zero_written == zero_to_write); |
|||
assert(zero_read == zero_to_write); |
|||
assert(zero_client_closed) ; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
struct sockaddr * |
|||
create_unix_address (void) |
|||
{ |
|||
struct stat tstat; |
|||
if (lstat(SOCKFILE, &tstat) == 0) { |
|||
assert(S_ISSOCK(tstat.st_mode)); |
|||
unlink(SOCKFILE); |
|||
} |
|||
|
|||
struct sockaddr_un *address = calloc(1, sizeof(struct sockaddr_un)); |
|||
address->sun_family = AF_UNIX; |
|||
strcpy(address->sun_path, SOCKFILE); |
|||
|
|||
return (struct sockaddr*)address; |
|||
} |
|||
|
|||
void |
|||
free_unix_address (struct sockaddr *address) |
|||
{ |
|||
struct stat tstat; |
|||
if (lstat(SOCKFILE, &tstat) == 0) { |
|||
assert(S_ISSOCK(tstat.st_mode)); |
|||
unlink(SOCKFILE); |
|||
} |
|||
free(address); |
|||
} |
|||
|
|||
|
|||
int |
|||
main (void) |
|||
{ |
|||
fprintf(stderr, "sizeof(evcom_server): %d\n", (int)sizeof(evcom_server)); |
|||
fprintf(stderr, "sizeof(evcom_stream): %d\n", (int)sizeof(evcom_stream)); |
|||
fprintf(stderr, "sizeof(evcom_reader): %d\n", (int)sizeof(evcom_reader)); |
|||
fprintf(stderr, "sizeof(evcom_writer): %d\n", (int)sizeof(evcom_writer)); |
|||
|
|||
evcom_ignore_sigpipe(); |
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
gnutls_global_init(); |
|||
|
|||
gnutls_dh_params_init (&dh_params); |
|||
|
|||
fsync((int)stderr); |
|||
gnutls_dh_params_generate2 (dh_params, DH_BITS); |
|||
|
|||
gnutls_anon_allocate_server_credentials (&server_credentials); |
|||
gnutls_anon_set_server_dh_params (server_credentials, dh_params); |
|||
#endif |
|||
|
|||
|
|||
struct sockaddr_in tcp_address; |
|||
memset(&tcp_address, 0, sizeof(struct sockaddr_in)); |
|||
tcp_address.sin_family = AF_INET; |
|||
tcp_address.sin_port = htons(PORT); |
|||
tcp_address.sin_addr.s_addr = INADDR_ANY; |
|||
|
|||
use_tls = 0; |
|||
|
|||
fprintf(stderr, "pair_pingpong use_pipe=1: "); |
|||
assert(pair_pingpong(1) == 0); |
|||
fprintf(stderr, "\n"); |
|||
|
|||
fprintf(stderr, "pair_pingpong use_pipe=0: "); |
|||
assert(pair_pingpong(0) == 0); |
|||
fprintf(stderr, "\n"); |
|||
|
|||
fprintf(stderr, "zero_stream tcp: "); |
|||
assert(zero_stream((struct sockaddr*)&tcp_address, 5*1024*1024) == 0); |
|||
fprintf(stderr, "\n"); |
|||
|
|||
fprintf(stderr, "pipe_stream: "); |
|||
assert(pipe_stream() == 0); |
|||
fprintf(stderr, "\n"); |
|||
|
|||
fprintf(stderr, "pingpong tcp: "); |
|||
assert(pingpong((struct sockaddr*)&tcp_address) == 0); |
|||
fprintf(stderr, "\n"); |
|||
|
|||
fprintf(stderr, "connint tcp: "); |
|||
assert(connint((struct sockaddr*)&tcp_address) == 0); |
|||
fprintf(stderr, "\n"); |
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
use_tls = 1; |
|||
|
|||
fprintf(stderr, "zero_stream ssl: "); |
|||
assert(zero_stream((struct sockaddr*)&tcp_address, 50*1024) == 0); |
|||
fprintf(stderr, "\n"); |
|||
|
|||
fprintf(stderr, "pair_pingpong ssl use_pipe=1: "); |
|||
assert(pair_pingpong(1) == 0); |
|||
fprintf(stderr, "\n"); |
|||
|
|||
fprintf(stderr, "pair_pingpong ssl use_pipe=0: "); |
|||
assert(pair_pingpong(0) == 0); |
|||
fprintf(stderr, "\n"); |
|||
|
|||
fprintf(stderr, "pingpong ssl: "); |
|||
assert(pingpong((struct sockaddr*)&tcp_address) == 0); |
|||
fprintf(stderr, "\n"); |
|||
|
|||
fprintf(stderr, "connint ssl: "); |
|||
assert(connint((struct sockaddr*)&tcp_address) == 0); |
|||
fprintf(stderr, "\n"); |
|||
|
|||
#endif |
|||
|
|||
struct sockaddr *unix_address; |
|||
|
|||
use_tls = 0; |
|||
|
|||
fprintf(stderr, "pingpong unix: "); |
|||
unix_address = create_unix_address(); |
|||
assert(pingpong(unix_address) == 0); |
|||
free_unix_address(unix_address); |
|||
fprintf(stderr, "\n"); |
|||
|
|||
fprintf(stderr, "connint unix: "); |
|||
unix_address = create_unix_address(); |
|||
assert(connint(unix_address) == 0); |
|||
free_unix_address(unix_address); |
|||
fprintf(stderr, "\n"); |
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
use_tls = 1; |
|||
|
|||
fprintf(stderr, "pingpong unix ssl: "); |
|||
unix_address = create_unix_address(); |
|||
assert(pingpong(unix_address) == 0); |
|||
free_unix_address(unix_address); |
|||
fprintf(stderr, "\n"); |
|||
|
|||
fprintf(stderr, "connint unix ssl: "); |
|||
unix_address = create_unix_address(); |
|||
assert(connint(unix_address) == 0); |
|||
free_unix_address(unix_address); |
|||
fprintf(stderr, "\n"); |
|||
#endif |
|||
|
|||
return 0; |
|||
} |
@ -1,98 +0,0 @@ |
|||
#!/usr/bin/env ruby |
|||
require 'socket' |
|||
|
|||
def test(description) |
|||
pid = fork do |
|||
exec(File.dirname(__FILE__) + "/echo") |
|||
end |
|||
|
|||
puts "#{description}: " |
|||
begin |
|||
sleep 0.5 # give time for the server to start |
|||
yield(pid) |
|||
rescue |
|||
puts "\033[1;31mFAIL\033[m" |
|||
raise $! |
|||
ensure |
|||
`kill -9 #{pid}` |
|||
end |
|||
puts "\033[1;32mPASS\033[m" |
|||
end |
|||
|
|||
test("make sure echo server works") do |
|||
socket = TCPSocket.open("localhost", 5000) |
|||
w = socket.write("hello"); |
|||
raise "error" unless w == 5 |
|||
|
|||
got = socket.recv(5); |
|||
raise "error" unless got == "hello" |
|||
|
|||
socket.close |
|||
end |
|||
|
|||
test("doing nothing should not timeout the server") do |pid| |
|||
10.times do |
|||
print "." |
|||
STDOUT.flush |
|||
if Process.waitpid(pid, Process::WNOHANG) |
|||
raise "server died when it shouldn't have" |
|||
end |
|||
sleep 1 |
|||
end |
|||
puts "" |
|||
end |
|||
|
|||
test("connecting and doing nothing to should timeout in 5 seconds") do |pid| |
|||
socket = TCPSocket.open("localhost", 5000) |
|||
i = 0 |
|||
10.times do |
|||
print "." |
|||
STDOUT.flush |
|||
break if Process.waitpid(pid, Process::WNOHANG) |
|||
sleep 1 |
|||
i+=1 |
|||
end |
|||
puts "" |
|||
raise "died too soon (after #{i} seconds)" if i < 5 |
|||
raise "died too late (after #{i} seconds)" if i > 6 |
|||
end |
|||
|
|||
|
|||
test("connecting and writing once to should timeout in 5 seconds") do |pid| |
|||
socket = TCPSocket.open("localhost", 5000) |
|||
w = socket.write("hello"); |
|||
raise "error" unless w == 5 |
|||
|
|||
i = 0 |
|||
10.times do |
|||
print "." |
|||
STDOUT.flush |
|||
break if Process.waitpid(pid, Process::WNOHANG) |
|||
sleep 1 |
|||
i+=1 |
|||
end |
|||
puts "" |
|||
raise "died too soon (after #{i} seconds)" if i < 5 |
|||
raise "died too late (after #{i} seconds)" if i > 6 |
|||
end |
|||
|
|||
test("connecting waiting 3, writing once to should timeout in 8 seconds") do |pid| |
|||
socket = TCPSocket.open("localhost", 5000) |
|||
|
|||
sleep 3 |
|||
|
|||
w = socket.write("hello"); |
|||
raise "error" unless w == 5 |
|||
|
|||
i = 0 |
|||
10.times do |
|||
print "." |
|||
STDOUT.flush |
|||
break if Process.waitpid(pid, Process::WNOHANG) |
|||
sleep 1 |
|||
i+=1 |
|||
end |
|||
puts "" |
|||
raise "died too soon (after #{i} seconds)" if i < 5 |
|||
raise "died too late (after #{i} seconds)" if i > 6 |
|||
end |
@ -1,641 +0,0 @@ |
|||
var sys = require('sys'); |
|||
var events = require('events'); |
|||
|
|||
// FIXME: The TCP binding isn't actually used here, but it needs to be
|
|||
// loaded before the http binding.
|
|||
process.binding('tcp'); |
|||
|
|||
var http = process.binding('http'); |
|||
|
|||
var CRLF = "\r\n"; |
|||
var STATUS_CODES = exports.STATUS_CODES = { |
|||
100 : 'Continue', |
|||
101 : 'Switching Protocols', |
|||
200 : 'OK', |
|||
201 : 'Created', |
|||
202 : 'Accepted', |
|||
203 : 'Non-Authoritative Information', |
|||
204 : 'No Content', |
|||
205 : 'Reset Content', |
|||
206 : 'Partial Content', |
|||
300 : 'Multiple Choices', |
|||
301 : 'Moved Permanently', |
|||
302 : 'Moved Temporarily', |
|||
303 : 'See Other', |
|||
304 : 'Not Modified', |
|||
305 : 'Use Proxy', |
|||
400 : 'Bad Request', |
|||
401 : 'Unauthorized', |
|||
402 : 'Payment Required', |
|||
403 : 'Forbidden', |
|||
404 : 'Not Found', |
|||
405 : 'Method Not Allowed', |
|||
406 : 'Not Acceptable', |
|||
407 : 'Proxy Authentication Required', |
|||
408 : 'Request Time-out', |
|||
409 : 'Conflict', |
|||
410 : 'Gone', |
|||
411 : 'Length Required', |
|||
412 : 'Precondition Failed', |
|||
413 : 'Request Entity Too Large', |
|||
414 : 'Request-URI Too Large', |
|||
415 : 'Unsupported Media Type', |
|||
500 : 'Internal Server Error', |
|||
501 : 'Not Implemented', |
|||
502 : 'Bad Gateway', |
|||
503 : 'Service Unavailable', |
|||
504 : 'Gateway Time-out', |
|||
505 : 'HTTP Version not supported' |
|||
}; |
|||
|
|||
var connection_expression = /Connection/i; |
|||
var transfer_encoding_expression = /Transfer-Encoding/i; |
|||
var close_expression = /close/i; |
|||
var chunk_expression = /chunk/i; |
|||
var content_length_expression = /Content-Length/i; |
|||
|
|||
|
|||
/* Abstract base class for ServerRequest and ClientResponse. */ |
|||
function IncomingMessage (connection) { |
|||
events.EventEmitter.call(this); |
|||
|
|||
this.connection = connection; |
|||
this.httpVersion = null; |
|||
this.headers = {}; |
|||
|
|||
// request (server) only
|
|||
this.url = ""; |
|||
|
|||
this.method = null; |
|||
|
|||
// response (client) only
|
|||
this.statusCode = null; |
|||
this.client = this.connection; |
|||
} |
|||
sys.inherits(IncomingMessage, events.EventEmitter); |
|||
exports.IncomingMessage = IncomingMessage; |
|||
|
|||
IncomingMessage.prototype._parseQueryString = function () { |
|||
throw new Error("_parseQueryString is deprecated. Use require(\"querystring\") to parse query strings.\n"); |
|||
}; |
|||
|
|||
IncomingMessage.prototype.setBodyEncoding = function (enc) { |
|||
// TODO: Find a cleaner way of doing this.
|
|||
this.connection.setEncoding(enc); |
|||
}; |
|||
|
|||
IncomingMessage.prototype.pause = function () { |
|||
this.connection.pause(); |
|||
}; |
|||
|
|||
IncomingMessage.prototype.resume = function () { |
|||
this.connection.resume(); |
|||
}; |
|||
|
|||
IncomingMessage.prototype._addHeaderLine = function (field, value) { |
|||
if (field in this.headers) { |
|||
// TODO Certain headers like 'Content-Type' should not be concatinated.
|
|||
// See https://www.google.com/reader/view/?tab=my#overview-page
|
|||
this.headers[field] += ", " + value; |
|||
} else { |
|||
this.headers[field] = value; |
|||
} |
|||
}; |
|||
|
|||
function OutgoingMessage (connection) { |
|||
events.EventEmitter.call(this, connection); |
|||
|
|||
this.connection = connection; |
|||
|
|||
this.output = []; |
|||
this.outputEncodings = []; |
|||
|
|||
this.closeOnFinish = false; |
|||
this.chunked_encoding = false; |
|||
this.should_keep_alive = true; |
|||
this.use_chunked_encoding_by_default = true; |
|||
|
|||
this.flushing = false; |
|||
this.headWritten = false; |
|||
|
|||
this.finished = false; |
|||
} |
|||
sys.inherits(OutgoingMessage, events.EventEmitter); |
|||
exports.OutgoingMessage = OutgoingMessage; |
|||
|
|||
OutgoingMessage.prototype._send = function (data, encoding) { |
|||
var length = this.output.length; |
|||
|
|||
if (length === 0) { |
|||
this.output.push(data); |
|||
encoding = encoding || "ascii"; |
|||
this.outputEncodings.push(encoding); |
|||
return; |
|||
} |
|||
|
|||
var lastEncoding = this.outputEncodings[length-1]; |
|||
var lastData = this.output[length-1]; |
|||
|
|||
if ((lastEncoding === encoding) || |
|||
(!encoding && data.constructor === lastData.constructor)) { |
|||
if (lastData.constructor === String) { |
|||
this.output[length-1] = lastData + data; |
|||
} else { |
|||
this.output[length-1] = lastData.concat(data); |
|||
} |
|||
return; |
|||
} |
|||
|
|||
this.output.push(data); |
|||
encoding = encoding || "ascii"; |
|||
this.outputEncodings.push(encoding); |
|||
}; |
|||
|
|||
OutgoingMessage.prototype.sendHeaderLines = function (first_line, headers) { |
|||
var sent_connection_header = false; |
|||
var sent_content_length_header = false; |
|||
var sent_transfer_encoding_header = false; |
|||
|
|||
// first_line in the case of request is: "GET /index.html HTTP/1.1\r\n"
|
|||
// in the case of response it is: "HTTP/1.1 200 OK\r\n"
|
|||
var message_header = first_line; |
|||
var field, value; |
|||
for (var i in headers) { |
|||
if (headers[i] instanceof Array) { |
|||
field = headers[i][0]; |
|||
value = headers[i][1]; |
|||
} else { |
|||
if (!headers.hasOwnProperty(i)) continue; |
|||
field = i; |
|||
value = headers[i]; |
|||
} |
|||
|
|||
message_header += field + ": " + value + CRLF; |
|||
|
|||
if (connection_expression.test(field)) { |
|||
sent_connection_header = true; |
|||
if (close_expression.test(value)) this.closeOnFinish = true; |
|||
|
|||
} else if (transfer_encoding_expression.test(field)) { |
|||
sent_transfer_encoding_header = true; |
|||
if (chunk_expression.test(value)) this.chunked_encoding = true; |
|||
|
|||
} else if (content_length_expression.test(field)) { |
|||
sent_content_length_header = true; |
|||
|
|||
} |
|||
} |
|||
|
|||
// keep-alive logic
|
|||
if (sent_connection_header == false) { |
|||
if (this.should_keep_alive && |
|||
(sent_content_length_header || this.use_chunked_encoding_by_default)) { |
|||
message_header += "Connection: keep-alive\r\n"; |
|||
} else { |
|||
this.closeOnFinish = true; |
|||
message_header += "Connection: close\r\n"; |
|||
} |
|||
} |
|||
|
|||
if (sent_content_length_header == false && sent_transfer_encoding_header == false) { |
|||
if (this.use_chunked_encoding_by_default) { |
|||
message_header += "Transfer-Encoding: chunked\r\n"; |
|||
this.chunked_encoding = true; |
|||
} |
|||
else { |
|||
this.closeOnFinish = true; |
|||
} |
|||
} |
|||
|
|||
message_header += CRLF; |
|||
|
|||
this._send(message_header); |
|||
// wait until the first body chunk, or close(), is sent to flush.
|
|||
}; |
|||
|
|||
|
|||
OutgoingMessage.prototype.sendBody = function () { |
|||
throw new Error("sendBody() has been renamed to write(). " + |
|||
"The 'body' event has been renamed to 'data' and " + |
|||
"the 'complete' event has been renamed to 'end'."); |
|||
}; |
|||
|
|||
|
|||
OutgoingMessage.prototype.write = function (chunk, encoding) { |
|||
if ( (this instanceof ServerResponse) && !this.headWritten) { |
|||
throw new Error("writeHead() must be called before write()") |
|||
} |
|||
|
|||
encoding = encoding || "ascii"; |
|||
if (this.chunked_encoding) { |
|||
this._send(process._byteLength(chunk, encoding).toString(16)); |
|||
this._send(CRLF); |
|||
this._send(chunk, encoding); |
|||
this._send(CRLF); |
|||
} else { |
|||
this._send(chunk, encoding); |
|||
} |
|||
|
|||
if (this.flushing) { |
|||
this.flush(); |
|||
} else { |
|||
this.flushing = true; |
|||
} |
|||
}; |
|||
|
|||
OutgoingMessage.prototype.flush = function () { |
|||
this.emit("flush"); |
|||
}; |
|||
|
|||
OutgoingMessage.prototype.finish = function () { |
|||
throw new Error("finish() has been renamed to close()."); |
|||
}; |
|||
|
|||
OutgoingMessage.prototype.close = function () { |
|||
if (this.chunked_encoding) this._send("0\r\n\r\n"); // last chunk
|
|||
this.finished = true; |
|||
this.flush(); |
|||
}; |
|||
|
|||
|
|||
function ServerResponse (req) { |
|||
OutgoingMessage.call(this, req.connection); |
|||
|
|||
if (req.httpVersionMajor < 1 || req.httpVersionMinor < 1) { |
|||
this.use_chunked_encoding_by_default = false; |
|||
this.should_keep_alive = false; |
|||
} |
|||
} |
|||
sys.inherits(ServerResponse, OutgoingMessage); |
|||
exports.ServerResponse = ServerResponse; |
|||
|
|||
|
|||
ServerResponse.prototype.writeHead = function (statusCode) { |
|||
var reasonPhrase, headers, headerIndex; |
|||
|
|||
if (typeof arguments[1] == 'string') { |
|||
reasonPhrase = arguments[1]; |
|||
headerIndex = 2; |
|||
} else { |
|||
reasonPhrase = STATUS_CODES[statusCode] || "unknown"; |
|||
headerIndex = 1; |
|||
} |
|||
|
|||
if (typeof arguments[headerIndex] == 'object') { |
|||
headers = arguments[headerIndex]; |
|||
} else { |
|||
headers = {}; |
|||
} |
|||
|
|||
var status_line = "HTTP/1.1 " + statusCode.toString() + " " |
|||
+ reasonPhrase + CRLF; |
|||
this.sendHeaderLines(status_line, headers); |
|||
this.headWritten = true; |
|||
}; |
|||
|
|||
// TODO eventually remove sendHeader(), writeHeader()
|
|||
ServerResponse.prototype.sendHeader = ServerResponse.prototype.writeHead; |
|||
ServerResponse.prototype.writeHeader = ServerResponse.prototype.writeHead; |
|||
|
|||
function ClientRequest (connection, method, url, headers) { |
|||
OutgoingMessage.call(this, connection); |
|||
|
|||
this.should_keep_alive = false; |
|||
if (method === "GET" || method === "HEAD") { |
|||
this.use_chunked_encoding_by_default = false; |
|||
} else { |
|||
this.use_chunked_encoding_by_default = true; |
|||
} |
|||
this.closeOnFinish = true; |
|||
|
|||
this.sendHeaderLines(method + " " + url + " HTTP/1.1\r\n", headers); |
|||
} |
|||
sys.inherits(ClientRequest, OutgoingMessage); |
|||
exports.ClientRequest = ClientRequest; |
|||
|
|||
ClientRequest.prototype.finish = function () { |
|||
throw new Error( "finish() has been renamed to close() and no longer takes " |
|||
+ "a response handler as an argument. Manually add a 'response' listener " |
|||
+ "to the request object." |
|||
); |
|||
}; |
|||
|
|||
ClientRequest.prototype.close = function () { |
|||
if (arguments.length > 0) { |
|||
throw new Error( "ClientRequest.prototype.close does not take any arguments. " |
|||
+ "Add a response listener manually to the request object." |
|||
); |
|||
} |
|||
OutgoingMessage.prototype.close.call(this); |
|||
}; |
|||
|
|||
|
|||
function createIncomingMessageStream (connection, incoming_listener) { |
|||
var incoming, field, value; |
|||
|
|||
connection.addListener("messageBegin", function () { |
|||
incoming = new IncomingMessage(connection); |
|||
field = null; |
|||
value = null; |
|||
}); |
|||
|
|||
// Only servers will get URL events.
|
|||
connection.addListener("url", function (data) { |
|||
incoming.url += data; |
|||
}); |
|||
|
|||
connection.addListener("headerField", function (data) { |
|||
if (value) { |
|||
incoming._addHeaderLine(field, value); |
|||
field = null; |
|||
value = null; |
|||
} |
|||
if (field) { |
|||
field += data; |
|||
} else { |
|||
field = data; |
|||
} |
|||
}); |
|||
|
|||
connection.addListener("headerValue", function (data) { |
|||
if (value) { |
|||
value += data; |
|||
} else { |
|||
value = data; |
|||
} |
|||
}); |
|||
|
|||
connection.addListener("headerComplete", function (info) { |
|||
if (field && value) { |
|||
incoming._addHeaderLine(field, value); |
|||
} |
|||
|
|||
incoming.httpVersion = info.httpVersion; |
|||
incoming.httpVersionMajor = info.versionMajor; |
|||
incoming.httpVersionMinor = info.versionMinor; |
|||
|
|||
if (info.method) { |
|||
// server only
|
|||
incoming.method = info.method; |
|||
} else { |
|||
// client only
|
|||
incoming.statusCode = info.statusCode; |
|||
} |
|||
|
|||
incoming_listener(incoming, info.should_keep_alive); |
|||
}); |
|||
|
|||
connection.addListener("body", function (chunk) { |
|||
incoming.emit('data', chunk); |
|||
}); |
|||
|
|||
connection.addListener("messageComplete", function () { |
|||
incoming.emit('end'); |
|||
}); |
|||
} |
|||
|
|||
/* Returns true if the message queue is finished and the connection |
|||
* should be closed. */ |
|||
function flushMessageQueue (connection, queue) { |
|||
while (queue[0]) { |
|||
var message = queue[0]; |
|||
|
|||
while (message.output.length > 0) { |
|||
if (connection.readyState !== "open" && connection.readyState !== "writeOnly") { |
|||
return true; |
|||
} |
|||
|
|||
var data = message.output.shift(); |
|||
var encoding = message.outputEncodings.shift(); |
|||
|
|||
connection.write(data, encoding); |
|||
} |
|||
|
|||
if (!message.finished) break; |
|||
|
|||
message.emit("sent"); |
|||
queue.shift(); |
|||
|
|||
if (message.closeOnFinish) return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
|
|||
exports.createServer = function (requestListener, options) { |
|||
var server = new http.Server(); |
|||
//server.setOptions(options);
|
|||
server.addListener("request", requestListener); |
|||
server.addListener("connection", connectionListener); |
|||
return server; |
|||
}; |
|||
|
|||
function connectionListener (connection) { |
|||
// An array of responses for each connection. In pipelined connections
|
|||
// we need to keep track of the order they were sent.
|
|||
var responses = []; |
|||
|
|||
connection.resetParser(); |
|||
|
|||
// is this really needed?
|
|||
connection.addListener("end", function () { |
|||
if (responses.length == 0) { |
|||
connection.close(); |
|||
} else { |
|||
responses[responses.length-1].closeOnFinish = true; |
|||
} |
|||
}); |
|||
|
|||
|
|||
createIncomingMessageStream(connection, function (incoming, should_keep_alive) { |
|||
var req = incoming; |
|||
|
|||
var res = new ServerResponse(req); |
|||
res.should_keep_alive = should_keep_alive; |
|||
res.addListener("flush", function () { |
|||
if (flushMessageQueue(connection, responses)) { |
|||
connection.close(); |
|||
} |
|||
}); |
|||
responses.push(res); |
|||
|
|||
connection.server.emit("request", req, res); |
|||
}); |
|||
} |
|||
|
|||
|
|||
exports.createClient = function (port, host) { |
|||
var client = new http.Client(); |
|||
var secure_credentials={ secure : false }; |
|||
|
|||
var requests = []; |
|||
var currentRequest; |
|||
|
|||
client.tcpSetSecure = client.setSecure; |
|||
client.setSecure = function(format_type, ca_certs, crl_list, private_key, certificate) { |
|||
secure_credentials.secure = true; |
|||
secure_credentials.format_type = format_type; |
|||
secure_credentials.ca_certs = ca_certs; |
|||
secure_credentials.crl_list = crl_list; |
|||
secure_credentials.private_key = private_key; |
|||
secure_credentials.certificate = certificate; |
|||
} |
|||
|
|||
client._reconnect = function () { |
|||
if (client.readyState != "opening") { |
|||
//sys.debug("HTTP CLIENT: reconnecting readyState = " + client.readyState);
|
|||
client.connect(port, host); |
|||
if (secure_credentials.secure) { |
|||
client.tcpSetSecure(secure_credentials.format_type, |
|||
secure_credentials.ca_certs, |
|||
secure_credentials.crl_list, |
|||
secure_credentials.private_key, |
|||
secure_credentials.certificate); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
client._pushRequest = function (req) { |
|||
req.addListener("flush", function () { |
|||
if (client.readyState == "closed") { |
|||
//sys.debug("HTTP CLIENT request flush. reconnect. readyState = " + client.readyState);
|
|||
client._reconnect(); |
|||
return; |
|||
} |
|||
//sys.debug("client flush readyState = " + client.readyState);
|
|||
if (req == currentRequest) flushMessageQueue(client, [req]); |
|||
}); |
|||
requests.push(req); |
|||
}; |
|||
|
|||
client.addListener("connect", function () { |
|||
client.resetParser(); |
|||
currentRequest = requests.shift(); |
|||
currentRequest.flush(); |
|||
}); |
|||
|
|||
client.addListener("end", function () { |
|||
//sys.debug("client got end closing. readyState = " + client.readyState);
|
|||
client.close(); |
|||
}); |
|||
|
|||
client.addListener("close", function (had_error) { |
|||
if (had_error) { |
|||
client.emit("error"); |
|||
return; |
|||
} |
|||
|
|||
//sys.debug("HTTP CLIENT onClose. readyState = " + client.readyState);
|
|||
|
|||
// If there are more requests to handle, reconnect.
|
|||
if (requests.length > 0) { |
|||
client._reconnect(); |
|||
} |
|||
}); |
|||
|
|||
createIncomingMessageStream(client, function (res) { |
|||
//sys.debug("incoming response!");
|
|||
|
|||
res.addListener('end', function ( ) { |
|||
//sys.debug("request complete disconnecting. readyState = " + client.readyState);
|
|||
client.close(); |
|||
}); |
|||
|
|||
currentRequest.emit("response", res); |
|||
}); |
|||
|
|||
return client; |
|||
}; |
|||
|
|||
http.Client.prototype.get = function () { |
|||
throw new Error("client.get(...) is now client.request('GET', ...)"); |
|||
}; |
|||
|
|||
http.Client.prototype.head = function () { |
|||
throw new Error("client.head(...) is now client.request('HEAD', ...)"); |
|||
}; |
|||
|
|||
http.Client.prototype.post = function () { |
|||
throw new Error("client.post(...) is now client.request('POST', ...)"); |
|||
}; |
|||
|
|||
http.Client.prototype.del = function () { |
|||
throw new Error("client.del(...) is now client.request('DELETE', ...)"); |
|||
}; |
|||
|
|||
http.Client.prototype.put = function () { |
|||
throw new Error("client.put(...) is now client.request('PUT', ...)"); |
|||
}; |
|||
|
|||
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(this, method || "GET", url, headers); |
|||
this._pushRequest(req); |
|||
return req; |
|||
}; |
|||
|
|||
|
|||
exports.cat = function (url, encoding_, headers_) { |
|||
var encoding = 'utf8', |
|||
headers = {}, |
|||
callback = null; |
|||
|
|||
// parse the arguments for the various options... very ugly
|
|||
if (typeof(arguments[1]) == 'string') { |
|||
encoding = arguments[1]; |
|||
if (typeof(arguments[2]) == 'object') { |
|||
headers = arguments[2]; |
|||
if (typeof(arguments[3]) == 'function') callback = arguments[3]; |
|||
} else { |
|||
if (typeof(arguments[2]) == 'function') callback = arguments[2]; |
|||
} |
|||
} else { |
|||
// didn't specify encoding
|
|||
if (typeof(arguments[1]) == 'object') { |
|||
headers = arguments[1]; |
|||
callback = arguments[2]; |
|||
} else { |
|||
callback = arguments[1]; |
|||
} |
|||
} |
|||
|
|||
var url = require("url").parse(url); |
|||
|
|||
var hasHost = false; |
|||
for (var i in headers) { |
|||
if (i.toLowerCase() === "host") { |
|||
hasHost = true; |
|||
break; |
|||
} |
|||
} |
|||
if (!hasHost) headers["Host"] = url.hostname; |
|||
|
|||
var content = ""; |
|||
|
|||
var client = exports.createClient(url.port || 80, url.hostname); |
|||
var req = client.request((url.pathname || "/")+(url.search || "")+(url.hash || ""), headers); |
|||
|
|||
req.addListener('response', function (res) { |
|||
if (res.statusCode < 200 || res.statusCode >= 300) { |
|||
if (callback) callback(res.statusCode); |
|||
client.close(); |
|||
return; |
|||
} |
|||
res.setBodyEncoding(encoding); |
|||
res.addListener('data', function (chunk) { content += chunk; }); |
|||
res.addListener('end', function () { |
|||
if (callback) callback(null, content); |
|||
}); |
|||
}); |
|||
|
|||
client.addListener("error", function (err) { |
|||
// todo an error should actually be passed here...
|
|||
if (callback) callback(new Error('Connection error')); |
|||
}); |
|||
|
|||
req.close(); |
|||
}; |
@ -1,26 +0,0 @@ |
|||
var tcp = process.binding('tcp'); |
|||
|
|||
var TLS_STATUS_CODES = { |
|||
1 : 'JS_GNUTLS_CERT_VALIDATED', |
|||
0 : 'JS_GNUTLS_CERT_UNDEFINED', |
|||
} |
|||
TLS_STATUS_CODES[-100] = 'JS_GNUTLS_CERT_SIGNER_NOT_FOUND'; |
|||
TLS_STATUS_CODES[-101] = 'JS_GNUTLS_CERT_SIGNER_NOT_CA'; |
|||
TLS_STATUS_CODES[-102] = 'JS_GNUTLS_CERT_INVALID'; |
|||
TLS_STATUS_CODES[-103] = 'JS_GNUTLS_CERT_NOT_ACTIVATED'; |
|||
TLS_STATUS_CODES[-104] = 'JS_GNUTLS_CERT_EXPIRED'; |
|||
TLS_STATUS_CODES[-105] = 'JS_GNUTLS_CERT_REVOKED'; |
|||
TLS_STATUS_CODES[-106] = 'JS_GNUTLS_CERT_DOES_NOT_MATCH_HOSTNAME'; |
|||
|
|||
exports.createServer = function (on_connection, options) { |
|||
var server = new tcp.Server(); |
|||
server.addListener("connection", on_connection); |
|||
//server.setOptions(options);
|
|||
return server; |
|||
}; |
|||
|
|||
exports.createConnection = function (port, host) { |
|||
var connection = new tcp.Connection(); |
|||
connection.connect(port, host); |
|||
return connection; |
|||
}; |
@ -1,392 +0,0 @@ |
|||
#include <node_http.h> |
|||
|
|||
#include <assert.h> |
|||
#include <stdio.h> |
|||
#include <strings.h> |
|||
|
|||
using namespace v8; |
|||
using namespace node; |
|||
|
|||
Persistent<FunctionTemplate> HTTPConnection::client_constructor_template; |
|||
Persistent<FunctionTemplate> HTTPConnection::server_constructor_template; |
|||
|
|||
static Persistent<String> method_symbol; |
|||
static Persistent<String> status_code_symbol; |
|||
static Persistent<String> http_version_symbol; |
|||
static Persistent<String> version_major_symbol; |
|||
static Persistent<String> version_minor_symbol; |
|||
static Persistent<String> should_keep_alive_symbol; |
|||
|
|||
static Persistent<String> message_begin_symbol; |
|||
static Persistent<String> message_complete_symbol; |
|||
static Persistent<String> url_symbol; |
|||
static Persistent<String> query_string_symbol; |
|||
static Persistent<String> path_symbol; |
|||
static Persistent<String> fragment_symbol; |
|||
static Persistent<String> header_field_symbol; |
|||
static Persistent<String> header_value_symbol; |
|||
static Persistent<String> header_complete_symbol; |
|||
static Persistent<String> body_symbol; |
|||
static Persistent<String> end_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; |
|||
|
|||
static struct http_parser_settings settings; |
|||
|
|||
void |
|||
HTTPConnection::Initialize (Handle<Object> target) |
|||
{ |
|||
HandleScope scope; |
|||
|
|||
Local<FunctionTemplate> t = FunctionTemplate::New(NewClient); |
|||
client_constructor_template = Persistent<FunctionTemplate>::New(t); |
|||
client_constructor_template->Inherit(Connection::constructor_template); |
|||
client_constructor_template->InstanceTemplate()->SetInternalFieldCount(1); |
|||
client_constructor_template->SetClassName(String::NewSymbol("Client")); |
|||
NODE_SET_PROTOTYPE_METHOD(client_constructor_template, "resetParser", ResetParser); |
|||
target->Set(String::NewSymbol("Client"), client_constructor_template->GetFunction()); |
|||
|
|||
t = FunctionTemplate::New(NewServer); |
|||
server_constructor_template = Persistent<FunctionTemplate>::New(t); |
|||
server_constructor_template->Inherit(Connection::constructor_template); |
|||
server_constructor_template->InstanceTemplate()->SetInternalFieldCount(1); |
|||
NODE_SET_PROTOTYPE_METHOD(server_constructor_template, "resetParser", ResetParser); |
|||
server_constructor_template->SetClassName(String::NewSymbol("ServerSideConnection")); |
|||
|
|||
end_symbol = NODE_PSYMBOL("end"); |
|||
|
|||
settings.on_message_begin = on_message_begin; |
|||
settings.on_path = on_path; |
|||
settings.on_query_string = on_query_string; |
|||
settings.on_url = on_url; |
|||
settings.on_fragment = on_fragment; |
|||
settings.on_header_field = on_header_field; |
|||
settings.on_header_value = on_header_value; |
|||
settings.on_headers_complete = on_headers_complete; |
|||
settings.on_body = on_body; |
|||
settings.on_message_complete = on_message_complete; |
|||
} |
|||
|
|||
Handle<Value> |
|||
HTTPConnection::NewClient (const Arguments& args) |
|||
{ |
|||
HandleScope scope; |
|||
|
|||
HTTPConnection *connection = new HTTPConnection(HTTP_RESPONSE); |
|||
connection->Wrap(args.This()); |
|||
|
|||
return args.This(); |
|||
} |
|||
|
|||
Handle<Value> |
|||
HTTPConnection::NewServer (const Arguments& args) |
|||
{ |
|||
HandleScope scope; |
|||
|
|||
HTTPConnection *connection = new HTTPConnection(HTTP_REQUEST); |
|||
connection->Wrap(args.This()); |
|||
|
|||
return args.This(); |
|||
} |
|||
|
|||
|
|||
Handle<Value> HTTPConnection::ResetParser(const Arguments& args) { |
|||
HandleScope scope; |
|||
HTTPConnection *connection = ObjectWrap::Unwrap<HTTPConnection>(args.Holder()); |
|||
connection->ResetParser(); |
|||
return Undefined(); |
|||
} |
|||
|
|||
|
|||
void |
|||
HTTPConnection::OnReceive (const void *buf, size_t len) |
|||
{ |
|||
HandleScope scope; |
|||
|
|||
assert(refs_); |
|||
size_t nparsed; |
|||
|
|||
nparsed = http_parser_execute(&parser_, settings, static_cast<const char*>(buf), len); |
|||
|
|||
if (nparsed != len) { |
|||
ForceClose(); |
|||
} |
|||
} |
|||
|
|||
void |
|||
HTTPConnection::OnEOF () |
|||
{ |
|||
HandleScope scope; |
|||
assert(refs_); |
|||
size_t nparsed; |
|||
nparsed = http_parser_execute(&parser_, settings, NULL, 0); |
|||
Emit(end_symbol, 0, NULL); |
|||
} |
|||
|
|||
int |
|||
HTTPConnection::on_message_begin (http_parser *parser) |
|||
{ |
|||
if (message_begin_symbol.IsEmpty()) { |
|||
method_symbol = NODE_PSYMBOL("method"); |
|||
status_code_symbol = NODE_PSYMBOL("statusCode"); |
|||
http_version_symbol = NODE_PSYMBOL("httpVersion"); |
|||
version_major_symbol = NODE_PSYMBOL("versionMajor"); |
|||
version_minor_symbol = NODE_PSYMBOL("versionMinor"); |
|||
should_keep_alive_symbol = NODE_PSYMBOL("should_keep_alive"); |
|||
|
|||
message_begin_symbol = NODE_PSYMBOL("messageBegin"); |
|||
message_complete_symbol = NODE_PSYMBOL("messageComplete"); |
|||
url_symbol = NODE_PSYMBOL("url"); |
|||
query_string_symbol = NODE_PSYMBOL("queryString"); |
|||
path_symbol = NODE_PSYMBOL("path"); |
|||
fragment_symbol = NODE_PSYMBOL("fragment"); |
|||
header_field_symbol = NODE_PSYMBOL("headerField"); |
|||
header_value_symbol = NODE_PSYMBOL("headerValue"); |
|||
header_complete_symbol = NODE_PSYMBOL("headerComplete"); |
|||
body_symbol = NODE_PSYMBOL("body"); |
|||
|
|||
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); |
|||
assert(connection->refs_); |
|||
connection->Emit(message_begin_symbol, 0, NULL); |
|||
return 0; |
|||
} |
|||
|
|||
int |
|||
HTTPConnection::on_message_complete (http_parser *parser) |
|||
{ |
|||
HTTPConnection *connection = static_cast<HTTPConnection*> (parser->data); |
|||
assert(connection->refs_); |
|||
connection->Emit(message_complete_symbol, 0, NULL); |
|||
return 0; |
|||
} |
|||
|
|||
int |
|||
HTTPConnection::on_url (http_parser *parser, const char *buf, size_t len) |
|||
{ |
|||
HTTPConnection *connection = static_cast<HTTPConnection*>(parser->data); |
|||
assert(connection->refs_); |
|||
Local<Value> argv[1] = { String::New(buf, len) }; |
|||
connection->Emit(url_symbol, 1, argv); |
|||
return 0; |
|||
} |
|||
|
|||
int |
|||
HTTPConnection::on_query_string (http_parser *parser, const char *buf, size_t len) |
|||
{ |
|||
HTTPConnection *connection = static_cast<HTTPConnection*>(parser->data); |
|||
assert(connection->refs_); |
|||
Local<Value> argv[1] = { String::New(buf, len) }; |
|||
connection->Emit(query_string_symbol, 1, argv); |
|||
return 0; |
|||
} |
|||
|
|||
int |
|||
HTTPConnection::on_path (http_parser *parser, const char *buf, size_t len) |
|||
{ |
|||
HTTPConnection *connection = static_cast<HTTPConnection*>(parser->data); |
|||
assert(connection->refs_); |
|||
Local<Value> argv[1] = { String::New(buf, len) }; |
|||
connection->Emit(path_symbol, 1, argv); |
|||
return 0; |
|||
} |
|||
|
|||
int |
|||
HTTPConnection::on_fragment (http_parser *parser, const char *buf, size_t len) |
|||
{ |
|||
HTTPConnection *connection = static_cast<HTTPConnection*>(parser->data); |
|||
assert(connection->refs_); |
|||
Local<Value> argv[1] = { String::New(buf, len) }; |
|||
connection->Emit(fragment_symbol, 1, argv); |
|||
return 0; |
|||
} |
|||
|
|||
const static char normalizer[] = |
|||
"\0------------------------------" |
|||
"-----------------0123456789-----" |
|||
"--abcdefghijklmnopqrstuvwxyz----" |
|||
"--abcdefghijklmnopqrstuvwxyz----" |
|||
"--------------------------------" |
|||
"--------------------------------" |
|||
"--------------------------------" |
|||
"--------------------------------"; |
|||
|
|||
int |
|||
HTTPConnection::on_header_field (http_parser *parser, const char *buf, size_t len) |
|||
{ |
|||
HTTPConnection *connection = static_cast<HTTPConnection*>(parser->data); |
|||
assert(connection->refs_); |
|||
|
|||
// NORMALIZE
|
|||
size_t i; |
|||
char *nonconstbuf = (char*)buf; // FIXME
|
|||
for (i = 0; i < len; i++) { nonconstbuf[i] = normalizer[buf[i]]; } |
|||
|
|||
Local<Value> argv[1] = { String::New(buf, len) }; |
|||
connection->Emit(header_field_symbol, 1, argv); |
|||
return 0; |
|||
} |
|||
|
|||
int |
|||
HTTPConnection::on_header_value (http_parser *parser, const char *buf, size_t len) |
|||
{ |
|||
HTTPConnection *connection = static_cast<HTTPConnection*>(parser->data); |
|||
assert(connection->refs_); |
|||
|
|||
Local<Value> argv[1] = { String::New(buf, len) }; |
|||
connection->Emit(header_value_symbol, 1, argv); |
|||
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) |
|||
{ |
|||
HTTPConnection *connection = static_cast<HTTPConnection*> (parser->data); |
|||
assert(connection->refs_); |
|||
|
|||
Local<Object> message_info = Object::New(); |
|||
|
|||
// METHOD
|
|||
if (connection->type_ == HTTP_REQUEST) { |
|||
message_info->Set(method_symbol, method_to_str(connection->parser_.method)); |
|||
} |
|||
|
|||
// STATUS
|
|||
if (connection->type_ == HTTP_RESPONSE) { |
|||
message_info->Set(status_code_symbol, |
|||
Integer::New(connection->parser_.status_code)); |
|||
} |
|||
|
|||
// VERSION
|
|||
char version[10]; |
|||
snprintf( version |
|||
, 10 |
|||
, "%d.%d" |
|||
, connection->parser_.http_major |
|||
, connection->parser_.http_minor |
|||
); |
|||
message_info->Set(http_version_symbol, String::New(version)); |
|||
message_info->Set(version_major_symbol, |
|||
Integer::New(connection->parser_.http_major)); |
|||
message_info->Set(version_minor_symbol, |
|||
Integer::New(connection->parser_.http_minor)); |
|||
|
|||
message_info->Set(should_keep_alive_symbol, |
|||
http_should_keep_alive(&connection->parser_) ? True() : False()); |
|||
|
|||
Local<Value> argv[1] = { message_info }; |
|||
|
|||
connection->Emit(header_complete_symbol, 1, argv); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
int |
|||
HTTPConnection::on_body (http_parser *parser, const char *buf, size_t len) |
|||
{ |
|||
assert(len != 0); |
|||
|
|||
HTTPConnection *connection = static_cast<HTTPConnection*> (parser->data); |
|||
assert(connection->refs_); |
|||
|
|||
// TODO each message should have their encoding.
|
|||
// don't look at the conneciton for encoding
|
|||
Local<Value> data = Encode(buf, len, connection->encoding_); |
|||
connection->Emit(body_symbol, 1, &data); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
Persistent<FunctionTemplate> HTTPServer::constructor_template; |
|||
|
|||
void |
|||
HTTPServer::Initialize (Handle<Object> target) |
|||
{ |
|||
HandleScope scope; |
|||
|
|||
Local<FunctionTemplate> t = FunctionTemplate::New(New); |
|||
constructor_template = Persistent<FunctionTemplate>::New(t); |
|||
constructor_template->Inherit(Server::constructor_template); |
|||
constructor_template->InstanceTemplate()->SetInternalFieldCount(1); |
|||
constructor_template->SetClassName(String::NewSymbol("Server")); |
|||
target->Set(String::NewSymbol("Server"), constructor_template->GetFunction()); |
|||
} |
|||
|
|||
Handle<Value> |
|||
HTTPServer::New (const Arguments& args) |
|||
{ |
|||
HandleScope scope; |
|||
|
|||
HTTPServer *server = new HTTPServer(); |
|||
server->Wrap(args.This()); |
|||
|
|||
return args.This(); |
|||
} |
|||
|
|||
Handle<FunctionTemplate> |
|||
HTTPServer::GetConnectionTemplate (void) |
|||
{ |
|||
return HTTPConnection::server_constructor_template; |
|||
} |
|||
|
|||
Connection* |
|||
HTTPServer::UnwrapConnection (Local<Object> connection) |
|||
{ |
|||
HandleScope scope; |
|||
return ObjectWrap::Unwrap<HTTPConnection>(connection); |
|||
} |
|||
|
@ -1,68 +0,0 @@ |
|||
#ifndef node_http_h |
|||
#define node_http_h |
|||
|
|||
#include <node_net.h> |
|||
#include <v8.h> |
|||
#include <http_parser.h> |
|||
|
|||
namespace node { |
|||
|
|||
class HTTPConnection : public Connection { |
|||
public: |
|||
static void Initialize (v8::Handle<v8::Object> target); |
|||
|
|||
static v8::Persistent<v8::FunctionTemplate> client_constructor_template; |
|||
static v8::Persistent<v8::FunctionTemplate> server_constructor_template; |
|||
|
|||
protected: |
|||
static v8::Handle<v8::Value> NewClient (const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> NewServer (const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> ResetParser(const v8::Arguments& args); |
|||
|
|||
HTTPConnection (enum http_parser_type t) |
|||
: Connection() |
|||
{ |
|||
type_ = t; |
|||
ResetParser(); |
|||
} |
|||
|
|||
void ResetParser() { |
|||
http_parser_init (&parser_, type_); |
|||
parser_.data = this; |
|||
} |
|||
|
|||
void OnReceive (const void *buf, size_t len); |
|||
void OnEOF (); |
|||
|
|||
static int on_message_begin (http_parser *parser); |
|||
static int on_url (http_parser *parser, const char *at, size_t length); |
|||
static int on_query_string (http_parser *parser, const char *at, size_t length); |
|||
static int on_path (http_parser *parser, const char *at, size_t length); |
|||
static int on_fragment (http_parser *parser, const char *at, size_t length); |
|||
static int on_header_field (http_parser *parser, const char *buf, size_t len); |
|||
static int on_header_value (http_parser *parser, const char *buf, size_t len); |
|||
static int on_headers_complete (http_parser *parser); |
|||
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_; |
|||
friend class HTTPServer; |
|||
}; |
|||
|
|||
class HTTPServer : public Server { |
|||
public: |
|||
static void Initialize (v8::Handle<v8::Object> target); |
|||
static v8::Persistent<v8::FunctionTemplate> constructor_template; |
|||
|
|||
protected: |
|||
static v8::Handle<v8::Value> New (const v8::Arguments& args); |
|||
|
|||
HTTPServer (void) : Server() {} |
|||
|
|||
v8::Handle<v8::FunctionTemplate> GetConnectionTemplate (void); |
|||
Connection* UnwrapConnection (v8::Local<v8::Object> connection); |
|||
}; |
|||
|
|||
} // namespace node
|
|||
#endif |
File diff suppressed because it is too large
@ -1,284 +0,0 @@ |
|||
// Copyright 2009 Ryan Dahl <ry@tinyclouds.org>
|
|||
#ifndef SRC_NET_H_ |
|||
#define SRC_NET_H_ |
|||
|
|||
#include <node.h> |
|||
#include <node_events.h> |
|||
#include <v8.h> |
|||
#include <evcom.h> |
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
#include <gnutls/gnutls.h> |
|||
#include <gnutls/x509.h> |
|||
#endif |
|||
|
|||
|
|||
namespace node { |
|||
|
|||
class Server; |
|||
|
|||
class Connection : public EventEmitter { |
|||
public: |
|||
static void Initialize(v8::Handle<v8::Object> target); |
|||
|
|||
protected: |
|||
/* v8 interface */ |
|||
static v8::Persistent<v8::FunctionTemplate> constructor_template; |
|||
static v8::Handle<v8::Value> New(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> Connect(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> Send(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> Write(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> SendUtf8(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> Close(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> ForceClose(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> SetEncoding(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> Pause(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> Resume(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> SetTimeout(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> SetNoDelay(const v8::Arguments& args); |
|||
|
|||
static v8::Handle<v8::Value> ReadyStateGetter(v8::Local<v8::String> _, |
|||
const v8::AccessorInfo& info); |
|||
static v8::Handle<v8::Value> FDGetter(v8::Local<v8::String> _, |
|||
const v8::AccessorInfo& info); |
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
static v8::Handle<v8::Value> SetSecure(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> VerifyPeer(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> GetPeerCertificate(const v8::Arguments& args); |
|||
#endif |
|||
|
|||
Connection() : EventEmitter() { |
|||
encoding_ = BINARY; |
|||
|
|||
host_ = NULL; |
|||
port_ = NULL; |
|||
|
|||
Init(); |
|||
} |
|||
virtual ~Connection(); |
|||
|
|||
int Connect(struct sockaddr *address) { |
|||
return evcom_stream_connect(&stream_, address); |
|||
} |
|||
|
|||
ssize_t Write(const char *buf, size_t len) { |
|||
return evcom_stream_write(&stream_, buf, len); |
|||
} |
|||
|
|||
void Close() { |
|||
evcom_stream_close(&stream_); |
|||
} |
|||
|
|||
void ForceClose() { |
|||
evcom_stream_force_close(&stream_); |
|||
} |
|||
|
|||
void Pause() { |
|||
evcom_stream_read_pause(&stream_); |
|||
} |
|||
|
|||
void Resume() { |
|||
evcom_stream_read_resume(&stream_); |
|||
} |
|||
|
|||
void SetTimeout(float timeout) { |
|||
evcom_stream_reset_timeout(&stream_, timeout); |
|||
} |
|||
|
|||
void SetNoDelay(bool no_delay) { |
|||
evcom_stream_set_no_delay(&stream_, no_delay); |
|||
} |
|||
|
|||
virtual void OnConnect(); |
|||
virtual void OnReceive(const void *buf, size_t len); |
|||
virtual void OnEOF(); |
|||
virtual void OnClose(); |
|||
virtual void OnTimeout(); |
|||
virtual void OnDrain(); |
|||
|
|||
v8::Local<v8::Object> GetProtocol(); |
|||
|
|||
enum evcom_stream_state ReadyState() { |
|||
return evcom_stream_state(&stream_); |
|||
} |
|||
|
|||
enum encoding encoding_; |
|||
bool resolving_; |
|||
bool secure_; |
|||
#if EVCOM_HAVE_GNUTLS |
|||
gnutls_certificate_credentials_t credentials; |
|||
#endif |
|||
|
|||
private: |
|||
|
|||
/* liboi callbacks */ |
|||
static void on_connect(evcom_stream *s) { |
|||
Connection *connection = static_cast<Connection*>(s->data); |
|||
connection->OnConnect(); |
|||
} |
|||
|
|||
static void on_read(evcom_stream *s, const void *buf, size_t len) { |
|||
Connection *connection = static_cast<Connection*>(s->data); |
|||
assert(connection->refs_); |
|||
if (len == 0) |
|||
connection->OnEOF(); |
|||
else |
|||
connection->OnReceive(buf, len); |
|||
} |
|||
|
|||
static void on_close(evcom_stream *s) { |
|||
Connection *connection = static_cast<Connection*>(s->data); |
|||
|
|||
evcom_stream_detach(s); |
|||
|
|||
assert(connection->stream_.recvfd < 0); |
|||
assert(connection->stream_.sendfd < 0); |
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
if (connection->secure_) { |
|||
if (connection->stream_.session) { |
|||
gnutls_deinit(connection->stream_.session); |
|||
connection->stream_.session = NULL; |
|||
} |
|||
if (!connection->stream_.server && connection->credentials) { |
|||
gnutls_certificate_free_credentials(connection->credentials); |
|||
connection->credentials = NULL; |
|||
} |
|||
} |
|||
#endif |
|||
|
|||
connection->OnClose(); |
|||
|
|||
assert(connection->refs_); |
|||
|
|||
connection->Unref(); |
|||
} |
|||
|
|||
static void on_timeout(evcom_stream *s) { |
|||
Connection *connection = static_cast<Connection*>(s->data); |
|||
connection->OnTimeout(); |
|||
} |
|||
|
|||
static void on_drain(evcom_stream *s) { |
|||
Connection *connection = static_cast<Connection*>(s->data); |
|||
connection->OnDrain(); |
|||
} |
|||
|
|||
void Init(); // constructor helper.
|
|||
|
|||
static int Resolve(eio_req *req); |
|||
static int AfterResolve(eio_req *req); |
|||
char *host_; |
|||
char *port_; |
|||
evcom_stream stream_; |
|||
|
|||
friend class Server; |
|||
}; |
|||
|
|||
class Server : public EventEmitter { |
|||
public: |
|||
static void Initialize(v8::Handle<v8::Object> target); |
|||
|
|||
protected: |
|||
static v8::Persistent<v8::FunctionTemplate> constructor_template; |
|||
static v8::Handle<v8::Value> New(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> Listen(const v8::Arguments& args); |
|||
static v8::Handle<v8::Value> Close(const v8::Arguments& args); |
|||
#if EVCOM_HAVE_GNUTLS |
|||
static v8::Handle<v8::Value> SetSecure(const v8::Arguments& args); |
|||
#endif |
|||
|
|||
Server() : EventEmitter() { |
|||
evcom_server_init(&server_); |
|||
server_.on_connection = Server::on_connection; |
|||
server_.on_close = Server::on_close; |
|||
server_.data = this; |
|||
secure_ = false; |
|||
} |
|||
|
|||
virtual ~Server() { |
|||
assert(server_.fd >= 0); |
|||
} |
|||
|
|||
int Listen(struct sockaddr *address, int backlog) { |
|||
int r = evcom_server_listen(&server_, address, backlog); |
|||
if (r != 0) return r; |
|||
evcom_server_attach(EV_DEFAULT_ &server_); |
|||
Ref(); |
|||
return 0; |
|||
} |
|||
|
|||
void Close() { |
|||
evcom_server_close(&server_); |
|||
} |
|||
|
|||
virtual v8::Handle<v8::FunctionTemplate> GetConnectionTemplate(); |
|||
virtual Connection* UnwrapConnection(v8::Local<v8::Object> connection); |
|||
|
|||
private: |
|||
Connection* OnConnection(struct sockaddr *addr); |
|||
|
|||
static evcom_stream* on_connection(evcom_server *s, struct sockaddr *addr) { |
|||
Server *server = static_cast<Server*>(s->data); |
|||
Connection *connection = server->OnConnection(addr); |
|||
return &connection->stream_; |
|||
} |
|||
|
|||
void OnClose(int errorno); |
|||
|
|||
static void on_close(evcom_server *s) { |
|||
Server *server = static_cast<Server*>(s->data); |
|||
evcom_server_detach(s); |
|||
server->OnClose(s->errorno); |
|||
server->Unref(); |
|||
} |
|||
|
|||
evcom_server server_; |
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
gnutls_certificate_credentials_t credentials; |
|||
#endif |
|||
bool secure_; |
|||
}; |
|||
|
|||
} // namespace node
|
|||
#endif // SRC_NET_H_
|
|||
|
|||
#if EVCOM_HAVE_GNUTLS |
|||
void init_tls_session(evcom_stream* stream_, |
|||
gnutls_certificate_credentials_t credentials, |
|||
gnutls_connection_end_t session_type); |
|||
|
|||
int verify_certificate_chain(gnutls_session_t session, |
|||
const char *hostname, |
|||
const gnutls_datum_t * cert_chain, |
|||
int cert_chain_length, |
|||
gnutls_x509_crl_t *crl_list, |
|||
int crl_list_size, |
|||
gnutls_x509_crt_t *ca_list, |
|||
int ca_list_size); |
|||
|
|||
int verify_cert2(gnutls_x509_crt_t crt, |
|||
gnutls_x509_crt_t issuer, |
|||
gnutls_x509_crl_t * crl_list, |
|||
int crl_list_size); |
|||
|
|||
int verify_last_cert(gnutls_x509_crt_t crt, |
|||
gnutls_x509_crt_t * ca_list, |
|||
int ca_list_size, |
|||
gnutls_x509_crl_t * crl_list, |
|||
int crl_list_size); |
|||
|
|||
#define JS_GNUTLS_CERT_VALIDATED 1 |
|||
#define JS_GNUTLS_CERT_UNDEFINED 0 |
|||
|
|||
#define JS_GNUTLS_CERT_SIGNER_NOT_FOUND -100 |
|||
#define JS_GNUTLS_CERT_SIGNER_NOT_CA -101 |
|||
#define JS_GNUTLS_CERT_INVALID -102 |
|||
#define JS_GNUTLS_CERT_NOT_ACTIVATED -103 |
|||
#define JS_GNUTLS_CERT_EXPIRED -104 |
|||
#define JS_GNUTLS_CERT_REVOKED -105 |
|||
#define JS_GNUTLS_CERT_DOES_NOT_MATCH_HOSTNAME -106 |
|||
|
|||
#endif |
Loading…
Reference in new issue