aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/curl/src/tool_cb_rea.c
blob: 0fe40143007fb4d76628776087ef3c23b2477725 (plain) (blame)
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
/***************************************************************************
 *                                  _   _ ____  _
 *  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 "tool_setup.h"

#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif

#include "curlx.h"

#include "tool_cfgable.h"
#include "tool_cb_rea.h"
#include "tool_operate.h"
#include "tool_util.h"
#include "tool_msgs.h"
#include "tool_sleep.h"

#include "memdebug.h" /* keep this as LAST include */

/*
** callback for CURLOPT_READFUNCTION
*/

size_t tool_read_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
{
  ssize_t rc = 0;
  struct per_transfer *per = userdata;
  struct OperationConfig *config = per->config;

  if((per->uploadfilesize != -1) &&
     (per->uploadedsofar == per->uploadfilesize)) {
    /* done */
    return 0;
  }

  if(config->timeout_ms) {
    struct timeval now = tvnow();
    long msdelta = tvdiff(now, per->start);

    if(msdelta > config->timeout_ms)
      /* timeout */
      return 0;
#ifndef _WIN32
    /* this logic waits on read activity on a file descriptor that is not a
       socket which makes it not work with select() on Windows */
    else {
      fd_set bits;
      struct timeval timeout;
      long wait = config->timeout_ms - msdelta;

      /* wait this long at the most */
      timeout.tv_sec = wait/1000;
      timeout.tv_usec = (int)((wait%1000)*1000);

      FD_ZERO(&bits);
      FD_SET(per->infd, &bits);
      if(!select(per->infd + 1, &bits, NULL, NULL, &timeout))
        return 0; /* timeout */
    }
#endif
  }

  rc = read(per->infd, buffer, sz*nmemb);
  if(rc < 0) {
    if(errno == EAGAIN) {
      errno = 0;
      config->readbusy = TRUE;
      return CURL_READFUNC_PAUSE;
    }
    /* since size_t is unsigned we cannot return negative values fine */
    rc = 0;
  }
  if((per->uploadfilesize != -1) &&
     (per->uploadedsofar + rc > per->uploadfilesize)) {
    /* do not allow uploading more than originally set out to do */
    curl_off_t delta = per->uploadedsofar + rc - per->uploadfilesize;
    warnf(per->config->global, "File size larger in the end than when "
          "started. Dropping at least %" CURL_FORMAT_CURL_OFF_T " bytes",
          delta);
    rc = (ssize_t)(per->uploadfilesize - per->uploadedsofar);
  }
  config->readbusy = FALSE;

  /* when select() returned zero here, it timed out */
  return (size_t)rc;
}

/*
** callback for CURLOPT_XFERINFOFUNCTION used to unpause busy reads
*/

int tool_readbusy_cb(void *clientp,
                     curl_off_t dltotal, curl_off_t dlnow,
                     curl_off_t ultotal, curl_off_t ulnow)
{
  struct per_transfer *per = clientp;
  struct OperationConfig *config = per->config;

  (void)dltotal;  /* unused */
  (void)dlnow;  /* unused */
  (void)ultotal;  /* unused */
  (void)ulnow;  /* unused */

  if(config->readbusy) {
    /* lame code to keep the rate down because the input might not deliver
       anything, get paused again and come back here immediately */
    static long rate = 500;
    static struct timeval prev;
    static curl_off_t ulprev;

    if(ulprev == ulnow) {
      /* it did not upload anything since last call */
      struct timeval now = tvnow();
      if(prev.tv_sec)
        /* get a rolling average rate */
        /* rate = rate - rate/4 + tvdiff(now, prev)/4; */
        rate -= rate/4 - tvdiff(now, prev)/4;
      prev = now;
    }
    else {
      rate = 50;
      ulprev = ulnow;
    }
    if(rate >= 50) {
      /* keeps the looping down to 20 times per second in the crazy case */
      config->readbusy = FALSE;
      curl_easy_pause(per->curl, CURLPAUSE_CONT);
    }
    else
      /* sleep half a period */
      tool_go_sleep(25);
  }

  return per->noprogress? 0 : CURL_PROGRESSFUNC_CONTINUE;
}