Coverage Report

Created: 2026-08-14 09:29

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