Coverage Report

Created: 2026-07-14 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Utilities/cmcurl/lib/sendf.c
Line
Count
Source
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
#include "curl_setup.h"
25
26
#ifdef HAVE_NETINET_IN_H
27
#include <netinet/in.h>
28
#endif
29
30
#ifdef HAVE_LINUX_TCP_H
31
#include <linux/tcp.h>
32
#elif defined(HAVE_NETINET_TCP_H)
33
#include <netinet/tcp.h>
34
#endif
35
36
#include "urldata.h"
37
#include "sendf.h"
38
#include "curl_trc.h"
39
#include "transfer.h"
40
#include "cfilters.h"
41
#include "connect.h"
42
#include "cw-out.h"
43
#include "cw-pause.h"
44
#include "multiif.h"
45
#include "progress.h"
46
47
static void cl_reset_writer(struct Curl_easy *data)
48
0
{
49
0
  struct Curl_cwriter *writer = data->req.writer_stack;
50
0
  while(writer) {
51
0
    data->req.writer_stack = writer->next;
52
0
    writer->cwt->do_close(data, writer);
53
0
    curlx_free(writer);
54
0
    writer = data->req.writer_stack;
55
0
  }
56
0
}
57
58
static void cl_reset_reader(struct Curl_easy *data)
59
0
{
60
0
  struct Curl_creader *reader = data->req.reader_stack;
61
0
  data->req.reader_started = FALSE;
62
0
  while(reader) {
63
0
    data->req.reader_stack = reader->next;
64
0
    reader->crt->do_close(data, reader);
65
0
    curlx_free(reader);
66
0
    reader = data->req.reader_stack;
67
0
  }
68
0
}
69
70
void Curl_client_cleanup(struct Curl_easy *data)
71
0
{
72
0
  cl_reset_reader(data);
73
0
  cl_reset_writer(data);
74
75
0
  data->req.bytecount = 0;
76
0
  data->req.headerline = 0;
77
0
}
78
79
void Curl_client_reset(struct Curl_easy *data)
80
0
{
81
0
  if(data->req.rewind_read) {
82
    /* already requested */
83
0
    CURL_TRC_READ(data, "client_reset, will rewind reader");
84
0
  }
85
0
  else {
86
0
    CURL_TRC_READ(data, "client_reset, clear readers");
87
0
    cl_reset_reader(data);
88
0
  }
89
0
  cl_reset_writer(data);
90
91
0
  data->req.bytecount = 0;
92
0
  data->req.headerline = 0;
93
0
}
94
95
CURLcode Curl_client_start(struct Curl_easy *data)
96
0
{
97
0
  if(data->req.rewind_read) {
98
0
    struct Curl_creader *r = data->req.reader_stack;
99
0
    CURLcode result = CURLE_OK;
100
101
0
    CURL_TRC_READ(data, "client start, rewind readers");
102
0
    while(r) {
103
0
      result = r->crt->cntrl(data, r, CURL_CRCNTRL_REWIND);
104
0
      if(result) {
105
0
        failf(data, "rewind of client reader '%s' failed: %d",
106
0
              r->crt->name, (int)result);
107
0
        return result;
108
0
      }
109
0
      r = r->next;
110
0
    }
111
0
    data->req.rewind_read = FALSE;
112
0
    cl_reset_reader(data);
113
0
  }
114
0
  return CURLE_OK;
115
0
}
116
117
bool Curl_creader_will_rewind(struct Curl_easy *data)
118
0
{
119
0
  return (bool)data->req.rewind_read;
120
0
}
121
122
void Curl_creader_set_rewind(struct Curl_easy *data, bool enable)
123
0
{
124
0
  data->req.rewind_read = !!enable;
125
0
}
126
127
/* Write data using an unencoding writer stack. */
128
CURLcode Curl_cwriter_write(struct Curl_easy *data,
129
                            struct Curl_cwriter *writer, int type,
130
                            const char *buf, size_t nbytes)
131
0
{
132
0
  if(!writer)
133
0
    return CURLE_WRITE_ERROR;
134
0
  return writer->cwt->do_write(data, writer, type, buf, nbytes);
135
0
}
136
137
CURLcode Curl_cwriter_def_init(struct Curl_easy *data,
138
                               struct Curl_cwriter *writer)
139
0
{
140
0
  (void)data;
141
0
  (void)writer;
142
0
  return CURLE_OK;
143
0
}
144
145
CURLcode Curl_cwriter_def_write(struct Curl_easy *data,
146
                                struct Curl_cwriter *writer, int type,
147
                                const char *buf, size_t nbytes)
148
0
{
149
0
  return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
150
0
}
151
152
void Curl_cwriter_def_close(struct Curl_easy *data,
153
                            struct Curl_cwriter *writer)
154
0
{
155
0
  (void)data;
156
0
  (void)writer;
157
0
}
158
159
static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit)
160
0
{
161
0
  if(limit != -1) {
162
    /* How much more are we allowed to write? */
163
0
    return curlx_sotouz_range(limit - data->req.bytecount, 0, SIZE_MAX);
164
0
  }
165
0
  return SIZE_MAX;
166
0
}
167
168
struct cw_download_ctx {
169
  struct Curl_cwriter super;
170
  BIT(started_response);
171
  BIT(started_body);
172
};
173
174
/* Download client writer in phase CURL_CW_PROTOCOL that
175
 * sees the "real" download body data. */
176
static CURLcode cw_download_write(struct Curl_easy *data,
177
                                  struct Curl_cwriter *writer, int type,
178
                                  const char *buf, size_t nbytes)
179
0
{
180
0
  struct cw_download_ctx *ctx = writer->ctx;
181
0
  CURLcode result;
182
0
  size_t nwrite, excess_len = 0;
183
0
  bool is_connect = !!(type & CLIENTWRITE_CONNECT);
184
185
0
  if(!ctx->started_response &&
186
0
     !(type & CLIENTWRITE_CONNECT) &&
187
0
     (!(type & CLIENTWRITE_INFO) || data->req.upload_done)) {
188
0
    Curl_pgrsTime(data, TIMER_STARTTRANSFER);
189
0
    ctx->started_response = TRUE;
190
0
  }
191
192
0
  if(!(type & CLIENTWRITE_BODY)) {
193
0
    if(is_connect && data->set.suppress_connect_headers)
194
0
      return CURLE_OK;
195
0
    result = Curl_cwriter_write(data, writer->next, type, buf, nbytes);
196
0
    CURL_TRC_WRITE(data, "download_write header(type=%x, blen=%zu) -> %d",
197
0
                   (unsigned int)type, nbytes, (int)result);
198
0
    return result;
199
0
  }
200
201
0
  if(!ctx->started_body &&
202
0
     !(type & (CLIENTWRITE_INFO | CLIENTWRITE_CONNECT))) {
203
0
    Curl_rlimit_start(&data->progress.dl.rlimit, Curl_pgrs_now(data),
204
0
                      data->req.size);
205
0
    ctx->started_body = TRUE;
206
0
  }
207
208
  /* Here, we deal with REAL BODY bytes. All filtering and transfer
209
   * encodings have been applied and only the true content, e.g. BODY,
210
   * bytes are passed here.
211
   * This allows us to check sizes, update stats, etc. independent
212
   * from the protocol in play. */
213
214
0
  if(data->req.no_body && nbytes > 0) {
215
    /* BODY arrives although we want none, bail out */
216
0
    streamclose(data->conn, "ignoring body");
217
0
    CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu), "
218
0
                   "did not want a BODY", (unsigned int)type, nbytes);
