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
25 #include "curl_setup.h"
26
27 #include "urldata.h"
28 #include "cfilters.h"
29 #include "dynbuf.h"
30 #include "doh.h"
31 #include "multiif.h"
32 #include "progress.h"
33 #include "request.h"
34 #include "sendf.h"
35 #include "transfer.h"
36 #include "url.h"
37
38 /* The last 3 #include files should be in this order */
39 #include "curl_printf.h"
40 #include "curl_memory.h"
41 #include "memdebug.h"
42
Curl_req_init(struct SingleRequest * req)43 void Curl_req_init(struct SingleRequest *req)
44 {
45 memset(req, 0, sizeof(*req));
46 }
47
Curl_req_soft_reset(struct SingleRequest * req,struct Curl_easy * data)48 CURLcode Curl_req_soft_reset(struct SingleRequest *req,
49 struct Curl_easy *data)
50 {
51 CURLcode result;
52
53 req->done = FALSE;
54 req->upload_done = FALSE;
55 req->download_done = FALSE;
56 req->ignorebody = FALSE;
57 req->shutdown = FALSE;
58 req->bytecount = 0;
59 req->writebytecount = 0;
60 req->header = TRUE; /* assume header */
61 req->headerline = 0;
62 req->headerbytecount = 0;
63 req->allheadercount = 0;
64 req->deductheadercount = 0;
65
66 result = Curl_client_start(data);
67 if(result)
68 return result;
69
70 if(!req->sendbuf_init) {
71 Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1,
72 BUFQ_OPT_SOFT_LIMIT);
73 req->sendbuf_init = TRUE;
74 }
75 else {
76 Curl_bufq_reset(&req->sendbuf);
77 if(data->set.upload_buffer_size != req->sendbuf.chunk_size) {
78 Curl_bufq_free(&req->sendbuf);
79 Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1,
80 BUFQ_OPT_SOFT_LIMIT);
81 }
82 }
83
84 return CURLE_OK;
85 }
86
Curl_req_start(struct SingleRequest * req,struct Curl_easy * data)87 CURLcode Curl_req_start(struct SingleRequest *req,
88 struct Curl_easy *data)
89 {
90 req->start = Curl_now();
91 return Curl_req_soft_reset(req, data);
92 }
93
94 static CURLcode req_flush(struct Curl_easy *data);
95
Curl_req_done(struct SingleRequest * req,struct Curl_easy * data,bool aborted)96 CURLcode Curl_req_done(struct SingleRequest *req,
97 struct Curl_easy *data, bool aborted)
98 {
99 (void)req;
100 if(!aborted)
101 (void)req_flush(data);
102 Curl_client_reset(data);
103 return CURLE_OK;
104 }
105
Curl_req_hard_reset(struct SingleRequest * req,struct Curl_easy * data)106 void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data)
107 {
108 struct curltime t0 = {0, 0};
109
110 /* This is a bit ugly. `req->p` is a union and we assume we can
111 * free this safely without leaks. */
112 Curl_safefree(req->p.ftp);
113 Curl_safefree(req->newurl);
114 Curl_client_reset(data);
115 if(req->sendbuf_init)
116 Curl_bufq_reset(&req->sendbuf);
117
118 #ifndef CURL_DISABLE_DOH
119 if(req->doh) {
120 Curl_close(&req->doh->probe[0].easy);
121 Curl_close(&req->doh->probe[1].easy);
122 }
123 #endif
124 /* Can no longer memset() this struct as we need to keep some state */
125 req->size = -1;
126 req->maxdownload = -1;
127 req->bytecount = 0;
128 req->writebytecount = 0;
129 req->start = t0;
130 req->headerbytecount = 0;
131 req->allheadercount = 0;
132 req->deductheadercount = 0;
133 req->headerline = 0;
134 req->offset = 0;
135 req->httpcode = 0;
136 req->keepon = 0;
137 req->upgr101 = UPGR101_INIT;
138 req->timeofdoc = 0;
139 req->location = NULL;
140 req->newurl = NULL;
141 #ifndef CURL_DISABLE_COOKIES
142 req->setcookies = 0;
143 #endif
144 req->header = FALSE;
145 req->content_range = FALSE;
146 req->download_done = FALSE;
147 req->eos_written = FALSE;
148 req->eos_read = FALSE;
149 req->upload_done = FALSE;
150 req->upload_aborted = FALSE;
151 req->ignorebody = FALSE;
152 req->http_bodyless = FALSE;
153 req->chunk = FALSE;
154 req->ignore_cl = FALSE;
155 req->upload_chunky = FALSE;
156 req->getheader = FALSE;
157 req->no_body = data->set.opt_no_body;
158 req->authneg = FALSE;
159 req->shutdown = FALSE;
160 #ifdef USE_HYPER
161 req->bodywritten = FALSE;
162 #endif
163 }
164
Curl_req_free(struct SingleRequest * req,struct Curl_easy * data)165 void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data)
166 {
167 /* This is a bit ugly. `req->p` is a union and we assume we can
168 * free this safely without leaks. */
169 Curl_safefree(req->p.ftp);
170 Curl_safefree(req->newurl);
171 if(req->sendbuf_init)
172 Curl_bufq_free(&req->sendbuf);
173 Curl_client_cleanup(data);
174
175 #ifndef CURL_DISABLE_DOH
176 if(req->doh) {
177 Curl_close(&req->doh->probe[0].easy);
178 Curl_close(&req->doh->probe[1].easy);
179 Curl_dyn_free(&req->doh->probe[0].serverdoh);
180 Curl_dyn_free(&req->doh->probe[1].serverdoh);
181 curl_slist_free_all(req->doh->headers);
182 Curl_safefree(req->doh);
183 }
184 #endif
185 }
186
xfer_send(struct Curl_easy * data,const char * buf,size_t blen,size_t hds_len,size_t * pnwritten)187 static CURLcode xfer_send(struct Curl_easy *data,
188 const char *buf, size_t blen,
189 size_t hds_len, size_t *pnwritten)
190 {
191 CURLcode result = CURLE_OK;
192
193 *pnwritten = 0;
194 #ifdef DEBUGBUILD
195 {
196 /* Allow debug builds to override this logic to force short initial
197 sends
198 */
199 char *p = getenv("CURL_SMALLREQSEND");
200 if(p) {
201 size_t altsize = (size_t)strtoul(p, NULL, 10);
202 if(altsize && altsize < blen)
203 blen = altsize;
204 }
205 }
206 #endif
207 /* Make sure this doesn't send more body bytes than what the max send
208 speed says. The headers do not count to the max speed. */
209 if(data->set.max_send_speed) {
210 size_t body_bytes = blen - hds_len;
211 if((curl_off_t)body_bytes > data->set.max_send_speed)
212 blen = hds_len + (size_t)data->set.max_send_speed;
213 }
214
215 result = Curl_xfer_send(data, buf, blen, pnwritten);
216 if(!result && *pnwritten) {
217 if(hds_len)
218 Curl_debug(data, CURLINFO_HEADER_OUT, (char *)buf,
219 CURLMIN(hds_len, *pnwritten));
220 if(*pnwritten > hds_len) {
221 size_t body_len = *pnwritten - hds_len;
222 Curl_debug(data, CURLINFO_DATA_OUT, (char *)buf + hds_len, body_len);
223 data->req.writebytecount += body_len;
224 Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
225 }
226 }
227 return result;
228 }
229
req_send_buffer_flush(struct Curl_easy * data)230 static CURLcode req_send_buffer_flush(struct Curl_easy *data)
231 {
232 CURLcode result = CURLE_OK;
233 const unsigned char *buf;
234 size_t blen;
235
236 while(Curl_bufq_peek(&data->req.sendbuf, &buf, &blen)) {
237 size_t nwritten, hds_len = CURLMIN(data->req.sendbuf_hds_len, blen);
238 result = xfer_send(data, (const char *)buf, blen, hds_len, &nwritten);
239 if(result)
240 break;
241
242 Curl_bufq_skip(&data->req.sendbuf, nwritten);
243 if(hds_len) {
244 data->req.sendbuf_hds_len -= CURLMIN(hds_len, nwritten);
245 }
246 /* leave if we could not send all. Maybe network blocking or
247 * speed limits on transfer */
248 if(nwritten < blen)
249 break;
250 }
251 return result;
252 }
253
req_set_upload_done(struct Curl_easy * data)254 static CURLcode req_set_upload_done(struct Curl_easy *data)
255 {
256 DEBUGASSERT(!data->req.upload_done);
257 data->req.upload_done = TRUE;
258 data->req.keepon &= ~(KEEP_SEND|KEEP_SEND_TIMED); /* we're done sending */
259
260 Curl_creader_done(data, data->req.upload_aborted);
261
262 if(data->req.upload_aborted) {
263 if(data->req.writebytecount)
264 infof(data, "abort upload after having sent %" CURL_FORMAT_CURL_OFF_T
265 " bytes", data->req.writebytecount);
266 else
267 infof(data, "abort upload");
268 }
269 else if(data->req.writebytecount)
270 infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
271 " bytes", data->req.writebytecount);
272 else if(!data->req.download_done)
273 infof(data, Curl_creader_total_length(data)?
274 "We are completely uploaded and fine" :
275 "Request completely sent off");
276
277 return Curl_xfer_send_close(data);
278 }
279
req_flush(struct Curl_easy * data)280 static CURLcode req_flush(struct Curl_easy *data)
281 {
282 CURLcode result;
283
284 if(!data || !data->conn)
285 return CURLE_FAILED_INIT;
286
287 if(!Curl_bufq_is_empty(&data->req.sendbuf)) {
288 result = req_send_buffer_flush(data);
289 if(result)
290 return result;
291 if(!Curl_bufq_is_empty(&data->req.sendbuf)) {
292 return CURLE_AGAIN;
293 }
294 }
295
296 if(!data->req.upload_done && data->req.eos_read &&
297 Curl_bufq_is_empty(&data->req.sendbuf)) {
298 if(data->req.shutdown) {
299 bool done;
300 result = Curl_xfer_send_shutdown(data, &done);
301 if(result)
302 return result;
303 if(!done)
304 return CURLE_AGAIN;
305 }
306 return req_set_upload_done(data);
307 }
308 return CURLE_OK;
309 }
310
add_from_client(void * reader_ctx,unsigned char * buf,size_t buflen,CURLcode * err)311 static ssize_t add_from_client(void *reader_ctx,
312 unsigned char *buf, size_t buflen,
313 CURLcode *err)
314 {
315 struct Curl_easy *data = reader_ctx;
316 size_t nread;
317 bool eos;
318
319 *err = Curl_client_read(data, (char *)buf, buflen, &nread, &eos);
320 if(*err)
321 return -1;
322 if(eos)
323 data->req.eos_read = TRUE;
324 return (ssize_t)nread;
325 }
326
327 #ifndef USE_HYPER
328
req_send_buffer_add(struct Curl_easy * data,const char * buf,size_t blen,size_t hds_len)329 static CURLcode req_send_buffer_add(struct Curl_easy *data,
330 const char *buf, size_t blen,
331 size_t hds_len)
332 {
333 CURLcode result = CURLE_OK;
334 ssize_t n;
335 n = Curl_bufq_write(&data->req.sendbuf,
336 (const unsigned char *)buf, blen, &result);
337 if(n < 0)
338 return result;
339 /* We rely on a SOFTLIMIT on sendbuf, so it can take all data in */
340 DEBUGASSERT((size_t)n == blen);
341 data->req.sendbuf_hds_len += hds_len;
342 return CURLE_OK;
343 }
344
Curl_req_send(struct Curl_easy * data,struct dynbuf * req)345 CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req)
346 {
347 CURLcode result;
348 const char *buf;
349 size_t blen, nwritten;
350
351 if(!data || !data->conn)
352 return CURLE_FAILED_INIT;
353
354 buf = Curl_dyn_ptr(req);
355 blen = Curl_dyn_len(req);
356 if(!Curl_creader_total_length(data)) {
357 /* Request without body. Try to send directly from the buf given. */
358 data->req.eos_read = TRUE;
359 result = xfer_send(data, buf, blen, blen, &nwritten);
360 if(result)
361 return result;
362 buf += nwritten;
363 blen -= nwritten;
364 }
365
366 if(blen) {
367 /* Either we have a request body, or we could not send the complete
368 * request in one go. Buffer the remainder and try to add as much
369 * body bytes as room is left in the buffer. Then flush. */
370 result = req_send_buffer_add(data, buf, blen, blen);
371 if(result)
372 return result;
373
374 return Curl_req_send_more(data);
375 }
376 return CURLE_OK;
377 }
378 #endif /* !USE_HYPER */
379
Curl_req_want_send(struct Curl_easy * data)380 bool Curl_req_want_send(struct Curl_easy *data)
381 {
382 return data->req.sendbuf_init && !Curl_bufq_is_empty(&data->req.sendbuf);
383 }
384
Curl_req_done_sending(struct Curl_easy * data)385 bool Curl_req_done_sending(struct Curl_easy *data)
386 {
387 if(data->req.upload_done) {
388 DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf));
389 return TRUE;
390 }
391 return FALSE;
392 }
393
Curl_req_send_more(struct Curl_easy * data)394 CURLcode Curl_req_send_more(struct Curl_easy *data)
395 {
396 CURLcode result;
397
398 /* Fill our send buffer if more from client can be read. */
399 if(!data->req.eos_read && !Curl_bufq_is_full(&data->req.sendbuf)) {
400 ssize_t nread = Curl_bufq_sipn(&data->req.sendbuf, 0,
401 add_from_client, data, &result);
402 if(nread < 0 && result != CURLE_AGAIN)
403 return result;
404 }
405
406 result = req_flush(data);
407 if(result == CURLE_AGAIN)
408 result = CURLE_OK;
409
410 return result;
411 }
412
Curl_req_abort_sending(struct Curl_easy * data)413 CURLcode Curl_req_abort_sending(struct Curl_easy *data)
414 {
415 if(!data->req.upload_done) {
416 Curl_bufq_reset(&data->req.sendbuf);
417 data->req.upload_aborted = TRUE;
418 return req_set_upload_done(data);
419 }
420 return CURLE_OK;
421 }
422