http.c

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

C
2,402
字号
    } else        list_produce(trans->caller, trans);    return;    error:    conn_destroy(trans->conn);    trans->conn = NULL;    error(0, "Couldn't fetch <%s>", octstr_get_cstr(trans->url));    trans->status = -1;    list_produce(trans->caller, trans);}/* * Build a complete HTTP request given the host, port, path and headers.  * Add Host: and Content-Length: headers (and others that may be necessary). * Return the request as an Octstr. */static Octstr *build_request(char *method_name, Octstr *path_or_url,                              Octstr *host, long port, List *headers,                              Octstr *request_body) {    /* XXX headers missing */    Octstr *request;    int i;    request = octstr_format("%s %S HTTP/1.1\r\n",                            method_name, path_or_url);    octstr_format_append(request, "Host: %S", host);    if (port != HTTP_PORT)        octstr_format_append(request, ":%ld", port);    octstr_append(request, octstr_imm("\r\n"));    for (i = 0; headers != NULL && i < list_len(headers); ++i) {        octstr_append(request, list_get(headers, i));        octstr_append(request, octstr_imm("\r\n"));    }    octstr_append(request, octstr_imm("\r\n"));    if (request_body != NULL)        octstr_append(request, request_body);    return request;}/* * Parse the URL to get the hostname and the port to connect to and the * path within the host. * * Return -1 if the URL seems malformed. * * We assume HTTP URLs of the form specified in "3.2.2 http URL" in * RFC 2616: *  *  http_URL = "http:" "//" [ userid : password "@"] host [ ":" port ] [ abs_path [ "?" query ]]  */int parse_url(Octstr *url, Octstr **host, long *port, Octstr **path,               int *ssl, Octstr **username, Octstr **password) {    Octstr *prefix, *prefix_https;    long prefix_len;    int host_len, colon, slash, at, auth_sep = 0;    prefix = octstr_imm("http://");    prefix_https = octstr_imm("https://");    prefix_len = octstr_len(prefix);    if (octstr_case_search(url, prefix, 0) != 0) {        if (octstr_case_search(url, prefix_https, 0) == 0) {#ifdef HAVE_LIBSSL            debug("gwlib.http", 0, "HTTPS URL; Using SSL for the connection");            prefix = prefix_https;            prefix_len = octstr_len(prefix_https);              *ssl = 1;#else            error(0, "Attempt to use HTTPS <%s> but SSL not compiled in",                   octstr_get_cstr(url));            return -1;#endif        } else {            error(0, "URL <%s> doesn't start with `%s' nor `%s'",                  octstr_get_cstr(url), octstr_get_cstr(prefix),                  octstr_get_cstr(prefix_https));            return -1;        }    }    if (octstr_len(url) == prefix_len) {        error(0, "URL <%s> is malformed.", octstr_get_cstr(url));        return -1;    }    colon = octstr_search_char(url, ':', prefix_len);    slash = octstr_search_char(url, '/', prefix_len);    if (colon == prefix_len || slash == prefix_len) {        error(0, "URL <%s> is malformed.", octstr_get_cstr(url));        return -1;    }    at = octstr_search_char(url, '@', prefix_len);    if (at != -1) {        if ((slash == -1 || ( slash != -1 && at < slash))) {            auth_sep = octstr_search_char(url, ':', prefix_len);            if (auth_sep != -1 && (auth_sep < at)) {                octstr_set_char(url, auth_sep, '@');                colon = octstr_search_char(url, ':', prefix_len);            }        } else {            at = -1;        }    }    if (slash == -1 && colon == -1) {        /* Just the hostname, no port or path. */        host_len = octstr_len(url) - prefix_len;#ifdef HAVE_LIBSSL        *port = *ssl ? HTTPS_PORT : HTTP_PORT;#else        *port = HTTP_PORT;#endif /* HAVE_LIBSSL */    } else if (slash == -1) {        /* Port, but not path. */        host_len = colon - prefix_len;        if (octstr_parse_long(port, url, colon + 1, 10) == -1) {            error(0, "URL <%s> has malformed port number.",                  octstr_get_cstr(url));            return -1;        }    } else if (colon == -1 || colon > slash) {        /* Path, but not port. */        host_len = slash - prefix_len;#ifdef HAVE_LIBSSL        *port = *ssl ? HTTPS_PORT : HTTP_PORT;#else        *port = HTTP_PORT;#endif /* HAVE_LIBSSL */    } else if (colon < slash) {        /* Both path and port. */        host_len = colon - prefix_len;        if (octstr_parse_long(port, url, colon + 1, 10) == -1) {            error(0, "URL <%s> has malformed port number.",                  octstr_get_cstr(url));            return -1;        }    } else {        error(0, "Internal error in URL parsing logic.");        return -1;    }    if (at != -1) {        int at2, i;        at2 = octstr_search_char(url, '@', prefix_len);        *username = octstr_copy(url, prefix_len, at2 - prefix_len);        if (at2 != at)            *password = octstr_copy(url, at2 + 1, at - at2 - 1);        else            *password = NULL;        if (auth_sep != -1)            octstr_set_char(url, auth_sep, ':');        for (i = at2 + 1; i < at ; i++)            octstr_set_char(url, i, '*');        host_len = host_len - at + prefix_len - 1;        prefix_len = at + 1;    }    *host = octstr_copy(url, prefix_len, host_len);    if (slash == -1)        *path = octstr_create("/");    else        *path = octstr_copy(url, slash, octstr_len(url) - slash);    return 0;}static Connection *get_connection(HTTPServer *trans) {    Octstr *path;    Connection *conn;    Octstr *host, *our_host = NULL;    int port;    conn = NULL;    path = NULL;    /* May not be NULL if we're retrying this transaction. */    octstr_destroy(trans->host);    trans->host = NULL;    if (parse_url(trans->url, &trans->host, &trans->port, &path, &trans->ssl,                  &trans->username, &trans->password) == -1)        goto error;    if (proxy_used_for_host(trans->host)) {        host = proxy_hostname;        port = proxy_port;    } else {        host = trans->host;        port = trans->port;    }    if (trans->retrying) {#ifdef HAVE_LIBSSL        if (trans->ssl) conn = conn_open_ssl(host, port, trans->certkeyfile, our_host);        else#endif /* HAVE_LIBSSL */            conn = conn_open_tcp_nb(host, port, our_host);        debug("gwlib.http", 0, "HTTP: Opening NEW connection to `%s:%d' (fd=%d).",              octstr_get_cstr(host), port, conn_get_id(conn));    } else        conn = conn_pool_get(host, port, trans->ssl, trans->certkeyfile,                             our_host);    if (conn == NULL)        goto error;    octstr_destroy(path);    return conn;    error:    conn_destroy(conn);    octstr_destroy(path);    error(0, "Couldn't send request to <%s>", octstr_get_cstr(trans->url));    return NULL;}/* * Build and send the HTTP request. Return socket from which the * response can be read or -1 for error. */static int send_request(HTTPServer *trans) {    Octstr *path, *request;    path = NULL;    request = NULL;    octstr_destroy(trans->host);    trans->host = NULL;    if (parse_url(trans->url, &trans->host, &trans->port, &path, &trans->ssl,                  &trans->username, &trans->password) == -1)        goto error;    if (trans->username != NULL)        http_add_basic_auth(trans->request_headers, trans->username,                            trans->password);    if (proxy_used_for_host(trans->host)) {        proxy_add_authentication(trans->request_headers);        request = build_request(http_method2name(trans->method),                                trans->url, trans->host, trans->port,                                 trans->request_headers,                                 trans->request_body);    } else {        request = build_request(http_method2name(trans->method),path,                                 trans->host, trans->port,                                trans->request_headers,                                trans->request_body);    }    debug("wsp.http", 0, "HTTP: Sending request:");    octstr_dump(request, 0);    if (conn_write(trans->conn, request) == -1)        goto error;    octstr_destroy(path);    octstr_destroy(request);    return 0;    error:    conn_destroy(trans->conn);    trans->conn = NULL;    octstr_destroy(path);    octstr_destroy(request);    error(0, "Couldn't send request to <%s>", octstr_get_cstr(trans->url));    return -1;}/* * This thread starts the transaction: it connects to the server and sends * the request. It then sends the transaction to the read_response_thread * via started_requests_queue. */static void write_request_thread(void *arg) {    HTTPServer *trans;    char buf[128];        int rc;    while (run_status == running) {        trans = list_consume(pending_requests);        if (trans == NULL)            break;        gw_assert(trans->state == request_not_sent);        trans->conn = get_connection(trans);        if (trans->conn == NULL)            list_produce(trans->caller, trans);        else {            if (conn_is_connected(trans->conn) == 0) {                debug("gwlib.http", 0, "Socket connected at once");                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);                }            } else { /* Socket not connected, wait for connection */                debug("gwlib.http", 0, "Socket connecting");                trans->state = connecting;                conn_register(trans->conn, client_fdset, handle_transaction, trans);            }        }    }}static void start_client_threads(void) {    if (!client_threads_are_running) {        /*          * To be really certain, we must repeat the test, but use the         * lock first. If the test failed, however, we _know_ we've         * already initialized. This strategy of double testing avoids         * using the lock more than a few times at startup.         */        mutex_lock(client_thread_lock);        if (!client_threads_are_running) {            client_fdset = fdset_create();            gwthread_create(write_request_thread, NULL);            client_threads_are_running = 1;        }        mutex_unlock(client_thread_lock);    }}void http_start_request(HTTPCaller *caller, int method, Octstr *url, List *headers,                        Octstr *body, int follow, void *id, Octstr *certkeyfile) {    HTTPServer *trans;    int follow_remaining;    if (follow)        follow_remaining = HTTP_MAX_FOLLOW;    else        follow_remaining = 0;    trans = server_create(caller, method, url, headers, body, follow_remaining,                           certkeyfile);    if (id == NULL)        /* We don't leave this NULL so http_receive_result can use NULL         * to signal no more requests */        trans->request_id = http_start_request;    else        trans->request_id = id;    list_produce(pending_requests, trans);    start_client_threads();}void *http_receive_result(HTTPCaller *caller, int *status, Octstr **final_url,                          List **headers, Octstr **body) {    HTTPServer *trans;    void *request_id;    trans = list_consume(caller);    if (trans == NULL)        return NULL;    request_id = trans->request_id;    *status = trans->status;    if (trans->status >= 0) {        *final_url = trans->url;        *headers = trans->response->headers;        *body = trans->response->body;        trans->url = NULL;        trans->response->headers = NULL;        trans->response->body = NULL;    } else {        *final_url = NULL;        *headers = NULL;        *body = NULL;    }    server_destroy(trans);    return request_id;}int http_get_real(int method, Octstr *url, List *request_headers, Octstr **final_url,                  List **reply_headers, Octstr **reply_body) {    HTTPCaller *caller;    int status;    void *ret;    caller = http_caller_create();    http_start_request(caller, method, url, request_headers,                        NULL, 1, http_get_real, NULL);    ret = http_receive_result(caller, &status, final_url,                               reply_headers, reply_body);    http_caller_destroy(caller);    if (ret == NULL)        return -1;    return status;}static void client_init(void) {    pending_requests = list_create();    list_add_producer(pending_requests);    client_thread_lock = mutex_create();}static void client_shutdown(void) {    list_remove_producer(pending_requests);    gwthread_join_every(write_request_thread);    list_destroy(pending_requests, server_destroy);    mutex_destroy(client_thread_lock);    fdset_destroy(client_fdset);}/*********************************************************************** * HTTP server interface. *//* * Information about a client that has connected to the server we implement. */struct HTTPClient {    int port;    Connection *conn;    Octstr *ip;    enum {        reading_request_line,        reading_request,        request_is_being_handled,        sending_reply    } state;    int method;  /* HTTP_METHOD_ value */    Octstr *url;    int use_version_1_0;    int persistent_conn;    HTTPEntity *request;};

⌨️ 快捷键说明

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