server/connection/request functions
parent
0ab75876dc
commit
0a514f657e
@ -0,0 +1,8 @@
|
||||
#ifndef _LIGHTTPD_ANGEL_H_
|
||||
#define _LIGHTTPD_ANGEL_H_
|
||||
|
||||
/* interface to the angel; implementation needs to work without angel too */
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,127 @@
|
||||
|
||||
#include "connection.h"
|
||||
#include "network.h"
|
||||
#include "log.h"
|
||||
|
||||
static void parse_request_body(server *srv, connection *con) {
|
||||
if (con->state == CON_STATE_HANDLE_RESPONSE && !con->in->is_closed) {
|
||||
/* TODO: parse chunked encoded request body */
|
||||
if (con->in->bytes_in < con->request.content_length) {
|
||||
chunkqueue_steal_len(con->in, con->raw_in, con->request.content_length - con->in->bytes_in);
|
||||
if (con->in->bytes_in == con->request.content_length) con->in->is_closed = TRUE;
|
||||
} else if (con->request.content_length == -1) {
|
||||
chunkqueue_steal_all(con->in, con->raw_in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void connection_cb(struct ev_loop *loop, ev_io *w, int revents) {
|
||||
connection_socket *con_sock = (connection_socket*) w->data;
|
||||
server *srv = con_sock->srv;
|
||||
connection *con = con_sock->con;
|
||||
|
||||
if (revents && EV_READ) {
|
||||
if (con->in->is_closed) {
|
||||
/* don't read the next request before current one is done */
|
||||
ev_io_set(w, w->fd, w->events && ~EV_READ);
|
||||
} else {
|
||||
switch(network_read(srv, con, w->fd, con->raw_in)) {
|
||||
case NETWORK_STATUS_SUCCESS:
|
||||
parse_request_body(srv, con);
|
||||
joblist_append(srv, con);
|
||||
break;
|
||||
case NETWORK_STATUS_FATAL_ERROR:
|
||||
connection_set_state(srv, con, CON_STATE_ERROR);
|
||||
joblist_append(srv, con);
|
||||
break;
|
||||
case NETWORK_STATUS_CONNECTION_CLOSE:
|
||||
connection_set_state(srv, con, CON_STATE_CLOSE);
|
||||
joblist_append(srv, con);
|
||||
break;
|
||||
case NETWORK_STATUS_WAIT_FOR_EVENT:
|
||||
break;
|
||||
case NETWORK_STATUS_WAIT_FOR_AIO_EVENT:
|
||||
/* TODO ? */
|
||||
ev_io_set(w, w->fd, w->events && ~EV_READ);
|
||||
break;
|
||||
case NETWORK_STATUS_WAIT_FOR_FD:
|
||||
/* TODO */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (revents && EV_WRITE) {
|
||||
if (con->raw_out->length > 0) {
|
||||
network_write(srv, con, w->fd, con->raw_out);
|
||||
joblist_append(srv, con);
|
||||
}
|
||||
if (con->raw_out->length == 0) {
|
||||
ev_io_set(w, w->fd, w->events && ~EV_WRITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connection* connection_new(server *srv) {
|
||||
connection *con = g_slice_new0(connection);
|
||||
UNUSED(srv);
|
||||
|
||||
con->raw_in = chunkqueue_new();
|
||||
con->raw_out = chunkqueue_new();
|
||||
con->in = chunkqueue_new();
|
||||
con->out = chunkqueue_new();
|
||||
|
||||
con->sock.srv = srv; con->sock.con = con; con->sock.watcher.data = con;
|
||||
ev_io_init(&con->sock.watcher, connection_cb, -1, 0);
|
||||
con->remote_addr_str = g_string_sized_new(0);
|
||||
con->local_addr_str = g_string_sized_new(0);
|
||||
|
||||
action_stack_init(&con->action_stack);
|
||||
|
||||
request_init(&con->request, con->raw_in);
|
||||
|
||||
return con;
|
||||
}
|
||||
|
||||
void connection_reset(server *srv, connection *con) {
|
||||
chunkqueue_reset(con->raw_in);
|
||||
chunkqueue_reset(con->raw_out);
|
||||
chunkqueue_reset(con->in);
|
||||
chunkqueue_reset(con->out);
|
||||
|
||||
ev_io_stop(srv->loop, &con->sock.watcher);
|
||||
close(con->sock.watcher.fd);
|
||||
ev_io_set(&con->sock.watcher, -1, 0);
|
||||
g_string_truncate(con->remote_addr_str, 0);
|
||||
g_string_truncate(con->local_addr_str, 0);
|
||||
|
||||
action_stack_reset(srv, &con->action_stack);
|
||||
|
||||
request_reset(&con->request);
|
||||
}
|
||||
|
||||
void connection_free(server *srv, connection *con) {
|
||||
chunkqueue_free(con->raw_in);
|
||||
chunkqueue_free(con->raw_out);
|
||||
chunkqueue_free(con->in);
|
||||
chunkqueue_free(con->out);
|
||||
|
||||
ev_io_stop(srv->loop, &con->sock.watcher);
|
||||
close(con->sock.watcher.fd);
|
||||
ev_io_set(&con->sock.watcher, -1, 0);
|
||||
g_string_free(con->remote_addr_str, TRUE);
|
||||
g_string_free(con->local_addr_str, TRUE);
|
||||
|
||||
action_stack_clear(srv, &con->action_stack);
|
||||
|
||||
request_clear(&con->request);
|
||||
|
||||
g_slice_free(connection, con);
|
||||
}
|
||||
|
||||
void connection_set_state(server *srv, connection *con, connection_state_t state) {
|
||||
}
|
||||
|
||||
void connection_state_machine(server *srv, connection *con) {
|
||||
|
||||
}
|
@ -1,23 +1,54 @@
|
||||
#ifndef _LIGHTTPD_CONNECTION_H_
|
||||
#define _LIGHTTPD_CONNECTION_H_
|
||||
|
||||
#include "base.h"
|
||||
|
||||
typedef enum {
|
||||
CON_STATE_REQUEST_START, /** after the connect, the request is initialized, keep-alive starts here again */
|
||||
CON_STATE_READ_REQUEST_HEADER, /** loop in the read-request-header until the full header is received */
|
||||
CON_STATE_VALIDATE_REQUEST_HEADER, /** validate the request-header */
|
||||
CON_STATE_HANDLE_RESPONSE, /** find a handler for the request */
|
||||
CON_STATE_RESPONSE_END, /** successful request, connection closed */
|
||||
CON_STATE_ERROR, /** fatal error, connection closed */
|
||||
CON_STATE_CLOSE /** connection reset by peer */
|
||||
} connection_state_t;
|
||||
|
||||
struct connection_socket;
|
||||
typedef struct connection_socket connection_socket;
|
||||
|
||||
struct connection_socket {
|
||||
server *srv;
|
||||
connection *con;
|
||||
ev_io watcher;
|
||||
};
|
||||
|
||||
struct connection {
|
||||
guint idx; /** index in connection table */
|
||||
connection_state_t state;
|
||||
|
||||
chunkqueue *raw_in, *raw_out;
|
||||
chunkqueue *in, *out;
|
||||
|
||||
connection_socket sock;
|
||||
sock_addr remote_addr, local_addr;
|
||||
GString *remote_addr_str, *local_addr_str;
|
||||
gboolean is_ssl;
|
||||
|
||||
action_stack action_stack;
|
||||
|
||||
gpointer *options;
|
||||
gpointer *options; /* TODO */
|
||||
|
||||
request request;
|
||||
physical physical;
|
||||
|
||||
GMutex *mutex;
|
||||
|
||||
struct log_t *log;
|
||||
gint log_level;
|
||||
};
|
||||
|
||||
LI_API connection* connection_new(server *srv);
|
||||
LI_API void connection_reset(server *srv, connection *con);
|
||||
LI_API void connection_free(server *srv, connection *con);
|
||||
|
||||
LI_API void connection_set_state(server *srv, connection *con, connection_state_t state);
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,114 @@
|
||||
|
||||
#include "network.h"
|
||||
|
||||
/** repeats write after EINTR */
|
||||
static ssize_t net_write(int fd, void *buf, ssize_t nbyte) {
|
||||
ssize_t r;
|
||||
while (-1 == (r = write(fd, buf, nbyte))) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
/* Try again */
|
||||
break;
|
||||
default:
|
||||
/* report error */
|
||||
return r;
|
||||
}
|
||||
}
|
||||
/* return bytes written */
|
||||
return r;
|
||||
}
|
||||
|
||||
network_status_t network_write(server *srv, connection *con, int fd, chunkqueue *cq) {
|
||||
const ssize_t blocksize = 16*1024; /* 16k */
|
||||
const off_t max_write = 16 * blocksize; /* 256k */
|
||||
char *block_data;
|
||||
off_t block_len;
|
||||
ssize_t r;
|
||||
off_t len;
|
||||
chunkiter ci;
|
||||
|
||||
do {
|
||||
ci = chunkqueue_iter(cq);
|
||||
switch (chunkiter_read(srv, con, ci, 0, blocksize, &block_data, &block_len)) {
|
||||
case HANDLER_GO_ON:
|
||||
break;
|
||||
case HANDLER_WAIT_FOR_FD:
|
||||
return len ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT;
|
||||
case HANDLER_ERROR:
|
||||
default:
|
||||
return NETWORK_STATUS_FATAL_ERROR;
|
||||
}
|
||||
|
||||
if (-1 == (r = net_write(fd, block_data, block_len))) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
#if EWOULDBLOCK != EAGAIN
|
||||
case EWOULDBLOCK
|
||||
#endif
|
||||
return len ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT;
|
||||
case ECONNRESET:
|
||||
return NETWORK_STATUS_CONNECTION_CLOSE;
|
||||
default:
|
||||
CON_ERROR(srv, con, "oops, read from fd=%d failed: %s (%d)", fd, strerror(errno), errno );
|
||||
return NETWORK_STATUS_FATAL_ERROR;
|
||||
}
|
||||
} else if (0 == r) {
|
||||
return len ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT;
|
||||
}
|
||||
chunkqueue_skip(cq, r);
|
||||
len += r;
|
||||
} while (r == blocksize && len < max_write);
|
||||
|
||||
return NETWORK_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/** repeats read after EINTR */
|
||||
static ssize_t net_read(int fd, void *buf, ssize_t nbyte) {
|
||||
ssize_t r;
|
||||
while (-1 == (r = read(fd, buf, nbyte))) {
|
||||
switch (errno) {
|
||||
case EINTR:
|
||||
/* Try again */
|
||||
break;
|
||||
default:
|
||||
/* report error */
|
||||
return r;
|
||||
}
|
||||
}
|
||||
/* return bytes read */
|
||||
return r;
|
||||
}
|
||||
|
||||
network_status_t network_read(server *srv, connection *con, int fd, chunkqueue *cq) {
|
||||
const ssize_t blocksize = 16*1024; /* 16k */
|
||||
const off_t max_read = 16 * blocksize; /* 256k */
|
||||
ssize_t r;
|
||||
off_t len;
|
||||
|
||||
do {
|
||||
GString *buf = g_string_sized_new(blocksize);
|
||||
if (-1 == (r = net_read(fd, buf->str, blocksize))) {
|
||||
g_string_free(buf, TRUE);
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
#if EWOULDBLOCK != EAGAIN
|
||||
case EWOULDBLOCK
|
||||
#endif
|
||||
return len ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT;
|
||||
case ECONNRESET:
|
||||
return NETWORK_STATUS_CONNECTION_CLOSE;
|
||||
default:
|
||||
CON_ERROR(srv, con, "oops, read from fd=%d failed: %s (%d)", fd, strerror(errno), errno );
|
||||
return NETWORK_STATUS_FATAL_ERROR;
|
||||
}
|
||||
} else if (0 == r) {
|
||||
g_string_free(buf, TRUE);
|
||||
return len ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_CONNECTION_CLOSE;
|
||||
}
|
||||
g_string_truncate(buf, r);
|
||||
chunkqueue_append_string(cq, buf);
|
||||
len += r;
|
||||
} while (r == blocksize && len < max_read);
|
||||
|
||||
return NETWORK_STATUS_SUCCESS;
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
#ifndef _LIGHTTPD_NETWORK_H_
|
||||
#define _LIGHTTPD_NETWORK_H_
|
||||
|
||||
#include "base.h"
|
||||
|
||||
typedef enum {
|
||||
NETWORK_STATUS_SUCCESS,
|
||||
NETWORK_STATUS_FATAL_ERROR,
|
||||
NETWORK_STATUS_CONNECTION_CLOSE,
|
||||
NETWORK_STATUS_WAIT_FOR_EVENT,
|
||||
NETWORK_STATUS_WAIT_FOR_AIO_EVENT,
|
||||
NETWORK_STATUS_WAIT_FOR_FD,
|
||||
} network_status_t;
|
||||
|
||||
LI_API network_status_t network_write(server *srv, connection *con, int fd, chunkqueue *cq);
|
||||
LI_API network_status_t network_read(server *srv, connection *con, int fd, chunkqueue *cq);
|
||||
|
||||
#endif
|
@ -0,0 +1,25 @@
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void fatal(const gchar* msg) {
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
abort();
|
||||
}
|
||||
|
||||
void fd_init(int fd) {
|
||||
#ifdef _WIN32
|
||||
int i = 1;
|
||||
#endif
|
||||
#ifdef FD_CLOEXEC
|
||||
/* close fd on exec (cgi) */
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
#endif
|
||||
#ifdef O_NONBLOCK
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR);
|
||||
#elif defined _WIN32
|
||||
ioctlsocket(fd, FIONBIO, &i);
|
||||
#endif
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
#ifndef _LIGHTTPD_UTILS_H_
|
||||
#define _LIGHTTPD_UTILS_H_
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
LI_API void fatal(const gchar* msg);
|
||||
|
||||
/* set O_NONBLOCK and FD_CLOEXEC */
|
||||
LI_API void fd_init(int fd);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue