📄 http.c
字号:
{ DEBUGP(("Host `%s' has not issued a general basic challenge.\n", hostname)); } if (do_challenge) { request_set_header (req, "Authorization", basic_authentication_encode (user, passwd), rel_value); } return do_challenge;}static voidregister_basic_auth_host (const char *hostname){ if (!basic_authed_hosts) { basic_authed_hosts = make_nocase_string_hash_table (1); } if (!hash_table_contains(basic_authed_hosts, hostname)) { hash_table_put (basic_authed_hosts, xstrdup(hostname), NULL); DEBUGP(("Inserted `%s' into basic_authed_hosts\n", hostname)); }}/* Send the contents of FILE_NAME to SOCK. Make sure that exactly PROMISED_SIZE bytes are sent over the wire -- if the file is longer, read only that much; if the file is shorter, report an error. */static intpost_file (int sock, const char *file_name, wgint promised_size){ static char chunk[8192]; wgint written = 0; int write_error; FILE *fp; DEBUGP (("[writing POST file %s ... ", file_name)); fp = fopen (file_name, "rb"); if (!fp) return -1; while (!feof (fp) && written < promised_size) { int towrite; int length = fread (chunk, 1, sizeof (chunk), fp); if (length == 0) break; towrite = MIN (promised_size - written, length); write_error = fd_write (sock, chunk, towrite, -1); if (write_error < 0) { fclose (fp); return -1; } written += towrite; } fclose (fp); /* If we've written less than was promised, report a (probably nonsensical) error rather than break the promise. */ if (written < promised_size) { errno = EINVAL; return -1; } assert (written == promised_size); DEBUGP (("done]\n")); return 0;}/* Determine whether [START, PEEKED + PEEKLEN) contains an empty line. If so, return the pointer to the position after the line, otherwise return NULL. This is used as callback to fd_read_hunk. The data between START and PEEKED has been read and cannot be "unread"; the data after PEEKED has only been peeked. */static const char *response_head_terminator (const char *start, const char *peeked, int peeklen){ const char *p, *end; /* If at first peek, verify whether HUNK starts with "HTTP". If not, this is a HTTP/0.9 request and we must bail out without reading anything. */ if (start == peeked && 0 != memcmp (start, "HTTP", MIN (peeklen, 4))) return start; /* Look for "\n[\r]\n", and return the following position if found. Start two chars before the current to cover the possibility that part of the terminator (e.g. "\n\r") arrived in the previous batch. */ p = peeked - start < 2 ? start : peeked - 2; end = peeked + peeklen; /* Check for \n\r\n or \n\n anywhere in [p, end-2). */ for (; p < end - 2; p++) if (*p == '\n') { if (p[1] == '\r' && p[2] == '\n') return p + 3; else if (p[1] == '\n') return p + 2; } /* p==end-2: check for \n\n directly preceding END. */ if (p[0] == '\n' && p[1] == '\n') return p + 2; return NULL;}/* The maximum size of a single HTTP response we care to read. Rather than being a limit of the reader implementation, this limit prevents Wget from slurping all available memory upon encountering malicious or buggy server output, thus protecting the user. Define it to 0 to remove the limit. */#define HTTP_RESPONSE_MAX_SIZE 65536/* Read the HTTP request head from FD and return it. The error conditions are the same as with fd_read_hunk. To support HTTP/0.9 responses, this function tries to make sure that the data begins with "HTTP". If this is not the case, no data is read and an empty request is returned, so that the remaining data can be treated as body. */static char *read_http_response_head (int fd){ return fd_read_hunk (fd, response_head_terminator, 512, HTTP_RESPONSE_MAX_SIZE);}struct response { /* The response data. */ const char *data; /* The array of pointers that indicate where each header starts. For example, given this HTTP response: HTTP/1.0 200 Ok Description: some text Etag: x The headers are located like this: "HTTP/1.0 200 Ok\r\nDescription: some\r\n text\r\nEtag: x\r\n\r\n" ^ ^ ^ ^ headers[0] headers[1] headers[2] headers[3] I.e. headers[0] points to the beginning of the request, headers[1] points to the end of the first header and the beginning of the second one, etc. */ const char **headers;};/* Create a new response object from the text of the HTTP response, available in HEAD. That text is automatically split into constituent header lines for fast retrieval using resp_header_*. */static struct response *resp_new (const char *head){ const char *hdr; int count, size; struct response *resp = xnew0 (struct response); resp->data = head; if (*head == '\0') { /* Empty head means that we're dealing with a headerless (HTTP/0.9) response. In that case, don't set HEADERS at all. */ return resp; } /* Split HEAD into header lines, so that resp_header_* functions don't need to do this over and over again. */ size = count = 0; hdr = head; while (1) { DO_REALLOC (resp->headers, size, count + 1, const char *); resp->headers[count++] = hdr; /* Break upon encountering an empty line. */ if (!hdr[0] || (hdr[0] == '\r' && hdr[1] == '\n') || hdr[0] == '\n') break; /* Find the end of HDR, including continuations. */ do { const char *end = strchr (hdr, '\n'); if (end) hdr = end + 1; else hdr += strlen (hdr); } while (*hdr == ' ' || *hdr == '\t'); } DO_REALLOC (resp->headers, size, count + 1, const char *); resp->headers[count] = NULL; return resp;}/* Locate the header named NAME in the request data, starting with position START. This allows the code to loop through the request data, filtering for all requests of a given name. Returns the found position, or -1 for failure. The code that uses this function typically looks like this: for (pos = 0; (pos = resp_header_locate (...)) != -1; pos++) ... do something with header ... If you only care about one header, use resp_header_get instead of this function. */static intresp_header_locate (const struct response *resp, const char *name, int start, const char **begptr, const char **endptr){ int i; const char **headers = resp->headers; int name_len; if (!headers || !headers[1]) return -1; name_len = strlen (name); if (start > 0) i = start; else i = 1; for (; headers[i + 1]; i++) { const char *b = headers[i]; const char *e = headers[i + 1]; if (e - b > name_len && b[name_len] == ':' && 0 == strncasecmp (b, name, name_len)) { b += name_len + 1; while (b < e && ISSPACE (*b)) ++b; while (b < e && ISSPACE (e[-1])) --e; *begptr = b; *endptr = e; return i; } } return -1;}/* Find and retrieve the header named NAME in the request data. If found, set *BEGPTR to its starting, and *ENDPTR to its ending position, and return true. Otherwise return false. This function is used as a building block for resp_header_copy and resp_header_strdup. */static boolresp_header_get (const struct response *resp, const char *name, const char **begptr, const char **endptr){ int pos = resp_header_locate (resp, name, 0, begptr, endptr); return pos != -1;}/* Copy the response header named NAME to buffer BUF, no longer than BUFSIZE (BUFSIZE includes the terminating 0). If the header exists, true is returned, false otherwise. If there should be no limit on the size of the header, use resp_header_strdup instead. If BUFSIZE is 0, no data is copied, but the boolean indication of whether the header is present is still returned. */static boolresp_header_copy (const struct response *resp, const char *name, char *buf, int bufsize){ const char *b, *e; if (!resp_header_get (resp, name, &b, &e)) return false; if (bufsize) { int len = MIN (e - b, bufsize - 1); memcpy (buf, b, len); buf[len] = '\0'; } return true;}/* Return the value of header named NAME in RESP, allocated with malloc. If such a header does not exist in RESP, return NULL. */static char *resp_header_strdup (const struct response *resp, const char *name){ const char *b, *e; if (!resp_header_get (resp, name, &b, &e)) return NULL; return strdupdelim (b, e);}/* Parse the HTTP status line, which is of format: HTTP-Version SP Status-Code SP Reason-Phrase The function returns the status-code, or -1 if the status line appears malformed. The pointer to "reason-phrase" message is returned in *MESSAGE. */static intresp_status (const struct response *resp, char **message){ int status; const char *p, *end; if (!resp->headers) { /* For a HTTP/0.9 response, assume status 200. */ if (message) *message = xstrdup (_("No headers, assuming HTTP/0.9")); return 200; } p = resp->headers[0]; end = resp->headers[1]; if (!end) return -1; /* "HTTP" */ if (end - p < 4 || 0 != strncmp (p, "HTTP", 4)) return -1; p += 4; /* Match the HTTP version. This is optional because Gnutella servers have been reported to not specify HTTP version. */ if (p < end && *p == '/') { ++p; while (p < end && ISDIGIT (*p)) ++p; if (p < end && *p == '.') ++p; while (p < end && ISDIGIT (*p)) ++p; } while (p < end && ISSPACE (*p)) ++p; if (end - p < 3 || !ISDIGIT (p[0]) || !ISDIGIT (p[1]) || !ISDIGIT (p[2])) return -1; status = 100 * (p[0] - '0') + 10 * (p[1] - '0') + (p[2] - '0'); p += 3; if (message) { while (p < end && ISSPACE (*p)) ++p; while (p < end && ISSPACE (end[-1])) --end; *message = strdupdelim (p, end); } return status;}/* Release the resources used by RESP. */static voidresp_free (struct response *resp){ xfree_null (resp->headers); xfree (resp);}/* Print a single line of response, the characters [b, e). We tried getting away with logprintf (LOG_VERBOSE, "%s%.*s\n", prefix, (int) (e - b), b); but that failed to escape the non-printable characters and, in fact, caused crashes in UTF-8 locales. */static voidprint_response_line(const char *prefix, const char *b, const char *e){ char *copy; BOUNDED_TO_ALLOCA(b, e, copy); logprintf (LOG_VERBOSE, "%s%s\n", prefix, escnonprint(copy));}/* Print the server response, line by line, omitting the trailing CRLF from individual header lines, and prefixed with PREFIX. */static void
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -