WebUtils.cxx
Go to the documentation of this file.
1 #include "LEM/WebUtils.h"
2 
3 #include <curl/curl.h>
4 
5 #include <cassert>
6 #include <iostream>
7 
8 #include "TString.h"
9 #include "TStopwatch.h"
10 
11 #include "string.h" // memcpy
12 #include "unistd.h" // sleep
13 
14 namespace lem
15 {
16  //......................................................................
17  size_t writeMemoryCallback(void* contents, size_t size,
18  size_t nmemb, void* userp)
19  {
20  assert(userp);
21  std::string* str = (std::string*)userp;
22 
23  const size_t recvdSize = size*nmemb;
24  const size_t prevSize = str->size();
25  str->resize(prevSize+recvdSize);
26  memcpy((char*)str->c_str()+prevSize, contents, recvdSize);
27  return recvdSize;
28  }
29 
30  //......................................................................
32  const std::string& query,
33  const std::string& postdata,
34  int minPort,
35  int maxPort,
36  int queryTimeout,
37  int retryTimeout,
38  bool& ok)
39  {
40  ok = false;
41 
42  const time_t t0 = time(0);
43 
44  // No, this is explicitly not thread-safe. Caller should call this themselves
45  // curl_global_init(CURL_GLOBAL_ALL);
46  CURL* curl_handle = curl_easy_init();
47  assert(curl_handle);
48 
49  // Don't send "Expect: 100-continue" for long queries, just forge
50  // ahead. The chances of being rejected are pretty low and the server
51  // doesn't send a continue anyway.
52  curl_slist* slist = 0;
53  slist = curl_slist_append(slist, "Expect:");
54  curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, slist);
55 
56  // Configure callback function
57  std::string response;
58  curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writeMemoryCallback);
59  curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &response);
60 
61  // Timeout for the HTTP connection. Will try again with a new connection up
62  // to retryTimeout.
63  curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, queryTimeout);
64 
65  // Work around this crash
66  // http://stackoverflow.com/questions/9191668/error-longjmp-causes-uninitialized-stack-frame
67  curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
68 
69  // Allow redirection
70  curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
71 
72  if(!postdata.empty()){
73  curl_easy_setopt(curl_handle, CURLOPT_POST, 1);
74  curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, postdata.c_str());
75  }
76 
77  double realTime = -1;
78 
79  int k = 0;
80  while(true){
81  const TString url = TString::Format("%s:%d/%s",
82  host.c_str(),
83  minPort+rand()%(maxPort-minPort+1),
84  query.c_str());
85  curl_easy_setopt(curl_handle, CURLOPT_URL, url.Data());
86 
87  std::cout << "Making query " << url;
88  if(!postdata.empty()) std::cout << " with " << postdata.size() << " bytes of postdata";
90 
91  response.clear();
92  TStopwatch sw;
93  CURLcode code = curl_easy_perform(curl_handle);
94  sw.Stop();
95  realTime = sw.RealTime();
96 
97  if(code != CURLE_OK){
98  std::cerr << "Connection failed: " << curl_easy_strerror(code)
99  << std::endl;
100  }
101  else {
102  long http_code;
103  curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code);
104  if(http_code == 200 && code != CURLE_ABORTED_BY_CALLBACK){
105  //Succeeded
106  ok = true;
107  break;
108  }
109  else{
110  std::cerr << "Connection failed with HTTP " << http_code
111  << ": " << response << std::endl;
112  }
113  }
114 
115  // For subsequent iterations round the loop let's have a lot of debugging
116  // output.
117  curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1);
118 
119  const int delay = 1 + ((double)random()/(double)RAND_MAX) * (1 << k++);
120  const time_t t1 = time(0);
121  if(t1+delay > t0+retryTimeout) break;
122 
123  std::cerr << "Iteration " << k
124  << ": will wait for " << delay << " sec and try again. "
125  << t1-t0 << " sec since we started. Will retry until "
126  << retryTimeout << " sec." << std::endl;
127  sleep(delay);
128  } // end while
129 
130  curl_easy_cleanup(curl_handle);
131  // No, this is explicitly not thread-safe. Caller should call this themselves
132  // curl_global_cleanup();
133 
134  curl_slist_free_all(slist);
135 
136  if(!response.empty() && response.size() < 20){
137  std::cout << "Response: " << response << " in " << realTime << " sec" << std::endl;
138  }
139  else{
140  std::cout << "Response: " << response.size() << " bytes in " << realTime << " sec" << std::endl;
141  }
142 
143  return response;
144  }
145 
146  //......................................................................
148  const std::string& query,
149  int minPort,
150  int maxPort,
151  int queryTimeout,
152  int retryTimeout,
153  bool& ok)
154  {
155  return get_http_response_internal(host, query, "",
156  minPort, maxPort,
157  queryTimeout, retryTimeout,
158  ok);
159  }
160 
161  //......................................................................
163  const std::string& query,
164  const std::string& postdata,
165  int minPort,
166  int maxPort,
167  int queryTimeout,
168  int retryTimeout,
169  bool& ok)
170  {
171  return get_http_response_internal(host, query, postdata,
172  minPort, maxPort,
173  queryTimeout, retryTimeout,
174  ok);
175  }
176 
177  //......................................................................
179  {
180  char* e = getenv("NOVA_RELEASE");
181  if(e) return e;
182  e = getenv("SRT_BASE_RELEASE");
183  if(e) return e;
184  std::cerr << "Unable to determine current release. Aborting." << std::endl;
185  abort();
186  }
187 
188 } // namespace
size_t writeMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
Definition: WebUtils.cxx:17
OStream cerr
Definition: OStream.cxx:7
std::string get_http_response_internal(const std::string &host, const std::string &query, const std::string &postdata, int minPort, int maxPort, int queryTimeout, int retryTimeout, bool &ok)
Definition: WebUtils.cxx:31
::xsd::cxx::tree::time< char, simple_type > time
Definition: Database.h:194
PID
Definition: FillPIDs.h:14
std::string getenv(std::string const &name)
std::string post_query(const std::string &host, const std::string &query, const std::string &postdata, int minPort, int maxPort, int queryTimeout, int retryTimeout, bool &ok)
Make an HTTP POST query.
Definition: WebUtils.cxx:162
std::string get_release()
Figure out the current release. TODO: does this belong somewhere else?
Definition: WebUtils.cxx:178
OStream cout
Definition: OStream.cxx:6
Definition: inftrees.h:24
Float_t sw
Definition: plot.C:20
assert(nhit_max >=nhit_nbins)
void Format(TGraph *gr, int lcol, int lsty, int lwid, int mcol, int msty, double msiz)
Definition: Style.cxx:154
std::string get_query(const std::string &host, const std::string &query, int minPort, int maxPort, int queryTimeout, int retryTimeout, bool &ok)
Make an HTTP GET query.
Definition: WebUtils.cxx:147
Float_t e
Definition: plot.C:35
enum BeamMode string