http.c

来自「mms client」· C语言 代码 · 共 2,402 行 · 第 1/5 页

C
2,402
字号
                read_chunked_body_data(ent, conn);                break;            case reading_chunked_body_crlf:                read_chunked_body_crlf(ent, conn);                break;            case reading_chunked_body_trailer:                read_chunked_body_trailer(ent, conn);                break;            case reading_body_until_eof:                read_body_until_eof(ent, conn);                break;            case reading_body_with_length:                read_body_with_length(ent, conn);                break;            case body_error:                return -1;            case entity_done:                return 0;            default:                panic(0, "Internal error: Invalid HTTPEntity state.");        }    } while (ent->state != old_state);    /*     * If we got here, then the loop ended because a non-final state     * needed more input.     */    return 1;}/*********************************************************************** * HTTP client interface. *//* * Maximum number of HTTP redirections to follow. Making this infinite * could cause infinite looping if the redirections loop. */enum {    HTTP_MAX_FOLLOW = 5};/* * The implemented HTTP method strings * Order is sequenced by the enum in the header */static char *http_methods[] = {    "GET", "POST", "HEAD"};/* * Information about a server we've connected to. */typedef struct {    HTTPCaller *caller;    void *request_id;    int method;             /* uses enums from http.h for the HTTP methods */    Octstr *url;    List *request_headers;    Octstr *request_body;   /* NULL for GET or HEAD, non-NULL for POST */    enum {        connecting,        request_not_sent,        reading_status,        reading_entity,        transaction_done    } state;    long status;    int persistent;    HTTPEntity *response; /* Can only be NULL if status < 0 */    Connection *conn;    Octstr *host;    long port;    int retrying;    int follow_remaining;    Octstr *certkeyfile;    int ssl;    Octstr *username;   /* For basic authentication */    Octstr *password;} HTTPServer;static int send_request(HTTPServer *trans);static HTTPServer *server_create(HTTPCaller *caller, int method, Octstr *url,                                 List *headers, Octstr *body, int follow_remaining,                                 Octstr *certkeyfile) {    HTTPServer *trans;    trans = gw_malloc(sizeof(*trans));    trans->caller = caller;    trans->request_id = NULL;    trans->method = method;    trans->url = octstr_duplicate(url);    trans->request_headers = http_header_duplicate(headers);    trans->request_body = octstr_duplicate(body);    trans->state = request_not_sent;    trans->status = -1;    trans->persistent = 0;    trans->response = NULL;    trans->conn = NULL;    trans->host = NULL;    trans->port = 0;    trans->username = NULL;    trans->password = NULL;    trans->retrying = 0;    trans->follow_remaining = follow_remaining;    trans->certkeyfile = certkeyfile;    trans->ssl = 0;    return trans;}static void server_destroy(void *p) {    HTTPServer *trans;    trans = p;    octstr_destroy(trans->url);    http_destroy_headers(trans->request_headers);    trans->request_headers = NULL;    octstr_destroy(trans->request_body);    entity_destroy(trans->response);    octstr_destroy(trans->host);    gw_free(trans);}/* * Pool of open, but unused connections to servers or proxies. Key is * "servername:port", value is List with Connection objects. */static Dict *conn_pool = NULL;static Mutex *conn_pool_lock = NULL;static void conn_pool_item_destroy(void *item) {    Connection *conn;    while ((conn = list_extract_first(item)) != NULL)        conn_destroy(conn);    list_destroy(item, NULL);}static void conn_pool_init(void) {    conn_pool = dict_create(1024, conn_pool_item_destroy);    conn_pool_lock = mutex_create();}static void conn_pool_shutdown(void) {    dict_destroy(conn_pool);    mutex_destroy(conn_pool_lock);}static Octstr *conn_pool_key(Octstr *host, int port) {    return octstr_format("%S:%d", host, port);}static Connection *conn_pool_get(Octstr *host, int port, int ssl, Octstr *certkeyfile,                                 Octstr *our_host) {    Octstr *key;    List *list;    Connection *conn;    mutex_lock(conn_pool_lock);    key = conn_pool_key(host, port);    list = dict_get(conn_pool, key);    octstr_destroy(key);    if (list == NULL)        conn = NULL;    else {        while (1) {            conn = list_extract_first(list);            if (conn == NULL)                break;            /* Check whether the server has closed the connection while             * it has been in the pool. */            conn_wait(conn, 0);            if (!conn_eof(conn) && !conn_read_error(conn))                break;            conn_destroy(conn);        }    }    mutex_unlock(conn_pool_lock);    if (conn == NULL) {#ifdef HAVE_LIBSSL        if (ssl)            conn = conn_open_ssl(host, port, certkeyfile, our_host);        else#endif /* HAVE_LIBSSL */            conn = conn_open_tcp_nb(host, port, our_host);        debug("gwlib.http", 0, "HTTP: Opening connection to `%s:%d' (fd=%d).",              octstr_get_cstr(host), port, conn_get_id(conn));    } else {        debug("gwlib.http", 0, "HTTP: Reusing connection to `%s:%d' (fd=%d).",              octstr_get_cstr(host), port, conn_get_id(conn));     }    return conn;}#ifdef USE_KEEPALIVEstatic void conn_pool_put(Connection *conn, Octstr *host, int port) {    Octstr *key;    List *list;    mutex_lock(conn_pool_lock);    key = conn_pool_key(host, port);    list = dict_get(conn_pool, key);    if (list == NULL) {        list = list_create();        dict_put(conn_pool, key, list);    }    list_append(list, conn);    octstr_destroy(key);    mutex_unlock(conn_pool_lock);}#endif/* * Internal lists of completely unhandled requests and requests for which * a request has been sent but response has not yet been read. */static List *pending_requests = NULL;/* * Have background threads been started? */static Mutex *client_thread_lock = NULL;static volatile sig_atomic_t client_threads_are_running = 0;/* * Set of all connections to all servers. Used with conn_register to * do I/O on several connections with a single thread. */static FDSet *client_fdset = NULL;HTTPCaller *http_caller_create(void) {    HTTPCaller *caller;    caller = list_create();    list_add_producer(caller);    return caller;}void http_caller_destroy(HTTPCaller *caller) {    list_destroy(caller, server_destroy);}void http_caller_signal_shutdown(HTTPCaller *caller) {    list_remove_producer(caller);}static Octstr *get_redirection_location(HTTPServer *trans) {    if (trans->status < 0 || trans->follow_remaining <= 0)        return NULL;    if (trans->status != HTTP_MOVED_PERMANENTLY &&        trans->status != HTTP_FOUND && trans->status != HTTP_SEE_OTHER)        return NULL;    if (trans->response == NULL)        return NULL;    return http_header_find_first(trans->response->headers, "Location");}/* * Read and parse the status response line from an HTTP server. * Fill in trans->persistent and trans->status with the findings. * Return -1 for error, 1 for status line not yet available, 0 for OK. */static int client_read_status(HTTPServer *trans) {    Octstr *line, *version;    long space;    int ret;    line = conn_read_line(trans->conn);    if (line == NULL) {        if (conn_eof(trans->conn) || conn_read_error(trans->conn))            return -1;        return 1;    }    debug("gwlib.http", 0, "HTTP: Status line: <%s>", octstr_get_cstr(line));    space = octstr_search_char(line, ' ', 0);    if (space == -1)        goto error;    version = octstr_copy(line, 0, space);    ret = parse_http_version(version);    octstr_destroy(version);    if (ret == -1)        goto error;    trans->persistent = ret;    octstr_delete(line, 0, space + 1);    space = octstr_search_char(line, ' ', 0);    if (space == -1)        goto error;    octstr_truncate(line, space);    if (octstr_parse_long(&trans->status, line, 0, 10) == -1)        goto error;    octstr_destroy(line);    return 0;    error:    error(0, "HTTP: Malformed status line from HTTP server: <%s>",          octstr_get_cstr(line));    octstr_destroy(line);    return -1;}static int response_expectation(int method, int status) {    if (status == HTTP_NO_CONTENT ||        status == HTTP_NOT_MODIFIED ||        http_status_class(status) == HTTP_STATUS_PROVISIONAL ||        method == HTTP_METHOD_HEAD)        return expect_no_body;    else        return expect_body;}static void handle_transaction(Connection *conn, void *data) {    HTTPServer *trans;    int ret;    Octstr *h;    int rc;    char buf[128];    trans = data;    if (run_status != running) {        conn_unregister(conn);        return;    }    while (trans->state != transaction_done) {        switch (trans->state) {            case connecting:                debug("gwlib.http", 0, "Get info about connecting socket");                if (conn_get_connect_result(trans->conn) != 0) {                    debug("gwlib.http", 0, "Socket not connected");                    conn_unregister(conn);                    goto error;                }                if (trans->method == HTTP_METHOD_POST) {                    /*                      * Add a Content-Length header.  Override an existing one, if                     * necessary.  We must have an accurate one in order to use the                     * connection for more than a single request.                     */                    http_header_remove_all(trans->request_headers, "Content-Length");                    sprintf(buf, "%ld", octstr_len(trans->request_body));                    http_header_add(trans->request_headers, "Content-Length", buf);                }                /*                  * ok, this has to be an GET or HEAD request method then,                 * if it contains a body, then this is not HTTP conform, so at                 * least warn the user                  */                else if (trans->request_body != NULL) {                    warning(0, "HTTP: GET or HEAD method request contains body:");                    octstr_dump(trans->request_body, 0);                }                if ((rc = send_request(trans)) == 0) {                    trans->state = reading_status;                    conn_register(trans->conn, client_fdset, handle_transaction,                                   trans);                } else {                    list_produce(trans->caller, trans);                }                break;            case reading_status:                ret = client_read_status(trans);                if (ret < 0) {                    /*                     * Couldn't read the status from the socket. This may mean                      * that the socket had been closed by the server after an                      * idle timeout, so we close the connection and try again,                      * opening a new socket, but only once.                     */                    if (trans->retrying) {                        debug("gwlib.http",0,"Failed while retrying");                        goto error;                    } else {                        conn_destroy(trans->conn);                        trans->conn = NULL;                        trans->retrying = 1;                        trans->state = request_not_sent;                        list_produce(pending_requests, trans);                        return;                    }                } else if (ret == 0) {                    /* Got the status, go read headers and body next. */                    trans->state = reading_entity;                    trans->response =                    entity_create(response_expectation(trans->method, trans->status));                } else                    return;                break;            case reading_entity:                ret = entity_read(trans->response, conn);                if (ret < 0) {                    debug("gwlib.http",0,"Failed reading entity");                    goto error;                } else if (ret == 0 && http_status_class(trans->status)                           == HTTP_STATUS_PROVISIONAL) {                    /* This was a provisional reply; get the real one now. */                    trans->state = reading_status;                } else if (ret == 0) {                    trans->state = transaction_done;                } else {                    return;                }                break;            default:                panic(0, "Internal error: Invalid HTTPServer state.");        }    }    conn_unregister(trans->conn);    h = http_header_find_first(trans->response->headers, "Connection");    if (h != NULL && octstr_compare(h, octstr_imm("close")) == 0)        trans->persistent = 0;    octstr_destroy(h);#ifdef USE_KEEPALIVE     if (trans->persistent) {        if (proxy_used_for_host(trans->host))            conn_pool_put(trans->conn, proxy_hostname, proxy_port);        else            conn_pool_put(trans->conn, trans->host, trans->port);    } else#endif        conn_destroy(trans->conn);    trans->conn = NULL;    h = get_redirection_location(trans);    if (h != NULL) {        octstr_strip_blanks(h);        octstr_destroy(trans->url);        trans->url = h;        trans->state = request_not_sent;        trans->status = -1;        http_destroy_headers(trans->response->headers);        trans->response->headers = list_create();        octstr_destroy(trans->response->body);        trans->response->body = octstr_create("");        --trans->follow_remaining;        conn_destroy(trans->conn);        trans->conn = NULL;        list_produce(pending_requests, trans);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?