commit 94f5d599d18442fe4f4339e93b38a0bdd83ac87e
parent 7470481c1eff46db13b807c8da0d13aed08c2b1d
Author: Nihal Jere <nihal@nihaljere.xyz>
Date: Thu, 18 Feb 2021 12:00:21 -0600
http.c: add redirection support
Diffstat:
M | http.c | | | 110 | ++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------- |
1 file changed, 73 insertions(+), 37 deletions(-)
diff --git a/http.c b/http.c
@@ -16,6 +16,7 @@
#include "util.h"
#define READ_BUF_SIZ 16384
+#define MAX_REDIRECTIONS 5
#ifndef TLS_CA_CERT_FILE
#define TLS_CA_CERT_FILE "/etc/ssl/cert.pem"
@@ -161,7 +162,7 @@ https_request(void)
const char *errstr;
size_t n, len;
ssize_t r;
- int status = 0, ret = 1, stdport;
+ int status = 0, stdport;
if (!(t = tls_client())) {
fprintf(stderr, "tls_client: %s\n", tls_error(t));
@@ -220,9 +221,19 @@ https_request(void)
}
buf[len] = '\0';
- if (!strncmp(buf, "HTTP/1.0 200 ", sizeof("HTTP/1.0 200 ") - 1) ||
- !strncmp(buf, "HTTP/1.1 200 ", sizeof("HTTP/1.1 200 ") - 1))
- status = 1;
+ if (!strncmp(buf, "HTTP/1.0 ", sizeof("HTTP/1.0 ") - 1) ||
+ !strncmp(buf, "HTTP/1.1 ", sizeof("HTTP/1.1 ") - 1)) {
+ p = buf + sizeof("HTTP/1.1 ") - 1;
+ /* OK, we can read response */
+ if (!strncmp(p, "200 ", sizeof("200 ") - 1)) {
+ status = 2;
+ /* redirection */
+ } else if (!strncmp(p, "302 ", sizeof("302 ") - 1) ||
+ !strncmp(p, "303 ", sizeof("303 ") - 1) ||
+ !strncmp(p, "307 ", sizeof("307 ") - 1)) {
+ status = 3;
+ }
+ }
if (!(p = strstr(buf, "\r\n\r\n"))) {
fprintf(stderr, "no HTTP header found or header too big\n");
@@ -231,13 +242,28 @@ https_request(void)
*p = '\0'; /* NUL terminate header part */
p += strlen("\r\n\r\n");
- if (status) {
+ if (status == 2) {
n = len - (p - buf);
r = fwrite(p, 1, n, dest);
if (ferror(dest)) {
fprintf(stderr, "fwrite: %s\n", strerror(errno));
goto err;
}
+ } else if (status == 3) {
+ p = strstr(buf, "\r\nLocation: ");
+ if (p == NULL) {
+ fprintf(stderr, "http: no Location given for 3xx response\n");
+ goto err;
+ }
+ p += sizeof("\r\nLocation: ") - 1;
+
+ p = memccpy(url, p, '\r', 2048);
+ if (p == NULL) {
+ fprintf(stderr, "http: Location url too long\n");
+ goto err;
+ }
+
+ *(p-1) = '\0';
} else {
/* if not 200 OK print header */
fputs(buf, stderr);
@@ -258,7 +284,7 @@ https_request(void)
}
len += r;
- if (status) {
+ if (status == 2) {
r = fwrite(buf, 1, r, dest);
if (ferror(dest)) {
fprintf(stderr, "fwrite: %s\n", strerror(errno));
@@ -267,7 +293,6 @@ https_request(void)
}
}
- ret = 0;
err:
if (t) {
@@ -275,7 +300,7 @@ err:
tls_free(t);
}
- return status ? ret : 2;
+ return status;
}
static int
@@ -284,7 +309,7 @@ http_request(void)
char buf[READ_BUF_SIZ], *p;
size_t n, len;
ssize_t r;
- int fd = -1, status = 0, ret = 1, stdport;
+ int fd = -1, status = 0, stdport;
fd = edial(u.host, u.port);
@@ -357,6 +382,15 @@ http_request(void)
fprintf(stderr, "http: no Location given for 3xx response\n");
goto err;
}
+ p += sizeof("\r\nLocation: ") - 1;
+
+ p = memccpy(url, p, '\r', 2048);
+ if (p == NULL) {
+ fprintf(stderr, "http: Location url too long\n");
+ goto err;
+ }
+
+ *(p-1) = '\0';
} else {
/* if not 200 OK print header */
fputs(buf, stderr);
@@ -383,56 +417,58 @@ http_request(void)
}
}
- ret = 0;
err:
if (fd != -1)
close(fd);
- return status ? ret : 2;
+
+ return status;
}
int
http_fetch(char *loc, char *path)
{
- int statuscode;
+ int statuscode = 3, redirs = 0;
if (strlen(loc) > 2047)
die("%s: url too long: %s", __func__, url);
strcpy(url, loc);
- if (parseuri(url, &u) == -1)
- die("invalid url: %s", url);
-
- if (config_timeout > 0) {
- signal(SIGALRM, sighandler);
- }
-
mkdirs(dirname(path));
if ((dest = fopen(path, "w")) == NULL) {
die("%s: failed to open %s:", __func__, path);
}
- if (!strcmp(u.proto, "https")) {
- if (tls_init())
- die("tls_init failed");
- if (!(tls_config = tls_config_new()))
- die("tls config failed");
- if (!u.port[0] && !strcmp(u.proto, "https"))
- memcpy(u.port, "443", 4);
- statuscode = https_request();
- } else if (!strcmp(u.proto, "http")) {
- if (!u.port[0])
- memcpy(u.port, "80", 3);
- statuscode = http_request();
- } else {
- if (u.proto[0])
- die("unsupported protocol specified: %s", u.proto);
- else
- die("no protocol specified");
- }
+ while (statuscode == 3 && redirs < MAX_REDIRECTIONS) {
+ if (parseuri(url, &u) == -1)
+ die("invalid url: %s", url);
+ if (config_timeout > 0) {
+ signal(SIGALRM, sighandler);
+ }
+
+ if (!strcmp(u.proto, "https")) {
+ if (tls_init())
+ die("tls_init failed");
+ if (!(tls_config = tls_config_new()))
+ die("tls config failed");
+ if (!u.port[0] && !strcmp(u.proto, "https"))
+ memcpy(u.port, "443", 4);
+ statuscode = https_request();
+ } else if (!strcmp(u.proto, "http")) {
+ if (!u.port[0])
+ memcpy(u.port, "80", 3);
+ statuscode = http_request();
+ } else {
+ if (u.proto[0])
+ die("unsupported protocol specified: %s", u.proto);
+ else
+ die("no protocol specified");
+ }
+
+ }
fclose(dest);
return statuscode;