Core: HttpRequest improvements

git-svn-id: svn://ultimatepp.org/upp/trunk@4993 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2012-05-22 10:36:29 +00:00
parent 20f5560f86
commit 8307fa6b7e
4 changed files with 981 additions and 861 deletions

View file

@ -202,6 +202,30 @@ HttpRequest& HttpRequest::Header(const char *id, const String& data)
return *this;
}
HttpRequest& HttpRequest::Cookie(const HttpCookie& c)
{
cookies.GetAdd(String(c.id) << '?' << c.domain << '?' << c.path) = c;
return *this;
}
HttpRequest& HttpRequest::Cookie(const String& id, const String& value, const String& domain, const String& path)
{
HttpCookie c;
c.id = id;
c.value = value;
c.domain = domain;
c.path = path;
return Cookie(c);
}
HttpRequest& HttpRequest::CopyCookies(const HttpRequest& r)
{
const HttpHeader& h = r.GetHttpHeader();
for(int i = 0; i < h.cookies.GetCount(); i++)
Cookie(h.cookies[i]);
return *this;
}
void HttpRequest::HttpError(const char *s)
{
if(IsError())
@ -458,6 +482,17 @@ void HttpRequest::StartRequest()
if(ctype.GetCount())
data << "Content-Type: " << ctype << "\r\n";
}
String cs;
for(int i = 0; i < cookies.GetCount(); i++) {
const HttpCookie& c = cookies[i];
if(host.EndsWith(c.domain) && path.StartsWith(c.path)) {
if(cs.GetCount())
cs << "; ";
cs << c.id << '=' << c.value;
}
}
if(cs.GetCount())
data << "Cookie: " << cs << "\r\n";
if(!IsNull(proxy_host) && !IsNull(proxy_username))
data << "Proxy-Authorization: Basic " << Base64Encode(proxy_username + ':' + proxy_password) << "\r\n";
if(!IsNull(digest))
@ -632,11 +667,7 @@ bool HttpRequest::ReadingBody()
void HttpRequest::CopyCookies()
{
int q = header.fields.Find("set-cookie");
while(q >= 0) {
Cookie(header.fields[q]);
q = header.fields.FindNext(q);
}
CopyCookies(*this);
}
void HttpRequest::Finish()
@ -671,6 +702,7 @@ void HttpRequest::Finish()
}
if(status_code >= 300 && status_code < 400) {
String url = GetRedirectUrl();
GET();
if(url.GetCount() && redirect_count++ < max_redirects) {
LLOG("--- HTTP redirect " << url);
Url(url);

View file

@ -1,458 +1,484 @@
String WwwFormat(Time tm);
String MIMECharsetName(byte charset);
String UrlEncode(const char *s, const char *end);
String UrlEncode(const char *s, int len);
String UrlEncode(const String& s);
String UrlDecode(const char *s, const char *end);
String UrlDecode(const char *s, int len);
String UrlDecode(const String& s);
String Base64Encode(const char *s, const char *end);
String Base64Encode(const char *s, int len);
String Base64Encode(const String& data);
String Base64Decode(const char *s, const char *end);
String Base64Decode(const char *s, int len);
String Base64Decode(const String& data);
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 auxthread_t auxthread__ 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 };
struct SSLInfo {
String cipher;
bool cert_avail;
bool cert_verified; // Peer verification not yet working - this is always false
String cert_subject;
String cert_issuer;
Date cert_notbefore;
Date cert_notafter;
int cert_version;
String cert_serial;
};
class TcpSocket {
enum { BUFFERSIZE = 512 };
enum { NONE, CONNECT, ACCEPT, SSL_CONNECTED };
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 global_timeout;
int start_time;
int errorcode;
String errordesc;
struct SSL {
virtual bool Start() = 0;
virtual bool Wait(dword flags, int end_time) = 0;
virtual int Send(const void *buffer, int maxlen) = 0;
virtual int Recv(void *buffer, int maxlen) = 0;
virtual void Close() = 0;
virtual dword Handshake() = 0;
virtual ~SSL() {}
};
One<SSL> ssl;
One<SSLInfo> sslinfo;
String cert, pkey;
bool asn1;
struct SSLImp;
friend struct SSLImp;
static SSL *(*CreateSSL)(TcpSocket& socket);
static SSL *CreateSSLImp(TcpSocket& socket);
friend void InitCreateSSL();
friend class IpAddrInfo;
int GetEndTime() const;
bool RawWait(dword flags, int end_time);
bool Wait(dword events, int end_time);
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 end_time);
int Get_();
int Peek_();
int Peek_(int end_time);
int Peek(int end_time) { return ptr < end ? *ptr : Peek_(end_time); }
bool IsGlobalTimeout();
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();
static void Init();
public:
Callback WhenWait;
enum { ERROR_GLOBAL_TIMEOUT = -1000000 };
static String GetHostName();
int GetDone() const { return done; }
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); }
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 Put(const char *s, int len);
int Put(const String& s) { return Put(s.Begin(), s.GetLength()); }
bool GetAll(void *buffer, int len);
String GetAll(int len);
String GetLine(int maxlen = 65536);
bool PutAll(const char *s, int len);
bool PutAll(const String& s);
bool StartSSL();
bool IsSSL() const { return ssl; }
bool SSLHandshake();
void SSLCertificate(const String& cert, const String& pkey, bool asn1);
const SSLInfo *GetSSLInfo() const { return ~sslinfo; }
TcpSocket& Timeout(int ms) { timeout = ms; return *this; }
int GetTimeout() const { return timeout; }
TcpSocket& GlobalTimeout(int ms);
TcpSocket& NoGlobalTimeout() { return GlobalTimeout(Null); }
TcpSocket& Blocking() { return Timeout(Null); }
TcpSocket& WaitStep(int ms) { waitstep = ms; return *this; }
int GetWaitStep() const { return waitstep; }
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;
String f1, f2, f3;
VectorMap<String, String> fields;
bool scgi;
String operator[](const char *id) const { return fields.Get(id, Null); }
bool Response(String& protocol, int& code, String& reason);
bool Request(String& method, String& uri, String& version);
String GetProtocol() const { return f1; }
int GetCode() const;
String GetReason() const { return f3; }
String GetMethod() const { return f1; }
String GetURI() const { return f2; }
String GetVersion() const { return f3; }
int64 GetContentLength() const;
void Clear();
bool ParseAdd(const String& hdrs);
bool Parse(const String& hdrs);
bool ParseSCGI(const String& scgi_hdr);
bool Read(TcpSocket& socket);
};
class HttpRequest : public TcpSocket {
int phase;
String data;
int count;
HttpHeader header;
String error;
String body;
enum {
DEFAULT_HTTP_PORT = 80,
DEFAULT_HTTPS_PORT = 443
};
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 ssl_proxy_host;
int ssl_proxy_port;
String ssl_proxy_username;
String ssl_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 cookies;
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 StartConnect();
void ProcessSSLProxyResponse();
void AfterConnect();
void StartRequest();
bool SendingData();
bool ReadingHeader();
void StartBody();
bool ReadingBody();
void ReadingChunkHeader();
void Finish();
bool IsRequestTimeout();
void CopyCookies();
void HttpError(const char *s);
void ContentOut(const void *ptr, int size);
void Out(const void *ptr, int size);
String CalculateDigest(const String& authenticate) const;
public:
Callback2<const void *, int> WhenContent;
Callback WhenStart;
Callback WhenDo;
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& ClearPost() { PostData(Null); GET(); return *this; }
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& UserAgent(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 *p);
HttpRequest& ProxyAuth(const String& u, const String& p) { proxy_username = u; proxy_password = p; return *this; }
HttpRequest& SSLProxy(const String& host, int port) { ssl_proxy_host = host; ssl_proxy_port = port; return *this; }
HttpRequest& SSLProxy(const char *p);
HttpRequest& SSLProxyAuth(const String& u, const String& p) { ssl_proxy_username = u; ssl_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 *id) { return header[id]; }
String operator[](const char *id) { return GetHeader(id); }
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 {
BEGIN, START, DNS,
SSLPROXYREQUEST, SSLPROXYRESPONSE, SSLHANDSHAKE,
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();
void New();
HttpRequest();
HttpRequest(const char *url);
static void Trace(bool b = true);
};
bool HttpResponse(TcpSocket& socket, bool scgi, int code, const char *phrase,
const char *content_type = NULL, const String& data = Null,
const char *server = NULL);
String WwwFormat(Time tm);
String MIMECharsetName(byte charset);
String UrlEncode(const char *s, const char *end);
String UrlEncode(const char *s, int len);
String UrlEncode(const String& s);
String UrlDecode(const char *s, const char *end);
String UrlDecode(const char *s, int len);
String UrlDecode(const String& s);
String Base64Encode(const char *s, const char *end);
String Base64Encode(const char *s, int len);
String Base64Encode(const String& data);
String Base64Decode(const char *s, const char *end);
String Base64Decode(const char *s, int len);
String Base64Decode(const String& data);
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 auxthread_t auxthread__ 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 };
struct SSLInfo {
String cipher;
bool cert_avail;
bool cert_verified; // Peer verification not yet working - this is always false
String cert_subject;
String cert_issuer;
Date cert_notbefore;
Date cert_notafter;
int cert_version;
String cert_serial;
};
class TcpSocket {
enum { BUFFERSIZE = 512 };
enum { NONE, CONNECT, ACCEPT, SSL_CONNECTED };
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 global_timeout;
int start_time;
int errorcode;
String errordesc;
struct SSL {
virtual bool Start() = 0;
virtual bool Wait(dword flags, int end_time) = 0;
virtual int Send(const void *buffer, int maxlen) = 0;
virtual int Recv(void *buffer, int maxlen) = 0;
virtual void Close() = 0;
virtual dword Handshake() = 0;
virtual ~SSL() {}
};
One<SSL> ssl;
One<SSLInfo> sslinfo;
String cert, pkey;
bool asn1;
struct SSLImp;
friend struct SSLImp;
static SSL *(*CreateSSL)(TcpSocket& socket);
static SSL *CreateSSLImp(TcpSocket& socket);
friend void InitCreateSSL();
friend class IpAddrInfo;
int GetEndTime() const;
bool RawWait(dword flags, int end_time);
bool Wait(dword events, int end_time);
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 end_time);
int Get_();
int Peek_();
int Peek_(int end_time);
int Peek(int end_time) { return ptr < end ? *ptr : Peek_(end_time); }
bool IsGlobalTimeout();
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();
static void Init();
public:
Callback WhenWait;
enum { ERROR_GLOBAL_TIMEOUT = -1000000 };
static String GetHostName();
int GetDone() const { return done; }
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); }
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 Put(const char *s, int len);
int Put(const String& s) { return Put(s.Begin(), s.GetLength()); }
bool GetAll(void *buffer, int len);
String GetAll(int len);
String GetLine(int maxlen = 65536);
bool PutAll(const char *s, int len);
bool PutAll(const String& s);
bool StartSSL();
bool IsSSL() const { return ssl; }
bool SSLHandshake();
void SSLCertificate(const String& cert, const String& pkey, bool asn1);
const SSLInfo *GetSSLInfo() const { return ~sslinfo; }
TcpSocket& Timeout(int ms) { timeout = ms; return *this; }
int GetTimeout() const { return timeout; }
TcpSocket& GlobalTimeout(int ms);
TcpSocket& NoGlobalTimeout() { return GlobalTimeout(Null); }
TcpSocket& Blocking() { return Timeout(Null); }
TcpSocket& WaitStep(int ms) { waitstep = ms; return *this; }
int GetWaitStep() const { return waitstep; }
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 HttpCookie : Moveable<HttpCookie> {
String id;
String value;
String domain;
String path;
String raw;
void Clear();
bool Parse(const String& cookie);
};
struct HttpHeader {
String first_line;
String f1, f2, f3;
VectorMap<String, String> fields;
VectorMap<String, HttpCookie> cookies;
bool scgi;
String operator[](const char *id) const { return fields.Get(id, Null); }
String GetCookie(const char *id) const;
bool Response(String& protocol, int& code, String& reason) const;
bool Request(String& method, String& uri, String& version) const;
String GetProtocol() const { return f1; }
int GetCode() const;
String GetReason() const { return f3; }
String GetMethod() const { return f1; }
String GetURI() const { return f2; }
String GetVersion() const { return f3; }
int64 GetContentLength() const;
void Clear();
bool ParseAdd(const String& hdrs);
bool Parse(const String& hdrs);
bool ParseSCGI(const String& scgi_hdr);
bool Read(TcpSocket& socket);
HttpHeader() { scgi = false; }
private:
void Add(const String& id, const String& value);
HttpHeader(const HttpHeader&);
};
class HttpRequest : public TcpSocket {
int phase;
String data;
int count;
HttpHeader header;
String error;
String body;
enum {
DEFAULT_HTTP_PORT = 80,
DEFAULT_HTTPS_PORT = 443
};
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 ssl_proxy_host;
int ssl_proxy_port;
String ssl_proxy_username;
String ssl_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;
VectorMap<String, HttpCookie> cookies;
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 StartConnect();
void ProcessSSLProxyResponse();
void AfterConnect();
void StartRequest();
bool SendingData();
bool ReadingHeader();
void StartBody();
bool ReadingBody();
void ReadingChunkHeader();
void Finish();
bool IsRequestTimeout();
void CopyCookies();
void HttpError(const char *s);
void ContentOut(const void *ptr, int size);
void Out(const void *ptr, int size);
String CalculateDigest(const String& authenticate) const;
public:
Callback2<const void *, int> WhenContent;
Callback WhenStart;
Callback WhenDo;
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& ClearPost() { PostData(Null); GET(); return *this; }
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 HttpCookie& c);
HttpRequest& Cookie(const String& id, const String& value,
const String& domain = Null, const String& path = Null);
HttpRequest& CopyCookies(const HttpRequest& r);
HttpRequest& StdHeaders(bool sh) { std_headers = sh; return *this; }
HttpRequest& NoStdHeaders() { return StdHeaders(false); }
HttpRequest& Accept(const String& a) { accept = a; return *this; }
HttpRequest& UserAgent(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 *p);
HttpRequest& ProxyAuth(const String& u, const String& p) { proxy_username = u; proxy_password = p; return *this; }
HttpRequest& SSLProxy(const String& host, int port) { ssl_proxy_host = host; ssl_proxy_port = port; return *this; }
HttpRequest& SSLProxy(const char *p);
HttpRequest& SSLProxyAuth(const String& u, const String& p) { ssl_proxy_username = u; ssl_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 *id) { return header[id]; }
String operator[](const char *id) { return GetHeader(id); }
String GetRedirectUrl();
int GetContentLength();
int GetStatusCode() const { return status_code; }
String GetReasonPhrase() const { return reason_phrase; }
const HttpHeader& GetHttpHeader() const { return header; }
String GetCookie(const char *id) { return header.GetCookie(id); }
String GetContent() const { return body; }
String operator~() const { return GetContent(); }
operator String() const { return GetContent(); }
void ClearContent() { body.Clear(); }
enum Phase {
BEGIN, START, DNS,
SSLPROXYREQUEST, SSLPROXYRESPONSE, SSLHANDSHAKE,
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();
void New();
HttpRequest();
HttpRequest(const char *url);
static void Trace(bool b = true);
};
bool HttpResponse(TcpSocket& socket, bool scgi, int code, const char *phrase,
const char *content_type = NULL, const String& data = Null,
const char *server = NULL);

