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