PageRenderTime 48ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/Externals/wxWidgets3/src/common/http.cpp

https://gitlab.com/WoomyNightClub/dolphin
C++ | 551 lines | 397 code | 110 blank | 44 comment | 70 complexity | 0a44ce1133e694ae2685f5f4e45b885f MD5 | raw file
  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name: src/common/http.cpp
  3. // Purpose: HTTP protocol
  4. // Author: Guilhem Lavaux
  5. // Modified by: Simo Virokannas (authentication, Dec 2005)
  6. // Created: August 1997
  7. // Copyright: (c) 1997, 1998 Guilhem Lavaux
  8. // Licence: wxWindows licence
  9. /////////////////////////////////////////////////////////////////////////////
  10. // For compilers that support precompilation, includes "wx.h".
  11. #include "wx/wxprec.h"
  12. #ifdef __BORLANDC__
  13. #pragma hdrstop
  14. #endif
  15. #if wxUSE_PROTOCOL_HTTP
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #ifndef WX_PRECOMP
  19. #include "wx/string.h"
  20. #include "wx/app.h"
  21. #endif
  22. #include "wx/tokenzr.h"
  23. #include "wx/socket.h"
  24. #include "wx/protocol/protocol.h"
  25. #include "wx/url.h"
  26. #include "wx/protocol/http.h"
  27. #include "wx/sckstrm.h"
  28. #include "wx/thread.h"
  29. // ----------------------------------------------------------------------------
  30. // wxHTTP
  31. // ----------------------------------------------------------------------------
  32. IMPLEMENT_DYNAMIC_CLASS(wxHTTP, wxProtocol)
  33. IMPLEMENT_PROTOCOL(wxHTTP, wxT("http"), wxT("80"), true)
  34. wxHTTP::wxHTTP()
  35. : wxProtocol()
  36. {
  37. m_addr = NULL;
  38. m_read = false;
  39. m_proxy_mode = false;
  40. m_http_response = 0;
  41. SetNotify(wxSOCKET_LOST_FLAG);
  42. }
  43. wxHTTP::~wxHTTP()
  44. {
  45. ClearHeaders();
  46. delete m_addr;
  47. }
  48. void wxHTTP::ClearHeaders()
  49. {
  50. m_headers.clear();
  51. }
  52. void wxHTTP::ClearCookies()
  53. {
  54. m_cookies.clear();
  55. }
  56. wxString wxHTTP::GetContentType() const
  57. {
  58. return GetHeader(wxT("Content-Type"));
  59. }
  60. void wxHTTP::SetProxyMode(bool on)
  61. {
  62. m_proxy_mode = on;
  63. }
  64. wxHTTP::wxHeaderIterator wxHTTP::FindHeader(const wxString& header)
  65. {
  66. wxHeaderIterator it = m_headers.begin();
  67. for ( wxHeaderIterator en = m_headers.end(); it != en; ++it )
  68. {
  69. if ( header.CmpNoCase(it->first) == 0 )
  70. break;
  71. }
  72. return it;
  73. }
  74. wxHTTP::wxHeaderConstIterator wxHTTP::FindHeader(const wxString& header) const
  75. {
  76. wxHeaderConstIterator it = m_headers.begin();
  77. for ( wxHeaderConstIterator en = m_headers.end(); it != en; ++it )
  78. {
  79. if ( header.CmpNoCase(it->first) == 0 )
  80. break;
  81. }
  82. return it;
  83. }
  84. wxHTTP::wxCookieIterator wxHTTP::FindCookie(const wxString& cookie)
  85. {
  86. wxCookieIterator it = m_cookies.begin();
  87. for ( wxCookieIterator en = m_cookies.end(); it != en; ++it )
  88. {
  89. if ( cookie.CmpNoCase(it->first) == 0 )
  90. break;
  91. }
  92. return it;
  93. }
  94. wxHTTP::wxCookieConstIterator wxHTTP::FindCookie(const wxString& cookie) const
  95. {
  96. wxCookieConstIterator it = m_cookies.begin();
  97. for ( wxCookieConstIterator en = m_cookies.end(); it != en; ++it )
  98. {
  99. if ( cookie.CmpNoCase(it->first) == 0 )
  100. break;
  101. }
  102. return it;
  103. }
  104. void wxHTTP::SetHeader(const wxString& header, const wxString& h_data)
  105. {
  106. if (m_read) {
  107. ClearHeaders();
  108. m_read = false;
  109. }
  110. wxHeaderIterator it = FindHeader(header);
  111. if (it != m_headers.end())
  112. it->second = h_data;
  113. else
  114. m_headers[header] = h_data;
  115. }
  116. wxString wxHTTP::GetHeader(const wxString& header) const
  117. {
  118. wxHeaderConstIterator it = FindHeader(header);
  119. return it == m_headers.end() ? wxGetEmptyString() : it->second;
  120. }
  121. wxString wxHTTP::GetCookie(const wxString& cookie) const
  122. {
  123. wxCookieConstIterator it = FindCookie(cookie);
  124. return it == m_cookies.end() ? wxGetEmptyString() : it->second;
  125. }
  126. wxString wxHTTP::GenerateAuthString(const wxString& user, const wxString& pass) const
  127. {
  128. // TODO: Use wxBase64Encode() now that we have it instead of reproducing it
  129. static const char *base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  130. wxString buf;
  131. wxString toencode;
  132. buf.Printf(wxT("Basic "));
  133. toencode.Printf(wxT("%s:%s"),user.c_str(),pass.c_str());
  134. size_t len = toencode.length();
  135. const wxChar *from = toencode.c_str();
  136. while (len >= 3) { // encode full blocks first
  137. buf << wxString::Format(wxT("%c%c"), base64[(from[0] >> 2) & 0x3f], base64[((from[0] << 4) & 0x30) | ((from[1] >> 4) & 0xf)]);
  138. buf << wxString::Format(wxT("%c%c"), base64[((from[1] << 2) & 0x3c) | ((from[2] >> 6) & 0x3)], base64[from[2] & 0x3f]);
  139. from += 3;
  140. len -= 3;
  141. }
  142. if (len > 0) { // pad the remaining characters
  143. buf << wxString::Format(wxT("%c"), base64[(from[0] >> 2) & 0x3f]);
  144. if (len == 1) {
  145. buf << wxString::Format(wxT("%c="), base64[(from[0] << 4) & 0x30]);
  146. } else {
  147. buf << wxString::Format(wxT("%c%c"), base64[((from[0] << 4) & 0x30) | ((from[1] >> 4) & 0xf)], base64[(from[1] << 2) & 0x3c]);
  148. }
  149. buf << wxT("=");
  150. }
  151. return buf;
  152. }
  153. void wxHTTP::SetPostBuffer(const wxString& post_buf)
  154. {
  155. // Use To8BitData() for backwards compatibility in this deprecated method.
  156. // The new code should use the other overload or SetPostText() and specify
  157. // the encoding to use for the text explicitly.
  158. wxScopedCharBuffer scb = post_buf.To8BitData();
  159. if ( scb.length() )
  160. {
  161. m_postBuffer.Clear();
  162. m_postBuffer.AppendData(scb.data(), scb.length());
  163. }
  164. }
  165. bool
  166. wxHTTP::SetPostBuffer(const wxString& contentType,
  167. const wxMemoryBuffer& data)
  168. {
  169. m_postBuffer = data;
  170. m_contentType = contentType;
  171. return !m_postBuffer.IsEmpty();
  172. }
  173. bool
  174. wxHTTP::SetPostText(const wxString& contentType,
  175. const wxString& data,
  176. const wxMBConv& conv)
  177. {
  178. #if wxUSE_UNICODE
  179. wxScopedCharBuffer scb = data.mb_str(conv);
  180. const size_t len = scb.length();
  181. const char* const buf = scb.data();
  182. #else // !wxUSE_UNICODE
  183. const size_t len = data.length();
  184. const char* const buf = data.mb_str(conv);
  185. #endif // wxUSE_UNICODE/!wxUSE_UNICODE
  186. if ( !len )
  187. return false;
  188. m_postBuffer.Clear();
  189. m_postBuffer.AppendData(buf, len);
  190. m_contentType = contentType;
  191. return true;
  192. }
  193. void wxHTTP::SendHeaders()
  194. {
  195. typedef wxStringToStringHashMap::iterator iterator;
  196. wxString buf;
  197. for (iterator it = m_headers.begin(), en = m_headers.end(); it != en; ++it )
  198. {
  199. buf.Printf(wxT("%s: %s\r\n"), it->first.c_str(), it->second.c_str());
  200. const wxWX2MBbuf cbuf = buf.mb_str();
  201. Write(cbuf, strlen(cbuf));
  202. }
  203. }
  204. bool wxHTTP::ParseHeaders()
  205. {
  206. wxString line;
  207. wxStringTokenizer tokenzr;
  208. ClearHeaders();
  209. ClearCookies();
  210. m_read = true;
  211. for ( ;; )
  212. {
  213. m_lastError = ReadLine(this, line);
  214. if (m_lastError != wxPROTO_NOERR)
  215. return false;
  216. if ( line.empty() )
  217. break;
  218. wxString left_str = line.BeforeFirst(':');
  219. if(!left_str.CmpNoCase("Set-Cookie"))
  220. {
  221. wxString cookieName = line.AfterFirst(':').Strip(wxString::both).BeforeFirst('=');
  222. wxString cookieValue = line.AfterFirst(':').Strip(wxString::both).AfterFirst('=').BeforeFirst(';');
  223. m_cookies[cookieName] = cookieValue;
  224. // For compatibility
  225. m_headers[left_str] = line.AfterFirst(':').Strip(wxString::both);
  226. }
  227. else
  228. {
  229. m_headers[left_str] = line.AfterFirst(':').Strip(wxString::both);
  230. }
  231. }
  232. return true;
  233. }
  234. bool wxHTTP::Connect(const wxString& host, unsigned short port)
  235. {
  236. wxIPV4address *addr;
  237. if (m_addr) {
  238. wxDELETE(m_addr);
  239. Close();
  240. }
  241. m_addr = addr = new wxIPV4address();
  242. if (!addr->Hostname(host)) {
  243. wxDELETE(m_addr);
  244. m_lastError = wxPROTO_NETERR;
  245. return false;
  246. }
  247. if ( port )
  248. addr->Service(port);
  249. else if (!addr->Service(wxT("http")))
  250. addr->Service(80);
  251. wxString hostHdr = host;
  252. if ( port && port != 80 )
  253. hostHdr << wxT(":") << port;
  254. SetHeader(wxT("Host"), hostHdr);
  255. m_lastError = wxPROTO_NOERR;
  256. return true;
  257. }
  258. bool wxHTTP::Connect(const wxSockAddress& addr, bool WXUNUSED(wait))
  259. {
  260. if (m_addr) {
  261. delete m_addr;
  262. Close();
  263. }
  264. m_addr = addr.Clone();
  265. wxIPV4address *ipv4addr = wxDynamicCast(&addr, wxIPV4address);
  266. if ( ipv4addr )
  267. {
  268. wxString hostHdr = ipv4addr->OrigHostname();
  269. unsigned short port = ipv4addr->Service();
  270. if ( port && port != 80 )
  271. hostHdr << wxT(":") << port;
  272. SetHeader(wxT("Host"), hostHdr);
  273. }
  274. m_lastError = wxPROTO_NOERR;
  275. return true;
  276. }
  277. bool wxHTTP::BuildRequest(const wxString& path, const wxString& method)
  278. {
  279. // Use the data in the post buffer, if any.
  280. if ( !m_postBuffer.IsEmpty() )
  281. {
  282. wxString len;
  283. len << m_postBuffer.GetDataLen();
  284. // Content length must be correct, so always set, possibly
  285. // overriding the value set explicitly by a previous call to
  286. // SetHeader("Content-Length").
  287. SetHeader(wxS("Content-Length"), len);
  288. // However if the user had explicitly set the content type, don't
  289. // override it with the content type passed to SetPostText().
  290. if ( !m_contentType.empty() && GetContentType().empty() )
  291. SetHeader(wxS("Content-Type"), m_contentType);
  292. }
  293. m_http_response = 0;
  294. // If there is no User-Agent defined, define it.
  295. if ( GetHeader(wxT("User-Agent")).empty() )
  296. SetHeader(wxT("User-Agent"), wxT("wxWidgets 2.x"));
  297. // Send authentication information
  298. if (!m_username.empty() || !m_password.empty()) {
  299. SetHeader(wxT("Authorization"), GenerateAuthString(m_username, m_password));
  300. }
  301. SaveState();
  302. // we may use non blocking sockets only if we can dispatch events from them
  303. int flags = wxIsMainThread() && wxApp::IsMainLoopRunning() ? wxSOCKET_NONE
  304. : wxSOCKET_BLOCK;
  305. // and we must use wxSOCKET_WAITALL to ensure that all data is sent
  306. flags |= wxSOCKET_WAITALL;
  307. SetFlags(flags);
  308. Notify(false);
  309. wxString buf;
  310. buf.Printf(wxT("%s %s HTTP/1.0\r\n"), method, path);
  311. const wxWX2MBbuf pathbuf = buf.mb_str();
  312. Write(pathbuf, strlen(pathbuf));
  313. SendHeaders();
  314. Write("\r\n", 2);
  315. if ( !m_postBuffer.IsEmpty() ) {
  316. Write(m_postBuffer.GetData(), m_postBuffer.GetDataLen());
  317. m_postBuffer.Clear();
  318. }
  319. wxString tmp_str;
  320. m_lastError = ReadLine(this, tmp_str);
  321. if (m_lastError != wxPROTO_NOERR) {
  322. RestoreState();
  323. return false;
  324. }
  325. if (!tmp_str.Contains(wxT("HTTP/"))) {
  326. // TODO: support HTTP v0.9 which can have no header.
  327. // FIXME: tmp_str is not put back in the in-queue of the socket.
  328. m_lastError = wxPROTO_NOERR;
  329. SetHeader(wxT("Content-Length"), wxT("-1"));
  330. SetHeader(wxT("Content-Type"), wxT("none/none"));
  331. RestoreState();
  332. return true;
  333. }
  334. wxStringTokenizer token(tmp_str,wxT(' '));
  335. wxString tmp_str2;
  336. bool ret_value;
  337. token.NextToken();
  338. tmp_str2 = token.NextToken();
  339. m_http_response = wxAtoi(tmp_str2);
  340. switch ( tmp_str2[0u].GetValue() )
  341. {
  342. case wxT('1'):
  343. /* INFORMATION / SUCCESS */
  344. break;
  345. case wxT('2'):
  346. /* SUCCESS */
  347. break;
  348. case wxT('3'):
  349. /* REDIRECTION */
  350. break;
  351. default:
  352. m_lastError = wxPROTO_NOFILE;
  353. RestoreState();
  354. return false;
  355. }
  356. m_lastError = wxPROTO_NOERR;
  357. ret_value = ParseHeaders();
  358. RestoreState();
  359. return ret_value;
  360. }
  361. bool wxHTTP::Abort(void)
  362. {
  363. return wxSocketClient::Close();
  364. }
  365. // ----------------------------------------------------------------------------
  366. // wxHTTPStream and wxHTTP::GetInputStream
  367. // ----------------------------------------------------------------------------
  368. class wxHTTPStream : public wxSocketInputStream
  369. {
  370. public:
  371. wxHTTP *m_http;
  372. size_t m_httpsize;
  373. unsigned long m_read_bytes;
  374. wxHTTPStream(wxHTTP *http) : wxSocketInputStream(*http)
  375. {
  376. m_http = http;
  377. m_httpsize = 0;
  378. m_read_bytes = 0;
  379. }
  380. size_t GetSize() const { return m_httpsize; }
  381. virtual ~wxHTTPStream(void) { m_http->Abort(); }
  382. protected:
  383. size_t OnSysRead(void *buffer, size_t bufsize);
  384. wxDECLARE_NO_COPY_CLASS(wxHTTPStream);
  385. };
  386. size_t wxHTTPStream::OnSysRead(void *buffer, size_t bufsize)
  387. {
  388. if (m_read_bytes >= m_httpsize)
  389. {
  390. m_lasterror = wxSTREAM_EOF;
  391. return 0;
  392. }
  393. size_t ret = wxSocketInputStream::OnSysRead(buffer, bufsize);
  394. m_read_bytes += ret;
  395. if (m_httpsize==(size_t)-1 && m_lasterror == wxSTREAM_READ_ERROR )
  396. {
  397. // if m_httpsize is (size_t) -1 this means read until connection closed
  398. // which is equivalent to getting a READ_ERROR, for clients however this
  399. // must be translated into EOF, as it is the expected way of signalling
  400. // end end of the content
  401. m_lasterror = wxSTREAM_EOF;
  402. }
  403. return ret;
  404. }
  405. wxInputStream *wxHTTP::GetInputStream(const wxString& path)
  406. {
  407. wxHTTPStream *inp_stream;
  408. wxString new_path;
  409. m_lastError = wxPROTO_CONNERR; // all following returns share this type of error
  410. if (!m_addr)
  411. return NULL;
  412. // We set m_connected back to false so wxSocketBase will know what to do.
  413. #ifdef __WXMAC__
  414. wxSocketClient::Connect(*m_addr , false );
  415. wxSocketClient::WaitOnConnect(10);
  416. if (!wxSocketClient::IsConnected())
  417. return NULL;
  418. #else
  419. if (!wxProtocol::Connect(*m_addr))
  420. return NULL;
  421. #endif
  422. // Use the user-specified method if any or determine the method to use
  423. // automatically depending on whether we have anything to post or not.
  424. wxString method = m_method;
  425. if (method.empty())
  426. method = m_postBuffer.IsEmpty() ? wxS("GET"): wxS("POST");
  427. if (!BuildRequest(path, method))
  428. return NULL;
  429. inp_stream = new wxHTTPStream(this);
  430. if (!GetHeader(wxT("Content-Length")).empty())
  431. inp_stream->m_httpsize = wxAtoi(GetHeader(wxT("Content-Length")));
  432. else
  433. inp_stream->m_httpsize = (size_t)-1;
  434. inp_stream->m_read_bytes = 0;
  435. Notify(false);
  436. SetFlags(wxSOCKET_BLOCK | wxSOCKET_WAITALL);
  437. // no error; reset m_lastError
  438. m_lastError = wxPROTO_NOERR;
  439. return inp_stream;
  440. }
  441. #endif // wxUSE_PROTOCOL_HTTP