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