📄 http.c
字号:
/* HTTP support. Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.This file is part of GNU Wget.GNU Wget is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 3 of the License, or (at your option) any later version.GNU Wget is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with Wget. If not, see <http://www.gnu.org/licenses/>.Additional permission under GNU GPL version 3 section 7If you modify this program, or any covered work, by linking orcombining it with the OpenSSL project's OpenSSL library (or amodified version of that library), containing parts covered by theterms of the OpenSSL or SSLeay licenses, the Free Software Foundationgrants you additional permission to convey the resulting work.Corresponding Source for a non-source form of such a combinationshall include the source code for the parts of OpenSSL used as wellas that of the covered work. */#include <config.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#ifdef HAVE_UNISTD_H# include <unistd.h>#endif#include <assert.h>#include <errno.h>#include <time.h>#include <locale.h>#include "wget.h"#include "hash.h"#include "http.h"#include "utils.h"#include "url.h"#include "host.h"#include "retr.h"#include "connect.h"#include "netrc.h"#ifdef HAVE_SSL# include "ssl.h"#endif#ifdef ENABLE_NTLM# include "http-ntlm.h"#endif#include "cookies.h"#ifdef ENABLE_DIGEST# include "gen-md5.h"#endif#include "convert.h"#include "spider.h"#ifdef TESTING#include "test.h"#endifextern char *version_string;/* Forward decls. */static char *create_authorization_line (const char *, const char *, const char *, const char *, const char *, bool *);static char *basic_authentication_encode (const char *, const char *);static bool known_authentication_scheme_p (const char *, const char *);static void load_cookies (void);#ifndef MIN# define MIN(x, y) ((x) > (y) ? (y) : (x))#endifstatic bool cookies_loaded_p;static struct cookie_jar *wget_cookie_jar;#define TEXTHTML_S "text/html"#define TEXTXHTML_S "application/xhtml+xml"/* Some status code validation macros: */#define H_20X(x) (((x) >= 200) && ((x) < 300))#define H_PARTIAL(x) ((x) == HTTP_STATUS_PARTIAL_CONTENTS)#define H_REDIRECTED(x) ((x) == HTTP_STATUS_MOVED_PERMANENTLY \ || (x) == HTTP_STATUS_MOVED_TEMPORARILY \ || (x) == HTTP_STATUS_SEE_OTHER \ || (x) == HTTP_STATUS_TEMPORARY_REDIRECT)/* HTTP/1.0 status codes from RFC1945, provided for reference. *//* Successful 2xx. */#define HTTP_STATUS_OK 200#define HTTP_STATUS_CREATED 201#define HTTP_STATUS_ACCEPTED 202#define HTTP_STATUS_NO_CONTENT 204#define HTTP_STATUS_PARTIAL_CONTENTS 206/* Redirection 3xx. */#define HTTP_STATUS_MULTIPLE_CHOICES 300#define HTTP_STATUS_MOVED_PERMANENTLY 301#define HTTP_STATUS_MOVED_TEMPORARILY 302#define HTTP_STATUS_SEE_OTHER 303 /* from HTTP/1.1 */#define HTTP_STATUS_NOT_MODIFIED 304#define HTTP_STATUS_TEMPORARY_REDIRECT 307 /* from HTTP/1.1 *//* Client error 4xx. */#define HTTP_STATUS_BAD_REQUEST 400#define HTTP_STATUS_UNAUTHORIZED 401#define HTTP_STATUS_FORBIDDEN 403#define HTTP_STATUS_NOT_FOUND 404#define HTTP_STATUS_RANGE_NOT_SATISFIABLE 416/* Server errors 5xx. */#define HTTP_STATUS_INTERNAL 500#define HTTP_STATUS_NOT_IMPLEMENTED 501#define HTTP_STATUS_BAD_GATEWAY 502#define HTTP_STATUS_UNAVAILABLE 503enum rp { rel_none, rel_name, rel_value, rel_both};struct request { const char *method; char *arg; struct request_header { char *name, *value; enum rp release_policy; } *headers; int hcount, hcapacity;};/* Create a new, empty request. At least request_set_method must be called before the request can be used. */static struct request *request_new (void){ struct request *req = xnew0 (struct request); req->hcapacity = 8; req->headers = xnew_array (struct request_header, req->hcapacity); return req;}/* Set the request's method and its arguments. METH should be a literal string (or it should outlive the request) because it will not be freed. ARG will be freed by request_free. */static voidrequest_set_method (struct request *req, const char *meth, char *arg){ req->method = meth; req->arg = arg;}/* Return the method string passed with the last call to request_set_method. */static const char *request_method (const struct request *req){ return req->method;}/* Free one header according to the release policy specified with request_set_header. */static voidrelease_header (struct request_header *hdr){ switch (hdr->release_policy) { case rel_none: break; case rel_name: xfree (hdr->name); break; case rel_value: xfree (hdr->value); break; case rel_both: xfree (hdr->name); xfree (hdr->value); break; }}/* Set the request named NAME to VALUE. Specifically, this means that a "NAME: VALUE\r\n" header line will be used in the request. If a header with the same name previously existed in the request, its value will be replaced by this one. A NULL value means do nothing. RELEASE_POLICY determines whether NAME and VALUE should be released (freed) with request_free. Allowed values are: - rel_none - don't free NAME or VALUE - rel_name - free NAME when done - rel_value - free VALUE when done - rel_both - free both NAME and VALUE when done Setting release policy is useful when arguments come from different sources. For example: // Don't free literal strings! request_set_header (req, "Pragma", "no-cache", rel_none); // Don't free a global variable, we'll need it later. request_set_header (req, "Referer", opt.referer, rel_none); // Value freshly allocated, free it when done. request_set_header (req, "Range", aprintf ("bytes=%s-", number_to_static_string (hs->restval)), rel_value); */static voidrequest_set_header (struct request *req, char *name, char *value, enum rp release_policy){ struct request_header *hdr; int i; if (!value) { /* A NULL value is a no-op; if freeing the name is requested, free it now to avoid leaks. */ if (release_policy == rel_name || release_policy == rel_both) xfree (name); return; } for (i = 0; i < req->hcount; i++) { hdr = &req->headers[i]; if (0 == strcasecmp (name, hdr->name)) { /* Replace existing header. */ release_header (hdr); hdr->name = name; hdr->value = value; hdr->release_policy = release_policy; return; } } /* Install new header. */ if (req->hcount >= req->hcapacity) { req->hcapacity <<= 1; req->headers = xrealloc (req->headers, req->hcapacity * sizeof (*hdr)); } hdr = &req->headers[req->hcount++]; hdr->name = name; hdr->value = value; hdr->release_policy = release_policy;}/* Like request_set_header, but sets the whole header line, as provided by the user using the `--header' option. For example, request_set_user_header (req, "Foo: bar") works just like request_set_header (req, "Foo", "bar"). */static voidrequest_set_user_header (struct request *req, const char *header){ char *name; const char *p = strchr (header, ':'); if (!p) return; BOUNDED_TO_ALLOCA (header, p, name); ++p; while (ISSPACE (*p)) ++p; request_set_header (req, xstrdup (name), (char *) p, rel_name);}/* Remove the header with specified name from REQ. Returns true if the header was actually removed, false otherwise. */static boolrequest_remove_header (struct request *req, char *name){ int i; for (i = 0; i < req->hcount; i++) { struct request_header *hdr = &req->headers[i]; if (0 == strcasecmp (name, hdr->name)) { release_header (hdr); /* Move the remaining headers by one. */ if (i < req->hcount - 1) memmove (hdr, hdr + 1, (req->hcount - i - 1) * sizeof (*hdr)); --req->hcount; return true; } } return false;}#define APPEND(p, str) do { \ int A_len = strlen (str); \ memcpy (p, str, A_len); \ p += A_len; \} while (0)/* Construct the request and write it to FD using fd_write. */static intrequest_send (const struct request *req, int fd){ char *request_string, *p; int i, size, write_error; /* Count the request size. */ size = 0; /* METHOD " " ARG " " "HTTP/1.0" "\r\n" */ size += strlen (req->method) + 1 + strlen (req->arg) + 1 + 8 + 2; for (i = 0; i < req->hcount; i++) { struct request_header *hdr = &req->headers[i]; /* NAME ": " VALUE "\r\n" */ size += strlen (hdr->name) + 2 + strlen (hdr->value) + 2; } /* "\r\n\0" */ size += 3; p = request_string = alloca_array (char, size); /* Generate the request. */ APPEND (p, req->method); *p++ = ' '; APPEND (p, req->arg); *p++ = ' '; memcpy (p, "HTTP/1.0\r\n", 10); p += 10; for (i = 0; i < req->hcount; i++) { struct request_header *hdr = &req->headers[i]; APPEND (p, hdr->name); *p++ = ':', *p++ = ' '; APPEND (p, hdr->value); *p++ = '\r', *p++ = '\n'; } *p++ = '\r', *p++ = '\n', *p++ = '\0'; assert (p - request_string == size);#undef APPEND DEBUGP (("\n---request begin---\n%s---request end---\n", request_string)); /* Send the request to the server. */ write_error = fd_write (fd, request_string, size - 1, -1); if (write_error < 0) logprintf (LOG_VERBOSE, _("Failed writing HTTP request: %s.\n"), fd_errstr (fd)); return write_error;}/* Release the resources used by REQ. */static voidrequest_free (struct request *req){ int i; xfree_null (req->arg); for (i = 0; i < req->hcount; i++) release_header (&req->headers[i]); xfree_null (req->headers); xfree (req);}static struct hash_table *basic_authed_hosts;/* Find out if this host has issued a Basic challenge yet; if so, give * it the username, password. A temporary measure until we can get * proper authentication in place. */static intmaybe_send_basic_creds (const char *hostname, const char *user, const char *passwd, struct request *req){ int did_challenge = 0; if (basic_authed_hosts && hash_table_contains(basic_authed_hosts, hostname)) { DEBUGP(("Found `%s' in basic_authed_hosts.\n", hostname)); request_set_header (req, "Authorization", basic_authentication_encode (user, passwd), rel_value); did_challenge = 1; } else { DEBUGP(("Host `%s' has not issued a general basic challenge.\n", hostname));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -