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
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;
|
|
}
|
|
|