--- src/lib/libfetch/http.c 2005/03/02 05:15:13 1.3 +++ src/lib/libfetch/http.c 2007/08/05 21:48:12 1.4 @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2000-2004 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,7 +25,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/lib/libfetch/http.c,v 1.13.2.23 2003/06/06 06:45:25 des Exp $ + * $FreeBSD: src/lib/libfetch/http.c,v 1.78 2007/05/08 19:28:03 des Exp $ * $DragonFly$ */ @@ -76,6 +76,9 @@ #include #include +#include +#include + #include "fetch.h" #include "common.h" #include "httperr.h" @@ -89,12 +92,15 @@ #define HTTP_MOVED_PERM 301 #define HTTP_MOVED_TEMP 302 #define HTTP_SEE_OTHER 303 +#define HTTP_TEMP_REDIRECT 307 #define HTTP_NEED_AUTH 401 #define HTTP_NEED_PROXY_AUTH 407 +#define HTTP_BAD_RANGE 416 #define HTTP_PROTOCOL_ERROR 999 #define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \ || (xyz) == HTTP_MOVED_TEMP \ + || (xyz) == HTTP_TEMP_REDIRECT \ || (xyz) == HTTP_SEE_OTHER) #define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599) @@ -513,22 +519,34 @@ _http_parse_range(const char *p, off_t * if (strncasecmp(p, "bytes ", 6) != 0) return (-1); - for (first = 0, p += 6; *p && isdigit(*p); ++p) - first = first * 10 + *p - '0'; - if (*p != '-') - return (-1); - for (last = 0, ++p; *p && isdigit(*p); ++p) - last = last * 10 + *p - '0'; + p += 6; + if (*p == '*') { + first = last = -1; + ++p; + } else { + for (first = 0; *p && isdigit(*p); ++p) + first = first * 10 + *p - '0'; + if (*p != '-') + return (-1); + for (last = 0, ++p; *p && isdigit(*p); ++p) + last = last * 10 + *p - '0'; + } if (first > last || *p != '/') return (-1); for (len = 0, ++p; *p && isdigit(*p); ++p) len = len * 10 + *p - '0'; if (*p || len < last - first + 1) return (-1); - DEBUG(fprintf(stderr, "content range: [%lld-%lld/%lld]\n", - (long long)first, (long long)last, (long long)len)); + if (first == -1) { + DEBUG(fprintf(stderr, "content range: [*/%lld]\n", + (long long)len)); + *length = 0; + } else { + DEBUG(fprintf(stderr, "content range: [%lld-%lld/%lld]\n", + (long long)first, (long long)last, (long long)len)); + *length = last - first + 1; + } *offset = first; - *length = last - first + 1; *size = len; return (0); } @@ -553,7 +571,7 @@ _http_base64(const char *src) int t, r; l = strlen(src); - if ((str = malloc(((l + 2) / 3) * 4)) == NULL) + if ((str = malloc(((l + 2) / 3) * 4 + 1)) == NULL) return (NULL); dst = str; r = 0; @@ -657,7 +675,7 @@ _http_connect(struct url *URL, struct ur { conn_t *conn; int verbose; - int af; + int af, val; #ifdef INET6 af = AF_UNSPEC; @@ -692,6 +710,10 @@ _http_connect(struct url *URL, struct ur _fetch_syserr(); return (NULL); } + + val = 1; + setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, sizeof(val)); + return (conn); } @@ -704,7 +726,7 @@ _http_get_proxy(const char *flags) if (flags != NULL && strchr(flags, 'd') != NULL) return (NULL); if (((p = getenv("HTTP_PROXY")) || (p = getenv("http_proxy"))) && - (purl = fetchParseURL(p))) { + *p && (purl = fetchParseURL(p))) { if (!*purl->scheme) strcpy(purl->scheme, SCHEME_HTTP); if (!purl->port) @@ -772,7 +794,7 @@ _http_request(struct url *URL, const cha conn_t *conn; struct url *url, *new; int chunked, direct, need_auth, noredirect, verbose; - int e, i, n; + int e, i, n, val; off_t offset, clength, length, size; time_t mtime; const char *p; @@ -894,6 +916,20 @@ _http_request(struct url *URL, const cha _http_cmd(conn, "Connection: close"); _http_cmd(conn, ""); + /* + * Force the queued request to be dispatched. Normally, one + * would do this with shutdown(2) but squid proxies can be + * configured to disallow such half-closed connections. To + * be compatible with such configurations, fiddle with socket + * options to force the pending data to be written. + */ + val = 0; + setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &val, + sizeof(val)); + val = 1; + setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &val, + sizeof(val)); + /* get reply */ switch (_http_get_reply(conn)) { case HTTP_OK: @@ -904,15 +940,15 @@ _http_request(struct url *URL, const cha case HTTP_MOVED_TEMP: case HTTP_SEE_OTHER: /* - * Not so fine, but we still have to read the headers to - * get the new location. + * Not so fine, but we still have to read the + * headers to get the new location. */ break; case HTTP_NEED_AUTH: if (need_auth) { /* - * We already sent out authorization code, so there's - * nothing more we can do. + * We already sent out authorization code, + * so there's nothing more we can do. */ _http_seterr(conn->err); goto ouch; @@ -923,11 +959,19 @@ _http_request(struct url *URL, const cha break; case HTTP_NEED_PROXY_AUTH: /* - * If we're talking to a proxy, we already sent our proxy - * authorization code, so there's nothing more we can do. + * If we're talking to a proxy, we already sent + * our proxy authorization code, so there's + * nothing more we can do. */ _http_seterr(conn->err); goto ouch; + case HTTP_BAD_RANGE: + /* + * This can happen if we ask for 0 bytes because + * we already have the whole file. Consider this + * a success for now, and check sizes later. + */ + break; case HTTP_PROTOCOL_ERROR: /* fall through */ case -1: @@ -1009,6 +1053,19 @@ _http_request(struct url *URL, const cha continue; } + /* requested range not satisfiable */ + if (conn->err == HTTP_BAD_RANGE) { + if (url->offset == size && url->length == 0) { + /* asked for 0 bytes; fake it */ + offset = url->offset; + conn->err = HTTP_OK; + break; + } else { + _http_seterr(conn->err); + goto ouch; + } + } + /* we have a hit or an error */ if (conn->err == HTTP_OK || conn->err == HTTP_PARTIAL || HTTP_ERROR(conn->err)) break;