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 + -
显示快捷键?