Main MRPT website > C++ reference for MRPT 1.9.9
net_utils.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "comms-precomp.h" // Precompiled headers
11 
12 #include <mrpt/comms/net_utils.h>
15 #include <mrpt/utils/CTicTac.h>
17 
18 #include <thread>
19 #include <future>
20 #include <cstring>
21 #include <stdio.h>
22 
23 #if defined(MRPT_OS_LINUX) || defined(MRPT_OS_APPLE)
24 #define INVALID_SOCKET (-1)
25 #include <sys/socket.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <sys/ioctl.h>
31 #include <netdb.h>
32 #include <arpa/inet.h>
33 #include <netinet/in.h>
34 #endif
35 
36 #ifdef MRPT_OS_WINDOWS
37 #include <winsock.h>
38 #endif
39 
40 using namespace mrpt;
41 using namespace mrpt::system;
42 using namespace mrpt::utils;
43 using namespace mrpt::comms;
44 using namespace mrpt::comms::net;
45 using namespace std;
46 
47 /*---------------------------------------------------------------
48  http_get
49  ---------------------------------------------------------------*/
51  const string& url, string& out_content, string& out_errormsg, int port,
52  const string& auth_user, const string& auth_pass,
53  int* out_http_responsecode, mrpt::utils::TParameters<string>* extra_headers,
54  mrpt::utils::TParameters<string>* out_headers, int timeout_ms)
55 {
58  url, data, out_errormsg, port, auth_user, auth_pass,
59  out_http_responsecode, extra_headers, out_headers, timeout_ms);
60 
61  out_content.resize(data.size());
62  if (!data.empty()) ::memcpy(&out_content[0], &data[0], data.size());
63 
64  return ret;
65 }
66 
68  const string& http_method, const string& http_send_content,
69  const string& url, vector_byte& out_content, string& out_errormsg, int port,
70  const string& auth_user, const string& auth_pass,
71  int* out_http_responsecode, mrpt::utils::TParameters<string>* extra_headers,
72  mrpt::utils::TParameters<string>* out_headers, int timeout_ms)
73 {
74  // Reset output data:
75  out_content.clear();
76  if (out_http_responsecode) *out_http_responsecode = 0;
77  if (out_headers) out_headers->clear();
78 
79  // URL must be:
80  // http://<SERVER>/<LOCAL_ADDR>
81 
82  if (0 != ::strncmp(url.c_str(), "http://", 7))
83  {
84  out_errormsg = "URL must start with 'http://'";
85  return net::erBadURL;
86  }
87 
88  string server_addr = url.substr(7);
89  string get_object = "/";
90 
91  // Remove from the first "/" on:
92  size_t pos = server_addr.find("/");
93  if (pos == 0)
94  {
95  out_errormsg = "Server name not found in URL";
96  return net::erBadURL;
97  }
98  if (pos != string::npos)
99  {
100  get_object = server_addr.substr(pos);
101  server_addr.resize(pos);
102  }
103 
104  CClientTCPSocket sock;
105 
106  try
107  {
108  // Connect:
109  sock.connect(server_addr, port, timeout_ms);
110  }
111  catch (std::exception& e)
112  {
113  out_errormsg = e.what();
114  return net::erCouldntConnect;
115  }
116 
117  try
118  {
119  // Set the user-defined headers (we may overwrite them if needed)
120  TParameters<string> headers_to_send;
121  if (extra_headers) headers_to_send = *extra_headers;
122 
123  headers_to_send["Connection"] = "close"; // Don't keep alive
124 
125  if (!headers_to_send.has("User-Agent"))
126  headers_to_send["User-Agent"] = "MRPT Library";
127 
128  // Implement HTTP Basic authentication:
129  // See: http://en.wikipedia.org/wiki/Basic_access_authentication
130  if (!auth_user.empty())
131  {
132  string auth_str = auth_user + string(":") + auth_pass;
133  vector_byte v(auth_str.size());
134  ::memcpy(&v[0], &auth_str[0], auth_str.size());
135 
136  string encoded_str;
137  mrpt::system::encodeBase64(v, encoded_str);
138 
139  headers_to_send["Authorization"] = string("Basic ") + encoded_str;
140  }
141 
142  if (!http_send_content.empty() &&
143  headers_to_send.find("Content-Length") == headers_to_send.end())
144  {
145  headers_to_send["Content-Length"] =
146  mrpt::format("%u", (unsigned int)http_send_content.size());
147  }
148 
149  // Prepare the request string
150  // ---------------------------------
151  string req = format(
152  "%s %s HTTP/1.1\r\n"
153  "Host: %s\r\n",
154  http_method.c_str(), get_object.c_str(), server_addr.c_str());
155 
156  // Other headers:
157  for (TParameters<string>::const_iterator i = headers_to_send.begin();
158  i != headers_to_send.end(); ++i)
159  {
160  req += i->first;
161  req += ": ";
162  req += i->second;
163  req += "\r\n";
164  }
165 
166  // End:
167  req += "\r\n";
168 
169  // Any POST data?
170  req += http_send_content;
171 
172  // Send:
173  sock.sendString(req);
174 
175  // Read answer:
176  vector_byte buf;
177  buf.reserve(1 << 14);
178 
179  size_t total_read = 0;
180  bool content_length_read = false;
181  bool all_headers_read = false;
182  size_t content_length = 0;
183  size_t content_offset = 0;
184  int http_code = 0;
186 
187  CTicTac watchdog;
188  watchdog.Tic();
189 
190  while (!content_length_read ||
191  total_read < (content_offset + content_length))
192  {
193  // Read until "Content-Length: XXX \r\n" is read or the whole
194  // message is read,
195  // or an error code is read.
196  size_t to_read_now;
197 
198  if (!content_length_read)
199  {
200  to_read_now = 1500;
201  }
202  else
203  {
204  to_read_now = (content_length + content_offset) - total_read;
205  }
206 
207  // make room for the data to come:
208  buf.resize(total_read + to_read_now + 1);
209 
210  // Read:
211  size_t len =
212  sock.readAsync(&buf[total_read], to_read_now, timeout_ms, 100);
213  if (!len)
214  {
215  //
216  if (!sock.isConnected())
217  {
218  if (all_headers_read) // It seems we're done...
219  break;
220  else
221  {
222  out_errormsg = "Connection to server was lost";
223  return net::erCouldntConnect;
224  }
225  }
226 
227  if (watchdog.Tac() > 1e-3 * timeout_ms)
228  {
229  out_errormsg = "Timeout waiting answer from server";
230  return net::erCouldntConnect;
231  }
232  std::this_thread::sleep_for(10ms);
233  continue;
234  }
235  total_read += len;
236  watchdog.Tic();
237 
238  buf[total_read] = '\0';
239 
240  // do we have a \r\n\r\n ??
241  if (!all_headers_read)
242  {
243  const char* ptr = ::strstr(
244  reinterpret_cast<const char*>(&buf[0]), "\r\n\r\n");
245  if (ptr)
246  {
247  all_headers_read = true;
248  const size_t pos_dblret = ((char*)ptr) - (char*)(&buf[0]);
249 
250  // Process the headers:
251  // ------------------------------
252  if (!::strncmp("HTTP/", (const char*)&buf[0], 5))
253  {
254  http_code = ::atoi((const char*)&buf[9]);
255  }
256  else
257  {
258  // May it be a "SOURCETABLE " answer for NTRIP
259  // protocol??
260  if (!::strncmp(
261  "SOURCETABLE ", (const char*)&buf[0], 12))
262  {
263  http_code = ::atoi((const char*)&buf[12]);
264  }
265  else
266  {
267  out_errormsg =
268  "Server didn't send an HTTP/1.1 answer.";
269  return net::erOtherHTTPError;
270  }
271  }
272 
273  // Check the HTTP code and the content-length:
274  content_offset = pos_dblret + 4;
275 
276  // Do we have a "Content-Length:"??
277  const char* ptr_len = ::strstr(
278  reinterpret_cast<const char*>(&buf[0]),
279  "Content-Length:");
280  if (ptr_len)
281  {
282  content_length = ::atol(ptr_len + 15);
283  content_length_read = true;
284  }
285 
286  // Parse the rest of HTTP headers:
287  {
288  string aux_all_headers;
289  deque<string> lstLines;
290  aux_all_headers.resize(content_offset);
291  ::memcpy(&aux_all_headers[0], &buf[0], content_offset);
292 
294  aux_all_headers, "\r\n", lstLines);
295 
296  for (deque<string>::const_iterator i = lstLines.begin();
297  i != lstLines.end(); ++i)
298  {
299  const size_t p = i->find(":");
300  if (p == string::npos) continue;
301 
302  const string key = i->substr(0, p);
303  const string val = i->substr(p + 2);
304  rx_headers[key] = val;
305  }
306  }
307  }
308  }
309  } // end while
310 
311  if (out_http_responsecode) *out_http_responsecode = http_code;
312  if (out_headers) *out_headers = rx_headers;
313 
314  // Remove the headers from the content:
315  buf.erase(buf.begin(), buf.begin() + content_offset);
316 
317  // Process: "Transfer-Encoding: chunked"
318  if (rx_headers.has("Transfer-Encoding") &&
319  rx_headers["Transfer-Encoding"] == "chunked")
320  {
321  // See: http://en.wikipedia.org/wiki/Chunked_transfer_encoding
322 
323  size_t index = 0;
324  while (index < buf.size())
325  {
326  if (buf[index] == '\r' && buf[index + 1] == '\n')
327  {
328  buf.erase(buf.begin() + index, buf.begin() + index + 2);
329  continue;
330  }
331 
332  const char* pCRLF = ::strstr((const char*)&buf[index], "\r\n");
333  if (!pCRLF) break;
334 
335  const size_t len_substr = ((char*)pCRLF) - (char*)(&buf[index]);
336 
337  string sLen((const char*)&buf[index], len_substr);
338  sLen = string("0x") + sLen;
339 
340  unsigned int lenChunk;
341  int fields = ::sscanf(sLen.c_str(), "%x", &lenChunk);
342  if (!fields) break;
343 
344  // Remove the len of this chunk header from the data:
345  buf.erase(
346  buf.begin() + index, buf.begin() + index + len_substr + 2);
347 
348  index += lenChunk;
349 
350  if (!lenChunk)
351  {
352  buf.resize(index);
353  break;
354  }
355  }
356  }
357 
358  // Set the content output:
359  out_content.swap(buf);
360 
361  if (http_code == 200)
362  {
363  return net::erOk;
364  }
365  else
366  {
367  out_errormsg = format("HTTP error %i", http_code);
368  return net::erOtherHTTPError;
369  }
370  }
371  catch (std::exception& e)
372  {
373  out_errormsg = e.what();
374  return net::erCouldntConnect;
375  }
376 
377  return net::erOk;
378 }
379 
380 /*---------------------------------------------------------------
381  http_get
382 ---------------------------------------------------------------*/
384  const string& url, vector_byte& out_content, string& out_errormsg, int port,
385  const string& auth_user, const string& auth_pass,
386  int* out_http_responsecode, mrpt::utils::TParameters<string>* extra_headers,
387  mrpt::utils::TParameters<string>* out_headers, int timeout_ms)
388 {
389  return http_request(
390  "GET", "", url, out_content, out_errormsg, port, auth_user, auth_pass,
391  out_http_responsecode, extra_headers, out_headers, timeout_ms);
392 }
393 
394 /** Resolve a server address by its name, returning its IP address as a string -
395  * This method has a timeout for the maximum time to wait for the DNS server.
396  * For example: server_name="www.google.com" -> out_ip="209.85.227.99"
397  *
398  * \return true on success, false on timeout or other error.
399  */
401  const std::string& server_name, std::string& out_ip,
402  const unsigned int timeout_ms)
403 {
404  // Firstly: If it's a numeric address already, do nothing:
405  if (server_name.find_first_not_of("0123456789. ") == std::string::npos)
406  {
407  // It's a pure IP address:
408  out_ip = server_name;
409  return true;
410  }
411 
412  // Solve DNS --------------
413  // It seems that the only reliable way of *with a timeout* is to launch a
414  // separate thread.
415 
416  std::future<std::string> dns_result =
417  std::async(std::launch::async, [&]() {
418 // Windows-specific stuff:
419 #ifdef MRPT_OS_WINDOWS
420  {
421  // Init the WinSock Library:
422  WORD wVersionRequested = MAKEWORD(2, 0);
423  WSADATA wsaData;
424  if (WSAStartup(wVersionRequested, &wsaData))
425  {
426  std::cerr
427  << "thread_DNS_solver_async: Error calling WSAStartup";
428  return std::string();
429  }
430  }
431 #endif
432 
433  // Do the DNS lookup:
434  std::string dns_result;
435 
436  hostent* he = gethostbyname(server_name.c_str());
437  if (!he)
438  {
439  dns_result.clear(); // empty string -> error.
440  }
441  else
442  {
443  struct in_addr ADDR;
444  ::memcpy(
445  &ADDR, he->h_addr,
446  sizeof(ADDR)); // Was: *((struct in_addr *)he->h_addr);
447  // Convert address to text:
448  dns_result = string(inet_ntoa(ADDR));
449  }
450 
451 #ifdef MRPT_OS_WINDOWS
452  WSACleanup();
453 #endif
454  return dns_result;
455  });
456 
457  auto status = dns_result.wait_for(std::chrono::milliseconds(timeout_ms));
458 
459  if (status == std::future_status::ready)
460  {
461  // Done: Anyway, it can still be an error result:
462  out_ip = dns_result.get();
463  return !out_ip.empty();
464  }
465  else
466  {
467  // Timeout:
468  out_ip.clear();
469 
470  return false;
471  }
472 }
473 
474 /** Returns a description of the last Sockets error */
476 {
477 #ifdef MRPT_OS_WINDOWS
478  const int errnum = WSAGetLastError();
479  char* s = nullptr;
480  FormatMessageA(
481  FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
482  FORMAT_MESSAGE_IGNORE_INSERTS,
483  nullptr, errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&s,
484  0, nullptr);
485  const std::string str = mrpt::format("%s [errno=%d]", s, errnum);
486  LocalFree(s);
487  return str;
488 #else
489  return std::string(strerror(errno));
490 #endif
491 }
492 
493 MRPT_TODO("Modify ping to run on Windows + Test this");
495  const std::string& address, const int& max_attempts,
496  std::string* output_str /*=NULL*/)
497 {
498  using namespace std;
499 
500  // Format a command string
501  string cmd_str = "ping";
502 
503 // different "count" argument for Windows and *NIX systems
504 #if defined(MRPT_OS_LINUX) || defined(MRPT_OS_APPLE)
505  string count_arg_str = "-c " + num2str(max_attempts);
506 #else
507  THROW_EXCEPTION("Ping not implemented on Windows Yet.")
508 
509  string count_arg_str = "-n " + num2str(max_attempts);
510 #endif
511 
512  string redirection_str = "2>&1";
513 
514  // Join the command subparts
515  string cmd_full_str = mrpt::format(
516  "%s %s %s %s", cmd_str.c_str(), count_arg_str.c_str(), address.c_str(),
517  redirection_str.c_str());
518 
519  // Finally exec the command
520  int code = executeCommand(cmd_full_str, output_str);
521 
522  return (code == 0);
523 }
Classes for serialization, sockets, ini-file manipulation, streams, list of properties-values, timewatch, extensions to STL.
For usage when passing a dynamic number of (numeric) arguments to a function, by name.
Definition: TParameters.h:57
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
std::vector< uint8_t > vector_byte
Definition: types_simple.h:27
void connect(const std::string &remotePartAddress, unsigned short remotePartTCPPort, unsigned int timeout_ms=0)
Establishes a connection with a remote part.
This namespace provides a OS-independent interface to many useful functions: filenames manipulation...
Definition: math_frwds.h:30
bool isConnected()
Returns true if this objects represents a successfully connected socket.
#define THROW_EXCEPTION(msg)
void encodeBase64(const vector_byte &inputData, std::string &outString)
Encode a sequence of bytes as a string in base-64.
Definition: base64.cpp:28
ERRORCODE_HTTP http_get(const string &url, vector_byte &out_content, string &out_errormsg, int port=80, const string &auth_user=string(), const string &auth_pass=string(), int *out_http_responsecode=nullptr, mrpt::utils::TParameters< string > *extra_headers=nullptr, mrpt::utils::TParameters< string > *out_headers=nullptr, int timeout_ms=1000)
Perform an HTTP GET operation (version for retrieving the data as a vector_byte)
Definition: net_utils.cpp:383
std::string getLastSocketErrorStr()
Returns a description of the last Sockets error.
Definition: net_utils.cpp:475
STL namespace.
const Scalar * const_iterator
Definition: eigen_plugins.h:27
GLdouble s
Definition: glext.h:3676
GLenum GLsizei len
Definition: glext.h:4712
void Tic()
Starts the stopwatch.
Definition: CTicTac.cpp:82
int executeCommand(const std::string &command, std::string *output=NULL, const std::string &mode="r")
Execute Generic Shell Command.
Definition: os.cpp:661
MRPT_TODO("Modify ping to run on Windows + Test this")
ERRORCODE_HTTP
Possible returns from a HTTP request.
Definition: net_utils.h:31
bool DNS_resolve_async(const std::string &server_name, std::string &out_ip, const unsigned int timeout_ms=3000)
Resolve a server address by its name, returning its IP address as a string - This method has a timeou...
Definition: net_utils.cpp:400
GLuint index
Definition: glext.h:4054
This class implements a high-performance stopwatch.
Definition: CTicTac.h:23
int val
Definition: mrpt_jpeglib.h:955
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:19
ERRORCODE_HTTP http_request(const string &http_method, const string &http_send_content, const string &url, vector_byte &out_content, string &out_errormsg, int port=80, const string &auth_user=string(), const string &auth_pass=string(), int *out_http_responsecode=nullptr, mrpt::utils::TParameters< string > *extra_headers=nullptr, mrpt::utils::TParameters< string > *out_headers=nullptr, int timeout_ms=1000)
Generic function for HTTP GET & POST methods.
Definition: net_utils.cpp:67
GLsizei const GLchar ** string
Definition: glext.h:4101
void tokenize(const std::string &inString, const std::string &inDelimiters, std::deque< std::string > &outTokens, bool skipBlankTokens=true) noexcept
Tokenizes a string according to a set of delimiting characters.
const GLdouble * v
Definition: glext.h:3678
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
Definition: inftrees.h:28
A TCP socket that can be connected to a TCP server, implementing MRPT&#39;s CStream interface for passing...
std::string num2str(T const &value)
Convert number instance to string.
Definition: string_utils.h:147
_u8 status
Definition: rplidar_cmd.h:20
double Tac()
Stops the stopwatch.
Definition: CTicTac.cpp:97
A set of useful routines for networking.
Definition: net_utils.h:23
GLuint address
Definition: glext.h:6947
bool Ping(const std::string &address, const int &max_attempts, std::string *output_str=NULL)
Ping an IP address.
Definition: net_utils.cpp:494
Serial and networking devices and utilities.
size_t readAsync(void *Buffer, const size_t Count, const int timeoutStart_ms=-1, const int timeoutBetween_ms=-1)
A method for reading from the socket with an optional timeout.
GLsizei GLsizei GLenum GLenum const GLvoid * data
Definition: glext.h:3546
GLfloat GLfloat p
Definition: glext.h:6305
void sendString(const std::string &str)
Writes a string to the socket.
void memcpy(void *dest, size_t destSize, const void *src, size_t copyCount) noexcept
An OS and compiler independent version of "memcpy".
Definition: os.cpp:355
bool has(const std::string &s) const
Definition: TParameters.h:86



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ae4571287 Thu Nov 23 00:06:53 2017 +0100 at dom oct 27 23:51:55 CET 2019