219
0
    data->req.download_done = TRUE;
220
0
    if(data->info.header_size)
221
      /* if headers have been received, this is fine */
222
0
      return CURLE_OK;
223
0
    return CURLE_WEIRD_SERVER_REPLY;
224
0
  }
225
226
  /* Determine if we see any bytes in excess to what is allowed.
227
   * We write the allowed bytes and handle excess further below.
228
   * This gives deterministic BODY writes on varying buffer receive
229
   * lengths. */
230
0
  nwrite = nbytes;
231
0
  if(data->req.maxdownload != -1) {
232
0
    size_t wmax = get_max_body_write_len(data, data->req.maxdownload);
233
0
    if(nwrite > wmax) {
234
0
      excess_len = nbytes - wmax;
235
0
      nwrite = wmax;
236
0
    }
237
238
0
    if(nwrite == wmax) {
239
0
      data->req.download_done = TRUE;
240
0
    }
241
242
0
    if((type & CLIENTWRITE_EOS) && !data->req.no_body &&
243
0
       (data->req.size > data->req.bytecount)) {
244
0
      failf(data, "end of response with %" FMT_OFF_T " bytes missing",
245
0
            data->req.size - data->req.bytecount);
246
0
      return CURLE_PARTIAL_FILE;
247
0
    }
248
0
  }
249
250
  /* Error on too large filesize is handled below, after writing
251
   * the permitted bytes */
252
0
  if(data->set.max_filesize && !data->req.ignorebody) {
253
0
    size_t wmax = get_max_body_write_len(data, data->set.max_filesize);
254
0
    if(nwrite > wmax) {
255
0
      nwrite = wmax;
256
0
    }
257
0
  }
258
259
0
  if(!data->req.ignorebody && (nwrite || (type & CLIENTWRITE_EOS))) {
260
0
    result = Curl_cwriter_write(data, writer->next, type, buf, nwrite);
261
0
    CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu) -> %d",
262
0
                   (unsigned int)type, nbytes, (int)result);
263
0
    if(result)
264
0
      return result;
265
0
  }
266
267
  /* Update stats, write and report progress */
268
0
  if(nwrite) {
269
0
    data->req.bytecount += nwrite;
270
0
    Curl_pgrs_download_inc(data, nwrite);
271
0
  }
272
273
0
  if(excess_len) {
274
0
    if(!data->req.ignorebody) {
275
0
      infof(data,
276
0
            "Excess found writing body:"
277
0
            " excess = %zu"
278
0
            ", size = %" FMT_OFF_T
279
0
            ", maxdownload = %" FMT_OFF_T
280
0
            ", bytecount = %" FMT_OFF_T,
281
0
            excess_len, data->req.size, data->req.maxdownload,
282
0
            data->req.bytecount);
283
0
      connclose(data->conn, "excess found in a read");
284
0
    }
285
0
  }
286
0
  else if((nwrite < nbytes) && !data->req.ignorebody) {
287
0
    failf(data, "Exceeded the maximum allowed file size "
288
0
          "(%" FMT_OFF_T ") with %" FMT_OFF_T " bytes",
289
0
          data->set.max_filesize, data->req.bytecount);
290
0
    return CURLE_FILESIZE_EXCEEDED;
291
0
  }
292
293
0
  return CURLE_OK;
294
0
}
295
296
static const struct Curl_cwtype cw_download = {
297
  "protocol",
298
  NULL,
299
  Curl_cwriter_def_init,
300
  cw_download_write,
301
  Curl_cwriter_def_close,
302
  sizeof(struct cw_download_ctx)
303
};
304
305
/* RAW client writer in phase CURL_CW_RAW that
306
 * enabled tracing of raw data. */
307
static CURLcode cw_raw_write(struct Curl_easy *data,
308
                             struct Curl_cwriter *writer, int type,
309
                             const char *buf, size_t nbytes)
310
0
{
311
0
  if(type & CLIENTWRITE_BODY && data->set.verbose && !data->req.ignorebody) {
312
0
    Curl_debug(data, CURLINFO_DATA_IN, buf, nbytes);
313
0
  }
314
0
  return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
315
0
}
316
317
static const struct Curl_cwtype cw_raw = {
318
  "raw",
319
  NULL,
320
  Curl_cwriter_def_init,
321
  cw_raw_write,
322
  Curl_cwriter_def_close,
323
  sizeof(struct Curl_cwriter)
324
};
325
326
static CURLcode do_init_writer_stack(struct Curl_easy *data)
327
0
{
328
0
  struct Curl_cwriter *writer;
329
0
  CURLcode result;
330
331
0
  DEBUGASSERT(!data->req.writer_stack);
332
0
  result = Curl_cwriter_create(&data->req.writer_stack,
333
0
                               data, &Curl_cwt_out, CURL_CW_CLIENT);
334
0
  if(result)
335
0
    return result;
336
337
  /* This places the "pause" writer behind the "download" writer that
338
   * is added below. Meaning the "download" can do checks on content length
339
   * and other things *before* write outs are buffered for paused transfers. */
340
0
  result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause,
341
0
                               CURL_CW_PROTOCOL);
342
0
  if(!result) {
343
0
    result = Curl_cwriter_add(data, writer);
344
0
    if(result)
345
0
      Curl_cwriter_free(data, writer);
346
0
  }
347
0
  if(result)
348
0
    return result;
349
350
0
  result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL);
351
0
  if(!result) {
352
0
    result = Curl_cwriter_add(data, writer);
353
0
    if(result)
354
0
      Curl_cwriter_free(data, writer);
355
0
  }
356
0
  if(result)
357
0
    return result;
358
359
0
  result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW);
360
0
  if(!result) {
361
0
    result = Curl_cwriter_add(data, writer);
362
0
    if(result)
363
0
      Curl_cwriter_free(data, writer);
364
0
  }
365
0
  if(result)
366
0
    return result;
367
368
0
  return result;
369
0
}
370
371
/* Curl_client_write() sends data to the write callback(s)
372
373
   The bit pattern defines to what "streams" to write to. Body and/or header.
374
   The defines are in sendf.h of course.
375
 */
376
CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf,
377
                           size_t len)
378
0
{
379
0
  CURLcode result;
380
381
  /* it is one of those, at least */
382
0
  DEBUGASSERT(type &
383
0
              (CLIENTWRITE_BODY | CLIENTWRITE_HEADER | CLIENTWRITE_INFO));
384
  /* BODY is only BODY (with optional EOS) */
385
0
  DEBUGASSERT(!(type & CLIENTWRITE_BODY) ||
386
0
              ((type & ~(CLIENTWRITE_BODY | CLIENTWRITE_EOS)) == 0));
387
  /* INFO is only INFO (with optional EOS) */
388
0
  DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
389
0
              ((type & ~(CLIENTWRITE_INFO | CLIENTWRITE_EOS)) == 0));
390
391
0
  if(!data->req.writer_stack) {
392
0
    result = do_init_writer_stack(data);
393
0
    if(result)
394
0
      return result;
395
0
    DEBUGASSERT(data->req.writer_stack);
396
0
  }
397
398
0
  result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, len);
399
0
  CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d",
400
0
                 (unsigned int)type, len, (int)result);
401
0
  return result;
402
0
}
403
404
/* Create an unencoding writer stage using the given handler. */
405
CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter,
406
                             struct Curl_easy *data,
407
                             const struct Curl_cwtype *cwt,
408
                             Curl_cwriter_phase phase)
409
0
{
410
0
  struct Curl_cwriter *writer = NULL;
411
0
  CURLcode result = CURLE_OUT_OF_MEMORY;
412
0
  void *p;
413
414
0
  DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter));
415
0
  p = curlx_calloc(1, cwt->cwriter_size);
416
0
  if(!p)
417
0
    goto out;
418
419
0
  writer = (struct Curl_cwriter *)p;
420
0
  writer->cwt = cwt;
421
0
  writer->ctx = p;
422
0
  writer->phase = phase;
423
0
  result = cwt->do_init(data, writer);
424
425
0
out:
426
0
  *pwriter = result ? NULL : writer;
427
0
  if(result)
428
0
    curlx_free(writer);
429
0
  return result;
430
0
}
431
432
void Curl_cwriter_free(struct Curl_easy *data,
433
                       struct Curl_cwriter *writer)
434
0
{
435
0
  if(writer) {
436
0
    writer->cwt->do_close(data, writer);
437
0
    curlx_free(writer);
438
0
  }
439
0
}
440
441
size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase)
442
0
{
443
0
  struct Curl_cwriter *w;
444
0
  size_t n = 0;
445
446
0
  for(w = data->req.writer_stack; w; w = w->next) {
447
0
    if(w->phase == phase)
448
0
      ++n;
449
0
  }
450
0
  return n;
451
0
}
452
453
CURLcode Curl_cwriter_add(struct Curl_easy *data,
454
                          struct Curl_cwriter *writer)
455
0
{
456
0
  CURLcode result;
457
0
  struct Curl_cwriter **anchor = &data->req.writer_stack;
458
459
0
  if(!*anchor) {
460
0
    result = do_init_writer_stack(data);
461
0
    if(result)
462
0
      return result;
463
0
  }
464
465
  /* Insert the writer as first in its phase.
466
   * Skip existing writers of lower phases. */
467
0
  while(*anchor && (*anchor)->phase < writer->phase)
468
0
    anchor = &((*anchor)->next);
469
0
  writer->next = *anchor;
470
0
  *anchor = writer;
471
0
  return CURLE_OK;
472
0
}
473
474
struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data,
475
                                              const char *name)
476
0
{
477
0
  struct Curl_cwriter *writer;
478
0
  for(writer = data->req.writer_stack; writer; writer = writer->next) {
479
0
    if(!strcmp(name, writer->cwt->name))
480
0
      return writer;
481
0
  }
482
0
  return NULL;
483
0
}
484
485
struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data,
486
                                              const struct Curl_cwtype *cwt)
487
0
{
488
0
  struct Curl_cwriter *writer;
489
0
  for(writer = data->req.writer_stack; writer; writer = writer->next) {
490
0
    if(writer->cwt == cwt)
491
0
      return writer;
492
0
  }
493
0
  return NULL;
494
0
}
495
496
bool Curl_cwriter_is_content_decoding(struct Curl_easy *data)
497
0
{
498
0
  struct Curl_cwriter *writer;
499
0
  for(writer = data->req.writer_stack; writer; writer = writer->next) {
500
0
    if(writer->phase == CURL_CW_CONTENT_DECODE)
501
0
      return TRUE;
502
0
  }
503
0
  return FALSE;
504
0
}
505
506
bool Curl_cwriter_is_paused(struct Curl_easy *data)
507
0
{
508
0
  return Curl_cw_out_is_paused(data);
509
0
}
510
511
CURLcode Curl_cwriter_unpause(struct Curl_easy *data)
512
0
{
513
0
  return Curl_cw_out_unpause(data);
514
0
}
515
516
CURLcode Curl_creader_read(struct Curl_easy *data,
517
                           struct Curl_creader *reader,
518
                           char *buf, size_t blen, size_t *nread, bool *eos)
519
0
{
520
0
  *nread = 0;
521
0
  *eos = FALSE;
522
0
  if(!reader)
523
0
    return CURLE_READ_ERROR;
524
0
  return reader->crt->do_read(data, reader, buf, blen, nread, eos);
525
0
}
526
527
void Curl_creader_clear_eos(struct Curl_easy *data,
528
                            struct Curl_creader *reader)
529
0
{
530
0
  while(reader) {
531
0
    (void)reader->crt->cntrl(data, reader, CURL_CRCNTRL_CLEAR_EOS);
532
0
    reader = reader->next;
533
0
  }
534
0
}
535
536
CURLcode Curl_creader_def_init(struct Curl_easy *data,
537
                               struct Curl_creader *reader)
538
0
{
539
0
  (void)data;
540
0
  (void)reader;
541
0
  return CURLE_OK;
542
0
}
543
544
void Curl_creader_def_close(struct Curl_easy *data,
545
                            struct Curl_creader *reader)
546
0
{
547
0
  (void)data;
548
0
  (void)reader;
549
0
}
550
551
CURLcode Curl_creader_def_read(struct Curl_easy *data,
552
                               struct Curl_creader *reader,
553
                               char *buf, size_t blen,
554
                               size_t *nread, bool *eos)
555
0
{
556
0
  if(reader->next)
557
0
    return reader->next->crt->do_read(data, reader->next, buf, blen,
558
0
                                      nread, eos);
559
0
  else {
560
0
    *nread = 0;
561
0
    *eos = FALSE;
562
0
    return CURLE_READ_ERROR;
563
0
  }
564
0
}
565
566
bool Curl_creader_def_needs_rewind(struct Curl_easy *data,
567
                                   struct Curl_creader *reader)
568
0
{
569
0
  (void)data;
570
0
  (void)reader;
571
0
  return FALSE;
572
0
}
573
574
curl_off_t Curl_creader_def_total_length(struct Curl_easy *data,
575
                                         struct Curl_creader *reader)
576
0
{
577
0
  return reader->next ?
578
0
         reader->next->crt->total_length(data, reader->next) : -1;
579
0
}
580
581
CURLcode Curl_creader_def_resume_from(struct Curl_easy *data,
582
                                      struct Curl_creader *reader,
583
                                      curl_off_t offset)
