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