OpenSDN source code
http_curl.cc
Go to the documentation of this file.
1 /***************************************************************************
2  * _ _ ____ _
3  * Copyright (C) 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
4  *
5  * This software is licensed as described in the file COPYING, which
6  * you should have received as part of this distribution. The terms
7  * are also available at http://curl.haxx.se/docs/copyright.html.
8  *
9  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
10  * copies of the Software, and permit persons to whom the Software is
11  * furnished to do so, under the terms of the COPYING file.
12  *
13  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
14  * KIND, either express or implied.
15  *
16  ***************************************************************************/
17 
18 /*
19  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
20  */
21 
22 #include "http_client.h"
23 #include "http_curl.h"
24 #include <curl/curl.h>
25 #include <boost/asio.hpp>
26 #include <boost/bind/bind.hpp>
27 #include <boost/intrusive_ptr.hpp>
28 
29 using namespace boost::placeholders;
30 
31 #define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */
32 
34 
35 /* Update the event timer after curl_multi library calls */
36 static int multi_timer_cb(CURLM *multi, long timeout_ms, HttpClient *client)
37 {
38 
39  if ( timeout_ms > 0 )
40  {
41  client->StartTimer(timeout_ms);
42  }
43  else
44  {
45  client->CancelTimer();
46  timer_cb(client->GlobalInfo());
47  }
48 
49  return 0;
50 }
51 
52 /* Die if we get a bad CURLMcode somewhere */
53 static void mcode_or_die(const char *where, CURLMcode code)
54 {
55  if ( CURLM_OK != code )
56  {
57  const char *s;
58  switch ( code )
59  {
60  case CURLM_CALL_MULTI_PERFORM: s="CURLM_CALL_MULTI_PERFORM"; break;
61  case CURLM_BAD_HANDLE: s="CURLM_BAD_HANDLE"; break;
62  case CURLM_BAD_EASY_HANDLE: s="CURLM_BAD_EASY_HANDLE"; break;
63  case CURLM_OUT_OF_MEMORY: s="CURLM_OUT_OF_MEMORY"; break;
64  case CURLM_INTERNAL_ERROR: s="CURLM_INTERNAL_ERROR"; break;
65  case CURLM_UNKNOWN_OPTION: s="CURLM_UNKNOWN_OPTION"; break;
66  case CURLM_LAST: s="CURLM_LAST"; break;
67  case CURLM_BAD_SOCKET: s="CURLM_BAD_SOCKET"; break;
68  default: s="CURLM_unknown"; break;
69  }
70 
71  fprintf(MSG_OUT, "\nERROR: %s returns %s", where, s);
72  }
73 }
74 
75 /* Check for completed transfers, and remove their easy handles */
77 {
78  char *eff_url;
79  CURLMsg *msg;
80  int msgs_left;
81  ConnInfo *conn;
82  CURL *easy;
83  CURLcode res;
84 
85  while ((msg = curl_multi_info_read(g->multi, &msgs_left)))
86  {
87  if (msg->msg == CURLMSG_DONE)
88  {
89  easy = msg->easy_handle;
90  res = msg->data.result;
91  curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
92  curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
93 
94  if (conn) {
95  boost::system::error_code error(res, curl_error_category);
96  std::string empty_str("");
97  if (conn->connection->HttpClientCb() != NULL)
98  conn->connection->HttpClientCb()(empty_str, error);
99  }
100  }
101  }
102 }
103 
104 typedef boost::intrusive_ptr<HttpClientSession> TcpSessionPtr;
105 
106 static void event_cb_impl(GlobalInfo *g, TcpSessionPtr session, int action,
107  const boost::system::error_code &error,
108  std::size_t bytes_transferred)
109 {
110 
111  if (session->IsClosed()) return;
112 
113  if (g->client->IsErrorHard(error)) return;
114 
115  CURLMcode rc;
116  rc = curl_multi_socket_action(g->multi, session->socket()->native_handle(), action, &g->still_running);
117 
118  mcode_or_die("event_cb: curl_multi_socket_action", rc);
119  check_multi_info(g);
120 
121  if ( g->still_running <= 0 )
122  {
123  g->client->CancelTimer();
124  }
125 }
126 
127 /* Called by asio when there is an action on a socket */
128 static void event_cb(GlobalInfo *g, TcpSessionPtr session, int action,
129  const boost::system::error_code &error, std::size_t bytes_transferred)
130 {
131  std::scoped_lock lock(session->mutex());
132 
133  // Ignore if the connection is already deleted.
134  if (!session->Connection()) return;
135 
136  HttpClient *client = session->Connection()->client();
137  // Ignore if httpclient is null when we get this from meta_data args from VM
138  if (!client) {
139  LOG(ERROR,"HttpClient:unable to call ProcessEvent() as HttpClient is NULL" <<
140  ", session :" << session <<
141  ", session->Connection(): " << session->Connection() <<
142  ", HttpClient :" << session->Connection()->client());
143  return;
144  }
145 
146  client->ProcessEvent(boost::bind(&event_cb_impl, g, session, action, error,
147  bytes_transferred));
148 }
149 
150 /* Called by asio when our timeout expires */
152 {
153  CURLMcode rc;
154  rc = curl_multi_socket_action(g->multi, CURL_SOCKET_TIMEOUT, 0, &g->still_running);
155  mcode_or_die("timer_cb: curl_multi_socket_action", rc);
156 
157  // When timeout happens, call multi_perform to check if we still have
158  // pending handles; if yes, continue running the timer
159  rc = curl_multi_perform(g->multi, &g->still_running);
160  mcode_or_die("timer_cb: curl_multi_perform", rc);
161 
162  check_multi_info(g);
163  return (g->still_running > 0);
164 }
165 
166 /* Clean up any data */
167 static void remsock(SockInfo *sock_info, GlobalInfo *g)
168 {
169  if ( sock_info )
170  {
171  free(sock_info);
172  }
173 }
174 
175 static bool setsock(SockInfo *sock_info, curl_socket_t s, CURL*e, int act, GlobalInfo *g)
176 {
177  if (!sock_info || !sock_info->conn_info || !sock_info->conn_info->connection)
178  {
179  return false;
180  }
181 
182  HttpClientSession *session = sock_info->conn_info->connection->session();
183  if (!session || session->IsClosed())
184  return false;
185 
186  boost::asio::ip::tcp::socket * tcp_socket = session->socket();
187 
188  sock_info->action = act;
189 
190  if ( act == CURL_POLL_IN )
191  {
192  tcp_socket->async_read_some(boost::asio::null_buffers(),
193  boost::bind(&event_cb, g, TcpSessionPtr(session),
194  act, _1, _2));
195  }
196  else if ( act == CURL_POLL_OUT )
197  {
198  tcp_socket->async_write_some(boost::asio::null_buffers(),
199  boost::bind(&event_cb, g, TcpSessionPtr(session),
200  act, _1, _2));
201  }
202  else if ( act == CURL_POLL_INOUT )
203  {
204  tcp_socket->async_read_some(boost::asio::null_buffers(),
205  boost::bind(&event_cb, g, TcpSessionPtr(session),
206  act, _1, _2));
207  tcp_socket->async_write_some(boost::asio::null_buffers(),
208  boost::bind(&event_cb, g, TcpSessionPtr(session),
209  act, _1, _2));
210  }
211  return true;
212 }
213 
214 
215 static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
216 {
217  // store socket details in SockInfo
218  SockInfo *sock_info = (SockInfo *)calloc(sizeof(SockInfo), 1);
219  curl_easy_getinfo(easy, CURLINFO_PRIVATE, &sock_info->conn_info);
220 
221  if (!setsock(sock_info, s, easy, action, g)) {
222  free(sock_info);
223  return;
224  }
225 
226  curl_multi_assign(g->multi, s, sock_info);
227 }
228 
229 /* CURLMOPT_SOCKETFUNCTION */
230 static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
231 {
232  GlobalInfo *g = (GlobalInfo*) cbp;
233  SockInfo *sock_info = (SockInfo *)sockp;
234 
235  if ( what == CURL_POLL_REMOVE )
236  {
237  remsock(sock_info, g);
238  }
239  else
240  {
241  if ( !sockp )
242  {
243  addsock(s, e, what, g);
244  }
245  else
246  {
247  setsock(sock_info, s, e, what, g);
248  }
249  }
250  return 0;
251 }
252 
253 
254 /* CURLOPT_WRITEFUNCTION */
255 static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
256 {
257  size_t written = size * nmemb;
258 
259  HttpConnection *conn = static_cast<HttpConnection *>(data);
260  conn->AssignData((char *)ptr, written);
261  return written;
262 }
263 
264 /* CURLOPT_HEADERFUNCTION */
265 static size_t header_cb(void *ptr, size_t size, size_t nmemb, void *data)
266 {
267  size_t written = size * nmemb;
268 
269  HttpConnection *conn = static_cast<HttpConnection *>(data);
270  conn->AssignHeader((char *)ptr, written);
271  return written;
272 }
273 
274 static size_t read_cb(void *ptr, size_t size, size_t nmemb, void *data)
275 {
276  HttpConnection *conn = static_cast<HttpConnection *>(data);
277  ConnInfo *curl_handle = conn->curl_handle();
278  char *str = curl_handle->post;
279 
280  size_t maxb = size*nmemb;
281  size_t offset = conn->GetOffset();
282  size_t datasize = 0;
283  if (curl_handle->post_len > offset)
284  datasize = curl_handle->post_len - offset;
285 
286  if (maxb >= datasize) {
287  memcpy(ptr, str + offset, datasize);
288  conn->UpdateOffset(datasize);
289  return datasize;
290  } else {
291  memcpy(ptr, str + offset, maxb);
292  conn->UpdateOffset(maxb);
293  return maxb;
294  }
295 
296  return 0;
297 }
298 
299 
300 /* CURLOPT_PROGRESSFUNCTION */
301 static int prog_cb (void *p, double dltotal, double dlnow, double ult,
302  double uln)
303 {
304  (void)ult;
305  (void)uln;
306 
307  return 0;
308 }
309 
310 /* CURLOPT_OPENSOCKETFUNCTION */
311 static curl_socket_t open_socket(void *data,
312  curlsocktype purpose,
313  struct curl_sockaddr *address)
314 {
315  HttpConnection *conn = static_cast<HttpConnection *>(data);
316 
317  curl_socket_t sockfd = CURL_SOCKET_BAD;
318 
319  /* restrict to ipv4 */
320  if (purpose == CURLSOCKTYPE_IPCXN && address->family == AF_INET)
321  {
322  HttpClientSession *session = conn->CreateSession();
323  if (session) {
324  sockfd = session->socket()->native_handle();
325  conn->set_session(session);
326  }
327  }
328 
329  return sockfd;
330 }
331 
332 /* CURLOPT_CLOSESOCKETFUNCTION */
333 static int close_socket(void *clientp, curl_socket_t item)
334 {
335  HttpConnection *conn = static_cast<HttpConnection *>(clientp);
336  conn->delete_session();
337  return 0;
338 }
339 
340 static int send_perform(ConnInfo *conn, GlobalInfo *g) {
341  // add the handle
342  CURLMcode m_rc = curl_multi_add_handle(g->multi, conn->easy);
343  if (m_rc != CURLM_OK)
344  return m_rc;
345 
346  // start sending data rightaway; use timer to re-invoke multi_perform
347  int counter = 0;
348  CURLMcode rc = curl_multi_perform(g->multi, &counter);
349  if (rc == CURLM_OK && counter <= 0) {
350  // send done; invoke callback to indicate this
351  if (conn->connection && conn->connection->session()) {
352  const boost::system::error_code ec;
353  event_cb(g, TcpSessionPtr(conn->connection->session()), 0, ec, 0);
354  }
355  } else {
356  // start timer and check for send completion on timeout
358  }
359 
360  return rc;
361 }
362 
363 void del_conn(HttpConnection *connection, GlobalInfo *g) {
364 
365  if (connection->session()) {
366  std::scoped_lock lock(connection->session()->mutex());
367  connection->session()->SetConnection(NULL);
368  }
369 
370  struct _ConnInfo *curl_handle = connection->curl_handle();
371  if (curl_handle) {
373  del_curl_handle(curl_handle, g);
374  }
375 }
376 
377 void del_curl_handle(ConnInfo *curl_handle, GlobalInfo *g) {
378  if (curl_handle) {
379  curl_easy_setopt(curl_handle->easy, CURLOPT_PRIVATE, NULL);
380  curl_multi_remove_handle(g->multi, curl_handle->easy);
381  curl_slist_free_all(curl_handle->headers);
382  free(curl_handle->post);
383  free(curl_handle->url);
384  curl_easy_cleanup(curl_handle->easy);
385  free(curl_handle);
386  }
387 }
388 
389 /* Create a new easy handle, and add it to the global curl_multi */
391  bool header, bool short_timeout, bool reuse)
392 {
393  ConnInfo *conn = (ConnInfo *)calloc(1, sizeof(ConnInfo));
394  memset(conn, 0, sizeof(ConnInfo));
395  conn->error[CURL_ERROR_SIZE]='\0';
396 
397  conn->easy = curl_easy_init();
398 
399  if ( !conn->easy ) {
400  free(conn);
401  return NULL;
402  }
403  conn->global = g;
404  curl_easy_setopt(conn->easy, CURLOPT_FOLLOWLOCATION, 1L);
405  curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb);
406  curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, connection);
407  curl_easy_setopt(conn->easy, CURLOPT_HEADERFUNCTION, header_cb);
408  curl_easy_setopt(conn->easy, CURLOPT_HEADERDATA, connection);
409  curl_easy_setopt(conn->easy, CURLOPT_READFUNCTION, read_cb);
410  curl_easy_setopt(conn->easy, CURLOPT_READDATA, connection);
411  curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
412  curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
413  curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 1L);
414  curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
415  curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn);
416  curl_easy_setopt(conn->easy, CURLOPT_CONNECTTIMEOUT, 4L); // in secs
417  if (short_timeout) {
418  /* set the timeout limits to abort the connection */
419  curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_TIME, 3L);
420  curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_LIMIT, 10L);
421  } else {
422  /* set longer timeout limits */
423  curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_TIME, 30L);
424  curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_LIMIT, 10L);
425  }
426  curl_easy_setopt(conn->easy, CURLOPT_FORBID_REUSE, 1L);
427 
428  /* call this function to get a socket */
429  curl_easy_setopt(conn->easy, CURLOPT_OPENSOCKETFUNCTION, open_socket);
430  curl_easy_setopt(conn->easy, CURLOPT_OPENSOCKETDATA, connection);
431 
432  /* call this function to close a socket */
433  curl_easy_setopt(conn->easy, CURLOPT_CLOSESOCKETFUNCTION, close_socket);
434  curl_easy_setopt(conn->easy, CURLOPT_CLOSESOCKETDATA, connection);
435 
436  return conn;
437 }
438 
439 void set_url(ConnInfo *conn, const char *url) {
440  conn->url = strdup(url);
441  curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url);
442 }
443 
444 void set_header_options(ConnInfo *conn, const char *options) {
445  conn->headers = curl_slist_append(conn->headers, options);
446  curl_easy_setopt(conn->easy, CURLOPT_HTTPHEADER, conn->headers);
447 }
448 
449 void set_ssl_options(ConnInfo *conn, const char *client_cert,
450  const char *client_cert_type, const char *client_key,
451  const char *ca_cert) {
452  curl_easy_setopt(conn->easy, CURLOPT_SSLCERT, client_cert);
453  curl_easy_setopt(conn->easy, CURLOPT_SSLCERTTYPE, client_cert_type);
454  curl_easy_setopt(conn->easy, CURLOPT_SSLKEY, client_key);
455  curl_easy_setopt(conn->easy, CURLOPT_CAINFO, ca_cert);
456  if (ca_cert[0] == '\0') {
457  // Disable certificate validation if CA certificate path doesnt exist
458  curl_easy_setopt(conn->easy, CURLOPT_SSL_VERIFYPEER, 0L);
459  curl_easy_setopt(conn->easy, CURLOPT_SSL_VERIFYHOST, 0L);
460  }
461 }
462 
463 void set_post_string(ConnInfo *conn, const char *post, uint32_t len) {
464  conn->post = (char *) malloc(len);
465  memcpy(conn->post, post, len);
466  conn->post_len = len;
467  curl_easy_setopt(conn->easy, CURLOPT_POST, 1);
468  curl_easy_setopt(conn->easy, CURLOPT_POSTFIELDS, conn->post);
469  curl_easy_setopt(conn->easy, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)len);
470 }
471 
472 void set_put_string(ConnInfo *conn, const char *put, uint32_t len) {
473  conn->post = (char *) malloc(len);
474  memcpy(conn->post, put, len);
475  conn->post_len = len;
476  curl_easy_setopt(conn->easy, CURLOPT_UPLOAD, 1);
477  curl_easy_setopt(conn->easy, CURLOPT_PUT, 1);
478  curl_easy_setopt(conn->easy, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)len);
479 }
480 
481 int http_get(ConnInfo *conn, GlobalInfo *g) {
482  CURLMcode rc = curl_multi_add_handle(g->multi, conn->easy);
483  return (int)rc;
484 }
485 
486 int http_head(ConnInfo *conn, GlobalInfo *g) {
487  curl_easy_setopt(conn->easy, CURLOPT_CUSTOMREQUEST, "HEAD");
488  CURLMcode rc = curl_multi_add_handle(g->multi, conn->easy);
489  return (int)rc;
490 }
491 
492 int http_put(ConnInfo *conn, GlobalInfo *g) {
493  return send_perform(conn, g);
494 }
495 
496 int http_post(ConnInfo *conn, GlobalInfo *g) {
497  return send_perform(conn, g);
498 }
499 
501  curl_easy_setopt(conn->easy, CURLOPT_CUSTOMREQUEST, "DELETE");
502  CURLMcode rc = curl_multi_add_handle(g->multi, conn->easy);
503  return (int)rc;
504 }
505 
506 int curl_init(HttpClient *client)
507 {
508  struct _GlobalInfo *g = client->GlobalInfo();
509 
510  memset(g, 0, sizeof(GlobalInfo));
511  g->multi = curl_multi_init();
512  g->client = client;
513 
514  curl_multi_setopt(g->multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
515  curl_multi_setopt(g->multi, CURLMOPT_SOCKETDATA, g);
516  curl_multi_setopt(g->multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
517  curl_multi_setopt(g->multi, CURLMOPT_TIMERDATA, client);
518 
519  return 0;
520 }
void SetConnection(HttpConnection *conn)
Definition: http_client.h:49
std::mutex & mutex()
Definition: http_client.h:51
struct _GlobalInfo * GlobalInfo()
Definition: http_client.h:204
void ProcessEvent(EnqueuedCb cb)
Definition: http_client.cc:479
void StartTimer(long)
Definition: http_client.cc:492
static const uint32_t kDefaultTimeout
Definition: http_client.h:185
void CancelTimer()
Definition: http_client.cc:497
bool IsErrorHard(const boost::system::error_code &ec)
Definition: http_client.cc:501
HttpClientSession * CreateSession()
Definition: http_client.cc:108
size_t GetOffset()
Definition: http_client.cc:369
HttpCb HttpClientCb()
Definition: http_client.h:115
HttpClientSession * session()
Definition: http_client.h:101
void AssignData(const char *ptr, size_t size)
Definition: http_client.cc:306
void delete_session()
Definition: http_client.cc:117
struct _ConnInfo * curl_handle()
Definition: http_client.h:98
void set_session(HttpClientSession *session)
Definition: http_client.cc:129
void UpdateOffset(size_t bytes)
Definition: http_client.cc:365
void AssignHeader(const char *ptr, size_t size)
Definition: http_client.cc:316
void set_curl_handle(struct _ConnInfo *handle)
Definition: http_client.h:107
bool IsClosed() const
Definition: tcp_session.h:121
virtual Socket * socket() const
Definition: tcp_session.h:82
#define MSG_OUT
Definition: http_curl.cc:31
int http_post(ConnInfo *conn, GlobalInfo *g)
Definition: http_curl.cc:496
void set_header_options(ConnInfo *conn, const char *options)
Definition: http_curl.cc:444
static void remsock(SockInfo *sock_info, GlobalInfo *g)
Definition: http_curl.cc:167
void set_post_string(ConnInfo *conn, const char *post, uint32_t len)
Definition: http_curl.cc:463
void del_conn(HttpConnection *connection, GlobalInfo *g)
Definition: http_curl.cc:363
int http_delete(ConnInfo *conn, GlobalInfo *g)
Definition: http_curl.cc:500
boost::intrusive_ptr< HttpClientSession > TcpSessionPtr
Definition: http_curl.cc:104
int http_head(ConnInfo *conn, GlobalInfo *g)
Definition: http_curl.cc:486
static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
Definition: http_curl.cc:255
void set_url(ConnInfo *conn, const char *url)
Definition: http_curl.cc:439
void set_ssl_options(ConnInfo *conn, const char *client_cert, const char *client_cert_type, const char *client_key, const char *ca_cert)
Definition: http_curl.cc:449
void set_put_string(ConnInfo *conn, const char *put, uint32_t len)
Definition: http_curl.cc:472
static curl_socket_t open_socket(void *data, curlsocktype purpose, struct curl_sockaddr *address)
Definition: http_curl.cc:311
static void event_cb(GlobalInfo *g, TcpSessionPtr session, int action, const boost::system::error_code &error, std::size_t bytes_transferred)
Definition: http_curl.cc:128
void del_curl_handle(ConnInfo *curl_handle, GlobalInfo *g)
Definition: http_curl.cc:377
static void event_cb_impl(GlobalInfo *g, TcpSessionPtr session, int action, const boost::system::error_code &error, std::size_t bytes_transferred)
Definition: http_curl.cc:106
int http_put(ConnInfo *conn, GlobalInfo *g)
Definition: http_curl.cc:492
static bool setsock(SockInfo *sock_info, curl_socket_t s, CURL *e, int act, GlobalInfo *g)
Definition: http_curl.cc:175
ConnInfo * new_conn(HttpConnection *connection, GlobalInfo *g, bool header, bool short_timeout, bool reuse)
Definition: http_curl.cc:390
static void check_multi_info(GlobalInfo *g)
Definition: http_curl.cc:76
const CurlErrorCategory curl_error_category
Definition: http_curl.cc:33
static int multi_timer_cb(CURLM *multi, long timeout_ms, HttpClient *client)
Definition: http_curl.cc:36
bool timer_cb(GlobalInfo *g)
Definition: http_curl.cc:151
static size_t header_cb(void *ptr, size_t size, size_t nmemb, void *data)
Definition: http_curl.cc:265
int http_get(ConnInfo *conn, GlobalInfo *g)
Definition: http_curl.cc:481
static size_t read_cb(void *ptr, size_t size, size_t nmemb, void *data)
Definition: http_curl.cc:274
static void mcode_or_die(const char *where, CURLMcode code)
Definition: http_curl.cc:53
static int prog_cb(void *p, double dltotal, double dlnow, double ult, double uln)
Definition: http_curl.cc:301
static int send_perform(ConnInfo *conn, GlobalInfo *g)
Definition: http_curl.cc:340
static int close_socket(void *clientp, curl_socket_t item)
Definition: http_curl.cc:333
static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
Definition: http_curl.cc:230
int curl_init(HttpClient *client)
Definition: http_curl.cc:506
static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
Definition: http_curl.cc:215
#define LOG(_Level, _Msg)
Definition: logging.h:34
CURL * easy
Definition: http_curl.h:32
char * url
Definition: http_curl.h:33
GlobalInfo * global
Definition: http_curl.h:37
uint32_t post_len
Definition: http_curl.h:35
char * post
Definition: http_curl.h:34
char error[CURL_ERROR_SIZE+1]
Definition: http_curl.h:38
struct curl_slist * headers
Definition: http_curl.h:36
HttpConnection * connection
Definition: http_curl.h:39
CURLM * multi
Definition: http_curl.h:24
int still_running
Definition: http_curl.h:25
HttpClient * client
Definition: http_curl.h:26
ConnInfo * conn_info
Definition: http_curl.h:45
int action
Definition: http_curl.h:44