Core: developing Web

git-svn-id: svn://ultimatepp.org/upp/trunk@4744 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2012-04-03 18:26:28 +00:00
parent 530adca9d7
commit 2c5141ead8
4 changed files with 417 additions and 372 deletions

View file

@ -5,6 +5,11 @@ NAMESPACE_UPP
bool HttpRequest_Trace__;
#define LLOG(x) do { if(HttpRequest_Trace__) RLOG(x); } while(0)
#ifdef _DEBUG
_DBG_
#define ENDZIP
#endif
void HttpRequest::Trace(bool b)
{
@ -29,6 +34,7 @@ void HttpRequest::Init()
gzip = false;
WhenContent = callback(this, &HttpRequest::ContentOut);
chunk = 4096;
timeout = 120000;
}
HttpRequest::HttpRequest()
@ -203,6 +209,7 @@ bool HttpRequest::Do()
case START:
retry_count = 0;
redirect_count = 0;
start_time = msecs();
gzip = false;
StartRequest();
break;
@ -239,6 +246,9 @@ bool HttpRequest::Do()
header.Parse(data);
Finish();
break;
case FINISHED:
case FAILED:
return false;
default:
NEVER();
}
@ -247,7 +257,7 @@ bool HttpRequest::Do()
if(IsSocketError() || IsError())
phase = FAILED;
else
if(IsTimeout()) {
if(msecs() - start_time >= timeout) {
HttpError("connection timed out");
phase = FAILED;
}
@ -268,6 +278,23 @@ bool HttpRequest::Do()
void HttpRequest::Finish()
{
if(gzip) {
#ifdef ENDZIP
body = GZDecompress(body);
if(body.IsVoid()) {
HttpError("gzip decompress at finish error");
phase = FAILED;
return;
}
#else
z.End();
if(z.IsError()) {
HttpError("gzip format error (finish)");
phase = FAILED;
return;
}
#endif
}
Close();
if(status_code == 401 && !IsNull(username)) {
String authenticate = header["www-authenticate"];
@ -346,7 +373,7 @@ void HttpRequest::StartBody()
return;
}
if(!header.Response(protocol, status_code, response_phrase)) {
if(!header.Response(protocol, status_code, reason_phrase)) {
HttpError("invalid HTTP response");
return;
}
@ -403,13 +430,21 @@ bool HttpRequest::ReadingBody()
if(count >= 0)
n = min(n, count);
String s = Get(n);
if(gzip) {
DDUMP(s.GetCount());
DDUMP(count);
DDUMP(IsEof());
if(s.GetCount() == 0)
return !IsEof();
#ifndef ENDZIP
if(gzip)
z.Put(~s, s.GetCount());
}
else
#endif
Out(~s, s.GetCount());
DDUMP(IsEof());
if(count >= 0) {
count -= n;
count -= s.GetCount();
DDUMP(count);
return !IsEof() && count > 0;
}
return !IsEof();
@ -488,7 +523,7 @@ bool HttpRequest::ReadingHeader()
for(;;) {
int c = Get();
if(c < 0)
return false;
return !IsEof();
else
data.Cat(c);
if(data.GetCount() > 3) {

View file

@ -16,7 +16,7 @@ NAMESPACE_UPP
#pragma comment(lib, "ws2_32.lib")
#endif
#define LLOG(x) // DLOG("TCP " << x)
#define LLOG(x) // DLOG("TCP " << x)
#ifdef PLATFORM_POSIX
@ -113,7 +113,6 @@ void TcpSocket::Reset()
ipv6 = false;
ptr = end = buffer;
is_error = false;
is_timeout = false;
is_abort = false;
}
@ -129,7 +128,7 @@ TcpSocket::TcpSocket()
bool TcpSocket::Open(int family, int type, int protocol)
{
Init();
CloseRaw();
Close();
ClearError();
if((socket = ::socket(family, type, protocol)) == INVALID_SOCKET)
return false;
@ -188,7 +187,7 @@ bool TcpSocket::Listen(int port, int listen_count, bool ipv6_, bool reuse)
bool TcpSocket::Accept(TcpSocket& ls)
{
CloseRaw();
Close();
if(timeout && !ls.WaitRead())
return false;
if(!Open(ls.ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0))
@ -241,7 +240,7 @@ void TcpSocket::Linger(int msecs)
void TcpSocket::Attach(SOCKET s)
{
CloseRaw();
Close();
socket = s;
}
@ -269,7 +268,7 @@ bool TcpSocket::Connect(const char *host, int port)
if(connect(socket, rp->ai_addr, rp->ai_addrlen) == 0 ||
GetErrorCode() == SOCKERR(EINPROGRESS) || GetErrorCode() == SOCKERR(EWOULDBLOCK))
break;
CloseRaw();
Close();
}
rp = rp->ai_next;
if(!rp) {
@ -283,33 +282,22 @@ bool TcpSocket::Connect(const char *host, int port)
return true;
}
bool TcpSocket::CloseRaw()
void TcpSocket::Close()
{
SOCKET old_socket = socket;
socket = INVALID_SOCKET;
if(old_socket != INVALID_SOCKET) {
LLOG("TcpSocket::CloseRaw(" << (int)old_socket << ")");
LLOG("TCP close " << (int)socket);
if(socket != INVALID_SOCKET) {
int res;
#if defined(PLATFORM_WIN32)
res = closesocket(old_socket);
res = closesocket(socket);
#elif defined(PLATFORM_POSIX)
res = close(old_socket);
res = close(socket);
#else
#error Unsupported platform
#endif
if(res && !IsError()) {
if(res && !IsError())
SetSockError("close");
return false;
}
socket = INVALID_SOCKET;
}
return true;
}
bool TcpSocket::Close()
{
if(socket == INVALID_SOCKET)
return false;
return !IsError() && WaitWrite() && CloseRaw();
}
bool TcpSocket::WouldBlock()
@ -349,23 +337,14 @@ String TcpSocket::GetHostName()
return buffer;
}
TcpSocket& TcpSocket::GlobalTimeout(int ms)
{
global = true;
starttime = msecs();
timeout = ms;
return *this;
}
bool TcpSocket::Wait(dword flags)
{
LLOG("Wait(" << timeout << ", " << flags << ")");
if((flags & WAIT_READ) && ptr != end)
return true;
int end_time = (global ? starttime : msecs()) + timeout;
int end_time = msecs() + timeout;
if(socket == INVALID_SOCKET)
return false;
is_timeout = false;
for(;;) {
if(IsError() || IsAbort())
return false;
@ -385,7 +364,8 @@ bool TcpSocket::Wait(dword flags)
FD_SET(socket, fdset);
int avail = select((int)socket + 1,
flags & WAIT_READ ? fdset : NULL,
flags & WAIT_WRITE ? fdset : NULL, NULL, tvalp);
flags & WAIT_WRITE ? fdset : NULL,
flags & WAIT_EXCEPTION ? fdset : NULL, tvalp);
LLOG("Wait select avail: " << avail);
if(avail < 0) {
SetSockError("wait");
@ -393,17 +373,18 @@ bool TcpSocket::Wait(dword flags)
}
if(avail > 0)
return true;
if(to <= 0) {
is_timeout = true;
if(to <= 0 && timeout) {
return false;
}
WhenWait();
if(timeout == 0)
return false;
}
}
int TcpSocket::Put(const char *s, int length)
{
LLOG("Put(@ " << socket << ": " << length);
LLOG("Put " << socket << ": " << length);
ASSERT(IsOpen());
if(length < 0 && s)
length = (int)strlen(s);
@ -416,7 +397,7 @@ int TcpSocket::Put(const char *s, int length)
return done;
peek = false;
int count = Send(s + done, length - done);
if(IsError() || timeout == 0)
if(IsError() || timeout == 0 && count == 0 && peek)
return done;
if(count > 0)
done += count;
@ -446,12 +427,9 @@ int TcpSocket::Recv(void *buf, int amount)
void TcpSocket::ReadBuffer()
{
ptr = buffer;
end = buffer + Recv(buffer, BUFFERSIZE);
if(ptr == end && timeout) {
WaitRead();
ptr = end = buffer;
if(WaitRead())
end = buffer + Recv(buffer, BUFFERSIZE);
}
}
int TcpSocket::Get_()
@ -491,15 +469,14 @@ int TcpSocket::Get(void *buffer, int count)
ptr += count;
return count;
}
int part = Recv((char *)buffer + done, count - done);
if(part > 0)
done += part;
while(timeout != 0 && part >= 0 && done < count && !IsError() && !IsEof()) {
while(done < count && !IsError() && !IsEof()) {
if(!WaitRead())
break;
part = Recv((char *)buffer + done, count - done);
int part = Recv((char *)buffer + done, count - done);
if(part > 0)
done += part;
if(timeout == 0)
break;
}
return done;
}
@ -547,24 +524,55 @@ void TcpSocket::SetSockError(const char *context)
SetSockError(context, TcpSocketErrorDesc(GetErrorCode()));
}
/*
int SocketWaitEvent::Wait(int timeout)
{
fd_set read[1], write[1], except[1];
for(int i = 0; i < socket.GetCount()
FD_ZERO(fdset);
FD_SET(socket, fdset);
int avail = select((int)socket + 1,
flags & WAIT_READ ? fdset : NULL,
flags & WAIT_WRITE ? fdset : NULL, NULL, tvalp);
LLOG("Wait select avail: " << avail);
if(avail < 0) {
SetSockError("wait");
return false;
FD_ZERO(read);
FD_ZERO(write);
FD_ZERO(exception);
int maxindex = -1;
for(int i = 0; i < socket.GetCount(); i++) {
const Tuple2<int, dword>& s = socket[i];
if(s.a >= 0) {
const Tuple2<SOCKET, dword>& s = socket[i];
if(s.b & WAIT_READ)
FD_SET(s.a, read);
if(s.b & WAIT_WRITE)
FD_SET(s.a, write);
if(s.b & WAIT_EXCEPTION)
FD_SET(s.a, exception);
maxindex = max(s.a, maxindex);
}
}
if(avail > 0)
return true;
timeval *tvalp = NULL;
timeval tval;
if(!IsNull(timeout)) {
tval.tv_sec = timeout / 1000;
tval.tv_usec = 1000 * (timeout % 1000);
tvalp = &tval;
}
return select(maxindex + 1, read, write, exception, tvalp);
}
dword SocketWaitEvent::Get(int i) const
{
int s = socket[i].a;
if(s < 0)
return 0;
dword events = 0;
if(FD_ISSET(s, read))
events |= WAIT_READ;
if(FD_ISSET(s, write))
events |= WAIT_WRITE;
if(FD_ISSET(s, exception))
events |= WAIT_EXCEPTION;
return events;
}
SocketWaitEvent::SocketWaitEvent()
{
FD_ZERO(read);
FD_ZERO(write);
FD_ZERO(exception);
}
*/
END_UPP_NAMESPACE

View file

@ -1,301 +1,300 @@
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()); }
enum { WAIT_READ = 1, WAIT_WRITE = 2, WAIT_EXCEPTION = 3 };
class TcpSocket {
enum { BUFFERSIZE = 512 };
SOCKET socket;
char buffer[BUFFERSIZE];
char *ptr;
char *end;
bool is_eof;
bool is_error;
bool is_timeout;
bool is_abort;
bool ipv6;
bool global;
int timeout;
int starttime;
int waitstep;
int done;
int errorcode;
String errordesc;
bool CloseRaw();
SOCKET AcceptRaw(dword *ipaddr, int timeout_msec);
bool Open(int family, int type, int protocol);
int Recv(void *buffer, int maxlen);
int Send(const void *buffer, int maxlen);
void ReadBuffer();
int Get_();
int Peek_();
void Reset();
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; }
bool IsTimeout() const { return is_timeout; }
void ClearTimeout() { is_timeout = false; }
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 Listen(int port, int listen_count, bool ipv6 = false, bool reuse = true);
bool Accept(TcpSocket& listen_socket);
bool 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(); }
TcpSocket& Timeout(int ms) { timeout = ms; global = false; return *this; }
TcpSocket& GlobalTimeout(int ms);
TcpSocket& Blocking() { return Timeout(Null); }
TcpSocket();
~TcpSocket() { Close(); }
};
class SocketWaitEvent {
Vector< Tuple2<SOCKET, dword> > socket;
public:
void Clear() { socket.Clear(); }
void Add(SOCKET s, dword events) { socket.Add(MakeTuple(s, events)); }
void Add(TcpSocket& s, dword events) { Add(s.GetSOCKET(), events); }
int Wait(int timeout);
};
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;
String host;
int port;
String proxy_host;
int proxy_port;
String proxy_username;
String proxy_password;
String path;
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 response_phrase;
int retry_count;
int redirect_count;
int chunk;
int bodylen;
bool gzip;
Zlib z;
void Init();
void StartPhase(int s);
void StartBody();
bool SendingData();
bool ReadingHeader();
bool ReadingBody();
void StartRequest();
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& 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& 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 GetResponsePhrase() const { return response_phrase; }
String GetContent() const { return body; }
String operator~() const { return GetContent(); }
operator String() const { return GetContent(); }
void ClearContent() { body.Clear(); }
enum Phase {
START, REQUEST, HEADER, BODY, CHUNK_HEADER, CHUNK_BODY, TRAILER, FINISHED, FAILED
};
bool Do();
int GetPhase() const { return phase; }
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()); }
enum { WAIT_READ = 1, WAIT_WRITE = 2, WAIT_EXCEPTION = 4, WAIT_ALL = 7 };
class TcpSocket {
enum { BUFFERSIZE = 512 };
SOCKET socket;
char buffer[BUFFERSIZE];
char *ptr;
char *end;
bool is_eof;
bool is_error;
bool is_abort;
bool ipv6;
bool global;
int timeout;
int waitstep;
int done;
int errorcode;
String errordesc;
SOCKET AcceptRaw(dword *ipaddr, int timeout_msec);
bool Open(int family, int type, int protocol);
int Recv(void *buffer, int maxlen);
int Send(const void *buffer, int maxlen);
void ReadBuffer();
int Get_();
int Peek_();
void Reset();
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 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(); }
TcpSocket& Timeout(int ms) { timeout = ms; global = false; return *this; }
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;
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;
int bodylen;
bool gzip;
Zlib z;
void Init();
void StartPhase(int s);
void StartBody();
bool SendingData();
bool ReadingHeader();
bool ReadingBody();
void StartRequest();
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& 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, REQUEST, HEADER, BODY, CHUNK_HEADER, CHUNK_BODY, TRAILER, FINISHED, FAILED
};
bool Do();
int GetPhase() const { return phase; }
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);
};

View file

@ -235,7 +235,9 @@ void Zlib::End()
Poke32le(h + 4, total);
WhenOut(h, 8);
}
if(gzip && mode == INFLATE &&
DDUMP(footer.GetCount());
_DBG_ if(0 && gzip && mode == INFLATE &&
(footer.GetCount() != 8 || Peek32le(~footer) != (int)crc || Peek32le(~footer + 4) != total)) {
LLOG("ZLIB GZIP FOOTER ERROR");
error = true;
@ -275,6 +277,7 @@ Zlib::Zlib()
WhenOut = callback(this, &Zlib::PutOut);
mode = NONE;
gzip = false;
error = false;
}
Zlib::~Zlib()