diff --git a/uppsrc/Core/Http.cpp b/uppsrc/Core/Http.cpp index c348d87b8..fd718db66 100644 --- a/uppsrc/Core/Http.cpp +++ b/uppsrc/Core/Http.cpp @@ -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); diff --git a/uppsrc/Core/Inet.h b/uppsrc/Core/Inet.h index 59fff2da4..5574736a8 100644 --- a/uppsrc/Core/Inet.h +++ b/uppsrc/Core/Inet.h @@ -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; - One 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 > 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 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 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; + One 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 > 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 { + 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 fields; + VectorMap 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 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 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); diff --git a/uppsrc/Core/InetUtil.cpp b/uppsrc/Core/InetUtil.cpp index e3cfccea9..caf72cc6d 100644 --- a/uppsrc/Core/InetUtil.cpp +++ b/uppsrc/Core/InetUtil.cpp @@ -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 diff --git a/uppsrc/Core/src.tpp/Inet$en-us.tpp b/uppsrc/Core/src.tpp/Inet$en-us.tpp index 2cf146d38..f26701038 100644 --- a/uppsrc/Core/src.tpp/Inet$en-us.tpp +++ b/uppsrc/Core/src.tpp/Inet$en-us.tpp @@ -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