Coverage Report

Created: 2026-10-22 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/cw-pause.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
#include "urldata.h"
27
#include "bufq.h"
28
#include "cfilters.h"
29
#include "sendf.h"
30
#include "curl_trc.h"
31
#include "cw-pause.h"
32
33
34
/* body dynbuf sizes */
35
28
#define CW_PAUSE_BUF_CHUNK         (16 * 1024)
36
/* when content decoding, write data in chunks */
37
#define CW_PAUSE_DEC_WRITE_CHUNK   4096
38
39
struct cw_pause_buf {
40
  struct cw_pause_buf *next;
41
  struct bufq b;
42
  int type;
43
};
44
45
static struct cw_pause_buf *cw_pause_buf_create(int type, size_t buflen)
46
220
{
47
220
  struct cw_pause_buf *cwbuf = curlx_calloc(1, sizeof(*cwbuf));
48
220
  if(cwbuf) {
49
219
    cwbuf->type = type;
50
219
    if(type & CLIENTWRITE_BODY)
51
28
      Curl_bufq_init2(&cwbuf->b, CW_PAUSE_BUF_CHUNK, 1,
52
28
                      (BUFQ_OPT_SOFT_LIMIT | BUFQ_OPT_NO_SPARES));
53
191
    else
54
191
      Curl_bufq_init(&cwbuf->b, buflen, 1);
55
219
  }
56
220
  return cwbuf;
57
220
}
58
59
static void cw_pause_buf_free(struct cw_pause_buf *cwbuf)
60
219
{
61
219
  if(cwbuf) {
62
219
    Curl_bufq_free(&cwbuf->b);
63
219
    curlx_free(cwbuf);
64
219
  }
65
219
}
66
67
struct cw_pause_ctx {
68
  struct Curl_cwriter super;
69
  struct cw_pause_buf *buf;
70
  struct cw_pause_buf *tail;
71
  size_t buf_total;
72
};
73
74
static CURLcode cw_pause_init(struct Curl_easy *data,
75
                              struct Curl_cwriter *writer)
76
904
{
77
904
  struct cw_pause_ctx *ctx = writer->ctx;
78
904
  (void)data;
79
904
  ctx->buf = NULL;
80
904
  ctx->tail = NULL;
81
904
  return CURLE_OK;
82
904
}
83
84
static void cw_pause_bufs_free(struct cw_pause_ctx *ctx)
85
904
{
86
954
  while(ctx->buf) {
87
50
    struct cw_pause_buf *next = ctx->buf->next;
88
50
    cw_pause_buf_free(ctx->buf);
89
50
    ctx->buf = next;
90
50
  }
91
904
  ctx->tail = NULL;
92
904
}
93
94
static void cw_pause_close(struct Curl_easy *data, struct Curl_cwriter *writer)
95
904
{
96
904
  struct cw_pause_ctx *ctx = writer->ctx;
97
98
904
  (void)data;
99
904
  cw_pause_bufs_free(ctx);
100
904
}
101
102
static CURLcode cw_pause_flush(struct Curl_easy *data,
103
                               struct Curl_cwriter *cw_pause)
104
958
{
105
958
  struct cw_pause_ctx *ctx = (struct cw_pause_ctx *)cw_pause;
106
958
  CURLcode result = CURLE_OK;
107
108
  /* write the front of the chain until it blocks or gets empty */
109
1.13k
  while(ctx->buf && !Curl_cwriter_is_paused(data)) {
110
208
    struct cw_pause_buf *first = ctx->buf;
111
208
    size_t blen, wlen = 0;
112
208
    const unsigned char *buf = NULL;
113
114
208
    if(Curl_bufq_peek(&first->b, &buf, &blen)) {
115
187
      int wtype = first->type;
116
187
      wlen = (first->type & CLIENTWRITE_BODY) ?
117
148
             CURLMIN(blen, CW_PAUSE_DEC_WRITE_CHUNK) : blen;
118
      /* EOS only goes with the very last piece, same as in cw_pause_write */
119
187
      if((wlen < Curl_bufq_len(&first->b)) || first->next)
120
154
        wtype &= ~CLIENTWRITE_EOS;
121
187
      result = Curl_cwriter_write(data, cw_pause->next, wtype,
122
187
                                  (const char *)buf, wlen);
123
187
      CURL_TRC_WRITE(data, "[PAUSE] flushed %zu/%zu bytes, type=%x -> %d",
124
187
                     wlen, ctx->buf_total, (unsigned int)wtype,
125
187
                     (int)result);
126
187
      Curl_bufq_skip(&first->b, wlen);
127
187
      DEBUGASSERT(ctx->buf_total >= wlen);
128
187
      ctx->buf_total -= wlen;
129
187
      if(result)
130
31
        return result;
131
187
    }
132
21
    else if(first->type & CLIENTWRITE_EOS) {
133
0
      result = Curl_cwriter_write(data, cw_pause->next, first->type,
134
0
                                  (const char *)buf, 0);
135
0
      CURL_TRC_WRITE(data, "[PAUSE] flushed 0/%zu bytes, type=%x -> %d",
136
0
                     ctx->buf_total, (unsigned int)first->type,
137
0
                     (int)result);
138
0
    }
139
140
177
    if(Curl_bufq_is_empty(&first->b)) {
141
169
      ctx->buf = first->next;
142
169
      if(!ctx->buf)
143
28
        ctx->tail = NULL;
144
169
      cw_pause_buf_free(first);
145
169
    }
146
177
  }
147
148
927
  if(!result)
149
927
    result = Curl_cwriter_flush(data, cw_pause->next);
150
927
  return result;
151
958
}
152
153
static CURLcode cw_pause_write(struct Curl_easy *data,
154
                               struct Curl_cwriter *writer, int type,
155
                               const char *buf, size_t blen)
