From 0cd2e735fd54f96e4765b823cded724c61572031 Mon Sep 17 00:00:00 2001 From: Nakidai Date: Sun, 24 Nov 2024 15:51:45 +0300 Subject: Add code --- .gitignore | 5 + LICENSE | 9 ++ Makefile | 28 ++++++ README | 9 ++ include/libhttpc.h | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/header.c | 141 ++++++++++++++++++++++++++++ src/libhttpc.c | 8 ++ src/malloc.c | 5 + src/method.c | 47 ++++++++++ src/request.c | 88 ++++++++++++++++++ src/response.c | 32 +++++++ src/status.c | 114 +++++++++++++++++++++++ 12 files changed, 752 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 include/libhttpc.h create mode 100644 src/header.c create mode 100644 src/libhttpc.c create mode 100644 src/malloc.c create mode 100644 src/method.c create mode 100644 src/request.c create mode 100644 src/response.c create mode 100644 src/status.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89aff01 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.a +*.so +compile_commands.json +obj/ +.cache/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6e7ddd6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +Copyright 2024 Nakidai + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0da4d45 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +OBJS += obj/header.o +OBJS += obj/libhttpc.o +OBJS += obj/malloc.o +OBJS += obj/method.o +OBJS += obj/request.o +OBJS += obj/response.o +OBJS += obj/status.o + +all: libhttpc.a libhttpc.so + +obj: + mkdir -p $@ + +obj/%.o: src/%.c | obj + ${CC} -c -std=c99 -fPIC -Iinclude -o $@ ${CFLAGS} $< + +${OBJS}: include/libhttpc.h + +libhttpc.a libhttpc.so: ${OBJS} + +libhttpc.so: + cc -shared -o $@ ${LDFLAGS} ${LDLIBS} $^ + +libhttpc.a: + ar rcs $@ $^ + +clean: + rm -f ${OBJS} libhttpc.a libhttpc.so diff --git a/README b/README new file mode 100644 index 0000000..bdf997e --- /dev/null +++ b/README @@ -0,0 +1,9 @@ +libhttpc +<======> +HTTP Library (Cute :3) + +This library provides some QoL functions for parsing requests and constructing +responses. + +Notice that libhttpc doesn't do any validation, validate data yourself or use +reverse proxy for this task. diff --git a/include/libhttpc.h b/include/libhttpc.h new file mode 100644 index 0000000..ab0a28b --- /dev/null +++ b/include/libhttpc.h @@ -0,0 +1,266 @@ +#ifndef __LIBHTTPC_H__ +#define __LIBHTTPC_H__ + +#include +#include +#include + + +/** + * Just typedef to make libhttpc_malloc definition more readable + * @see LibHTTPC_malloc + */ +typedef void *LibHTTPC_Malloc(size_t); +/** + * Just typedef to make libhttpc_realloc definition more readable + * @see libhttpc_realloc + */ +typedef void *LibHTTPC_Realloc(void *, size_t); + +/** + * Enum that contains all headers that are supported by the HTTP/1.1 + */ +enum LibHTTPC_Header +{ + /* General headers */ + LibHTTPC_Header_CACHE_CONTROL, + LibHTTPC_Header_CONNECTION, + LibHTTPC_Header_DATE, + LibHTTPC_Header_PRAGMA, + LibHTTPC_Header_TRAILER, + LibHTTPC_Header_TRANSFER_ENCODING, + LibHTTPC_Header_UPGRADE, + LibHTTPC_Header_VIA, + LibHTTPC_Header_WARNING, + /* Request headers */ + LibHTTPC_Header_ACCEPT, + LibHTTPC_Header_ACCEPT_CHARSET, + LibHTTPC_Header_ACCEPT_ENCODING, + LibHTTPC_Header_ACCEPT_LANGUAGE, + LibHTTPC_Header_AUTHORIZATION, + LibHTTPC_Header_EXPECT, + LibHTTPC_Header_FROM, + LibHTTPC_Header_HOST, + LibHTTPC_Header_IF_MATCH, + LibHTTPC_Header_IF_MODIFIED_SINCE, + LibHTTPC_Header_IF_NONE_MATCH, + LibHTTPC_Header_IF_RANGE, + LibHTTPC_Header_IF_UNMODIFIED_SINCE, + LibHTTPC_Header_MAX_FORWARDS, + LibHTTPC_Header_PROXY_AUTHORIZATION, + LibHTTPC_Header_RANGE, + LibHTTPC_Header_REFERER, + LibHTTPC_Header_TE, + LibHTTPC_Header_USER_AGENT, + /* Response headers */ + LibHTTPC_Header_ACCEPT_RANGES, + LibHTTPC_Header_AGE, + LibHTTPC_Header_ETAG, + LibHTTPC_Header_LOCATION, + LibHTTPC_Header_PROXY_AUTHENTICATE, + LibHTTPC_Header_RETRY_AFTER, + LibHTTPC_Header_SERVER, + LibHTTPC_Header_VARY, + LibHTTPC_Header_WWW_AUTHENTICATE, + /* Entity headers */ + LibHTTPC_Header_ALLOW, + LibHTTPC_Header_CONTENT_ENCODING, + LibHTTPC_Header_CONTENT_LANGUAGE, + LibHTTPC_Header_CONTENT_LENGTH, + LibHTTPC_Header_CONTENT_LOCATION, + LibHTTPC_Header_CONTENT_MD5, + LibHTTPC_Header_CONTENT_RANGE, + LibHTTPC_Header_CONTENT_TYPE, + LibHTTPC_Header_EXPIRES, + LibHTTPC_Header_LAST_MODIFIED, + + LibHTTPC_Header_EXTENSION_HEADER, +}; + +/** + * Enum that contains all methods that are supported by the HTTP/1.1 + */ +enum LibHTTPC_Method +{ + LibHTTPC_Method_OPTIONS, + LibHTTPC_Method_GET, + LibHTTPC_Method_HEAD, + LibHTTPC_Method_POST, + LibHTTPC_Method_PUT, + LibHTTPC_Method_DELETE, + LibHTTPC_Method_TRACE, + LibHTTPC_Method_CONNECT, + LibHTTPC_Method_EXTENSION_METHOD, +}; + +/** + * Enum that contains all statuses that are supported by the HTTP/1.1 + */ +enum LibHTTPC_Status +{ + /* 1xx */ + LibHTTPC_Status_CONTINUE = 100, + LibHTTPC_Status_SWITCHING_PROTOCOLS = 101, + /* 2xx */ + LibHTTPC_Status_OK = 200, + LibHTTPC_Status_CREATED = 201, + LibHTTPC_Status_ACCEPTED = 202, + LibHTTPC_Status_NONAUTHORITATIVE_INFO = 203, + LibHTTPC_Status_NO_CONTENT = 204, + LibHTTPC_Status_RESET_CONTENT = 205, + LibHTTPC_Status_PARTIAL_CONTENT = 206, + /* 3xx */ + LibHTTPC_Status_MULTIPLE_CHOICES = 300, + LibHTTPC_Status_MOVED_PERMANENTLY = 301, + LibHTTPC_Status_FOUND = 302, + LibHTTPC_Status_SEE_OTHER = 303, + LibHTTPC_Status_NOT_MODIFIED = 304, + LibHTTPC_Status_USE_PROXY = 305, + /* 306 is unused in HTTP/1.1 */ + LibHTTPC_Status_TEMPORARY_REDIRECT = 307, + /* 4xx */ + LibHTTPC_Status_BAD_REQUEST = 400, + LibHTTPC_Status_UNAUTHORIZED = 401, + LibHTTPC_Status_PAYMENT_REQUIRED = 402, + LibHTTPC_Status_FORBIDDEN = 403, + LibHTTPC_Status_NOT_FOUND = 404, + LibHTTPC_Status_METHOD_NOT_ALLOWED = 405, + LibHTTPC_Status_NOT_ACCEPTABLE = 406, + LibHTTPC_Status_PROXY_AUTH_REQUIRED = 407, + LibHTTPC_Status_REQUEST_TIMEOUT = 408, + LibHTTPC_Status_CONFLICT = 409, + LibHTTPC_Status_GONE = 410, + LibHTTPC_Status_LENGTH_REQUIRED = 411, + LibHTTPC_Status_PRECONDITION_FAILED = 412, + LibHTTPC_Status_ENTITY_TOO_LARGE = 413, + LibHTTPC_Status_URI_TOO_LONG = 414, + LibHTTPC_Status_UNSUPPORTED_MEDIA_TYPE = 415, + LibHTTPC_Status_RANGE_NOT_SATISFIABLE = 416, + LibHTTPC_Status_EXPECTATION_FAILED = 417, + /* 5xx */ + LibHTTPC_Status_INTERNAL_SERVER_ERROR = 500, + LibHTTPC_Status_NOT_IMPLEMENTED = 501, + LibHTTPC_Status_BAD_GATEWAY = 502, + LibHTTPC_Status_SERVICE_UNAVAILABLE = 503, + LibHTTPC_Status_GATEWAY_TIMEOUT = 504, + LibHTTPC_Status_HTTP_VER_NOT_SUPPORTED = 505, +}; + +/** + * Struct that contains parsed request. + * @see LibHTTPC_loadRequest + */ +struct LibHTTPC_Request +{ + char *buf; /**< Pointer to char *request */ + char *method, *uri, *version; /**< Request line */ + char **header_names; /**< \ Header parallel array */ + char **header_values; /**< / Header parallel array */ + char *body; /**< Pointer to body */ + size_t header_count; /**< Length of header array */ + int selfalloc; /**< Marker if request was allocated by the library */ + int header_selfalloc; /**< Marker if header array was allocated by the library */ +}; + +/** + * Struct that contains response + * @see LibHTTPC_dumpResponse + */ +struct LibHTTPC_Response +{ + char *buf; /**< Pointer to char *response */ + char *version, *status, *phrase; /**< Response line */ + char **header_names; /**< \ Header parallel array */ + char **header_values; /**< / Header parallel array */ + char *body; /**< Pointer to body */ + size_t header_count; /**< Length of header array */ +}; + +/** + * Malloc used by the library + */ +extern LibHTTPC_Malloc *LibHTTPC_malloc; +/** + * Realloc used by the library + */ +extern LibHTTPC_Realloc *LibHTTPC_realloc; + +/** + * Setup LibHTTPC_malloc and LibHTTPC_realloc + * @param malloc Malloc implementation + * @param realloc Realloc implementation + */ +void LibHTTPC(LibHTTPC_Malloc *malloc, LibHTTPC_Realloc *realloc); + +/** + * Parse header name + */ +enum LibHTTPC_Header LibHTTPC_loadHeader(const char *header); +/** + * Parse method name + */ +enum LibHTTPC_Method LibHTTPC_loadMethod(const char *method); + +/** + * Get name of the header by enum LibHTTPC_Header + */ +const char *LibHTTPC_dumpHeader(enum LibHTTPC_Header header); +/** + * Get name of the header by enum LibHTTPC_Method + */ +const char *LibHTTPC_dumpMethod(enum LibHTTPC_Method method); +/** + * Get name of the header by enum LibHTTPC_Status + */ +const char *LibHTTPC_dumpStatus(enum LibHTTPC_Status status); + +/** + * Load request from string. Be careful as this function will destroy buf after + * run + * @param[out] request_buf Pointer to buffer where to save request. + * + * request_buf.header_count should be either set to size of header_names and header_values or to + * -1. + * + * If (request_buf.header_count == -1), then library will allocate header array itself and set + * request_buf.header_count to amount of headers. + * + * If (request_buf == NULL), then library will allocate buffer itself using LibHTTPC_malloc and + * LibHTTPC_realloc. It will also set request_buf.header_count to -1. + * + * If function wants to use malloc or realloc, but they aren't set, it will return NULL. + * @param[in] buf Buffer that contains request to parse + * @see LibHTTPC + * @see LibHTTPC_Request_ + */ +struct LibHTTPC_Request *LibHTTPC_loadRequest(struct LibHTTPC_Request *request_buf, char *buf); +/** + * Construct C-String with resopnse that can be sent to client from + * LibHTTPC_Response struct + * @param[in] response Pointer to response + * @param[out] buf Pointer to buffer where to save formatted response + * @param buf_len Size of buf + */ +char *LibHTTPC_dumpResponse(struct LibHTTPC_Response *response, char *buf, size_t buf_len); + +/** + * Destructor for LibHTTPC_Request + * @param request Request to free + */ +void LibHTTPC_Request_(struct LibHTTPC_Request *request); + +/** + * Not implemented yet + */ +struct LibHTTPC_Request *LibHTTPC_readRequest( + int sockfd, + struct LibHTTPC_Request *request_buf, + char *buf, size_t buf_len +); + +/** + * Not implemented yet + */ +int LibHTTPC_writeResponse(int sockfd, struct LibHTTPC_Response *response); + +#endif /* __LIBHTTPC_H__ */ 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 + + +#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 + + +#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 +#include + + +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 +#include + + +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 + + +#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; +} -- cgit 1.4.1