-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathhttp.c
More file actions
156 lines (128 loc) · 3.32 KB
/
Copy pathhttp.c
File metadata and controls
156 lines (128 loc) · 3.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file http.c
* @brief HTTP/HTTPS client implementation using libcurl.
*
* TLS verification uses the bundled Mozilla CA store (see ca_certs.c
* and vendor/cacert/) rather than the host system's trust store, so
* downloads behave identically regardless of the user's
* ca-certificates package state.
*/
#include <curl/curl.h>
#include "ca_certs.h"
#include "ice.h"
/**
* @brief Context for http_download's curl callbacks.
*/
struct download_ctx {
http_progress_fn progress;
void *user_ctx;
};
/**
* @brief libcurl write callback that appends data to an sbuf.
*/
static size_t write_to_sbuf(void *data, size_t size, size_t nmemb, void *userp)
{
struct sbuf *sb = userp;
size_t bytes = size * nmemb;
sbuf_add(sb, data, bytes);
return bytes;
}
/**
* @brief libcurl progress callback that delegates to the user's
* progress function.
*/
static int curl_progress_cb(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
curl_off_t ultotal, curl_off_t ulnow)
{
struct download_ctx *ctx = clientp;
(void)ultotal;
(void)ulnow;
if (ctx->progress)
ctx->progress((size_t)dltotal, (size_t)dlnow, ctx->user_ctx);
return 0;
}
/**
* @brief Set common curl options (timeouts, redirects, errors).
*/
static void curl_set_defaults(CURL *curl, const char *url)
{
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "esp-ice/" VERSION);
ca_certs_apply(curl);
}
void http_default_progress(size_t total, size_t now, void *ctx)
{
(void)ctx;
if (!total)
fprintf(stderr, "\r %zu bytes", now);
else
fprintf(stderr, "\r %d%% %zu / %zu bytes",
(int)(now * 100 / total), now, total);
}
int http_download(const char *url, const char *path, http_progress_fn progress,
void *ctx)
{
CURL *curl;
FILE *fp;
CURLcode res;
struct download_ctx dl_ctx = {progress, ctx};
fp = fopen(path, "wb");
if (!fp) {
err_errno("cannot create '%s'", path);
return -1;
}
curl = curl_easy_init();
if (!curl) {
fclose(fp);
err("curl_easy_init failed");
return -1;
}
curl_set_defaults(curl, url);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
if (progress) {
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION,
curl_progress_cb);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &dl_ctx);
}
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
if (res != CURLE_OK) {
err("download failed: %s: %s", url, curl_easy_strerror(res));
return -1;
}
return 0;
}
int http_get(const char *url, struct sbuf *body)
{
CURL *curl;
CURLcode res;
long status;
curl = curl_easy_init();
if (!curl) {
err("curl_easy_init failed");
return -1;
}
curl_set_defaults(curl, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_to_sbuf);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, body);
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
err("HTTP request failed: %s: %s", url,
curl_easy_strerror(res));
curl_easy_cleanup(curl);
return -1;
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
curl_easy_cleanup(curl);
return (int)status;
}