Files
wxWidgets/src/common/http.cpp
Robin Dunn ce22d615fe Fix so the Host: header in wxHTTP really works. With virtual hosts it
is vital that the original host name given in the URL is used in the
Host: header so the sever will use the right vhost config.  Previously
it was using the value returned from gethostbyaddr which will often
not be the same as the hostname in the URL.


git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@14332 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2002-02-21 00:01:32 +00:00

352 lines
6.8 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: http.cpp
// Purpose: HTTP protocol
// Author: Guilhem Lavaux
// Modified by:
// Created: August 1997
// RCS-ID: $Id$
// Copyright: (c) 1997, 1998 Guilhem Lavaux
// Licence: wxWindows license
/////////////////////////////////////////////////////////////////////////////
#ifdef __GNUG__
#pragma implementation "http.h"
#endif
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_PROTOCOL_HTTP
#include <stdio.h>
#include <stdlib.h>
#include "wx/string.h"
#include "wx/tokenzr.h"
#include "wx/socket.h"
#include "wx/protocol/protocol.h"
#include "wx/url.h"
#include "wx/protocol/http.h"
#include "wx/sckstrm.h"
IMPLEMENT_DYNAMIC_CLASS(wxHTTP, wxProtocol)
IMPLEMENT_PROTOCOL(wxHTTP, wxT("http"), wxT("80"), TRUE)
#define HTTP_BSIZE 2048
wxHTTP::wxHTTP()
: wxProtocol(),
m_headers(wxKEY_STRING)
{
m_addr = NULL;
m_read = FALSE;
m_proxy_mode = FALSE;
SetNotify(wxSOCKET_LOST_FLAG);
}
wxHTTP::~wxHTTP()
{
ClearHeaders();
delete m_addr;
}
void wxHTTP::ClearHeaders()
{
// wxString isn't a wxObject
wxNode *node = m_headers.First();
wxString *string;
while (node) {
string = (wxString *)node->Data();
delete string;
node = node->Next();
}
m_headers.Clear();
}
wxString wxHTTP::GetContentType()
{
return GetHeader(wxT("Content-Type"));
}
void wxHTTP::SetProxyMode(bool on)
{
m_proxy_mode = on;
}
void wxHTTP::SetHeader(const wxString& header, const wxString& h_data)
{
if (m_read) {
ClearHeaders();
m_read = FALSE;
}
wxNode *node = m_headers.Find(header);
if (!node)
m_headers.Append(header, (wxObject *)(new wxString(h_data)));
else {
wxString *str = (wxString *)node->Data();
(*str) = h_data;
}
}
wxString wxHTTP::GetHeader(const wxString& header)
{
wxNode *node;
wxString upper_header;
upper_header = header.Upper();
node = m_headers.Find(upper_header);
if (!node)
return wxEmptyString;
return *((wxString *)node->Data());
}
void wxHTTP::SendHeaders()
{
wxNode *head = m_headers.First();
while (head)
{
wxString *str = (wxString *)head->Data();
wxString buf;
buf.Printf(wxT("%s: %s\r\n"), head->GetKeyString(), str->GetData());
const wxWX2MBbuf cbuf = buf.mb_str();
Write(cbuf, strlen(cbuf));
head = head->Next();
}
}
bool wxHTTP::ParseHeaders()
{
wxString line;
wxStringTokenizer tokenzr;
ClearHeaders();
m_read = TRUE;
#if defined(__VISAGECPP__)
// VA just can't stand while(1)
bool bOs2var = TRUE;
while(bOs2var) {
#else
while (1) {
#endif
m_perr = GetLine(this, line);
if (m_perr != wxPROTO_NOERR)
return FALSE;
if (line.Length() == 0)
break;
wxString left_str = line.BeforeFirst(':');
wxString *str = new wxString(line.AfterFirst(':').Strip(wxString::both));
left_str.MakeUpper();
m_headers.Append(left_str, (wxObject *) str);
}
return TRUE;
}
bool wxHTTP::Connect(const wxString& host)
{
wxIPV4address *addr;
if (m_addr) {
delete m_addr;
m_addr = NULL;
Close();
}
m_addr = addr = new wxIPV4address();
if (!addr->Hostname(host)) {
delete m_addr;
m_addr = NULL;
m_perr = wxPROTO_NETERR;
return FALSE;
}
if (!addr->Service(wxT("http")))
addr->Service(80);
SetHeader(wxT("Host"), host);
return TRUE;
}
bool wxHTTP::Connect(wxSockAddress& addr, bool WXUNUSED(wait))
{
if (m_addr) {
delete m_addr;
Close();
}
m_addr = addr.Clone();
wxIPV4address *ipv4addr = wxDynamicCast(&addr, wxIPV4address);
if (ipv4addr)
SetHeader(wxT("Host"), ipv4addr->OrigHostname());
return TRUE;
}
bool wxHTTP::BuildRequest(const wxString& path, wxHTTP_Req req)
{
const wxChar *request;
switch (req) {
case wxHTTP_GET:
request = wxT("GET");
break;
default:
return FALSE;
}
// If there is no User-Agent defined, define it.
if (GetHeader(wxT("User-Agent")).IsNull())
SetHeader(wxT("User-Agent"), wxT("wxWindows 2.x"));
SaveState();
SetFlags(wxSOCKET_NONE);
Notify(FALSE);
wxString buf;
buf.Printf(wxT("%s %s HTTP/1.0\r\n"), request, path.c_str());
const wxWX2MBbuf pathbuf = wxConvLibc.cWX2MB(buf);
Write(pathbuf, strlen(wxMBSTRINGCAST pathbuf));
SendHeaders();
Write("\r\n", 2);
wxString tmp_str;
m_perr = GetLine(this, tmp_str);
if (m_perr != wxPROTO_NOERR) {
RestoreState();
return FALSE;
}
if (!tmp_str.Contains(wxT("HTTP/"))) {
// TODO: support HTTP v0.9 which can have no header.
// FIXME: tmp_str is not put back in the in-queue of the socket.
SetHeader(wxT("Content-Length"), wxT("-1"));
SetHeader(wxT("Content-Type"), wxT("none/none"));
RestoreState();
return TRUE;
}
wxStringTokenizer token(tmp_str,wxT(' '));
wxString tmp_str2;
bool ret_value;
token.NextToken();
tmp_str2 = token.NextToken();
switch (tmp_str2[0u]) {
case wxT('1'):
/* INFORMATION / SUCCESS */
break;
case wxT('2'):
/* SUCCESS */
break;
case wxT('3'):
/* REDIRECTION */
break;
default:
m_perr = wxPROTO_NOFILE;
RestoreState();
return FALSE;
}
ret_value = ParseHeaders();
RestoreState();
return ret_value;
}
class wxHTTPStream : public wxSocketInputStream
{
public:
wxHTTP *m_http;
size_t m_httpsize;
unsigned long m_read_bytes;
wxHTTPStream(wxHTTP *http) : wxSocketInputStream(*http), m_http(http) {}
size_t GetSize() const { return m_httpsize; }
virtual ~wxHTTPStream(void) { m_http->Abort(); }
protected:
size_t OnSysRead(void *buffer, size_t bufsize);
};
size_t wxHTTPStream::OnSysRead(void *buffer, size_t bufsize)
{
size_t ret;
if (m_httpsize > 0 && m_read_bytes >= m_httpsize)
return 0;
ret = wxSocketInputStream::OnSysRead(buffer, bufsize);
m_read_bytes += ret;
return ret;
}
bool wxHTTP::Abort(void)
{
return wxSocketClient::Close();
}
wxInputStream *wxHTTP::GetInputStream(const wxString& path)
{
wxHTTPStream *inp_stream;
wxString new_path;
m_perr = wxPROTO_CONNERR;
if (!m_addr)
return NULL;
// We set m_connected back to FALSE so wxSocketBase will know what to do.
#ifdef __WXMAC__
wxSocketClient::Connect(*m_addr , FALSE );
wxSocketClient::WaitOnConnect(10);
if (!wxSocketClient::IsConnected())
return NULL;
#else
if (!wxProtocol::Connect(*m_addr))
return NULL;
#endif
if (!BuildRequest(path, wxHTTP_GET))
return NULL;
inp_stream = new wxHTTPStream(this);
if (!GetHeader(wxT("Content-Length")).IsEmpty())
inp_stream->m_httpsize = wxAtoi(WXSTRINGCAST GetHeader(wxT("Content-Length")));
else
inp_stream->m_httpsize = (size_t)-1;
inp_stream->m_read_bytes = 0;
Notify(FALSE);
SetFlags(wxSOCKET_BLOCK | wxSOCKET_WAITALL);
return inp_stream;
}
#endif // wxUSE_PROTOCOL_HTTP