summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/header.c141
-rw-r--r--src/libhttpc.c8
-rw-r--r--src/malloc.c5
-rw-r--r--src/method.c47
-rw-r--r--src/request.c88
-rw-r--r--src/response.c32
-rw-r--r--src/status.c114
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;
+}