Holding server response header data after parsing it

I'm creating a program to retrieve web content in order to learn how these things work. I would like it to be as correct as possible, ideally fully compliant.

But I'm not sure this is the right approach to the problem. Is the following structure an appropriate way to hold server response header data after parsing it?

typedef enum {
OPTIONS = 1, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT, PATCH
} HTTP_Methods;

typedef enum {
KEEP_ALIVE = 1, CLOSE
} HTTP_Connection_Field;

typedef enum {
CHUNKED = 1, COMPRESS, DEFLATE, GZIP, IDENTITY
} HTTP_Transfer_Encoding;

typedef struct {
uint8_t http_version; //e.g. HTTP/1.1 = 11, HTTP/1.0 = 10, HTTP/0.9 = 9
uint16_t response_code; //e.g. 200, 404, 500, ...
char *accept_ranges;
uint32_t age; //Non-negative, time in seconds, at least 31 bits of range
HTTP_Methods allow;
char *cache_control;
HTTP_Connection_Field connection;
char *content_encoding;
char *content_language;
uint32_t content_length;
char *content_location;
char *content_MD5;
char *content_disposition;
char *content_range;
char *content_type;
time_t date;
char *etag;
time_t expires;
time_t last_modified;
char *location;
char *p3p;
char *pragma;
char *proxy_authenticate;
char *refresh;
time_t retry_after;
char *server;
char *strict_transport_security;
char *trailer;
HTTP_Transfer_Encoding transfer_encoding;
char *vary;
char *via;
char *warning;
char *www_authenticate;


Maybe grouping the members that are not very commonly used into a different struct that is only allocated when they are in use would be a better idea?

-
Adding all of these headers explicitly into a struct doesn't work very well because it's entirely possible to send/receive non-standard headers. You really just need a map of strings to strings. –  Chris Hayes Dec 15 '13 at 5:50
Because this is C, you might want to permit a NULL value for every byte of the struct, (especially the HTTP_Methods and other enums,) so you can use a simple memset(&hrh, 0, sizeof(HTTP_Response_Header)); to initialize it. –  John Deters Dec 15 '13 at 6:19
@ChrisHayes Your comment should be submitted as an answer. –  200_success Dec 15 '13 at 7:41
@200_success Probably, but I was feeling lazy earlier. :) Expanded and posted as an answer now though. –  Chris Hayes Dec 15 '13 at 8:14

The way I would do it is by simply representing the headers as map of char* to char*. That'll be the most flexible and extensible situation, since HTTP headers are added over time, and custom headers are completely acceptable and allowed for by the standard. Any solution involving struct fields will require updating as the standard expands, and can't handle custom headers easily. (You could handle custom headers the same way as standard ones, but then you'd have the problem that their meaning is not standard, and different clients could use them to represent different things.)

The downside of making the map values be char* is that you'll have to translate into the proper data type wherever you retrieve them from the map, rather than only performing this translation when you read the request headers. This is an acceptable trade-off for the flexibility you gain, and you can find a way to encapsulate it if desired.

-

Good artists copy; great artists steal. Except you can't really steal Apache, since it's free software. ☺

Take a look at how they define struct request_rec. (Their task is to parse an HTTP request into a data structure, rather than an HTTP server response, but the idea is similar.) As you can see, they take a hybrid approach: the most relevant information, such as the URI in their case, or the content length in your case, is stored directly in the struct. However, the struct also has pointers to tables that store each header verbatim, since it's impossible for your struct to cover all of the possible headers.

Normally, I would recommend grouping your struct members by data type (putting all time_t members together, for example) to avoid wasting padding space due to alignment. However, most of the members are pointers and time_t, which should be the same size anyway, so there shouldn't be much of a difference. Try it both ways and compare sizeof HTTP_Response_Header in each case.

-
+1 Thanks, it's a very useful answer. –  2013Asker Dec 24 '13 at 5:32