toys/simple-http-server-c/source/tiny_http_server.c

309 lines
7.7 KiB
C

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <assert.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include "http_parser.h"
#include "dynamic_buffer.h"
#include "http_response.h"
#include "util.h"
#include "cJSON.h"
#define SERVER_STRING "Server: tinyhttpserver/0.1.0\r\n"
int OnMessageBegin(http_parser *o)
{
(void)o;
printf("\n***MESSAGE BEGIN***\n\n");
return 0;
}
int OnHeadersComplete(http_parser *o)
{
(void)o;
printf("\n***HEADERS COMPLETE***\n\n");
return 0;
}
int OnMessageComplete(http_parser *o)
{
(void)o;
printf("\n***MESSAGE COMPLETE***\n\n");
return 0;
}
int OnUrl(http_parser* o, const char* at, size_t length)
{
(void)o;
printf("Url: %.*s\n", (int)length, at);
return 0;
}
int OnHeaderField(http_parser* o, const char* at, size_t length)
{
(void)o;
printf("Header field: %.*s\n", (int)length, at);
return 0;
}
int OnHeaderValue(http_parser* o, const char* at, size_t length)
{
(void)o;
printf("Header value: %.*s\n", (int)length, at);
return 0;
}
int OnBody(http_parser* o, const char* at, size_t length)
{
(void)o;
cJSON *json = cJSON_ParseWithLength(at, length);
if (json == NULL)
{
char *dst = (char *)malloc(length * 2);
printf("Body Other: %.*s\n", (int)length, at);
if (HttpUrlEncodeWithLength(at, length, dst, length * 2) > 0)
{
printf("Body Other Encoded: %s\n", dst);
}
free(dst);
}
else
{
char *json_str = cJSON_Print(json);
printf("Body JSON: %s\n", json_str);
cJSON_free(json_str);
cJSON_Delete(json);
}
return 0;
}
void *ParseRequest(void *arg);
static void ReceiveAndParseRequestHeader(int sockfd, DynamicBuffer *cache)
{
int end_of_header = 0;
uint8_t* p = NULL;
uint8_t* q = NULL;
size_t state = 0;
ssize_t receive_size;
DynamicBuffer_Clear(cache);
while (!end_of_header)
{
receive_size = recv(sockfd, DynamicBuffer_AvailableBuffer(cache), DynamicBuffer_AvailableCapacity(cache) - 1, 0);
if (receive_size > 0)
{
p = DynamicBuffer_AvailableBuffer(cache);
DynamicBuffer_Resize(cache, DynamicBuffer_Size(cache) + receive_size);
/// Small state machine to judge whether reach to the end of response header.
for (q = p + receive_size; p < q; p++)
{
if (*p == '\r' && (state == 0 || state == 2))
{
state++;
}
else if (*p == '\n' && (state == 1 || state == 3))
{
state++;
}
else
{
state = 0;
}
if (state == 4)
{
p++;
break;
}
}
if (state == 4)
{
end_of_header = 1;
}
else
{
/// The buffer is not enough
DynamicBuffer_Reserve(cache, DynamicBuffer_Capacity(cache) + receive_size);
}
}
else
{
printf("[E] recv error!\n");
break;
}
}
if (end_of_header)
{
/// Dump the received data
// printf("%.*s\n", (int)DynamicBuffer_Size(cache), (char *)DynamicBuffer_BufferAt(cache, 0));
/// Pop all the header data
// DynamicBuffer_ResetBuffer(cache, (size_t)(p - (uint8_t *)DynamicBuffer_Buffer(cache)));
ParseRequest(cache);
}
else /// Some errors occurred
{
DynamicBuffer_Clear(cache);
}
}
void *ParseRequest(void *arg)
{
DynamicBuffer *db = (DynamicBuffer *)arg;
http_parser_settings settings;
memset(&settings, 0, sizeof(settings));
settings.on_message_begin = OnMessageBegin;
settings.on_url = OnUrl;
settings.on_header_field = OnHeaderField;
settings.on_header_value = OnHeaderValue;
settings.on_headers_complete = OnHeadersComplete;
settings.on_body = OnBody;
settings.on_message_complete = OnMessageComplete;
http_parser parser;
http_parser_init(&parser, HTTP_REQUEST);
size_t nparsed = http_parser_execute(&parser, &settings, (char *)DynamicBuffer_Buffer(db), DynamicBuffer_Size(db));
if (nparsed != (size_t)DynamicBuffer_Size(db))
{
fprintf(stderr,
"Error: %s (%s)\n",
http_errno_description(HTTP_PARSER_ERRNO(&parser)),
http_errno_name(HTTP_PARSER_ERRNO(&parser)));
return NULL;
}
return NULL;
}
int TinyHttpServerStartup(const char *host, uint16_t *port)
{
int httpd = 0;
struct sockaddr_in server_addr;
httpd = socket(AF_INET, SOCK_STREAM, 0);
if (httpd == -1)
{
printf("[E] Failed to create server socket\n");
exit(EXIT_FAILURE);
}
// bzero(&server_addr, sizeof(server_addr));
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(*port);
server_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(httpd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
printf("[E] Failed to bind server socket on %s:%d\n", host, *port);
exit(EXIT_FAILURE);
}
// If dynamically allocating a port
if (*port == 0)
{
socklen_t namelen = sizeof(server_addr);
if (getsockname(httpd, (struct sockaddr *)&server_addr, &namelen) == -1)
{
printf("[E] Failed to getsockname\n");
exit(EXIT_FAILURE);
}
*port = ntohs(server_addr.sin_port);
}
if (listen(httpd, 5) < 0)
{
printf("[E] Failed to bind server socket on %s:%d\n", host, *port);
exit(EXIT_FAILURE);
}
return httpd;
}
void TinyHttpServerRun(const char *host, uint16_t port)
{
int server_sock_fd = -1;
int client_sock_fd = -1;
struct sockaddr_in client_name;
socklen_t client_name_len = sizeof(client_name);
pthread_t new_client_thread;
DynamicBuffer cache;
DynamicBuffer_Construct(&cache);
DynamicBuffer output;
DynamicBuffer_Construct(&output);
HttpResponseHeader response_header;
HttpResponseHeader_Construct(&response_header);
DynamicBuffer_AppendString(&output, "{\"code\": 0, \"msg\": \"OK\"}");
server_sock_fd = TinyHttpServerStartup(host, &port);
printf("TinyHttpServer running on %s:%d\n", host, port);
while (1)
{
client_sock_fd = accept(server_sock_fd, (struct sockaddr *)&client_name, &client_name_len);
if (client_sock_fd == -1)
{
printf("[E] Failed to accept a new client socket!\n");
exit(EXIT_FAILURE);
}
ReceiveAndParseRequestHeader(client_sock_fd, &cache);
HttpResponseHeader_Start(&response_header, "200", "OK");
HttpResponseHeader_AppendField(&response_header, "Content-Type", "application/json");
HttpResponseHeader_AppendField(&response_header, "Date", Rfc822DateNow());
HttpResponseHeader_EndWithContentLength(&response_header, DynamicBuffer_Size(&output));
send(client_sock_fd, HttpResponseHeader_AsString(&response_header), HttpResponseHeader_Size(&response_header), 0);
send(client_sock_fd, DynamicBuffer_Buffer(&output), DynamicBuffer_Size(&output), 0);
close(client_sock_fd);
client_sock_fd = -1;
}
DynamicBuffer_Destruct(&cache);
HttpResponseHeader_Destruct(&response_header);
}
int main(int argc, char *argv[])
{
TinyHttpServerRun("0.0.0.0", 8898);
return EXIT_SUCCESS;
}