156
8.30k
{
157
8.30k
  struct cw_pause_ctx *ctx = writer->ctx;
158
8.30k
  CURLcode result = CURLE_OK;
159
8.30k
  size_t wlen = 0;
160
161
8.30k
  if(ctx->buf && !Curl_cwriter_is_paused(data)) {
162
18
    result = cw_pause_flush(data, writer);
163
18
    if(result)
164
5
      return result;
165
18
  }
166
167
8.58k
  while(!ctx->buf && !Curl_cwriter_is_paused(data)) {
168
8.36k
    int wtype = type;
169
8.36k
    DEBUGASSERT(!ctx->buf);
170
    /* content decoding might blow up size considerably, write smaller
171
     * chunks to make pausing need buffer less. */
172
8.36k
    wlen = (type & CLIENTWRITE_BODY) ?
173
6.22k
           CURLMIN(blen, CW_PAUSE_DEC_WRITE_CHUNK) : blen;
174
8.36k
    if(wlen < blen)
175
292
      wtype &= ~CLIENTWRITE_EOS;
176
8.36k
    result = Curl_cwriter_write(data, writer->next, wtype, buf, wlen);
177
8.36k
    if(result)
178
294
      return result;
179
8.07k
    buf += wlen;
180
8.07k
    blen -= wlen;
181
8.07k
    if(!blen)
182
7.78k
      return result;
183
8.07k
  }
184
185
220
  do {
186
220
    size_t nwritten = 0;
187
220
    if(ctx->tail && (ctx->tail->type == type) && (type & CLIENTWRITE_BODY)) {
188
      /* same type and body, append to tail buffer which has a soft
189
       * limit and should take everything up to OOM. */
190
0
      result = Curl_bufq_cwrite(&ctx->tail->b, buf, blen, &nwritten);
191
0
    }
192
220
    else {
193
      /* Need a new buf, type changed */
194
220
      struct cw_pause_buf *cwbuf = cw_pause_buf_create(type, blen);
195
220
      if(!cwbuf)
196
1
        return CURLE_OUT_OF_MEMORY;
197
219
      if(ctx->tail)
198
178
        ctx->tail->next = cwbuf;
199
41
      else
200
41
        ctx->buf = cwbuf;
201
219
      ctx->tail = cwbuf;
202
219
      result = Curl_bufq_cwrite(&ctx->tail->b, buf, blen, &nwritten);
203
219
    }
204
219
    CURL_TRC_WRITE(data, "[PAUSE] buffer %zu more bytes of type %x, "
205
219
                   "total=%zu -> %d", nwritten, (unsigned int)type,
206
219
                   ctx->buf_total + wlen, (int)result);
207
219
    if(result)
208
2
      return result;
209
217
    buf += nwritten;
210
217
    blen -= nwritten;
211
217
    ctx->buf_total += nwritten;
212
217
  } while(blen);
213
214
217
  return result;
215
220
}
216
217
const struct Curl_cwtype Curl_cwt_pause = {
218
  "cw-pause",
219
  NULL,
220
  0,
221
  cw_pause_init,
222
  cw_pause_write,
223
  cw_pause_flush,
224
  cw_pause_close,
225
  sizeof(struct cw_pause_ctx)
226
};