+static const char *
+response_head_terminator (const char *hunk, int oldlen, int peeklen)
+{
+ const char *start, *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 (oldlen == 0 && 0 != memcmp (hunk, "HTTP", MIN (peeklen, 4)))
+ return hunk;
+
+ if (oldlen < 4)
+ start = hunk;
+ else
+ 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 int
+resp_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 int
+resp_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 int
+resp_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);
+}
+