584
0
{
585
0
  (void)data;
586
0
  (void)reader;
587
0
  (void)offset;
588
0
  return CURLE_READ_ERROR;
589
0
}
590
591
CURLcode Curl_creader_def_cntrl(struct Curl_easy *data,
592
                                struct Curl_creader *reader,
593
                                Curl_creader_cntrl opcode)
594
0
{
595
0
  (void)data;
596
0
  (void)reader;
597
0
  (void)opcode;
598
0
  return CURLE_OK;
599
0
}
600
601
bool Curl_creader_def_is_paused(struct Curl_easy *data,
602
                                struct Curl_creader *reader)
603
0
{
604
0
  (void)data;
605
0
  (void)reader;
606
0
  return FALSE;
607
0
}
608
609
void Curl_creader_def_done(struct Curl_easy *data,
610
                           struct Curl_creader *reader, int premature)
611
0
{
612
0
  (void)data;
613
0
  (void)reader;
614
0
  (void)premature;
615
0
}
616
617
struct cr_in_ctx {
618
  struct Curl_creader super;
619
  curl_read_callback read_cb;
620
  void *cb_user_data;
621
  curl_off_t total_len;
622
  curl_off_t read_len;
623
  CURLcode error_result;
624
  BIT(seen_eos);
625
  BIT(errored);
626
  BIT(has_used_cb);
627
  BIT(is_paused);
628
};
629
630
static CURLcode cr_in_init(struct Curl_easy *data, struct Curl_creader *reader)
631
0
{
632
0
  struct cr_in_ctx *ctx = reader->ctx;
633
0
  ctx->read_cb = data->state.fread_func;
634
0
  ctx->cb_user_data = data->state.in;
635
0
  ctx->total_len = -1;
636
0
  ctx->read_len = 0;
637
0
  return CURLE_OK;
638
0
}
639
640
/* Real client reader to installed client callbacks. */
641
static CURLcode cr_in_read(struct Curl_easy *data,
642
                           struct Curl_creader *reader,
643
                           char *buf, size_t blen,
644
                           size_t *pnread, bool *peos)
645
0
{
646
0
  struct cr_in_ctx *ctx = reader->ctx;
647
0
  CURLcode result = CURLE_OK;
648
0
  size_t nread;
649
650
0
  ctx->is_paused = FALSE;
651
652
  /* Once we have errored, we will return the same error forever */
653
0
  if(ctx->errored) {
654
0
    *pnread = 0;
655
0
    *peos = FALSE;
656
0
    return ctx->error_result;
657
0
  }
658
0
  if(ctx->seen_eos) {
659
0
    *pnread = 0;
660
0
    *peos = TRUE;
661
0
    return CURLE_OK;
662
0
  }
663
  /* respect length limitations */
664
0
  if(ctx->total_len >= 0) {
665
0
    blen = curlx_sotouz_range(ctx->total_len - ctx->read_len, 0, blen);
666
0
  }
667
0
  nread = 0;
668
0
  if(ctx->read_cb && blen) {
669
0
    Curl_set_in_callback(data, TRUE);
670
0
    nread = ctx->read_cb(buf, 1, blen, ctx->cb_user_data);
671
0
    Curl_set_in_callback(data, FALSE);
672
0
    ctx->has_used_cb = TRUE;
673
0
  }
674
675
0
  switch(nread) {
676
0
  case 0:
677
0
    if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) {
678
0
      failf(data, "client read function EOF fail, "
679
0
            "only %" FMT_OFF_T "/%" FMT_OFF_T " of needed bytes read",
680
0
            ctx->read_len, ctx->total_len);
681
0
      result = CURLE_READ_ERROR;
682
0
      break;
683
0
    }
684
0
    *pnread = 0;
685
0
    *peos = TRUE;
686
0
    ctx->seen_eos = TRUE;
687
0
    break;
688
689
0
  case CURL_READFUNC_ABORT:
690
0
    failf(data, "operation aborted by callback");
691
0
    *pnread = 0;
692
0
    *peos = FALSE;
693
0
    ctx->errored = TRUE;
694
0
    ctx->error_result = CURLE_ABORTED_BY_CALLBACK;
695
0
    result = CURLE_ABORTED_BY_CALLBACK;
696
0
    break;
697
698
0
  case CURL_READFUNC_PAUSE:
699
0
    if(data->conn->scheme->flags & PROTOPT_NONETWORK) {
700
      /* protocols that work without network cannot be paused. This is
701
         actually only file:// now, and it cannot pause since the transfer
702
         is not done using the "normal" procedure. */
703
0
      failf(data, "Read callback asked for PAUSE when not supported");
704
0
      result = CURLE_READ_ERROR;
705
0
      break;
706
0
    }
707
    /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
708
0
    CURL_TRC_READ(data, "cr_in_read, callback returned CURL_READFUNC_PAUSE");
709
0
    ctx->is_paused = TRUE;
710
0
    *pnread = 0;
711
0
    *peos = FALSE;
712
0
    result = Curl_xfer_pause_send(data, TRUE);
713
0
    break; /* nothing was read */
714
715
0
  default:
716
0
    if(nread > blen) {
717
      /* the read function returned a too large value */
718
0
      failf(data, "read function returned funny value");
719
0
      *pnread = 0;
720
0
      *peos = FALSE;
721
0
      ctx->errored = TRUE;
722
0
      ctx->error_result = CURLE_READ_ERROR;
723
0
      result = CURLE_READ_ERROR;
724
0
      break;
725
0
    }
726
0
    ctx->read_len += nread;
727
0
    if(ctx->total_len >= 0)
728
0
      ctx->seen_eos = (ctx->read_len >= ctx->total_len);
729
0
    *pnread = nread;
730
0
    *peos = (bool)ctx->seen_eos;
731
0
    break;
732
0
  }
733
0
  CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%" FMT_OFF_T
734
0
                ", read=%" FMT_OFF_T ") -> %d, nread=%zu, eos=%d",
735
0
                blen, ctx->total_len, ctx->read_len, (int)result,
736
0
                *pnread, *peos);
737
0
  return result;
738
0
}
739
740
static bool cr_in_needs_rewind(struct Curl_easy *data,
741
                               struct Curl_creader *reader)
742
0
{
743
0
  struct cr_in_ctx *ctx = reader->ctx;
744
0
  (void)data;
745
0
  return (bool)ctx->has_used_cb;
746
0
}
747
748
static curl_off_t cr_in_total_length(struct Curl_easy *data,
749
                                     struct Curl_creader *reader)
750
0
{
751
0
  struct cr_in_ctx *ctx = reader->ctx;
752
0
  (void)data;
753
0
  return ctx->total_len;
754
0
}
755
756
static CURLcode cr_in_resume_from(struct Curl_easy *data,
757
                                  struct Curl_creader *reader,
758
                                  curl_off_t offset)
