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->upload_aborted = FALSE;
56 req->download_done = FALSE;
57 req->eos_written = FALSE;
58 req->eos_read = FALSE;
59 req->eos_sent = FALSE;
60 req->ignorebody = FALSE;
61 req->shutdown = FALSE;
62 req->bytecount = 0;
63 req->writebytecount = 0;
64 req->header = TRUE; /* assume header */
65 req->headerline = 0;
66 req->headerbytecount = 0;
67 req->allheadercount = 0;
68 req->deductheadercount = 0;
69
70 result = Curl_client_start(data);
71 if(result)
72 return result;
73
74 if(!req->sendbuf_init) {
75 Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1,
76 BUFQ_OPT_SOFT_LIMIT);
77 req->sendbuf_init = TRUE;
78 }
79 else {
80 Curl_bufq_reset(&req->sendbuf);
81 if(data->set.upload_buffer_size != req->sendbuf.chunk_size) {
82 Curl_bufq_free(&req->sendbuf);
83 Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1,
84 BUFQ_OPT_SOFT_LIMIT);
85 }
86 }
87
88 return CURLE_OK;
89 }
90
Curl_req_start(struct SingleRequest * req,struct Curl_easy * data)91 CURLcode Curl_req_start(struct SingleRequest *req,
92 struct Curl_easy *data)
93 {
94 req->start = Curl_now();
95 return Curl_req_soft_reset(req, data);
96 }
97
98 static CURLcode req_flush(struct Curl_easy *data);
99
Curl_req_done(struct SingleRequest * req,struct Curl_easy * data,bool aborted)100 CURLcode Curl_req_done(struct SingleRequest *req,
101 struct Curl_easy *data, bool aborted)
102 {
103 (void)req;
104 if(!aborted)
105 (void)req_flush(data);
106 Curl_client_reset(data);
107 #ifndef CURL_DISABLE_DOH
108 Curl_doh_close(data);
109 #endif
110 return CURLE_OK;
111 }
112
Curl_req_hard_reset(struct SingleRequest * req,struct Curl_easy * data)113 void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data)
114 {
115 struct curltime t0 = {0, 0};
116
117 /* This is a bit ugly. `req->p` is a union and we assume we can
118 * free this safely without leaks. */
119 Curl_safefree(req->p.ftp);
120 Curl_safefree(req->newurl);
121 Curl_client_reset(data);
122 if(req->sendbuf_init)
123 Curl_bufq_reset(&req->sendbuf);
124
125 #ifndef CURL_DISABLE_DOH
126 Curl_doh_close(data);
127 #endif
128 /* Can no longer memset() this struct as we need to keep some state */
129 req->size = -1;
130 req->maxdownload = -1;
131 req->bytecount = 0;
132 req->writebytecount = 0;
133 req->start = t0;
134 req->headerbytecount = 0;
135 req->allheadercount = 0;
136 req->deductheadercount = 0;
137 req->headerline = 0;
138 req->offset = 0;
139 req->httpcode = 0;
140 req->keepon = 0;
141 req->upgr101 = UPGR101_INIT;
142 req->timeofdoc = 0;
143 req->location = NULL;
144 req->newurl = NULL;
145 #ifndef CURL_DISABLE_COOKIES
146 req->setcookies = 0;
147 #endif
148 req->header = FALSE;
149 req->content_range = FALSE;
150 req->download_done = FALSE;
151 req->eos_written = FALSE;
152 req->eos_read = FALSE;
153 req->eos_sent = FALSE;
154 req->upload_done = FALSE;
155 req->upload_aborted = FALSE;
156 req->ignorebody = FALSE;
157 req->http_bodyless = FALSE;
158 req->chunk = FALSE;
159 req->ignore_cl = FALSE;
160 req->upload_chunky = FALSE;
161 req->getheader = FALSE;
162 req->no_body = data->set.opt_no_body;
163 req->authneg = FALSE;
164 req->shutdown = FALSE;
165 }
166
Curl_req_free(struct SingleRequest * req,struct Curl_easy * data)167 void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data)
168 {
169 /* This is a bit ugly. `req->p` is a union and we assume we can
170 * free this safely without leaks. */
171 Curl_safefree(req->p.ftp);
172 Curl_safefree(req->newurl);
173 if(req->sendbuf_init)
174 Curl_bufq_free(&req->sendbuf);
175 Curl_client_cleanup(data);
176
177 #ifndef CURL_DISABLE_DOH
178 Curl_doh_cleanup(data);
179 #endif
180 }
181
xfer_send(struct Curl_easy * data,const char * buf,size_t blen,size_t hds_len,size_t * pnwritten)182 static CURLcode xfer_send(struct Curl_easy *data,
183 const char *buf, size_t blen,
184 size_t hds_len, size_t *pnwritten)
185 {
186 CURLcode result = CURLE_OK;
187 bool eos = FALSE;
188
189 *pnwritten = 0;
190 DEBUGASSERT(hds_len <= blen);
191 #ifdef DEBUGBUILD
192 {
193 /* Allow debug builds to override this logic to force short initial
194 sends */
195 size_t body_len = blen - hds_len;
196 char *p = getenv("CURL_SMALLREQSEND");
197 if(p) {
198 size_t body_small = (size_t)strtoul(p, NULL, 10);
199 if(body_small && body_small < body_len)
200 blen = hds_len + body_small;
201 }
202 }
203 #endif
204 /* Make sure this does not send more body bytes than what the max send
205 speed says. The headers do not count to the max speed. */
206 if(data->set.max_send_speed) {
207 size_t body_bytes = blen - hds_len;
208 if((curl_off_t)body_bytes > data->set.max_send_speed)
209 blen = hds_len + (size_t)data->set.max_send_speed;
210 }
211
212 if(data->req.eos_read &&
213 (Curl_bufq_is_empty(&data->req.sendbuf) ||
214 Curl_bufq_len(&data->req.sendbuf) == blen)) {
215 DEBUGF(infof(data, "sending last upload chunk of %zu bytes", blen));
216 eos = TRUE;
217 }
218 result = Curl_xfer_send(data, buf, blen, eos, pnwritten);
219 if(!result) {
220 if(eos && (blen == *pnwritten))
221 data->req.eos_sent = TRUE;
222 if(*pnwritten) {
223 if(hds_len)
224 Curl_debug(data, CURLINFO_HEADER_OUT, (char *)buf,
225 CURLMIN(hds_len, *pnwritten));
226 if(*pnwritten > hds_len) {
227 size_t body_len = *pnwritten - hds_len;
228 Curl_debug(data, CURLINFO_DATA_OUT, (char *)buf + hds_len, body_len);
229 data->req.writebytecount += body_len;
230 Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
231 }
232 }
233 }
234 return result;
235 }
236
req_send_buffer_flush(struct Curl_easy * data)237 static CURLcode req_send_buffer_flush(struct Curl_easy *data)
238 {
239 CURLcode result = CURLE_OK;
240 const unsigned char *buf;
241 size_t blen;
242
243 while(Curl_bufq_peek(&data->req.sendbuf, &buf, &blen)) {
244 size_t nwritten, hds_len = CURLMIN(data->req.sendbuf_hds_len, blen);
245 result = xfer_send(data, (const char *)buf, blen, hds_len, &nwritten);
246 if(result)
247 break;
248
249 Curl_bufq_skip(&data->req.sendbuf, nwritten);
250 if(hds_len) {
251 data->req.sendbuf_hds_len -= CURLMIN(hds_len, nwritten);
252 }
253 /* leave if we could not send all. Maybe network blocking or
254 * speed limits on transfer */
255 if(nwritten < blen)
256 break;
257 }
258 return result;
259 }
260
req_set_upload_done(struct Curl_easy * data)261 static CURLcode req_set_upload_done(struct Curl_easy *data)
262 {
263 DEBUGASSERT(!data->req.upload_done);
264 data->req.upload_done = TRUE;
265 data->req.keepon &= ~(KEEP_SEND|KEEP_SEND_TIMED); /* we are done sending */
266
267 Curl_pgrsTime(data, TIMER_POSTRANSFER);
268 Curl_creader_done(data, data->req.upload_aborted);
269
270 if(data->req.upload_aborted) {
271 Curl_bufq_reset(&data->req.sendbuf);
272 if(data->req.writebytecount)
273 infof(data, "abort upload after having sent %" FMT_OFF_T " bytes",
274 data->req.writebytecount);
275 else
276 infof(data, "abort upload");
277 }
278 else if(data->req.writebytecount)
279 infof(data, "upload completely sent off: %" FMT_OFF_T " bytes",
280 data->req.writebytecount);
281 else if(!data->req.download_done) {
282 DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf));
283 infof(data, Curl_creader_total_length(data) ?
284 "We are completely uploaded and fine" :
285 "Request completely sent off");
286 }
287
288 return Curl_xfer_send_close(data);
289 }
290
req_flush(struct Curl_easy * data)291 static CURLcode req_flush(struct Curl_easy *data)
292 {
293 CURLcode result;
294
295 if(!data || !data->conn)
296 return CURLE_FAILED_INIT;
297
298 if(!Curl_bufq_is_empty(&data->req.sendbuf)) {
299 result = req_send_buffer_flush(data);
300 if(result)
301 return result;
302 if(!Curl_bufq_is_empty(&data->req.sendbuf)) {
303 DEBUGF(infof(data, "Curl_req_flush(len=%zu) -> EAGAIN",
304 Curl_bufq_len(&data->req.sendbuf)));
305 return CURLE_AGAIN;
306 }
307 }
308 else if(Curl_xfer_needs_flush(data)) {
309 DEBUGF(infof(data, "Curl_req_flush(), xfer send_pending"));
310 return Curl_xfer_flush(data);
311 }
312
313 if(data->req.eos_read && !data->req.eos_sent) {
314 char tmp;
315 size_t nwritten;
316 result = xfer_send(data, &tmp, 0, 0, &nwritten);
317 if(result)
318 return result;
319 DEBUGASSERT(data->req.eos_sent);
320 }
321
322 if(!data->req.upload_done && data->req.eos_read && data->req.eos_sent) {
323 DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf));
324 if(data->req.shutdown) {
325 bool done;
326 result = Curl_xfer_send_shutdown(data, &done);
327 if(result && data->req.shutdown_err_ignore) {
328 infof(data, "Shutdown send direction error: %d. Broken server? "
329 "Proceeding as if everything is ok.", result);
330 result = CURLE_OK;
331 done = TRUE;
332 }
333
334 if(result)
335 return result;
336 if(!done)
337 return CURLE_AGAIN;
338 }
339 return req_set_upload_done(data);
340 }
341 return CURLE_OK;
342 }
343
add_from_client(void * reader_ctx,unsigned char * buf,size_t buflen,CURLcode * err)344 static ssize_t add_from_client(void *reader_ctx,
345 unsigned char *buf, size_t buflen,
346 CURLcode *err)
347 {
348 struct Curl_easy *data = reader_ctx;
349 size_t nread;
350 bool eos;
351
352 *err = Curl_client_read(data, (char *)buf, buflen, &nread, &eos);
353 if(*err)
354 return -1;
355 if(eos)
356 data->req.eos_read = TRUE;
357 return (ssize_t)nread;
358 }
359
req_send_buffer_add(struct Curl_easy * data,const char * buf,size_t blen,size_t hds_len)360 static CURLcode req_send_buffer_add(struct Curl_easy *data,
361 const char *buf, size_t blen,
362 size_t hds_len)
363 {
364 CURLcode result = CURLE_OK;
365 ssize_t n;
366 n = Curl_bufq_write(&data->req.sendbuf,
367 (const unsigned char *)buf, blen, &result);
368 if(n < 0)
369 return result;
370 /* We rely on a SOFTLIMIT on sendbuf, so it can take all data in */
371 DEBUGASSERT((size_t)n == blen);
372 data->req.sendbuf_hds_len += hds_len;
373 return CURLE_OK;
374 }
375
Curl_req_send(struct Curl_easy * data,struct dynbuf * req)376 CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req)
377 {
378 CURLcode result;
379 const char *buf;
380 size_t blen, nwritten;
381
382 if(!data || !data->conn)
383 return CURLE_FAILED_INIT;
384
385 buf = Curl_dyn_ptr(req);
386 blen = Curl_dyn_len(req);
387 if(!Curl_creader_total_length(data)) {
388 /* Request without body. Try to send directly from the buf given. */
389 data->req.eos_read = TRUE;
390 result = xfer_send(data, buf, blen, blen, &nwritten);
391 if(result)
392 return result;
393 buf += nwritten;
394 blen -= nwritten;
395 }
396
397 if(blen) {
398 /* Either we have a request body, or we could not send the complete
399 * request in one go. Buffer the remainder and try to add as much
400 * body bytes as room is left in the buffer. Then flush. */
401 result = req_send_buffer_add(data, buf, blen, blen);
402 if(result)
403 return result;
404
405 return Curl_req_send_more(data);
406 }
407 return CURLE_OK;
408 }
409
Curl_req_sendbuf_empty(struct Curl_easy * data)410 bool Curl_req_sendbuf_empty(struct Curl_easy *data)
411 {
412 return !data->req.sendbuf_init || Curl_bufq_is_empty(&data->req.sendbuf);
413 }
414
Curl_req_want_send(struct Curl_easy * data)415 bool Curl_req_want_send(struct Curl_easy *data)
416 {
417 /* Not done and
418 * - KEEP_SEND and not PAUSEd.
419 * - or request has buffered data to send
420 * - or transfer connection has pending data to send */
421 return !data->req.done &&
422 (((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) ||
423 !Curl_req_sendbuf_empty(data) ||
424 Curl_xfer_needs_flush(data));
425 }
426
Curl_req_done_sending(struct Curl_easy * data)427 bool Curl_req_done_sending(struct Curl_easy *data)
428 {
429 return data->req.upload_done && !Curl_req_want_send(data);
430 }
431
Curl_req_send_more(struct Curl_easy * data)432 CURLcode Curl_req_send_more(struct Curl_easy *data)
433 {
434 CURLcode result;
435
436 /* Fill our send buffer if more from client can be read. */
437 if(!data->req.upload_aborted &&
438 !data->req.eos_read &&
439 !(data->req.keepon & KEEP_SEND_PAUSE) &&
440 !Curl_bufq_is_full(&data->req.sendbuf)) {
441 ssize_t nread = Curl_bufq_sipn(&data->req.sendbuf, 0,
442 add_from_client, data, &result);
443 if(nread < 0 && result != CURLE_AGAIN)
444 return result;
445 }
446
447 result = req_flush(data);
448 if(result == CURLE_AGAIN)
449 result = CURLE_OK;
450
451 return result;
452 }
453
Curl_req_abort_sending(struct Curl_easy * data)454 CURLcode Curl_req_abort_sending(struct Curl_easy *data)
455 {
456 if(!data->req.upload_done) {
457 Curl_bufq_reset(&data->req.sendbuf);
458 data->req.upload_aborted = TRUE;
459 /* no longer KEEP_SEND and KEEP_SEND_PAUSE */
460 data->req.keepon &= ~KEEP_SENDBITS;
461 return req_set_upload_done(data);
462 }
463 return CURLE_OK;
464 }
465
Curl_req_stop_send_recv(struct Curl_easy * data)466 CURLcode Curl_req_stop_send_recv(struct Curl_easy *data)
467 {
468 /* stop receiving and ALL sending as well, including PAUSE and HOLD.
469 * We might still be paused on receive client writes though, so
470 * keep those bits around. */
471 data->req.keepon &= ~(KEEP_RECV|KEEP_SENDBITS);
472 return Curl_req_abort_sending(data);
473 }
474