1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
|
#ifndef HEADER_CURL_SENDF_H
#define HEADER_CURL_SENDF_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "curl_trc.h"
/**
* Type of data that is being written to the client (application)
* - data written can be either BODY or META data
* - META data is either INFO or HEADER
* - INFO is meta information, e.g. not BODY, that cannot be interpreted
* as headers of a response. Example FTP/IMAP pingpong answers.
* - HEADER can have additional bits set (more than one)
* - STATUS special "header", e.g. response status line in HTTP
* - CONNECT header was received during proxying the connection
* - 1XX header is part of an intermediate response, e.g. HTTP 1xx code
* - TRAILER header is trailing response data, e.g. HTTP trailers
* BODY, INFO and HEADER should not be mixed, as this would lead to
* confusion on how to interpret/format/convert the data.
*/
#define CLIENTWRITE_BODY (1<<0) /* non-meta information, BODY */
#define CLIENTWRITE_INFO (1<<1) /* meta information, not a HEADER */
#define CLIENTWRITE_HEADER (1<<2) /* meta information, HEADER */
#define CLIENTWRITE_STATUS (1<<3) /* a special status HEADER */
#define CLIENTWRITE_CONNECT (1<<4) /* a CONNECT related HEADER */
#define CLIENTWRITE_1XX (1<<5) /* a 1xx response related HEADER */
#define CLIENTWRITE_TRAILER (1<<6) /* a trailer HEADER */
#define CLIENTWRITE_EOS (1<<7) /* End Of transfer download Stream */
/**
* Write `len` bytes at `prt` to the client. `type` indicates what
* kind of data is being written.
*/
CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *ptr,
size_t len) WARN_UNUSED_RESULT;
/**
* Free all resources related to client writing.
*/
void Curl_client_cleanup(struct Curl_easy *data);
/**
* Reset readers and writer chains, keep rewind information
* when necessary.
*/
void Curl_client_reset(struct Curl_easy *data);
/**
* A new request is starting, perform any ops like rewinding
* previous readers when needed.
*/
CURLcode Curl_client_start(struct Curl_easy *data);
/**
* Client Writers - a chain passing transfer BODY data to the client.
* Main application: HTTP and related protocols
* Other uses: monitoring of download progress
*
* Writers in the chain are order by their `phase`. First come all
* writers in CURL_CW_RAW, followed by any in CURL_CW_TRANSFER_DECODE,
* followed by any in CURL_CW_PROTOCOL, etc.
*
* When adding a writer, it is inserted as first in its phase. This means
* the order of adding writers of the same phase matters, but writers for
* different phases may be added in any order.
*
* Writers which do modify the BODY data written are expected to be of
* phases TRANSFER_DECODE or CONTENT_DECODE. The other phases are intended
* for monitoring writers. Which do *not* modify the data but gather
* statistics or update progress reporting.
*/
/* Phase a writer operates at. */
typedef enum {
CURL_CW_RAW, /* raw data written, before any decoding */
CURL_CW_TRANSFER_DECODE, /* remove transfer-encodings */
CURL_CW_PROTOCOL, /* after transfer, but before content decoding */
CURL_CW_CONTENT_DECODE, /* remove content-encodings */
CURL_CW_CLIENT /* data written to client */
} Curl_cwriter_phase;
/* Client Writer Type, provides the implementation */
struct Curl_cwtype {
const char *name; /* writer name. */
const char *alias; /* writer name alias, maybe NULL. */
CURLcode (*do_init)(struct Curl_easy *data,
struct Curl_cwriter *writer);
CURLcode (*do_write)(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes);
void (*do_close)(struct Curl_easy *data,
struct Curl_cwriter *writer);
size_t cwriter_size; /* sizeof() allocated struct Curl_cwriter */
};
/* Client writer instance, allocated on creation.
* `void *ctx` is the pointer from the allocation of
* the `struct Curl_cwriter` itself. This is suitable for "downcasting"
* by the writers implementation. See https://github.com/curl/curl/pull/13054
* for the alignment problems that arise otherwise.
*/
struct Curl_cwriter {
const struct Curl_cwtype *cwt; /* type implementation */
struct Curl_cwriter *next; /* Downstream writer. */
void *ctx; /* allocated instance pointer */
Curl_cwriter_phase phase; /* phase at which it operates */
};
/**
* Create a new cwriter instance with given type and phase. Is not
* inserted into the writer chain by this call.
* Invokes `writer->do_init()`.
*/
CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter,
struct Curl_easy *data,
const struct Curl_cwtype *ce_handler,
Curl_cwriter_phase phase);
/**
* Free a cwriter instance.
* Invokes `writer->do_close()`.
*/
void Curl_cwriter_free(struct Curl_easy *data,
struct Curl_cwriter *writer);
/**
* Count the number of writers installed of the given phase.
*/
size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase);
/**
* Adds a writer to the transfer's writer chain.
* The writers `phase` determines where in the chain it is inserted.
*/
CURLcode Curl_cwriter_add(struct Curl_easy *data,
struct Curl_cwriter *writer);
/**
* Look up an installed client writer on `data` by its type.
* @return first writer with that type or NULL
*/
struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data,
const struct Curl_cwtype *cwt);
void Curl_cwriter_remove_by_name(struct Curl_easy *data,
const char *name);
struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data,
const char *name);
/**
* Convenience method for calling `writer->do_write()` that
* checks for NULL writer.
*/
CURLcode Curl_cwriter_write(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes);
/**
* Return TRUE iff client writer is paused.
*/
bool Curl_cwriter_is_paused(struct Curl_easy *data);
/**
* Unpause client writer and flush any buffered date to the client.
*/
CURLcode Curl_cwriter_unpause(struct Curl_easy *data);
/**
* Default implementations for do_init, do_write, do_close that
* do nothing and pass the data through.
*/
CURLcode Curl_cwriter_def_init(struct Curl_easy *data,
struct Curl_cwriter *writer);
CURLcode Curl_cwriter_def_write(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes);
void Curl_cwriter_def_close(struct Curl_easy *data,
struct Curl_cwriter *writer);
/* Client Reader Type, provides the implementation */
struct Curl_crtype {
const char *name; /* writer name. */
CURLcode (*do_init)(struct Curl_easy *data, struct Curl_creader *reader);
CURLcode (*do_read)(struct Curl_easy *data, struct Curl_creader *reader,
char *buf, size_t blen, size_t *nread, bool *eos);
void (*do_close)(struct Curl_easy *data, struct Curl_creader *reader);
bool (*needs_rewind)(struct Curl_easy *data, struct Curl_creader *reader);
curl_off_t (*total_length)(struct Curl_easy *data,
struct Curl_creader *reader);
CURLcode (*resume_from)(struct Curl_easy *data,
struct Curl_creader *reader, curl_off_t offset);
CURLcode (*rewind)(struct Curl_easy *data, struct Curl_creader *reader);
CURLcode (*unpause)(struct Curl_easy *data, struct Curl_creader *reader);
bool (*is_paused)(struct Curl_easy *data, struct Curl_creader *reader);
void (*done)(struct Curl_easy *data,
struct Curl_creader *reader, int premature);
size_t creader_size; /* sizeof() allocated struct Curl_creader */
};
/* Phase a reader operates at. */
typedef enum {
CURL_CR_NET, /* data send to the network (connection filters) */
CURL_CR_TRANSFER_ENCODE, /* add transfer-encodings */
CURL_CR_PROTOCOL, /* before transfer, but after content decoding */
CURL_CR_CONTENT_ENCODE, /* add content-encodings */
CURL_CR_CLIENT /* data read from client */
} Curl_creader_phase;
/* Client reader instance, allocated on creation.
* `void *ctx` is the pointer from the allocation of
* the `struct Curl_cwriter` itself. This is suitable for "downcasting"
* by the writers implementation. See https://github.com/curl/curl/pull/13054
* for the alignment problems that arise otherwise.
*/
struct Curl_creader {
const struct Curl_crtype *crt; /* type implementation */
struct Curl_creader *next; /* Downstream reader. */
void *ctx;
Curl_creader_phase phase; /* phase at which it operates */
};
/**
* Default implementations for do_init, do_write, do_close that
* do nothing and pass the data through.
*/
CURLcode Curl_creader_def_init(struct Curl_easy *data,
struct Curl_creader *reader);
void Curl_creader_def_close(struct Curl_easy *data,
struct Curl_creader *reader);
CURLcode Curl_creader_def_read(struct Curl_easy *data,
struct Curl_creader *reader,
char *buf, size_t blen,
size_t *nread, bool *eos);
bool Curl_creader_def_needs_rewind(struct Curl_easy *data,
struct Curl_creader *reader);
curl_off_t Curl_creader_def_total_length(struct Curl_easy *data,
struct Curl_creader *reader);
CURLcode Curl_creader_def_resume_from(struct Curl_easy *data,
struct Curl_creader *reader,
curl_off_t offset);
CURLcode Curl_creader_def_rewind(struct Curl_easy *data,
struct Curl_creader *reader);
CURLcode Curl_creader_def_unpause(struct Curl_easy *data,
struct Curl_creader *reader);
bool Curl_creader_def_is_paused(struct Curl_easy *data,
struct Curl_creader *reader);
void Curl_creader_def_done(struct Curl_easy *data,
struct Curl_creader *reader, int premature);
/**
* Convenience method for calling `reader->do_read()` that
* checks for NULL reader.
*/
CURLcode Curl_creader_read(struct Curl_easy *data,
struct Curl_creader *reader,
char *buf, size_t blen, size_t *nread, bool *eos);
/**
* Create a new creader instance with given type and phase. Is not
* inserted into the writer chain by this call.
* Invokes `reader->do_init()`.
*/
CURLcode Curl_creader_create(struct Curl_creader **preader,
struct Curl_easy *data,
const struct Curl_crtype *cr_handler,
Curl_creader_phase phase);
/**
* Free a creader instance.
* Invokes `reader->do_close()`.
*/
void Curl_creader_free(struct Curl_easy *data, struct Curl_creader *reader);
/**
* Adds a reader to the transfer's reader chain.
* The readers `phase` determines where in the chain it is inserted.
*/
CURLcode Curl_creader_add(struct Curl_easy *data,
struct Curl_creader *reader);
/**
* Set the given reader, which needs to be of type CURL_CR_CLIENT,
* as the new first reader. Discard any installed readers and init
* the reader chain anew.
* The function takes ownership of `r`.
*/
CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r);
/**
* Read at most `blen` bytes at `buf` from the client.
* @param data the transfer to read client bytes for
* @param buf the memory location to read to
* @param blen the amount of memory at `buf`
* @param nread on return the number of bytes read into `buf`
* @param eos TRUE iff bytes are the end of data from client
* @return CURLE_OK on successful read (even 0 length) or error
*/
CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen,
size_t *nread, bool *eos) WARN_UNUSED_RESULT;
/**
* TRUE iff client reader needs rewing before it can be used for
* a retry request.
*/
bool Curl_creader_needs_rewind(struct Curl_easy *data);
/**
* TRUE iff client reader will rewind at next start
*/
bool Curl_creader_will_rewind(struct Curl_easy *data);
/**
* En-/disable rewind of client reader at next start.
*/
void Curl_creader_set_rewind(struct Curl_easy *data, bool enable);
/**
* Get the total length of bytes provided by the installed readers.
* This is independent of the amount already delivered and is calculated
* by all readers in the stack. If a reader like "chunked" or
* "crlf conversion" is installed, the returned length will be -1.
* @return -1 if length is indeterminate
*/
curl_off_t Curl_creader_total_length(struct Curl_easy *data);
/**
* Get the total length of bytes provided by the reader at phase
* CURL_CR_CLIENT. This may not match the amount of bytes read
* for a request, depending if other, encoding readers are also installed.
* However it allows for rough estimation of the overall length.
* @return -1 if length is indeterminate
*/
curl_off_t Curl_creader_client_length(struct Curl_easy *data);
/**
* Ask the installed reader at phase CURL_CR_CLIENT to start
* reading from the given offset. On success, this will reduce
* the `total_length()` by the amount.
* @param data the transfer to read client bytes for
* @param offset the offset where to start reads from, negative
* values will be ignored.
* @return CURLE_OK if offset could be set
* CURLE_READ_ERROR if not supported by reader or seek/read failed
* of offset larger then total length
* CURLE_PARTIAL_FILE if offset led to 0 total length
*/
CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset);
/**
* Unpause all installed readers.
*/
CURLcode Curl_creader_unpause(struct Curl_easy *data);
/**
* Return TRUE iff any of the installed readers is paused.
*/
bool Curl_creader_is_paused(struct Curl_easy *data);
/**
* Tell all client readers that they are done.
*/
void Curl_creader_done(struct Curl_easy *data, int premature);
/**
* Look up an installed client reader on `data` by its type.
* @return first reader with that type or NULL
*/
struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data,
const struct Curl_crtype *crt);
/**
* Set the client reader to provide 0 bytes, immediate EOS.
*/
CURLcode Curl_creader_set_null(struct Curl_easy *data);
/**
* Set the client reader the reads from fread callback.
*/
CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len);
/**
* Set the client reader the reads from the supplied buf (NOT COPIED).
*/
CURLcode Curl_creader_set_buf(struct Curl_easy *data,
const char *buf, size_t blen);
#endif /* HEADER_CURL_SENDF_H */
|