http.c
来自「mms client」· C语言 代码 · 共 2,402 行 · 第 1/5 页
C
2,402 行
/* * http.c - HTTP protocol server and client implementation * * Implements major parts of the Hypertext Transfer Protocol HTTP/1.1 (RFC 2616) * See http://www.w3.org/Protocols/rfc2616/rfc2616.txt * * Lars Wirzenius *//* XXX re-implement socket pools, with idle connection killing to save sockets *//* XXX implement http_abort *//* XXX give maximum input size *//* XXX kill http_get_real *//* XXX the proxy exceptions list should be a dict, I guess *//* XXX set maximum number of concurrent connections to same host, total? *//* XXX 100 status codes. *//* XXX stop destroying persistent connections when a request is redirected */#include <ctype.h>#include <errno.h>#include <unistd.h>#include <string.h>#include <signal.h>#include <sys/types.h>#include <sys/socket.h>#include "gwlib.h"/* comment this out if you don't want Keep-Alive HTTP requests */#define USE_KEEPALIVE 1/*********************************************************************** * Stuff used in several sub-modules. *//* * Default port to connect to for HTTP connections. */enum { HTTP_PORT = 80, HTTPS_PORT = 443};/* * Status of this module. */static enum { limbo, running, terminating } run_status = limbo;/* * Read some headers, i.e., until the first empty line (read and discard * the empty line as well). Return -1 for error, 0 for all headers read, * 1 for more headers to follow. */static int read_some_headers(Connection *conn, List *headers) { Octstr *line, *prev; if (list_len(headers) == 0) prev = NULL; else prev = list_get(headers, list_len(headers) - 1); for (;;) { line = conn_read_line(conn); if (line == NULL) { if (conn_eof(conn)) return -1; return 1; } if (octstr_len(line) == 0) { octstr_destroy(line); break; } if (isspace(octstr_get_char(line, 0)) && prev != NULL) { octstr_append(prev, line); octstr_destroy(line); } else { list_append(headers, line); prev = line; } } return 0;}/* * Check that the HTTP version string is valid. Return -1 for invalid, * 0 for version 1.0, 1 for 1.x. */static int parse_http_version(Octstr *version) { Octstr *prefix; long prefix_len; int digit; prefix = octstr_imm("HTTP/1."); prefix_len = octstr_len(prefix); if (octstr_ncompare(version, prefix, prefix_len) != 0) return -1; if (octstr_len(version) != prefix_len + 1) return -1; digit = octstr_get_char(version, prefix_len); if (!isdigit(digit)) return -1; if (digit == '0') return 0; return 1;}/*********************************************************************** * Proxy support. *//* * Data and functions needed to support proxy operations. If proxy_hostname * is NULL, no proxy is used. */static Mutex *proxy_mutex = NULL;static Octstr *proxy_hostname = NULL;static int proxy_port = 0;static Octstr *proxy_username = NULL;static Octstr *proxy_password = NULL;static List *proxy_exceptions = NULL;static void proxy_add_authentication(List *headers) { Octstr *os; if (proxy_username == NULL || proxy_password == NULL) return; os = octstr_format("%S:%S", proxy_username, proxy_password); octstr_binary_to_base64(os); octstr_strip_blanks(os); octstr_insert(os, octstr_imm("Basic "), 0); http_header_add(headers, "Proxy-Authorization", octstr_get_cstr(os)); octstr_destroy(os);}static void proxy_init(void) { proxy_mutex = mutex_create(); proxy_exceptions = list_create();}static void proxy_shutdown(void) { http_close_proxy(); mutex_destroy(proxy_mutex); proxy_mutex = NULL;}static int proxy_used_for_host(Octstr *host) { int i; mutex_lock(proxy_mutex); if (proxy_hostname == NULL) { mutex_unlock(proxy_mutex); return 0; } for (i = 0; i < list_len(proxy_exceptions); ++i) { if (octstr_compare(host, list_get(proxy_exceptions, i)) == 0) { mutex_unlock(proxy_mutex); return 0; } } mutex_unlock(proxy_mutex); return 1;}void http_use_proxy(Octstr *hostname, int port, List *exceptions, Octstr *username, Octstr *password) { Octstr *e; int i; gw_assert(run_status == running); gw_assert(hostname != NULL); gw_assert(octstr_len(hostname) > 0); gw_assert(port > 0); http_close_proxy(); mutex_lock(proxy_mutex); proxy_hostname = octstr_duplicate(hostname); proxy_port = port; proxy_exceptions = list_create(); for (i = 0; i < list_len(exceptions); ++i) { e = list_get(exceptions, i); debug("gwlib.http", 0, "HTTP: Proxy exception `%s'.", octstr_get_cstr(e)); list_append(proxy_exceptions, octstr_duplicate(e)); } proxy_username = octstr_duplicate(username); proxy_password = octstr_duplicate(password); debug("gwlib.http", 0, "Using proxy <%s:%d>", octstr_get_cstr(proxy_hostname), proxy_port); mutex_unlock(proxy_mutex);}void http_close_proxy(void) { gw_assert(run_status == running || run_status == terminating); mutex_lock(proxy_mutex); proxy_port = 0; octstr_destroy(proxy_hostname); octstr_destroy(proxy_username); octstr_destroy(proxy_password); proxy_hostname = NULL; proxy_username = NULL; proxy_password = NULL; list_destroy(proxy_exceptions, octstr_destroy_item); proxy_exceptions = NULL; mutex_unlock(proxy_mutex);}/*********************************************************************** * Common functions for reading request or result entities. *//* * Value to pass to entity_create. */enum body_expectation { /* * Message must not have a body, even if the headers indicate one. * (i.e. response to HEAD method). */ expect_no_body, /* * Message will have a body if Content-Length or Transfer-Encoding * headers are present (i.e. most request methods). */ expect_body_if_indicated, /* * Message will have a body, possibly zero-length. * (i.e. 200 OK responses to a GET method.) */ expect_body};enum entity_state { reading_headers, reading_chunked_body_len, reading_chunked_body_data, reading_chunked_body_crlf, reading_chunked_body_trailer, reading_body_until_eof, reading_body_with_length, body_error, entity_done};typedef struct { List *headers; Octstr *body; enum body_expectation expect_state; enum entity_state state; long chunked_body_chunk_len; long expected_body_len;} HTTPEntity;/* * The rules for message bodies (length and presence) are defined * in RFC2616 paragraph 4.3 and 4.4. */static void deduce_body_state(HTTPEntity *ent) { Octstr *h = NULL; if (ent->expect_state == expect_no_body) { ent->state = entity_done; return; } ent->state = body_error; /* safety net */ h = http_header_find_first(ent->headers, "Transfer-Encoding"); if (h != NULL) { octstr_strip_blanks(h); if (octstr_str_compare(h, "chunked") != 0) { error(0, "HTTP: Unknown Transfer-Encoding <%s>", octstr_get_cstr(h)); ent->state = body_error; } else { ent->state = reading_chunked_body_len; } octstr_destroy(h); return; } h = http_header_find_first(ent->headers, "Content-Length"); if (h != NULL) { if (octstr_parse_long(&ent->expected_body_len, h, 0, 10) == -1) { error(0, "HTTP: Content-Length header wrong: <%s>", octstr_get_cstr(h)); ent->state = body_error; } else { ent->state = reading_body_with_length; } octstr_destroy(h); return; } if (ent->expect_state == expect_body) ent->state = reading_body_until_eof; else ent->state = entity_done;}/* * Create a HTTPEntity structure suitable for reading the expected * result or request message and decoding the transferred entity (if any). * See the definition of enum body_expectation for the possible values * of exp. */static HTTPEntity *entity_create(enum body_expectation exp) { HTTPEntity *ent; ent = gw_malloc(sizeof(*ent)); ent->headers = http_create_empty_headers(); ent->body = octstr_create(""); ent->chunked_body_chunk_len = -1; ent->expected_body_len = -1; ent->state = reading_headers; ent->expect_state = exp; return ent;}static void entity_destroy(HTTPEntity *ent) { if (ent == NULL) return; http_destroy_headers(ent->headers); octstr_destroy(ent->body); gw_free(ent);}static void read_chunked_body_len(HTTPEntity *ent, Connection *conn) { Octstr *os; long len; os = conn_read_line(conn); if (os == NULL) { if (conn_read_error(conn) || conn_eof(conn)) ent->state = body_error; return; } if (octstr_parse_long(&len, os, 0, 16) == -1) { octstr_destroy(os); ent->state = body_error; return; } octstr_destroy(os); if (len == 0) ent->state = reading_chunked_body_trailer; else { ent->state = reading_chunked_body_data; ent->chunked_body_chunk_len = len; }}static void read_chunked_body_data(HTTPEntity *ent, Connection *conn) { Octstr *os; os = conn_read_fixed(conn, ent->chunked_body_chunk_len); if (os == NULL) { if (conn_read_error(conn) || conn_eof(conn)) ent->state = body_error; } else { octstr_append(ent->body, os); octstr_destroy(os); ent->state = reading_chunked_body_crlf; }}static void read_chunked_body_crlf(HTTPEntity *ent, Connection *conn) { Octstr *os; os = conn_read_line(conn); if (os == NULL) { if (conn_read_error(conn) || conn_eof(conn)) ent->state = body_error; } else { octstr_destroy(os); ent->state = reading_chunked_body_len; }}static void read_chunked_body_trailer(HTTPEntity *ent, Connection *conn) { int ret; ret = read_some_headers(conn, ent->headers); if (ret == -1) ent->state = body_error; if (ret == 0) ent->state = entity_done;}static void read_body_until_eof(HTTPEntity *ent, Connection *conn) { Octstr *os; while ((os = conn_read_everything(conn)) != NULL) { octstr_append(ent->body, os); octstr_destroy(os); } if (conn_read_error(conn)) ent->state = body_error; if (conn_eof(conn)) ent->state = entity_done;}static void read_body_with_length(HTTPEntity *ent, Connection *conn) { Octstr *os; os = conn_read_fixed(conn, ent->expected_body_len); if (os == NULL) return; octstr_destroy(ent->body); ent->body = os; ent->state = entity_done;}/* * Read headers and body (if any) from this connection. Return 0 if it's * complete, 1 if we expect more input, and -1 if there is something wrong. */static int entity_read(HTTPEntity *ent, Connection *conn) { int ret; enum entity_state old_state; /* * In this loop, each state will process as much input as it needs * and then switch to the next state, unless it's a final state in * which case it returns directly, or unless it needs more input. * So keep looping as long as the state changes. */ do { old_state = ent->state; switch (ent->state) { case reading_headers: ret = read_some_headers(conn, ent->headers); if (ret == 0) deduce_body_state(ent); if (ret < 0) return -1; break; case reading_chunked_body_len: read_chunked_body_len(ent, conn); break; case reading_chunked_body_data:
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?