made IsMainLoopRunning() static and implemented it for wxAppConsole too
[wxWidgets.git] / src / common / http.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: http.cpp
3 // Purpose: HTTP protocol
4 // Author: Guilhem Lavaux
5 // Modified by:
6 // Created: August 1997
7 // RCS-ID: $Id$
8 // Copyright: (c) 1997, 1998 Guilhem Lavaux
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "http.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #if wxUSE_PROTOCOL_HTTP
24
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #ifndef WX_PRECOMP
29 #include "wx/string.h"
30 #include "wx/app.h"
31 #endif
32
33 #include "wx/tokenzr.h"
34 #include "wx/socket.h"
35 #include "wx/protocol/protocol.h"
36 #include "wx/url.h"
37 #include "wx/protocol/http.h"
38 #include "wx/sckstrm.h"
39
40 IMPLEMENT_DYNAMIC_CLASS(wxHTTP, wxProtocol)
41 IMPLEMENT_PROTOCOL(wxHTTP, wxT("http"), wxT("80"), true)
42
43 #define HTTP_BSIZE 2048
44
45 wxHTTP::wxHTTP()
46 : wxProtocol()
47 {
48 m_addr = NULL;
49 m_read = false;
50 m_proxy_mode = false;
51 m_post_buf = wxEmptyString;
52 m_http_response = 0;
53
54 SetNotify(wxSOCKET_LOST_FLAG);
55 }
56
57 wxHTTP::~wxHTTP()
58 {
59 ClearHeaders();
60
61 delete m_addr;
62 }
63
64 void wxHTTP::ClearHeaders()
65 {
66 m_headers.clear();
67 }
68
69 wxString wxHTTP::GetContentType()
70 {
71 return GetHeader(wxT("Content-Type"));
72 }
73
74 void wxHTTP::SetProxyMode(bool on)
75 {
76 m_proxy_mode = on;
77 }
78
79 wxHTTP::wxHeaderIterator wxHTTP::FindHeader(const wxString& header)
80 {
81 wxHeaderIterator it = m_headers.begin();
82 for ( wxHeaderIterator en = m_headers.end(); it != en; ++it )
83 {
84 if ( wxStricmp(it->first, header) == 0 )
85 break;
86 }
87
88 return it;
89 }
90
91 wxHTTP::wxHeaderConstIterator wxHTTP::FindHeader(const wxString& header) const
92 {
93 wxHeaderConstIterator it = m_headers.begin();
94 for ( wxHeaderConstIterator en = m_headers.end(); it != en; ++it )
95 {
96 if ( wxStricmp(it->first, header) == 0 )
97 break;
98 }
99
100 return it;
101 }
102
103 void wxHTTP::SetHeader(const wxString& header, const wxString& h_data)
104 {
105 if (m_read) {
106 ClearHeaders();
107 m_read = false;
108 }
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
117 wxString wxHTTP::GetHeader(const wxString& header) const
118 {
119 wxHeaderConstIterator it = FindHeader(header);
120
121 return it == m_headers.end() ? wxGetEmptyString() : it->second;
122 }
123
124 void wxHTTP::SetPostBuffer(const wxString& post_buf)
125 {
126 m_post_buf = post_buf;
127 }
128
129 void wxHTTP::SendHeaders()
130 {
131 typedef wxStringToStringHashMap::iterator iterator;
132 wxString buf;
133
134 for (iterator it = m_headers.begin(), en = m_headers.end(); it != en; ++it )
135 {
136 buf.Printf(wxT("%s: %s\r\n"), it->first.c_str(), it->second.c_str());
137
138 const wxWX2MBbuf cbuf = buf.mb_str();
139 Write(cbuf, strlen(cbuf));
140 }
141 }
142
143 bool wxHTTP::ParseHeaders()
144 {
145 wxString line;
146 wxStringTokenizer tokenzr;
147
148 ClearHeaders();
149 m_read = true;
150
151 #if defined(__VISAGECPP__)
152 // VA just can't stand while(1)
153 bool bOs2var = true;
154 while(bOs2var)
155 #else
156 while (1)
157 #endif
158 {
159 m_perr = GetLine(this, line);
160 if (m_perr != wxPROTO_NOERR)
161 return false;
162
163 if (line.Length() == 0)
164 break;
165
166 wxString left_str = line.BeforeFirst(':');
167 m_headers[left_str] = line.AfterFirst(':').Strip(wxString::both);
168 }
169 return true;
170 }
171
172 bool wxHTTP::Connect(const wxString& host, unsigned short port)
173 {
174 wxIPV4address *addr;
175
176 if (m_addr) {
177 delete m_addr;
178 m_addr = NULL;
179 Close();
180 }
181
182 m_addr = addr = new wxIPV4address();
183
184 if (!addr->Hostname(host)) {
185 delete m_addr;
186 m_addr = NULL;
187 m_perr = wxPROTO_NETERR;
188 return false;
189 }
190
191 if ( port ) addr->Service(port);
192 else if (!addr->Service(wxT("http")))
193 addr->Service(80);
194
195 SetHeader(wxT("Host"), host);
196
197 return true;
198 }
199
200 bool wxHTTP::Connect(wxSockAddress& addr, bool WXUNUSED(wait))
201 {
202 if (m_addr) {
203 delete m_addr;
204 Close();
205 }
206
207 m_addr = addr.Clone();
208
209 wxIPV4address *ipv4addr = wxDynamicCast(&addr, wxIPV4address);
210 if (ipv4addr)
211 SetHeader(wxT("Host"), ipv4addr->OrigHostname());
212
213 return true;
214 }
215
216 bool wxHTTP::BuildRequest(const wxString& path, wxHTTP_Req req)
217 {
218 const wxChar *request;
219
220 switch (req) {
221 case wxHTTP_GET:
222 request = wxT("GET");
223 break;
224 case wxHTTP_POST:
225 request = wxT("POST");
226 if ( GetHeader( wxT("Content-Length") ).IsNull() )
227 SetHeader( wxT("Content-Length"), wxString::Format( wxT("%lu"), (unsigned long)m_post_buf.Len() ) );
228 break;
229 default:
230 return false;
231 }
232
233 m_http_response = 0;
234
235 // If there is no User-Agent defined, define it.
236 if (GetHeader(wxT("User-Agent")).IsNull())
237 SetHeader(wxT("User-Agent"), wxT("wxWidgets 2.x"));
238
239 SaveState();
240
241 // we may use non blocking sockets only if we can dispatch events from them
242 SetFlags( wxIsMainThread() && wxApp::IsMainLoopRunning() ? wxSOCKET_NONE
243 : wxSOCKET_BLOCK );
244 Notify(false);
245
246 wxString buf;
247 buf.Printf(wxT("%s %s HTTP/1.0\r\n"), request, path.c_str());
248 const wxWX2MBbuf pathbuf = wxConvLocal.cWX2MB(buf);
249 Write(pathbuf, strlen(wxMBSTRINGCAST pathbuf));
250 SendHeaders();
251 Write("\r\n", 2);
252
253 if ( req == wxHTTP_POST ) {
254 Write(m_post_buf.mbc_str(), m_post_buf.Len());
255 m_post_buf = wxEmptyString;
256 }
257
258 wxString tmp_str;
259 m_perr = GetLine(this, tmp_str);
260 if (m_perr != wxPROTO_NOERR) {
261 RestoreState();
262 return false;
263 }
264
265 if (!tmp_str.Contains(wxT("HTTP/"))) {
266 // TODO: support HTTP v0.9 which can have no header.
267 // FIXME: tmp_str is not put back in the in-queue of the socket.
268 SetHeader(wxT("Content-Length"), wxT("-1"));
269 SetHeader(wxT("Content-Type"), wxT("none/none"));
270 RestoreState();
271 return true;
272 }
273
274 wxStringTokenizer token(tmp_str,wxT(' '));
275 wxString tmp_str2;
276 bool ret_value;
277
278 token.NextToken();
279 tmp_str2 = token.NextToken();
280
281 m_http_response = wxAtoi(tmp_str2);
282
283 switch (tmp_str2[0u]) {
284 case wxT('1'):
285 /* INFORMATION / SUCCESS */
286 break;
287 case wxT('2'):
288 /* SUCCESS */
289 break;
290 case wxT('3'):
291 /* REDIRECTION */
292 break;
293 default:
294 m_perr = wxPROTO_NOFILE;
295 RestoreState();
296 return false;
297 }
298
299 ret_value = ParseHeaders();
300 RestoreState();
301 return ret_value;
302 }
303
304 class wxHTTPStream : public wxSocketInputStream
305 {
306 public:
307 wxHTTP *m_http;
308 size_t m_httpsize;
309 unsigned long m_read_bytes;
310
311 wxHTTPStream(wxHTTP *http) : wxSocketInputStream(*http), m_http(http) {}
312 size_t GetSize() const { return m_httpsize; }
313 virtual ~wxHTTPStream(void) { m_http->Abort(); }
314
315 protected:
316 size_t OnSysRead(void *buffer, size_t bufsize);
317
318 DECLARE_NO_COPY_CLASS(wxHTTPStream)
319 };
320
321 size_t wxHTTPStream::OnSysRead(void *buffer, size_t bufsize)
322 {
323 if (m_httpsize > 0 && m_read_bytes >= m_httpsize)
324 {
325 m_lasterror = wxSTREAM_EOF;
326 return 0;
327 }
328
329 size_t ret = wxSocketInputStream::OnSysRead(buffer, bufsize);
330 m_read_bytes += ret;
331
332 return ret;
333 }
334
335 bool wxHTTP::Abort(void)
336 {
337 return wxSocketClient::Close();
338 }
339
340 wxInputStream *wxHTTP::GetInputStream(const wxString& path)
341 {
342 wxHTTPStream *inp_stream;
343
344 wxString new_path;
345
346 m_perr = wxPROTO_CONNERR;
347 if (!m_addr)
348 return NULL;
349
350 // We set m_connected back to false so wxSocketBase will know what to do.
351 #ifdef __WXMAC__
352 wxSocketClient::Connect(*m_addr , false );
353 wxSocketClient::WaitOnConnect(10);
354
355 if (!wxSocketClient::IsConnected())
356 return NULL;
357 #else
358 if (!wxProtocol::Connect(*m_addr))
359 return NULL;
360 #endif
361
362 if (!BuildRequest(path, m_post_buf.IsEmpty() ? wxHTTP_GET : wxHTTP_POST))
363 return NULL;
364
365 inp_stream = new wxHTTPStream(this);
366
367 if (!GetHeader(wxT("Content-Length")).IsEmpty())
368 inp_stream->m_httpsize = wxAtoi(WXSTRINGCAST GetHeader(wxT("Content-Length")));
369 else
370 inp_stream->m_httpsize = (size_t)-1;
371
372 inp_stream->m_read_bytes = 0;
373
374 Notify(false);
375 SetFlags(wxSOCKET_BLOCK | wxSOCKET_WAITALL);
376
377 return inp_stream;
378 }
379
380 #endif // wxUSE_PROTOCOL_HTTP
381