I'm implementing a simple HTTP server in C, and this is the first part of it. Basically, it takes a string containing a "raw" HTTP request, and parse it into a struct Request
, in a more machine readable form.
It consists of 3 files: main.c, lib.h, and lib.c.
main.c
#include <stdio.h>
#include "lib.h"
int main(void) {
char *raw_request = "GET / HTTP/1.1\r\n"
"Host: localhost:8080\r\n"
"Connection: keep-alive\r\n"
"Upgrade-Insecure-Requests: 1\r\n"
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6\r\n"
"Accept-Language: en-us\r\n"
"DNT: 1\r\n"
"Accept-Encoding: gzip, deflate\r\n"
"\r\n"
"Usually GET requests don\'t have a body\r\n"
"But I don\'t care in this case :)";
struct Request *req = parse_request(raw_request);
if (req) {
printf("Method: %d\n", req->method);
printf("Request-URI: %s\n", req->url);
printf("HTTP-Version: %s\n", req->version);
puts("Headers:");
struct Header *h;
for (h=req->headers; h; h=h->next) {
printf("%32s: %s\n", h->name, h->value);
}
puts("message-body:");
puts(req->body);
}
free_request(req);
return 0;
}
lib.h
#ifndef C11CODEREVIEW_LIB_H
#define C11CODEREVIEW_LIB_H
typedef enum Method {UNSUPPORTED, GET, HEAD} Method;
typedef struct Header {
char *name;
char *value;
struct Header *next;
} Header;
typedef struct Request {
enum Method method;
char *url;
char *version;
struct Header *headers;
char *body;
} Request;
struct Request *parse_request(const char *raw);
void free_header(struct Header *h);
void free_request(struct Request *req);
#endif //C11CODEREVIEW_LIB_H
lib.c
#include <stdlib.h>
#include <string.h>
#include "lib.h"
struct Request *parse_request(const char *raw) {
struct Request *req = NULL;
req = malloc(sizeof(struct Request));
if (!req) {
return NULL;
}
memset(req, 0, sizeof(struct Request));
// Method
size_t meth_len = strcspn(raw, " ");
if (memcmp(raw, "GET", strlen("GET")) == 0) {
req->method = GET;
} else if (memcmp(raw, "HEAD", strlen("HEAD")) == 0) {
req->method = HEAD;
} else {
req->method = UNSUPPORTED;
}
raw += meth_len + 1; // move past <SP>
// Request-URI
size_t url_len = strcspn(raw, " ");
req->url = malloc(url_len + 1);
if (!req->url) {
free_request(req);
return NULL;
}
memcpy(req->url, raw, url_len);
req->url[url_len] = '\0';
raw += url_len + 1; // move past <SP>
// HTTP-Version
size_t ver_len = strcspn(raw, "\r\n");
req->version = malloc(ver_len + 1);
if (!req->version) {
free_request(req);
return NULL;
}
memcpy(req->version, raw, ver_len);
req->version[ver_len] = '\0';
raw += ver_len + 2; // move past <CR><LF>
struct Header *header = NULL, *last = NULL;
while (raw[0]!='\r' || raw[1]!='\n') {
last = header;
header = malloc(sizeof(Header));
if (!header) {
free_request(req);
return NULL;
}
// name
size_t name_len = strcspn(raw, ":");
header->name = malloc(name_len + 1);
if (!header->name) {
free_request(req);
return NULL;
}
memcpy(header->name, raw, name_len);
header->name[name_len] = '\0';
raw += name_len + 1; // move past :
while (*raw == ' ') {
raw++;
}
// value
size_t value_len = strcspn(raw, "\r\n");
header->value = malloc(value_len + 1);
if (!header->value) {
free_request(req);
return NULL;
}
memcpy(header->value, raw, value_len);
header->value[value_len] = '\0';
raw += value_len + 2; // move past <CR><LF>
// next
header->next = last;
}
req->headers = header;
raw += 2; // move past <CR><LF>
size_t body_len = strlen(raw);
req->body = malloc(body_len + 1);
if (!req->body) {
free_request(req);
return NULL;
}
memcpy(req->body, raw, body_len);
req->body[body_len] = '\0';
return req;
}
void free_header(struct Header *h) {
if (h) {
free(h->name);
free(h->value);
free_header(h->next);
free(h);
}
}
void free_request(struct Request *req) {
free(req->url);
free(req->version);
free_header(req->headers);
free(req->body);
free(req);
}
Output:
Method: 1
Request-URI: /
HTTP-Version: HTTP/1.1
Headers:
Accept-Encoding: gzip, deflate
DNT: 1
Accept-Language: en-us
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Upgrade-Insecure-Requests: 1
Connection: keep-alive
Host: localhost:8080
message-body:
Usually GET requests don't have a body
But I don't care in this case :)
Process finished with exit code 0
Question:
The
if (!foo) {
free_request(req);
return NULL;
}
structures are ugly! How can I improve it?