xref: /curl/tests/http/clients/upload-pausing.c (revision c730c854)
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,double dltotal,double dlnow,double ultotal,double ulnow)161 static int progress_callback(void *clientp,
162                              double dltotal,
163                              double dlnow,
164                              double ultotal,
165                              double 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