759
0
{
760
0
  struct cr_in_ctx *ctx = reader->ctx;
761
0
  int seekerr = CURL_SEEKFUNC_CANTSEEK;
762
763
0
  DEBUGASSERT(data->conn);
764
  /* already started reading? */
765
0
  if(ctx->read_len)
766
0
    return CURLE_READ_ERROR;
767
768
0
  if(data->set.seek_func) {
769
0
    Curl_set_in_callback(data, TRUE);
770
0
    seekerr = data->set.seek_func(data->set.seek_client, offset, SEEK_SET);
771
0
    Curl_set_in_callback(data, FALSE);
772
0
  }
773
774
0
  if(seekerr != CURL_SEEKFUNC_OK) {
775
0
    curl_off_t passed = 0;
776
777
0
    if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
778
0
      failf(data, "Could not seek stream");
779
0
      return CURLE_READ_ERROR;
780
0
    }
781
    /* when seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
782
0
    do {
783
0
      char scratch[4 * 1024];
784
0
      size_t readthisamountnow =
785
0
        (offset - passed > (curl_off_t)sizeof(scratch)) ?
786
0
        sizeof(scratch) :
787
0
        curlx_sotouz(offset - passed);
788
0
      size_t actuallyread;
789
790
0
      Curl_set_in_callback(data, TRUE);
791
0
      actuallyread = ctx->read_cb(scratch, 1, readthisamountnow,
792
0
                                  ctx->cb_user_data);
793
0
      Curl_set_in_callback(data, FALSE);
794
795
0
      passed += actuallyread;
796
0
      if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
797
        /* this checks for greater-than only to make sure that the
798
           CURL_READFUNC_ABORT return code still aborts */
799
0
        failf(data, "Could only read %" FMT_OFF_T " bytes from the input",
800
0
              passed);
801
0
        return CURLE_READ_ERROR;
802
0
      }
803
0
    } while(passed < offset);
804
0
  }
805
806
  /* now, decrease the size of the read */
807
0
  if(ctx->total_len > 0) {
808
0
    ctx->total_len -= offset;
809
810
0
    if(ctx->total_len <= 0) {
811
0
      failf(data, "File already completely uploaded");
812
0
      return CURLE_PARTIAL_FILE;
813
0
    }
814
0
  }
815
  /* we have passed, proceed as normal */
816
0
  return CURLE_OK;
817
0
}
818
819
static CURLcode cr_in_rewind(struct Curl_easy *data,
820
                             struct Curl_creader *reader)
821
0
{
822
0
  struct cr_in_ctx *ctx = reader->ctx;
823
824
  /* If we never invoked the callback, there is noting to rewind */
825
0
  if(!ctx->has_used_cb)
826
0
    return CURLE_OK;
827
828
0
  if(data->set.seek_func) {
829
0
    int err;
830
831
0
    Curl_set_in_callback(data, TRUE);
832
0
    err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
833
0
    Curl_set_in_callback(data, FALSE);
834
0
    CURL_TRC_READ(data, "cr_in, rewind via set.seek_func -> %d", err);
835
0
    if(err) {
836
0
      failf(data, "seek callback returned error %d", err);
837
0
      return CURLE_SEND_FAIL_REWIND;
838
0
    }
839
0
  }
840
0
  else if(data->set.ioctl_func) {
841
0
    curlioerr err;
842
843
0
    Curl_set_in_callback(data, TRUE);
844
0
    err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
845
0
                                 data->set.ioctl_client);
846
0
    Curl_set_in_callback(data, FALSE);
847
0
    CURL_TRC_READ(data, "cr_in, rewind via set.ioctl_func -> %d", (int)err);
848
0
    if(err) {
849
0
      failf(data, "ioctl callback returned error %d", (int)err);
850
0
      return CURLE_SEND_FAIL_REWIND;
851
0
    }
852
0
  }
853
0
  else {
854
    /* If no CURLOPT_READFUNCTION is used, we know that we operate on a
855
       given FILE * stream and we can actually attempt to rewind that
856
       ourselves with fseek() */
857
0
#if defined(__clang__) && __clang_major__ >= 16
858
0
#pragma clang diagnostic push
859
0
#pragma clang diagnostic ignored "-Wcast-function-type-strict"
860
0
#endif
861
0
    if(data->state.fread_func == (curl_read_callback)fread) {
862
0
#if defined(__clang__) && __clang_major__ >= 16
863
0
#pragma clang diagnostic pop
864
0
#endif
865
0
      int err = fseek(data->state.in, 0, SEEK_SET);
866
0
      CURL_TRC_READ(data, "cr_in, rewind via fseek -> %d(%d)",
867
0
                    (int)err, (int)errno);
868
0
      if(err != -1)
869
        /* successful rewind */
870
0
        return CURLE_OK;
871
0
    }
872
873
    /* no callback set or failure above, makes us fail at once */
874
0
    failf(data, "necessary data rewind was not possible");
875
0
    return CURLE_SEND_FAIL_REWIND;
876
0
  }
877
0
  return CURLE_OK;
878
0
}
879
880
static CURLcode cr_in_cntrl(struct Curl_easy *data,
881
                            struct Curl_creader *reader,
882
                            Curl_creader_cntrl opcode)
883
0
{
884
0
  struct cr_in_ctx *ctx = reader->ctx;
885
886
0
  switch(opcode) {
887
0
  case CURL_CRCNTRL_REWIND:
888
0
    return cr_in_rewind(data, reader);
889
0
  case CURL_CRCNTRL_UNPAUSE:
890
0
    ctx->is_paused = FALSE;
891
0
    break;
892
0
  case CURL_CRCNTRL_CLEAR_EOS:
893
0
    ctx->seen_eos = FALSE;
894
0
    break;
895
0
  default:
896
0
    break;
897
0
  }
898
0
  return CURLE_OK;
899
0
}
900
901
static bool cr_in_is_paused(struct Curl_easy *data,
902
                            struct Curl_creader *reader)
903
0
{
904
0
  struct cr_in_ctx *ctx = reader->ctx;
905
0
  (void)data;
906
0
  return (bool)ctx->is_paused;
907
0
}
908
909
static const struct Curl_crtype cr_in = {
910
  "cr-in",
911
  cr_in_init,
912
  cr_in_read,
913
  Curl_creader_def_close,
914
  cr_in_needs_rewind,
915
  cr_in_total_length,
916
  cr_in_resume_from,
917
  cr_in_cntrl,
918
  cr_in_is_paused,
919
  Curl_creader_def_done,
920
  sizeof(struct cr_in_ctx)
921
};
922
923
CURLcode Curl_creader_create(struct Curl_creader **preader,
924
                             struct Curl_easy *data,
925
                             const struct Curl_crtype *crt,
926
                             Curl_creader_phase phase)
