📄 http.c
字号:
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_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: 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; /* the full URL, including scheme, host, etc. */ Octstr *uri; /* the HTTP URI path only */ 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 Octstr *build_response(List *headers, Octstr *body);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->uri = NULL; 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); octstr_destroy(trans->uri); http_destroy_headers(trans->request_headers); trans->request_headers = NULL; octstr_destroy(trans->request_body); entity_destroy(trans->response); octstr_destroy(trans->host); octstr_destroy(trans->certkeyfile); octstr_destroy(trans->username); octstr_destroy(trans->password); 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_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; /* check for the redirection response codes */ if (trans->status != HTTP_MOVED_PERMANENTLY && trans->status != HTTP_FOUND && trans->status != HTTP_SEE_OTHER && trans->status != HTTP_TEMPORARY_REDIRECT) 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_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 {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -