You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

171 lines
4.1 KiB
C

#include "server.h"
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <ucontext.h>
#include <time.h>
//#include "app_args.h"
#include "request.h"
struct http_request;
struct http_server
{
int sock;
http_server_handler handler;
void* handler_data;
struct http_request* pending_requests;
bool debug;
int active;
ucontext_t context;
};
struct http_server* http_server_new( struct http_server_args* args, http_server_handler handler, void* handler_data )
{
// Create server structure
struct http_server* srv = (struct http_server*)malloc( sizeof(struct http_server) );
memset(srv,0,sizeof(*srv));
if( !srv ) {
printf( "Unable to allocate memory for server.\n" );
return NULL;
}
srv->handler = handler;
srv->handler_data = handler_data;
// Open server socket
srv->sock = socket( AF_INET, SOCK_STREAM, 0 );
if( srv->sock == -1 ) {
printf( "Error creating socket: %s\n", strerror(errno) );
exit(1);
}
// Allow multiple sockets to listen on this port
int optval = 1;
if( -1 == setsockopt( srv->sock, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval) ) ) {
printf( "Could not enable port reuse: %s\n", strerror(errno) );
}
// Bind socket
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(args->bind_port);
addr.sin_addr.s_addr = INADDR_ANY;
if( args->bind_address ) {
if( !inet_aton( args->bind_address, &addr.sin_addr ) ) {
printf( "Unable to parse address %s: %s\n", args->bind_address, strerror(errno) );
exit(1);
}
} else {
args->bind_address = "*";
}
if( -1 == bind( srv->sock, (struct sockaddr*)&addr, sizeof(addr) ) ) {
printf( "Error binding socket to %s:%d: %s\n",
args->bind_address, args->bind_port, strerror(errno)
);
exit(1);
}
if( -1 == listen( srv->sock, 10 ) ) {
printf( "Unable to listen on %s:%d: %s\n",
args->bind_address, args->bind_port, strerror(errno)
);
exit(1);
}
printf( "Listening on %s:%d\n", args->bind_address, args->bind_port );
if( -1 == fcntl( srv->sock, F_SETFL, O_NONBLOCK ) ) {
printf( "Unable to set O_NONBLOCK on server listening socket, continuing with reduced capacity: %s\n", strerror(errno) );
}
return srv;
}
void http_server_process( struct http_server* srv )
{
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int res = accept( srv->sock, (struct sockaddr*)&addr, &addrlen );
if( res == -1 ) {
//printf( "trying again... active: %d\n", srv->active );
switch( errno ) {
case EAGAIN:
case ENETDOWN:
case EPROTO:
case ENOPROTOOPT:
case EHOSTDOWN:
case ENONET:
case EHOSTUNREACH:
case ENETUNREACH:
break;
default:
printf( "Error when accepting connection: %s\n", strerror(errno) );
}
} else {
// Handle connection
//*
struct timespec ts;
clock_gettime( CLOCK_MONOTONIC, &ts );
uint64_t time = (uint64_t)ts.tv_sec * 1000000000 + (uint64_t)ts.tv_nsec;
printf( "Accepting connection at %ld\n", time );
//*/
srv->active += 1;
struct http_request* req = http_request_new( &srv->context, res, inet_ntoa(addr.sin_addr), srv->handler, srv->handler_data );
if( req ) {
http_request_append_list_front( req, &srv->pending_requests );
}
http_request_set_debug( req, srv->debug );
}
struct http_request* req;
// Clear completed requests at start of list
srv->active -= http_request_clear_completed_at( &srv->pending_requests );
int count = 0;
for( req = srv->pending_requests; req; req = http_request_get_next(req) ) {
// clear completed requests in middle of list
srv->active -= http_request_clear_completed( req );
// Process request
http_request_process( req, &srv->context );
count += 1;
}
//*
if( count > 0 ) {
printf( "Processed %d requests\n", count );
}
//*/
}
void http_server_release( struct http_server* srv )
{
printf( "Shutting down HTTP server..." );
// TODO: wait for all pending requests to finish
close( srv->sock );
free( srv );
printf( "done.\n" );
}
void http_server_set_debug( struct http_server* srv, bool debug )
{
srv->debug = debug;
}