diff --git a/uppsrc/Core/Http.cpp b/uppsrc/Core/Http.cpp index ca5e65935..5267c160e 100644 --- a/uppsrc/Core/Http.cpp +++ b/uppsrc/Core/Http.cpp @@ -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) { diff --git a/uppsrc/Core/Socket.cpp b/uppsrc/Core/Socket.cpp index 8f618b65d..7e09d8b82 100644 --- a/uppsrc/Core/Socket.cpp +++ b/uppsrc/Core/Socket.cpp @@ -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& s = socket[i]; + if(s.a >= 0) { + const Tuple2& 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 diff --git a/uppsrc/Core/Web.h b/uppsrc/Core/Web.h index 9612f4f27..97138d4df 100644 --- a/uppsrc/Core/Web.h +++ b/uppsrc/Core/Web.h @@ -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; - -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 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 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 > 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 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 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); +}; diff --git a/uppsrc/Core/z.cpp b/uppsrc/Core/z.cpp index 9f721912b..3b71ddf57 100644 --- a/uppsrc/Core/z.cpp +++ b/uppsrc/Core/z.cpp @@ -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()