HTTP.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. /*
  2. * Copyright (c) 2013-2019, The PurpleI2P Project
  3. *
  4. * This file is part of Purple i2pd project and licensed under BSD3
  5. *
  6. * See full license text in LICENSE file at top of project tree
  7. */
  8. #include <algorithm>
  9. #include <utility>
  10. #include <stdio.h>
  11. #include "util.h"
  12. #include "HTTP.h"
  13. #include <ctime>
  14. namespace i2p {
  15. namespace http {
  16. const std::vector<std::string> HTTP_METHODS = {
  17. "GET", "HEAD", "POST", "PUT", "PATCH",
  18. "DELETE", "OPTIONS", "CONNECT", "PROPFIND"
  19. };
  20. const std::vector<std::string> HTTP_VERSIONS = {
  21. "HTTP/1.0", "HTTP/1.1"
  22. };
  23. const std::vector<const char *> weekdays = {
  24. "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  25. };
  26. const std::vector<const char *> months = {
  27. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  28. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  29. };
  30. inline bool is_http_version(const std::string & str) {
  31. return std::find(HTTP_VERSIONS.begin(), HTTP_VERSIONS.end(), str) != std::end(HTTP_VERSIONS);
  32. }
  33. inline bool is_http_method(const std::string & str) {
  34. return std::find(HTTP_METHODS.begin(), HTTP_METHODS.end(), str) != std::end(HTTP_METHODS);
  35. }
  36. void strsplit(const std::string & line, std::vector<std::string> &tokens, char delim, std::size_t limit = 0) {
  37. std::size_t count = 0;
  38. std::stringstream ss(line);
  39. std::string token;
  40. while (1) {
  41. count++;
  42. if (limit > 0 && count >= limit)
  43. delim = '\n'; /* reset delimiter */
  44. if (!std::getline(ss, token, delim))
  45. break;
  46. tokens.push_back(token);
  47. }
  48. }
  49. static std::pair<std::string, std::string> parse_header_line(const std::string& line)
  50. {
  51. std::size_t pos = 0;
  52. std::size_t len = 1; /*: */
  53. std::size_t max = line.length();
  54. if ((pos = line.find(':', pos)) == std::string::npos)
  55. return std::make_pair("", ""); // no ':' found
  56. if (pos + 1 < max) // ':' at the end of header is valid
  57. {
  58. while ((pos + len) < max && isspace(line.at(pos + len)))
  59. len++;
  60. if (len == 1)
  61. return std::make_pair("", ""); // no following space, but something else
  62. }
  63. return std::make_pair(line.substr(0, pos), line.substr(pos + len));
  64. }
  65. void gen_rfc7231_date(std::string & out) {
  66. std::time_t now = std::time(nullptr);
  67. char buf[128];
  68. std::tm *tm = std::gmtime(&now);
  69. snprintf(buf, sizeof(buf), "%s, %02d %s %d %02d:%02d:%02d GMT",
  70. weekdays[tm->tm_wday], tm->tm_mday, months[tm->tm_mon],
  71. tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec
  72. );
  73. out = buf;
  74. }
  75. bool URL::parse(const char *str, std::size_t len) {
  76. std::string url(str, len ? len : strlen(str));
  77. return parse(url);
  78. }
  79. bool URL::parse(const std::string& url) {
  80. std::size_t pos_p = 0; /* < current parse position */
  81. std::size_t pos_c = 0; /* < work position */
  82. if(url.at(0) != '/' || pos_p > 0) {
  83. std::size_t pos_s = 0;
  84. /* schema */
  85. pos_c = url.find("://");
  86. if (pos_c != std::string::npos) {
  87. schema = url.substr(0, pos_c);
  88. pos_p = pos_c + 3;
  89. }
  90. /* user[:pass] */
  91. pos_s = url.find('/', pos_p); /* find first slash */
  92. pos_c = url.find('@', pos_p); /* find end of 'user' or 'user:pass' part */
  93. if (pos_c != std::string::npos && (pos_s == std::string::npos || pos_s > pos_c)) {
  94. std::size_t delim = url.find(':', pos_p);
  95. if (delim && delim != std::string::npos && delim < pos_c) {
  96. user = url.substr(pos_p, delim - pos_p);
  97. delim += 1;
  98. pass = url.substr(delim, pos_c - delim);
  99. } else if(delim) {
  100. user = url.substr(pos_p, pos_c - pos_p);
  101. }
  102. pos_p = pos_c + 1;
  103. }
  104. /* hostname[:port][/path] */
  105. pos_c = url.find_first_of(":/", pos_p);
  106. if (pos_c == std::string::npos) {
  107. /* only hostname, without post and path */
  108. host = url.substr(pos_p, std::string::npos);
  109. return true;
  110. } else if (url.at(pos_c) == ':') {
  111. host = url.substr(pos_p, pos_c - pos_p);
  112. /* port[/path] */
  113. pos_p = pos_c + 1;
  114. pos_c = url.find('/', pos_p);
  115. std::string port_str = (pos_c == std::string::npos)
  116. ? url.substr(pos_p, std::string::npos)
  117. : url.substr(pos_p, pos_c - pos_p);
  118. /* stoi throws exception on failure, we don't need it */
  119. for (char c : port_str) {
  120. if (c < '0' || c > '9')
  121. return false;
  122. port *= 10;
  123. port += c - '0';
  124. }
  125. if (pos_c == std::string::npos)
  126. return true; /* no path part */
  127. pos_p = pos_c;
  128. } else {
  129. /* start of path part found */
  130. host = url.substr(pos_p, pos_c - pos_p);
  131. pos_p = pos_c;
  132. }
  133. }
  134. /* pos_p now at start of path part */
  135. pos_c = url.find_first_of("?#", pos_p);
  136. if (pos_c == std::string::npos) {
  137. /* only path, without fragment and query */
  138. path = url.substr(pos_p, std::string::npos);
  139. return true;
  140. } else if (url.at(pos_c) == '?') {
  141. /* found query part */
  142. path = url.substr(pos_p, pos_c - pos_p);
  143. pos_p = pos_c + 1;
  144. pos_c = url.find('#', pos_p);
  145. if (pos_c == std::string::npos) {
  146. /* no fragment */
  147. query = url.substr(pos_p, std::string::npos);
  148. return true;
  149. } else {
  150. query = url.substr(pos_p, pos_c - pos_p);
  151. pos_p = pos_c + 1;
  152. }
  153. } else {
  154. /* found fragment part */
  155. path = url.substr(pos_p, pos_c - pos_p);
  156. pos_p = pos_c + 1;
  157. }
  158. /* pos_p now at start of fragment part */
  159. frag = url.substr(pos_p, std::string::npos);
  160. return true;
  161. }
  162. bool URL::parse_query(std::map<std::string, std::string> & params) {
  163. std::vector<std::string> tokens;
  164. strsplit(query, tokens, '&');
  165. params.clear();
  166. for (const auto& it : tokens) {
  167. std::size_t eq = it.find ('=');
  168. if (eq != std::string::npos) {
  169. auto e = std::pair<std::string, std::string>(it.substr(0, eq), it.substr(eq + 1));
  170. params.insert(e);
  171. } else {
  172. auto e = std::pair<std::string, std::string>(it, "");
  173. params.insert(e);
  174. }
  175. }
  176. return true;
  177. }
  178. std::string URL::to_string() {
  179. std::string out = "";
  180. if (schema != "") {
  181. out = schema + "://";
  182. if (user != "" && pass != "") {
  183. out += user + ":" + pass + "@";
  184. } else if (user != "") {
  185. out += user + "@";
  186. }
  187. if (port) {
  188. out += host + ":" + std::to_string(port);
  189. } else {
  190. out += host;
  191. }
  192. }
  193. out += path;
  194. if (query != "")
  195. out += "?" + query;
  196. if (frag != "")
  197. out += "#" + frag;
  198. return out;
  199. }
  200. bool URL::is_i2p() const
  201. {
  202. return host.rfind(".i2p") == ( host.size() - 4 );
  203. }
  204. void HTTPMsg::add_header(const char *name, std::string & value, bool replace) {
  205. add_header(name, value.c_str(), replace);
  206. }
  207. void HTTPMsg::add_header(const char *name, const char *value, bool replace) {
  208. std::size_t count = headers.count(name);
  209. if (count && !replace)
  210. return;
  211. if (count) {
  212. headers[name] = value;
  213. return;
  214. }
  215. headers.insert(std::pair<std::string, std::string>(name, value));
  216. }
  217. void HTTPMsg::del_header(const char *name) {
  218. headers.erase(name);
  219. }
  220. int HTTPReq::parse(const char *buf, size_t len) {
  221. std::string str(buf, len);
  222. return parse(str);
  223. }
  224. int HTTPReq::parse(const std::string& str) {
  225. enum { REQ_LINE, HEADER_LINE } expect = REQ_LINE;
  226. std::size_t eoh = str.find(HTTP_EOH); /* request head size */
  227. std::size_t eol = 0, pos = 0;
  228. URL url;
  229. if (eoh == std::string::npos)
  230. return 0; /* str not contains complete request */
  231. while ((eol = str.find(CRLF, pos)) != std::string::npos) {
  232. if (expect == REQ_LINE) {
  233. std::string line = str.substr(pos, eol - pos);
  234. std::vector<std::string> tokens;
  235. strsplit(line, tokens, ' ');
  236. if (tokens.size() != 3)
  237. return -1;
  238. if (!is_http_method(tokens[0]))
  239. return -1;
  240. if (!is_http_version(tokens[2]))
  241. return -1;
  242. if (!url.parse(tokens[1]))
  243. return -1;
  244. /* all ok */
  245. method = tokens[0];
  246. uri = tokens[1];
  247. version = tokens[2];
  248. expect = HEADER_LINE;
  249. }
  250. else
  251. {
  252. std::string line = str.substr(pos, eol - pos);
  253. auto p = parse_header_line(line);
  254. if (p.first.length () > 0)
  255. headers.push_back (p);
  256. else
  257. return -1;
  258. }
  259. pos = eol + strlen(CRLF);
  260. if (pos >= eoh)
  261. break;
  262. }
  263. return eoh + strlen(HTTP_EOH);
  264. }
  265. void HTTPReq::write(std::ostream & o)
  266. {
  267. o << method << " " << uri << " " << version << CRLF;
  268. for (auto & h : headers)
  269. o << h.first << ": " << h.second << CRLF;
  270. o << CRLF;
  271. }
  272. std::string HTTPReq::to_string()
  273. {
  274. std::stringstream ss;
  275. write(ss);
  276. return ss.str();
  277. }
  278. void HTTPReq::AddHeader (const std::string& name, const std::string& value)
  279. {
  280. headers.push_back (std::make_pair(name, value));
  281. }
  282. void HTTPReq::UpdateHeader (const std::string& name, const std::string& value)
  283. {
  284. for (auto& it : headers)
  285. if (it.first == name)
  286. {
  287. it.second = value;
  288. break;
  289. }
  290. }
  291. void HTTPReq::RemoveHeader (const std::string& name, const std::string& exempt)
  292. {
  293. for (auto it = headers.begin (); it != headers.end ();)
  294. {
  295. if (!it->first.compare(0, name.length (), name) && it->first != exempt)
  296. it = headers.erase (it);
  297. else
  298. it++;
  299. }
  300. }
  301. std::string HTTPReq::GetHeader (const std::string& name) const
  302. {
  303. for (auto& it : headers)
  304. if (it.first == name)
  305. return it.second;
  306. return "";
  307. }
  308. bool HTTPRes::is_chunked() const
  309. {
  310. auto it = headers.find("Transfer-Encoding");
  311. if (it == headers.end())
  312. return false;
  313. if (it->second.find("chunked") == std::string::npos)
  314. return true;
  315. return false;
  316. }
  317. bool HTTPRes::is_gzipped(bool includingI2PGzip) const
  318. {
  319. auto it = headers.find("Content-Encoding");
  320. if (it == headers.end())
  321. return false; /* no header */
  322. if (it->second.find("gzip") != std::string::npos)
  323. return true; /* gotcha! */
  324. if (includingI2PGzip && it->second.find("x-i2p-gzip") != std::string::npos)
  325. return true;
  326. return false;
  327. }
  328. long int HTTPMsg::content_length() const
  329. {
  330. unsigned long int length = 0;
  331. auto it = headers.find("Content-Length");
  332. if (it == headers.end())
  333. return -1;
  334. errno = 0;
  335. length = std::strtoul(it->second.c_str(), (char **) NULL, 10);
  336. if (errno != 0)
  337. return -1;
  338. return length;
  339. }
  340. int HTTPRes::parse(const char *buf, size_t len) {
  341. std::string str(buf, len);
  342. return parse(str);
  343. }
  344. int HTTPRes::parse(const std::string& str) {
  345. enum { RES_LINE, HEADER_LINE } expect = RES_LINE;
  346. std::size_t eoh = str.find(HTTP_EOH); /* request head size */
  347. std::size_t eol = 0, pos = 0;
  348. if (eoh == std::string::npos)
  349. return 0; /* str not contains complete request */
  350. while ((eol = str.find(CRLF, pos)) != std::string::npos) {
  351. if (expect == RES_LINE) {
  352. std::string line = str.substr(pos, eol - pos);
  353. std::vector<std::string> tokens;
  354. strsplit(line, tokens, ' ', 3);
  355. if (tokens.size() != 3)
  356. return -1;
  357. if (!is_http_version(tokens[0]))
  358. return -1;
  359. code = atoi(tokens[1].c_str());
  360. if (code < 100 || code >= 600)
  361. return -1;
  362. /* all ok */
  363. version = tokens[0];
  364. status = tokens[2];
  365. expect = HEADER_LINE;
  366. } else {
  367. std::string line = str.substr(pos, eol - pos);
  368. auto p = parse_header_line(line);
  369. if (p.first.length () > 0)
  370. headers.insert (p);
  371. else
  372. return -1;
  373. }
  374. pos = eol + strlen(CRLF);
  375. if (pos >= eoh)
  376. break;
  377. }
  378. return eoh + strlen(HTTP_EOH);
  379. }
  380. std::string HTTPRes::to_string() {
  381. if (version == "HTTP/1.1" && headers.count("Date") == 0) {
  382. std::string date;
  383. gen_rfc7231_date(date);
  384. add_header("Date", date.c_str());
  385. }
  386. if (status == "OK" && code != 200)
  387. status = HTTPCodeToStatus(code); // update
  388. if (body.length() > 0 && headers.count("Content-Length") == 0)
  389. add_header("Content-Length", std::to_string(body.length()).c_str());
  390. /* build response */
  391. std::stringstream ss;
  392. ss << version << " " << code << " " << status << CRLF;
  393. for (auto & h : headers) {
  394. ss << h.first << ": " << h.second << CRLF;
  395. }
  396. ss << CRLF;
  397. if (body.length() > 0)
  398. ss << body;
  399. return ss.str();
  400. }
  401. const char * HTTPCodeToStatus(int code) {
  402. const char *ptr;
  403. switch (code) {
  404. case 105: ptr = "Name Not Resolved"; break;
  405. /* success */
  406. case 200: ptr = "OK"; break;
  407. case 206: ptr = "Partial Content"; break;
  408. /* redirect */
  409. case 301: ptr = "Moved Permanently"; break;
  410. case 302: ptr = "Found"; break;
  411. case 304: ptr = "Not Modified"; break;
  412. case 307: ptr = "Temporary Redirect"; break;
  413. /* client error */
  414. case 400: ptr = "Bad Request"; break;
  415. case 401: ptr = "Unauthorized"; break;
  416. case 403: ptr = "Forbidden"; break;
  417. case 404: ptr = "Not Found"; break;
  418. case 407: ptr = "Proxy Authentication Required"; break;
  419. case 408: ptr = "Request Timeout"; break;
  420. /* server error */
  421. case 500: ptr = "Internal Server Error"; break;
  422. case 502: ptr = "Bad Gateway"; break;
  423. case 503: ptr = "Not Implemented"; break;
  424. case 504: ptr = "Gateway Timeout"; break;
  425. default: ptr = "Unknown Status"; break;
  426. }
  427. return ptr;
  428. }
  429. std::string UrlDecode(const std::string& data, bool allow_null) {
  430. std::string decoded(data);
  431. size_t pos = 0;
  432. while ((pos = decoded.find('%', pos)) != std::string::npos) {
  433. char c = strtol(decoded.substr(pos + 1, 2).c_str(), NULL, 16);
  434. if (c == '\0' && !allow_null) {
  435. pos += 3;
  436. continue;
  437. }
  438. decoded.replace(pos, 3, 1, c);
  439. pos++;
  440. }
  441. return decoded;
  442. }
  443. bool MergeChunkedResponse (std::istream& in, std::ostream& out) {
  444. std::string hexLen;
  445. while (!in.eof ()) {
  446. std::getline (in, hexLen);
  447. errno = 0;
  448. long int len = strtoul(hexLen.c_str(), (char **) NULL, 16);
  449. if (errno != 0)
  450. return false; /* conversion error */
  451. if (len == 0)
  452. return true; /* end of stream */
  453. if (len < 0 || len > 10 * 1024 * 1024) /* < 10Mb */
  454. return false; /* too large chunk */
  455. char * buf = new char[len];
  456. in.read (buf, len);
  457. out.write (buf, len);
  458. delete[] buf;
  459. std::getline (in, hexLen); // read \r\n after chunk
  460. }
  461. return true;
  462. }
  463. } // http
  464. } // i2p