OpenSDN source code
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
http_client.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3  */
4 
5 #include "http_client.h"
6 #include <boost/bind.hpp>
7 #include <boost/algorithm/string.hpp>
8 #include "base/address_util.h"
10 #include "io/event_manager.h"
11 #include "base/logging.h"
12 #include "http_curl.h"
13 
14 using namespace std;
15 using tbb::mutex;
16 
18  : TcpSession(client, socket) , delete_called_(0) {
19  set_observer(boost::bind(&HttpClientSession::OnEvent, this, _1, _2));
20 }
21 
23  return;
24 }
25 
27  if (connection_) {
28  connection_->client()->
29  ProcessEvent(boost::bind(&HttpClientSession::OnEventInternal,
30  this, TcpSessionPtr(session), event));
31  }
32 }
33 
35  if (event_cb_ && !event_cb_.empty()) {
36  event_cb_(static_cast<HttpClientSession *>(session.get()), event);
37  }
38 
39  if (event == CLOSE) {
40  goto error;
41  }
42  if (event == ACCEPT) {
43  goto error;
44  }
45  if (event == CONNECT_COMPLETE) {
46  goto error;
47  }
48  if (event == CONNECT_FAILED) {
49  goto error;
50  }
51  if (event == EVENT_NONE) {
52  goto error;
53  }
54  return;
55 
56 error:
57  // Call callback function with error;
58  return;
59 }
60 
62  event_cb_ = cb;
63 }
64 
65 namespace {
66  boost::asio::ip::address String2Addr(const std::string& host) {
67  boost::system::error_code ec;
68  return AddressFromString(host, &ec);
69  }
70 }
71 
72 HttpConnection::HttpConnection(boost::asio::ip::tcp::endpoint ep,
73  size_t id, HttpClient *client) :
74  host_(ep.address().to_string()), endpoint_(ep),
75  id_(id), cb_(NULL), offset_(0), curl_handle_(NULL),
76  session_(NULL), client_(client), use_ssl_(false), client_cert_(""),
77  client_cert_type_("PEM"), client_key_(""), ca_cert_(""), state_(STATUS) {
78 }
79 
80 HttpConnection::HttpConnection(const std::string& host, int port,
81  size_t id, HttpClient *client) :
82  host_(host), endpoint_(String2Addr(host), port),
83  id_(id), cb_(NULL), offset_(0), curl_handle_(NULL),
84  session_(NULL), client_(client), use_ssl_(false), client_cert_(""),
85  client_cert_type_("PEM"), client_key_(""), ca_cert_(""), state_(STATUS) {
86 }
87 
90 }
91 
92 std::string HttpConnection::make_url(std::string &path) {
93  std::ostringstream ret;
94 
95  if (use_ssl_) {
96  ret << "https://" << host_;
97  } else {
98  ret << "http://" << host_;
99  }
100  if (endpoint_.port() != 0) {
101  ret << ":" << endpoint_.port();
102  }
103  ret << "/" << path;
104 
105  return ret.str();
106 }
107 
110  static_cast<HttpClientSession *>(client_->CreateSession());
111  if (session) {
112  session->SetConnection(this);
113  }
114  return session;
115 }
116 
119  if (session_) {
120  {
121  tbb::mutex::scoped_lock lock(session_->mutex());
122  session_->SetConnection(NULL);
123  session_ = NULL;
124  }
125  client_->DeleteSession(session);
126  }
127 }
128 
130  session_ = session;
131  if (session && event_cb_ && !event_cb_.empty())
132  session->RegisterEventCb(event_cb_);
133 }
134 
135 int HttpConnection::HttpGet(const std::string &path, HttpCb cb) {
136  std::vector<std::string> hdr_options;
137  return HttpGet(path, false, true, false, hdr_options, cb);
138 }
139 
140 int HttpConnection::HttpGet(const std::string &path, bool header,
141  bool short_timeout, bool reuse,
142  std::vector<std::string> &hdr_options,
143  HttpCb cb) {
144  const std::string body;
145 
147  this, body, path, bool2bf(header, short_timeout,
148  reuse), hdr_options, cb, HTTP_GET));
149  return 0;
150 }
151 
152 int HttpConnection::HttpHead(const std::string &path, bool header, bool short_timeout,
153  bool reuse, std::vector<std::string> &hdr_options,
154  HttpCb cb) {
155  const std::string body;
157  this, body, path, bool2bf(header, short_timeout,
158  reuse), hdr_options, cb, HTTP_HEAD));
159  return 0;
160 }
161 
162 int HttpConnection::HttpPut(const std::string &put_string,
163  const std::string &path, HttpCb cb) {
164  std::vector<std::string> hdr_options;
165  return HttpPut(put_string, path, false, true, false, hdr_options, cb);
166 }
167 
168 int HttpConnection::HttpPut(const std::string &put_string,
169  const std::string &path, bool header,
170  bool short_timeout,
171  bool reuse, std::vector<std::string> &hdr_options,
172  HttpCb cb) {
174  this, put_string, path,
175  bool2bf(header, short_timeout, reuse),
176  hdr_options, cb, HTTP_PUT));
177  return 0;
178 }
179 
180 int HttpConnection::HttpPost(const std::string &post_string,
181  const std::string &path, HttpCb cb) {
182  std::vector<std::string> hdr_options;
183  return HttpPost(post_string, path, false, true, false, hdr_options, cb);
184 }
185 
186 int HttpConnection::HttpPost(const std::string &post_string,
187  const std::string &path, bool header,
188  bool short_timeout, bool reuse,
189  std::vector<std::string> &hdr_options, HttpCb cb) {
191  this, post_string, path, bool2bf(header,
192  short_timeout, reuse), hdr_options, cb, HTTP_POST));
193  return 0;
194 }
195 
196 int HttpConnection::HttpDelete(const std::string &path, HttpCb cb) {
197  std::vector<std::string> hdr_options;
198  return HttpDelete(path, false, true, false, hdr_options, cb);
199 }
200 
201 int HttpConnection::HttpDelete(const std::string &path, bool header, bool short_timeout,
202  bool reuse, std::vector<std::string> &hdr_options,
203  HttpCb cb) {
204  const std::string body;
206  this, body, path, bool2bf(header, short_timeout,
207  reuse), hdr_options, cb, HTTP_DELETE));
208  return 0;
209 }
210 
212  cb_ = NULL;
213 }
214 
215 void HttpConnection::HttpProcessInternal(const std::string body,
216  std::string path,
217  unsigned short hdr_shortTimeout_reuse,
218  std::vector<std::string> hdr_options,
219  HttpCb cb, http_method method) {
220  bool short_timeout, reuse;
221  bf2bool(hdr_shortTimeout_reuse, sent_hdr_, short_timeout, reuse);
222  state_ = STATUS;
223  status_ = 0;
224  version_.clear();
225  reason_.clear();
226  if (client()->AddConnection(this) == false) {
227  // connection already exists
228  if (!reuse)
229  return;
230  }
231 
232  struct _GlobalInfo *gi = client()->GlobalInfo();
233  struct _ConnInfo *curl_handle = new_conn(this, gi, sent_hdr_, short_timeout,
234  reuse);
235  if (!curl_handle) {
236  LOG(DEBUG, "Http : unable to create new connection");
237  return;
238  }
239 
240  if (curl_handle_) {
241  // delete existing curl_handle
243  }
244  curl_handle->connection = this;
245  set_curl_handle(curl_handle);
246 
247  cb_ = cb;
248 
249  std::string url = make_url(path);
250  set_url(curl_handle_, url.c_str());
251 
252  // set special curl options requested in http connection
253  std::map<CURLoption, int> *curl_options = this->curl_options();
254  std::map<CURLoption, int>::iterator iter = curl_options->begin();
255  while (iter != curl_options->end()) {
256  set_curl_option(curl_handle_->easy, iter->first, iter->second);
257  iter++;
258  }
259 
260  // set SSL curl options
261  if (use_ssl_) {
263  client_cert_type_.c_str(), client_key_.c_str(), ca_cert_.c_str());
264  }
265 
266  // Add header options to the get request
267  for (uint32_t i = 0; i < hdr_options.size(); ++i)
268  set_header_options(curl_handle_, hdr_options[i].c_str());
269 
270  switch (method) {
271  case HTTP_GET:
272  http_get(curl_handle_, gi);
273  break;
274 
275  case HTTP_HEAD:
276  http_head(curl_handle_, gi);
277  break;
278 
279  case HTTP_POST:
280  if (!hdr_options.size()) {
281  // if no header options are set, set the content type
282  set_header_options(curl_handle_, "Content-Type: application/xml");
283  }
284  set_post_string(curl_handle_, body.c_str(), body.size());
285  http_post(curl_handle_, gi);
286  break;
287 
288  case HTTP_PUT:
289  if (!hdr_options.size()) {
290  // if no header options are set, set the content type
291  set_header_options(curl_handle_, "Content-Type: application/xml");
292  }
293  set_put_string(curl_handle_, body.c_str(), body.size());
294  http_put(curl_handle_, gi);
295  break;
296 
297  case HTTP_DELETE:
299  break;
300 
301  default:
302  assert(0);
303  }
304 }
305 
306 void HttpConnection::AssignData(const char *ptr, size_t size) {
307 
308  buf_.assign(ptr, size);
309 
310  // callback to client
311  boost::system::error_code error;
312  if (cb_ != NULL)
313  cb_(buf_, error);
314 }
315 
316 void HttpConnection::AssignHeader(const char *ptr, size_t size) {
317 
318  buf_.assign(ptr, size);
319 
320  switch (state_) {
321  case STATUS: {
322  status_ = 0;
323  int i = buf_.find(' ', 0);
324  version_ = boost::algorithm::trim_copy(buf_.substr(0, i));
325  int j = buf_.find(' ', i+1);
326  status_ = atoi(buf_.substr(i+1, j).c_str());
327  reason_ = boost::algorithm::trim_copy(buf_.substr(j+1));
328 #ifdef __DEBUG__
329  //for (std::string::iterator ii=buf_.begin()+i+1;
330  // ii != buf_.begin()+j; ii++) {
331  // status_ = (status_ << 3) + (status_ << 1) + (*ii - '0');
332  //}
333  std::cout << "Status Line: " << std::dec << status_ << ":"
334  << reason_ << "(" << version_ << ")" << std::endl;
335 #endif
336  state_ = HEADER;
337  break;
338  }
339  case HEADER:
340  if (buf_ != "\r\n") {
341  std::istringstream iss(buf_);
342  std::string tok;
343  while (std::getline(iss, tok, '\r') && tok != "\n") {
344  unsigned int i = tok.find(':', 0);
345  if (i != std::string::npos) {
346  headers_.insert(std::make_pair(
347  boost::algorithm::trim_copy(tok.substr(0, i)),
348  boost::algorithm::trim_copy(tok.substr(i + 1))));
349  }
350  }
351  }
352  break;
353  }
354  boost::system::error_code error;
355  // callback to client *backward compatibility*
356  if (sent_hdr_ && cb_ != NULL) {
357  cb_(buf_, error);
358  }
359 }
360 
361 const std::string &HttpConnection::GetData() {
362  return buf_;
363 }
364 
365 void HttpConnection::UpdateOffset(size_t bytes) {
366  offset_ += bytes;
367 }
368 
370  return offset_;
371 }
372 
373 HttpClient::HttpClient(EventManager *evm, std::string task_name) :
374  TcpServer(evm),
375  curl_timer_(TimerManager::CreateTimer(*evm->io_service(), task_name,
376  TaskScheduler::GetInstance()->GetTaskId(task_name), 0)),
377  id_(0), work_queue_(TaskScheduler::GetInstance()->GetTaskId(task_name), 0,
378  boost::bind(&HttpClient::DequeueEvent, this, _1)) {
379  gi_ = (struct _GlobalInfo *)malloc(sizeof(struct _GlobalInfo));
380  memset(gi_, 0, sizeof(struct _GlobalInfo));
381 }
382 
384 
385  for (HttpConnectionMap::iterator iter = map_.begin(), next = iter;
386  iter != map_.end(); iter = next) {
387  next++;
388  RemoveConnectionInternal(iter->second);
389  }
390 
391  curl_multi_cleanup(gi_->multi);
393  SessionShutdown();
394 
395  /* Schedule a shutdown of WorkQueue */
397  assert(!map_.size());
398 }
399 
400 /*
401  * Ensure task that calls this function is not mutually exclusive
402  * to the task that runs HttpClient::ShutdownInternal()
403  *
404  * Tight loop to check the Callback has been scheduled
405  */
408  this));
409 
410  uint32_t count = 0;
411  while (!(work_queue_.deleted() == true) && count++ < 10000) {
412  usleep(1000);
413  }
414 
415  assert(work_queue_.deleted() == true);
416 }
417 
419  free(gi_);
420 }
421 
423  curl_init(this);
424 }
425 
428 }
429 
430 boost::asio::io_context *HttpClient::io_service() {
431  return this->event_manager()->io_service();
432 };
433 
435  HttpClientSession *session = new HttpClientSession(this, socket);
436  return session;
437 }
438 
441  Socket *socket = session->socket();
442  boost::system::error_code err;
443  socket->open(boost::asio::ip::tcp::v4(), err);
444 
445  if (err) {
446  LOG(ERROR, "http socket open failed: " << err);
447  return NULL;
448  }
449 
450  err = session->SetSocketOptions();
451  return session;
452 }
453 
454 HttpConnection *HttpClient::CreateConnection(boost::asio::ip::tcp::endpoint ep) {
455  HttpConnection *conn = new HttpConnection(ep, ++id_, this);
456  return conn;
457 }
458 
459 HttpConnection *HttpClient::CreateConnection(const std::string& host, int port) {
460  HttpConnection *conn = new HttpConnection(host, port, ++id_, this);
461  return conn;
462 }
463 
465  Key key = std::make_pair(conn->endpoint(), conn->id());
466  if (map_.find(key) == map_.end()) {
467  map_.insert(key, conn);
468  return true;
469  }
470  return false;
471 }
472 
474  connection->ClearCallback();
476  this, connection));
477 }
478 
480  if(!cb.empty()) {
481  work_queue_.Enqueue(cb);
482  }
483 }
484 
485 void HttpClient::TimerErrorHandler(std::string name, std::string error) {
486 }
487 
489  return timer_cb(gi_);
490 }
491 
492 void HttpClient::StartTimer(long timeout_ms) {
493  CancelTimer();
494  curl_timer_->Start(timeout_ms, boost::bind(&HttpClient::TimerCb, this));
495 }
496 
498  curl_timer_->Cancel();
499 }
500 
501 bool HttpClient::IsErrorHard(const boost::system::error_code &ec) {
503 }
504 
506  boost::asio::ip::tcp::endpoint endpoint = connection->endpoint();
507  size_t id = connection->id();
508  del_conn(connection, gi_);
509  map_.erase(std::make_pair(endpoint, id));
510  return;
511 }
512 
514  cb();
515  return true;
516 }
void UpdateOffset(size_t bytes)
Definition: http_client.cc:365
void ShutdownInternal()
Definition: http_client.cc:383
void SessionShutdown()
Definition: http_client.cc:426
tbb::mutex & mutex()
Definition: http_client.h:49
void Init()
Definition: http_client.cc:422
boost::asio::ip::tcp::endpoint endpoint_
Definition: http_client.h:151
enum HttpConnection::HTTPHeaderDataState state_
boost::asio::const_buffer Buffer
Definition: tcp_session.h:64
std::string ca_cert_
Definition: http_client.h:171
std::string version_
Definition: http_client.h:163
void ClearCallback()
Definition: http_client.cc:211
virtual void DeleteSession(TcpSession *session)
Definition: tcp_server.cc:197
The TaskScheduler keeps track of what tasks are currently schedulable. When a task is enqueued it is ...
Definition: task.h:178
boost::asio::ip::tcp::socket Socket
Definition: tcp_server.h:31
const std::string & GetData()
Definition: http_client.cc:361
CURLM * multi
Definition: http_curl.h:24
int HttpGet(const std::string &path, HttpCb)
Definition: http_client.cc:135
size_t id_
Definition: http_client.h:227
#define set_curl_option(handle, option, parameter)
Definition: http_curl.h:16
void StartTimer(long)
Definition: http_client.cc:492
int http_head(ConnInfo *conn, GlobalInfo *g)
Definition: http_curl.cc:487
HttpClient * client_
Definition: http_client.h:159
HttpConnection * CreateConnection(boost::asio::ip::tcp::endpoint)
Definition: http_client.cc:454
struct _GlobalInfo * gi_
Definition: http_client.h:224
boost::function< void()> EnqueuedCb
Definition: http_client.h:35
struct _GlobalInfo * GlobalInfo()
Definition: http_client.h:202
HttpClient(EventManager *evm, std::string task_name=std::string("http client"))
Definition: http_client.cc:373
void ProcessEvent(EnqueuedCb cb)
Definition: http_client.cc:479
virtual TcpSession * CreateSession()
Definition: tcp_server.cc:188
virtual ~HttpClient()
Definition: http_client.cc:418
boost::asio::io_context * io_service()
Definition: event_manager.h:42
std::pair< endpoint, size_t > Key
Definition: http_client.h:220
HttpConnection(boost::asio::ip::tcp::endpoint, size_t id, HttpClient *client)
Definition: http_client.cc:72
CURL * easy
Definition: http_curl.h:32
int http_delete(ConnInfo *conn, GlobalInfo *g)
Definition: http_curl.cc:501
EventManager * event_manager()
Definition: tcp_server.h:76
HttpClientSession * session()
Definition: http_client.h:99
void RemoveConnectionInternal(HttpConnection *)
Definition: http_client.cc:505
struct _ConnInfo * curl_handle_
Definition: http_client.h:156
std::string make_url(std::string &path)
Definition: http_client.cc:92
char * url
Definition: http_curl.h:33
int HttpPost(const std::string &post_string, const std::string &path, HttpCb)
Definition: http_client.cc:180
HttpClientSession * CreateSession()
Definition: http_client.cc:108
void ScheduleShutdown(bool delete_entries=true)
Definition: queue_task.h:160
bool deleted() const
Definition: queue_task.h:372
virtual TcpSession * AllocSession(Socket *socket)
Definition: http_client.cc:434
boost::function< void(std::string &, boost::system::error_code &)> HttpCb
Definition: http_client.h:70
virtual TcpSession * CreateSession()
Definition: http_client.cc:439
bool DequeueEvent(EnqueuedCb)
Definition: http_client.cc:513
std::string reason_
Definition: http_client.h:164
boost::asio::ip::tcp::endpoint endpoint()
Definition: http_client.h:101
const std::string host_
Definition: http_client.h:150
boost::asio::ip::tcp::socket Socket
Definition: tcp_session.h:60
SessionEventCb event_cb_
Definition: http_client.h:57
void set_observer(EventObserver observer)
Definition: tcp_session.cc:218
void Shutdown()
Definition: tcp_server.cc:143
bool TimerCb()
Definition: http_client.cc:488
int HttpDelete(const std::string &path, HttpCb)
Definition: http_client.cc:196
std::map< std::string, std::string > headers_
Definition: http_client.h:165
void CancelTimer()
Definition: http_client.cc:497
int curl_init(HttpClient *client)
Definition: http_curl.cc:507
unsigned short bool2bf(bool header, bool short_timeout, bool reuse)
Definition: http_client.h:133
void RegisterEventCb(SessionEventCb cb)
Definition: http_client.cc:61
HttpConnection * connection
Definition: http_curl.h:39
HttpConnection * connection_
Definition: http_client.h:54
void set_put_string(ConnInfo *conn, const char *put, uint32_t len)
Definition: http_curl.cc:473
std::map< CURLoption, int > * curl_options()
Definition: http_client.h:97
std::string client_cert_type_
Definition: http_client.h:169
int http_put(ConnInfo *conn, GlobalInfo *g)
Definition: http_curl.cc:493
HttpClientSession * session_
Definition: http_client.h:158
HttpClientSession(HttpClient *client, Socket *socket)
Definition: http_client.cc:17
static bool IsSocketErrorHard(const boost::system::error_code &ec)
Definition: tcp_session.cc:742
void TimerErrorHandler(std::string name, std::string error)
Definition: http_client.cc:485
void set_post_string(ConnInfo *conn, const char *post, uint32_t len)
Definition: http_curl.cc:464
boost::asio::ip::tcp::endpoint endpoint
Definition: http_client.h:219
void RemoveConnection(HttpConnection *)
Definition: http_client.cc:473
void OnEventInternal(TcpSessionPtr session, Event event)
Definition: http_client.cc:34
virtual void OnRead(Buffer buffer)
Definition: http_client.cc:22
char error[CURL_ERROR_SIZE+1]
Definition: http_curl.h:38
void AssignData(const char *ptr, size_t size)
Definition: http_client.cc:306
int http_get(ConnInfo *conn, GlobalInfo *g)
Definition: http_curl.cc:482
WorkQueue< EnqueuedCb > work_queue_
Definition: http_client.h:229
void del_curl_handle(ConnInfo *curl_handle, GlobalInfo *g)
Definition: http_curl.cc:378
bool Cancel()
Definition: timer.cc:150
boost::intrusive_ptr< TcpSession > TcpSessionPtr
Definition: http_client.h:40
void del_conn(HttpConnection *connection, GlobalInfo *g)
Definition: http_curl.cc:364
struct _ConnInfo * curl_handle()
Definition: http_client.h:96
IpAddress AddressFromString(const std::string &ip_address_str, boost::system::error_code *ec)
std::string client_key_
Definition: http_client.h:170
HttpClientSession::SessionEventCb event_cb_
Definition: http_client.h:161
int http_post(ConnInfo *conn, GlobalInfo *g)
Definition: http_curl.cc:497
int HttpHead(const std::string &path, bool header, bool short_timeout, bool reuse, std::vector< std::string > &hdr_options, HttpCb cb)
Definition: http_client.cc:152
bool AddConnection(HttpConnection *)
Definition: http_client.cc:464
bool IsErrorHard(const boost::system::error_code &ec)
Definition: http_client.cc:501
void HttpProcessInternal(const std::string body, std::string path, unsigned short header_shortTimeout_reuse, std::vector< std::string > hdr_options, HttpCb cb, http_method m)
Definition: http_client.cc:215
void OnEvent(TcpSession *session, Event event)
Definition: http_client.cc:26
HttpClient * client()
Definition: http_client.h:98
void set_header_options(ConnInfo *conn, const char *options)
Definition: http_curl.cc:445
bool Start(int time, Handler handler, ErrorHandler error_handler=NULL)
Definition: timer.cc:108
boost::asio::io_context * io_service()
Definition: http_client.cc:430
void SetConnection(HttpConnection *conn)
Definition: http_client.h:47
void set_url(ConnInfo *conn, const char *url)
Definition: http_curl.cc:440
int HttpPut(const std::string &put_string, const std::string &path, HttpCb)
Definition: http_client.cc:162
#define LOG(_Level, _Msg)
Definition: logging.h:33
void set_curl_handle(struct _ConnInfo *handle)
Definition: http_client.h:105
Timer * curl_timer_
Definition: http_client.h:225
boost::function< void(HttpClientSession *session, TcpSession::Event event)> SessionEventCb
Definition: http_client.h:39
void Shutdown()
Definition: http_client.cc:406
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:450
size_t GetOffset()
Definition: http_client.cc:369
bool timer_cb(GlobalInfo *g)
Definition: http_curl.cc:152
ConnInfo * new_conn(HttpConnection *connection, GlobalInfo *g, bool header, bool short_timeout, bool reuse)
Definition: http_curl.cc:391
HttpConnectionMap map_
Definition: http_client.h:226
void set_session(HttpClientSession *session)
Definition: http_client.cc:129
std::string buf_
Definition: http_client.h:155
virtual Socket * socket() const
Definition: tcp_session.h:86
bool Enqueue(QueueEntryT entry)
Definition: queue_task.h:248
void bf2bool(unsigned short bf, bool &header, bool &short_timeout, bool &reuse)
Definition: http_client.h:137
std::string client_cert_
Definition: http_client.h:168
void delete_session()
Definition: http_client.cc:117
void AssignHeader(const char *ptr, size_t size)
Definition: http_client.cc:316
virtual boost::system::error_code SetSocketOptions()
Definition: tcp_session.cc:868
static EventManager evm
static bool DeleteTimer(Timer *Timer)
Definition: timer.cc:222