View file

@ -1,390 +1,452 @@
#include "Core.h"
NAMESPACE_UPP
String WwwFormat(Time tm)
{
static const char *dayofweek[] =
{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const char *month[] =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
return String().Cat()
<< dayofweek[DayOfWeek(tm)] << ", "
<< (int)tm.day << ' ' << month[tm.month - 1]
<< ' ' << (int)tm.year
<< ' ' << Sprintf("%2d:%02d:%02d +0100", tm.hour, tm.minute, tm.second);
}
String MIMECharsetName(byte charset)
{
if(charset == CHARSET_DEFAULT)
charset = GetDefaultCharset();
switch(charset) {
case CHARSET_ISO8859_1: return "ISO-8859-1";
case CHARSET_ISO8859_2: return "ISO-8859-2";
case CHARSET_ISO8859_3: return "ISO-8859-3";
case CHARSET_ISO8859_4: return "ISO-8859-4";
case CHARSET_ISO8859_5: return "ISO-8859-5";
case CHARSET_ISO8859_6: return "ISO-8859-6";
case CHARSET_ISO8859_7: return "ISO-8859-7";
case CHARSET_ISO8859_8: return "ISO-8859-8";
case CHARSET_ISO8859_9: return "ISO-8859-9";
case CHARSET_ISO8859_10: return "ISO-8859-10";
case CHARSET_ISO8859_13: return "ISO-8859-13";
case CHARSET_ISO8859_14: return "ISO-8859-14";
case CHARSET_ISO8859_15: return "ISO-8859-15";
case CHARSET_ISO8859_16: return "ISO-8859-16";
case CHARSET_WIN1250: return "windows-1250";
case CHARSET_WIN1251: return "windows-1251";
case CHARSET_WIN1252: return "windows-1252";
case CHARSET_WIN1253: return "windows-1253";
case CHARSET_WIN1254: return "windows-1254";
case CHARSET_WIN1255: return "windows-1255";
case CHARSET_WIN1256: return "windows-1256";
case CHARSET_WIN1257: return "windows-1257";
case CHARSET_WIN1258: return "windows-1258";
// case CHARSET_KOI8_R:
// case CHARSET_CP852:
// case CHARSET_MJK:
case CHARSET_TOASCII: return "us-ascii";
case CHARSET_UTF8: return "UTF-8";
// case CHARSET_UNICODE:
default: return Null;
}
}
static const char hex_digits[] = "0123456789ABCDEF";
String UrlEncode(const char *p, const char *e)
{
StringBuffer out;
out.Reserve(e - p);
for(; p < e; p++)
{
const char *b = p;
while(p < e && (byte)*p > ' ' && (byte)*p < 127
&& (IsAlNum(*p) || *p == '.' || *p == '-' || *p == '_'))
p++;
if(p > b)
out.Cat(b, int(p - b));
if(p >= e)
break;
if(*p == ' ')
out << '+';
else
out << '%' << hex_digits[(*p >> 4) & 15] << hex_digits[*p & 15];
}
return out;
}
String UrlEncode(const char *s, int len)
{
return UrlEncode(s, s + len);
}
String UrlEncode(const String& s)
{
return UrlEncode(~s, s.GetLength());
}
String UrlDecode(const char *b, const char *e)
{
StringBuffer out;
byte d1, d2, d3, d4;
for(const char *p = b; p < e; p++)
if(*p == '+')
out.Cat(' ');
else if(*p == '%' && (d1 = ctoi(p[1])) < 16 && (d2 = ctoi(p[2])) < 16) {
out.Cat(d1 * 16 + d2);
p += 2;
}
else if(*p == '%' && (p[1] == 'u' || p[1] == 'U')
&& (d1 = ctoi(p[2])) < 16 && (d2 = ctoi(p[3])) < 16
&& (d3 = ctoi(p[4])) < 16 && (d4 = ctoi(p[5])) < 16) {
out.Cat(WString((d1 << 12) | (d2 << 8) | (d3 << 4) | d4, 1).ToString());
p += 5;
}
else
out.Cat(*p);
return out;
}
String UrlDecode(const char *s, int len)
{
return UrlDecode(s, s + len);
}
String UrlDecode(const String& s)
{
return UrlDecode(~s, s.GetLength());
}
String Base64Encode(const char *b, const char *e)
{
static const char encoder[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
if(b == e)
return Null;
int out = (int(e - b) + 2) / 3 * 4;
int rem = int(e - b) % 3;
e -= rem;
StringBuffer s(out);
char *p = s;
while(b < e)
{
p[0] = encoder[(b[0] >> 2) & 0x3F];
p[1] = encoder[((b[0] << 4) & 0x30) | ((b[1] >> 4) & 0x0F)];
p[2] = encoder[((b[1] << 2) & 0x3C) | ((b[2] >> 6) & 0x03)];
p[3] = encoder[b[2] & 0x3F];
b += 3;
p += 4;
}
if(rem == 1)
{
p[0] = encoder[(b[0] >> 2) & 0x3F];
p[1] = encoder[(b[0] << 4) & 0x30];
p[2] = p[3] = '=';
}
else if(rem == 2)
{
p[0] = encoder[(b[0] >> 2) & 0x3F];
p[1] = encoder[((b[0] << 4) & 0x30) | ((b[1] >> 4) & 0x0F)];
p[2] = encoder[(b[1] << 2) & 0x3C];
p[3] = '=';
}
return s;
}
String Base64Encode(const char *b, int len)
{
return Base64Encode(b, b + len);
}
String Base64Encode(const String& data)
{
return Base64Encode(~data, data.GetCount());
}
String Base64Decode(const char *b, const char *e)
{
static byte dec64[] =
{
/* 0x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 1x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 2x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F,
/* 3x */0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 4x */0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
/* 5x */0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 6x */0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
/* 7x */0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 8x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 9x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* Ax */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* Bx */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* Cx */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* Dx */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* Ex */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* Fx */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};
StringBuffer out;
byte c[4];
int pos = 0;
for(; b < e; b++)
if((byte)*b > ' ') {
byte ch = dec64[(byte)*b];
if(ch & 0xC0)
break;
c[pos++] = ch;
if(pos == 4) {
out.Cat((c[0] << 2) | (c[1] >> 4));
out.Cat((c[1] << 4) | (c[2] >> 2));
out.Cat((c[2] << 6) | (c[3] >> 0));
pos = 0;
}
}
if(pos >= 2) {
out.Cat((c[0] << 2) | (c[1] >> 4));
if(pos >= 3) {
out.Cat((c[1] << 4) | (c[2] >> 2));
if(pos >= 4)
out.Cat((c[2] << 6) | (c[3] >> 0));
}
}
return out;
}
String Base64Decode(const char *s, int len)
{
return Base64Decode(s, s + len);
}
String Base64Decode(const String& data)
{
return Base64Decode(~data, data.GetLength());
}
void HttpHeader::Clear()
{
first_line.Clear();
fields.Clear();
f1 = f2 = f3 = Null;
scgi = false;
}
int64 HttpHeader::GetContentLength() const
{
return Nvl(ScanInt64((*this)["content-length"]), (int64)0);
}
bool HttpHeader::ParseAdd(const String& hdrs)
{
StringStream ss(hdrs);
first_line = ss.GetLine();
while(!ss.IsEof()) {
String s = ss.GetLine();
if(s.IsEmpty()) break;
int q = s.Find(':');
if(q >= 0)
fields.Add(ToLower(s.Mid(0, q))) = TrimLeft(s.Mid(q + 1));
}
const char *s = first_line;
if((byte)*s <= ' ')
return false;
while(*s != ' ' && *s)
f1.Cat(*s++);
while(*s == ' ')
s++;
if(!*s)
return false;
while(*s != ' ' && *s)
f2.Cat(*s++);
while(*s == ' ')
s++;
f3 = s;
return true;
}
bool HttpHeader::Parse(const String& hdrs)
{
Clear();
return ParseAdd(hdrs);
}
int CharFilterScgiHttp(int c)
{
return c == '_' ? '-' : c;
}
bool HttpHeader::ParseSCGI(const String& scgi_hdr)
{
Clear();
scgi = true;
String key, uri, qs;
const char *b = scgi_hdr;
const char *e = scgi_hdr.End();
int64 content_length = Null;
for(const char *s = scgi_hdr; s < e; s++) {
if(*s == '\0') {
String h(b, s);
b = s + 1;
if(key.GetCount()) {
if(key.StartsWith("http_"))
fields.Add(Filter(key.Mid(5), CharFilterScgiHttp), h);
if(key == "content_length")
content_length = ScanInt64(h);
if(key == "request_method")
f1 = h;
if(key == "request_uri")
uri = h;
if(key == "query_string")
qs = h;
if(key == "server_protocol")
f3 = h;
key.Clear();
}
else
key = ToLower(h);
}
}
f2 = uri + qs;
first_line = f1 + ' ' + f2 + ' ' + f3;
if(!IsNull(content_length) && content_length && fields.Find("content-length") < 0)
fields.Add("content-length", AsString(content_length));
return false;
}
bool HttpHeader::Read(TcpSocket& socket)
{
Clear();
String h;
if(IsDigit(socket.Peek())) {
int len = 0;
while(IsDigit(socket.Peek()))
len = 10 * len + socket.Get() - '0';
if(socket.Get() != ':' || len < 0 || len > 10000000)
return false;
h = socket.GetAll(len);
if(socket.Get() != ',')
return false;
return ParseSCGI(h);
}
h = socket.GetLine();
if(h.IsVoid())
return false;
h << "\r\n";
for(;;) {
String s = socket.GetLine();
if(s.IsVoid())
return false;
if(s.IsEmpty()) break;
h << s << "\r\n";
}
return Parse(h);
}
int HttpHeader::GetCode() const
{
return ScanInt(f2);
}
bool HttpHeader::Request(String& method, String& uri, String& version)
{
method = GetMethod();
uri = GetURI();
version = GetVersion();
return true;
}
bool HttpHeader::Response(String& protocol, int& code, String& reason)
{
protocol = GetProtocol();
code = GetCode();
reason = GetReason();
return !IsNull(code);
}
bool HttpResponse(TcpSocket& socket, bool scgi, int code, const char *phrase,
const char *content_type, const String& data, const char *server)
{
String r;
r << (scgi ? "Status: " : "HTTP/1.1 ") << code << ' ' << phrase << "\r\n"
"Date: " << WwwFormat(GetUtcTime()) << "\r\n"
"Server: " << (server ? server : "U++ based server") << "\r\n"
"Connection: close\r\n";
if(data.GetCount())
r << "Content-Length: " << data.GetCount() << "\r\n";
if(content_type)
r << "Content-Type: " << content_type << "\r\n";
r << "\r\n";
LOG(r + data);
if(!socket.PutAll(r))
return false;
return data.GetCount() == 0 || socket.PutAll(data);
}
END_UPP_NAMESPACE
#include "Core.h"
NAMESPACE_UPP
String WwwFormat(Time tm)
{
static const char *dayofweek[] =
{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const char *month[] =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
return String().Cat()
<< dayofweek[DayOfWeek(tm)] << ", "
<< (int)tm.day << ' ' << month[tm.month - 1]
<< ' ' << (int)tm.year
<< ' ' << Sprintf("%2d:%02d:%02d +0100", tm.hour, tm.minute, tm.second);
}
String MIMECharsetName(byte charset)
{
if(charset == CHARSET_DEFAULT)
charset = GetDefaultCharset();
switch(charset) {
case CHARSET_ISO8859_1: return "ISO-8859-1";
case CHARSET_ISO8859_2: return "ISO-8859-2";
case CHARSET_ISO8859_3: return "ISO-8859-3";
case CHARSET_ISO8859_4: return "ISO-8859-4";
case CHARSET_ISO8859_5: return "ISO-8859-5";
case CHARSET_ISO8859_6: return "ISO-8859-6";
case CHARSET_ISO8859_7: return "ISO-8859-7";
case CHARSET_ISO8859_8: return "ISO-8859-8";
case CHARSET_ISO8859_9: return "ISO-8859-9";
case CHARSET_ISO8859_10: return "ISO-8859-10";
case CHARSET_ISO8859_13: return "ISO-8859-13";
case CHARSET_ISO8859_14: return "ISO-8859-14";
case CHARSET_ISO8859_15: return "ISO-8859-15";
case CHARSET_ISO8859_16: return "ISO-8859-16";
case CHARSET_WIN1250: return "windows-1250";
case CHARSET_WIN1251: return "windows-1251";
case CHARSET_WIN1252: return "windows-1252";
case CHARSET_WIN1253: return "windows-1253";
case CHARSET_WIN1254: return "windows-1254";
case CHARSET_WIN1255: return "windows-1255";
case CHARSET_WIN1256: return "windows-1256";
case CHARSET_WIN1257: return "windows-1257";
case CHARSET_WIN1258: return "windows-1258";
// case CHARSET_KOI8_R:
// case CHARSET_CP852:
// case CHARSET_MJK:
case CHARSET_TOASCII: return "us-ascii";
case CHARSET_UTF8: return "UTF-8";
// case CHARSET_UNICODE:
default: return Null;
}
}
static const char hex_digits[] = "0123456789ABCDEF";
String UrlEncode(const char *p, const char *e)
{
StringBuffer out;
out.Reserve(e - p);
for(; p < e; p++)
{
const char *b = p;
while(p < e && (byte)*p > ' ' && (byte)*p < 127
&& (IsAlNum(*p) || *p == '.' || *p == '-' || *p == '_'))
p++;
if(p > b)
out.Cat(b, int(p - b));
if(p >= e)
break;
if(*p == ' ')
out << '+';
else
out << '%' << hex_digits[(*p >> 4) & 15] << hex_digits[*p & 15];
}
return out;
}
String UrlEncode(const char *s, int len)
{
return UrlEncode(s, s + len);
}
String UrlEncode(const String& s)
{
return UrlEncode(~s, s.GetLength());
}
String UrlDecode(const char *b, const char *e)
{
StringBuffer out;
byte d1, d2, d3, d4;
for(const char *p = b; p < e; p++)
if(*p == '+')
out.Cat(' ');
else if(*p == '%' && (d1 = ctoi(p[1])) < 16 && (d2 = ctoi(p[2])) < 16) {
out.Cat(d1 * 16 + d2);
p += 2;
}
else if(*p == '%' && (p[1] == 'u' || p[1] == 'U')
&& (d1 = ctoi(p[2])) < 16 && (d2 = ctoi(p[3])) < 16
&& (d3 = ctoi(p[4])) < 16 && (d4 = ctoi(p[5])) < 16) {
out.Cat(WString((d1 << 12) | (d2 << 8) | (d3 << 4) | d4, 1).ToString());
p += 5;
}
else
out.Cat(*p);
return out;
}
String UrlDecode(const char *s, int len)
{
return UrlDecode(s, s + len);
}
String UrlDecode(const String& s)
{
return UrlDecode(~s, s.GetLength());
}
String Base64Encode(const char *b, const char *e)
{
static const char encoder[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
if(b == e)
return Null;
int out = (int(e - b) + 2) / 3 * 4;
int rem = int(e - b) % 3;
e -= rem;
StringBuffer s(out);
char *p = s;
while(b < e)
{
p[0] = encoder[(b[0] >> 2) & 0x3F];
p[1] = encoder[((b[0] << 4) & 0x30) | ((b[1] >> 4) & 0x0F)];
p[2] = encoder[((b[1] << 2) & 0x3C) | ((b[2] >> 6) & 0x03)];
p[3] = encoder[b[2] & 0x3F];
b += 3;
p += 4;
}
if(rem == 1)
{
p[0] = encoder[(b[0] >> 2) & 0x3F];
p[1] = encoder[(b[0] << 4) & 0x30];
p[2] = p[3] = '=';
}
else if(rem == 2)
{
p[0] = encoder[(b[0] >> 2) & 0x3F];
p[1] = encoder[((b[0] << 4) & 0x30) | ((b[1] >> 4) & 0x0F)];
p[2] = encoder[(b[1] << 2) & 0x3C];
p[3] = '=';
}
return s;
}
String Base64Encode(const char *b, int len)
{
return Base64Encode(b, b + len);
}
String Base64Encode(const String& data)
{
return Base64Encode(~data, data.GetCount());
}
String Base64Decode(const char *b, const char *e)
{
static byte dec64[] =
{
/* 0x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 1x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 2x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F,
/* 3x */0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 4x */0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
/* 5x */0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 6x */0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
/* 7x */0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 8x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 9x */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* Ax */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* Bx */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* Cx */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* Dx */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* Ex */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* Fx */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};
StringBuffer out;
byte c[4];
int pos = 0;
for(; b < e; b++)
if((byte)*b > ' ') {
byte ch = dec64[(byte)*b];
if(ch & 0xC0)
break;
c[pos++] = ch;
if(pos == 4) {
out.Cat((c[0] << 2) | (c[1] >> 4));
out.Cat((c[1] << 4) | (c[2] >> 2));
out.Cat((c[2] << 6) | (c[3] >> 0));
pos = 0;
}
}
if(pos >= 2) {
out.Cat((c[0] << 2) | (c[1] >> 4));
if(pos >= 3) {
out.Cat((c[1] << 4) | (c[2] >> 2));
if(pos >= 4)
out.Cat((c[2] << 6) | (c[3] >> 0));
}
}
return out;
}
String Base64Decode(const char *s, int len)
{
return Base64Decode(s, s + len);
}
String Base64Decode(const String& data)
{
return Base64Decode(~data, data.GetLength());
}
void HttpCookie::Clear()
{
id.Clear();
value.Clear();
domain.Clear();
path.Clear();
raw.Clear();
}
bool HttpCookie::Parse(const String& cookie)
{
Clear();
const char *s = cookie;
raw = cookie;
bool first = true;
while(s && *s) {
while(*s == ' ')
s++;
const char *e = strchr(s, ';');
if(!e)
e = s + strlen(s);
const char *eq = strchr(s, '=');
if(eq && eq < e) {
String h = String(s, eq);
if(first) {
id = h;
value = String(eq + 1, e);
first = false;
}
else {
h = ToLower(h);
if(h == "domain")
domain = String(eq + 1, e);
if(h == "path")
path = String(eq + 1, e);
}
}
if(*e == 0)
break;
s = e + 1;
}
return !first;
}
void HttpHeader::Clear()
{
first_line.Clear();
fields.Clear();
cookies.Clear();
f1 = f2 = f3 = Null;
scgi = false;
}
int64 HttpHeader::GetContentLength() const
{
return Nvl(ScanInt64((*this)["content-length"]), (int64)0);
}
void HttpHeader::Add(const String& id_, const String& value)
{
String id = ToLower(id_);
fields.Add(id, value);
if(id == "set-cookie") {
HttpCookie c;
if(c.Parse(value))
cookies.Add(c.id, c);
}
}
bool HttpHeader::ParseAdd(const String& hdrs)
{
StringStream ss(hdrs);
first_line = ss.GetLine();
while(!ss.IsEof()) {
String s = ss.GetLine();
if(s.IsEmpty()) break;
int q = s.Find(':');
if(q >= 0)
Add(ToLower(s.Mid(0, q)), TrimLeft(s.Mid(q + 1)));
}
const char *s = first_line;
if((byte)*s <= ' ')
return false;
while(*s != ' ' && *s)
f1.Cat(*s++);
while(*s == ' ')
s++;
if(!*s)
return false;
while(*s != ' ' && *s)
f2.Cat(*s++);
while(*s == ' ')
s++;
f3 = s;
return true;
}
bool HttpHeader::Parse(const String& hdrs)
{
Clear();
return ParseAdd(hdrs);
}
int CharFilterScgiHttp(int c)
{
return c == '_' ? '-' : c;
}
bool HttpHeader::ParseSCGI(const String& scgi_hdr)
{
Clear();
scgi = true;
String key, uri, qs;
const char *b = scgi_hdr;
const char *e = scgi_hdr.End();
int64 content_length = Null;
for(const char *s = scgi_hdr; s < e; s++) {
if(*s == '\0') {
String h(b, s);
b = s + 1;
if(key.GetCount()) {
if(key.StartsWith("http_"))
Add(Filter(key.Mid(5), CharFilterScgiHttp), h);
if(key == "content_length")
content_length = ScanInt64(h);
if(key == "request_method")
f1 = h;
if(key == "request_uri")
uri = h;
if(key == "query_string")
qs = h;
if(key == "server_protocol")
f3 = h;
key.Clear();
}
else
key = ToLower(h);
}
}
f2 = uri + qs;
first_line = f1 + ' ' + f2 + ' ' + f3;
if(!IsNull(content_length) && content_length && fields.Find("content-length") < 0)
fields.Add("content-length", AsString(content_length));
return false;
}
bool HttpHeader::Read(TcpSocket& socket)
{
Clear();
String h;
if(IsDigit(socket.Peek())) {
int len = 0;
while(IsDigit(socket.Peek()))
len = 10 * len + socket.Get() - '0';
if(socket.Get() != ':' || len < 0 || len > 10000000)
return false;
h = socket.GetAll(len);
if(socket.Get() != ',')
return false;
return ParseSCGI(h);
}
h = socket.GetLine();
if(h.IsVoid())
return false;
h << "\r\n";
for(;;) {
String s = socket.GetLine();
if(s.IsVoid())
return false;
if(s.IsEmpty()) break;
h << s << "\r\n";
}
return Parse(h);
}
int HttpHeader::GetCode() const
{
return ScanInt(f2);
}
bool HttpHeader::Request(String& method, String& uri, String& version) const
{
method = GetMethod();
uri = GetURI();
version = GetVersion();
return true;
}
String HttpHeader::GetCookie(const char *id) const
{
int q = cookies.Find(id);
return q < 0 ? String() : cookies[q].value;
}
bool HttpHeader::Response(String& protocol, int& code, String& reason) const
{
protocol = GetProtocol();
code = GetCode();
reason = GetReason();
return !IsNull(code);
}
bool HttpResponse(TcpSocket& socket, bool scgi, int code, const char *phrase,
const char *content_type, const String& data, const char *server)
{
String r;
r << (scgi ? "Status: " : "HTTP/1.1 ") << code << ' ' << phrase << "\r\n"
"Date: " << WwwFormat(GetUtcTime()) << "\r\n"
"Server: " << (server ? server : "U++ based server") << "\r\n"
"Connection: close\r\n";
if(data.GetCount())
r << "Content-Length: " << data.GetCount() << "\r\n";
if(content_type)
r << "Content-Type: " << content_type << "\r\n";
r << "\r\n";
LOG(r + data);
if(!socket.PutAll(r))
return false;
return data.GetCount() == 0 || socket.PutAll(data);
}
END_UPP_NAMESPACE

View file

@ -115,16 +115,16 @@ Note that the same key can be present multiple times.&]
be lower`-case) or empty string if such field is not present.&]
[s3;%% &]
[s4; &]
[s5;:HttpHeader`:`:Response`(String`&`,int`&`,String`&`): [@(0.0.255) bool]_[* Response](
[_^String^ String][@(0.0.255) `&]_[*@3 protocol], [@(0.0.255) int`&]_[*@3 code],
[_^String^ String][@(0.0.255) `&]_[*@3 reason])&]
[s2;%% Parses the [* first`_line ]as http response.&]
[s5;:HttpHeader`:`:Response`(String`&`,int`&`,String`&`)const: [@(0.0.255) bool]_[* Respo
nse]([_^String^ String][@(0.0.255) `&]_[*@3 protocol], [@(0.0.255) int`&]_[*@3 code],
[_^String^ String][@(0.0.255) `&]_[*@3 reason])_[@(0.0.255) const]&]
[s2;%% Returns first line fields of http response.&]
[s3;%% &]
[s4; &]
[s5;:HttpHeader`:`:Request`(String`&`,String`&`,String`&`): [@(0.0.255) bool]_[* Request](
[_^String^ String][@(0.0.255) `&]_[*@3 method], [_^String^ String][@(0.0.255) `&]_[*@3 uri],
[_^String^ String][@(0.0.255) `&]_[*@3 version])&]
[s2;%% Parses the [* first`_line ]as http request.&]
[s5;:HttpHeader`:`:Request`(String`&`,String`&`,String`&`)const: [@(0.0.255) bool]_[* Req
uest]([_^String^ String][@(0.0.255) `&]_[*@3 method], [_^String^ String][@(0.0.255) `&]_[*@3 u
ri], [_^String^ String][@(0.0.255) `&]_[*@3 version])_[@(0.0.255) const]&]
[s2;%% Returns first line fields of http request.&]
[s3;%% &]
[s4; &]
[s5;:HttpHeader`:`:GetProtocol`(`)const: [_^String^ String]_[* GetProtocol]()_[@(0.0.255) c