309 lines
7.7 KiB
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;
|
|
}
|
|
|