diff --git a/uppsrc/Core/Core.h b/uppsrc/Core/Core.h index 7de591391..01e32c46e 100644 --- a/uppsrc/Core/Core.h +++ b/uppsrc/Core/Core.h @@ -169,6 +169,33 @@ #endif #endif +#ifdef PLATFORM_WIN32 +#define W_P(w, p) w +#if !defined(PLATFORM_CYGWIN) +#include +#endif +typedef int socklen_t; +#endif + +#ifdef PLATFORM_POSIX + +#define W_P(w, p) p +#include +#include +#include +#include +//#include +enum +{ + INVALID_SOCKET = -1, + TCP_NODELAY = 1, + SD_RECEIVE = 0, + SD_SEND = 1, + SD_BOTH = 2, +}; +typedef int SOCKET; +#endif + #include #include #include @@ -281,6 +308,8 @@ NAMESPACE_UPP #include "LocalProcess.h" +#include "Web.h" + #include "Win32Util.h" #if (defined(HEAPDBG) || defined(TESTLEAKS)) && defined(PLATFORM_POSIX) diff --git a/uppsrc/Core/Core.upp b/uppsrc/Core/Core.upp index c8195ede2..4e3389cb0 100644 --- a/uppsrc/Core/Core.upp +++ b/uppsrc/Core/Core.upp @@ -145,6 +145,11 @@ file Hash.h, MD5.cpp, SHA1.cpp, + Web readonly separator, + Web.h, + WebUtil.cpp, + Socket.cpp, + Http.cpp, "Runtime linking" readonly separator, dli.h, dli_header.h, diff --git a/uppsrc/Core/Http.cpp b/uppsrc/Core/Http.cpp new file mode 100644 index 000000000..5ec83b1f0 --- /dev/null +++ b/uppsrc/Core/Http.cpp @@ -0,0 +1,488 @@ +#include "Core.h" + +NAMESPACE_UPP + +bool HttpRequest_Trace__; + +#ifdef _DEBUG +#define LLOG(x) LOG(x) +#else +#define LLOG(x) if(HttpRequest_Trace__) RLOG(x); else; +#endif + +void HttpRequest::Trace(bool b) +{ + HttpRequest_Trace__ = b; +} + +void HttpRequest::Init() +{ + port = 0; + proxy_port = 0; + max_header_size = DEFAULT_MAX_HEADER_SIZE; + max_content_size = DEFAULT_MAX_CONTENT_SIZE; + max_redirects = DEFAULT_MAX_REDIRECTS; + max_retries = DEFAULT_MAX_RETRIES; + force_digest = false; + std_headers = true; + hasurlvar = false; + method = METHOD_GET; + phase = START; + redirect_count = 0; + retry_count = 0; +} + +HttpRequest::HttpRequest() +{ + Init(); +} + +HttpRequest::HttpRequest(const char *url) +{ + Init(); + Url(url); +} + +HttpRequest& HttpRequest::Url(const char *u) +{ + const char *t = u; + while(*t && *t != '?') + if(*t++ == '/' && *t == '/') { + u = ++t; + break; + } + t = u; + while(*u && *u != ':' && *u != '/' && *u != '?') + u++; + if(*u == '?' && u[1]) + hasurlvar = true; + host = String(t, u); + port = 0; + if(*u == ':') + port = ScanInt(u + 1, &u); + path = u; + int q = path.Find('#'); + if(q >= 0) + path.Trim(q); + return *this; +} + +HttpRequest& HttpRequest::Proxy(const char *p) +{ + const char *t = p; + while(*p && *p != ':') + p++; + proxy_host = String(t, p); + proxy_port = 80; + if(*p++ == ':' && IsDigit(*p)) + proxy_port = ScanInt(p); + return *this; +} + +HttpRequest& HttpRequest::Post(const char *id, const String& data) +{ + POST(); + if(postdata.GetCount()) + postdata << '&'; + postdata << id << '=' << UrlEncode(data); + return *this; +} + +HttpRequest& HttpRequest::UrlVar(const char *id, const String& data) +{ + int c = *path.Last(); + if(hasurlvar && c != '&') + path << '&'; + if(!hasurlvar && c != '?') + path << '?'; + path << id << '=' << UrlEncode(data); + hasurlvar = true; + return *this; +} + +String HttpRequest::CalculateDigest(const String& authenticate) const +{ + const char *p = authenticate; + String realm, qop, nonce, opaque; + while(*p) { + if(!IsAlNum(*p)) { + p++; + continue; + } + else { + const char *b = p; + while(IsAlNum(*p)) + p++; + String var = ToLower(String(b, p)); + String value; + while(*p && (byte)*p <= ' ') + p++; + if(*p == '=') { + p++; + while(*p && (byte)*p <= ' ') + p++; + if(*p == '\"') { + p++; + while(*p && *p != '\"') + if(*p != '\\' || *++p) + value.Cat(*p++); + if(*p == '\"') + p++; + } + else { + b = p; + while(*p && *p != ',' && (byte)*p > ' ') + p++; + value = String(b, p); + } + } + if(var == "realm") + realm = value; + else if(var == "qop") + qop = value; + else if(var == "nonce") + nonce = value; + else if(var == "opaque") + opaque = value; + } + } + String hv1, hv2; + hv1 << username << ':' << realm << ':' << password; + String ha1 = MD5String(hv1); + hv2 << (method == METHOD_GET ? "GET" : method == METHOD_PUT ? "PUT" : method == METHOD_POST ? "POST" : "READ") + << ':' << path; + String ha2 = MD5String(hv2); + int nc = 1; + String cnonce = FormatIntHex(Random(), 8); + String hv; + hv << ha1 + << ':' << nonce + << ':' << FormatIntHex(nc, 8) + << ':' << cnonce + << ':' << qop << ':' << ha2; + String ha = MD5String(hv); + String auth; + auth << "username=" << AsCString(username) + << ", realm=" << AsCString(realm) + << ", nonce=" << AsCString(nonce) + << ", uri=" << AsCString(path) + << ", qop=" << AsCString(qop) + << ", nc=" << AsCString(FormatIntHex(nc, 8)) + << ", cnonce=" << cnonce + << ", response=" << AsCString(ha); + if(!IsNull(opaque)) + auth << ", opaque=" << AsCString(opaque); + return auth; +} + +HttpRequest& HttpRequest::Header(const char *id, const String& data) +{ + request_headers << id << ": " << data << "\r\n"; + return *this; +} + +void HttpRequest::HttpError(const char *s) +{ + error = NFormat(t_("%s:%d: ") + String(s), host, port); + LLOG("HTTP ERROR: " << error); + Close(); +} + +void HttpRequest::StartPhase(int s) +{ + LLOG("Starting status " << s); + phase = s; + data.Clear(); +} + +bool HttpRequest::Do() +{ + int c1, c2; + switch(phase) { + case START: + retry_count = 0; + redirect_count = 0; + StartRequest(); + break; + case REQUEST: + if(SendingData()) + break; + StartPhase(HEADER); + break; + case HEADER: + if(ReadingHeader()) + break; + StartBody(); + break; + case BODY: + if(ReadingBody()) + break; + Finish(); + break; + case CHUNK_HEADER: + ReadingChunkHeader(); + break; + case CHUNK_BODY: + if(ReadingBody()) + break; + c1 = Get(); + c2 = Get(); + if(c1 != '\r' || c2 != '\n') + HttpError("missing ending CRLF in chunked transfer"); + StartPhase(CHUNK_HEADER); + break; + case TRAILER: + if(ReadingHeader()) + break; + header.Parse(data); + Finish(); + break; + default: + NEVER(); + } + + if(phase != FAILED) + if(IsSocketError() || IsError()) + phase = FAILED; + else + if(IsTimeout()) { + HttpError("connection timed out"); + phase = FAILED; + } + else + if(IsAbort()) { + HttpError("connection was aborted"); + phase = FAILED; + } + + if(phase == FAILED) { + if(retry_count++ < max_retries) { + LLOG("HTTP retry on error " << GetErrorDesc()); + StartRequest(); + } + } + return phase != FINISHED && phase != FAILED; +} + +void HttpRequest::Finish() +{ + Close(); + if(status_code == 401 && !IsNull(username)) { + String authenticate = header["www-authenticate"]; + if(authenticate.GetCount() && redirect_count++ < max_redirects) { + LLOG("HTTP auth digest"); + Digest(CalculateDigest(authenticate)); + StartRequest(); + return; + } + } + if(status_code >= 300 && status_code < 400) { + String url = GetRedirectUrl(); + if(url.GetCount() && redirect_count++ < max_redirects) { + LLOG("HTTP redirect " << url); + Url(url); + StartRequest(); + retry_count = 0; + return; + } + } + if(GetHeader("content-encoding") == "gzip") + body = GZDecompress(body); + phase = FINISHED; +} + +void HttpRequest::ReadingChunkHeader() +{ + for(;;) { + int c = Get(); + if(c < 0) + break; + else + if(c == '\n') { + LLOG("Chunk header: " << data); + int n = ScanInt(~data, NULL, 16); + if(IsNull(n)) { + HttpError("invalid chunk header"); + break; + } + if(n == 0) { + StartPhase(TRAILER); + break; + } + count += n; + StartPhase(CHUNK_BODY); + break; + } + if(c != '\r') + data.Cat(c); + } +} + +String HttpRequest::GetRedirectUrl() +{ + String redirect_url = TrimLeft(header["location"]); + int q = redirect_url.Find('?'); + int p = path.Find('?'); + if(p >= 0 && q < 0) + redirect_url.Cat(path.Mid(p)); + return redirect_url; +} + +int HttpRequest::GetContentLength() +{ + return Nvl(ScanInt(header["content-length"]), -1); +} + +void HttpRequest::StartBody() +{ + LLOG("HTTP Header received: "); + LLOG(data); + header.Clear(); + if(!header.Parse(data)) { + HttpError("invalid HTTP header"); + return; + } + + if(!header.Response(protocol, status_code, response_phrase)) { + HttpError("invalid HTTP response"); + return; + } + + LLOG("HTTP status code: " << status_code); + + count = GetContentLength(); + + if(count > 0) + body.Reserve(count); + + if(method == METHOD_HEAD) + phase = FINISHED; + else + if(header["transfer-encoding"] == "chunked") { + count = 0; + StartPhase(CHUNK_HEADER); + } + else + StartPhase(BODY); + body.Clear(); +} + +bool HttpRequest::ReadingBody() +{ + LLOG("HTTP reading data " << count); + for(;;) { + int n = 2048; + if(count >= 0) + n = min(n, count - body.GetLength()); + String s = Get(n); + if(s.GetCount() + body.GetCount() > max_content_size) { + HttpError("content length exceeded " + AsString(max_content_size)); + return true; + } + body.Cat(s); + if(count < 0 ? IsEof() : body.GetCount() >= count) + return false; + } +} + +void HttpRequest::StartRequest() +{ + Close(); + ClearError(); + bool use_proxy = !IsNull(proxy_host); + + int p = use_proxy ? proxy_port : port; + if(!Connect(use_proxy ? proxy_host : host, p ? p : DEFAULT_HTTP_PORT)) + return; + + StartPhase(REQUEST); + count = 0; + String ctype = contenttype; + switch(method) { + case METHOD_GET: data << "GET "; break; + case METHOD_POST: + data << "POST "; + if(IsNull(ctype)) + ctype = "application/x-www-form-urlencoded"; + break; + case METHOD_PUT: + data << "PUT "; + if(IsNull(ctype)) + ctype = "application/x-www-form-urlencoded"; + break; + case METHOD_HEAD: data << "HEAD "; break; + default: NEVER(); // invalid method + } + String host_port = host; + if(port) + host_port << ':' << port; + String url; + url << "http://" << host_port << Nvl(path, "/"); + if(use_proxy) + data << url; + else + data << Nvl(path, "/"); + data << " HTTP/1.1\r\n"; + if(std_headers) { + data << "URL: " << url << "\r\n" + << "Host: " << host_port << "\r\n" + << "Connection: close\r\n" + << "Accept: " << Nvl(accept, "*/*") << "\r\n" + << "Accept-Encoding: gzip\r\n" + << "Agent: " << Nvl(agent, "Ultimate++ HTTP client") << "\r\n"; + if(postdata.GetCount()) + data << "Content-Length: " << postdata.GetCount() << "\r\n"; + if(ctype.GetCount()) + data << "Content-Type: " << ctype << "\r\n"; + } + if(use_proxy && !IsNull(proxy_username)) + data << "Proxy-Authorization: Basic " << Base64Encode(proxy_username + ':' + proxy_password) << "\r\n"; + if(!IsNull(digest)) + data << "Authorization: Digest " << digest << "\r\n"; + else + if(!force_digest && (!IsNull(username) || !IsNull(password))) + data << "Authorization: Basic " << Base64Encode(username + ":" + password) << "\r\n"; + data << request_headers << "\r\n" << postdata; // !!! POST PHASE !!! + LLOG("HTTP REQUEST " << host << ":" << port); + LLOG("HTTP request:\n" << data); +} + +bool HttpRequest::SendingData() +{ + for(;;) { + int n = min(2048, data.GetLength() - count); + n = Put(~data + count, n); + if(n == 0) + break; + count += n; + } + return count < data.GetLength(); +} + +bool HttpRequest::ReadingHeader() +{ + for(;;) { + int c = Get(); + if(c < 0) + return false; + else + data.Cat(c); + if(data.GetCount() > 3) { + const char *h = data.Last(); + if(h[0] == '\n' && (h[-1] == '\r' && h[-2] == '\n' || h[-1] == '\n')) + return false; + } + if(data.GetCount() > max_header_size) { + HttpError("HTTP header exceeded " + AsString(max_header_size)); + return true; + } + } +} + +String HttpRequest::Execute() +{ + while(Do()); + return IsSuccess() ? GetContent() : String::GetVoid(); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Core/Socket.cpp b/uppsrc/Core/Socket.cpp new file mode 100644 index 000000000..0e4b90567 --- /dev/null +++ b/uppsrc/Core/Socket.cpp @@ -0,0 +1,550 @@ +#include "Core.h" + +#ifdef PLATFORM_WIN32 +#include +#include +#include +#endif + +#ifdef PLATFORM_POSIX +#include +#endif + +NAMESPACE_UPP + +#ifdef PLATFORM_WIN32 +#pragma comment(lib, "ws2_32.lib") +#endif + +#define LLOG(x) // DLOG("TCP " << x) + +#ifdef PLATFORM_POSIX + +#define SOCKERR(x) x + +const char *TcpSocketErrorDesc(int code) +{ + return strerror(code); +} + +int TcpSocket::GetErrorCode() +{ + return errno; +} + +#else + +#define SOCKERR(x) WSA##x + +const char *TcpSocketErrorDesc(int code) +{ + static Tuple2 err[] = { + { WSAEINTR, "Interrupted function call." }, + { WSAEACCES, "Permission denied." }, + { WSAEFAULT, "Bad address." }, + { WSAEINVAL, "Invalid argument." }, + { WSAEMFILE, "Too many open files." }, + { WSAEWOULDBLOCK, "Resource temporarily unavailable." }, + { WSAEINPROGRESS, "Operation now in progress." }, + { WSAEALREADY, "Operation already in progress." }, + { WSAENOTSOCK, "TcpSocket operation on nonsocket." }, + { WSAEDESTADDRREQ, "Destination address required." }, + { WSAEMSGSIZE, "Message too long." }, + { WSAEPROTOTYPE, "Protocol wrong type for socket." }, + { WSAENOPROTOOPT, "Bad protocol option." }, + { WSAEPROTONOSUPPORT, "Protocol not supported." }, + { WSAESOCKTNOSUPPORT, "TcpSocket type not supported." }, + { WSAEOPNOTSUPP, "Operation not supported." }, + { WSAEPFNOSUPPORT, "Protocol family not supported." }, + { WSAEAFNOSUPPORT, "Address family not supported by protocol family." }, + { WSAEADDRINUSE, "Address already in use." }, + { WSAEADDRNOTAVAIL, "Cannot assign requested address." }, + { WSAENETDOWN, "Network is down." }, + { WSAENETUNREACH, "Network is unreachable." }, + { WSAENETRESET, "Network dropped connection on reset." }, + { WSAECONNABORTED, "Software caused connection abort." }, + { WSAECONNRESET, "Connection reset by peer." }, + { WSAENOBUFS, "No buffer space available." }, + { WSAEISCONN, "TcpSocket is already connected." }, + { WSAENOTCONN, "TcpSocket is not connected." }, + { WSAESHUTDOWN, "Cannot send after socket shutdown." }, + { WSAETIMEDOUT, "Connection timed out." }, + { WSAECONNREFUSED, "Connection refused." }, + { WSAEHOSTDOWN, "Host is down." }, + { WSAEHOSTUNREACH, "No route to host." }, + { WSAEPROCLIM, "Too many processes." }, + { WSASYSNOTREADY, "Network subsystem is unavailable." }, + { WSAVERNOTSUPPORTED, "Winsock.dll version out of range." }, + { WSANOTINITIALISED, "Successful WSAStartup not yet performed." }, + { WSAEDISCON, "Graceful shutdown in progress." }, + { WSATYPE_NOT_FOUND, "Class type not found." }, + { WSAHOST_NOT_FOUND, "Host not found." }, + { WSATRY_AGAIN, "Nonauthoritative host not found." }, + { WSANO_RECOVERY, "This is a nonrecoverable error." }, + { WSANO_DATA, "Valid name, no data record of requested type." }, + { WSASYSCALLFAILURE, "System call failure." }, + }; + const Tuple2 *x = FindTuple(err, __countof(err), code); + return x ? x->b : "Unknown error code."; +} + +int TcpSocket::GetErrorCode() +{ + return WSAGetLastError(); +} + +#endif + + +void TcpSocket::Init() +{ +#if defined(PLATFORM_WIN32) + ONCELOCK { + WSADATA wsadata; + WSAStartup(MAKEWORD(2, 2), &wsadata); + } +#endif +} + +void TcpSocket::Reset() +{ + is_eof = false; + socket = INVALID_SOCKET; + ipv6 = false; + ptr = end = buffer; + is_error = false; + is_timeout = false; + is_abort = false; +} + +TcpSocket::TcpSocket() +{ + ClearError(); + Reset(); + timeout = Null; + waitstep = 20; + global = false; +} + +bool TcpSocket::Open(int family, int type, int protocol) +{ + Init(); + CloseRaw(); + ClearError(); + if((socket = ::socket(family, type, protocol)) == INVALID_SOCKET) + return false; + LLOG("TcpSocket::Data::Open() -> " << (int)socket); +#ifdef PLATFORM_WIN32 + u_long arg = 1; + if(ioctlsocket(socket, FIONBIO, &arg)) + SetSockError("ioctlsocket(FIO[N]BIO)"); +#else + if(fcntl(socket, F_SETFL, (fcntl(socket, F_GETFL, 0) | O_NONBLOCK))) + SetSockError("fcntl(O_[NON]BLOCK)"); +#endif + return true; +} + +bool TcpSocket::Listen(int port, int listen_count, bool ipv6_, bool reuse) +{ + ipv6 = ipv6_; + if(!Open(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0)) + return false; + sockaddr_in sin; +#ifdef PLATFORM_WIN32 + SOCKADDR_IN6 sin6; + if(ipv6 && IsWinVista()) +#else + sockaddr_in6 sin6; + if(ipv6) +#endif + { + Zero(sin6); + sin.sin_family = AF_INET6; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = htonl(INADDR_ANY); + } + else { + Zero(sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + sin.sin_addr.s_addr = htonl(INADDR_ANY); + } + if(reuse) { + int optval = 1; + setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval)); + } + if(bind(socket, ipv6 ? (const sockaddr *)&sin6 : (const sockaddr *)&sin, + ipv6 ? sizeof(sin6) : sizeof(sin))) { + SetSockError(Format("bind(port=%d)", port)); + return false; + } + if(listen(socket, listen_count)) { + SetSockError(Format("listen(port=%d, count=%d)", port, listen_count)); + return false; + } + return true; +} + +bool TcpSocket::Accept(TcpSocket& ls) +{ + CloseRaw(); + if(timeout && !ls.WaitRead()) + return false; + if(!Open(ls.ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0)) + return false; + socket = accept(ls.GetSOCKET(), NULL, NULL); + if(socket == INVALID_SOCKET) { + SetSockError("accept"); + return false; + } + return true; +} + +String TcpSocket::GetPeerAddr() const +{ + if(!IsOpen()) + return Null; + sockaddr_in addr; + socklen_t l = sizeof(addr); + if(getpeername(socket, (sockaddr *)&addr, &l) != 0) + return Null; + if(l > sizeof(addr)) + return Null; +#ifdef PLATFORM_WIN32 + return inet_ntoa(addr.sin_addr); +#else + char h[200]; + return inet_ntop(AF_INET, &addr.sin_addr, h, 200); +#endif +} + +void TcpSocket::NoDelay() +{ + ASSERT(IsOpen()); + int __true = 1; + LLOG("NoDelay(" << (int)socket << ")"); + if(setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (const char *)&__true, sizeof(__true))) + SetSockError("setsockopt(TCP_NODELAY)"); +} + +void TcpSocket::Linger(int msecs) +{ + ASSERT(IsOpen()); + linger ls; + ls.l_onoff = !IsNull(msecs) ? 1 : 0; + ls.l_linger = !IsNull(msecs) ? (msecs + 999) / 1000 : 0; + if(setsockopt(socket, SOL_SOCKET, SO_LINGER, + reinterpret_cast(&ls), sizeof(ls))) + SetSockError("setsockopt(SO_LINGER)"); +} + +void TcpSocket::Attach(SOCKET s) +{ + CloseRaw(); + socket = s; +} + +bool TcpSocket::Connect(const char *host, int port) +{ + LLOG("TcpSocket::Data::OpenClient(" << host << ':' << port << ')'); + + Init(); + + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + addrinfo *result; + if(getaddrinfo(host, ~AsString(port), &hints, &result) || !result) { + SetSockError(Format("getaddrinfo(%s) failed", host)); + return false; + } + + addrinfo *rp = result; + for(;;) { + if(Open(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) { + if(connect(socket, rp->ai_addr, rp->ai_addrlen) == 0 || + GetErrorCode() == SOCKERR(EINPROGRESS) || GetErrorCode() == SOCKERR(EWOULDBLOCK)) + break; + CloseRaw(); + } + rp = rp->ai_next; + if(!rp) { + SetSockError(Format("unable to open or bind socket for %s", host)); + freeaddrinfo(result); + return false; + } + } + + freeaddrinfo(result); + return true; +} + +bool TcpSocket::CloseRaw() +{ + SOCKET old_socket = socket; + socket = INVALID_SOCKET; + if(old_socket != INVALID_SOCKET) { + LLOG("TcpSocket::CloseRaw(" << (int)old_socket << ")"); + int res; +#if defined(PLATFORM_WIN32) + res = closesocket(old_socket); +#elif defined(PLATFORM_POSIX) + res = close(old_socket); +#else + #error Unsupported platform +#endif + if(res && !IsError()) { + SetSockError("close"); + return false; + } + } + return true; +} + +bool TcpSocket::Close() +{ + if(socket == INVALID_SOCKET) + return false; + return !IsError() && WaitWrite() && CloseRaw(); +} + +bool TcpSocket::WouldBlock() +{ + int c = GetErrorCode(); +#ifdef PLATFORM_POSIX + return c == SOCKERR(EWOULDBLOCK) || c == SOCKERR(EAGAIN); +#endif +#ifdef PLATFORM_WIN32 + return c == SOCKERR(EWOULDBLOCK); +#endif +} + +int TcpSocket::Send(const void *buf, int amount) +{ + int res = send(socket, (const char *)buf, amount, 0); + if(res < 0 && WouldBlock()) + res = 0; + else + if(res == 0 || res < 0) + SetSockError("send"); + return res; +} + +void TcpSocket::Shutdown() +{ + ASSERT(IsOpen()); + if(shutdown(socket, SD_SEND)) + SetSockError("shutdown(SD_SEND)"); +} + +String TcpSocket::GetHostName() +{ + Init(); + char buffer[256]; + gethostname(buffer, __countof(buffer)); + return buffer; +} + +TcpSocket& TcpSocket::GlobalTimeout(int ms) +{ + global = true; + starttime = msecs(); + timeout = ms; + return *this; +} + +bool TcpSocket::Wait(dword flags) +{ + LLOG("Wait(" << timeout << ", " << flags << ")"); + if((flags & WAIT_READ) && ptr != end) + return true; + int end_time = (global ? starttime : msecs()) + timeout; + if(socket == INVALID_SOCKET) + return false; + is_timeout = false; + for(;;) { + if(IsError() || IsAbort()) + return false; + int to = end_time - msecs(); + if(WhenWait) + to = waitstep; + timeval *tvalp = NULL; + timeval tval; + if(!IsNull(timeout) || WhenWait) { + to = max(to, 0); + tval.tv_sec = to / 1000; + tval.tv_usec = 1000 * (to % 1000); + tvalp = &tval; + } + fd_set fdset[1]; + FD_ZERO(fdset); + FD_SET(socket, fdset); + int avail = select((int)socket + 1, + flags & WAIT_READ ? fdset : NULL, + flags & WAIT_WRITE ? fdset : NULL, NULL, tvalp); + LLOG("Wait select avail: " << avail); + if(avail < 0) { + SetSockError("wait"); + return false; + } + if(avail > 0) + return true; + if(to <= 0) { + is_timeout = true; + return false; + } + WhenWait(); + } +} + +int TcpSocket::Put(const char *s, int length) +{ + LLOG("Put(@ " << socket << ": " << length); + ASSERT(IsOpen()); + if(length < 0 && s) + length = (int)strlen(s); + if(!s || length <= 0 || IsError() || IsAbort()) + return 0; + done = 0; + bool peek = false; + while(done < length) { + if(peek && !WaitWrite()) + return done; + peek = false; + int count = Send(s + done, length - done); + if(IsError() || timeout == 0) + return done; + if(count > 0) + done += count; + else + peek = true; + } + LLOG("//Put() -> " << done); + return done; +} + +int TcpSocket::Recv(void *buf, int amount) +{ + int res = recv(socket, (char *)buf, amount, 0); + if(res == 0) + is_eof = true; + else + if(res < 0 && WouldBlock()) + res = 0; + else + if(res < 0) + SetSockError("recv"); + LLOG("recv(" << socket << "): " << res << " bytes: " + << AsCString((char *)buf, (char *)buf + min(res, 16)) + << (res ? "" : IsEof() ? ", EOF" : ", WOULDBLOCK")); + return res; +} + +void TcpSocket::ReadBuffer() +{ + ptr = buffer; + end = buffer + Recv(buffer, BUFFERSIZE); + if(ptr == end && timeout) { + WaitRead(); + end = buffer + Recv(buffer, BUFFERSIZE); + } +} + +int TcpSocket::Get_() +{ + if(!IsOpen() || IsError() || IsEof() || IsAbort()) + return -1; + ReadBuffer(); + return ptr < end ? *ptr++ : -1; +} + +int TcpSocket::Peek_() +{ + if(!IsOpen() || IsError() || IsEof() || IsAbort()) + return -1; + ReadBuffer(); + return ptr < end ? *ptr : -1; +} + +int TcpSocket::Get(void *buffer, int count) +{ + LLOG("Get " << count); + + if(!IsOpen() || IsError() || IsEof() || IsAbort()) + return 0; + + String out; + int l = end - ptr; + done = 0; + if(l > 0) + if(l < count) { + memcpy(buffer, ptr, l); + done += l; + ptr = end; + } + else { + memcpy(buffer, ptr, count); + ptr += count; + return count; + } + int part = Recv((char *)buffer + done, count - done); + if(part > 0) + done += part; + while(timeout != 0 && part >= 0 && done < count && !IsError() && !IsEof()) { + if(!WaitRead()) + break; + part = Recv((char *)buffer + done, count - done); + if(part > 0) + done += part; + } + return done; +} + +String TcpSocket::Get(int count) +{ + if(count == 0) + return Null; + StringBuffer out(count); + int done = Get(out, count); + if(!done && IsEof()) + return String::GetVoid(); + out.SetLength(done); + return out; +} + +String TcpSocket::GetLine(int maxlen) +{ + String ln; + for(;;) { + int c = Peek(); + if(c < 0) + return String::GetVoid(); + Get(); + if(c == '\n') + return ln; + if(c != '\r') + ln.Cat(c); + } +} + +void TcpSocket::SetSockError(const char *context, const char *errdesc) +{ + String err; + errorcode = GetErrorCode(); + if(socket != INVALID_SOCKET) + err << "socket(" << (int)socket << ") / "; + err << context << ": " << errdesc; + errordesc = err; + is_error = true; +} + +void TcpSocket::SetSockError(const char *context) +{ + SetSockError(context, TcpSocketErrorDesc(GetErrorCode())); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Core/Web.h b/uppsrc/Core/Web.h new file mode 100644 index 000000000..6f1adf057 --- /dev/null +++ b/uppsrc/Core/Web.h @@ -0,0 +1,280 @@ +String FormatIP(dword _ip); + +String UrlEncode(const String& s); +String UrlEncode(const String& s, const char *specials); +String UrlDecode(const char *b, const char *e); +inline String UrlDecode(const String& s) { return UrlDecode(s.Begin(), s.End() ); } + +String Base64Encode(const char *b, const char *e); +inline String Base64Encode(const String& data) { return Base64Encode(data.Begin(), data.End()); } +String Base64Decode(const char *b, const char *e); +inline String Base64Decode(const String& data) { return Base64Decode(data.Begin(), data.End()); } + +enum { WAIT_READ = 1, WAIT_WRITE = 2, WAIT_EXCEPTION = 3 }; + +class TcpSocket { + enum { BUFFERSIZE = 512 }; + SOCKET socket; + char buffer[BUFFERSIZE]; + char *ptr; + char *end; + bool is_eof; + bool is_error; + bool is_timeout; + bool is_abort; + bool ipv6; + + bool global; + int timeout; + int starttime; + int waitstep; + int done; + + int errorcode; + String errordesc; + + + bool CloseRaw(); + SOCKET AcceptRaw(dword *ipaddr, int timeout_msec); + bool Open(int family, int type, int protocol); + int Recv(void *buffer, int maxlen); + int Send(const void *buffer, int maxlen); + + void ReadBuffer(); + int Get_(); + int Peek_(); + + void Reset(); + + void SetSockError(const char *context, const char *errdesc); + void SetSockError(const char *context); + + static int GetErrorCode(); + static bool WouldBlock(); + +public: + Callback WhenWait; + + TcpSocket& Timeout(int ms) { timeout = ms; global = false; return *this; } + TcpSocket& GlobalTimeout(int ms); + TcpSocket& Blocking() { return Timeout(Null); } + + int GetDone() const { return done; } + + TcpSocket(); + ~TcpSocket() { Close(); } + + static void Init(); + + bool IsOpen() const { return socket != INVALID_SOCKET; } + bool IsEof() const { return is_eof && ptr == end; } + + bool IsError() const { return is_error; } + void ClearError() { is_error = false; errorcode = 0; errordesc.Clear(); } + int GetError() const { return errorcode; } + String GetErrorDesc() const { return errordesc; } + + bool IsTimeout() const { return is_timeout; } + void ClearTimeout() { is_timeout = false; } + + void Abort() { is_abort = true; } + bool IsAbort() const { return is_abort; } + void ClearAbort() { is_abort = false; } + + SOCKET GetSOCKET() const { return socket; } + String GetPeerAddr() const; + + void Attach(SOCKET socket); + bool Connect(const char *host, int port); + bool Listen(int port, int listen_count, bool ipv6 = false, bool reuse = true); + bool Accept(TcpSocket& listen_socket); + bool Close(); + void Shutdown(); + + void NoDelay(); + void Linger(int msecs); + void NoLinger() { Linger(Null); } + void Reuse(bool reuse = true); + + bool Wait(dword flags); + bool WaitRead() { return Wait(WAIT_READ); } + bool WaitWrite() { return Wait(WAIT_WRITE); } + + int Peek() { return ptr < end ? *ptr : Peek_(); } + int Term() { return Peek(); } + int Get() { return ptr < end ? *ptr++ : Get_(); } + int Get(void *buffer, int len); + String Get(int len); + int GetAll(void *buffer, int len) { return Get(buffer, len) == len; } + String GetAll(int len) { String s = Get(len); return s.GetCount() == len ? s : String::GetVoid(); } + String GetLine(int maxlen = 2000000); + + int Put(const char *s, int len); + int Put(const String& s) { return Put(s.Begin(), s.GetLength()); } + bool PutAll(const char *s, int len) { return Put(s, len) == len; } + bool PutAll(const String& s) { return Put(s) == s.GetCount(); } + + static String GetHostName(); +}; + +struct HttpHeader { + String first_line; + VectorMap fields; + + String operator[](const char *id) { return fields.Get(id, Null); } + + bool Response(String& protocol, int& code, String& reason); + bool Request(String& method, String& uri, String& version); + + void Clear(); + bool Parse(const String& hdrs); +}; + +class HttpRequest : public TcpSocket { + int phase; + String data; + int count; + + HttpHeader header; + + String error; + String body; + + enum { + DEFAULT_HTTP_PORT = 80, + DEFAULT_MAX_HEADER_SIZE = 1000000, + DEFAULT_MAX_CONTENT_SIZE = 10000000, + DEFAULT_MAX_REDIRECTS = 5, + DEFAULT_MAX_RETRIES = 3, + }; + + enum { + METHOD_GET, + METHOD_POST, + METHOD_HEAD, + METHOD_PUT, + }; + + int max_header_size; + int max_content_size; + int max_redirects; + int max_retries; + + String host; + int port; + String proxy_host; + int proxy_port; + String proxy_username; + String proxy_password; + String path; + + int method; + String accept; + String agent; + bool force_digest; + bool is_post; + bool std_headers; + bool hasurlvar; + String contenttype; + String username; + String password; + String digest; + String request_headers; + String postdata; + + String protocol; + int status_code; + String response_phrase; + + int retry_count; + int redirect_count; + + void Init(); + + void StartPhase(int s); + void StartBody(); + bool SendingData(); + bool ReadingHeader(); + bool ReadingBody(); + void StartRequest(); + void ReadingChunkHeader(); + void Finish(); + + void HttpError(const char *s); + String Execute0(); + + String CalculateDigest(const String& authenticate) const; + +public: + 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& Method(int m) { method = m; return *this; } + HttpRequest& GET() { return Method(METHOD_GET); } + HttpRequest& POST() { return Method(METHOD_POST); } + HttpRequest& HEAD() { return Method(METHOD_HEAD); } + HttpRequest& PUT() { return Method(METHOD_PUT); } + + HttpRequest& Host(const String& h) { host = h; return *this; } + HttpRequest& Port(int p) { port = p; return *this; } + HttpRequest& Path(const String& p) { path = p; return *this; } + HttpRequest& User(const String& u, const String& p) { username = u; password = p; return *this; } + HttpRequest& Digest() { force_digest = true; return *this; } + HttpRequest& Digest(const String& d) { digest = d; return *this; } + HttpRequest& Url(const char *url); + HttpRequest& UrlVar(const char *id, const String& data); + HttpRequest& operator()(const char *id, const String& data) { return UrlVar(id, data); } + HttpRequest& PostData(const String& pd) { postdata = pd; return *this; } + HttpRequest& PostUData(const String& pd) { return PostData(UrlEncode(pd)); } + HttpRequest& Post(const String& data) { POST(); return PostData(data); } + HttpRequest& Post(const char *id, const String& data); + + HttpRequest& Headers(const String& h) { request_headers = h; return *this; } + HttpRequest& ClearHeaders() { return Headers(Null); } + HttpRequest& AddHeaders(const String& h) { request_headers.Cat(h); return *this; } + HttpRequest& Header(const char *id, const String& data); + + HttpRequest& StdHeaders(bool sh) { std_headers = sh; return *this; } + HttpRequest& NoStdHeaders() { return StdHeaders(false); } + HttpRequest& Accept(const String& a) { accept = a; return *this; } + HttpRequest& Agent(const String& a) { agent = a; return *this; } + HttpRequest& ContentType(const String& a) { contenttype = a; return *this; } + + HttpRequest& Proxy(const String& host, int port) { proxy_host = host; proxy_port = port; return *this; } + HttpRequest& Proxy(const char *url); + HttpRequest& ProxyAuth(const String& u, const String& p) { proxy_username = u; proxy_password = p; return *this; } + + bool IsSocketError() const { return TcpSocket::IsError(); } + bool IsHttpError() const { return !IsNull(error) ; } + bool IsError() const { return IsSocketError() || IsHttpError(); } + String GetErrorDesc() const { return IsSocketError() ? TcpSocket::GetErrorDesc() : error; } + void ClearError() { TcpSocket::ClearError(); error.Clear(); } + + String GetHeader(const char *s) { return header[s]; } + String operator[](const char *s) { return GetHeader(s); } + String GetRedirectUrl(); + int GetContentLength(); + int GetStatusCode() const { return status_code; } + String GetResponsePhrase() const { return response_phrase; } + String GetContent() const { return body; } + + enum Phase { + START, REQUEST, HEADER, BODY, CHUNK_HEADER, CHUNK_BODY, TRAILER, FINISHED, FAILED + }; + + bool Do(); + int GetPhase() const { return phase; } + bool InProgress() const { return phase != FAILED && phase != FINISHED; } + bool IsFailure() const { return phase == FAILED; } + bool IsSuccess() const { return phase == FINISHED && status_code >= 200 && status_code < 300; } + + String Execute(); + + HttpRequest(); + HttpRequest(const char *url); + + + static void Trace(bool b = true); +}; diff --git a/uppsrc/Core/WebUtil.cpp b/uppsrc/Core/WebUtil.cpp new file mode 100644 index 000000000..8b6353bd6 --- /dev/null +++ b/uppsrc/Core/WebUtil.cpp @@ -0,0 +1,220 @@ +#include "Core.h" + +NAMESPACE_UPP + +String FormatIP(dword _ip) +{ + byte ip[4]; + Poke32be(ip, _ip); + return Format("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); +} + +static const char hex_digits[] = "0123456789ABCDEF"; + +String UrlEncode(const String& s) +{ + const char *p = s, *e = s.End(); + String out; + 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 String& s, const char *specials) +{ + int l = (int)strlen(specials); + const char *p = s, *e = s.End(); + String out; + for(; p < e; p++) + { + const char *b = p; + while(p < e && (byte)*p > ' ' && (byte)*p < 127 && memchr(specials, *p, l) == 0) + 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 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 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 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; +} + +void HttpHeader::Clear() +{ + first_line.Clear(); + fields.Clear(); +} + +bool HttpHeader::Parse(const String& hdrs) +{ + StringStream ss(hdrs); + String s = ss.GetLine(); + first_line = s; + while(!ss.IsEof()) { + 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)); + } + return true; +} + +bool HttpHeader::Request(String& method, String& uri, String& version) +{ + const char *s = first_line; + if((byte)*s <= ' ') + return false; + method.Clear(); + while(*s != ' ' && *s) + method.Cat(*s++); + while(*s == ' ') + s++; + if(!*s) + return false; + uri.Clear(); + while(*s != ' ' && *s) + uri.Cat(*s++); + while(*s == ' ') + s++; + if(!*s) + return false; + version = s; + return true; +} + +bool HttpHeader::Response(String& protocol, int& code, String& reason) +{ + String c; + if(Request(protocol, c, reason)) { + code = ScanInt(c); + return !IsNull(code); + } + return false; +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Web/Web.h b/uppsrc/Web/Web.h index d04e5f16e..cc6bf3db5 100644 --- a/uppsrc/Web/Web.h +++ b/uppsrc/Web/Web.h @@ -17,15 +17,6 @@ typedef int socklen_t; #include #include //#include -enum -{ - INVALID_SOCKET = -1, - TCP_NODELAY = 1, - SD_RECEIVE = 0, - SD_SEND = 1, - SD_BOTH = 2, -}; -typedef int SOCKET; #else #error Unsupported platform #endif//PLATFORM switch diff --git a/uppsrc/Web/httpsrv.cpp b/uppsrc/Web/httpsrv.cpp index 06e73e44e..77f19a4a6 100644 --- a/uppsrc/Web/httpsrv.cpp +++ b/uppsrc/Web/httpsrv.cpp @@ -44,7 +44,7 @@ String MIMECharsetName(byte charset) } } -HttpRequest::HttpRequest(HttpServer& server, pick_ Socket& _socket, HttpQuery query_) +HttpServerRequest::HttpServerRequest(HttpServer& server, pick_ Socket& _socket, HttpQuery query_) : server(server), socket(_socket), query(query_) , request_ticks(GetTickCount()) { @@ -104,31 +104,31 @@ HttpRequest::HttpRequest(HttpServer& server, pick_ Socket& _socket, HttpQuery qu } } - LLOG("HttpRequest:\n" << query); + LLOG("HttpServerRequest:\n" << query); } -void HttpRequest::LogTime(const char *s, int level) +void HttpServerRequest::LogTime(const char *s, int level) { server.LogTime(NFormat("(ID:%d)%s", (int)(uintptr_t)this, s), level); } -int HttpRequest::GetDuration() const +int HttpServerRequest::GetDuration() const { return GetTickCount() - request_ticks; } -void HttpRequest::Write(String header, String body) +void HttpServerRequest::Write(String header, String body) { Write(header, body, 200, "OK"); } -void HttpRequest::Write(String header, String body, int result_code, String result_text) +void HttpServerRequest::Write(String header, String body, int result_code, String result_text) { int duration = GetDuration(); if(IsNull(header)) header = query.GetString("$$DEFAULT_HEADER"); - LogTime(NFormat("HttpRequest::Write(%d): done in %d msecs: %d bytes\n%s", + LogTime(NFormat("HttpServerRequest::Write(%d): done in %d msecs: %d bytes\n%s", result_code, duration, body.GetLength(), header), 2); server.AddRequest(GetTickCount() - request_ticks); @@ -146,11 +146,11 @@ void HttpRequest::Write(String header, String body, int result_code, String resu // out.Cat('\0', 8192); ASSERT(socket.IsOpen()); server.AddWrite(socket, out); - LogTime(NFormat("HttpRequest::Write(%d): %d bytes added to delayed write list", + LogTime(NFormat("HttpServerRequest::Write(%d): %d bytes added to delayed write list", result_code, out.GetLength()), 2); } -void HttpRequest::Redirect(String url) +void HttpServerRequest::Redirect(String url) { Htmls body = NFormat(t_("If you're not redirected automatically please use %s."), HtmlLink(url) / t_("this link")); body = HtmlPage(t_("Redirection to another web address"), body); @@ -168,11 +168,11 @@ void HttpRequest::Redirect(String url) } out.Cat(body); server.AddWrite(socket, out); - LogTime(NFormat("HttpRequest::Redirect() added to delayed write list: %s", url), 2); + LogTime(NFormat("HttpServerRequest::Redirect() added to delayed write list: %s", url), 2); */ } -void HttpRequest::Error(String err) +void HttpServerRequest::Error(String err) { LogTime(NFormat("error after %d msecs: %s", GetDuration(), err), 0); Htmls body = GetHttpErrorPage(query, err, server.IsShowQuery()); @@ -419,7 +419,7 @@ bool HttpServer::Accept() #define FOURCHAR(a,b,c,d) ((int(a)) + (int(b) * 0x100) + (int(c) * 0x10000) + (int(d) * 0x1000000)) -One HttpServer::GetRequest() +One HttpServer::GetRequest() { Socket conn = connection; connection.Clear(); @@ -553,7 +553,7 @@ One HttpServer::GetRequest() if(request_version >= 1000) request_state = (four == FOURCHAR('P', 'O', 'S', 'T') ? RS_POST_HEADERS : RS_GET_HEADERS); else { // simple HTTP request without headers - One req = new HttpRequest(*this, conn, request_query); + One req = new HttpServerRequest(*this, conn, request_query); req->LogTime(GetHttpURI(request_query), 1); return req; } @@ -577,7 +577,7 @@ One HttpServer::GetRequest() return NULL; } request_query.Set("$$DEFAULT_HEADER", default_header); - One req = new HttpRequest(*this, conn, request_query); + One req = new HttpServerRequest(*this, conn, request_query); req->LogTime(GetHttpURI(request_query), 1); return req; } @@ -612,7 +612,7 @@ One HttpServer::GetRequest() break; } else { - One req = new HttpRequest(*this, conn, request_query); + One req = new HttpServerRequest(*this, conn, request_query); req->LogTime(GetHttpURI(request_query), 1); return req; } @@ -673,7 +673,7 @@ One HttpServer::GetRequest() else if(!strnicmp(content, mtag, 10)) GetHttpPostData(request_query, post_data); } - One req = new HttpRequest(*this, conn, request_query); + One req = new HttpServerRequest(*this, conn, request_query); req->LogTime(GetHttpURI(request_query), 1); return req; } @@ -713,7 +713,7 @@ One HttpServer::GetRequest() if(strm.IsError()) return NULL; query.Set(WID__DEFAULT_HEADER, default_header); - One req = new HttpRequest(*this, sconn, query); + One req = new HttpServerRequest(*this, sconn, query); req->LogTime(GetHttpURI(query)); return req; } @@ -799,7 +799,7 @@ One HttpServer::GetRequest() LogTime(String() << "HttpServer::GetRequest: " << sconn.GetError()); return NULL; } - One req = new HttpRequest(*this, sconn, query); + One req = new HttpServerRequest(*this, sconn, query); req->LogTime(GetHttpURI(query)); return req; */ diff --git a/uppsrc/Web/httpsrv.h b/uppsrc/Web/httpsrv.h index 97afff603..3bec1ac02 100644 --- a/uppsrc/Web/httpsrv.h +++ b/uppsrc/Web/httpsrv.h @@ -21,12 +21,12 @@ inline String HttpBinary() { return "applicatio class HttpServer; -class HttpRequest +class HttpServerRequest { friend class HttpServer; public: - HttpRequest(HttpServer& server, pick_ Socket& socket, HttpQuery query); + HttpServerRequest(HttpServer& server, pick_ Socket& socket, HttpQuery query); HttpServer& GetServer() { return server; } Socket& GetSocket() { return socket; } @@ -88,7 +88,7 @@ public: #ifdef PLATFORM_WIN32 void GetWaitEvents(Vector& events); #endif - One GetRequest(); + One GetRequest(); Time GetStartTime() const { return start_time; } double GetHitCount() const { return hit_count; } @@ -179,7 +179,7 @@ private: int trailing_count; }; -inline bool HttpRequest::IsLogging() const { return server.IsLogging(); } +inline bool HttpServerRequest::IsLogging() const { return server.IsLogging(); } inline String GetHttpPath(HttpQuery query) { return query.GetString("$$PATH"); } inline String GetHttpQuery(HttpQuery query) { return query.GetString("$$QUERY"); } diff --git a/uppsrc/Web/util.cpp b/uppsrc/Web/util.cpp index dc49e5298..ab9fc36d0 100644 --- a/uppsrc/Web/util.cpp +++ b/uppsrc/Web/util.cpp @@ -84,81 +84,6 @@ static String ToQuotedHtml(const String& s) return out; } -String FormatIP(dword _ip) -{ - byte ip[4]; - Poke32be(ip, _ip); - return Format("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); -} - -static const char hex_digits[] = "0123456789ABCDEF"; - -String UrlEncode(const String& s) -{ - const char *p = s, *e = s.End(); - String out; - 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 String& s, const char *specials) -{ - int l = (int)strlen(specials); - const char *p = s, *e = s.End(); - String out; - for(; p < e; p++) - { - const char *b = p; - while(p < e && (byte)*p > ' ' && (byte)*p < 127 && memchr(specials, *p, l) == 0) - 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 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 GetRandomIdent(int length) { StringBuffer ident(length); @@ -255,92 +180,6 @@ String BinHexDecode(const char *p, const char *e) return out; } -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 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 ASCII85Encode(const byte *p, int length) { StringBuffer out; diff --git a/uppsrc/Web/util.h b/uppsrc/Web/util.h index 0a5ae6e3e..be775b835 100644 --- a/uppsrc/Web/util.h +++ b/uppsrc/Web/util.h @@ -4,11 +4,6 @@ String WwwFormat(Time tm); bool IsSameTextFile(const char *p, const char *q); String StringSample(const char *s, int limit); -String FormatIP(dword _ip); -String UrlEncode(const String& s); -String UrlEncode(const String& s, const char *specials); -String UrlDecode(const char *b, const char *e); -inline String UrlDecode(const String& s) { return UrlDecode(s.Begin(), s.End() ); } String GetRandomIdent(int length); String OtpEncode(const String& password, const String& otp_key); String EncryptString(const String& password, const String& otp_key); @@ -18,10 +13,6 @@ String BinhexEncode(const char *b, const char *e); inline String BinhexEncode(const String& data) { return BinhexEncode(data.Begin(), data.End()); } String BinHexDecode(const char *b, const char *e); inline String BinHexDecode(const String& data) { return BinHexDecode(data.Begin(), data.End()); } -String Base64Encode(const char *b, const char *e); -inline String Base64Encode(const String& data) { return Base64Encode(data.Begin(), data.End()); } -String Base64Decode(const char *b, const char *e); -inline String Base64Decode(const String& data) { return Base64Decode(data.Begin(), data.End()); } String ASCII85Encode(const byte *p, int length); inline String ASCII85Encode(const String& s) { return ASCII85Encode(s, s.GetLength()); } String ASCII85Decode(const byte *p, int length);