#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "../public/nsapi.h"
#include "sessionhandler.h"
#include "httprequest.h"
#include "httpparser.h"
#include "log.h"
#include "error.h"
#include "httplistener.h"
typedef struct _event_http_io {
HTTPRequest *request;
HttpParser *parser;
int error;
} EventHttpIO;
int connection_read(Connection *conn,
void *buf,
int len) {
return (
int)read(conn->fd, buf, len);
}
int connection_write(Connection *conn,
const void *buf,
int len) {
return (
int)write(conn->fd, buf, len);
}
void connection_close(Connection *conn) {
while(close(conn->fd)) {
if(errno !=
EINTR) {
log_ereport(
LOG_VERBOSE,
"connection close failed: %s", strerror(errno));
break;
}
log_ereport(
LOG_VERBOSE,
"connection close: EINTR");
}
}
int connection_ssl_read(Connection *conn,
void *buf,
int len) {
int ret = SSL_read(conn->ssl, buf, len);
if(ret <=
0) {
conn->ssl_error = SSL_get_error(conn->ssl, ret);
}
return ret;
}
int connection_ssl_write(Connection *conn,
const void *buf,
int len) {
int ret = SSL_write(conn->ssl, buf, len);
if(ret <=
0) {
conn->ssl_error = SSL_get_error(conn->ssl, ret);
}
return ret;
}
void connection_ssl_close(Connection *conn) {
if(!conn->ssl_error) {
int ret = SSL_shutdown(conn->ssl);
if(ret !=
1) {
conn->ssl_error = SSL_get_error(conn->ssl, ret);
log_ereport(
LOG_VERBOSE,
"SSL_shutdown failed: %d", conn->ssl_error);
}
}
while(close(conn->fd)) {
if(errno !=
EINTR) {
log_ereport(
LOG_VERBOSE,
"connection close failed: %s", strerror(errno));
break;
}
log_ereport(
LOG_VERBOSE,
"connection close: EINTR");
}
}
void connection_destroy(Connection *conn) {
conn->close(conn);
if(conn->ssl) {
SSL_free(conn->ssl);
}
free(conn);
}
IOStream* create_connection_iostream(
SessionHandler *sh,
Connection *conn,
pool_handle_t *pool,
WSBool *ssl)
{
IOStream *io =
NULL;
if(conn->ssl) {
io = sslstream_new(pool, conn->ssl);
*ssl =
1;
}
else {
io = sysstream_new(pool, conn->fd);
*ssl =
0;
}
return io;
}
SessionHandler* create_basic_session_handler() {
BasicSessionHandler *handler = malloc(
sizeof(BasicSessionHandler));
handler->threadpool = threadpool_new(
4,
8);
handler->sh.enqueue_connection = basic_enq_conn;
handler->sh.keep_alive = basic_keep_alive;
handler->sh.create_iostream = create_connection_iostream;
return (SessionHandler*)handler;
}
void basic_enq_conn(SessionHandler *handler, Connection *conn) {
BasicSessionHandler *sh = (BasicSessionHandler*)handler;
conn->session_handler = handler;
threadpool_run(sh->threadpool, basic_run_session, conn);
}
void* basic_run_session(
void *data) {
Connection *conn = (Connection*)data;
HTTPRequest request;
http_request_init(&request);
request.connection = conn;
netbuf *buf = malloc(
sizeof(netbuf));
buf->rdtimeout =
120;
buf->pos =
0;
buf->cursize =
0;
buf->maxsize =
2048;
buf->sd = &conn->fd;
buf->inbuf = malloc(
2048);
buf->errmsg =
NULL;
request.netbuf = buf;
HttpParser *parser = http_parser_new(&request);
int state;
int r;
r = conn->read(conn, buf->inbuf + buf->pos, buf->maxsize - buf->pos);
if(r <=
0) {
fprintf(stderr,
"%s\n",
"Error: Cannot read from socket");
return NULL;
}
buf->cursize += r;
while((state = http_parser_process(parser)) !=
0) {
if(state ==
2) {
fprintf(stderr,
"%s\n",
"Error: Cannot parse http request");
return NULL;
}
r = conn->read(conn, buf->inbuf + buf->pos, buf->maxsize - buf->pos);
if(r == -
1) {
fprintf(stderr,
"%s\n",
"Error: Cannot read from socket");
return NULL;
}
buf->cursize += r;
}
if(!http_parser_validate(parser)) {
log_ereport(
LOG_FAILURE,
"http_parser_validate failed");
return NULL;
}
r = handle_request(&request,
NULL,
NULL);
return NULL;
}
void basic_keep_alive(SessionHandler *handler, Connection *conn) {
connection_destroy(conn);
}
SessionHandler* create_event_session_handler() {
EventSessionHandler *handler = malloc(
sizeof(EventSessionHandler));
handler->eventhandler = get_default_event_handler();
handler->sh.enqueue_connection = evt_enq_conn;
handler->sh.keep_alive = evt_keep_alive;
handler->sh.create_iostream = create_connection_iostream;
return (SessionHandler*)handler;
}
void evt_enq_conn(SessionHandler *handler, Connection *conn) {
HTTPRequest *request = malloc(
sizeof(HTTPRequest));
http_request_init(request);
request->connection = conn;
conn->session_handler = handler;
int flags;
if ((flags = fcntl(conn->fd,
F_GETFL,
0)) == -
1) {
flags =
0;
}
if (fcntl(conn->fd,
F_SETFL, flags |
O_NONBLOCK) !=
0) {
perror(
"Error: start_event_session: fcntl");
}
netbuf *buf = malloc(
sizeof(netbuf));
buf->rdtimeout =
120;
buf->pos =
0;
buf->cursize =
0;
buf->maxsize =
2048;
buf->sd = &conn->fd;
buf->inbuf = malloc(
2048);
buf->errmsg =
NULL;
request->netbuf = buf;
HttpParser *parser = http_parser_new(request);
EventHttpIO *io = malloc(
sizeof(EventHttpIO));
if(io ==
NULL) {
}
io->request = request;
io->parser = parser;
io->error =
0;
Event *event = malloc(
sizeof(Event));
ZERO(event,
sizeof(Event));
event->fn = conn->ssl && !conn->ssl_accepted ? evt_request_ssl_accept : evt_request_input;
event->finish = evt_request_finish;
event->cookie = io;
EventHandler *ev = ev_instance(((EventSessionHandler*)handler)->eventhandler);
if(ev_pollin(ev, conn->fd, event) !=
0) {
log_ereport(
LOG_FAILURE,
"Cannot enqueue connection");
connection_destroy(conn);
}
}
int evt_request_ssl_accept(EventHandler *handler, Event *event) {
EventHttpIO *io = event->cookie;
Connection *conn = io->request->connection;
int ret = SSL_accept(conn->ssl);
if(ret <=
0) {
int error = SSL_get_error(conn->ssl, ret);
char *errstr;
switch(error) {
default: errstr =
"unknown";
break;
case SSL_ERROR_WANT_READ: {
event->events =
EVENT_POLLIN;
return 1;
}
case SSL_ERROR_WANT_WRITE: {
event->events =
EVENT_POLLOUT;
return 1;
}
case SSL_ERROR_ZERO_RETURN: errstr =
"SSL_ERROR_ZERO_RETURN";
break;
case SSL_ERROR_WANT_CONNECT: errstr =
"SSL_ERROR_WANT_CONNECT";
break;
case SSL_ERROR_WANT_ACCEPT: errstr =
"SSL_ERROR_WANT_ACCEPT";
break;
case SSL_ERROR_WANT_X509_LOOKUP: errstr =
"SSL_ERROR_WANT_X509_LOOKUP";
break;
case SSL_ERROR_SYSCALL: errstr =
"SSL_ERROR_SYSCALL";
break;
case SSL_ERROR_SSL: errstr =
"SSL_ERROR_SSL";
break;
}
log_ereport(
LOG_VERBOSE,
"SSL accept error[%d]: %s", error, errstr);
event->finish = evt_request_error;
io->error =
1;
return 0;
}
conn->ssl_accepted =
WS_TRUE;
event->fn = evt_request_input;
return evt_request_input(handler, event);
}
int evt_request_input(EventHandler *handler, Event *event) {
EventHttpIO *io = event->cookie;
HttpParser *parser = io->parser;
HTTPRequest *request = io->request;
Connection *conn = io->request->connection;
netbuf *buf = request->netbuf;
int state;
int r;
r = conn->read(
conn,
buf->inbuf + buf->pos,
buf->maxsize - buf->pos);
if(r <=
0) {
if(conn->ssl) {
switch(conn->ssl_error) {
case SSL_ERROR_WANT_READ: {
event->events =
EVENT_POLLIN;
return 1;
}
case SSL_ERROR_WANT_WRITE: {
event->events =
EVENT_POLLOUT;
return 1;
}
}
}
event->finish = evt_request_error;
io->error =
1;
return 0;
}
buf->cursize += r;
state = http_parser_process(parser);
if(state ==
2) {
fatal_error(request,
400);
event->finish = evt_request_error;
io->error =
2;
return 0;
}
else if(state ==
1) {
event->events =
EVENT_POLLIN;
return 1;
}
int flags;
if (-
1 == (flags = fcntl(request->connection->fd,
F_GETFL,
0))) {
flags =
0;
}
if (fcntl(request->connection->fd,
F_SETFL, flags & ~
O_NONBLOCK) !=
0) {
event->finish = evt_request_error;
io->error =
3;
return 0;
}
if(!http_parser_validate(parser)) {
log_ereport(
LOG_FAILURE,
"http_parser_validate failed");
event->finish = evt_request_error;
return 0;
}
return 0;
}
int evt_request_finish(EventHandler *h, Event *event) {
EventHttpIO *io = event->cookie;
HttpParser *parser = io->parser;
HTTPRequest *request = io->request;
int r = handle_request(request,
NULL, h);
if(r !=
0) {
connection_destroy(request->connection);
}
http_request_cleanup(request);
http_parser_free(parser);
free(io);
free(event);
return 0;
}
int evt_request_error(EventHandler *h, Event *event) {
EventHttpIO *io = event->cookie;
HttpParser *parser = io->parser;
HTTPRequest *request = io->request;
if(event->error) {
log_ereport(
LOG_VERBOSE,
"sessionhandler http io error: %d fd: %d", io->error, request->connection->fd);
}
free(request->netbuf->inbuf);
free(request->netbuf);
cfg_unref(request->connection->listener->cfg);
connection_destroy(request->connection);
http_request_cleanup(request);
http_parser_free(parser);
free(io);
free(event);
return 0;
}
void evt_keep_alive(SessionHandler *handler, Connection *conn) {
evt_enq_conn(handler, conn);
}