xref: /third_party/curl/lib/sendf.c (revision 13498266)
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#ifdef HAVE_NETINET_IN_H
28#include <netinet/in.h>
29#endif
30
31#ifdef HAVE_LINUX_TCP_H
32#include <linux/tcp.h>
33#elif defined(HAVE_NETINET_TCP_H)
34#include <netinet/tcp.h>
35#endif
36
37#include <curl/curl.h>
38
39#include "urldata.h"
40#include "sendf.h"
41#include "cfilters.h"
42#include "connect.h"
43#include "content_encoding.h"
44#include "vtls/vtls.h"
45#include "vssh/ssh.h"
46#include "easyif.h"
47#include "multiif.h"
48#include "strerror.h"
49#include "select.h"
50#include "strdup.h"
51#include "http2.h"
52#include "headers.h"
53#include "progress.h"
54#include "ws.h"
55
56/* The last 3 #include files should be in this order */
57#include "curl_printf.h"
58#include "curl_memory.h"
59#include "memdebug.h"
60
61
62static CURLcode do_init_stack(struct Curl_easy *data);
63
64#if defined(CURL_DO_LINEEND_CONV) && !defined(CURL_DISABLE_FTP)
65/*
66 * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF
67 * (\n), with special processing for CRLF sequences that are split between two
68 * blocks of data.  Remaining, bare CRs are changed to LFs.  The possibly new
69 * size of the data is returned.
70 */
71static size_t convert_lineends(struct Curl_easy *data,
72                               char *startPtr, size_t size)
73{
74  char *inPtr, *outPtr;
75
76  /* sanity check */
77  if(!startPtr || (size < 1)) {
78    return size;
79  }
80
81  if(data->state.prev_block_had_trailing_cr) {
82    /* The previous block of incoming data
83       had a trailing CR, which was turned into a LF. */
84    if(*startPtr == '\n') {
85      /* This block of incoming data starts with the
86         previous block's LF so get rid of it */
87      memmove(startPtr, startPtr + 1, size-1);
88      size--;
89      /* and it wasn't a bare CR but a CRLF conversion instead */
90      data->state.crlf_conversions++;
91    }
92    data->state.prev_block_had_trailing_cr = FALSE; /* reset the flag */
93  }
94
95  /* find 1st CR, if any */
96  inPtr = outPtr = memchr(startPtr, '\r', size);
97  if(inPtr) {
98    /* at least one CR, now look for CRLF */
99    while(inPtr < (startPtr + size-1)) {
100      /* note that it's size-1, so we'll never look past the last byte */
101      if(memcmp(inPtr, "\r\n", 2) == 0) {
102        /* CRLF found, bump past the CR and copy the NL */
103        inPtr++;
104        *outPtr = *inPtr;
105        /* keep track of how many CRLFs we converted */
106        data->state.crlf_conversions++;
107      }
108      else {
109        if(*inPtr == '\r') {
110          /* lone CR, move LF instead */
111          *outPtr = '\n';
112        }
113        else {
114          /* not a CRLF nor a CR, just copy whatever it is */
115          *outPtr = *inPtr;
116        }
117      }
118      outPtr++;
119      inPtr++;
120    } /* end of while loop */
121
122    if(inPtr < startPtr + size) {
123      /* handle last byte */
124      if(*inPtr == '\r') {
125        /* deal with a CR at the end of the buffer */
126        *outPtr = '\n'; /* copy a NL instead */
127        /* note that a CRLF might be split across two blocks */
128        data->state.prev_block_had_trailing_cr = TRUE;
129      }
130      else {
131        /* copy last byte */
132        *outPtr = *inPtr;
133      }
134      outPtr++;
135    }
136    if(outPtr < startPtr + size)
137      /* tidy up by null terminating the now shorter data */
138      *outPtr = '\0';
139
140    return (outPtr - startPtr);
141  }
142  return size;
143}
144#endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */
145
146/*
147 * Curl_nwrite() is an internal write function that sends data to the
148 * server. Works with a socket index for the connection.
149 *
150 * If the write would block (CURLE_AGAIN), it returns CURLE_OK and
151 * (*nwritten == 0). Otherwise we return regular CURLcode value.
152 */
153CURLcode Curl_nwrite(struct Curl_easy *data,
154                     int sockindex,
155                     const void *buf,
156                     size_t blen,
157                     ssize_t *pnwritten)
158{
159  ssize_t nwritten;
160  CURLcode result = CURLE_OK;
161  struct connectdata *conn;
162
163  DEBUGASSERT(sockindex >= 0 && sockindex < 2);
164  DEBUGASSERT(pnwritten);
165  DEBUGASSERT(data);
166  DEBUGASSERT(data->conn);
167  conn = data->conn;
168#ifdef CURLDEBUG
169  {
170    /* Allow debug builds to override this logic to force short sends
171    */
172    char *p = getenv("CURL_SMALLSENDS");
173    if(p) {
174      size_t altsize = (size_t)strtoul(p, NULL, 10);
175      if(altsize)
176        blen = CURLMIN(blen, altsize);
177    }
178  }
179#endif
180  nwritten = conn->send[sockindex](data, sockindex, buf, blen, &result);
181  if(result == CURLE_AGAIN) {
182    nwritten = 0;
183    result = CURLE_OK;
184  }
185  else if(result) {
186    nwritten = -1; /* make sure */
187  }
188  else {
189    DEBUGASSERT(nwritten >= 0);
190  }
191
192  *pnwritten = nwritten;
193  return result;
194}
195
196/*
197 * Curl_write() is an internal write function that sends data to the
198 * server. Works with plain sockets, SCP, SSL or kerberos.
199 *
200 * If the write would block (CURLE_AGAIN), we return CURLE_OK and
201 * (*written == 0). Otherwise we return regular CURLcode value.
202 */
203CURLcode Curl_write(struct Curl_easy *data,
204                    curl_socket_t sockfd,
205                    const void *mem,
206                    size_t len,
207                    ssize_t *written)
208{
209  struct connectdata *conn;
210  int num;
211
212  DEBUGASSERT(data);
213  DEBUGASSERT(data->conn);
214  conn = data->conn;
215  num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]);
216  return Curl_nwrite(data, num, mem, len, written);
217}
218
219static CURLcode pausewrite(struct Curl_easy *data,
220                           int type, /* what type of data */
221                           bool paused_body,
222                           const char *ptr,
223                           size_t len)
224{
225  /* signalled to pause sending on this connection, but since we have data
226     we want to send we need to dup it to save a copy for when the sending
227     is again enabled */
228  struct SingleRequest *k = &data->req;
229  struct UrlState *s = &data->state;
230  unsigned int i;
231  bool newtype = TRUE;
232
233  Curl_conn_ev_data_pause(data, TRUE);
234
235  if(s->tempcount) {
236    for(i = 0; i< s->tempcount; i++) {
237      if(s->tempwrite[i].type == type &&
238         !!s->tempwrite[i].paused_body == !!paused_body) {
239        /* data for this type exists */
240        newtype = FALSE;
241        break;
242      }
243    }
244    DEBUGASSERT(i < 3);
245    if(i >= 3)
246      /* There are more types to store than what fits: very bad */
247      return CURLE_OUT_OF_MEMORY;
248  }
249  else
250    i = 0;
251
252  if(newtype) {
253    /* store this information in the state struct for later use */
254    Curl_dyn_init(&s->tempwrite[i].b, DYN_PAUSE_BUFFER);
255    s->tempwrite[i].type = type;
256    s->tempwrite[i].paused_body = paused_body;
257    s->tempcount++;
258  }
259
260  if(Curl_dyn_addn(&s->tempwrite[i].b, (unsigned char *)ptr, len))
261    return CURLE_OUT_OF_MEMORY;
262
263  /* mark the connection as RECV paused */
264  k->keepon |= KEEP_RECV_PAUSE;
265
266  return CURLE_OK;
267}
268
269
270/* chop_write() writes chunks of data not larger than CURL_MAX_WRITE_SIZE via
271 * client write callback(s) and takes care of pause requests from the
272 * callbacks.
273 */
274static CURLcode chop_write(struct Curl_easy *data,
275                           int type,
276                           bool skip_body_write,
277                           char *optr,
278                           size_t olen)
279{
280  struct connectdata *conn = data->conn;
281  curl_write_callback writeheader = NULL;
282  curl_write_callback writebody = NULL;
283  char *ptr = optr;
284  size_t len = olen;
285  void *writebody_ptr = data->set.out;
286
287  if(!len)
288    return CURLE_OK;
289
290  /* If reading is paused, append this data to the already held data for this
291     type. */
292  if(data->req.keepon & KEEP_RECV_PAUSE)
293    return pausewrite(data, type, !skip_body_write, ptr, len);
294
295  /* Determine the callback(s) to use. */
296  if(!skip_body_write &&
297     ((type & CLIENTWRITE_BODY) ||
298      ((type & CLIENTWRITE_HEADER) && data->set.include_header))) {
299    writebody = data->set.fwrite_func;
300  }
301  if((type & (CLIENTWRITE_HEADER|CLIENTWRITE_INFO)) &&
302     (data->set.fwrite_header || data->set.writeheader)) {
303    /*
304     * Write headers to the same callback or to the especially setup
305     * header callback function (added after version 7.7.1).
306     */
307    writeheader =
308      data->set.fwrite_header? data->set.fwrite_header: data->set.fwrite_func;
309  }
310
311  /* Chop data, write chunks. */
312  while(len) {
313    size_t chunklen = len <= CURL_MAX_WRITE_SIZE? len: CURL_MAX_WRITE_SIZE;
314
315    if(writebody) {
316      size_t wrote;
317      Curl_set_in_callback(data, true);
318      wrote = writebody(ptr, 1, chunklen, writebody_ptr);
319      Curl_set_in_callback(data, false);
320
321      if(CURL_WRITEFUNC_PAUSE == wrote) {
322        if(conn->handler->flags & PROTOPT_NONETWORK) {
323          /* Protocols that work without network cannot be paused. This is
324             actually only FILE:// just now, and it can't pause since the
325             transfer isn't done using the "normal" procedure. */
326          failf(data, "Write callback asked for PAUSE when not supported");
327          return CURLE_WRITE_ERROR;
328        }
329        return pausewrite(data, type, TRUE, ptr, len);
330      }
331      if(wrote != chunklen) {
332        failf(data, "Failure writing output to destination");
333        return CURLE_WRITE_ERROR;
334      }
335    }
336
337    ptr += chunklen;
338    len -= chunklen;
339  }
340
341#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API)
342  /* HTTP header, but not status-line */
343  if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
344     (type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS) ) {
345    unsigned char htype = (unsigned char)
346      (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
347       (type & CLIENTWRITE_1XX ? CURLH_1XX :
348        (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
349         CURLH_HEADER)));
350    CURLcode result = Curl_headers_push(data, optr, htype);
351    if(result)
352      return result;
353  }
354#endif
355
356  if(writeheader) {
357    size_t wrote;
358
359    Curl_set_in_callback(data, true);
360    wrote = writeheader(optr, 1, olen, data->set.writeheader);
361    Curl_set_in_callback(data, false);
362
363    if(CURL_WRITEFUNC_PAUSE == wrote)
364      return pausewrite(data, type, FALSE, optr, olen);
365    if(wrote != olen) {
366      failf(data, "Failed writing header");
367      return CURLE_WRITE_ERROR;
368    }
369  }
370
371  return CURLE_OK;
372}
373
374
375/* Curl_client_write() sends data to the write callback(s)
376
377   The bit pattern defines to what "streams" to write to. Body and/or header.
378   The defines are in sendf.h of course.
379
380   If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the
381   local character encoding.  This is a problem and should be changed in
382   the future to leave the original data alone.
383 */
384CURLcode Curl_client_write(struct Curl_easy *data,
385                           int type, char *buf, size_t blen)
386{
387  CURLcode result;
388
389#if !defined(CURL_DISABLE_FTP) && defined(CURL_DO_LINEEND_CONV)
390  /* FTP data may need conversion. */
391  if((type & CLIENTWRITE_BODY) &&
392     (data->conn->handler->protocol & PROTO_FAMILY_FTP) &&
393     data->conn->proto.ftpc.transfertype == 'A') {
394    /* convert end-of-line markers */
395    blen = convert_lineends(data, buf, blen);
396  }
397#endif
398  /* it is one of those, at least */
399  DEBUGASSERT(type & (CLIENTWRITE_BODY|CLIENTWRITE_HEADER|CLIENTWRITE_INFO));
400  /* BODY is only BODY (with optional EOS) */
401  DEBUGASSERT(!(type & CLIENTWRITE_BODY) ||
402              ((type & ~(CLIENTWRITE_BODY|CLIENTWRITE_EOS)) == 0));
403  /* INFO is only INFO (with optional EOS) */
404  DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
405              ((type & ~(CLIENTWRITE_INFO|CLIENTWRITE_EOS)) == 0));
406
407  if(!data->req.writer_stack) {
408    result = do_init_stack(data);
409    if(result)
410      return result;
411    DEBUGASSERT(data->req.writer_stack);
412  }
413
414  return Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen);
415}
416
417CURLcode Curl_client_unpause(struct Curl_easy *data)
418{
419  CURLcode result = CURLE_OK;
420
421  if(data->state.tempcount) {
422    /* there are buffers for sending that can be delivered as the receive
423       pausing is lifted! */
424    unsigned int i;
425    unsigned int count = data->state.tempcount;
426    struct tempbuf writebuf[3]; /* there can only be three */
427
428    /* copy the structs to allow for immediate re-pausing */
429    for(i = 0; i < data->state.tempcount; i++) {
430      writebuf[i] = data->state.tempwrite[i];
431      Curl_dyn_init(&data->state.tempwrite[i].b, DYN_PAUSE_BUFFER);
432    }
433    data->state.tempcount = 0;
434
435    for(i = 0; i < count; i++) {
436      /* even if one function returns error, this loops through and frees
437         all buffers */
438      if(!result)
439        result = chop_write(data, writebuf[i].type,
440                            !writebuf[i].paused_body,
441                            Curl_dyn_ptr(&writebuf[i].b),
442                            Curl_dyn_len(&writebuf[i].b));
443      Curl_dyn_free(&writebuf[i].b);
444    }
445  }
446  return result;
447}
448
449void Curl_client_cleanup(struct Curl_easy *data)
450{
451  struct Curl_cwriter *writer = data->req.writer_stack;
452  size_t i;
453
454  while(writer) {
455    data->req.writer_stack = writer->next;
456    writer->cwt->do_close(data, writer);
457    free(writer);
458    writer = data->req.writer_stack;
459  }
460
461  for(i = 0; i < data->state.tempcount; i++) {
462    Curl_dyn_free(&data->state.tempwrite[i].b);
463  }
464  data->state.tempcount = 0;
465  data->req.bytecount = 0;
466  data->req.headerline = 0;
467}
468
469/* Write data using an unencoding writer stack. "nbytes" is not
470   allowed to be 0. */
471CURLcode Curl_cwriter_write(struct Curl_easy *data,
472                             struct Curl_cwriter *writer, int type,
473                             const char *buf, size_t nbytes)
474{
475  if(!writer)
476    return CURLE_WRITE_ERROR;
477  return writer->cwt->do_write(data, writer, type, buf, nbytes);
478}
479
480CURLcode Curl_cwriter_def_init(struct Curl_easy *data,
481                               struct Curl_cwriter *writer)
482{
483  (void)data;
484  (void)writer;
485  return CURLE_OK;
486}
487
488CURLcode Curl_cwriter_def_write(struct Curl_easy *data,
489                                struct Curl_cwriter *writer, int type,
490                                const char *buf, size_t nbytes)
491{
492  return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
493}
494
495void Curl_cwriter_def_close(struct Curl_easy *data,
496                            struct Curl_cwriter *writer)
497{
498  (void) data;
499  (void) writer;
500}
501
502/* Real client writer to installed callbacks. */
503static CURLcode cw_client_write(struct Curl_easy *data,
504                                struct Curl_cwriter *writer, int type,
505                                const char *buf, size_t nbytes)
506{
507  (void)writer;
508  if(!nbytes)
509    return CURLE_OK;
510  return chop_write(data, type, FALSE, (char *)buf, nbytes);
511}
512
513static const struct Curl_cwtype cw_client = {
514  "client",
515  NULL,
516  Curl_cwriter_def_init,
517  cw_client_write,
518  Curl_cwriter_def_close,
519  sizeof(struct Curl_cwriter)
520};
521
522static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit)
523{
524  if(limit != -1) {
525    /* How much more are we allowed to write? */
526    curl_off_t remain_diff;
527    remain_diff = limit - data->req.bytecount;
528    if(remain_diff < 0) {
529      /* already written too much! */
530      return 0;
531    }
532#if SIZEOF_CURL_OFF_T > SIZEOF_SIZE_T
533    else if(remain_diff > SSIZE_T_MAX) {
534      return SIZE_T_MAX;
535    }
536#endif
537    else {
538      return (size_t)remain_diff;
539    }
540  }
541  return SIZE_T_MAX;
542}
543
544/* Download client writer in phase CURL_CW_PROTOCOL that
545 * sees the "real" download body data. */
546static CURLcode cw_download_write(struct Curl_easy *data,
547                                  struct Curl_cwriter *writer, int type,
548                                  const char *buf, size_t nbytes)
549{
550  CURLcode result;
551  size_t nwrite, excess_len = 0;
552
553  if(!(type & CLIENTWRITE_BODY)) {
554    if((type & CLIENTWRITE_CONNECT) && data->set.suppress_connect_headers)
555      return CURLE_OK;
556    return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
557  }
558
559  if(!data->req.bytecount) {
560    Curl_pgrsTime(data, TIMER_STARTTRANSFER);
561    if(data->req.exp100 > EXP100_SEND_DATA)
562      /* set time stamp to compare with when waiting for the 100 */
563      data->req.start100 = Curl_now();
564  }
565
566  /* Here, we deal with REAL BODY bytes. All filtering and transfer
567   * encodings have been applied and only the true content, e.g. BODY,
568   * bytes are passed here.
569   * This allows us to check sizes, update stats, etc. independent
570   * from the protocol in play. */
571
572  if(data->req.no_body && nbytes > 0) {
573    /* BODY arrives although we want none, bail out */
574    streamclose(data->conn, "ignoring body");
575    DEBUGF(infof(data, "did not want a BODY, but seeing %zu bytes",
576                 nbytes));
577    data->req.download_done = TRUE;
578    return CURLE_WEIRD_SERVER_REPLY;
579  }
580
581  /* Determine if we see any bytes in excess to what is allowed.
582   * We write the allowed bytes and handle excess further below.
583   * This gives deterministic BODY writes on varying buffer receive
584   * lengths. */
585  nwrite = nbytes;
586  if(-1 != data->req.maxdownload) {
587    size_t wmax = get_max_body_write_len(data, data->req.maxdownload);
588    if(nwrite > wmax) {
589      excess_len = nbytes - wmax;
590      nwrite = wmax;
591    }
592
593    if(nwrite == wmax) {
594      data->req.download_done = TRUE;
595    }
596  }
597
598  /* Error on too large filesize is handled below, after writing
599   * the permitted bytes */
600  if(data->set.max_filesize) {
601    size_t wmax = get_max_body_write_len(data, data->set.max_filesize);
602    if(nwrite > wmax) {
603      nwrite = wmax;
604    }
605  }
606
607  /* Update stats, write and report progress */
608  data->req.bytecount += nwrite;
609  ++data->req.bodywrites;
610  if(!data->req.ignorebody && nwrite) {
611    result = Curl_cwriter_write(data, writer->next, type, buf, nwrite);
612    if(result)
613      return result;
614  }
615  result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
616  if(result)
617    return result;
618
619  if(excess_len) {
620    if(!data->req.ignorebody) {
621      infof(data,
622            "Excess found writing body:"
623            " excess = %zu"
624            ", size = %" CURL_FORMAT_CURL_OFF_T
625            ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
626            ", bytecount = %" CURL_FORMAT_CURL_OFF_T,
627            excess_len, data->req.size, data->req.maxdownload,
628            data->req.bytecount);
629      connclose(data->conn, "excess found in a read");
630    }
631  }
632  else if(nwrite < nbytes) {
633    failf(data, "Exceeded the maximum allowed file size "
634          "(%" CURL_FORMAT_CURL_OFF_T ") with %"
635          CURL_FORMAT_CURL_OFF_T " bytes",
636          data->set.max_filesize, data->req.bytecount);
637    return CURLE_FILESIZE_EXCEEDED;
638  }
639
640  return CURLE_OK;
641}
642
643static const struct Curl_cwtype cw_download = {
644  "download",
645  NULL,
646  Curl_cwriter_def_init,
647  cw_download_write,
648  Curl_cwriter_def_close,
649  sizeof(struct Curl_cwriter)
650};
651
652/* RAW client writer in phase CURL_CW_RAW that
653 * enabled tracing of raw data. */
654static CURLcode cw_raw_write(struct Curl_easy *data,
655                             struct Curl_cwriter *writer, int type,
656                             const char *buf, size_t nbytes)
657{
658  if(type & CLIENTWRITE_BODY && data->set.verbose && !data->req.ignorebody) {
659    Curl_debug(data, CURLINFO_DATA_IN, (char *)buf, nbytes);
660  }
661  return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
662}
663
664static const struct Curl_cwtype cw_raw = {
665  "raw",
666  NULL,
667  Curl_cwriter_def_init,
668  cw_raw_write,
669  Curl_cwriter_def_close,
670  sizeof(struct Curl_cwriter)
671};
672
673/* Create an unencoding writer stage using the given handler. */
674CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter,
675                                   struct Curl_easy *data,
676                                   const struct Curl_cwtype *cwt,
677                                   Curl_cwriter_phase phase)
678{
679  struct Curl_cwriter *writer;
680  CURLcode result = CURLE_OUT_OF_MEMORY;
681
682  DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter));
683  writer = (struct Curl_cwriter *) calloc(1, cwt->cwriter_size);
684  if(!writer)
685    goto out;
686
687  writer->cwt = cwt;
688  writer->phase = phase;
689  result = cwt->do_init(data, writer);
690
691out:
692  *pwriter = result? NULL : writer;
693  if(result)
694    free(writer);
695  return result;
696}
697
698void Curl_cwriter_free(struct Curl_easy *data,
699                             struct Curl_cwriter *writer)
700{
701  if(writer) {
702    writer->cwt->do_close(data, writer);
703    free(writer);
704  }
705}
706
707size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase)
708{
709  struct Curl_cwriter *w;
710  size_t n = 0;
711
712  for(w = data->req.writer_stack; w; w = w->next) {
713    if(w->phase == phase)
714      ++n;
715  }
716  return n;
717}
718
719static CURLcode do_init_stack(struct Curl_easy *data)
720{
721  struct Curl_cwriter *writer;
722  CURLcode result;
723
724  DEBUGASSERT(!data->req.writer_stack);
725  result = Curl_cwriter_create(&data->req.writer_stack,
726                               data, &cw_client, CURL_CW_CLIENT);
727  if(result)
728    return result;
729
730  result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL);
731  if(result)
732    return result;
733  result = Curl_cwriter_add(data, writer);
734  if(result) {
735    Curl_cwriter_free(data, writer);
736  }
737
738  result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW);
739  if(result)
740    return result;
741  result = Curl_cwriter_add(data, writer);
742  if(result) {
743    Curl_cwriter_free(data, writer);
744  }
745  return result;
746}
747
748CURLcode Curl_cwriter_add(struct Curl_easy *data,
749                          struct Curl_cwriter *writer)
750{
751  CURLcode result;
752  struct Curl_cwriter **anchor = &data->req.writer_stack;
753
754  if(!*anchor) {
755    result = do_init_stack(data);
756    if(result)
757      return result;
758  }
759
760  /* Insert the writer as first in its phase.
761   * Skip existing writers of lower phases. */
762  while(*anchor && (*anchor)->phase < writer->phase)
763    anchor = &((*anchor)->next);
764  writer->next = *anchor;
765  *anchor = writer;
766  return CURLE_OK;
767}
768
769void Curl_cwriter_remove_by_name(struct Curl_easy *data,
770                                 const char *name)
771{
772  struct Curl_cwriter **anchor = &data->req.writer_stack;
773
774  while(*anchor) {
775    if(!strcmp(name, (*anchor)->cwt->name)) {
776      struct Curl_cwriter *w = (*anchor);
777      *anchor = w->next;
778      Curl_cwriter_free(data, w);
779      continue;
780    }
781    anchor = &((*anchor)->next);
782  }
783}
784
785/*
786 * Internal read-from-socket function. This is meant to deal with plain
787 * sockets, SSL sockets and kerberos sockets.
788 *
789 * Returns a regular CURLcode value.
790 */
791CURLcode Curl_read(struct Curl_easy *data,   /* transfer */
792                   curl_socket_t sockfd,     /* read from this socket */
793                   char *buf,                /* store read data here */
794                   size_t sizerequested,     /* max amount to read */
795                   ssize_t *n)               /* amount bytes read */
796{
797  CURLcode result = CURLE_RECV_ERROR;
798  ssize_t nread = 0;
799  size_t bytesfromsocket = 0;
800  char *buffertofill = NULL;
801  struct connectdata *conn = data->conn;
802
803  /* Set 'num' to 0 or 1, depending on which socket that has been sent here.
804     If it is the second socket, we set num to 1. Otherwise to 0. This lets
805     us use the correct ssl handle. */
806  int num = (sockfd == conn->sock[SECONDARYSOCKET]);
807
808  *n = 0; /* reset amount to zero */
809
810  bytesfromsocket = CURLMIN(sizerequested, (size_t)data->set.buffer_size);
811  buffertofill = buf;
812
813  nread = conn->recv[num](data, num, buffertofill, bytesfromsocket, &result);
814  if(nread < 0)
815    goto out;
816
817  *n += nread;
818  result = CURLE_OK;
819out:
820  return result;
821}
822