1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24 /* <DESC>
25 * upload pausing
26 * </DESC>
27 */
28 /* This is based on the PoC client of issue #11769
29 */
30 #include <curl/curl.h>
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdlib.h>
35
36 #ifndef _MSC_VER
37 /* somewhat Unix-specific */
38 #include <unistd.h> /* getopt() */
39 #endif
40
41 #ifndef _MSC_VER
log_line_start(FILE * log,const char * idsbuf,curl_infotype type)42 static void log_line_start(FILE *log, const char *idsbuf, curl_infotype type)
43 {
44 /*
45 * This is the trace look that is similar to what libcurl makes on its
46 * own.
47 */
48 static const char * const s_infotype[] = {
49 "* ", "< ", "> ", "{ ", "} ", "{ ", "} "
50 };
51 if(idsbuf && *idsbuf)
52 fprintf(log, "%s%s", idsbuf, s_infotype[type]);
53 else
54 fputs(s_infotype[type], log);
55 }
56
57 #define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] "
58 #define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \
59 CURL_FORMAT_CURL_OFF_T "] "
60 /*
61 ** callback for CURLOPT_DEBUGFUNCTION
62 */
debug_cb(CURL * handle,curl_infotype type,char * data,size_t size,void * userdata)63 static int debug_cb(CURL *handle, curl_infotype type,
64 char *data, size_t size,
65 void *userdata)
66 {
67 FILE *output = stderr;
68 static int newl = 0;
69 static int traced_data = 0;
70 char idsbuf[60];
71 curl_off_t xfer_id, conn_id;
72
73 (void)handle; /* not used */
74 (void)userdata;
75
76 if(!curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) {
77 if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) &&
78 conn_id >= 0) {
79 curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2, xfer_id,
80 conn_id);
81 }
82 else {
83 curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id);
84 }
85 }
86 else
87 idsbuf[0] = 0;
88
89 switch(type) {
90 case CURLINFO_HEADER_OUT:
91 if(size > 0) {
92 size_t st = 0;
93 size_t i;
94 for(i = 0; i < size - 1; i++) {
95 if(data[i] == '\n') { /* LF */
96 if(!newl) {
97 log_line_start(output, idsbuf, type);
98 }
99 (void)fwrite(data + st, i - st + 1, 1, output);
100 st = i + 1;
101 newl = 0;
102 }
103 }
104 if(!newl)
105 log_line_start(output, idsbuf, type);
106 (void)fwrite(data + st, i - st + 1, 1, output);
107 }
108 newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
109 traced_data = 0;
110 break;
111 case CURLINFO_TEXT:
112 case CURLINFO_HEADER_IN:
113 if(!newl)
114 log_line_start(output, idsbuf, type);
115 (void)fwrite(data, size, 1, output);
116 newl = (size && (data[size - 1] != '\n')) ? 1 : 0;
117 traced_data = 0;
118 break;
119 case CURLINFO_DATA_OUT:
120 case CURLINFO_DATA_IN:
121 case CURLINFO_SSL_DATA_IN:
122 case CURLINFO_SSL_DATA_OUT:
123 if(!traced_data) {
124 if(!newl)
125 log_line_start(output, idsbuf, type);
126 fprintf(output, "[%ld bytes data]\n", (long)size);
127 newl = 0;
128 traced_data = 1;
129 }
130 break;
131 default: /* nada */
132 newl = 0;
133 traced_data = 1;
134 break;
135 }
136
137 return 0;
138 }
139
140 #define PAUSE_READ_AFTER 1
141 static size_t total_read = 0;
142
read_callback(char * ptr,size_t size,size_t nmemb,void * userdata)143 static size_t read_callback(char *ptr, size_t size, size_t nmemb,
144 void *userdata)
145 {
146 (void)size;
147 (void)nmemb;
148 (void)userdata;
149 if(total_read >= PAUSE_READ_AFTER) {
150 fprintf(stderr, "read_callback, return PAUSE\n");
151 return CURL_READFUNC_PAUSE;
152 }
153 else {
154 ptr[0] = '\n';
155 ++total_read;
156 fprintf(stderr, "read_callback, return 1 byte\n");
157 return 1;
158 }
159 }
160
progress_callback(void * clientp,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)161 static int progress_callback(void *clientp,
162 curl_off_t dltotal,
163 curl_off_t dlnow,
164 curl_off_t ultotal,
165 curl_off_t ulnow)
166 {
167 (void)dltotal;
168 (void)dlnow;
169 (void)ultotal;
170 (void)ulnow;
171 (void)clientp;
172 #if 0
173 /* Used to unpause on progress, but keeping for now. */
174 {
175 CURL *curl = (CURL *)clientp;
176 curl_easy_pause(curl, CURLPAUSE_CONT);
177 /* curl_easy_pause(curl, CURLPAUSE_RECV_CONT); */
178 }
179 #endif
180 return 0;
181 }
182
err(void)183 static int err(void)
184 {
185 fprintf(stderr, "something unexpected went wrong - bailing out!\n");
186 exit(2);
187 }
188
usage(const char * msg)189 static void usage(const char *msg)
190 {
191 if(msg)
192 fprintf(stderr, "%s\n", msg);
193 fprintf(stderr,
194 "usage: [options] url\n"
195 " upload and pause, options:\n"
196 " -V http_version (http/1.1, h2, h3) http version to use\n"
197 );
198 }
199 #endif /* !_MSC_VER */
200
main(int argc,char * argv[])201 int main(int argc, char *argv[])
202 {
203 #ifndef _MSC_VER
204 CURL *curl;
205 CURLcode rc = CURLE_OK;
206 CURLU *cu;
207 struct curl_slist *resolve = NULL;
208 char resolve_buf[1024];
209 char *url, *host = NULL, *port = NULL;
210 int http_version = CURL_HTTP_VERSION_1_1;
211 int ch;
212
213 while((ch = getopt(argc, argv, "V:")) != -1) {
214 switch(ch) {
215 case 'V': {
216 if(!strcmp("http/1.1", optarg))
217 http_version = CURL_HTTP_VERSION_1_1;
218 else if(!strcmp("h2", optarg))
219 http_version = CURL_HTTP_VERSION_2_0;
220 else if(!strcmp("h3", optarg))
221 http_version = CURL_HTTP_VERSION_3ONLY;
222 else {
223 usage("invalid http version");
224 return 1;
225 }
226 break;
227 }
228 default:
229 usage("invalid option");
230 return 1;
231 }
232 }
233 argc -= optind;
234 argv += optind;
235
236 if(argc != 1) {
237 usage("not enough arguments");
238 return 2;
239 }
240 url = argv[0];
241
242 curl_global_init(CURL_GLOBAL_DEFAULT);
243 curl_global_trace("ids,time");
244
245 cu = curl_url();
246 if(!cu) {
247 fprintf(stderr, "out of memory\n");
248 exit(1);
249 }
250 if(curl_url_set(cu, CURLUPART_URL, url, 0)) {
251 fprintf(stderr, "not a URL: '%s'\n", url);
252 exit(1);
253 }
254 if(curl_url_get(cu, CURLUPART_HOST, &host, 0)) {
255 fprintf(stderr, "could not get host of '%s'\n", url);
256 exit(1);
257 }
258 if(curl_url_get(cu, CURLUPART_PORT, &port, 0)) {
259 fprintf(stderr, "could not get port of '%s'\n", url);
260 exit(1);
261 }
262 memset(&resolve, 0, sizeof(resolve));
263 curl_msnprintf(resolve_buf, sizeof(resolve_buf)-1, "%s:%s:127.0.0.1",
264 host, port);
265 resolve = curl_slist_append(resolve, resolve_buf);
266
267 curl = curl_easy_init();
268 if(!curl) {
269 fprintf(stderr, "out of memory\n");
270 exit(1);
271 }
272 /* We want to use our own read function. */
273 curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
274
275 /* It will help us to continue the read function. */
276 curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
277 curl_easy_setopt(curl, CURLOPT_XFERINFODATA, curl);
278 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
279
280 /* It will help us to ensure that keepalive does not help. */
281 curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
282 curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 1L);
283 curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 1L);
284 curl_easy_setopt(curl, CURLOPT_TCP_KEEPCNT, 1L);
285
286 /* Enable uploading. */
287 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
288 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
289
290 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
291 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
292
293 if(curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L) != CURLE_OK ||
294 curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, debug_cb)
295 != CURLE_OK ||
296 curl_easy_setopt(curl, CURLOPT_RESOLVE, resolve) != CURLE_OK)
297 err();
298
299 curl_easy_setopt(curl, CURLOPT_URL, url);
300 curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, http_version);
301
302 rc = curl_easy_perform(curl);
303
304 if(curl) {
305 curl_easy_cleanup(curl);
306 }
307
308 curl_slist_free_all(resolve);
309 curl_free(host);
310 curl_free(port);
311 curl_url_cleanup(cu);
312 curl_global_cleanup();
313
314 return (int)rc;
315 #else
316 (void)argc;
317 (void)argv;
318 fprintf(stderr, "Not supported with this compiler.\n");
319 return 1;
320 #endif /* !_MSC_VER */
321 }
322