Skip to content

Commit a268533

Browse files
SeppoTakalorlubos
authored andcommitted
net: lib: downloader: Allow redirect to HTTPS from HTTP
Separate URI parsing and protocol detection to its own function and when receiving the "Location: <URI>" header, parse the URI again, so the next connection will use the correct protocol and port. Do not convert all HTTP headers to lowercase as URI is case-sensitive. Convert just the header names, not values after the first colon. Signed-off-by: Seppo Takalo <seppo.takalo@nordicsemi.no>
1 parent dd491d0 commit a268533

File tree

2 files changed

+113
-11
lines changed
  • subsys/net/lib/downloader/src/transports
  • tests/subsys/net/lib/downloader/src

2 files changed

+113
-11
lines changed

subsys/net/lib/downloader/src/transports/http.c

+41-11
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ static char *strnstr(const char *haystack, const char *needle, size_t haystack_l
128128
extern char *strnstr(const char *haystack, const char *needle, size_t haystack_sz);
129129
#endif
130130

131+
static int parse_protocol(struct downloader *dl, const char *url);
132+
131133
static int http_get_request_send(struct downloader *dl)
132134
{
133135
int err;
@@ -224,7 +226,19 @@ static int http_header_parse(struct downloader *dl, size_t buf_len)
224226
parse_len = buf_len;
225227
}
226228

229+
/* Convert HTTP headers to lowercase, but not the values (for example URI) */
230+
bool value = false;
227231
for (size_t i = 0; i < parse_len; i++) {
232+
if (dl->cfg.buf[i] == '\r' || dl->cfg.buf[i] == '\n') {
233+
value = false;
234+
}
235+
if (value) {
236+
continue;
237+
}
238+
if (dl->cfg.buf[i] == ':') {
239+
value = true;
240+
continue;
241+
}
228242
dl->cfg.buf[i] = tolower(dl->cfg.buf[i]);
229243
}
230244

@@ -251,11 +265,17 @@ static int http_header_parse(struct downloader *dl, size_t buf_len)
251265
if (q) {
252266

253267
/* Received entire line */
254-
p += strlen("\r\nlocation:");
268+
p += strlen("\r\nlocation: ");
255269
*q = '\0';
256270

257271
LOG_INF("Resource moved to %s", p);
258272

273+
err = parse_protocol(dl, p);
274+
if (err) {
275+
LOG_ERR("Failed to parse protocol, err %d, url %s", err, p);
276+
return -EBADMSG;
277+
}
278+
259279
err = dl_parse_url_host(p, dl->hostname, sizeof(dl->hostname));
260280
if (err) {
261281
LOG_ERR("Failed to parse hostname, err %d, url %s", err, p);
@@ -441,30 +461,24 @@ static bool dl_http_proto_supported(struct downloader *dl, const char *url)
441461
return false;
442462
}
443463

444-
static int dl_http_init(struct downloader *dl, struct downloader_host_cfg *dl_host_cfg,
445-
const char *url)
464+
static int parse_protocol(struct downloader *dl, const char *url)
446465
{
447466
int err;
448467
struct transport_params_http *http;
449468

450469
http = (struct transport_params_http *)dl->transport_internal;
451470

452-
/* Reset http internal struct except config. */
453-
struct downloader_transport_http_cfg tmp_cfg = http->cfg;
454-
455-
memset(http, 0, sizeof(struct transport_params_http));
456-
http->cfg = tmp_cfg;
457471

458472
http->sock.proto = IPPROTO_TCP;
459473
http->sock.type = SOCK_STREAM;
460474

461475
if (strncmp(url, HTTPS, (sizeof(HTTPS) - 1)) == 0 ||
462476
(strncmp(url, HTTP, (sizeof(HTTP) - 1)) != 0 &&
463-
(dl_host_cfg->sec_tag_count != 0 && dl_host_cfg->sec_tag_list != NULL))) {
477+
(dl->host_cfg.sec_tag_count != 0 && dl->host_cfg.sec_tag_list != NULL))) {
464478
http->sock.proto = IPPROTO_TLS_1_2;
465479
http->sock.type = SOCK_STREAM;
466480

467-
if (dl_host_cfg->sec_tag_list == NULL || dl_host_cfg->sec_tag_count == 0) {
481+
if (dl->host_cfg.sec_tag_list == NULL || dl->host_cfg.sec_tag_count == 0) {
468482
LOG_WRN("No security tag provided for TLS/DTLS");
469483
return -EINVAL;
470484
}
@@ -483,14 +497,30 @@ static int dl_http_init(struct downloader *dl, struct downloader_host_cfg *dl_ho
483497
LOG_DBG("Port not specified, using default: %d", http->sock.port);
484498
}
485499

486-
if (dl_host_cfg->set_native_tls) {
500+
if (dl->host_cfg.set_native_tls) {
487501
LOG_DBG("Enabled native TLS");
488502
http->sock.type |= SOCK_NATIVE_TLS;
489503
}
490504

491505
return 0;
492506
}
493507

508+
static int dl_http_init(struct downloader *dl, struct downloader_host_cfg *dl_host_cfg,
509+
const char *url)
510+
{
511+
struct transport_params_http *http;
512+
513+
http = (struct transport_params_http *)dl->transport_internal;
514+
515+
/* Reset http internal struct except config. */
516+
struct downloader_transport_http_cfg tmp_cfg = http->cfg;
517+
518+
memset(http, 0, sizeof(struct transport_params_http));
519+
http->cfg = tmp_cfg;
520+
521+
return parse_protocol(dl, url);
522+
}
523+
494524
static int dl_http_deinit(struct downloader *dl)
495525
{
496526
struct transport_params_http *http;

tests/subsys/net/lib/downloader/src/main.c

+72
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,14 @@
146146
"Vary: Accept-Encoding\r\n" \
147147
"X-Cache: HIT\r\n\r\n"
148148

149+
#define HTTP_HDR_REDIRECT "HTTP/1.1 308 Permanent Redirect\r\n" \
150+
"Date: Wed, 29 Jan 2025 11:16:09 GMT\r\n" \
151+
"Content-Type: text/html\r\n" \
152+
"Content-Length: 164\r\n" \
153+
"Connection: keep-alive\r\n" \
154+
"Location: https://server.com/path/to/file.end\r\n" \
155+
"\r\n\r\n"
156+
149157
#define PAYLOAD "This is the payload!"
150158

151159
#define FD 0
@@ -939,6 +947,43 @@ static ssize_t z_impl_zsock_recvfrom_partial_then_econnreset(
939947
return -ECONNRESET;
940948
}
941949

950+
static ssize_t z_impl_zsock_recvfrom_http_redirect_and_close(
951+
int sock, void *buf, size_t max_len, int flags, struct sockaddr *src_addr,
952+
socklen_t *addrlen)
953+
{
954+
switch (z_impl_zsock_recvfrom_fake.call_count) {
955+
case 1:
956+
memcpy(buf, HTTP_HDR_REDIRECT, strlen(HTTP_HDR_REDIRECT));
957+
return strlen(HTTP_HDR_REDIRECT);
958+
case 2:
959+
/* connection closed */
960+
return 0;
961+
}
962+
963+
return 0;
964+
}
965+
966+
int z_impl_zsock_socket_http_then_https(int family, int type, int proto)
967+
{
968+
switch (z_impl_zsock_socket_fake.call_count) {
969+
case 1:
970+
TEST_ASSERT_EQUAL(SOCK_STREAM, type);
971+
TEST_ASSERT_EQUAL(IPPROTO_TCP, proto);
972+
break;
973+
case 2:
974+
default:
975+
TEST_ASSERT_EQUAL(SOCK_STREAM, type);
976+
TEST_ASSERT_EQUAL(IPPROTO_TLS_1_2, proto);
977+
RESET_FAKE(z_impl_zsock_setsockopt);
978+
RESET_FAKE(z_impl_zsock_recvfrom);
979+
z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_https_ok;
980+
z_impl_zsock_recvfrom_fake.custom_fake =
981+
z_impl_zsock_recvfrom_http_header_then_data;
982+
}
983+
984+
return FD;
985+
}
986+
942987
static ssize_t z_impl_zsock_recvfrom_coap(
943988
int sock, void *buf, size_t max_len, int flags, struct sockaddr *src_addr,
944989
socklen_t *addrlen)
@@ -2323,6 +2368,33 @@ void test_downloader_downloaded_size_get(void)
23232368
dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1));
23242369
}
23252370

2371+
void test_downloader_http_redirect_to_https(void)
2372+
{
2373+
int err;
2374+
struct downloader_evt evt;
2375+
2376+
err = downloader_init(&dl, &dl_cfg);
2377+
TEST_ASSERT_EQUAL(0, err);
2378+
2379+
zsock_getaddrinfo_fake.custom_fake = zsock_getaddrinfo_server_ok;
2380+
zsock_freeaddrinfo_fake.custom_fake = zsock_freeaddrinfo_server_ipv6;
2381+
z_impl_zsock_socket_fake.custom_fake = z_impl_zsock_socket_http_then_https;
2382+
z_impl_zsock_connect_fake.custom_fake = z_impl_zsock_connect_ipv6_ok;
2383+
z_impl_zsock_setsockopt_fake.custom_fake = z_impl_zsock_setsockopt_http_ok;
2384+
z_impl_zsock_sendto_fake.custom_fake = z_impl_zsock_sendto_ok;
2385+
z_impl_zsock_recvfrom_fake.custom_fake = z_impl_zsock_recvfrom_http_redirect_and_close;
2386+
2387+
2388+
err = downloader_get(&dl, &dl_host_conf_w_sec_tags, HTTP_URL, 0);
2389+
TEST_ASSERT_EQUAL(0, err);
2390+
2391+
evt = dl_wait_for_event(DOWNLOADER_EVT_DONE, K_SECONDS(3));
2392+
2393+
downloader_deinit(&dl);
2394+
dl_wait_for_event(DOWNLOADER_EVT_DEINITIALIZED, K_SECONDS(1));
2395+
2396+
}
2397+
23262398
void setUp(void)
23272399
{
23282400
RESET_FAKE(z_impl_zsock_setsockopt);

0 commit comments

Comments
 (0)