Core: HttpRequest - fixed some specific redirection cases

git-svn-id: svn://ultimatepp.org/upp/trunk@4769 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2012-04-11 16:53:39 +00:00
parent 9af96b4c05
commit b9663a32d2
3 changed files with 1009 additions and 985 deletions

File diff suppressed because it is too large Load diff

View file

@ -293,7 +293,9 @@ bool TcpSocket::Open(int family, int type, int protocol)
bool TcpSocket::Listen(int port, int listen_count, bool ipv6_, bool reuse)
{
Close();
Init();
Reset();
ipv6 = ipv6_;
if(!Open(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0))
@ -337,6 +339,9 @@ bool TcpSocket::Listen(int port, int listen_count, bool ipv6_, bool reuse)
bool TcpSocket::Accept(TcpSocket& ls)
{
Close();
Init();
Reset();
if(timeout && !ls.WaitRead())
return false;
if(!Open(ls.ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0))
@ -418,6 +423,7 @@ bool TcpSocket::Connect(IpAddrInfo& info)
{
LLOG("TCP Connect addrinfo");
Init();
Reset();
addrinfo *result = info.GetResult();
return result && RawConnect(result);
}
@ -427,6 +433,7 @@ bool TcpSocket::Connect(const char *host, int port)
LLOG("TCP Connect(" << host << ':' << port << ')');
Init();
Reset();
IpAddrInfo info;
if(!info.Execute(host, port)) {
SetSockError(Format("getaddrinfo(%s) failed", host));
@ -456,7 +463,7 @@ void TcpSocket::RawClose()
void TcpSocket::Close()
{
if(ssl)
ssl->Close();
ssl->Close();
else
RawClose();
ssl.Clear();
@ -486,7 +493,7 @@ int TcpSocket::RawSend(const void *buf, int amount)
int TcpSocket::Send(const void *buf, int amount)
{
return ssl ? ssl->Send(buf, amount) : RawSend(buf, amount);
return ssl ? ssl->Send(buf, amount) : RawSend(buf, amount);
}
void TcpSocket::Shutdown()
@ -551,7 +558,7 @@ bool TcpSocket::RawWait(dword flags)
bool TcpSocket::Wait(dword flags)
{
return ssl ? ssl->Wait(flags) : RawWait(flags);
return ssl ? ssl->Wait(flags) : RawWait(flags);
}
int TcpSocket::Put(const char *s, int length)
@ -599,7 +606,7 @@ int TcpSocket::RawRecv(void *buf, int amount)
int TcpSocket::Recv(void *buffer, int maxlen)
{
return ssl ? ssl->Recv(buffer, maxlen) : RawRecv(buffer, maxlen);
return ssl ? ssl->Recv(buffer, maxlen) : RawRecv(buffer, maxlen);
}
void TcpSocket::ReadBuffer()
@ -685,31 +692,31 @@ String TcpSocket::GetLine(int maxlen)
}
}
void TcpSocket::SetSockError(const char *context, int code, const char *errdesc)
void TcpSocket::SetSockError(const char *context, int code, const char *errdesc)
{
errorcode = code;
errordesc.Clear();
errorcode = code;
errordesc.Clear();
if(socket != INVALID_SOCKET)
errordesc << "socket(" << (int)socket << ") / ";
errordesc << context << ": " << errdesc;
errordesc << "socket(" << (int)socket << ") / ";
errordesc << context << ": " << errdesc;
is_error = true;
}
void TcpSocket::SetSockError(const char *context, const char *errdesc)
{
SetSockError(context, GetErrorCode(), errdesc);
}
void TcpSocket::SetSockError(const char *context, const char *errdesc)
{
SetSockError(context, GetErrorCode(), errdesc);
}
void TcpSocket::SetSockError(const char *context)
{
SetSockError(context, TcpSocketErrorDesc(GetErrorCode()));
}
TcpSocket::SSL *(*TcpSocket::CreateSSL)(TcpSocket& socket);
TcpSocket::SSL *(*TcpSocket::CreateSSL)(TcpSocket& socket);
bool TcpSocket::StartSSL()
{
ASSERT(IsOpen());
ASSERT(IsOpen());
if(!CreateSSL) {
errorcode = -1;
errordesc = "Missing SSL support (Core/SSL)";
@ -720,8 +727,8 @@ bool TcpSocket::StartSSL()
errordesc = "Socket not open or listening";
return false;
}
ssl = (*CreateSSL)(*this);
if(!ssl->Start()) {
ssl = (*CreateSSL)(*this);
if(!ssl->Start()) {
ssl.Clear();
return false;
}

View file

@ -1,372 +1,375 @@
String FormatIP(dword _ip);
String UrlEncode(const String& s);
String UrlEncode(const String& s, const char *specials);
String UrlDecode(const char *b, const char *e);
inline String UrlDecode(const String& s) { return UrlDecode(s.Begin(), s.End() ); }
String Base64Encode(const char *b, const char *e);
inline String Base64Encode(const String& data) { return Base64Encode(data.Begin(), data.End()); }
String Base64Decode(const char *b, const char *e);
inline String Base64Decode(const String& data) { return Base64Decode(data.Begin(), data.End()); }
class IpAddrInfo {
enum { COUNT = 32 };
struct Entry {
const char *host;
const char *port;
int status;
addrinfo *addr;
};
static Entry pool[COUNT];
enum {
EMPTY = 0, WORKING, CANCELED, RESOLVED, FAILED
};
String host, port;
Entry *entry;
Entry exe[1];
static void EnterPool();
static void LeavePool();
static rawthread_t rawthread__ Thread(void *ptr);
void Start();
public:
void Start(const String& host, int port);
bool InProgress();
bool Execute(const String& host, int port);
addrinfo *GetResult();
void Clear();
IpAddrInfo();
~IpAddrInfo() { Clear(); }
};
enum { WAIT_READ = 1, WAIT_WRITE = 2, WAIT_EXCEPTION = 4, WAIT_ALL = 7 };
class TcpSocket {
enum { BUFFERSIZE = 512 };
enum { NONE, CONNECT, ACCEPT };
SOCKET socket;
int mode;
char buffer[BUFFERSIZE];
char *ptr;
char *end;
bool is_eof;
bool is_error;
bool is_abort;
bool ipv6;
int timeout;
int waitstep;
int done;
int errorcode;
String errordesc;
struct SSL {
virtual bool Start() = 0;
virtual bool Wait(dword flags) = 0;
virtual int Send(const void *buffer, int maxlen) = 0;
virtual int Recv(void *buffer, int maxlen) = 0;
virtual void Close() = 0;
virtual ~SSL() {}
};
One<SSL> ssl;
struct SSLImp;
friend struct SSLImp;
static SSL *(*CreateSSL)(TcpSocket& socket);
static SSL *CreateSSLImp(TcpSocket& socket);
friend void InitCreateSSL();
bool RawWait(dword flags);
SOCKET AcceptRaw(dword *ipaddr, int timeout_msec);
bool Open(int family, int type, int protocol);
int RawRecv(void *buffer, int maxlen);
int Recv(void *buffer, int maxlen);
int RawSend(const void *buffer, int maxlen);
int Send(const void *buffer, int maxlen);
bool RawConnect(addrinfo *info);
void RawClose();
void ReadBuffer();
int Get_();
int Peek_();
void Reset();
void SetSockError(const char *context, int code, const char *errdesc);
void SetSockError(const char *context, const char *errdesc);
void SetSockError(const char *context);
static int GetErrorCode();
static bool WouldBlock();
public:
Callback WhenWait;
static String GetHostName();
int GetDone() const { return done; }
static void Init();
bool IsOpen() const { return socket != INVALID_SOCKET; }
bool IsEof() const { return is_eof && ptr == end; }
bool IsError() const { return is_error; }
void ClearError() { is_error = false; errorcode = 0; errordesc.Clear(); }
int GetError() const { return errorcode; }
String GetErrorDesc() const { return errordesc; }
void Abort() { is_abort = true; }
bool IsAbort() const { return is_abort; }
void ClearAbort() { is_abort = false; }
SOCKET GetSOCKET() const { return socket; }
String GetPeerAddr() const;
void Attach(SOCKET socket);
bool Connect(const char *host, int port);
bool Connect(IpAddrInfo& info);
bool Listen(int port, int listen_count, bool ipv6 = false, bool reuse = true);
bool Accept(TcpSocket& listen_socket);
void Close();
void Shutdown();
void NoDelay();
void Linger(int msecs);
void NoLinger() { Linger(Null); }
void Reuse(bool reuse = true);
bool Wait(dword events);
bool WaitRead() { return Wait(WAIT_READ); }
bool WaitWrite() { return Wait(WAIT_WRITE); }
int Peek() { return ptr < end ? *ptr : Peek_(); }
int Term() { return Peek(); }
int Get() { return ptr < end ? *ptr++ : Get_(); }
int Get(void *buffer, int len);
String Get(int len);
int GetAll(void *buffer, int len) { return Get(buffer, len) == len; }
String GetAll(int len) { String s = Get(len); return s.GetCount() == len ? s : String::GetVoid(); }
String GetLine(int maxlen = 2000000);
int Put(const char *s, int len);
int Put(const String& s) { return Put(s.Begin(), s.GetLength()); }
bool PutAll(const char *s, int len) { return Put(s, len) == len; }
bool PutAll(const String& s) { return Put(s) == s.GetCount(); }
bool StartSSL();
bool IsSSL() const { return ssl; }
TcpSocket& Timeout(int ms) { timeout = ms; return *this; }
int GetTimeout() const { return timeout; }
TcpSocket& Blocking() { return Timeout(Null); }
TcpSocket();
~TcpSocket() { Close(); }
};
class SocketWaitEvent {
Vector< Tuple2<int, dword> > socket;
fd_set read[1], write[1], exception[1];
public:
void Clear() { socket.Clear(); }
void Add(SOCKET s, dword events = WAIT_ALL) { socket.Add(MakeTuple((int)s, events)); }
void Add(TcpSocket& s, dword events = WAIT_ALL) { Add(s.GetSOCKET(), events); }
int Wait(int timeout);
dword Get(int i) const;
dword operator[](int i) const { return Get(i); }
SocketWaitEvent();
};
struct HttpHeader {
String first_line;
VectorMap<String, String> fields;
String operator[](const char *id) { return fields.Get(id, Null); }
bool Response(String& protocol, int& code, String& reason);
bool Request(String& method, String& uri, String& version);
void Clear();
bool Parse(const String& hdrs);
};
class HttpRequest : public TcpSocket {
int phase;
String data;
int count;
HttpHeader header;
String error;
String body;
enum {
DEFAULT_HTTP_PORT = 80,
};
enum {
METHOD_GET,
METHOD_POST,
METHOD_HEAD,
METHOD_PUT,
};
int max_header_size;
int max_content_size;
int max_redirects;
int max_retries;
int timeout;
String host;
int port;
String proxy_host;
int proxy_port;
String proxy_username;
String proxy_password;
String path;
bool ssl;
int method;
String accept;
String agent;
bool force_digest;
bool is_post;
bool std_headers;
bool hasurlvar;
String contenttype;
String username;
String password;
String digest;
String request_headers;
String postdata;
String protocol;
int status_code;
String reason_phrase;
int start_time;
int retry_count;
int redirect_count;
int chunk;
IpAddrInfo addrinfo;
int bodylen;
bool gzip;
Zlib z;
void Init();
void StartPhase(int s);
void Start();
void Dns();
void StartRequest();
bool SendingData();
bool ReadingHeader();
void StartBody();
bool ReadingBody();
void ReadingChunkHeader();
void Finish();
void HttpError(const char *s);
void ContentOut(const void *ptr, dword size);
void Out(const void *ptr, dword size);
String CalculateDigest(const String& authenticate) const;
public:
Callback2<const void *, dword> WhenContent;
HttpRequest& MaxHeaderSize(int m) { max_header_size = m; return *this; }
HttpRequest& MaxContentSize(int m) { max_content_size = m; return *this; }
HttpRequest& MaxRedirect(int n) { max_redirects = n; return *this; }
HttpRequest& MaxRetries(int n) { max_retries = n; return *this; }
HttpRequest& RequestTimeout(int ms) { timeout = ms; return *this; }
HttpRequest& ChunkSize(int n) { chunk = n; return *this; }
HttpRequest& Method(int m) { method = m; return *this; }
HttpRequest& GET() { return Method(METHOD_GET); }
HttpRequest& POST() { return Method(METHOD_POST); }
HttpRequest& HEAD() { return Method(METHOD_HEAD); }
HttpRequest& PUT() { return Method(METHOD_PUT); }
HttpRequest& Host(const String& h) { host = h; return *this; }
HttpRequest& Port(int p) { port = p; return *this; }
HttpRequest& SSL(bool b = true) { ssl = b; return *this; }
HttpRequest& Path(const String& p) { path = p; return *this; }
HttpRequest& User(const String& u, const String& p) { username = u; password = p; return *this; }
HttpRequest& Digest() { force_digest = true; return *this; }
HttpRequest& Digest(const String& d) { digest = d; return *this; }
HttpRequest& Url(const char *url);
HttpRequest& UrlVar(const char *id, const String& data);
HttpRequest& operator()(const char *id, const String& data) { return UrlVar(id, data); }
HttpRequest& PostData(const String& pd) { postdata = pd; return *this; }
HttpRequest& PostUData(const String& pd) { return PostData(UrlEncode(pd)); }
HttpRequest& Post(const String& data) { POST(); return PostData(data); }
HttpRequest& Post(const char *id, const String& data);
HttpRequest& Headers(const String& h) { request_headers = h; return *this; }
HttpRequest& ClearHeaders() { return Headers(Null); }
HttpRequest& AddHeaders(const String& h) { request_headers.Cat(h); return *this; }
HttpRequest& Header(const char *id, const String& data);
HttpRequest& StdHeaders(bool sh) { std_headers = sh; return *this; }
HttpRequest& NoStdHeaders() { return StdHeaders(false); }
HttpRequest& Accept(const String& a) { accept = a; return *this; }
HttpRequest& Agent(const String& a) { agent = a; return *this; }
HttpRequest& ContentType(const String& a) { contenttype = a; return *this; }
HttpRequest& Proxy(const String& host, int port) { proxy_host = host; proxy_port = port; return *this; }
HttpRequest& Proxy(const char *url);
HttpRequest& ProxyAuth(const String& u, const String& p) { proxy_username = u; proxy_password = p; return *this; }
bool IsSocketError() const { return TcpSocket::IsError(); }
bool IsHttpError() const { return !IsNull(error) ; }
bool IsError() const { return IsSocketError() || IsHttpError(); }
String GetErrorDesc() const { return IsSocketError() ? TcpSocket::GetErrorDesc() : error; }
void ClearError() { TcpSocket::ClearError(); error.Clear(); }
String GetHeader(const char *s) { return header[s]; }
String operator[](const char *s) { return GetHeader(s); }
String GetRedirectUrl();
int GetContentLength();
int GetStatusCode() const { return status_code; }
String GetReasonPhrase() const { return reason_phrase; }
String GetContent() const { return body; }
String operator~() const { return GetContent(); }
operator String() const { return GetContent(); }
void ClearContent() { body.Clear(); }
enum Phase {
START, DNS, REQUEST, HEADER, BODY, CHUNK_HEADER, CHUNK_BODY, TRAILER, FINISHED, FAILED
};
bool Do();
int GetPhase() const { return phase; }
String GetPhaseName() const;
bool InProgress() const { return phase != FAILED && phase != FINISHED; }
bool IsFailure() const { return phase == FAILED; }
bool IsSuccess() const { return phase == FINISHED && status_code >= 200 && status_code < 300; }
String Execute();
HttpRequest();
HttpRequest(const char *url);
static void Trace(bool b = true);
};
String FormatIP(dword _ip);
String UrlEncode(const String& s);
String UrlEncode(const String& s, const char *specials);
String UrlDecode(const char *b, const char *e);
inline String UrlDecode(const String& s) { return UrlDecode(s.Begin(), s.End() ); }
String Base64Encode(const char *b, const char *e);
inline String Base64Encode(const String& data) { return Base64Encode(data.Begin(), data.End()); }
String Base64Decode(const char *b, const char *e);
inline String Base64Decode(const String& data) { return Base64Decode(data.Begin(), data.End()); }
class IpAddrInfo {
enum { COUNT = 32 };
struct Entry {
const char *host;
const char *port;
int status;
addrinfo *addr;
};
static Entry pool[COUNT];
enum {
EMPTY = 0, WORKING, CANCELED, RESOLVED, FAILED
};
String host, port;
Entry *entry;
Entry exe[1];
static void EnterPool();
static void LeavePool();
static rawthread_t rawthread__ Thread(void *ptr);
void Start();
public:
void Start(const String& host, int port);
bool InProgress();
bool Execute(const String& host, int port);
addrinfo *GetResult();
void Clear();
IpAddrInfo();
~IpAddrInfo() { Clear(); }
};
enum { WAIT_READ = 1, WAIT_WRITE = 2, WAIT_EXCEPTION = 4, WAIT_ALL = 7 };
class TcpSocket {
enum { BUFFERSIZE = 512 };
enum { NONE, CONNECT, ACCEPT };
SOCKET socket;
int mode;
char buffer[BUFFERSIZE];
char *ptr;
char *end;
bool is_eof;
bool is_error;
bool is_abort;
bool ipv6;
int timeout;
int waitstep;
int done;
int errorcode;
String errordesc;
struct SSL {
virtual bool Start() = 0;
virtual bool Wait(dword flags) = 0;
virtual int Send(const void *buffer, int maxlen) = 0;
virtual int Recv(void *buffer, int maxlen) = 0;
virtual void Close() = 0;
virtual ~SSL() {}
};
One<SSL> ssl;
struct SSLImp;
friend struct SSLImp;
static SSL *(*CreateSSL)(TcpSocket& socket);
static SSL *CreateSSLImp(TcpSocket& socket);
friend void InitCreateSSL();
bool RawWait(dword flags);
SOCKET AcceptRaw(dword *ipaddr, int timeout_msec);
bool Open(int family, int type, int protocol);
int RawRecv(void *buffer, int maxlen);
int Recv(void *buffer, int maxlen);
int RawSend(const void *buffer, int maxlen);
int Send(const void *buffer, int maxlen);
bool RawConnect(addrinfo *info);
void RawClose();
void ReadBuffer();
int Get_();
int Peek_();
void Reset();
void SetSockError(const char *context, int code, const char *errdesc);
void SetSockError(const char *context, const char *errdesc);
void SetSockError(const char *context);
static int GetErrorCode();
static bool WouldBlock();
public:
Callback WhenWait;
static String GetHostName();
int GetDone() const { return done; }
static void Init();
bool IsOpen() const { return socket != INVALID_SOCKET; }
bool IsEof() const { return is_eof && ptr == end; }
bool IsError() const { return is_error; }
void ClearError() { is_error = false; errorcode = 0; errordesc.Clear(); }
int GetError() const { return errorcode; }
String GetErrorDesc() const { return errordesc; }
void Abort() { is_abort = true; }
bool IsAbort() const { return is_abort; }
void ClearAbort() { is_abort = false; }
SOCKET GetSOCKET() const { return socket; }
String GetPeerAddr() const;
void Attach(SOCKET socket);
bool Connect(const char *host, int port);
bool Connect(IpAddrInfo& info);
bool Listen(int port, int listen_count, bool ipv6 = false, bool reuse = true);
bool Accept(TcpSocket& listen_socket);
void Close();
void Shutdown();
void NoDelay();
void Linger(int msecs);
void NoLinger() { Linger(Null); }
void Reuse(bool reuse = true);
bool Wait(dword events);
bool WaitRead() { return Wait(WAIT_READ); }
bool WaitWrite() { return Wait(WAIT_WRITE); }
int Peek() { return ptr < end ? *ptr : Peek_(); }
int Term() { return Peek(); }
int Get() { return ptr < end ? *ptr++ : Get_(); }
int Get(void *buffer, int len);
String Get(int len);
int GetAll(void *buffer, int len) { return Get(buffer, len) == len; }
String GetAll(int len) { String s = Get(len); return s.GetCount() == len ? s : String::GetVoid(); }
String GetLine(int maxlen = 2000000);
int Put(const char *s, int len);
int Put(const String& s) { return Put(s.Begin(), s.GetLength()); }
bool PutAll(const char *s, int len) { return Put(s, len) == len; }
bool PutAll(const String& s) { return Put(s) == s.GetCount(); }
bool StartSSL();
bool IsSSL() const { return ssl; }
TcpSocket& Timeout(int ms) { timeout = ms; return *this; }
int GetTimeout() const { return timeout; }
TcpSocket& Blocking() { return Timeout(Null); }
TcpSocket();
~TcpSocket() { Close(); }
};
class SocketWaitEvent {
Vector< Tuple2<int, dword> > socket;
fd_set read[1], write[1], exception[1];
public:
void Clear() { socket.Clear(); }
void Add(SOCKET s, dword events = WAIT_ALL) { socket.Add(MakeTuple((int)s, events)); }
void Add(TcpSocket& s, dword events = WAIT_ALL) { Add(s.GetSOCKET(), events); }
int Wait(int timeout);
dword Get(int i) const;
dword operator[](int i) const { return Get(i); }
SocketWaitEvent();
};
struct HttpHeader {
String first_line;
VectorMap<String, String> fields;
String operator[](const char *id) { return fields.Get(id, Null); }
bool Response(String& protocol, int& code, String& reason);
bool Request(String& method, String& uri, String& version);
void Clear();
bool Parse(const String& hdrs);
};
class HttpRequest : public TcpSocket {
int phase;
String data;
int count;
HttpHeader header;
String error;
String body;
enum {
DEFAULT_HTTP_PORT = 80,
};
enum {
METHOD_GET,
METHOD_POST,
METHOD_HEAD,
METHOD_PUT,
};
int max_header_size;
int max_content_size;
int max_redirects;
int max_retries;
int timeout;
String host;
int port;
String proxy_host;
int proxy_port;
String proxy_username;
String proxy_password;
String path;
bool ssl;
int method;
String accept;
String agent;
bool force_digest;
bool is_post;
bool std_headers;
bool hasurlvar;
String contenttype;
String username;
String password;
String digest;
String request_headers;
String postdata;
String protocol;
int status_code;
String reason_phrase;
int start_time;
int retry_count;
int redirect_count;
int chunk;
IpAddrInfo addrinfo;
int bodylen;
bool gzip;
Zlib z;
void Init();
void StartPhase(int s);
void Start();
void Dns();
void StartRequest();
bool SendingData();
bool ReadingHeader();
void StartBody();
bool ReadingBody();
void ReadingChunkHeader();
void Finish();
void CopyCookies();
void HttpError(const char *s);
void ContentOut(const void *ptr, dword size);
void Out(const void *ptr, dword size);
String CalculateDigest(const String& authenticate) const;
public:
Callback2<const void *, dword> WhenContent;
HttpRequest& MaxHeaderSize(int m) { max_header_size = m; return *this; }
HttpRequest& MaxContentSize(int m) { max_content_size = m; return *this; }
HttpRequest& MaxRedirect(int n) { max_redirects = n; return *this; }
HttpRequest& MaxRetries(int n) { max_retries = n; return *this; }
HttpRequest& RequestTimeout(int ms) { timeout = ms; return *this; }
HttpRequest& ChunkSize(int n) { chunk = n; return *this; }
HttpRequest& Method(int m) { method = m; return *this; }
HttpRequest& GET() { return Method(METHOD_GET); }
HttpRequest& POST() { return Method(METHOD_POST); }
HttpRequest& HEAD() { return Method(METHOD_HEAD); }
HttpRequest& PUT() { return Method(METHOD_PUT); }
HttpRequest& Host(const String& h) { host = h; return *this; }
HttpRequest& Port(int p) { port = p; return *this; }
HttpRequest& SSL(bool b = true) { ssl = b; return *this; }
HttpRequest& Path(const String& p) { path = p; return *this; }
HttpRequest& User(const String& u, const String& p) { username = u; password = p; return *this; }
HttpRequest& Digest() { force_digest = true; return *this; }
HttpRequest& Digest(const String& d) { digest = d; return *this; }
HttpRequest& Url(const char *url);
HttpRequest& UrlVar(const char *id, const String& data);
HttpRequest& operator()(const char *id, const String& data) { return UrlVar(id, data); }
HttpRequest& PostData(const String& pd) { postdata = pd; return *this; }
HttpRequest& PostUData(const String& pd) { return PostData(UrlEncode(pd)); }
HttpRequest& Post(const String& data) { POST(); return PostData(data); }
HttpRequest& Post(const char *id, const String& data);
HttpRequest& Headers(const String& h) { request_headers = h; return *this; }
HttpRequest& ClearHeaders() { return Headers(Null); }
HttpRequest& AddHeaders(const String& h) { request_headers.Cat(h); return *this; }
HttpRequest& Header(const char *id, const String& data);
HttpRequest& Cookie(const String& cookie) { return Header("Cookie", cookie); }
HttpRequest& StdHeaders(bool sh) { std_headers = sh; return *this; }
HttpRequest& NoStdHeaders() { return StdHeaders(false); }
HttpRequest& Accept(const String& a) { accept = a; return *this; }
HttpRequest& Agent(const String& a) { agent = a; return *this; }
HttpRequest& ContentType(const String& a) { contenttype = a; return *this; }
HttpRequest& Proxy(const String& host, int port) { proxy_host = host; proxy_port = port; return *this; }
HttpRequest& Proxy(const char *url);
HttpRequest& ProxyAuth(const String& u, const String& p) { proxy_username = u; proxy_password = p; return *this; }
bool IsSocketError() const { return TcpSocket::IsError(); }
bool IsHttpError() const { return !IsNull(error) ; }
bool IsError() const { return IsSocketError() || IsHttpError(); }
String GetErrorDesc() const { return IsSocketError() ? TcpSocket::GetErrorDesc() : error; }
void ClearError() { TcpSocket::ClearError(); error.Clear(); }
String GetHeader(const char *s) { return header[s]; }
String operator[](const char *s) { return GetHeader(s); }
String GetRedirectUrl();
int GetContentLength();
int GetStatusCode() const { return status_code; }
String GetReasonPhrase() const { return reason_phrase; }
String GetContent() const { return body; }
String operator~() const { return GetContent(); }
operator String() const { return GetContent(); }
void ClearContent() { body.Clear(); }
enum Phase {
START, DNS, REQUEST, HEADER, BODY, CHUNK_HEADER, CHUNK_BODY, TRAILER, FINISHED, FAILED
};
bool Do();
int GetPhase() const { return phase; }
String GetPhaseName() const;
bool InProgress() const { return phase != FAILED && phase != FINISHED; }
bool IsFailure() const { return phase == FAILED; }
bool IsSuccess() const { return phase == FINISHED && status_code >= 200 && status_code < 300; }
String Execute();
HttpRequest();
HttpRequest(const char *url);
static void Trace(bool b = true);
};