📄 http.c
字号:
error: conn_destroy(conn); 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 *request; request = NULL; /* * we have to assume all values in trans are already set * by parse_url() before calling this. */ 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), trans->uri, 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(request); return 0; error: conn_destroy(trans->conn); trans->conn = NULL; 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); /* * get the connection to use * also calls parse_url() to populate the trans values */ 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_set_interface(const Octstr *our_host){ http_interface = octstr_duplicate(our_host);}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); octstr_destroy(http_interface); http_interface = NULL;}/*********************************************************************** * 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; unsigned long conn_time; /* store time for timeouting */ HTTPEntity *request;};static HTTPClient *client_create(int port, Connection *conn, Octstr *ip){ HTTPClient *p; #ifdef HAVE_LIBSSL if (conn_get_ssl(conn)) debug("gwlib.http", 0, "HTTP: Creating SSL-enabled HTTPClient for `%s', using cipher '%s'.", octstr_get_cstr(ip), SSL_get_cipher_version(conn_get_ssl(conn))); else#endif debug("gwlib.http", 0, "HTTP: Creating HTTPClient for `%s'.", octstr_get_cstr(ip)); p = gw_malloc(sizeof(*p)); p->port = port; p->conn = conn; p->ip = ip; p->state = reading_request_line; p->url = NULL; p->use_version_1_0 = 0; p->persistent_conn = 1; p->conn_time = time(NULL); p->request = NULL; return p;}static void client_destroy(void *client){ HTTPClient *p; if (client == NULL) return; p = client; debug("gwlib.http", 0, "HTTP: Destroying HTTPClient area %p.", p); gw_assert_allocated(p, __FILE__, __LINE__, __func__); debug("gwlib.http", 0, "HTTP: Destroying HTTPClient for `%s'.", octstr_get_cstr(p->ip)); conn_destroy(p->conn); octstr_destroy(p->ip); octstr_destroy(p->url); entity_destroy(p->request); gw_free(p);}static void client_reset(HTTPClient *p){ debug("gwlib.http", 0, "HTTP: Resetting HTTPClient for `%s'.", octstr_get_cstr(p->ip)); p->state = reading_request_line; p->conn_time = time(NULL); gw_assert(p->request == NULL);}/* * Checks whether the client connection is meant to be persistent or not. * Returns 1 for true, 0 for false. */static int client_is_persistent(List *headers, int use_version_1_0){ Octstr *h = http_header_find_first(headers, "Connection"); if (h == NULL) { return !use_version_1_0; } else { if (!use_version_1_0) { if (octstr_case_compare(h, octstr_imm("keep-alive")) == 0) { octstr_destroy(h); return 1; } else { octstr_destroy(h); return 0; } } else if (octstr_case_compare(h, octstr_imm("close")) == 0) { octstr_destroy(h); return 0; } octstr_destroy(h); } return 1;}/* * Port specific lists of clients with requests. */struct port { List *clients_with_requests; Counter *active_consumers;};static Mutex *port_mutex = NULL;static Dict *port_collection = NULL;static void port_init(void){ port_mutex = mutex_create(); port_collection = dict_create(1024, NULL);}static void port_shutdown(void){ mutex_destroy(port_mutex); dict_destroy(port_collection);}static Octstr *port_key(int port){ return octstr_format("%d", port);}static void port_add(int port){ Octstr *key; struct port *p; p = gw_malloc(sizeof(*p)); p->clients_with_requests = list_create(); list_add_producer(p->clients_with_requests); p->active_consumers = counter_create(); key = port_key(port); mutex_lock(port_mutex); dict_put(port_collection, key, p); mutex_unlock(port_mutex); octstr_destroy(key);}static void port_remove(int port){ Octstr *key; struct port *p; key = port_key(port); mutex_lock(port_mutex); p = dict_remove(port_collection, key); mutex_unlock(port_mutex); octstr_destroy(key); list_remove_producer(p->clients_with_requests); while (counter_value(p->active_consumers) > 0) gwthread_sleep(0.1); /* Reasonable use of busy waiting. */ list_destroy(p->clients_with_requests, client_destroy); counter_destroy(p->active_consumers); gw_free(p);}static void port_put_request(HTTPClient *client){ Octstr *key; struct port *p; mutex_lock(port_mutex); key = port_key(client->port); p = dict_get(port_collection, key); gw_assert(p != NULL); list_produce(p->clients_with_requests, client); octstr_destroy(key); mutex_unlock(port_mutex);}static HTTPClient *port_get_request(int port){ Octstr *key; struct port *p; HTTPClient *client; mutex_lock(port_mutex); key = port_key(port); p = dict_get(port_collection, key); octstr_destroy(key); if (p == NULL) { client = NULL; mutex_unlock(port_mutex); } else { counter_increase(p->active_consumers); mutex_unlock(port_mutex); /* Placement of this unlock is tricky. */ client = list_consume(p->clients_with_requests); counter_decrease(p->active_consumers); } return client;}/* * Maximum number of servers (ports) we have open at the same time. */enum { MAX_SERVERS = 32 };/* * Variables related to server side implementation. */static Mutex *server_thread_lock = NULL;static volatile sig_atomic_t server_thread_is_running = 0;static long server_thread_id = -1;static FDSet *server_fdset = NULL;static List *new_server_sockets = NULL;static List *closed_server_sockets = NULL;static int keep_servers_open = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -