📄 http.c
字号:
abort_connection(conn); }}static void http_send_header(struct connection *);voidhttp_protocol_handler(struct connection *conn){ /* setcstate(conn, S_CONN); */ set_connection_timeout(conn); if (!has_keepalive_connection(conn)) { make_connection(conn, &conn->socket, http_send_header); } else { http_send_header(conn); }}voidproxy_protocol_handler(struct connection *conn){ http_protocol_handler(conn);}static void http_get_header(struct connection *);#define IS_PROXY_URI(x) ((x)->protocol == PROTOCOL_PROXY)#define connection_is_https_proxy(conn) \ (IS_PROXY_URI((conn)->uri) && (conn)->proxied_uri->protocol == PROTOCOL_HTTPS)static voidhttp_send_header(struct connection *conn){ struct http_connection_info *info; int trace = get_opt_bool("protocol.http.trace"); struct string header; unsigned char *post_data = NULL; struct auth_entry *entry; struct uri *uri = conn->proxied_uri; /* Set to the real uri */ unsigned char *optstr; int use_connect, talking_to_proxy; set_connection_timeout(conn); /* Sanity check for a host */ if (!uri || !uri->host || !*uri->host || !uri->hostlen) { http_end_request(conn, S_BAD_URL, 0); return; } info = mem_calloc(1, sizeof(*info)); if (!info) { abort_conn_with_state(conn, S_OUT_OF_MEM); return; } /* If called from HTTPS proxy connection the connection info might have * already been allocated. */ mem_free_set(&conn->info, info); info->sent_version.major = 1; info->sent_version.minor = 1; info->bl_flags = get_blacklist_flags(uri); if (info->bl_flags & SERVER_BLACKLIST_HTTP10 || get_opt_bool("protocol.http.bugs.http10")) { info->sent_version.major = 1; info->sent_version.minor = 0; } if (!init_string(&header)) { http_end_request(conn, S_OUT_OF_MEM, 0); return; } talking_to_proxy = IS_PROXY_URI(conn->uri) && !conn->socket.ssl; use_connect = connection_is_https_proxy(conn) && !conn->socket.ssl; if (trace) { add_to_string(&header, "TRACE "); } else if (use_connect) { add_to_string(&header, "CONNECT "); } else if (uri->post) { add_to_string(&header, "POST "); conn->unrestartable = 1; } else { add_to_string(&header, "GET "); } if (!talking_to_proxy) { add_char_to_string(&header, '/'); } if (use_connect) { /* Add port if it was specified or the default port */ add_uri_to_string(&header, uri, URI_HTTP_CONNECT); } else { if (connection_is_https_proxy(conn) && conn->socket.ssl) { add_url_to_http_string(&header, uri, URI_DATA); } else if (talking_to_proxy) { add_url_to_http_string(&header, uri, URI_PROXY); } else { add_url_to_http_string(&header, conn->uri, URI_DATA); } } add_to_string(&header, " HTTP/"); add_long_to_string(&header, info->sent_version.major); add_char_to_string(&header, '.'); add_long_to_string(&header, info->sent_version.minor); add_crlf_to_string(&header); add_to_string(&header, "Host: "); add_uri_to_string(&header, uri, URI_HTTP_HOST); add_crlf_to_string(&header); if (talking_to_proxy) { unsigned char *user = get_opt_str("protocol.http.proxy.user"); unsigned char *passwd = get_opt_str("protocol.http.proxy.passwd"); if (proxy_auth.digest) { unsigned char *response; int userlen = int_min(strlen(user), HTTP_AUTH_USER_MAXLEN - 1); int passwordlen = int_min(strlen(passwd), HTTP_AUTH_PASSWORD_MAXLEN - 1); if (userlen) memcpy(proxy_auth.user, user, userlen); proxy_auth.user[userlen] = '\0'; if (passwordlen) memcpy(proxy_auth.password, passwd, passwordlen); proxy_auth.password[passwordlen] = '\0'; /* FIXME: @uri is the proxied URI. Maybe the passed URI * should be the proxy URI aka conn->uri. --jonas */ response = get_http_auth_digest_response(&proxy_auth, uri); if (response) { add_to_string(&header, "Proxy-Authorization: Digest "); add_to_string(&header, response); add_crlf_to_string(&header); mem_free(response); } } else { if (user[0]) { unsigned char *proxy_data; proxy_data = straconcat(user, ":", passwd, NULL); if (proxy_data) { unsigned char *proxy_64 = base64_encode(proxy_data); if (proxy_64) { add_to_string(&header, "Proxy-Authorization: Basic "); add_to_string(&header, proxy_64); add_crlf_to_string(&header); mem_free(proxy_64); } mem_free(proxy_data); } } } } optstr = get_opt_str("protocol.http.user_agent"); if (*optstr && strcmp(optstr, " ")) { unsigned char *ustr, ts[64] = ""; add_to_string(&header, "User-Agent: "); if (!list_empty(terminals)) { unsigned int tslen = 0; struct terminal *term = terminals.prev; ulongcat(ts, &tslen, term->width, 3, 0); ts[tslen++] = 'x'; ulongcat(ts, &tslen, term->height, 3, 0); } ustr = subst_user_agent(optstr, VERSION_STRING, system_name, ts); if (ustr) { add_to_string(&header, ustr); mem_free(ustr); } add_crlf_to_string(&header); } switch (get_opt_int("protocol.http.referer.policy")) { case REFERER_NONE: /* oh well */ break; case REFERER_FAKE: optstr = get_opt_str("protocol.http.referer.fake"); if (!optstr[0]) break; add_to_string(&header, "Referer: "); add_to_string(&header, optstr); add_crlf_to_string(&header); break; case REFERER_TRUE: if (!conn->referrer) break; add_to_string(&header, "Referer: "); add_url_to_http_string(&header, conn->referrer, URI_HTTP_REFERRER); add_crlf_to_string(&header); break; case REFERER_SAME_URL: add_to_string(&header, "Referer: "); add_url_to_http_string(&header, uri, URI_HTTP_REFERRER); add_crlf_to_string(&header); break; } add_to_string(&header, "Accept: */*"); add_crlf_to_string(&header); /* TODO: Make this encoding.c function. */#if defined(CONFIG_GZIP) || defined(CONFIG_BZIP2) add_to_string(&header, "Accept-Encoding: ");#ifdef BUG_517#ifdef CONFIG_BZIP2 add_to_string(&header, "bzip2");#endif#endif#ifdef CONFIG_GZIP#ifdef BUG_517#ifdef CONFIG_BZIP2 add_to_string(&header, ", ");#endif#endif add_to_string(&header, "gzip");#endif add_crlf_to_string(&header);#endif if (!accept_charset) { init_accept_charset(); } if (!(info->bl_flags & SERVER_BLACKLIST_NO_CHARSET) && !get_opt_bool("protocol.http.bugs.accept_charset") && accept_charset) { add_to_string(&header, accept_charset); } optstr = get_opt_str("protocol.http.accept_language"); if (optstr[0]) { add_to_string(&header, "Accept-Language: "); add_to_string(&header, optstr); add_crlf_to_string(&header); }#ifdef ENABLE_NLS else if (get_opt_bool("protocol.http.accept_ui_language")) { unsigned char *code = language_to_iso639(current_language); if (code) { add_to_string(&header, "Accept-Language: "); add_to_string(&header, code); add_crlf_to_string(&header); } }#endif /* FIXME: What about post-HTTP/1.1?? --Zas */ if (HTTP_1_1(info->sent_version)) { if (!IS_PROXY_URI(conn->uri)) { add_to_string(&header, "Connection: "); } else { add_to_string(&header, "Proxy-Connection: "); } if (!uri->post || !get_opt_bool("protocol.http.bugs.post_no_keepalive")) { add_to_string(&header, "Keep-Alive"); } else { add_to_string(&header, "close"); } add_crlf_to_string(&header); } if (conn->cached) { if (!conn->cached->incomplete && conn->cached->head && conn->cached->last_modified && conn->cache_mode <= CACHE_MODE_CHECK_IF_MODIFIED) { add_to_string(&header, "If-Modified-Since: "); add_to_string(&header, conn->cached->last_modified); add_crlf_to_string(&header); } } if (conn->cache_mode >= CACHE_MODE_FORCE_RELOAD) { add_to_string(&header, "Pragma: no-cache"); add_crlf_to_string(&header); add_to_string(&header, "Cache-Control: no-cache"); add_crlf_to_string(&header); } if (conn->from || (conn->progress.start > 0)) { /* conn->from takes precedence. conn->progress.start is set only the first * time, then conn->from gets updated and in case of any retries * etc we have everything interesting in conn->from already. */ add_to_string(&header, "Range: bytes="); add_long_to_string(&header, conn->from ? conn->from : conn->progress.start); add_char_to_string(&header, '-'); add_crlf_to_string(&header); } entry = find_auth(uri); if (entry) { if (entry->digest) { unsigned char *response; response = get_http_auth_digest_response(entry, uri); if (response) { add_to_string(&header, "Authorization: Digest "); add_to_string(&header, response); add_crlf_to_string(&header); mem_free(response); } } else { /* RFC2617 section 2 [Basic Authentication Scheme] * * To receive authorization, the client sends the userid * and password, separated by a single colon (":") * character, within a base64 [7] encoded string in the * credentials. */ unsigned char *id; /* Create base64 encoded string. */ id = straconcat(entry->user, ":", entry->password, NULL); if (id) { unsigned char *base64 = base64_encode(id); mem_free_set(&id, base64); } if (id) { add_to_string(&header, "Authorization: Basic "); add_to_string(&header, id); add_crlf_to_string(&header); mem_free(id); } } } if (uri->post) { /* We search for first '\n' in uri->post to get content type * as set by get_form_uri(). This '\n' is dropped if any * and replaced by correct '\r\n' termination here. */ unsigned char *postend = strchr(uri->post, '\n'); if (postend) { add_to_string(&header, "Content-Type: "); add_bytes_to_string(&header, uri->post, postend - uri->post); add_crlf_to_string(&header); } post_data = postend ? postend + 1 : uri->post; add_to_string(&header, "Content-Length: "); add_long_to_string(&header, strlen(post_data) / 2); add_crlf_to_string(&header); }#ifdef CONFIG_COOKIES { struct string *cookies = send_cookies(uri); if (cookies) { add_to_string(&header, "Cookie: "); add_string_to_string(&header, cookies); add_crlf_to_string(&header); done_string(cookies); } }#endif add_crlf_to_string(&header); if (post_data) {#define POST_BUFFER_SIZE 4096 unsigned char *post = post_data; unsigned char buffer[POST_BUFFER_SIZE]; int n = 0; while (post[0] && post[1]) { int h1, h2; h1 = unhx(post[0]); assert(h1 >= 0 && h1 < 16); if_assert_failed h1 = 0; h2 = unhx(post[1]); assert(h2 >= 0 && h2 < 16); if_assert_failed h2 = 0; buffer[n++] = (h1<<4) + h2; post += 2; if (n == POST_BUFFER_SIZE) { add_bytes_to_string(&header, buffer, n); n = 0; } } if (n) add_bytes_to_string(&header, buffer, n);#undef POST_BUFFER_SIZE } write_to_socket(conn, &conn->socket, header.source, header.length, http_get_header); done_string(&header); set_connection_state(conn, S_SENT);}/* This function decompresses the data block given in @data (if it was * compressed), which is long @len bytes. The decompressed data block is given * back to the world as the return value and its length is stored into * @new_len. * * In this function, value of either info->chunk_remaining or info->length is * being changed (it depends on if chunked mode is used or not). * * Note that the function is still a little esotheric for me. Don't take it * lightly and don't mess with it without grave reason! If you dare to touch * this without testing the changes on slashdot, freshmeat and cvsweb * (including revision history), don't dare to send me any patches! ;) --pasky * * This function gotta die. */static unsigned char *decompress_data(struct connection *conn, unsigned char *data, int len, int *new_len){ struct http_connection_info *info = conn->info; /* to_read is number of bytes to be read from the decoder. It is 65536 * (then we are just emptying the decoder buffer as we finished the walk * through the incoming stream already) or PIPE_BUF / 2 (when we are * still walking through the stream - then we write PIPE_BUF / 2 to the * pipe and read it back to the decoder ASAP; the point is that we can't * write more than PIPE_BUF to the pipe at once, but we also have to * never let read_encoded() (gzread(), in fact) to empty the pipe - that * causes further malfunction of zlib :[ ... so we will make sure that
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -