mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-07-11 22:03:01 -06:00
Core: developing Web
git-svn-id: svn://ultimatepp.org/upp/trunk@4744 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
530adca9d7
commit
2c5141ead8
4 changed files with 417 additions and 372 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue