#include "utils.h"
#include "url.h"
#include "host.h"
-#include "rbuf.h"
#include "retr.h"
#include "headers.h"
#include "connect.h"
if (length == 0)
break;
towrite = WMIN (promised_size - written, length);
- write_error = xwrite (sock, chunk, towrite, -1);
+ write_error = fd_write (sock, chunk, towrite, -1);
if (write_error < 0)
{
fclose (fp);
DEBUGP (("done]\n"));
return 0;
}
+\f
+static const char *
+next_header (const char *h)
+{
+ const char *end = NULL;
+ const char *p = h;
+ do
+ {
+ p = strchr (p, '\n');
+ if (!p)
+ return end;
+ end = ++p;
+ }
+ while (*p == ' ' || *p == '\t');
+
+ return end;
+}
+
\f
/* Functions to be used as arguments to header_process(): */
{
DEBUGP (("Disabling further reuse of socket %d.\n", pconn.socket));
pconn_active = 0;
- xclose (pconn.socket);
+ fd_close (pconn.socket);
xfree (pconn.host);
xzero (pconn);
}
still hope -- read below. */
if (0 != strcasecmp (host, pconn.host))
{
- /* This is somewhat evil, but works in practice: if the address
- that pconn.socket is connected to is one of the IP addresses
- HOST resolves to, we don't need to reconnect. #### Is it
- correct to do this by default? */
+ /* If pconn.socket is already talking to HOST, we needn't
+ reconnect. This happens often when both sites are virtual
+ hosts distinguished only by name and served by the same
+ network interface, and hence the same web server (possibly
+ set up by the ISP and serving many different web sites).
+ This admittedly non-standard optimization does not contradict
+ HTTP and works well with popular server software. */
+
int found;
ip_address ip;
struct address_list *al;
- if (!socket_ip_address (pconn.socket, &ip, 0))
+ if (ssl)
+ /* Don't try to talk to two different SSL sites over the same
+ secure connection! (Besides, it's not clear if name-based
+ virtual hosting is even possible with SSL.) */
+ return 0;
+
+ /* If pconn.socket's peer is one of the IP addresses HOST
+ resolves to, pconn.socket is for all intents and purposes
+ already talking to HOST. */
+
+ if (!socket_ip_address (pconn.socket, &ip, ENDPOINT_PEER))
{
- /* Can't get the peer's address -- something must be wrong
- with the connection. */
+ /* Can't get the peer's address -- something must be very
+ wrong with the connection. */
invalidate_persistent ();
return 0;
}
return 0;
}
- found = address_list_find (al, &ip);
+ found = address_list_contains (al, &ip);
address_list_release (al);
if (!found)
return 0;
- /* HOST resolves to an address pconn.sock is connected to -- no
- need to reconnect. */
+ /* The persistent connection's peer address was found among the
+ addresses HOST resolved to; therefore, pconn.sock is in fact
+ already talking to HOST -- no need to reconnect. */
}
/* Finally, check whether the connection is still open. This is
if (pconn_active && (fd) == pconn.socket) \
invalidate_persistent (); \
else \
- xclose (fd); \
+ fd_close (fd); \
} \
} while (0)
if (pconn_active && (fd) == pconn.socket) \
invalidate_persistent (); \
else \
- xclose (fd); \
+ fd_close (fd); \
} while (0)
\f
struct http_stat
will print it if there is enough information to do so (almost
always), returning the error to the caller (i.e. http_loop).
- Various HTTP parameters are stored to hs. Although it parses the
- response code correctly, it is not used in a sane way. The caller
- can do that, though.
+ Various HTTP parameters are stored to hs.
If PROXY is non-NULL, the connection will be made to the proxy
server, and u->url will be requested. */
char *pragma_h, *referer, *useragent, *range, *wwwauth;
char *authenticate_h;
char *proxyauth;
- char *all_headers;
char *port_maybe;
char *request_keep_alive;
- int sock, hcount, all_length, statcode;
+ int sock, hcount, statcode;
int write_error;
long contlen, contrange;
struct url *conn;
FILE *fp;
int auth_tried_already;
- struct rbuf rbuf;
int using_ssl = 0;
char *cookies = NULL;
+ char *head;
+ const char *hdr_beg, *hdr_end;
+
/* Whether this connection will be kept alive after the HTTP request
is done. */
int keep_alive;
if (sock == E_HOST)
return HOSTERR;
else if (sock < 0)
- return CONNECT_ERROR (errno);
+ return (retryable_socket_connect_error (errno)
+ ? CONERROR : CONIMPOSSIBLE);
#ifdef HAVE_SSL
if (conn->scheme == SCHEME_HTTPS)
logputs (LOG_VERBOSE, "\n");
logprintf (LOG_NOTQUIET,
_("Unable to establish SSL connection.\n"));
- xclose (sock);
+ fd_close (sock);
return CONSSLERR;
}
using_ssl = 1;
}
else
{
- logprintf (LOG_VERBOSE, _("Reusing connection to %s:%hu.\n"),
- conn->host, conn->port);
- /* #### pc_last_fd should be accessed through an accessor
- function. */
+ logprintf (LOG_VERBOSE, _("Reusing existing connection to %s:%d.\n"),
+ pconn.host, pconn.port);
sock = pconn.socket;
using_ssl = pconn.ssl;
DEBUGP (("Reusing fd %d.\n", sock));
post_content_type ? post_content_type : "",
post_content_length ? post_content_length : "",
opt.user_header ? opt.user_header : "");
- DEBUGP (("---request begin---\n%s", request));
+ DEBUGP (("\n---request begin---\n%s", request));
/* Free the temporary memory. */
xfree_null (wwwauth);
xfree (full_path);
/* Send the request to server. */
- write_error = xwrite (sock, request, strlen (request), -1);
+ write_error = fd_write (sock, request, strlen (request), -1);
if (write_error >= 0)
{
if (opt.post_data)
{
DEBUGP (("[POST data: %s]\n", opt.post_data));
- write_error = xwrite (sock, opt.post_data, post_data_size, -1);
+ write_error = fd_write (sock, opt.post_data, post_data_size, -1);
}
else if (opt.post_file_name && post_data_size != 0)
write_error = post_file (sock, opt.post_file_name, post_data_size);
statcode = -1;
*dt &= ~RETROKF;
- /* Before reading anything, initialize the rbuf. */
- rbuf_initialize (&rbuf, sock);
- all_headers = NULL;
- all_length = 0;
- /* Header-fetching loop. */
- hcount = 0;
- while (1)
- {
- char *hdr;
- int status;
-
- ++hcount;
- /* Get the header. */
- status = header_get (&rbuf, &hdr,
- /* Disallow continuations for status line. */
- (hcount == 1 ? HG_NO_CONTINUATIONS : HG_NONE));
+ DEBUGP (("\n---response begin---\n"));
- /* Check for errors. */
- if (status == HG_EOF && *hdr)
+ head = fd_read_head (sock);
+ if (!head)
+ {
+ logputs (LOG_VERBOSE, "\n");
+ if (errno == 0)
{
- /* This used to be an unconditional error, but that was
- somewhat controversial, because of a large number of
- broken CGI's that happily "forget" to send the second EOL
- before closing the connection of a HEAD request.
-
- So, the deal is to check whether the header is empty
- (*hdr is zero if it is); if yes, it means that the
- previous header was fully retrieved, and that -- most
- probably -- the request is complete. "...be liberal in
- what you accept." Oh boy. */
- logputs (LOG_VERBOSE, "\n");
- logputs (LOG_NOTQUIET, _("End of file while parsing headers.\n"));
- xfree (hdr);
- xfree_null (type);
- xfree_null (all_headers);
+ logputs (LOG_NOTQUIET, _("No data received.\n"));
CLOSE_INVALIDATE (sock);
return HEOF;
}
- else if (status == HG_ERROR)
+ else
{
- logputs (LOG_VERBOSE, "\n");
logprintf (LOG_NOTQUIET, _("Read error (%s) in headers.\n"),
strerror (errno));
- xfree (hdr);
- xfree_null (type);
- xfree_null (all_headers);
CLOSE_INVALIDATE (sock);
return HERR;
}
+ }
- /* If the headers are to be saved to a file later, save them to
- memory now. */
- if (opt.save_headers)
- {
- int lh = strlen (hdr);
- all_headers = (char *)xrealloc (all_headers, all_length + lh + 2);
- memcpy (all_headers + all_length, hdr, lh);
- all_length += lh;
- all_headers[all_length++] = '\n';
- all_headers[all_length] = '\0';
- }
+ /* Loop through the headers and process them. */
+
+ hcount = 0;
+ for (hdr_beg = head;
+ (hdr_end = next_header (hdr_beg));
+ hdr_beg = hdr_end)
+ {
+ char *hdr = strdupdelim (hdr_beg, hdr_end);
+ {
+ char *tmp = hdr + strlen (hdr);
+ if (tmp > hdr && tmp[-1] == '\n')
+ *--tmp = '\0';
+ if (tmp > hdr && tmp[-1] == '\r')
+ *--tmp = '\0';
+ }
+ ++hcount;
/* Check for status line. */
if (hcount == 1)
done_header:
xfree (hdr);
}
+ DEBUGP (("---response end---\n"));
logputs (LOG_VERBOSE, "\n");
CLOSE_INVALIDATE (sock); /* would be CLOSE_FINISH, but there
might be more bytes in the body. */
xfree_null (type);
- xfree_null (all_headers);
return NEWLOCATION;
}
}
/* Mark as successfully retrieved. */
*dt |= RETROKF;
xfree_null (type);
- xfree_null (all_headers);
CLOSE_INVALIDATE (sock); /* would be CLOSE_FINISH, but there
might be more bytes in the body. */
return RETRUNNEEDED;
Continued download failed on this file, which conflicts with `-c'.\n\
Refusing to truncate existing file `%s'.\n\n"), *hs->local_file);
xfree_null (type);
- xfree_null (all_headers);
CLOSE_INVALIDATE (sock);
return CONTNOTSUPPORTED;
}
/* This means the whole request was somehow misunderstood by the
server. Bail out. */
xfree_null (type);
- xfree_null (all_headers);
CLOSE_INVALIDATE (sock);
return RANGEERR;
}
hs->len = 0L;
hs->res = 0;
xfree_null (type);
- xfree_null (all_headers);
CLOSE_INVALIDATE (sock); /* would be CLOSE_FINISH, but there
might be more bytes in the body. */
return RETRFINISHED;
logprintf (LOG_NOTQUIET, "%s: %s\n", *hs->local_file, strerror (errno));
CLOSE_INVALIDATE (sock); /* would be CLOSE_FINISH, but there
might be more bytes in the body. */
- xfree_null (all_headers);
return FOPENERR;
}
}
/* #### This confuses the code that checks for file size. There
should be some overhead information. */
if (opt.save_headers)
- fwrite (all_headers, 1, all_length, fp);
+ fwrite (head, 1, strlen (head), fp);
/* Get the contents of the document. */
- hs->res = get_contents (sock, fp, &hs->len, hs->restval,
+ hs->res = fd_read_body (sock, fp, &hs->len, hs->restval,
(contlen != -1 ? contlen : 0),
- &rbuf, keep_alive, &hs->dltime);
+ keep_alive, &hs->dltime);
if (hs->res >= 0)
CLOSE_FINISH (sock);
if (flush_res == EOF)
hs->res = -2;
}
- xfree_null (all_headers);
if (hs->res == -2)
return FWRITEERR;
return RETRFINISHED;