first commit! 🎉
This commit is contained in:
commit
1b584f3a33
9 changed files with 683 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
http.dSYM/
|
||||
http
|
7
src/appstate.h
Normal file
7
src/appstate.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include "log.h"
|
||||
|
||||
typedef struct AppState {
|
||||
int sock;
|
||||
Logger *log;
|
||||
} AppState;
|
||||
static AppState app = {0};
|
238
src/http.c
Normal file
238
src/http.c
Normal file
|
@ -0,0 +1,238 @@
|
|||
#include "http.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
char *http_method_string(HTTPMethod method) {
|
||||
switch (method) {
|
||||
case METHOD_GET:
|
||||
return METHOD_GET_STR;
|
||||
case METHOD_HEAD:
|
||||
return METHOD_HEAD_STR;
|
||||
case METHOD_POST:
|
||||
return METHOD_POST_STR;
|
||||
case METHOD_PUT:
|
||||
return METHOD_PUT_STR;
|
||||
case METHOD_DELETE:
|
||||
return METHOD_DELETE_STR;
|
||||
case METHOD_CONNECT:
|
||||
return METHOD_CONNECT_STR;
|
||||
case METHOD_OPTIONS:
|
||||
return METHOD_OPTIONS_STR;
|
||||
case METHOD_TRACE:
|
||||
return METHOD_TRACE_STR;
|
||||
case METHOD_PATCH:
|
||||
return METHOD_PATCH_STR;
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
int parse_method(HTTPRequest *request, char *data, size_t data_len) {
|
||||
char *end = strchr(data, ' ');
|
||||
if (end == NULL)
|
||||
return -1;
|
||||
size_t len = (end - data) % 8;
|
||||
char method[len + 1];
|
||||
strncpy(method, data, len);
|
||||
method[len] = 0;
|
||||
|
||||
if (strcmp(method, METHOD_GET_STR) == 0)
|
||||
request->method = METHOD_GET;
|
||||
else if (strcmp(method, METHOD_HEAD_STR) == 0)
|
||||
request->method = METHOD_HEAD;
|
||||
else if (strcmp(method, METHOD_POST_STR) == 0)
|
||||
request->method = METHOD_POST;
|
||||
else if (strcmp(method, METHOD_PUT_STR) == 0)
|
||||
request->method = METHOD_PUT;
|
||||
else if (strcmp(method, METHOD_DELETE_STR) == 0)
|
||||
request->method = METHOD_DELETE;
|
||||
else if (strcmp(method, METHOD_CONNECT_STR) == 0)
|
||||
request->method = METHOD_CONNECT;
|
||||
else if (strcmp(method, METHOD_OPTIONS_STR) == 0)
|
||||
request->method = METHOD_OPTIONS;
|
||||
else if (strcmp(method, METHOD_TRACE_STR) == 0)
|
||||
request->method = METHOD_TRACE;
|
||||
else if (strcmp(method, METHOD_PATCH_STR) == 0)
|
||||
request->method = METHOD_PATCH;
|
||||
else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_request_uri(HTTPRequest *request, char *data, size_t data_len) {
|
||||
char *start = strstr(data, " /");
|
||||
if (start == NULL)
|
||||
return -1;
|
||||
start++;
|
||||
char *end = strchr(start, ' ');
|
||||
size_t len = end - start;
|
||||
request->uri = malloc(sizeof(char) * (len + 1));
|
||||
strncpy(request->uri, start, len);
|
||||
request->uri[len] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_headers(HTTPRequest *request, char *data, size_t data_len) {
|
||||
request->headers = NULL;
|
||||
|
||||
char *start = data;
|
||||
while (start < data + data_len) {
|
||||
char *end = strstr(start, "\r\n");
|
||||
if (end == NULL)
|
||||
break;
|
||||
size_t linelen = end - start;
|
||||
char line[linelen + 1];
|
||||
strncpy(line, start, linelen);
|
||||
line[linelen] = 0;
|
||||
|
||||
char *keyend = strstr(line, ":");
|
||||
if (keyend == NULL)
|
||||
break;
|
||||
|
||||
char *valstart = keyend + 1;
|
||||
while (*valstart == ' ' && valstart < line + linelen)
|
||||
valstart++;
|
||||
if (valstart == NULL || valstart >= line + linelen)
|
||||
break;
|
||||
|
||||
size_t keylen = keyend - line;
|
||||
size_t valuelen = linelen - (valstart - line);
|
||||
|
||||
HTTPHeader *header = malloc(sizeof(HTTPHeader));
|
||||
header->key = malloc(sizeof(char) * (keylen + 1));
|
||||
header->value = malloc(sizeof(char) * (valuelen + 1));
|
||||
strncpy(header->key, line, keylen);
|
||||
header->key[keylen] = 0;
|
||||
strncpy(header->value, valstart, valuelen);
|
||||
header->value[valuelen] = 0;
|
||||
|
||||
request->headers = add_node(request->headers, header->key, header);
|
||||
|
||||
start += linelen + 2; // skip \r\n
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _free_header_nodes(Node *tree) {
|
||||
if (tree == NULL)
|
||||
return;
|
||||
_free_header_nodes(tree->left);
|
||||
_free_header_nodes(tree->right);
|
||||
HTTPHeader *header = tree->data;
|
||||
free(header->key);
|
||||
free(header->value);
|
||||
free(tree);
|
||||
}
|
||||
void free_http_request(HTTPRequest *request) {
|
||||
free(request->body);
|
||||
_free_header_nodes(request->headers);
|
||||
free(request);
|
||||
}
|
||||
|
||||
void _push_http_header_list(Node *tree, HTTPHeader **list, size_t index) {
|
||||
if (tree == NULL)
|
||||
return;
|
||||
_push_http_header_list(tree->right, list, index);
|
||||
list[index + 1] = tree->data;
|
||||
_push_http_header_list(tree->left, list, index + 2);
|
||||
}
|
||||
size_t list_http_headers(HTTPRequest *request, HTTPHeader **list) {
|
||||
size_t len = tree_size(request->headers);
|
||||
list = malloc(sizeof(HTTPHeader*) * len);
|
||||
_push_http_header_list(request->headers, list, 0);
|
||||
return len;
|
||||
}
|
||||
|
||||
char *get_http_header(HTTPRequest *request, char *key) {
|
||||
Node *node = get_node(request->headers, key);
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
HTTPHeader *header = node->data;
|
||||
if (header == NULL)
|
||||
return NULL;
|
||||
return header->value;
|
||||
}
|
||||
|
||||
int recvhttp(int sock, HTTPRequest *request, struct sockaddr *addr, socklen_t *addr_len) {
|
||||
size_t buflen = RECV_BUFFER_SIZE;
|
||||
char *recvbuf = malloc(buflen);
|
||||
memset(recvbuf, 0, buflen);
|
||||
size_t total_bytes = 0;
|
||||
int bytes = 0;
|
||||
|
||||
while (1) {
|
||||
bytes = recvfrom(
|
||||
sock,
|
||||
recvbuf + total_bytes,
|
||||
RECV_BUFFER_SIZE,
|
||||
0,
|
||||
addr,
|
||||
addr_len);
|
||||
|
||||
if (bytes == -1) {
|
||||
fprintf(stderr, "recv failed: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
total_bytes += bytes;
|
||||
|
||||
// HTTP request end
|
||||
if (total_bytes >= 4 && strcmp(recvbuf + total_bytes - 4, "\r\n\r\n") == 0)
|
||||
break;
|
||||
|
||||
if (bytes < RECV_BUFFER_SIZE)
|
||||
break;
|
||||
|
||||
// add to buffer size if we expect more data
|
||||
size_t newbuflen = buflen + RECV_BUFFER_SIZE;
|
||||
char *newbuf = malloc(newbuflen);
|
||||
memset(newbuf, 0, newbuflen);
|
||||
memcpy(newbuf, recvbuf, buflen);
|
||||
free(recvbuf);
|
||||
recvbuf = newbuf;
|
||||
buflen = newbuflen;
|
||||
}
|
||||
|
||||
if (recvbuf[total_bytes] != 0) {
|
||||
if (total_bytes == buflen) {
|
||||
size_t newbuflen = buflen + 1;
|
||||
char *newbuf = malloc(newbuflen);
|
||||
memcpy(newbuf, recvbuf, buflen);
|
||||
free(recvbuf);
|
||||
recvbuf = newbuf;
|
||||
buflen = newbuflen;
|
||||
}
|
||||
recvbuf[total_bytes] = 0;
|
||||
}
|
||||
|
||||
if (parse_method(request, recvbuf, total_bytes) == -1) {
|
||||
fprintf(stderr, "failed to parse request method\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (parse_request_uri(request, recvbuf, total_bytes) == -1) {
|
||||
fprintf(stderr, "failed to parse request uri\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *header_start = strstr(recvbuf, "\r\n");
|
||||
if (header_start == NULL || (header_start - recvbuf) + 2 > total_bytes) {
|
||||
fprintf(stderr, "warn: client with no headers?\n");
|
||||
} else {
|
||||
header_start += 2;
|
||||
if (parse_headers(request, header_start, total_bytes) == -1) {
|
||||
fprintf(stderr, "failed to parse request headers\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
request->body = strstr(recvbuf, "\r\n\r\n");
|
||||
request->body_len = request->body != NULL ? strlen(request->body) : 0;
|
||||
return 0;
|
||||
}
|
117
src/http.h
Normal file
117
src/http.h
Normal file
|
@ -0,0 +1,117 @@
|
|||
#include "node.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define SOCK_QUEUE 1024
|
||||
#define RECV_BUFFER_SIZE 1024 // starting length; buffer size will be increased as needed
|
||||
|
||||
#define METHOD_GET_STR "GET"
|
||||
#define METHOD_HEAD_STR "HEAD"
|
||||
#define METHOD_POST_STR "POST"
|
||||
#define METHOD_PUT_STR "PUT"
|
||||
#define METHOD_DELETE_STR "DELETE"
|
||||
#define METHOD_CONNECT_STR "CONNECT"
|
||||
#define METHOD_OPTIONS_STR "OPTIONS"
|
||||
#define METHOD_TRACE_STR "TRACE"
|
||||
#define METHOD_PATCH_STR "PATCH"
|
||||
|
||||
typedef enum HTTPMethod {
|
||||
METHOD_GET,
|
||||
METHOD_HEAD,
|
||||
METHOD_POST,
|
||||
METHOD_PUT,
|
||||
METHOD_DELETE,
|
||||
METHOD_CONNECT,
|
||||
METHOD_OPTIONS,
|
||||
METHOD_TRACE,
|
||||
METHOD_PATCH,
|
||||
} HTTPMethod;
|
||||
|
||||
#define HTTP_Continue 100
|
||||
#define HTTP_Switching_Protocols 101
|
||||
#define HTTP_Processing 102
|
||||
#define HTTP_Early_Hints 103
|
||||
#define HTTP_OK 200
|
||||
#define HTTP_Created 201
|
||||
#define HTTP_Accepted 202
|
||||
#define HTTP_Non_Authoritative_Information 203
|
||||
#define HTTP_No_Content 204
|
||||
#define HTTP_Reset_Content 205
|
||||
#define HTTP_Partial_Content 206
|
||||
#define HTTP_Multi_Status 207
|
||||
#define HTTP_Already_Reported 208
|
||||
#define HTTP_IM_Used 226
|
||||
#define HTTP_Multiple_Choices 300
|
||||
#define HTTP_Moved_Permanently 301
|
||||
#define HTTP_Found 302
|
||||
#define HTTP_See_Other 303
|
||||
#define HTTP_Not_Modified 304
|
||||
#define HTTP_Temporary_Redirect 307
|
||||
#define HTTP_Permanent_Redirect 308
|
||||
#define HTTP_Bad_Request 400
|
||||
#define HTTP_Unauthorized 401
|
||||
#define HTTP_Payment_Required 402
|
||||
#define HTTP_Forbidden 403
|
||||
#define HTTP_Not_Found 404
|
||||
#define HTTP_Method_Not_Allowed 405
|
||||
#define HTTP_Not_Acceptable 406
|
||||
#define HTTP_Proxy_Authentication_Required 407
|
||||
#define HTTP_Request_Timeout 408
|
||||
#define HTTP_Conflict 409
|
||||
#define HTTP_Gone 410
|
||||
#define HTTP_Length_Required 411
|
||||
#define HTTP_Precondition_Failed 412
|
||||
#define HTTP_Content_Too_Large 413
|
||||
#define HTTP_URI_Too_Long 414
|
||||
#define HTTP_Unsupported_Media_Type 415
|
||||
#define HTTP_Range_Not_Satisfiable 416
|
||||
#define HTTP_Expectation_Failed 417
|
||||
#define HTTP_TEAPOT 418
|
||||
#define HTTP_Misdirected_Request 421
|
||||
#define HTTP_Unprocessable_Content 422
|
||||
#define HTTP_Locked 423
|
||||
#define HTTP_Failed_Dependency 424
|
||||
#define HTTP_Too_Early 425
|
||||
#define HTTP_Upgrade_Required 426
|
||||
#define HTTP_Precondition_Required 428
|
||||
#define HTTP_Too_Many_Requests 429
|
||||
#define HTTP_Request_Header_Fields_Too_Large 431
|
||||
#define HTTP_Unavailable_For_Legal_Reasons 451
|
||||
#define HTTP_Internal_Server_Error 500
|
||||
#define HTTP_Not_Implemented 501
|
||||
#define HTTP_Bad_Gateway 502
|
||||
#define HTTP_Service_Unavailable 503
|
||||
#define HTTP_Gateway_Timeout 504
|
||||
#define HTTP_HTTP_Version_Not_Supported 505
|
||||
#define HTTP_Variant_Also_Negotiates 506
|
||||
#define HTTP_Insufficient_Storage 507
|
||||
#define HTTP_Loop_Detected 508
|
||||
#define HTTP_Not_Extended 510
|
||||
#define HTTP_Network_Authentication_Required 511
|
||||
|
||||
typedef struct HTTPRequest {
|
||||
HTTPMethod method;
|
||||
char *uri;
|
||||
Node *headers;
|
||||
struct sockaddr_in addr;
|
||||
char *body;
|
||||
size_t body_len;
|
||||
} HTTPRequest;
|
||||
|
||||
typedef struct HTTPHeader {
|
||||
char *key;
|
||||
char *value;
|
||||
} HTTPHeader;
|
||||
|
||||
static const char RESPONSE_OK[] =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: ari's awesome server\r\n"
|
||||
"Content-Length: %ld\r\n"
|
||||
"\r\n"
|
||||
"%s";
|
||||
|
||||
int recvhttp(int sock, HTTPRequest *request, struct sockaddr *addr, socklen_t *addr_len);
|
||||
void free_http_request(HTTPRequest *request);
|
||||
char *http_method_string(HTTPMethod method);
|
||||
size_t list_http_headers(HTTPRequest *request, HTTPHeader **list);
|
||||
char *get_http_header(HTTPRequest *request, char *key);
|
63
src/log.c
Normal file
63
src/log.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
#include "log.h"
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
|
||||
#define TIMESTAMP_SIZE 32
|
||||
|
||||
void newlog(Logger *logger, const char *name, FILE *file) {
|
||||
logger->name = name;
|
||||
logger->file = file;
|
||||
logger->lock = 0;
|
||||
}
|
||||
|
||||
void _gettimestamp(char *result, size_t result_len) {
|
||||
time_t timer = time(NULL);
|
||||
struct tm *tm_info = localtime(&timer);
|
||||
strftime(result, result_len, "%Y-%m-%d %H:%M:%S", tm_info);
|
||||
}
|
||||
|
||||
void _logf(Logger *logger, const char *type, const char *format, va_list args) {
|
||||
while (logger->lock != 0);
|
||||
logger->lock = 1;
|
||||
|
||||
char timestamp[TIMESTAMP_SIZE] = {0};
|
||||
_gettimestamp(timestamp, TIMESTAMP_SIZE);
|
||||
|
||||
fprintf(logger->file, "[%s] [%s] ", timestamp, type);
|
||||
vfprintf(logger->file, format, args);
|
||||
fprintf(logger->file, "\n");
|
||||
|
||||
logger->lock = 0;
|
||||
}
|
||||
|
||||
void loginfo(Logger *logger, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
_logf(logger, LOG_INFO, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void logwarn(Logger *logger, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
_logf(logger, LOG_WARN, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void logerror(Logger *logger, const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
_logf(logger, LOG_ERROR, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void logdebug(Logger *logger, const char *format, ...) {
|
||||
#ifdef DEBUG
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
_logf(logger, LOG_DEBUG, format, args);
|
||||
va_end(args);
|
||||
#else
|
||||
#endif /* ifdef DEBUG */
|
||||
}
|
19
src/log.h
Normal file
19
src/log.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#define LOG_INFO "INFO"
|
||||
#define LOG_WARN "WARN"
|
||||
#define LOG_ERROR "ERROR"
|
||||
#define LOG_DEBUG "DEBUG"
|
||||
|
||||
typedef struct Logger {
|
||||
const char *name;
|
||||
FILE *file;
|
||||
char lock;
|
||||
} Logger;
|
||||
|
||||
void newlog(Logger *logger, const char *name, FILE *file);
|
||||
|
||||
void loginfo(Logger *logger, const char *format, ...);
|
||||
void logwarn(Logger *logger, const char *format, ...);
|
||||
void logerror(Logger *logger, const char *format, ...);
|
||||
void logdebug(Logger *logger, const char *format, ...);
|
158
src/main.c
Normal file
158
src/main.c
Normal file
|
@ -0,0 +1,158 @@
|
|||
#include "appstate.h"
|
||||
#include "http.h"
|
||||
|
||||
#include <_stdio.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/_types/_socklen_t.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define TIMESTAMP_SIZE 32
|
||||
|
||||
static void catch_signal(int signo) {
|
||||
if (close(app.sock)) {
|
||||
fprintf(stderr, "failed to close socket: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int serve(AppState *app) {
|
||||
struct sockaddr addr;
|
||||
socklen_t addr_len = sizeof(struct sockaddr);
|
||||
int accept_sock = accept(
|
||||
app->sock,
|
||||
&addr,
|
||||
&addr_len);
|
||||
|
||||
if (accept_sock == -1) {
|
||||
fprintf(stderr, "failed to accept socket connection: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// TODO: hand off request to thread pool
|
||||
|
||||
HTTPRequest request;
|
||||
if (recvhttp(accept_sock, &request, &addr, &addr_len) == -1) {
|
||||
fprintf(stderr, "failed to receive HTTP request\n");
|
||||
return -1;
|
||||
}
|
||||
char *straddr = inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr);
|
||||
|
||||
loginfo(app->log, "%s: %s %s [%s] (%ld bytes)",
|
||||
straddr,
|
||||
http_method_string(request.method),
|
||||
request.uri,
|
||||
get_http_header(&request, "User-Agent"),
|
||||
request.body_len);
|
||||
// printf("headers:\n");
|
||||
// HTTPHeader **headers;
|
||||
// size_t headers_len = list_http_headers(&request, headers);
|
||||
// for (size_t i = 0; i < headers_len; i++) {
|
||||
// HTTPHeader *header = headers[i];
|
||||
// printf("%s: %s\n", header->key, header->value);
|
||||
// }
|
||||
|
||||
char *response_body;
|
||||
if (asprintf(&response_body, "Received %ld bytes from you!\n", request.body_len) == -1) {
|
||||
fprintf(stderr, "failed to format response body: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *response;
|
||||
if (asprintf(&response, RESPONSE_OK, strlen(response_body), response_body) == -1) {
|
||||
fprintf(stderr, "failed to format response: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
free(response_body);
|
||||
|
||||
if (send(accept_sock, response, strlen(response), 0) == -1) {
|
||||
fprintf(stderr, "failed to send data to socket: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
free(response);
|
||||
|
||||
if (close(accept_sock) == -1) {
|
||||
fprintf(stderr, "failed to close client socket: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int create_server(struct in_addr in_addr, uint16_t port) {
|
||||
struct sockaddr_in service;
|
||||
memset(&service, 0, sizeof(struct sockaddr_in));
|
||||
service.sin_family = AF_INET;
|
||||
service.sin_addr = in_addr;
|
||||
service.sin_port = htons(port);
|
||||
|
||||
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == -1) {
|
||||
fprintf(stderr, "failed to create socket: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&service, sizeof(struct sockaddr_in)) == -1) {
|
||||
close(sock);
|
||||
fprintf(stderr, "failed to bind to port: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(sock, SOCK_QUEUE) == -1) {
|
||||
close(sock);
|
||||
fprintf(stderr, "failed to listen: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc >= 2 && strcmp(argv[1], "help") == 0) {
|
||||
printf("usage: %s <port> [address]\n", argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "usage: %s <port> [address]\n", argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (signal(SIGINT, catch_signal) == SIG_ERR) {
|
||||
fprintf(stderr, "failed to set signal handler: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int err = 0;
|
||||
in_port_t port = atoi(argv[1]);
|
||||
char *straddr;
|
||||
if (argc >= 3)
|
||||
straddr = argv[2];
|
||||
else
|
||||
straddr = "0.0.0.0";
|
||||
|
||||
struct in_addr in_address;
|
||||
inet_aton(straddr, &in_address);
|
||||
|
||||
int sock = create_server(in_address, port);
|
||||
if (sock == -1)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
app.sock = sock;
|
||||
app.log = malloc(sizeof(Logger));
|
||||
newlog(app.log, "http", stdout);
|
||||
|
||||
printf("now listening on %s:%d\n", straddr, port);
|
||||
while (serve(&app) != -1);
|
||||
|
||||
if (close(sock) == -1) {
|
||||
fprintf(stderr, "failed to close socket: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
65
src/node.c
Normal file
65
src/node.c
Normal file
|
@ -0,0 +1,65 @@
|
|||
#include "node.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
Node *add_node(Node *tree, char *key, void *data) {
|
||||
if (tree == NULL) {
|
||||
tree = malloc(sizeof(Node));
|
||||
tree->key = malloc(sizeof(char) * strlen(key));
|
||||
strcpy(tree->key, key);
|
||||
tree->data = data;
|
||||
tree->left = tree->right = NULL;
|
||||
return tree;
|
||||
}
|
||||
|
||||
if (strcmp(tree->key, key) > 0)
|
||||
tree->right = add_node(tree->right, key, data);
|
||||
else
|
||||
tree->left = add_node(tree->left, key, data);
|
||||
return tree;
|
||||
}
|
||||
|
||||
Node *get_node(Node *tree, char *key) {
|
||||
if (tree == NULL)
|
||||
return NULL;
|
||||
|
||||
int cmp = strcmp(tree->key, key);
|
||||
if (cmp == 0)
|
||||
return tree;
|
||||
else if (cmp > 0)
|
||||
return get_node(tree->right, key);
|
||||
else
|
||||
return get_node(tree->left, key);
|
||||
}
|
||||
|
||||
size_t tree_size(Node *tree) {
|
||||
if (tree == NULL)
|
||||
return 0;
|
||||
return tree_size(tree->left) + tree_size(tree->right) + 1;
|
||||
}
|
||||
|
||||
void _push_node_list(Node *tree, Node **list, size_t index) {
|
||||
if (tree == NULL)
|
||||
return;
|
||||
_push_node_list(tree->right, list, index);
|
||||
list[index + 1] = tree;
|
||||
_push_node_list(tree->left, list, index + 2);
|
||||
}
|
||||
size_t list_nodes(Node *tree, Node **list) {
|
||||
if (tree == NULL)
|
||||
return 0;
|
||||
size_t len = tree_size(tree);
|
||||
*list = malloc(sizeof(Node*) * len);
|
||||
|
||||
_push_node_list(tree, list, 0);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void free_node(Node *node) {
|
||||
free_node(node->left);
|
||||
free(node->key);
|
||||
free(node->data);
|
||||
free_node(node->right);
|
||||
free(node);
|
||||
}
|
14
src/node.h
Normal file
14
src/node.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include <stddef.h>
|
||||
|
||||
typedef struct Node {
|
||||
char *key;
|
||||
void *data;
|
||||
struct Node *left;
|
||||
struct Node *right;
|
||||
} Node;
|
||||
|
||||
Node *add_node(Node *tree, char *key, void *data);
|
||||
Node *get_node(Node *tree, char *key);
|
||||
void free_node(Node *node);
|
||||
size_t list_nodes(Node *tree, Node **list);
|
||||
size_t tree_size(Node *tree);
|
Loading…
Add table
Add a link
Reference in a new issue