1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) 1998 - 2020, 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 | ***************************************************************************/ |
22 | |
23 | #include <time.h> |
24 | |
25 | #include "test.h" |
26 | |
27 | #include "memdebug.h" |
28 | |
29 | #define PAUSE_TIME 2 |
30 | |
31 | |
32 | static const char name[] = "field" ; |
33 | |
34 | struct ReadThis { |
35 | CURL *easy; |
36 | time_t origin; |
37 | int count; |
38 | }; |
39 | |
40 | |
41 | static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *userp) |
42 | { |
43 | struct ReadThis *pooh = (struct ReadThis *) userp; |
44 | time_t delta; |
45 | |
46 | if(size * nmemb < 1) |
47 | return 0; |
48 | |
49 | switch(pooh->count++) { |
50 | case 0: |
51 | *ptr = '\x41'; /* ASCII A. */ |
52 | return 1; |
53 | case 1: |
54 | pooh->origin = time(NULL); |
55 | return CURL_READFUNC_PAUSE; |
56 | case 2: |
57 | delta = time(NULL) - pooh->origin; |
58 | *ptr = delta >= PAUSE_TIME? '\x42': '\x41'; /* ASCII A or B. */ |
59 | return 1; |
60 | case 3: |
61 | return 0; |
62 | } |
63 | fprintf(stderr, "Read callback called after EOF\n" ); |
64 | exit(1); |
65 | } |
66 | |
67 | #if !defined(LIB670) && !defined(LIB672) |
68 | static int xferinfo(void *clientp, curl_off_t dltotal, curl_off_t dlnow, |
69 | curl_off_t ultotal, curl_off_t ulnow) |
70 | { |
71 | struct ReadThis *pooh = (struct ReadThis *) clientp; |
72 | |
73 | (void) dltotal; |
74 | (void) dlnow; |
75 | (void) ultotal; |
76 | (void) ulnow; |
77 | |
78 | if(pooh->origin) { |
79 | time_t delta = time(NULL) - pooh->origin; |
80 | |
81 | if(delta >= 4 * PAUSE_TIME) { |
82 | fprintf(stderr, "unpausing failed: drain problem?\n" ); |
83 | return CURLE_ABORTED_BY_CALLBACK; |
84 | } |
85 | |
86 | if(delta >= PAUSE_TIME) |
87 | curl_easy_pause(pooh->easy, CURLPAUSE_CONT); |
88 | } |
89 | |
90 | return 0; |
91 | } |
92 | #endif |
93 | |
94 | int test(char *URL) |
95 | { |
96 | #if defined(LIB670) || defined(LIB671) |
97 | curl_mime *mime = NULL; |
98 | curl_mimepart *part; |
99 | #else |
100 | CURLFORMcode formrc; |
101 | struct curl_httppost *formpost = NULL; |
102 | struct curl_httppost *lastptr = NULL; |
103 | #endif |
104 | #if defined(LIB670) || defined(LIB672) |
105 | CURLM *multi = NULL; |
106 | CURLMcode mres; |
107 | CURLMsg *msg; |
108 | int msgs_left; |
109 | int still_running = 0; |
110 | #endif |
111 | |
112 | struct ReadThis pooh; |
113 | CURLcode result; |
114 | int res = TEST_ERR_FAILURE; |
115 | |
116 | /* |
117 | * Check proper pausing/unpausing from a mime or form read callback. |
118 | */ |
119 | |
120 | if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { |
121 | fprintf(stderr, "curl_global_init() failed\n" ); |
122 | return TEST_ERR_MAJOR_BAD; |
123 | } |
124 | |
125 | pooh.origin = (time_t) 0; |
126 | pooh.count = 0; |
127 | pooh.easy = curl_easy_init(); |
128 | |
129 | /* First set the URL that is about to receive our POST. */ |
130 | test_setopt(pooh.easy, CURLOPT_URL, URL); |
131 | |
132 | /* get verbose debug output please */ |
133 | test_setopt(pooh.easy, CURLOPT_VERBOSE, 1L); |
134 | |
135 | /* include headers in the output */ |
136 | test_setopt(pooh.easy, CURLOPT_HEADER, 1L); |
137 | |
138 | #if defined(LIB670) || defined(LIB671) |
139 | /* Build the mime tree. */ |
140 | mime = curl_mime_init(pooh.easy); |
141 | part = curl_mime_addpart(mime); |
142 | result = curl_mime_name(part, name); |
143 | if(!result) |
144 | res = curl_mime_data_cb(part, (curl_off_t) 2, read_callback, |
145 | NULL, NULL, &pooh); |
146 | |
147 | if(result) { |
148 | fprintf(stderr, |
149 | "Something went wrong when building the mime structure: %d\n" , |
150 | (int) result); |
151 | goto test_cleanup; |
152 | } |
153 | |
154 | /* Bind mime data to its easy handle. */ |
155 | if(!res) |
156 | test_setopt(pooh.easy, CURLOPT_MIMEPOST, mime); |
157 | #else |
158 | /* Build the form. */ |
159 | formrc = curl_formadd(&formpost, &lastptr, |
160 | CURLFORM_COPYNAME, name, |
161 | CURLFORM_STREAM, &pooh, |
162 | CURLFORM_CONTENTLEN, (curl_off_t) 2, |
163 | CURLFORM_END); |
164 | if(formrc) { |
165 | fprintf(stderr, "curl_formadd() = %d\n" , (int) formrc); |
166 | goto test_cleanup; |
167 | } |
168 | |
169 | /* We want to use our own read function. */ |
170 | test_setopt(pooh.easy, CURLOPT_READFUNCTION, read_callback); |
171 | |
172 | /* Send a multi-part formpost. */ |
173 | test_setopt(pooh.easy, CURLOPT_HTTPPOST, formpost); |
174 | #endif |
175 | |
176 | #if defined(LIB670) || defined(LIB672) |
177 | /* Use the multi interface. */ |
178 | multi = curl_multi_init(); |
179 | mres = curl_multi_add_handle(multi, pooh.easy); |
180 | while(!mres) { |
181 | struct timeval timeout; |
182 | int rc = 0; |
183 | fd_set fdread; |
184 | fd_set fdwrite; |
185 | fd_set fdexcept; |
186 | int maxfd = -1; |
187 | |
188 | mres = curl_multi_perform(multi, &still_running); |
189 | if(!still_running || mres != CURLM_OK) |
190 | break; |
191 | |
192 | if(pooh.origin) { |
193 | time_t delta = time(NULL) - pooh.origin; |
194 | |
195 | if(delta >= 4 * PAUSE_TIME) { |
196 | fprintf(stderr, "unpausing failed: drain problem?\n" ); |
197 | res = CURLE_OPERATION_TIMEDOUT; |
198 | break; |
199 | } |
200 | |
201 | if(delta >= PAUSE_TIME) |
202 | curl_easy_pause(pooh.easy, CURLPAUSE_CONT); |
203 | } |
204 | |
205 | FD_ZERO(&fdread); |
206 | FD_ZERO(&fdwrite); |
207 | FD_ZERO(&fdexcept); |
208 | timeout.tv_sec = 0; |
209 | timeout.tv_usec = 1000000 * PAUSE_TIME / 10; |
210 | mres = curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcept, &maxfd); |
211 | if(mres) |
212 | break; |
213 | #if defined(WIN32) || defined(_WIN32) |
214 | if(maxfd == -1) |
215 | Sleep(100); |
216 | else |
217 | #endif |
218 | rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcept, &timeout); |
219 | if(rc == -1) { |
220 | fprintf(stderr, "Select error\n" ); |
221 | break; |
222 | } |
223 | } |
224 | |
225 | if(mres != CURLM_OK) |
226 | for(;;) { |
227 | msg = curl_multi_info_read(multi, &msgs_left); |
228 | if(!msg) |
229 | break; |
230 | if(msg->msg == CURLMSG_DONE) { |
231 | result = msg->data.result; |
232 | res = (int) result; |
233 | } |
234 | } |
235 | |
236 | curl_multi_remove_handle(multi, pooh.easy); |
237 | curl_multi_cleanup(multi); |
238 | |
239 | #else |
240 | /* Use the easy interface. */ |
241 | test_setopt(pooh.easy, CURLOPT_XFERINFODATA, &pooh); |
242 | test_setopt(pooh.easy, CURLOPT_XFERINFOFUNCTION, xferinfo); |
243 | test_setopt(pooh.easy, CURLOPT_NOPROGRESS, 0L); |
244 | result = curl_easy_perform(pooh.easy); |
245 | res = (int) result; |
246 | #endif |
247 | |
248 | |
249 | test_cleanup: |
250 | curl_easy_cleanup(pooh.easy); |
251 | #if defined(LIB670) || defined(LIB671) |
252 | curl_mime_free(mime); |
253 | #else |
254 | curl_formfree(formpost); |
255 | #endif |
256 | |
257 | curl_global_cleanup(); |
258 | return res; |
259 | } |
260 | |