diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/header.c | 141 | ||||
| -rw-r--r-- | src/libhttpc.c | 8 | ||||
| -rw-r--r-- | src/malloc.c | 5 | ||||
| -rw-r--r-- | src/method.c | 47 | ||||
| -rw-r--r-- | src/request.c | 88 | ||||
| -rw-r--r-- | src/response.c | 32 | ||||
| -rw-r--r-- | src/status.c | 114 |
7 files changed, 435 insertions, 0 deletions
diff --git a/src/header.c b/src/header.c new file mode 100644 index 0000000..293f435 --- /dev/null +++ b/src/header.c @@ -0,0 +1,141 @@ +#include "libhttpc.h" + +#include <ctype.h> + + +#define HEADER(X) LibHTTPC_Header_##X + +/* General headers */ +#define S_CACHE_CONTROL "Cache-Control" +#define S_CONNECTION "Connection" +#define S_DATE "Date" +#define S_PRAGMA "Pragma" +#define S_TRAILER "Trailer" +#define S_TRANSFER_ENCODING "Transfer-Encoding" +#define S_UPGRADE "Upgrade" +#define S_VIA "Via" +#define S_WARNING "Warning" +/* Request headers */ +#define S_ACCEPT "Accept" +#define S_ACCEPT_CHARSET "Accept-Charset" +#define S_ACCEPT_ENCODING "Accept-Encoding" +#define S_ACCEPT_LANGUAGE "Accept-Language" +#define S_AUTHORIZATION "Authorization" +#define S_EXPECT "Expect" +#define S_FROM "From" +#define S_HOST "Host" +#define S_IF_MATCH "If-Match" +#define S_IF_MODIFIED_SINCE "If-Modified-Since" +#define S_IF_NONE_MATCH "If-None-Match" +#define S_IF_RANGE "If-Range" +#define S_IF_UNMODIFIED_SINCE "If-Unmodified-Since" +#define S_MAX_FORWARDS "Max-Forwards" +#define S_PROXY_AUTHORIZATION "Proxy-Authorization" +#define S_RANGE "Range" +#define S_REFERER "Referer" +#define S_TE "TE" +#define S_USER_AGENT "User-Agent" +/* Response headers */ +#define S_ACCEPT_RANGES "Accept-Ranges" +#define S_AGE "Age" +#define S_ETAG "ETag" +#define S_LOCATION "Location" +#define S_PROXY_AUTHENTICATE "Proxy-Authenticate" +#define S_RETRY_AFTER "Retry-After" +#define S_SERVER "Server" +#define S_VARY "Vary" +#define S_WWW_AUTHENTICATE "WWW-Authenticate" +/* Entity headers */ +#define S_ALLOW "Allow" +#define S_CONTENT_ENCODING "Content-Encoding" +#define S_CONTENT_LANGUAGE "Content-Language" +#define S_CONTENT_LENGTH "Content-Length" +#define S_CONTENT_LOCATION "Content-Location" +#define S_CONTENT_MD5 "Content-MD5" +#define S_CONTENT_RANGE "Content-Range" +#define S_CONTENT_TYPE "Content-Type" +#define S_EXPIRES "Expires" +#define S_LAST_MODIFIED "Last-Modified" + +static int strieq(const char *a, const char *b) +{ + while (*a) + if (tolower(*a) != tolower(*b)) + return 0; + return 1; +} + +#define CHECK() \ + do \ + { \ + /* General headers */ \ + CASE(CACHE_CONTROL); \ + CASE(CONNECTION); \ + CASE(DATE); \ + CASE(PRAGMA); \ + CASE(TRAILER); \ + CASE(TRANSFER_ENCODING); \ + CASE(UPGRADE); \ + CASE(VIA); \ + CASE(WARNING); \ + /* Request headers */ \ + CASE(ACCEPT); \ + CASE(ACCEPT_CHARSET); \ + CASE(ACCEPT_ENCODING); \ + CASE(ACCEPT_LANGUAGE); \ + CASE(AUTHORIZATION); \ + CASE(EXPECT); \ + CASE(FROM); \ + CASE(HOST); \ + CASE(IF_MATCH); \ + CASE(IF_MODIFIED_SINCE); \ + CASE(IF_NONE_MATCH); \ + CASE(IF_RANGE); \ + CASE(IF_UNMODIFIED_SINCE); \ + CASE(MAX_FORWARDS); \ + CASE(PROXY_AUTHORIZATION); \ + CASE(RANGE); \ + CASE(REFERER); \ + CASE(TE); \ + CASE(USER_AGENT); \ + /* Response headers */ \ + CASE(ACCEPT_RANGES); \ + CASE(AGE); \ + CASE(ETAG); \ + CASE(LOCATION); \ + CASE(PROXY_AUTHENTICATE); \ + CASE(RETRY_AFTER); \ + CASE(SERVER); \ + CASE(VARY); \ + CASE(WWW_AUTHENTICATE); \ + /* Entity headers */ \ + CASE(ALLOW); \ + CASE(CONTENT_ENCODING); \ + CASE(CONTENT_LANGUAGE); \ + CASE(CONTENT_LENGTH); \ + CASE(CONTENT_LOCATION); \ + CASE(CONTENT_MD5); \ + CASE(CONTENT_RANGE); \ + CASE(CONTENT_TYPE); \ + CASE(EXPIRES); \ + CASE(LAST_MODIFIED); \ + } while(0) + +enum LibHTTPC_Header LibHTTPC_loadHeader(const char *header) +{ +#define CASE(X) if (!strieq((S_##X), header)) return HEADER(X) + CHECK(); +#undef CASE + return HEADER(EXTENSION_HEADER); +} + +const char *LibHTTPC_dumpHeader(enum LibHTTPC_Header header) +{ + switch (header) + { +#define CASE(X) case HEADER(X): return S_##X; + CHECK(); +#undef CASE + } + return NULL; +} diff --git a/src/libhttpc.c b/src/libhttpc.c new file mode 100644 index 0000000..2f700b2 --- /dev/null +++ b/src/libhttpc.c @@ -0,0 +1,8 @@ +#include "libhttpc.h" + + +void LibHTTPC(LibHTTPC_Malloc *malloc, LibHTTPC_Realloc *realloc) +{ + LibHTTPC_malloc = malloc; + LibHTTPC_realloc = realloc; +} diff --git a/src/malloc.c b/src/malloc.c new file mode 100644 index 0000000..a654e31 --- /dev/null +++ b/src/malloc.c @@ -0,0 +1,5 @@ +#include "libhttpc.h" + + +LibHTTPC_Malloc *LibHTTPC_malloc = NULL; +LibHTTPC_Realloc *LibHTTPC_realloc = NULL; diff --git a/src/method.c b/src/method.c new file mode 100644 index 0000000..be514bc --- /dev/null +++ b/src/method.c @@ -0,0 +1,47 @@ +#include "libhttpc.h" + +#include <string.h> + + +#define METHOD(X) LibHTTPC_Method_##X + +#define S_OPTIONS "OPTIONS" +#define S_GET "GET" +#define S_HEAD "HEAD" +#define S_POST "POST" +#define S_PUT "PUT" +#define S_DELETE "DELETE" +#define S_TRACE "TRACE" +#define S_CONNECT "CONNECT" + +#define CHECK() \ + do \ + { \ + CASE(OPTIONS); \ + CASE(GET); \ + CASE(HEAD); \ + CASE(POST); \ + CASE(PUT); \ + CASE(DELETE); \ + CASE(TRACE); \ + CASE(CONNECT); \ + } while(0) + +enum LibHTTPC_Method LibHTTPC_loadMethod(const char *method) +{ +#define CASE(X) if (!strcmp((S_##X), method)) return METHOD(X) + CHECK(); +#undef CASE + return METHOD(EXTENSION_METHOD); +} + +const char *LibHTTPC_dumpMethod(enum LibHTTPC_Method method) +{ + switch (method) + { +#define CASE(X) case METHOD(X): return S_##X + CHECK(); +#undef CASE + } + return NULL; +} diff --git a/src/request.c b/src/request.c new file mode 100644 index 0000000..b582dcf --- /dev/null +++ b/src/request.c @@ -0,0 +1,88 @@ +#include "libhttpc.h" + +#include <stddef.h> +#include <string.h> + + +struct LibHTTPC_Request *LibHTTPC_loadRequest(struct LibHTTPC_Request *request_buf, char *buf) +{ + char *next; + int selfmalloc = 0; + + if (!buf) + return NULL; + + if (!request_buf) + { + request_buf = LibHTTPC_malloc(sizeof(struct LibHTTPC_Request)); + memset(request_buf, '\0', sizeof(struct LibHTTPC_Request)); + request_buf->selfalloc = 1; + request_buf->header_count = -1; + } + + if (request_buf->header_count == -1) + { + if (!LibHTTPC_malloc || !LibHTTPC_realloc) + return NULL; + selfmalloc = 1; + request_buf->header_selfalloc = 1; + request_buf->header_count = 0; + } + + request_buf->method = request_buf->buf = buf; + + next = strchr(request_buf->method, ' '); + *next = '\0'; + request_buf->uri = next + 1; + + next = strchr(request_buf->uri, ' '); + *next = '\0'; + request_buf->version = next + 1; + + next = strstr(request_buf->version, "\r\n"); + *next++ = '\0'; + + for (size_t i = 0; (next = strstr(next, "\r\n")); ++next, ++i) + { + next += 2; + + if (strstr(next, "\r\n") == next) + break; + + if (selfmalloc) + { + ++request_buf->header_count; + + if (!request_buf->header_names) + request_buf->header_names = LibHTTPC_malloc(sizeof(char *)); + else + request_buf->header_names = LibHTTPC_realloc(request_buf->header_names, sizeof(char *)); + + if (!request_buf->header_values) + request_buf->header_values = LibHTTPC_malloc(sizeof(char *)); + else + request_buf->header_values = LibHTTPC_realloc(request_buf->header_values, sizeof(char *)); + } + if (i < request_buf->header_count) + { + request_buf->header_names[i] = next; + next = strchr(next, ':'); + *next++ = '\0'; + request_buf->header_values[i] = next; + } + } + request_buf->body = next + 2; + + return request_buf; +} + +void LibHTTPC_Request_(struct LibHTTPC_Request *request) +{ + if (request->header_selfalloc) + { + free(request->header_names); + free(request->header_values); + } + if (request->selfalloc) + free(request); +} diff --git a/src/response.c b/src/response.c new file mode 100644 index 0000000..1000da6 --- /dev/null +++ b/src/response.c @@ -0,0 +1,32 @@ +#include "libhttpc.h" + +#include <stddef.h> +#include <string.h> + + +char *LibHTTPC_dumpResponse(struct LibHTTPC_Response *response, char *buf, size_t buf_len) +{ + if (!response->version || !response->status || !response->phrase) + return NULL; + +#define append(X) strncat(buf, (X), buf_len - strlen(buf)) + append(response->version); + append(""); + append(response->status); + append(" "); + append(response->phrase); + append("\r\n"); + for (size_t i = 0; i < response->header_count; ++i) + { + append(response->header_names[i]); + append(": "); + append(response->header_values[i]); + append("\r\n"); + } + append("\r\n"); + if (response->body) + append(response->body); +#undef append + + return NULL; +} diff --git a/src/status.c b/src/status.c new file mode 100644 index 0000000..0afe981 --- /dev/null +++ b/src/status.c @@ -0,0 +1,114 @@ +#include "libhttpc.h" + +#include <string.h> + + +#define STATUS(X) LibHTTPC_Status_##X + +/* 1xx */ +#define S_CONTINUE "Continue" +#define S_SWITCHING_PROTOCOLS "Switching protocols" +/* 2xx */ +#define S_OK "OK" +#define S_CREATED "Created" +#define S_ACCEPTED "Accepted" +#define S_NONAUTHORITATIVE_INFO "Non-Authoritative Information" +#define S_NO_CONTENT "No Content" +#define S_RESET_CONTENT "Reset Content" +#define S_PARTIAL_CONTENT "Partial Content" +/* 3xx */ +#define S_MULTIPLE_CHOICES "Multiple Choices" +#define S_MOVED_PERMANENTLY "Moved Permanently" +#define S_FOUND "Found" +#define S_SEE_OTHER "See Other" +#define S_NOT_MODIFIED "Not Modified" +#define S_USE_PROXY "Use Proxy" +#define S_TEMPORARY_REDIRECT "Temporary Redirect" +/* 4xx */ +#define S_BAD_REQUEST "Bad Request" +#define S_UNAUTHORIZED "Unauthorized" +#define S_PAYMENT_REQUIRED "Payment Required" +#define S_FORBIDDEN "Forbidden" +#define S_NOT_FOUND "Not Found" +#define S_METHOD_NOT_ALLOWED "Method Not Allowed" +#define S_NOT_ACCEPTABLE "Not Acceptable" +#define S_PROXY_AUTH_REQUIRED "Proxy Authentication Required" +#define S_REQUEST_TIMEOUT "Request Timeout" +#define S_CONFLICT "Conflict" +#define S_GONE "Gone" +#define S_LENGTH_REQUIRED "Length Required" +#define S_PRECONDITION_FAILED "Precondition Failed" +#define S_ENTITY_TOO_LARGE "Request Entity Too Large" +#define S_URI_TOO_LONG "Request-URI Too Long" +#define S_UNSUPPORTED_MEDIA_TYPE "Unsupported Media Type" +#define S_RANGE_NOT_SATISFIABLE "Requested Range Not Satisfiable" +#define S_EXPECTATION_FAILED "Expectation Failed" +/* 5xx */ +#define S_INTERNAL_SERVER_ERROR "Internal Server Error" +#define S_NOT_IMPLEMENTED "Not Implemented" +#define S_BAD_GATEWAY "Bad Gateway" +#define S_SERVICE_UNAVAILABLE "Service Unavailable" +#define S_GATEWAY_TIMEOUT "Gateway Timeout" +#define S_HTTP_VER_NOT_SUPPORTED "HTTP Version Not Supported" + +#define CHECK() \ + do \ + { \ + /* 1xx */ \ + CASE(CONTINUE); \ + CASE(SWITCHING_PROTOCOLS); \ + /* 2xx */ \ + CASE(OK); \ + CASE(CREATED); \ + CASE(ACCEPTED); \ + CASE(NONAUTHORITATIVE_INFO); \ + CASE(NO_CONTENT); \ + CASE(RESET_CONTENT); \ + CASE(PARTIAL_CONTENT); \ + /* 3xx */ \ + CASE(MULTIPLE_CHOICES); \ + CASE(MOVED_PERMANENTLY); \ + CASE(FOUND); \ + CASE(SEE_OTHER); \ + CASE(NOT_MODIFIED); \ + CASE(USE_PROXY); \ + /* 306 is unused in HTTP/1.1 */ \ + CASE(TEMPORARY_REDIRECT); \ + /* 4xx */ \ + CASE(BAD_REQUEST); \ + CASE(UNAUTHORIZED); \ + CASE(PAYMENT_REQUIRED); \ + CASE(FORBIDDEN); \ + CASE(NOT_FOUND); \ + CASE(METHOD_NOT_ALLOWED); \ + CASE(NOT_ACCEPTABLE); \ + CASE(PROXY_AUTH_REQUIRED); \ + CASE(REQUEST_TIMEOUT); \ + CASE(CONFLICT); \ + CASE(GONE); \ + CASE(LENGTH_REQUIRED); \ + CASE(PRECONDITION_FAILED); \ + CASE(ENTITY_TOO_LARGE); \ + CASE(URI_TOO_LONG); \ + CASE(UNSUPPORTED_MEDIA_TYPE); \ + CASE(RANGE_NOT_SATISFIABLE); \ + CASE(EXPECTATION_FAILED); \ + /* 5xx */ \ + CASE(INTERNAL_SERVER_ERROR); \ + CASE(NOT_IMPLEMENTED); \ + CASE(BAD_GATEWAY); \ + CASE(SERVICE_UNAVAILABLE); \ + CASE(GATEWAY_TIMEOUT); \ + CASE(HTTP_VER_NOT_SUPPORTED); \ + } while(0) + +const char *LibHTTPC_dumpStatus(enum LibHTTPC_Status status) +{ + switch (status) + { +#define CASE(X) case STATUS(X): return S_##X; + CHECK(); +#undef CASE + } + return NULL; +} |