Init
This commit is contained in:
commit
d7b6efe058
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
||||||
|
!CMakeLists.txt
|
||||||
|
!httplib.h
|
||||||
|
!httplib_internal.h
|
||||||
|
!httplib.c
|
||||||
|
!httputil.c
|
||||||
|
!httpser.c
|
||||||
|
!httpparse.c
|
6
CMakeLists.txt
Normal file
6
CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
cmake_minimum_required(VERSION 3.10.2)
|
||||||
|
project(simplehttplib C)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 99)
|
||||||
|
|
||||||
|
add_library(simplehttplib STATIC httplib.c httputil.c httpparse.c httpser.c httplib.h httplib_internal.h)
|
103
httplib.c
Normal file
103
httplib.c
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "httplib.h"
|
||||||
|
#include "httplib_internal.h"
|
||||||
|
|
||||||
|
int rfd;
|
||||||
|
|
||||||
|
int start_http_server(int port, http_request_handler handler) {
|
||||||
|
|
||||||
|
rfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (rfd < 0) {
|
||||||
|
return SOCKET_ERROR;
|
||||||
|
}
|
||||||
|
int option = 1;
|
||||||
|
setsockopt(rfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(int));
|
||||||
|
|
||||||
|
struct sockaddr_in server;
|
||||||
|
struct sockaddr client;
|
||||||
|
socklen_t client_len = sizeof(struct sockaddr);
|
||||||
|
|
||||||
|
server.sin_family = AF_INET;
|
||||||
|
server.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
server.sin_port = htons(port);
|
||||||
|
if (bind(rfd, (const struct sockaddr *) &server, sizeof(server)) < 0) {
|
||||||
|
return BIND_ERROR;
|
||||||
|
}
|
||||||
|
if (listen(rfd, 3) < 0) {
|
||||||
|
return LISTEN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cfd;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
cfd = accept(rfd, &client, &client_len);
|
||||||
|
if (cfd < 0) {
|
||||||
|
printf("Connection Error, waiting for next connection\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int pid = fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
|
||||||
|
http_request * req = new_request();
|
||||||
|
|
||||||
|
int result = receive(cfd, req);
|
||||||
|
|
||||||
|
http_response * res = new_response();
|
||||||
|
|
||||||
|
if (result == 1) handler(req, res); //TODO HTTP unsupported version response
|
||||||
|
else if (result == 4) set_status(res, 400, "Bad Request");
|
||||||
|
else set_status(res, 500, "Internal Server Error");
|
||||||
|
|
||||||
|
free_request(req);
|
||||||
|
|
||||||
|
respond(cfd, res);
|
||||||
|
|
||||||
|
close(cfd);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
close(cfd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int receive(int cfd, http_request * req_out) {
|
||||||
|
char buf[1024];
|
||||||
|
size_t bytes_received;
|
||||||
|
|
||||||
|
enum req_pos pos = METHOD;
|
||||||
|
int was_cr = 0;
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
struct tmp tmp;
|
||||||
|
tmp.buf = malloc(1024);
|
||||||
|
bzero(tmp.buf, 1024);
|
||||||
|
tmp.size = 1024;
|
||||||
|
tmp.pos = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
bytes_received = read(cfd, buf, 1024);
|
||||||
|
if ((result = parse_buffer(buf, bytes_received, req_out, &tmp, &pos, &was_cr))) break;
|
||||||
|
} while (bytes_received > 0);
|
||||||
|
free(tmp.buf);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void respond(int cfd, http_response * res) {
|
||||||
|
size_t res_size = response_size(res);
|
||||||
|
char * res_bytes = malloc(res_size);
|
||||||
|
int ser_err = serialize_response(res, res_bytes);
|
||||||
|
if (ser_err != 0) {
|
||||||
|
http_response * err_res = new_response();
|
||||||
|
set_status(res, 500, "Internal Server Error");
|
||||||
|
res_bytes = realloc(res_bytes, response_size(err_res));
|
||||||
|
serialize_response(err_res, res_bytes);
|
||||||
|
free(err_res);
|
||||||
|
}
|
||||||
|
free_response(res);
|
||||||
|
write(cfd, res_bytes, res_size);
|
||||||
|
free(res_bytes);
|
||||||
|
}
|
||||||
|
|
109
httplib.h
Normal file
109
httplib.h
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
#ifndef SIMPLEHTTPLIB_HTTPLIB_H
|
||||||
|
#define SIMPLEHTTPLIB_HTTPLIB_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a HTTP request method.
|
||||||
|
*/
|
||||||
|
typedef enum http_method_enum {
|
||||||
|
GET, POST, PUT, DELETE, PATCH, HEAD, CONNECT, OPTIONS, TRACE
|
||||||
|
} http_method;
|
||||||
|
/**
|
||||||
|
* Represents a HTTP header. Contains the header field key, the value and a pointer
|
||||||
|
* to the next header to form a linked list.
|
||||||
|
* All pointers of this struct must point to allocated memory or 0.
|
||||||
|
*/
|
||||||
|
typedef struct http_header_struct {
|
||||||
|
char * key;
|
||||||
|
char * value;
|
||||||
|
struct http_header_struct * next;
|
||||||
|
} http_header;
|
||||||
|
/**
|
||||||
|
* Represents a HTTP request. Contains the request method, the HTTP path, a linked list
|
||||||
|
* of headers, the query string (optional, without leading '?'), the request body (optional)
|
||||||
|
* and the content length of the body (0 if there is no body).
|
||||||
|
* All pointers of this struct must point to allocated memory or 0.
|
||||||
|
*/
|
||||||
|
typedef struct http_request_struct {
|
||||||
|
http_method method;
|
||||||
|
char * path;
|
||||||
|
http_header * headers;
|
||||||
|
char * query;
|
||||||
|
size_t content_length;
|
||||||
|
char * body;
|
||||||
|
} http_request;
|
||||||
|
/**
|
||||||
|
* Represents a HTTP response. Contains the HTTP status code, the status message,
|
||||||
|
* a linked list of headers, the response body (optional) and the content length
|
||||||
|
* of the body (0 if there is no body).
|
||||||
|
* All pointers of this struct must point to allocated memory or 0.
|
||||||
|
*/
|
||||||
|
typedef struct http_response_struct {
|
||||||
|
int status_code;
|
||||||
|
char * status_message;
|
||||||
|
http_header * headers;
|
||||||
|
size_t content_length;
|
||||||
|
char * body;
|
||||||
|
} http_response;
|
||||||
|
/**
|
||||||
|
* Function pointer type for the HTTP request handler function.
|
||||||
|
*/
|
||||||
|
typedef void (*http_request_handler) (http_request * req, http_response * res_out);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to start the HTTP server.
|
||||||
|
* @param port the port on which the server should listen
|
||||||
|
* @param handler a function pointer to the request handler function
|
||||||
|
* @return a negative integer, if the server exited with an error
|
||||||
|
*/
|
||||||
|
int start_http_server(int port, http_request_handler handler);
|
||||||
|
/**
|
||||||
|
* Helper function to add a header to a HTTP response.
|
||||||
|
* @param self a pointer to the response struct
|
||||||
|
* @param key the header field key
|
||||||
|
* @param value the header value
|
||||||
|
* @return 0 if the header was set, a negative value if the header was not valid
|
||||||
|
*/
|
||||||
|
int add_header(http_response * self, char * key, char * value);
|
||||||
|
/**
|
||||||
|
* Helper function to set the content length and to allocate the memory
|
||||||
|
* for the body of a response struct
|
||||||
|
* @param self a pointer to the response struct
|
||||||
|
* @param length the content length
|
||||||
|
*/
|
||||||
|
void set_content_length(http_response * self, size_t length);
|
||||||
|
/**
|
||||||
|
* Helper function to set the response status of a response struct
|
||||||
|
* @param self a pointer to the response struct
|
||||||
|
* @param code the status code
|
||||||
|
* @param message the status message
|
||||||
|
*/
|
||||||
|
void set_status(http_response * self, int code, char * message);
|
||||||
|
/**
|
||||||
|
* Helper function to get a header value from a HTTP.
|
||||||
|
* Returned value is allocated memory and needs to be freed, if not null.
|
||||||
|
* @param self a pointer to the request struct
|
||||||
|
* @param key the header field key
|
||||||
|
* @return the value of the header or null, if the header does not exist in the request
|
||||||
|
*/
|
||||||
|
char * get_header(http_request * self, char * key);
|
||||||
|
/**
|
||||||
|
* Helper function to get a query value from a HTTP request.
|
||||||
|
* Returned value is allocated memory and needs to be freed, if not null.
|
||||||
|
* @param self a pointer to the request struct
|
||||||
|
* @param key the query key
|
||||||
|
* @return the value of the query or null, if the query key does not exist in the request
|
||||||
|
*/
|
||||||
|
char * get_query_value(http_request * self, char * key);
|
||||||
|
/**
|
||||||
|
* Helper function to check if a header is valid.
|
||||||
|
* @param key the header field key
|
||||||
|
* @param value the header value
|
||||||
|
* @return true if the header is valid, otherwise false
|
||||||
|
*/
|
||||||
|
int is_valid_header(const char * key, const char * value);
|
||||||
|
|
||||||
|
#define SOCKET_ERROR -1;
|
||||||
|
#define BIND_ERROR -2;
|
||||||
|
#define LISTEN_ERROR -3;
|
||||||
|
|
||||||
|
#endif //SIMPLEHTTPLIB_HTTPLIB_H
|
42
httplib_internal.h
Normal file
42
httplib_internal.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef SIMPLEHTTPLIB_HTTPLIB_INTERNAL_H
|
||||||
|
#define SIMPLEHTTPLIB_HTTPLIB_INTERNAL_H
|
||||||
|
|
||||||
|
struct tmp {
|
||||||
|
char * buf;
|
||||||
|
size_t size;
|
||||||
|
size_t pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum req_pos {
|
||||||
|
METHOD,
|
||||||
|
PATH,
|
||||||
|
QUERY,
|
||||||
|
VERSION,
|
||||||
|
HEADERS,
|
||||||
|
BODY
|
||||||
|
};
|
||||||
|
|
||||||
|
void free_request(http_request * self);
|
||||||
|
void free_response(http_response * self);
|
||||||
|
|
||||||
|
http_header * new_header(char * key, char * value);
|
||||||
|
http_request * new_request();
|
||||||
|
http_response * new_response();
|
||||||
|
|
||||||
|
size_t header_string_size(http_header * header);
|
||||||
|
int header_to_string(http_header * header, char * str_out);
|
||||||
|
|
||||||
|
size_t response_size(http_response * res);
|
||||||
|
int serialize_response(http_response * res, char * str_out);
|
||||||
|
|
||||||
|
int parse_buffer(char * buf, size_t bytes, http_request * req, struct tmp * tmp, enum req_pos * pos, int * was_cr);
|
||||||
|
int parse_method(char * method_str, http_method * method_out);
|
||||||
|
int parse_header(http_request * self, char * str);
|
||||||
|
|
||||||
|
void increase_tmp(struct tmp * tmp);
|
||||||
|
void reset_tmp(struct tmp * tmp);
|
||||||
|
|
||||||
|
int receive(int cfd, http_request * req_out);
|
||||||
|
void respond(int cfd, http_response * res);
|
||||||
|
|
||||||
|
#endif //SIMPLEHTTPLIB_HTTPLIB_INTERNAL_H
|
168
httpparse.c
Normal file
168
httpparse.c
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "httplib.h"
|
||||||
|
#include "httplib_internal.h"
|
||||||
|
|
||||||
|
int parse_buffer(char * buf, size_t bytes, http_request * req, struct tmp * tmp, enum req_pos * pos, int * was_cr) {
|
||||||
|
for (int i = 0; i < bytes; i++) {
|
||||||
|
if (buf[i] == '\r') {
|
||||||
|
*was_cr = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*pos == METHOD && buf[i] == ' ') {
|
||||||
|
int res = parse_method(tmp->buf, &req->method);
|
||||||
|
if (res < 0) {
|
||||||
|
return 4;
|
||||||
|
} else {
|
||||||
|
*pos = PATH;
|
||||||
|
reset_tmp(tmp);
|
||||||
|
}
|
||||||
|
} else if (*pos == QUERY && buf[i] == ' ') {
|
||||||
|
req->query = malloc(strlen(tmp->buf) + 1);
|
||||||
|
strcpy(req->query, tmp->buf);
|
||||||
|
*pos = VERSION;
|
||||||
|
reset_tmp(tmp);
|
||||||
|
//TODO check if valid
|
||||||
|
} else if (*pos == PATH && buf[i] == ' ') {
|
||||||
|
*pos = VERSION;
|
||||||
|
req->path = malloc(strlen(tmp->buf) + 1);
|
||||||
|
strcpy(req->path, tmp->buf);
|
||||||
|
reset_tmp(tmp);
|
||||||
|
} else if (*pos == VERSION && *was_cr && buf[i] == '\n') {
|
||||||
|
*pos = HEADERS;
|
||||||
|
if (strcmp(tmp->buf, "HTTP/1.1") != 0) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
reset_tmp(tmp);
|
||||||
|
} else if (*pos == PATH && buf[i] == '?') {
|
||||||
|
*pos = QUERY;
|
||||||
|
req->path = malloc(strlen(tmp->buf) + 1);
|
||||||
|
strcpy(req->path, tmp->buf);
|
||||||
|
reset_tmp(tmp);
|
||||||
|
} else if (*pos == HEADERS && *was_cr && buf[i] == '\n') {
|
||||||
|
int res = parse_header(req, tmp->buf);
|
||||||
|
if (res < 0) {
|
||||||
|
return 4;
|
||||||
|
} else if (res > 0) {
|
||||||
|
*pos = BODY;
|
||||||
|
if (req->content_length > 0) {
|
||||||
|
req->body = malloc(req->content_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reset_tmp(tmp);
|
||||||
|
} else if (*pos != BODY) {
|
||||||
|
if (tmp->pos == tmp->size - 1) {
|
||||||
|
increase_tmp(tmp);
|
||||||
|
}
|
||||||
|
tmp->buf[tmp->pos] = buf[i];
|
||||||
|
tmp->pos++;
|
||||||
|
} else if (req->content_length > tmp->pos) {
|
||||||
|
req->body[tmp->pos] = buf[i];
|
||||||
|
tmp->pos++;
|
||||||
|
} else {
|
||||||
|
//END OF BODY
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*was_cr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*pos == BODY && req->content_length <= tmp->pos) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_method(char * method_str, http_method * method_out) {
|
||||||
|
if (!strcmp(method_str, "GET")) {
|
||||||
|
*method_out = GET;
|
||||||
|
} else if (!strcmp(method_str, "POST")) {
|
||||||
|
*method_out = POST;
|
||||||
|
} else if (!strcmp(method_str, "PUT")) {
|
||||||
|
*method_out = PUT;
|
||||||
|
} else if (!strcmp(method_str, "DELETE")) {
|
||||||
|
*method_out = DELETE;
|
||||||
|
} else if (!strcmp(method_str, "PATCH")) {
|
||||||
|
*method_out = PATCH;
|
||||||
|
} else if (!strcmp(method_str, "HEAD")) {
|
||||||
|
*method_out = HEAD;
|
||||||
|
} else if (!strcmp(method_str, "CONNECT")) {
|
||||||
|
*method_out = CONNECT;
|
||||||
|
} else if (!strcmp(method_str, "OPTIONS")) {
|
||||||
|
*method_out = OPTIONS;
|
||||||
|
} else if (!strcmp(method_str, "TRACE")) {
|
||||||
|
*method_out = TRACE;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_header(http_request * self, char * str) {
|
||||||
|
size_t len = strlen(str);
|
||||||
|
if (len == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * key = malloc(len);
|
||||||
|
bzero(key, len);
|
||||||
|
char * value = malloc(len);
|
||||||
|
bzero(value, len);
|
||||||
|
|
||||||
|
int key_end = 0;
|
||||||
|
int value_start = 0;
|
||||||
|
char c;
|
||||||
|
int i = 0;
|
||||||
|
int pos = 0;
|
||||||
|
while ((c = str[i]) != '\0') {
|
||||||
|
if (!key_end) {
|
||||||
|
if (c == ':') {
|
||||||
|
key_end = 1;
|
||||||
|
pos = 0;
|
||||||
|
} else {
|
||||||
|
key[pos] = c;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
} else if (!value_start && c != ' ') {
|
||||||
|
value_start = 1;
|
||||||
|
}
|
||||||
|
if (value_start) {
|
||||||
|
value[pos] = c;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
for (int j = pos - 1; j >= 0 ; j--) {
|
||||||
|
if (value[j] == ' ') {
|
||||||
|
value[j] = '\0';
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcasecmp("Content-Length", key) == 0) {
|
||||||
|
self->content_length = strtoul(value, 0, 10);
|
||||||
|
return 0;
|
||||||
|
} else if (is_valid_header(key, value)) {
|
||||||
|
if (self->headers == 0) {
|
||||||
|
self->headers = new_header(key, value);
|
||||||
|
} else {
|
||||||
|
http_header * run = self->headers;
|
||||||
|
while (run != 0) {
|
||||||
|
if (run->next == 0) {
|
||||||
|
run->next = new_header(key, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
run = run->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(key);
|
||||||
|
free(value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
free(key);
|
||||||
|
free(value);
|
||||||
|
return -1;
|
||||||
|
}
|
75
httpser.c
Normal file
75
httpser.c
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "httplib.h"
|
||||||
|
#include "httplib_internal.h"
|
||||||
|
|
||||||
|
int header_to_string(http_header * header, char * str_out) {
|
||||||
|
if (!is_valid_header(header->key, header->value))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
strcpy(str_out, header->key);
|
||||||
|
strcat(str_out, ":");
|
||||||
|
strcat(str_out, header->value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t header_string_size(http_header * header) {
|
||||||
|
size_t key_len = strlen(header->key);
|
||||||
|
size_t value_len = strlen(header->value);
|
||||||
|
return key_len + value_len + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int serialize_response(http_response * res, char * str_out) {
|
||||||
|
strcpy(str_out, "HTTP/1.1 ");
|
||||||
|
if (res->status_code < 100 || res->status_code > 599) return -1;
|
||||||
|
char status[4];
|
||||||
|
sprintf(status, "%i", res->status_code);
|
||||||
|
strcat(str_out, status);
|
||||||
|
strcat(str_out, " ");
|
||||||
|
strcat(str_out, res->status_message);
|
||||||
|
strcat(str_out, "\r\n");
|
||||||
|
http_header * run = res->headers;
|
||||||
|
while (run != 0) {
|
||||||
|
char * tmp = malloc(header_string_size(run));
|
||||||
|
int err = header_to_string(run, tmp);
|
||||||
|
if (err != 0) return err;
|
||||||
|
strcat(str_out, tmp);
|
||||||
|
free(tmp);
|
||||||
|
strcat(str_out, "\r\n");
|
||||||
|
run = run->next;
|
||||||
|
}
|
||||||
|
if (res->content_length > 0) {
|
||||||
|
strcat(str_out, "Content-Length:");
|
||||||
|
char * tmp = malloc(2 + (res->content_length / 10));
|
||||||
|
sprintf(tmp, "%lu", res->content_length);
|
||||||
|
strcat(str_out, tmp);
|
||||||
|
free(tmp);
|
||||||
|
strcat(str_out, "\r\n");
|
||||||
|
}
|
||||||
|
strcat(str_out, "\r\n");
|
||||||
|
size_t end_pos = 0;
|
||||||
|
while (str_out[end_pos] != '\0') end_pos++;
|
||||||
|
if (res->body != 0) {
|
||||||
|
for (size_t i = 0; i < res->content_length; i++) {
|
||||||
|
str_out[end_pos + i] = res->body[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t response_size(http_response * res) {
|
||||||
|
size_t res_line = 15 + strlen(res->status_message); //"HTTP/1.1" 000 (message)\r\n
|
||||||
|
size_t headers = 2; // \r\n
|
||||||
|
http_header * run = res->headers;
|
||||||
|
while (run != 0) {
|
||||||
|
headers += header_string_size(run) + 1; // \r\n instead of \0
|
||||||
|
run = run->next;
|
||||||
|
}
|
||||||
|
if (res->content_length > 0) {
|
||||||
|
headers += 18;
|
||||||
|
headers += res->content_length / 10;
|
||||||
|
}
|
||||||
|
size_t body = res->content_length;
|
||||||
|
return res_line + headers + body;
|
||||||
|
}
|
175
httputil.c
Normal file
175
httputil.c
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "httplib.h"
|
||||||
|
#include "httplib_internal.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
http_header * new_header(char * key, char * value) {
|
||||||
|
http_header * header = malloc(sizeof(http_header));
|
||||||
|
header->next = 0;
|
||||||
|
header->key = malloc(strlen(key)+1);
|
||||||
|
header->value = malloc(strlen(value)+1);
|
||||||
|
strcpy(header->key, key);
|
||||||
|
strcpy(header->value, value);
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_request * new_request() {
|
||||||
|
http_request * req = malloc(sizeof(http_request));
|
||||||
|
req->method = GET;
|
||||||
|
req->headers = 0;
|
||||||
|
req->path = 0;
|
||||||
|
req->query = 0;
|
||||||
|
req->content_length = 0;
|
||||||
|
req->body = 0;
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response * new_response() {
|
||||||
|
http_response * res = malloc(sizeof(http_request));
|
||||||
|
res->status_message = 0;
|
||||||
|
res->headers = 0;
|
||||||
|
res->body = 0;
|
||||||
|
res->content_length = 0;
|
||||||
|
res->status_code = 100;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int add_header(http_response * self, char * key, char * value) {
|
||||||
|
if (!is_valid_header(key, value)) return -1;
|
||||||
|
if (self->headers == 0) {
|
||||||
|
self->headers = new_header(key, value);
|
||||||
|
} else {
|
||||||
|
http_header * run = self->headers;
|
||||||
|
while (run != 0) {
|
||||||
|
if (run->next == 0) {
|
||||||
|
run->next = new_header(key, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
run = run->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * get_header(http_request * self, char * key) {
|
||||||
|
http_header * run = self->headers;
|
||||||
|
while (run != 0) {
|
||||||
|
if (!strcasecmp(key, run->key)) {
|
||||||
|
char * value = malloc(strlen(run->value)+1);
|
||||||
|
strcpy(value, run->value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
run = run->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_valid_header(const char * key, const char * value) {
|
||||||
|
for (int i = 0; key[i] != '\0'; i++) {
|
||||||
|
char c = key[i];
|
||||||
|
if ((c < 65 && c != '-') || (c > 90 && c < 97 && c != '_') || c > 122) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; value[i] != '\0'; i++) {
|
||||||
|
char c = value[i];
|
||||||
|
if (c < 32 || c > 126) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_request(http_request * self) {
|
||||||
|
if (self == 0) return;
|
||||||
|
if (self->headers != 0) {
|
||||||
|
http_header * run = self->headers;
|
||||||
|
while (run != 0) {
|
||||||
|
http_header * to_free = run;
|
||||||
|
run = run->next;
|
||||||
|
if (to_free->key != 0) free(to_free->key);
|
||||||
|
if (to_free->value != 0) free(to_free->value);
|
||||||
|
free(to_free);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self->query != 0) free(self->query);
|
||||||
|
if (self->body != 0) free(self->body);
|
||||||
|
if (self->path != 0) free(self->path);
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_response(http_response * self) {
|
||||||
|
if (self == 0) return;
|
||||||
|
if (self->headers != 0) {
|
||||||
|
http_header * run = self->headers;
|
||||||
|
while (run != 0) {
|
||||||
|
http_header * to_free = run;
|
||||||
|
run = run->next;
|
||||||
|
if (to_free->key != 0) free(to_free->key);
|
||||||
|
if (to_free->value != 0) free(to_free->value);
|
||||||
|
free(to_free);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self->status_message != 0) free(self->status_message);
|
||||||
|
if (self->body != 0) free(self->body);
|
||||||
|
free(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
char * get_query_value(http_request * self, char * key) {
|
||||||
|
if (self->query == 0) return NULL;
|
||||||
|
char * tmp = malloc(strlen(self->query)+1);
|
||||||
|
int pos = 0;
|
||||||
|
int value_start = -1;
|
||||||
|
for (int i = 0; self->query[i] != '\0'; i++) {
|
||||||
|
if (self->query[i] == '=') {
|
||||||
|
tmp[pos] = '\0';
|
||||||
|
if (!strcmp(key, tmp)) {
|
||||||
|
value_start = i+1;
|
||||||
|
}
|
||||||
|
pos = 0;
|
||||||
|
} else if (self->query[i] == '&') {
|
||||||
|
if (value_start > 0) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tmp[pos] = self->query[i];
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp[pos] = '\0';
|
||||||
|
|
||||||
|
if (value_start > 0) {
|
||||||
|
char * value = malloc(strlen(tmp)+1);
|
||||||
|
strcpy(value, tmp);
|
||||||
|
free(tmp);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
free(tmp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_content_length(http_response * self, size_t length) {
|
||||||
|
self->content_length = length;
|
||||||
|
if (length > 0)
|
||||||
|
self->body = malloc(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_status(http_response * self, int code, char * message) {
|
||||||
|
self->status_code = code;
|
||||||
|
self->status_message = malloc(strlen(message)+1);
|
||||||
|
strcpy(self->status_message, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void increase_tmp(struct tmp * tmp) {
|
||||||
|
tmp->size += 1024;
|
||||||
|
tmp->buf = realloc(tmp->buf, tmp->size);
|
||||||
|
bzero(tmp->buf + tmp->pos, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_tmp(struct tmp * tmp) {
|
||||||
|
tmp->pos = 0;
|
||||||
|
bzero(tmp->buf, tmp->size);
|
||||||
|
}
|
Loading…
Reference in a new issue