Coverage Report

Created: 2025-10-10 06:31

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