More work on http responses

This commit is contained in:
2014-07-20 21:25:53 +01:00
parent e56f8f3715
commit b51ca6262a
14 changed files with 419 additions and 53 deletions

26
src/http/basicresponses.c Normal file
View 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
View 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 */

View File

@@ -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);
}
}

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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