927
0
{
928
0
  struct Curl_creader *reader = NULL;
929
0
  CURLcode result = CURLE_OUT_OF_MEMORY;
930
0
  void *p;
931
932
0
  DEBUGASSERT(crt->creader_size >= sizeof(struct Curl_creader));
933
0
  p = curlx_calloc(1, crt->creader_size);
934
0
  if(!p)
935
0
    goto out;
936
937
0
  reader = (struct Curl_creader *)p;
938
0
  reader->crt = crt;
939
0
  reader->ctx = p;
940
0
  reader->phase = phase;
941
0
  result = crt->do_init(data, reader);
942
943
0
out:
944
0
  *preader = result ? NULL : reader;
945
0
  if(result)
946
0
    curlx_free(reader);
947
0
  return result;
948
0
}
949
950
void Curl_creader_free(struct Curl_easy *data, struct Curl_creader *reader)
951
0
{
952
0
  if(reader) {
953
0
    reader->crt->do_close(data, reader);
954
0
    curlx_free(reader);
955
0
  }
956
0
}
957
958
struct cr_lc_ctx {
959
  struct Curl_creader super;
960
  struct bufq buf;
961
  BIT(read_eos);  /* we read an EOS from the next reader */
962
  BIT(eos);       /* we have returned an EOS */
963
  BIT(prev_cr);   /* the last byte was a CR */
964
};
965
966
static CURLcode cr_lc_init(struct Curl_easy *data, struct Curl_creader *reader)
967
0
{
968
0
  struct cr_lc_ctx *ctx = reader->ctx;
969
0
  (void)data;
970
0
  Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT);
971
0
  return CURLE_OK;
972
0
}
973
974
static void cr_lc_close(struct Curl_easy *data, struct Curl_creader *reader)
975
0
{
976
0
  struct cr_lc_ctx *ctx = reader->ctx;
977
0
  (void)data;
978
0
  Curl_bufq_free(&ctx->buf);
979
0
}
980
981
/* client reader doing line end conversions. */
982
static CURLcode cr_lc_read(struct Curl_easy *data,
983
                           struct Curl_creader *reader,
984
                           char *buf, size_t blen,
985
                           size_t *pnread, bool *peos)
986
0
{
987
0
  struct cr_lc_ctx *ctx = reader->ctx;
988
0
  CURLcode result;
989
0
  size_t nread, i, start, n;
990
0
  bool eos;
991
992
0
  if(ctx->eos) {
993
0
    *pnread = 0;
994
0
    *peos = TRUE;
995
0
    return CURLE_OK;
996
0
  }
997
998
0
  if(Curl_bufq_is_empty(&ctx->buf)) {
999
0
    if(ctx->read_eos) {
1000
0
      ctx->eos = TRUE;
1001
0
      *pnread = 0;
1002
0
      *peos = TRUE;
1003
0
      return CURLE_OK;
1004
0
    }
1005
    /* Still getting data form the next reader, ctx->buf is empty */
1006
0
    result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
1007
0
    if(result)
1008
0
      return result;
1009
0
    ctx->read_eos = eos;
1010
1011
0
    if(!nread || !memchr(buf, '\n', nread)) {
1012
      /* nothing to convert, return this right away */
1013
0
      if(nread)
1014
0
        ctx->prev_cr = (buf[nread - 1] == '\r');
1015
0
      if(ctx->read_eos)
1016
0
        ctx->eos = TRUE;
1017
0
      *pnread = nread;
1018
0
      *peos = (bool)ctx->eos;
1019
0
      goto out;
1020
0
    }
1021
1022
    /* at least one \n might need conversion to '\r\n', place into ctx->buf */
1023
0
    for(i = start = 0; i < nread; ++i) {
1024
      /* if this byte is not an LF character, or if the preceding character is
1025
         a CR (meaning this already is a CRLF pair), go to next */
1026
0
      if((buf[i] != '\n') || ctx->prev_cr) {
1027
0
        ctx->prev_cr = (buf[i] == '\r');
1028
0
        continue;
1029
0
      }
1030
0
      ctx->prev_cr = FALSE;
1031
      /* on a soft limit bufq, we do not need to check length */
1032
0
      result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
1033
0
      if(!result)
1034
0
        result = Curl_bufq_cwrite(&ctx->buf, STRCONST("\r\n"), &n);
1035
0
      if(result)
1036
0
        return result;
1037
0
      start = i + 1;
1038
0
    }
1039
1040
0
    if(start < i) { /* leftover */
1041
0
      result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
1042
0
      if(result)
1043
0
        return result;
1044
0
    }
1045
0
  }
1046
1047
0
  DEBUGASSERT(!Curl_bufq_is_empty(&ctx->buf));
1048
0
  *peos = FALSE;
1049
0
  result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
1050
0
  if(!result && ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
1051
    /* no more data, read all, done. */
1052
0
    ctx->eos = TRUE;
1053
0
    *peos = TRUE;
1054
0
  }
1055
1056
0
out:
1057
0
  CURL_TRC_READ(data, "cr_lc_read(len=%zu) -> %d, nread=%zu, eos=%d",
1058
0
                blen, (int)result, *pnread, *peos);
1059
0
  return result;
1060
0
}
1061
1062
static curl_off_t cr_lc_total_length(struct Curl_easy *data,
1063
                                     struct Curl_creader *reader)
1064
0
{
1065
  /* this reader changes length depending on input */
1066
0
  (void)data;
1067
0
  (void)reader;
1068
0
  return -1;
1069
0
}
1070
1071
static const struct Curl_crtype cr_lc = {
1072
  "cr-lineconv",
1073
  cr_lc_init,
1074
  cr_lc_read,
1075
  cr_lc_close,
1076
  Curl_creader_def_needs_rewind,
1077
  cr_lc_total_length,
1078
  Curl_creader_def_resume_from,
1079
  Curl_creader_def_cntrl,
1080
  Curl_creader_def_is_paused,
1081
  Curl_creader_def_done,
1082
  sizeof(struct cr_lc_ctx)
1083
};
1084
1085
static CURLcode cr_lc_add(struct Curl_easy *data)
1086
0
{
1087
0
  struct Curl_creader *reader = NULL;
1088
0
  CURLcode result;
1089
1090
0
  result = Curl_creader_create(&reader, data, &cr_lc, CURL_CR_CONTENT_ENCODE);
1091
0
  if(!result)
1092
0
    result = Curl_creader_add(data, reader);
1093
1094
0
  if(result && reader)
1095
0
    Curl_creader_free(data, reader);
1096
0
  return result;
1097
0
}
1098
1099
static CURLcode do_init_reader_stack(struct Curl_easy *data,
1100
                                     struct Curl_creader *r)
