📄 http.c
字号:
start = hunk + oldlen - 4; end = hunk + oldlen + peeklen; for (; start < end - 1; start++) if (*start == '\n') { if (start < end - 2 && start[1] == '\r' && start[2] == '\n') return start + 3; if (start[1] == '\n') return start + 2; } return NULL;}/* The maximum size of a single HTTP response we care to read. This is not meant to impose an arbitrary limit, but to protect the user from Wget slurping up available memory upon encountering malicious or buggy server output. 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 1. Otherwise return 0. This function is used as a building block for resp_header_copy and resp_header_strdup. */static intresp_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, 1 is returned, otherwise 0. 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 intresp_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 0; if (bufsize) { int len = MIN (e - b, bufsize - 1); memcpy (buf, b, len); buf[len] = '\0'; } return 1;}/* 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 the server response, line by line, omitting the trailing CRLF from individual header lines, and prefixed with PREFIX. */static voidprint_server_response (const struct response *resp, const char *prefix){ int i; if (!resp->headers) return; for (i = 0; resp->headers[i + 1]; i++) { const char *b = resp->headers[i]; const char *e = resp->headers[i + 1]; /* Skip CRLF */ if (b < e && e[-1] == '\n') --e; if (b < e && e[-1] == '\r') --e; /* This is safe even on printfs with broken handling of "%.<n>s" because resp->headers ends with \0. */ logprintf (LOG_VERBOSE, "%s%.*s\n", prefix, e - b, b); }}/* Parse the `Content-Range' header and extract the information it contains. Returns 1 if successful, -1 otherwise. */static intparse_content_range (const char *hdr, wgint *first_byte_ptr, wgint *last_byte_ptr, wgint *entity_length_ptr){ wgint num; /* Ancient versions of Netscape proxy server, presumably predating rfc2068, sent out `Content-Range' without the "bytes" specifier. */ if (!strncasecmp (hdr, "bytes", 5)) { hdr += 5; /* "JavaWebServer/1.1.1" sends "bytes: x-y/z", contrary to the HTTP spec. */ if (*hdr == ':') ++hdr; while (ISSPACE (*hdr)) ++hdr; if (!*hdr) return 0; } if (!ISDIGIT (*hdr)) return 0; for (num = 0; ISDIGIT (*hdr); hdr++) num = 10 * num + (*hdr - '0'); if (*hdr != '-' || !ISDIGIT (*(hdr + 1))) return 0; *first_byte_ptr = num; ++hdr; for (num = 0; ISDIGIT (*hdr); hdr++) num = 10 * num + (*hdr - '0'); if (*hdr != '/' || !ISDIGIT (*(hdr + 1))) return 0; *last_byte_ptr = num; ++hdr; for (num = 0; ISDIGIT (*hdr); hdr++) num = 10 * num + (*hdr - '0'); *entity_length_ptr = num; return 1;}/* Read the body of the request, but don't store it anywhere and don't display a progress gauge. This is useful for reading the bodies of administrative responses to which we will soon issue another request. The response is not useful to the user, but reading it allows us to continue using the same connection to the server. If reading fails, 0 is returned, non-zero otherwise. In debug mode, the body is displayed for debugging purposes. */static intskip_short_body (int fd, wgint contlen){ enum { SKIP_SIZE = 512, /* size of the download buffer */ SKIP_THRESHOLD = 4096 /* the largest size we read */ }; char dlbuf[SKIP_SIZE + 1]; dlbuf[SKIP_SIZE] = '\0'; /* so DEBUGP can safely print it */ /* We shouldn't get here with unknown contlen. (This will change with HTTP/1.1, which supports "chunked" transfer.) */ assert (contlen != -1); /* If the body is too large, it makes more sense to simply close the connection than to try to read the body. */ if (contlen > SKIP_THRESHOLD) return 0; DEBUGP (("Skipping %s bytes of body: [", number_to_static_string (contlen))); while (contlen > 0) { int ret = fd_read (fd, dlbuf, MIN (contlen, SKIP_SIZE), -1.0); if (ret <= 0) { /* Don't normally report the error since this is an optimization that should be invisible to the user. */ DEBUGP (("] aborting (%s).\n", ret < 0 ? strerror (errno) : "EOF received")); return 0; } contlen -= ret; /* Safe even if %.*s bogusly expects terminating \0 because we've zero-terminated dlbuf above. */ DEBUGP (("%.*s", ret, dlbuf)); } DEBUGP (("] done.\n")); return 1;}/* Persistent connections. Currently, we cache the most recently used connection as persistent, provided that the HTTP server agrees to make it such. The persistence data is stored in the variables below. Ideally, it should be possible to cache an arbitrary fixed number of these connections. *//* Whether a persistent connection is active. */static int pconn_active;static struct { /* The socket of the connection. */ int socket; /* Host and port of the currently active persistent connection. */ char *host; int port; /* Whether a ssl handshake has occoured on this connection. */ int ssl; /* Whether the connection was authorized. This is only done by NTLM, which authorizes *connections* rather than individual requests. (That practice is peculiar for HTTP, but it is a useful optimization.) */ int authorized;#ifdef ENABLE_NTLM /* NTLM data of the current connection. */ struct ntlmdata ntlm;#endif} pconn;/* Mark the persistent connection as invalid and free the resources it uses. This is used by the CLOSE_* macros after they forcefully close a registered persistent connection. */static voidinvalidate_persistent (void){ DEBUGP (("Disabling further reuse of socket %d.\n", pconn.socket)); pconn_active = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -