added missing include for non-PCH compilation
[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() && (wxTheApp && wxTheApp->IsMainLoopRunning())
243 ? wxSOCKET_NONE
244 : wxSOCKET_BLOCK );
245 Notify(false);
246
247 wxString buf;
248 buf.Printf(wxT("%s %s HTTP/1.0\r\n"), request, path.c_str());
249 const wxWX2MBbuf pathbuf = wxConvLocal.cWX2MB(buf);
250 Write(pathbuf, strlen(wxMBSTRINGCAST pathbuf));
251 SendHeaders();
252 Write("\r\n", 2);
253
254 if ( req == wxHTTP_POST ) {
255 Write(m_post_buf.mbc_str(), m_post_buf.Len());
256 m_post_buf = wxEmptyString;
257 }
258
259 wxString tmp_str;
260 m_perr = GetLine(this, tmp_str);
261 if (m_perr != wxPROTO_NOERR) {
262 RestoreState();
263 return false;
264 }
265
266 if (!tmp_str.Contains(wxT("HTTP/"))) {
267 // TODO: support HTTP v0.9 which can have no header.
268 // FIXME: tmp_str is not put back in the in-queue of the socket.
269 SetHeader(wxT("Content-Length"), wxT("-1"));
270 SetHeader(wxT("Content-Type"), wxT("none/none"));
271 RestoreState();
272 return true;
273 }
274
275 wxStringTokenizer token(tmp_str,wxT(' '));
276 wxString tmp_str2;
277 bool ret_value;
278
279 token.NextToken();
280 tmp_str2 = token.NextToken();
281
282 m_http_response = wxAtoi(tmp_str2);
283
284 switch (tmp_str2[0u]) {
285 case wxT('1'):
286 /* INFORMATION / SUCCESS */
287 break;
288 case wxT('2'):
289 /* SUCCESS */
290 break;
291 case wxT('3'):
292 /* REDIRECTION */
293 break;
294 default:
295 m_perr = wxPROTO_NOFILE;
296 RestoreState();
297 return false;
298 }
299
300 ret_value = ParseHeaders();
301 RestoreState();
302 return ret_value;
303 }
304
305 class wxHTTPStream : public wxSocketInputStream
306 {
307 public:
308 wxHTTP *m_http;
309 size_t m_httpsize;
310 unsigned long m_read_bytes;
311
312 wxHTTPStream(wxHTTP *http) : wxSocketInputStream(*http), m_http(http) {}
313 size_t GetSize() const { return m_httpsize; }
314 virtual ~wxHTTPStream(void) { m_http->Abort(); }
315
316 protected:
317 size_t OnSysRead(void *buffer, size_t bufsize);
318
319 DECLARE_NO_COPY_CLASS(wxHTTPStream)
320 };
321
322 size_t wxHTTPStream::OnSysRead(void *buffer, size_t bufsize)
323 {
324 if (m_httpsize > 0 && m_read_bytes >= m_httpsize)
325 {
326 m_lasterror = wxSTREAM_EOF;
327 return 0;
328 }
329
330 size_t ret = wxSocketInputStream::OnSysRead(buffer, bufsize);
331 m_read_bytes += ret;
332
333 return ret;
334 }
335
336 bool wxHTTP::Abort(void)
337 {
338 return wxSocketClient::Close();
339 }
340
341 wxInputStream *wxHTTP::GetInputStream(const wxString& path)
342 {
343 wxHTTPStream *inp_stream;
344
345 wxString new_path;
346
347 m_perr = wxPROTO_CONNERR;
348 if (!m_addr)
349 return NULL;
350
351 // We set m_connected back to false so wxSocketBase will know what to do.
352 #ifdef __WXMAC__
353 wxSocketClient::Connect(*m_addr , false );
354 wxSocketClient::WaitOnConnect(10);
355
356 if (!wxSocketClient::IsConnected())
357 return NULL;
358 #else
359 if (!wxProtocol::Connect(*m_addr))
360 return NULL;
361 #endif
362
363 if (!BuildRequest(path, m_post_buf.IsEmpty() ? wxHTTP_GET : wxHTTP_POST))
364 return NULL;
365
366 inp_stream = new wxHTTPStream(this);
367
368 if (!GetHeader(wxT("Content-Length")).IsEmpty())
369 inp_stream->m_httpsize = wxAtoi(WXSTRINGCAST GetHeader(wxT("Content-Length")));
370 else
371 inp_stream->m_httpsize = (size_t)-1;
372
373 inp_stream->m_read_bytes = 0;
374
375 Notify(false);
376 SetFlags(wxSOCKET_BLOCK | wxSOCKET_WAITALL);
377
378 return inp_stream;
379 }
380
381 #endif // wxUSE_PROTOCOL_HTTP
382