📄 http.c
字号:
u->host, u->port, u->path,#ifdef HAVE_SSL u->scheme == SCHEME_HTTPS#else 0#endif ), rel_value); if (opt.post_data || opt.post_file_name) { request_set_header (req, "Content-Type", "application/x-www-form-urlencoded", rel_none); if (opt.post_data) post_data_size = strlen (opt.post_data); else { post_data_size = file_size (opt.post_file_name); if (post_data_size == -1) { logprintf (LOG_NOTQUIET, _("POST data file `%s' missing: %s\n"), opt.post_file_name, strerror (errno)); post_data_size = 0; } } request_set_header (req, "Content-Length", xstrdup (number_to_static_string (post_data_size)), rel_value); } /* Add the user headers. */ if (opt.user_headers) { int i; for (i = 0; opt.user_headers[i]; i++) request_set_user_header (req, opt.user_headers[i]); } retry_with_auth: /* We need to come back here when the initial attempt to retrieve without authorization header fails. (Expected to happen at least for the Digest authorization scheme.) */ keep_alive = 0; /* Establish the connection. */ if (!inhibit_keep_alive) { /* Look for a persistent connection to target host, unless a proxy is used. The exception is when SSL is in use, in which case the proxy is nothing but a passthrough to the target host, registered as a connection to the latter. */ struct url *relevant = conn;#ifdef HAVE_SSL if (u->scheme == SCHEME_HTTPS) relevant = u;#endif if (persistent_available_p (relevant->host, relevant->port,#ifdef HAVE_SSL relevant->scheme == SCHEME_HTTPS,#else 0,#endif &host_lookup_failed)) { sock = pconn.socket; using_ssl = pconn.ssl; logprintf (LOG_VERBOSE, _("Reusing existing connection to %s:%d.\n"), escnonprint (pconn.host), pconn.port); DEBUGP (("Reusing fd %d.\n", sock)); if (pconn.authorized) /* If the connection is already authorized, the "Basic" authorization added by code above is unnecessary and only hurts us. */ request_remove_header (req, "Authorization"); } } if (sock < 0) { /* In its current implementation, persistent_available_p will look up conn->host in some cases. If that lookup failed, we don't need to bother with connect_to_host. */ if (host_lookup_failed) { request_free (req); return HOSTERR; } sock = connect_to_host (conn->host, conn->port); if (sock == E_HOST) { request_free (req); return HOSTERR; } else if (sock < 0) { request_free (req); return (retryable_socket_connect_error (errno) ? CONERROR : CONIMPOSSIBLE); }#ifdef HAVE_SSL if (proxy && u->scheme == SCHEME_HTTPS) { /* When requesting SSL URLs through proxies, use the CONNECT method to request passthrough. */ struct request *connreq = request_new (); request_set_method (connreq, "CONNECT", aprintf ("%s:%d", u->host, u->port)); SET_USER_AGENT (connreq); if (proxyauth) { request_set_header (connreq, "Proxy-Authorization", proxyauth, rel_value); /* Now that PROXYAUTH is part of the CONNECT request, zero it out so we don't send proxy authorization with the regular request below. */ proxyauth = NULL; } /* Examples in rfc2817 use the Host header in CONNECT requests. I don't see how that gains anything, given that the contents of Host would be exactly the same as the contents of CONNECT. */ write_error = request_send (connreq, sock); request_free (connreq); if (write_error < 0) { CLOSE_INVALIDATE (sock); return WRITEFAILED; } head = read_http_response_head (sock); if (!head) { logprintf (LOG_VERBOSE, _("Failed reading proxy response: %s\n"), strerror (errno)); CLOSE_INVALIDATE (sock); return HERR; } message = NULL; if (!*head) { xfree (head); goto failed_tunnel; } DEBUGP (("proxy responded with: [%s]\n", head)); resp = resp_new (head); statcode = resp_status (resp, &message); resp_free (resp); xfree (head); if (statcode != 200) { failed_tunnel: logprintf (LOG_NOTQUIET, _("Proxy tunneling failed: %s"), message ? escnonprint (message) : "?"); xfree_null (message); return CONSSLERR; } xfree_null (message); /* SOCK is now *really* connected to u->host, so update CONN to reflect this. That way register_persistent will register SOCK as being connected to u->host:u->port. */ conn = u; } if (conn->scheme == SCHEME_HTTPS) { if (!ssl_connect (sock) || !ssl_check_certificate (sock, u->host)) { fd_close (sock); return CONSSLERR; } using_ssl = 1; }#endif /* HAVE_SSL */ } /* Send the request to server. */ write_error = request_send (req, sock); if (write_error >= 0) { if (opt.post_data) { DEBUGP (("[POST data: %s]\n", opt.post_data)); write_error = fd_write (sock, opt.post_data, post_data_size, -1.0); } else if (opt.post_file_name && post_data_size != 0) write_error = post_file (sock, opt.post_file_name, post_data_size); } if (write_error < 0) { CLOSE_INVALIDATE (sock); request_free (req); return WRITEFAILED; } logprintf (LOG_VERBOSE, _("%s request sent, awaiting response... "), proxy ? "Proxy" : "HTTP"); contlen = -1; contrange = 0; *dt &= ~RETROKF; head = read_http_response_head (sock); if (!head) { if (errno == 0) { logputs (LOG_NOTQUIET, _("No data received.\n")); CLOSE_INVALIDATE (sock); request_free (req); return HEOF; } else { logprintf (LOG_NOTQUIET, _("Read error (%s) in headers.\n"), strerror (errno)); CLOSE_INVALIDATE (sock); request_free (req); return HERR; } } DEBUGP (("\n---response begin---\n%s---response end---\n", head)); resp = resp_new (head); /* Check for status line. */ message = NULL; statcode = resp_status (resp, &message); if (!opt.server_response) logprintf (LOG_VERBOSE, "%2d %s\n", statcode, message ? escnonprint (message) : ""); else { logprintf (LOG_VERBOSE, "\n"); print_server_response (resp, " "); } if (!opt.ignore_length && resp_header_copy (resp, "Content-Length", hdrval, sizeof (hdrval))) { wgint parsed; errno = 0; parsed = str_to_wgint (hdrval, NULL, 10); if (parsed == WGINT_MAX && errno == ERANGE) /* Out of range. #### If Content-Length is out of range, it most likely means that the file is larger than 2G and that we're compiled without LFS. In that case we should probably refuse to even attempt to download the file. */ contlen = -1; else contlen = parsed; } /* Check for keep-alive related responses. */ if (!inhibit_keep_alive && contlen != -1) { if (resp_header_copy (resp, "Keep-Alive", NULL, 0)) keep_alive = 1; else if (resp_header_copy (resp, "Connection", hdrval, sizeof (hdrval))) { if (0 == strcasecmp (hdrval, "Keep-Alive")) keep_alive = 1; } } if (keep_alive) /* The server has promised that it will not close the connection when we're done. This means that we can register it. */ register_persistent (conn->host, conn->port, sock, using_ssl); if (statcode == HTTP_STATUS_UNAUTHORIZED) { /* Authorization is required. */ if (keep_alive && !head_only && skip_short_body (sock, contlen)) CLOSE_FINISH (sock); else CLOSE_INVALIDATE (sock); pconn.authorized = 0; if (!auth_finished && (user && passwd)) { /* IIS sends multiple copies of WWW-Authenticate, one with the value "negotiate", and other(s) with data. Loop over all the occurrences and pick the one we recognize. */ int wapos; const char *wabeg, *waend; char *www_authenticate = NULL; for (wapos = 0; (wapos = resp_header_locate (resp, "WWW-Authenticate", wapos, &wabeg, &waend)) != -1; ++wapos) if (known_authentication_scheme_p (wabeg, waend)) { BOUNDED_TO_ALLOCA (wabeg, waend, www_authenticate); break; } if (!www_authenticate) /* If the authentication header is missing or unrecognized, there's no sense in retrying. */ logputs (LOG_NOTQUIET, _("Unknown authentication scheme.\n")); else if (BEGINS_WITH (www_authenticate, "Basic")) /* If the authentication scheme is "Basic", which we send by default, there's no sense in retrying either. (This should be changed when we stop sending "Basic" data by default.) */ ; else { char *pth; pth = url_full_path (u); request_set_header (req, "Authorization", create_authorization_line (www_authenticate, user, passwd, request_method (req), pth, &auth_finished), rel_value); if (BEGINS_WITH (www_authenticate, "NTLM")) ntlm_seen = 1; xfree (pth); goto retry_with_auth; } } logputs (LOG_NOTQUIET, _("Authorization failed.\n")); request_free (req); return AUTHFAILED; } else /* statcode != HTTP_STATUS_UNAUTHORIZED */ { /* Kludge: if NTLM is used, mark the TCP connection as authorized. */ if (ntlm_seen) pconn.authorized = 1; } request_free (req); hs->statcode = statcode; if (statcode == -1) hs->error = xstrdup (_("Malformed status line")); else if (!*message) hs->error = xstrdup (_("(no description)")); else hs->error = xstrdup (message); xfree_null (message); type = resp_header_strdup (resp, "Content-Type"); if (type) { char *tmp = strchr (type, ';'); if (tmp) { while (tmp > type && ISSPACE (tmp[-1])) --tmp; *tmp = '\0'; } } hs->newloc = resp_header_strdup (resp, "Location"); hs->remote_time = resp_header_strdup (resp, "Last-Modified"); /* Handle (possibly multiple instances of) the Set-Cookie header. */ if (opt.cookies) { int scpos; const char *scbeg, *scend; /* The jar should have been created by now. */ assert (wget_cookie_jar != NULL); for (scpos = 0; (scpos = resp_header_locate (resp, "Set-Cookie", scpos, &scbeg, &scend)) != -1; ++scpos) { char *set_cookie; BOUNDED_TO_ALLOCA (scbeg, scend, set_cookie); cookie_handle_set_cookie (wget_cookie_jar, u->host, u->port, u->path, set_cookie); } } if (resp_header_copy (resp, "Content-Range", hdrval, sizeof (hdrval))) { wgint first_byte_pos, last_byte_pos, entity_length; if (parse_content_range (hdrval, &first_byte_pos, &last_byte_pos, &entity_length)) contrange = first_byte_pos; } resp_free (resp); /* 20x responses are counted among successful by default. */ if (H_20X (statcode)) *dt |= RETROKF; /* Return if redirected. */ if (H_REDIRECTED (statcode) || statcode == HTTP_STATUS_MULTIPLE_CHOICES) { /* RFC2068 says that in case of the 300 (multiple choices) response, the server can output a preferred URL through `Location' header; otherwise, the request should be treated like GET. So, if the location is set, it will be a redirection; otherwise, just proceed normally. */ if (statcode == HTTP_STATUS_MULTIPLE_CHOICES && !hs->newloc) *dt |= RETROKF; else { logprintf (LOG_VERBOSE, _("Location: %s%s\n"), hs->newloc ? escnonprint_uri (hs->newloc) : _("unspecified"), hs->newloc ? _(" [following]") : ""); if (keep_alive && !head_only && skip_short_body (sock, contlen)) CLOSE_FINISH (sock); else CLOSE_INVALIDATE (sock); xfree_null (type); return NEWLOCATION; } } /* If content-type is not given, assume text/html. This is because of the multitude of broken CGI's that "forget" to generate the content-type. */ if (!type || 0 == strncasecmp (type, TEXTHTML_S, strlen (TEXTHTML_S)) || 0 == strncasecmp (type, TEXTXHTML_S, strlen (TEXTXHTML_S))) *dt |= TEXTHTML; else *dt &= ~TEXTHTML; if (opt.html_extension && (*dt & TEXTHTML)) /* -E / --html-extension / html_extension = on was specified, and this is a text/html file. If some case-insensitive variation on ".htm[l]" isn't already the file's suffix, tack on ".html". */ { char *last_period_in_local_filename = strrchr (*hs->local_file, '.'); if (last_period_in_local_filename == NULL || !(0 == strcasecmp (last_period_in_local_filename, ".htm") || 0 == strcasecmp (last_period_in_local_filename, ".html"))) { int local_filename_len = strlen (*hs->local_file); /* Resize the local file, allowing for ".html" preceded by optional ".NUMBER". */ *hs->local_file = xrealloc (*hs->local_file, local_filename_len + 24 + sizeof (".html")); strcpy(*hs->local_file + local_filename_len, ".html"); /* If clobbering is not allowed and the file, as named, exists, tack on ".NUMBER.html" instead. */ if (!ALLOW_CLOBBER)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -