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 
   8 // Copyright:   (c) 1997, 1998 Guilhem Lavaux 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // For compilers that support precompilation, includes "wx.h". 
  13 #include "wx/wxprec.h" 
  19 #if wxUSE_PROTOCOL_HTTP 
  25     #include "wx/string.h" 
  29 #include "wx/tokenzr.h" 
  30 #include "wx/socket.h" 
  31 #include "wx/protocol/protocol.h" 
  33 #include "wx/protocol/http.h" 
  34 #include "wx/sckstrm.h" 
  35 #include "wx/thread.h" 
  37 IMPLEMENT_DYNAMIC_CLASS(wxHTTP
, wxProtocol
) 
  38 IMPLEMENT_PROTOCOL(wxHTTP
, wxT("http"), wxT("80"), true) 
  46     m_post_buf 
= wxEmptyString
; 
  49     SetNotify(wxSOCKET_LOST_FLAG
); 
  59 void wxHTTP::ClearHeaders() 
  64 wxString 
wxHTTP::GetContentType() 
  66     return GetHeader(wxT("Content-Type")); 
  69 void wxHTTP::SetProxyMode(bool on
) 
  74 wxHTTP::wxHeaderIterator 
wxHTTP::FindHeader(const wxString
& header
) 
  76     wxHeaderIterator it 
= m_headers
.begin(); 
  77     for ( wxHeaderIterator en 
= m_headers
.end(); it 
!= en
; ++it 
) 
  79         if ( header
.CmpNoCase(it
->first
) == 0 ) 
  86 wxHTTP::wxHeaderConstIterator 
wxHTTP::FindHeader(const wxString
& header
) const 
  88     wxHeaderConstIterator it 
= m_headers
.begin(); 
  89     for ( wxHeaderConstIterator en 
= m_headers
.end(); it 
!= en
; ++it 
) 
  91         if ( header
.CmpNoCase(it
->first
) == 0 ) 
  98 void wxHTTP::SetHeader(const wxString
& header
, const wxString
& h_data
) 
 105     wxHeaderIterator it 
= FindHeader(header
); 
 106     if (it 
!= m_headers
.end()) 
 109         m_headers
[header
] = h_data
; 
 112 wxString 
wxHTTP::GetHeader(const wxString
& header
) const 
 114     wxHeaderConstIterator it 
= FindHeader(header
); 
 116     return it 
== m_headers
.end() ? wxGetEmptyString() : it
->second
; 
 119 wxString 
wxHTTP::GenerateAuthString(const wxString
& user
, const wxString
& pass
) const 
 121     static const char *base64 
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 
 126     buf
.Printf(wxT("Basic ")); 
 128     toencode
.Printf(wxT("%s:%s"),user
.c_str(),pass
.c_str()); 
 130     size_t len 
= toencode
.length(); 
 131     const wxChar 
*from 
= toencode
.c_str(); 
 132     while (len 
>= 3) { // encode full blocks first 
 133         buf 
<< wxString::Format(wxT("%c%c"), base64
[(from
[0] >> 2) & 0x3f], base64
[((from
[0] << 4) & 0x30) | ((from
[1] >> 4) & 0xf)]); 
 134         buf 
<< wxString::Format(wxT("%c%c"), base64
[((from
[1] << 2) & 0x3c) | ((from
[2] >> 6) & 0x3)], base64
[from
[2] & 0x3f]); 
 138     if (len 
> 0) { // pad the remaining characters 
 139         buf 
<< wxString::Format(wxT("%c"), base64
[(from
[0] >> 2) & 0x3f]); 
 141             buf 
<< wxString::Format(wxT("%c="), base64
[(from
[0] << 4) & 0x30]); 
 143             buf 
<< wxString::Format(wxT("%c%c"), base64
[((from
[0] << 4) & 0x30) | ((from
[1] >> 4) & 0xf)], base64
[(from
[1] << 2) & 0x3c]); 
 151 void wxHTTP::SetPostBuffer(const wxString
& post_buf
) 
 153     m_post_buf 
= post_buf
; 
 156 void wxHTTP::SendHeaders() 
 158     typedef wxStringToStringHashMap::iterator iterator
; 
 161     for (iterator it 
= m_headers
.begin(), en 
= m_headers
.end(); it 
!= en
; ++it 
) 
 163         buf
.Printf(wxT("%s: %s\r\n"), it
->first
.c_str(), it
->second
.c_str()); 
 165         const wxWX2MBbuf cbuf 
= buf
.mb_str(); 
 166         Write(cbuf
, strlen(cbuf
)); 
 170 bool wxHTTP::ParseHeaders() 
 173     wxStringTokenizer tokenzr
; 
 180         m_perr 
= ReadLine(this, line
); 
 181         if (m_perr 
!= wxPROTO_NOERR
) 
 184         if (line
.length() == 0) 
 187         wxString left_str 
= line
.BeforeFirst(':'); 
 188         m_headers
[left_str
] = line
.AfterFirst(':').Strip(wxString::both
); 
 193 bool wxHTTP::Connect(const wxString
& host
, unsigned short port
) 
 203     m_addr 
= addr 
= new wxIPV4address(); 
 205     if (!addr
->Hostname(host
)) { 
 208         m_perr 
= wxPROTO_NETERR
; 
 214     else if (!addr
->Service(wxT("http"))) 
 217     SetHeader(wxT("Host"), host
); 
 222 bool wxHTTP::Connect(const wxSockAddress
& addr
, bool WXUNUSED(wait
)) 
 229     m_addr 
= addr
.Clone(); 
 231     wxIPV4address 
*ipv4addr 
= wxDynamicCast(&addr
, wxIPV4address
); 
 233         SetHeader(wxT("Host"), ipv4addr
->OrigHostname()); 
 238 bool wxHTTP::BuildRequest(const wxString
& path
, wxHTTP_Req req
) 
 240     const wxChar 
*request
; 
 245             request 
= wxT("GET"); 
 249             request 
= wxT("POST"); 
 250             if ( GetHeader( wxT("Content-Length") ).IsNull() ) 
 251                 SetHeader( wxT("Content-Length"), wxString::Format( wxT("%lu"), (unsigned long)m_post_buf
.Len() ) ); 
 260     // If there is no User-Agent defined, define it. 
 261     if (GetHeader(wxT("User-Agent")).IsNull()) 
 262         SetHeader(wxT("User-Agent"), wxT("wxWidgets 2.x")); 
 264     // Send authentication information 
 265     if (!m_username
.empty() || !m_password
.empty()) { 
 266         SetHeader(wxT("Authorization"), GenerateAuthString(m_username
, m_password
)); 
 271     // we may use non blocking sockets only if we can dispatch events from them 
 272     SetFlags( wxIsMainThread() && wxApp::IsMainLoopRunning() ? wxSOCKET_NONE
 
 277     buf
.Printf(wxT("%s %s HTTP/1.0\r\n"), request
, path
.c_str()); 
 278     const wxWX2MBbuf pathbuf 
= buf
.mb_str(); 
 279     Write(pathbuf
, strlen(pathbuf
)); 
 283     if ( req 
== wxHTTP_POST 
) { 
 284         Write(m_post_buf
.mbc_str(), m_post_buf
.Len()); 
 285         m_post_buf 
= wxEmptyString
; 
 289     m_perr 
= ReadLine(this, tmp_str
); 
 290     if (m_perr 
!= wxPROTO_NOERR
) { 
 295     if (!tmp_str
.Contains(wxT("HTTP/"))) { 
 296         // TODO: support HTTP v0.9 which can have no header. 
 297         // FIXME: tmp_str is not put back in the in-queue of the socket. 
 298         SetHeader(wxT("Content-Length"), wxT("-1")); 
 299         SetHeader(wxT("Content-Type"), wxT("none/none")); 
 304     wxStringTokenizer 
token(tmp_str
,wxT(' ')); 
 309     tmp_str2 
= token
.NextToken(); 
 311     m_http_response 
= wxAtoi(tmp_str2
); 
 313     switch ( tmp_str2
[0u].GetValue() ) 
 316             /* INFORMATION / SUCCESS */ 
 328             m_perr 
= wxPROTO_NOFILE
; 
 333     ret_value 
= ParseHeaders(); 
 338 class wxHTTPStream 
: public wxSocketInputStream
 
 343     unsigned long m_read_bytes
; 
 345     wxHTTPStream(wxHTTP 
*http
) : wxSocketInputStream(*http
), m_http(http
) {} 
 346     size_t GetSize() const { return m_httpsize
; } 
 347     virtual ~wxHTTPStream(void) { m_http
->Abort(); } 
 350     size_t OnSysRead(void *buffer
, size_t bufsize
); 
 352     DECLARE_NO_COPY_CLASS(wxHTTPStream
) 
 355 size_t wxHTTPStream::OnSysRead(void *buffer
, size_t bufsize
) 
 357     if (m_httpsize 
> 0 && m_read_bytes 
>= m_httpsize
) 
 359         m_lasterror 
= wxSTREAM_EOF
; 
 363     size_t ret 
= wxSocketInputStream::OnSysRead(buffer
, bufsize
); 
 366     if (m_httpsize
==(size_t)-1 && m_lasterror 
== wxSTREAM_READ_ERROR 
) 
 368         // if m_httpsize is (size_t) -1 this means read until connection closed 
 369         // which is equivalent to getting a READ_ERROR, for clients however this 
 370         // must be translated into EOF, as it is the expected way of signalling 
 371         // end end of the content 
 372         m_lasterror 
= wxSTREAM_EOF 
; 
 378 bool wxHTTP::Abort(void) 
 380     return wxSocketClient::Close(); 
 383 wxInputStream 
*wxHTTP::GetInputStream(const wxString
& path
) 
 385     wxHTTPStream 
*inp_stream
; 
 389     m_perr 
= wxPROTO_CONNERR
; 
 393     // We set m_connected back to false so wxSocketBase will know what to do. 
 395     wxSocketClient::Connect(*m_addr 
, false ); 
 396     wxSocketClient::WaitOnConnect(10); 
 398     if (!wxSocketClient::IsConnected()) 
 401     if (!wxProtocol::Connect(*m_addr
)) 
 405     if (!BuildRequest(path
, m_post_buf
.empty() ? wxHTTP_GET 
: wxHTTP_POST
)) 
 408     inp_stream 
= new wxHTTPStream(this); 
 410     if (!GetHeader(wxT("Content-Length")).empty()) 
 411         inp_stream
->m_httpsize 
= wxAtoi(GetHeader(wxT("Content-Length"))); 
 413         inp_stream
->m_httpsize 
= (size_t)-1; 
 415     inp_stream
->m_read_bytes 
= 0; 
 418     SetFlags(wxSOCKET_BLOCK 
| wxSOCKET_WAITALL
); 
 423 #endif // wxUSE_PROTOCOL_HTTP