1101
0
{
1102
0
  CURLcode result = CURLE_OK;
1103
0
  curl_off_t clen;
1104
1105
0
  DEBUGASSERT(r);
1106
0
  DEBUGASSERT(r->crt);
1107
0
  DEBUGASSERT(r->phase == CURL_CR_CLIENT);
1108
0
  DEBUGASSERT(!data->req.reader_stack);
1109
1110
0
  data->req.reader_stack = r;
1111
0
  clen = r->crt->total_length(data, r);
1112
  /* if we do not have 0 length init, and crlf conversion is wanted,
1113
   * add the reader for it */
1114
0
  if(clen && (data->set.crlf
1115
0
#ifdef CURL_PREFER_LF_LINEENDS
1116
0
     || data->state.prefer_ascii
1117
0
#endif
1118
0
    )) {
1119
0
    result = cr_lc_add(data);
1120
0
    if(result)
1121
0
      return result;
1122
0
  }
1123
1124
0
  return result;
1125
0
}
1126
1127
CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len)
1128
0
{
1129
0
  CURLcode result;
1130
0
  struct Curl_creader *r;
1131
0
  struct cr_in_ctx *ctx;
1132
1133
0
  result = Curl_creader_create(&r, data, &cr_in, CURL_CR_CLIENT);
1134
0
  if(result || !r)
1135
0
    goto out;
1136
0
  ctx = r->ctx;
1137
0
  ctx->total_len = len;
1138
1139
0
  cl_reset_reader(data);
1140
0
  result = do_init_reader_stack(data, r);
1141
0
out:
1142
0
  CURL_TRC_READ(data, "add fread reader, len=%" FMT_OFF_T " -> %d",
1143
0
                len, (int)result);
1144
0
  return result;
1145
0
}
1146
1147
CURLcode Curl_creader_add(struct Curl_easy *data,
1148
                          struct Curl_creader *reader)
1149
0
{
1150
0
  CURLcode result;
1151
0
  struct Curl_creader **anchor = &data->req.reader_stack;
1152
1153
0
  if(!*anchor) {
1154
0
    result = Curl_creader_set_fread(data, data->state.infilesize);
1155
0
    if(result)
1156
0
      return result;
1157
0
  }
1158
1159
  /* Insert the writer as first in its phase.
1160
   * Skip existing readers of lower phases. */
1161
0
  while(*anchor && (*anchor)->phase < reader->phase)
1162
0
    anchor = &((*anchor)->next);
1163
0
  reader->next = *anchor;
1164
0
  *anchor = reader;
1165
0
  return CURLE_OK;
1166
0
}
1167
1168
CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r)
1169
0
{
1170
0
  CURLcode result;
1171
1172
0
  DEBUGASSERT(r);
1173
0
  DEBUGASSERT(r->crt);
1174
0
  DEBUGASSERT(r->phase == CURL_CR_CLIENT);
1175
1176
0
  cl_reset_reader(data);
1177
0
  result = do_init_reader_stack(data, r);
1178
0
  if(result)
1179
0
    Curl_creader_free(data, r);
1180
0
  return result;
1181
0
}
1182
1183
CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen,
1184
                          size_t *nread, bool *eos)
1185
0
{
1186
0
  CURLcode result;
1187
1188
0
  DEBUGASSERT(buf);
1189
0
  DEBUGASSERT(blen);
1190
0
  DEBUGASSERT(nread);
1191
0
  DEBUGASSERT(eos);
1192
0
  *nread = 0;
1193
1194
0
  if(!data->req.reader_stack) {
1195
0
    result = Curl_creader_set_fread(data, data->state.infilesize);
1196
0
    if(result)
1197
0
      return result;
1198
0
    DEBUGASSERT(data->req.reader_stack);
1199
0
  }
1200
0
  if(!data->req.reader_started) {
1201
0
    Curl_rlimit_start(&data->progress.ul.rlimit, Curl_pgrs_now(data), -1);
1202
0
    data->req.reader_started = TRUE;
1203
0
  }
1204
1205
0
  if(Curl_rlimit_active(&data->progress.ul.rlimit)) {
1206
0
    curl_off_t ul_avail = Curl_rlimit_avail(&data->progress.ul.rlimit,
1207
0
                                            Curl_pgrs_now(data));
1208
0
    if(ul_avail <= 0) {
1209
0
      result = CURLE_OK;
1210
0
      *eos = FALSE;
1211
0
      goto out;
1212
0
    }
1213
0
    if(ul_avail < (curl_off_t)blen)
1214
0
      blen = (size_t)ul_avail;
1215
0
  }
1216
0
  result = Curl_creader_read(data, data->req.reader_stack, buf, blen,
1217
0
                             nread, eos);
1218
1219
0
out:
1220
0
  CURL_TRC_READ(data, "client_read(len=%zu) -> %d, nread=%zu, eos=%d",
1221
0
                blen, (int)result, *nread, *eos);
1222
0
  return result;
1223
0
}
1224
1225
bool Curl_creader_needs_rewind(struct Curl_easy *data)
1226
0
{
1227
0
  struct Curl_creader *reader = data->req.reader_stack;
1228
0
  while(reader) {
1229
0
    if(reader->crt->needs_rewind(data, reader)) {
1230
0
      CURL_TRC_READ(data, "client reader needs rewind before next request");
1231
0
      return TRUE;
1232
0
    }
1233
0
    reader = reader->next;
1234
0
  }
1235
0
  return FALSE;
1236
0
}
1237
1238
static CURLcode cr_null_read(struct Curl_easy *data,
1239
                             struct Curl_creader *reader,
1240
                             char *buf, size_t blen,
1241
                             size_t *pnread, bool *peos)
1242
0
{
1243
0
  (void)data;
1244
0
  (void)reader;
1245
0
  (void)buf;
1246
0
  (void)blen;
1247
0
  *pnread = 0;
1248
0
  *peos = TRUE;
1249
0
  return CURLE_OK;
1250
0
}
1251
1252
static curl_off_t cr_null_total_length(struct Curl_easy *data,
1253
                                       struct Curl_creader *reader)
1254
0
{
1255
  /* this reader changes length depending on input */
1256
0
  (void)data;
1257
0
  (void)reader;
1258
0
  return 0;
1259
0
}
1260
1261
static const struct Curl_crtype cr_null = {
1262
  "cr-null",
1263
  Curl_creader_def_init,
1264
  cr_null_read,
1265
  Curl_creader_def_close,
1266
  Curl_creader_def_needs_rewind,
1267
  cr_null_total_length,
1268
  Curl_creader_def_resume_from,
1269
  Curl_creader_def_cntrl,
1270
  Curl_creader_def_is_paused,
1271
  Curl_creader_def_done,
1272
  sizeof(struct Curl_creader)
1273
};
1274
1275
CURLcode Curl_creader_set_null(struct Curl_easy *data)
1276
0
{
1277
0
  struct Curl_creader *r;
1278
0
  CURLcode result;
1279
1280
0
  result = Curl_creader_create(&r, data, &cr_null, CURL_CR_CLIENT);
1281
0
  if(result)
1282
0
    return result;
1283
1284
0
  cl_reset_reader(data);
1285
0
  return do_init_reader_stack(data, r);
1286
0
}
1287
1288
struct cr_buf_ctx {
1289
  struct Curl_creader super;
1290
  const char *buf;
1291
  size_t blen;
1292
  size_t index;
1293
};
1294
1295
static CURLcode cr_buf_read(struct Curl_easy *data,
1296
                            struct Curl_creader *reader,
1297
                            char *buf, size_t blen,
1298
                            size_t *pnread, bool *peos)
