📄 http.c
字号:
/****************************************************************************** libprozilla - a download accelerator library Copyright (C) 2001 Kalum Somaratna This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA******************************************************************************//* HTTP support. *//* $Id: http.c,v 1.20 2001/10/27 11:24:40 kalum Exp $ */#include "common.h"#include "prozilla.h"#include "misc.h"#include "connect.h"#include "debug.h"#include "http.h"/* Some status code validation macros: */#define H_20X(x) (((x) >= 200) && ((x) < 300))#define H_PARTIAL(x) ((x) == HTTP_PARTIAL_CONTENTS)#define H_REDIRECTED(x) (((x) == HTTP_MOVED_PERMANENTLY) || ((x) == HTTP_MOVED_TEMPORARILY))/* HTTP/1.0 status codes from RFC1945, given for reference. *//* Successful 2xx. */#define HTTP_OK 200#define HTTP_CREATED 201#define HTTP_ACCEPTED 202#define HTTP_NO_CONTENT 204#define HTTP_PARTIAL_CONTENTS 206/* Redirection 3xx. */#define HTTP_MULTIPLE_CHOICES 300#define HTTP_MOVED_PERMANENTLY 301#define HTTP_MOVED_TEMPORARILY 302#define HTTP_NOT_MODIFIED 304/* Client error 4xx. */#define HTTP_BAD_REQUEST 400#define HTTP_UNAUTHORIZED 401#define HTTP_FORBIDDEN 403#define HTTP_NOT_FOUND 404/* Server errors 5xx. */#define HTTP_INTERNAL 500#define HTTP_NOT_IMPLEMENTED 501#define HTTP_BAD_GATEWAY 502#define HTTP_UNAVAILABLE 503#define HTTP_GATEWAY_TIMEOUT 504#define DYNAMIC_LINE_BUFFER 40/****************************************************************************** ...******************************************************************************/int buf_readchar(int fd, char *ret, struct timeval *timeout){ int res; res = krecv(fd, ret, 1, 0, timeout); if (res <= 0) return res; return 1;}/****************************************************************************** This is similar to buf_readchar(), only it doesn't move the buffer position.******************************************************************************/int buf_peek(int fd, char *ret, struct timeval *timeout){ int res; res = krecv(fd, ret, 1, MSG_PEEK, timeout); if (res <= 0) return res; return 1;}/****************************************************************************** Function to fetch a header from socket/file descriptor fd. The header may be of arbitrary length, since the function allocates as much memory as necessary for the header to fit. Most errors are handled. The header may be terminated by LF or CRLF. If the character after LF is SP or HT (horizontal tab), the header spans to another line (continuation header), as per RFC2068. The trailing CRLF or LF are stripped from the header, and it is zero-terminated.******************************************************************************/uerr_t fetch_next_header(int fd, char **hdr, struct timeval * timeout){ int i, bufsize, res; char next; bufsize = DYNAMIC_LINE_BUFFER; *hdr = kmalloc(bufsize); for (i = 0; 1; i++) { if (i > bufsize - 1) *hdr = krealloc(*hdr, (bufsize <<= 1)); res = buf_readchar(fd, *hdr + i, timeout); if (res == 1) { if ((*hdr)[i] == '\n') { if (!(i == 0 || (i == 1 && (*hdr)[0] == '\r'))) { /* If the header is non-empty, we need to check if it continues on to the other line. We do that by getting the next character without actually downloading it (i.e. peeking it). */ res = buf_peek(fd, &next, timeout); if (res == 0) return HEOF; else if (res == -1) return HERR; /* If the next character is SP or HT, just continue. */ if (next == '\t' || next == ' ') continue; } /* The header ends. */ (*hdr)[i] = '\0'; /* Get rid of '\r'. */ if (i > 0 && (*hdr)[i - 1] == '\r') (*hdr)[i - 1] = '\0'; break; } } else if (res == 0) return HEOF; else return HERR; } return HOK;}/****************************************************************************** ...******************************************************************************/int hparsestatline(const char *hdr, const char **rp){ int mjr, mnr; /* HTTP major and minor version. */ int statcode; /* HTTP status code. */ const char *p; *rp = NULL; /* The standard format of HTTP-Version is: HTTP/x.y, where x is major version, and y is minor version. */ if (strncmp(hdr, "HTTP/", 5) != 0) return -1; hdr += 5; p = hdr; for (mjr = 0; isdigit(*hdr); hdr++) mjr = 10 * mjr + (*hdr - '0'); if (*hdr != '.' || p == hdr) return -1; ++hdr; p = hdr; for (mnr = 0; isdigit(*hdr); hdr++) mnr = 10 * mnr + (*hdr - '0'); if (*hdr != ' ' || p == hdr) return -1; /* Wget will accept only 1.0 and higher HTTP-versions. The value of minor version can be safely ignored. */ if (mjr < 1) return -1; /* Skip the space. */ ++hdr; if (!(isdigit(*hdr) && isdigit(hdr[1]) && isdigit(hdr[2]))) return -1; statcode = 100 * (*hdr - '0') + 10 * (hdr[1] - '0') + (hdr[2] - '0'); /* RFC2068 requires a SPC here, even if there is no reason-phrase. As some servers/CGI are (incorrectly) setup to drop the SPC, we'll be liberal and allow the status line to end here. */ if (hdr[3] != ' ') { if (!hdr[3]) *rp = hdr + 3; else return -1; } else *rp = hdr + 4; return statcode;}/****************************************************************************** Skip LWS (linear white space), if present. Returns number of characters to skip.******************************************************************************/int hskip_lws(const char *hdr){ int i; for (i = 0; *hdr == ' ' || *hdr == '\t' || *hdr == '\r' || *hdr == '\n'; ++hdr) ++i; return i;}/****************************************************************************** Return the content length of the document body, if this is Content-length header, -1 otherwise.******************************************************************************/long hgetlen(const char *hdr){ const int l = 15; /* strlen("content-length:"). */ long len; if (strncasecmp(hdr, "content-length:", l)) return -1; hdr += (l + hskip_lws(hdr + l)); if (!*hdr) return -1; if (!isdigit(*hdr)) return -1; for (len = 0; isdigit(*hdr); hdr++) len = 10 * len + (*hdr - '0'); return len;}/****************************************************************************** Return the content-range in bytes, as returned by the server, if this is Content-range header, -1 otherwise.******************************************************************************/long hgetrange(const char *hdr){ const int l = 14; /* strlen("content-range:"). */ long len; if (strncasecmp(hdr, "content-range:", l)) return -1; hdr += (l + hskip_lws(hdr + l)); if (!*hdr) return -1; /* Nutscape proxy server sends content-length without "bytes" specifier, which is a breach of HTTP/1.1 draft. But heck, I must support it... */ if (!strncasecmp(hdr, "bytes", 5)) { hdr += 5; hdr += hskip_lws(hdr); if (!*hdr) return -1; } if (!isdigit(*hdr)) return -1; for (len = 0; isdigit(*hdr); hdr++) len = 10 * len + (*hdr - '0'); return len;}/****************************************************************************** Returns a malloc-ed copy of the location of the document, if the string hdr begins with LOCATION_H, or NULL.******************************************************************************/char *hgetlocation(const char *hdr){ const int l = 9; /* strlen("location:"). */ if (strncasecmp(hdr, "location:", l)) return NULL; hdr += (l + hskip_lws(hdr + l)); return kstrdup(hdr);}/****************************************************************************** Returns a malloc-ed copy of the last-modified date of the document, if the hdr begins with LASTMODIFIED_H.******************************************************************************/char *hgetmodified(const char *hdr){ const int l = 14; /* strlen("last-modified:"). */ if (strncasecmp(hdr, "last-modified:", l)) return NULL; hdr += (l + hskip_lws(hdr + l)); return kstrdup(hdr);}/****************************************************************************** Returns 0 if the header is accept-ranges, and it contains the word "none", -1 if there is no accept ranges, 1 is there is accept-ranges and it is not none.******************************************************************************/int hgetaccept_ranges(const char *hdr){ const int l = 14; /* strlen("accept-ranges:"). */ if (strncasecmp(hdr, "accept-ranges:", l)) return -1; hdr += (l + hskip_lws(hdr + l)); if (strstr(hdr, "none")) return 0; else return 1;}/****************************************************************************** ...******************************************************************************/uerr_t http_fetch_headers(connection_t * connection, http_stat_t * hs, char *command){ uerr_t err; int num_written, hcount, statcode, all_length; long contlen, contrange; char *hdr, *type, *all_headers; const char *error; hs->len = 0L; hs->contlen = -1; hs->accept_ranges = -1; hs->res = -1; hs->newloc = NULL; hs->remote_time = NULL; hs->error = NULL; num_written = ksend(connection->data_sock, command, strlen(command), 0, &connection->xfer_timeout); if (num_written != strlen(command)) { proz_debug(_("Failed writing HTTP request")); return WRITEERR; } all_headers = NULL; all_length = 0; contlen = contrange = -1; statcode = -1; type = NULL; /* Header-fetching loop. */ hcount = 0; for (;;) { ++hcount; /* Get the header. */ err = fetch_next_header(connection->data_sock, &hdr, &connection->xfer_timeout); proz_debug(_("Header = %s"), hdr); if (err == HEOF) { proz_debug(_("End of file while parsing headers")); kfree(hdr); if (type) kfree(type); if (all_headers) kfree(all_headers); return HEOF; } else if (err == HERR) { proz_debug(_("Read error in headers")); kfree(hdr); if (type) kfree(type); if (all_headers) kfree(all_headers); return HERR; } /* Exit on empty header. */ if (!*hdr) { kfree(hdr); break; } /* Check for errors documented in the first header. */ if (hcount == 1) { statcode = hparsestatline(hdr, &error); hs->statcode = statcode; /* Store the descriptive response. */ if (statcode == -1) /* Malformed request. */ hs->error = kstrdup(_("UNKNOWN")); else if (!*error) hs->error = kstrdup(_("(no description)")); else hs->error = kstrdup(error); } if (contlen == -1) { contlen = hgetlen(hdr); hs->contlen = contlen; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -