More work on http responses
This commit is contained in:
26
src/http/basicresponses.c
Normal file
26
src/http/basicresponses.c
Normal file
@@ -0,0 +1,26 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "http.h"
|
||||
#include "../main.h"
|
||||
|
||||
http_response* response_create_builtin(uint16_t code, char* errmsg) {
|
||||
http_response *resp = http_response_new(http_response_line_new(code));
|
||||
|
||||
http_header_list_add(resp->headers, http_header_new(HEADER_CONTENT_TYPE, "text/html"), false);
|
||||
|
||||
file_map* errorpage = map_file("content/error.html");
|
||||
http_response_append_body(resp, errorpage->map);
|
||||
free_mapped_file(errorpage);
|
||||
|
||||
char buffer[1024] = {0};
|
||||
|
||||
char* title_message = http_response_line_get_message(resp->resp);
|
||||
snprintf(buffer, 1023, "%s %hu - %s", (code >= 400) ? "Error" : "Response Code", code, title_message);
|
||||
resp->body = str_replace(resp->body, "{{title}}", buffer);
|
||||
resp->body = str_replace(resp->body, "{{body_title}}", buffer);
|
||||
|
||||
resp->body = str_replace(resp->body, "{{message}}", errmsg);
|
||||
|
||||
return resp;
|
||||
}
|
||||
25
src/http/basicresponses.h
Normal file
25
src/http/basicresponses.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* File: basicresponses.h
|
||||
* Author: sam
|
||||
*
|
||||
* Created on 20 July 2014, 15:19
|
||||
*/
|
||||
|
||||
#ifndef BASICRESPONSES_H
|
||||
#define BASICRESPONSES_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "http.h"
|
||||
#include "../main.h"
|
||||
|
||||
http_response* response_create_builtin(uint16_t code, char* errmsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* BASICRESPONSES_H */
|
||||
|
||||
153
src/http/http.c
153
src/http/http.c
@@ -2,8 +2,12 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "http.h"
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include "../main.h"
|
||||
#include "../ut/utarray.h"
|
||||
#include "http.h"
|
||||
#include "basicresponses.h"
|
||||
|
||||
/*
|
||||
* METHOD_GET, METHOD_POST, METHOD_HEAD, METHOD_PUT,
|
||||
@@ -11,6 +15,8 @@
|
||||
METHOD_CONNECT, METHOD_OTHER
|
||||
*/
|
||||
|
||||
UT_icd http_header_icd = {sizeof(http_header), NULL, NULL, NULL};
|
||||
|
||||
char* http_method_getstring(http_method method, char* method_other) {
|
||||
switch(method) {
|
||||
case METHOD_GET: return "GET";
|
||||
@@ -117,12 +123,12 @@ char* http_response_line_get_message(http_response_line *resp) {
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
void http_reponse_line_delete(http_response_line *resp) {
|
||||
void http_response_line_delete(http_response_line *resp) {
|
||||
free(resp->custom_message);
|
||||
free(resp);
|
||||
}
|
||||
|
||||
http_header *http_header_new(const char* name) {
|
||||
http_header *http_header_new(const char* name, const char* content) {
|
||||
http_header *header = calloc(1, sizeof(http_header));
|
||||
if (header == NULL) {
|
||||
fatal("calloc failed");
|
||||
@@ -130,6 +136,10 @@ http_header *http_header_new(const char* name) {
|
||||
header->name = calloc(strlen(name)+1, sizeof(char));
|
||||
strcpy(header->name, name);
|
||||
|
||||
if (content != NULL) {
|
||||
http_header_append_content(header, content);
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
void http_header_append_content(http_header *header, const char* content) {
|
||||
@@ -154,25 +164,69 @@ void http_header_delete(http_header *header) {
|
||||
free(header);
|
||||
}
|
||||
|
||||
http_header_list* http_header_list_new() {
|
||||
http_header_list* list = NULL;
|
||||
utarray_new(list, &http_header_icd);
|
||||
return list;
|
||||
}
|
||||
void http_header_list_add(http_header_list* list, http_header *header, bool replace) {
|
||||
if (replace == true) {
|
||||
http_header_list_remove(list, header->name);
|
||||
}
|
||||
utarray_push_back(list, header);
|
||||
free(header);
|
||||
}
|
||||
http_header* http_header_list_get(http_header_list* list, const char* name) {
|
||||
HTTP_HEADER_FOREACH(list, elem) {
|
||||
if (strcmp(elem->name, name) == 0) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
http_header** http_header_list_getall(http_header_list* list, const char* name, size_t *out_header_count) {
|
||||
http_header **headers = NULL;
|
||||
size_t count = 0;
|
||||
HTTP_HEADER_FOREACH(list, elem) {
|
||||
if (strcmp(elem->name, name) == 0) {
|
||||
count++;
|
||||
headers = realloc(headers, count * sizeof(http_header*));
|
||||
headers[count-1] = elem;
|
||||
}
|
||||
}
|
||||
*out_header_count = count;
|
||||
return headers;
|
||||
}
|
||||
void http_header_list_remove(http_header_list *list, const char* name) {
|
||||
http_header **headers;
|
||||
size_t count;
|
||||
headers = http_header_list_getall(list, name, &count);
|
||||
for(int i=0; i<count; i++) {
|
||||
int pos = utarray_eltidx(list,headers[i]);
|
||||
free(headers[i]->name);
|
||||
free(headers[i]->content);
|
||||
utarray_erase(list, pos, 1);
|
||||
}
|
||||
free(headers);
|
||||
}
|
||||
void http_header_list_delete(http_header_list *list) {
|
||||
HTTP_HEADER_FOREACH(list, elem) {
|
||||
free(elem->name);
|
||||
free(elem->content);
|
||||
}
|
||||
utarray_free(list);
|
||||
}
|
||||
|
||||
http_request *http_request_new() {
|
||||
http_request *req = calloc(1, sizeof(http_request));
|
||||
if (req == NULL) {
|
||||
fatal("calloc failed");
|
||||
}
|
||||
req->header_count = 0;
|
||||
req->body = NULL;
|
||||
req->headers = http_header_list_new();
|
||||
req->parsestatus = PARSE_REQUESTLINE;
|
||||
return req;
|
||||
}
|
||||
void http_request_add_header(http_request *req, http_header *header) {
|
||||
req->header_count++;
|
||||
req->headers = realloc(req->headers, req->header_count * sizeof(http_header*));
|
||||
if (req->headers == NULL) {
|
||||
fatal("calloc failed");
|
||||
}
|
||||
req->headers[req->header_count-1] = header;
|
||||
}
|
||||
void http_request_apppend_body(http_request *req, const char* body) {
|
||||
void http_request_append_body(http_request *req, const char* body) {
|
||||
uint32_t bodylen = 0;
|
||||
if (req->body != NULL) {
|
||||
bodylen = strlen(req->body);
|
||||
@@ -186,10 +240,73 @@ void http_request_apppend_body(http_request *req, const char* body) {
|
||||
}
|
||||
void http_request_delete(http_request *req) {
|
||||
http_request_line_delete(req->req);
|
||||
for(int i =0; i < req->header_count; i++) {
|
||||
http_header_delete(req->headers[i]);
|
||||
}
|
||||
free(req->headers);
|
||||
http_header_list_delete(req->headers);
|
||||
free(req->body);
|
||||
free(req);
|
||||
}
|
||||
|
||||
http_response* http_response_new(http_response_line *resp) {
|
||||
http_response *response = calloc(1, sizeof(http_response));
|
||||
response->resp = resp;
|
||||
response->headers = http_header_list_new();
|
||||
response->body = NULL;
|
||||
return response;
|
||||
}
|
||||
void http_response_append_body(http_response *resp, const char* body) {
|
||||
uint32_t bodylen = 0;
|
||||
if (resp->body != NULL) {
|
||||
bodylen = strlen(resp->body);
|
||||
}
|
||||
bodylen += strlen(body) + 1;
|
||||
if (resp->body == NULL) {
|
||||
resp->body = calloc(bodylen, sizeof(char));
|
||||
} else {
|
||||
resp->body = realloc(resp->body, bodylen * sizeof(char));
|
||||
}
|
||||
strcat(resp->body, body);
|
||||
}
|
||||
void http_response_delete(http_response *resp) {
|
||||
http_response_line_delete(resp->resp);
|
||||
http_header_list_delete(resp->headers);
|
||||
free(resp->body);
|
||||
free(resp);
|
||||
}
|
||||
void http_response_write(FILE *target, http_response *resp) {
|
||||
if (resp->resp->version == HTTP10) {
|
||||
fprintf(target, "HTTP/1.0 ");
|
||||
} else if (resp->resp->version == HTTP11) {
|
||||
fprintf(target, "HTTP/1.1 ");
|
||||
}
|
||||
//Write the response line
|
||||
fprintf(target, "%hu %s\r\n", resp->resp->code, http_response_line_get_message(resp->resp));
|
||||
|
||||
if (resp->resp->code != 100) { //No additional headers for Continue messages
|
||||
//Add content length header
|
||||
uint32_t messageLength = 0;
|
||||
if (resp->body != NULL) {
|
||||
messageLength = strlen(resp->body);
|
||||
}
|
||||
char messageLengthStr[100];
|
||||
snprintf(messageLengthStr, 99, "%u", messageLength);
|
||||
http_header_list_add(resp->headers, http_header_new(HEADER_CONTENT_LENGTH, messageLengthStr), true);
|
||||
|
||||
//Add date header
|
||||
time_t timenow = time(NULL);
|
||||
struct tm * timeinfo = gmtime(&timenow);
|
||||
char dateStr[100] = {0};
|
||||
strftime(dateStr, 99, FORMAT_HEADER_DATE, timeinfo);
|
||||
http_header_list_add(resp->headers, http_header_new(HEADER_DATE, dateStr), true);
|
||||
}
|
||||
|
||||
//write headers
|
||||
HTTP_HEADER_FOREACH(resp->headers, elem) {
|
||||
fprintf(target, "%s: %s\r\n", elem->name, elem->content);
|
||||
}
|
||||
fprintf(target, "\r\n");
|
||||
|
||||
//Write the request
|
||||
//TODO: chunked support for output
|
||||
if (resp->body != NULL) {
|
||||
fprintf(target, "%s", resp->body);
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,24 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "../ut/utarray.h"
|
||||
|
||||
#define HEADER_CONTENT_TYPE "Content-Type"
|
||||
#define HEADER_CONTENT_LENGTH "Content-Length"
|
||||
#define HEADER_USER_AGENT "User-Agent"
|
||||
#define HEADER_SERVER "Server"
|
||||
#define HEADER_LAST_MODIFIED "Last-Modified"
|
||||
#define HEADER_LOCATION "Location"
|
||||
#define HEADER_HOST "Host"
|
||||
#define HEADER_TRANSFER_ENCODING "Transfer-Encoding"
|
||||
#define HEADER_DATE "Date"
|
||||
#define HEADER_CONNECTION "Connection"
|
||||
#define HEADER_IF_MODIFIED_SINCE "If-Modified-Since"
|
||||
#define HEADER_IF_UNMODIFIED_SINCE "If-Unmodified-Since"
|
||||
|
||||
#define FORMAT_HEADER_DATE "%a, %e %h %Y %T %Z"
|
||||
|
||||
typedef enum http_method {
|
||||
METHOD_GET, METHOD_POST, METHOD_HEAD, METHOD_PUT,
|
||||
METHOD_DELETE, METHOD_OPTIONS, METHOD_TRACE,
|
||||
@@ -42,6 +59,15 @@ extern "C" {
|
||||
char* content;
|
||||
} http_header;
|
||||
|
||||
typedef UT_array http_header_list;
|
||||
|
||||
extern UT_icd http_header_icd;
|
||||
|
||||
#define HTTP_HEADER_FOREACH(list, elem) \
|
||||
for ( http_header *elem= (http_header*)utarray_front(list); \
|
||||
elem!= NULL; \
|
||||
elem=(http_header*)utarray_next(list,elem))
|
||||
|
||||
typedef enum http_request_parsestatus {
|
||||
PARSE_REQUESTLINE, PARSE_HEADERS, PARSE_BODY, PARSE_DONE, PARSE_FAIL
|
||||
} http_request_parsestatus;
|
||||
@@ -49,11 +75,16 @@ extern "C" {
|
||||
typedef struct http_request {
|
||||
http_request_line *req;
|
||||
http_request_parsestatus parsestatus;
|
||||
http_header **headers;
|
||||
uint32_t header_count;
|
||||
http_header_list *headers;
|
||||
char *body;
|
||||
} http_request;
|
||||
|
||||
typedef struct http_response {
|
||||
http_response_line *resp;
|
||||
http_header_list *headers;
|
||||
char* body;
|
||||
} http_response;
|
||||
|
||||
|
||||
char* http_method_getstring(http_method method, char* method_other);
|
||||
http_method http_method_fromstring(const char* method);
|
||||
@@ -63,17 +94,26 @@ extern "C" {
|
||||
|
||||
http_response_line* http_response_line_new(uint16_t code);
|
||||
char* http_response_line_get_message(http_response_line *resp);
|
||||
void http_reponse_line_delete(http_response_line *resp);
|
||||
void http_response_line_delete(http_response_line *resp);
|
||||
|
||||
http_header* http_header_new(const char* name);
|
||||
http_header* http_header_new(const char* name, const char* content);
|
||||
void http_header_append_content(http_header *header, const char* content);
|
||||
void http_header_delete(http_header *header);
|
||||
|
||||
http_request* http_request_new();
|
||||
void http_request_add_header(http_request *req, http_header *header);
|
||||
void http_request_apppend_body(http_request *req, const char* body);
|
||||
void http_request_delete(http_request *req);
|
||||
http_header_list* http_header_list_new();
|
||||
void http_header_list_add(http_header_list* list, http_header *header, bool replace);
|
||||
http_header* http_header_list_get(http_header_list* list, const char* name);
|
||||
http_header** http_header_list_getall(http_header_list* list, const char* name, size_t *out_header_count);
|
||||
void http_header_list_remove(http_header_list *list, const char* name);
|
||||
void http_header_list_delete(http_header_list *list);
|
||||
|
||||
http_request* http_request_new();
|
||||
void http_request_append_body(http_request *req, const char* body);
|
||||
void http_request_delete(http_request *req);
|
||||
http_response* http_response_new(http_response_line *resp);
|
||||
void http_response_append_body(http_response *resp, const char* body);
|
||||
void http_response_delete(http_response *resp);
|
||||
void http_response_write(FILE *target, http_response *resp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
#include <strings.h>
|
||||
#include "../main.h"
|
||||
#include "http.h"
|
||||
#include "basicresponses.h"
|
||||
|
||||
char* parse_request(http_request *req, char *input) {
|
||||
http_response* parse_request(http_request *req, char *input) {
|
||||
size_t line_count;
|
||||
char** lines = str_splitlines(input, &line_count);
|
||||
http_response* response = NULL;
|
||||
|
||||
switch(req->parsestatus) {
|
||||
case PARSE_REQUESTLINE:
|
||||
@@ -23,19 +25,26 @@ char* parse_request(http_request *req, char *input) {
|
||||
|
||||
int count = sscanf(requestStr, "%19s%*[ \t]%1023s%*[ \t]%15s", methodStr, uriStr, versionStr);
|
||||
if (count < 3) {
|
||||
response = response_create_builtin(400, "Could not parse request line");
|
||||
req->parsestatus = PARSE_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
http_method method = http_method_fromstring(methodStr);
|
||||
if (method == METHOD_INVALID) {
|
||||
response = response_create_builtin(405, "");
|
||||
req->parsestatus = PARSE_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
http_version version;
|
||||
if (strcasecmp(versionStr, "HTTP/1.0") == 0) { version = HTTP10; }
|
||||
if (strcasecmp(versionStr, "HTTP/1.1") == 0) { version = HTTP11; }
|
||||
else if (strcasecmp(versionStr, "HTTP/1.1") == 0) { version = HTTP11; }
|
||||
else {
|
||||
response = response_create_builtin(505, "");
|
||||
req->parsestatus = PARSE_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
http_request_line *request_line = http_request_line_new(method, NULL);
|
||||
request_line->version = version;
|
||||
@@ -47,6 +56,10 @@ char* parse_request(http_request *req, char *input) {
|
||||
req->req = request_line;
|
||||
req->parsestatus = PARSE_HEADERS;
|
||||
|
||||
if (req->req->version == HTTP11) {
|
||||
response = http_response_new(http_response_line_new(100));
|
||||
}
|
||||
|
||||
break;
|
||||
case PARSE_HEADERS:
|
||||
break;
|
||||
@@ -62,5 +75,5 @@ char* parse_request(http_request *req, char *input) {
|
||||
free(lines);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return response;
|
||||
}
|
||||
@@ -15,7 +15,7 @@ extern "C" {
|
||||
#include "../ut/utstring.h"
|
||||
#include "http.h"
|
||||
|
||||
char* parse_request(http_request *req, char *input);
|
||||
http_response *parse_request(http_request *req, char *input);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
80
src/main.c
80
src/main.c
@@ -10,6 +10,10 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "ut/utlist.h"
|
||||
#include "ut/utarray.h"
|
||||
@@ -17,21 +21,23 @@
|
||||
#include "socket.h"
|
||||
#include "http/http.h"
|
||||
#include "http/request.h"
|
||||
#include "http/basicresponses.h"
|
||||
|
||||
int serverfd = 0;
|
||||
char* teststr = "GET /testing/123 HTTP/1.1\r\n";
|
||||
char* teststr = "testing testing 123 123 omg";
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
int main(int argc, char** argv) {
|
||||
/*char *test = calloc(strlen(teststr)+1, sizeof(char));
|
||||
strcpy(test, teststr);
|
||||
|
||||
http_request *req = http_request_new();
|
||||
parse_request(req, test);
|
||||
http_response* resp = response_create_builtin(404, "testing");
|
||||
|
||||
return 0;*/
|
||||
http_response_write(stdout, resp);
|
||||
|
||||
http_response_delete(resp);
|
||||
|
||||
return 0;
|
||||
skt_elem *connections = NULL;
|
||||
|
||||
serverfd = svr_create();
|
||||
@@ -179,5 +185,67 @@ char** str_splitlines(char *str, size_t *line_count) {
|
||||
i++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
file_map* map_file(const char* filename) {
|
||||
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
fatal("Failed to open file for memory mapping");
|
||||
}
|
||||
size_t size = lseek(fd, 0L, SEEK_END);
|
||||
void* map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (map == MAP_FAILED) {
|
||||
fatal("Failed to mmap file");
|
||||
}
|
||||
close(fd);
|
||||
|
||||
file_map* filemap = calloc(1, sizeof(file_map));
|
||||
filemap->map = (char*)map;
|
||||
filemap->size = size;
|
||||
return filemap;
|
||||
}
|
||||
void free_mapped_file(file_map* file) {
|
||||
if (munmap((void*)file->map, file->size) < 0) {
|
||||
warning("failed to unmap file", true);
|
||||
}
|
||||
free(file);
|
||||
}
|
||||
|
||||
char* str_replace(char *haystack, const char *search, const char *replacement) {
|
||||
|
||||
size_t haystacklen = strlen(haystack);
|
||||
size_t searchlen = strlen(search);
|
||||
size_t replacementlen = strlen(replacement);
|
||||
|
||||
char* result = haystack;
|
||||
|
||||
if (searchlen > haystacklen || searchlen == 0) {
|
||||
return result;
|
||||
}
|
||||
if (strstr(replacement, search) != NULL) {
|
||||
warning("str_replace: replacement should not contain the search criteria", false);
|
||||
}
|
||||
int count = 0;
|
||||
while(count++ < 1000) {
|
||||
char* pos = strstr(result, search);
|
||||
if (pos == NULL) {
|
||||
break;
|
||||
}
|
||||
uint32_t start = (pos - result) / sizeof(char);
|
||||
uint32_t end = start + searchlen;
|
||||
|
||||
size_t resultlen = strlen(result);
|
||||
size_t newlen = resultlen + replacementlen - searchlen;
|
||||
|
||||
char* newstr = calloc(newlen+1, sizeof(char));
|
||||
strncpy(newstr, result, start);
|
||||
strcat(newstr, replacement);
|
||||
strcat(newstr, pos+(searchlen*sizeof(char)));
|
||||
|
||||
free(result);
|
||||
result = newstr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
19
src/main.h
19
src/main.h
@@ -14,13 +14,22 @@ extern "C" {
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
int main(int argc, char** argv);
|
||||
typedef struct file_map {
|
||||
char* map;
|
||||
size_t size;
|
||||
} file_map;
|
||||
|
||||
void fatal(char* msg);
|
||||
void warning(char* msg, bool showPError);
|
||||
void info(char* msg, ...);
|
||||
int main(int argc, char** argv);
|
||||
|
||||
char** str_splitlines(char *str, size_t *line_count);
|
||||
void fatal(char* msg);
|
||||
void warning(char* msg, bool showPError);
|
||||
void info(char* msg, ...);
|
||||
|
||||
char** str_splitlines(char *str, size_t *line_count);
|
||||
char* str_replace(char *str, const char *search, const char *replacement);
|
||||
|
||||
file_map* map_file(const char* filename);
|
||||
void free_mapped_file(file_map* map);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
Reference in New Issue
Block a user