1299
0
{
1300
0
  struct cr_buf_ctx *ctx = reader->ctx;
1301
0
  size_t nread = ctx->blen - ctx->index;
1302
1303
0
  if(!nread || !ctx->buf) {
1304
0
    *pnread = 0;
1305
0
    *peos = TRUE;
1306
0
  }
1307
0
  else {
1308
0
    if(nread > blen)
1309
0
      nread = blen;
1310
0
    memcpy(buf, ctx->buf + ctx->index, nread);
1311
0
    *pnread = nread;
1312
0
    ctx->index += nread;
1313
0
    *peos = (ctx->index == ctx->blen);
1314
0
  }
1315
0
  CURL_TRC_READ(data, "cr_buf_read(len=%zu) -> 0, nread=%zu, eos=%d",
1316
0
                blen, *pnread, *peos);
1317
0
  return CURLE_OK;
1318
0
}
1319
1320
static bool cr_buf_needs_rewind(struct Curl_easy *data,
1321
                                struct Curl_creader *reader)
1322
0
{
1323
0
  struct cr_buf_ctx *ctx = reader->ctx;
1324
0
  (void)data;
1325
0
  return ctx->index > 0;
1326
0
}
1327
1328
static CURLcode cr_buf_cntrl(struct Curl_easy *data,
1329
                             struct Curl_creader *reader,
1330
                             Curl_creader_cntrl opcode)
1331
0
{
1332
0
  struct cr_buf_ctx *ctx = reader->ctx;
1333
0
  (void)data;
1334
0
  switch(opcode) {
1335
0
  case CURL_CRCNTRL_REWIND:
1336
0
    ctx->index = 0;
1337
0
    break;
1338
0
  default:
1339
0
    break;
1340
0
  }
1341
0
  return CURLE_OK;
1342
0
}
1343
1344
static curl_off_t cr_buf_total_length(struct Curl_easy *data,
1345
                                      struct Curl_creader *reader)
1346
0
{
1347
0
  struct cr_buf_ctx *ctx = reader->ctx;
1348
0
  (void)data;
1349
0
  return (curl_off_t)ctx->blen;
1350
0
}
1351
1352
static CURLcode cr_buf_resume_from(struct Curl_easy *data,
1353
                                   struct Curl_creader *reader,
1354
                                   curl_off_t offset)
1355
0
{
1356
0
  struct cr_buf_ctx *ctx = reader->ctx;
1357
0
  size_t boffset;
1358
1359
0
  (void)data;
1360
0
  DEBUGASSERT(data->conn);
1361
  /* already started reading? */
1362
0
  if(ctx->index)
1363
0
    return CURLE_READ_ERROR;
1364
0
  boffset = curlx_sotouz_range(offset, 0, SIZE_MAX);
1365
0
  if(!boffset)
1366
0
    return CURLE_OK;
1367
0
  if(boffset > ctx->blen)
1368
0
    return CURLE_READ_ERROR;
1369
1370
0
  ctx->buf += boffset;
1371
0
  ctx->blen -= boffset;
1372
0
  return CURLE_OK;
1373
0
}
1374
1375
static const struct Curl_crtype cr_buf = {
1376
  "cr-buf",
1377
  Curl_creader_def_init,
1378
  cr_buf_read,
1379
  Curl_creader_def_close,
1380
  cr_buf_needs_rewind,
1381
  cr_buf_total_length,
1382
  cr_buf_resume_from,
1383
  cr_buf_cntrl,
1384
  Curl_creader_def_is_paused,
1385
  Curl_creader_def_done,
1386
  sizeof(struct cr_buf_ctx)
1387
};
1388
1389
CURLcode Curl_creader_set_buf(struct Curl_easy *data,
1390
                              const char *buf, size_t blen)
1391
0
{
1392
0
  CURLcode result;
1393
0
  struct Curl_creader *r;
1394
0
  struct cr_buf_ctx *ctx;
1395
1396
0
  result = Curl_creader_create(&r, data, &cr_buf, CURL_CR_CLIENT);
1397
0
  if(result)
1398
0
    goto out;
1399
0
  ctx = r->ctx;
1400
0
  ctx->buf = buf;
1401
0
  ctx->blen = blen;
1402
0
  ctx->index = 0;
1403
1404
0
  cl_reset_reader(data);
1405
0
  result = do_init_reader_stack(data, r);
1406
0
out:
1407
0
  CURL_TRC_READ(data, "add buf reader, len=%zu -> %d", blen, (int)result);
1408
0
  return result;
1409
0
}
1410
1411
curl_off_t Curl_creader_total_length(struct Curl_easy *data)
1412
0
{
1413
0
  struct Curl_creader *r = data->req.reader_stack;
1414
0
  return r ? r->crt->total_length(data, r) : -1;
1415
0
}
1416
1417
curl_off_t Curl_creader_client_length(struct Curl_easy *data)
1418
0
{
1419
0
  struct Curl_creader *r = data->req.reader_stack;
1420
0
  while(r && r->phase != CURL_CR_CLIENT)
1421
0
    r = r->next;
1422
0
  return r ? r->crt->total_length(data, r) : -1;
1423
0
}
1424
1425
CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset)
1426
0
{
1427
0
  struct Curl_creader *r = data->req.reader_stack;
1428
0
  while(r && r->phase != CURL_CR_CLIENT)
1429
0
    r = r->next;
1430
0
  return r ? r->crt->resume_from(data, r, offset) : CURLE_READ_ERROR;
1431
0
}
1432
1433
CURLcode Curl_creader_unpause(struct Curl_easy *data)
1434
0
{
1435
0
  struct Curl_creader *reader = data->req.reader_stack;
1436
0
  CURLcode result = CURLE_OK;
1437
1438
0
  while(reader) {
1439
0
    result = reader->crt->cntrl(data, reader, CURL_CRCNTRL_UNPAUSE);
1440
0
    CURL_TRC_READ(data, "unpausing %s -> %d", reader->crt->name, (int)result);
1441
0
    if(result)
1442
0
      break;
1443
0
    reader = reader->next;
1444
0
  }
1445
0
  return result;
1446
0
}
1447
1448
bool Curl_creader_is_paused(struct Curl_easy *data)
1449
0
{
1450
0
  struct Curl_creader *reader = data->req.reader_stack;
1451
1452
0
  while(reader) {
1453
0
    if(reader->crt->is_paused(data, reader))
1454
0
      return TRUE;
1455
0
    reader = reader->next;
1456
0
  }
1457
0
  return FALSE;
1458
0
}
1459
1460
void Curl_creader_done(struct Curl_easy *data, int premature)
1461
0
{
1462
0
  struct Curl_creader *reader = data->req.reader_stack;
1463
0
  while(reader) {
1464
0
    reader->crt->done(data, reader, premature);
1465
0
    reader = reader->next;
1466
0
  }
1467
0
}
1468
1469
struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data,
1470
                                              const struct Curl_crtype *crt)
1471
0
{
1472
0
  struct Curl_creader *r;
1473
0
  for(r = data->req.reader_stack; r; r = r->next) {
1474
0
    if(r->crt == crt)
1475
0
      return r;
1476
0
  }
1477
0
  return NULL;
1478
0
}