LightSpeed::JSON::PNode createArrayFromSet(LightSpeed::JSON::IFactory &factory, ConstStrA text, char sep) { LightSpeed::JSON::PNode arr = factory.newArray(); if (text.empty()) return arr; for (ConstStrA::SplitIterator iter = text.split(sep);iter.hasItems();) { ConstStrA name = iter.getNext(); if (!name.empty()) { arr->add(factory.newValue(name)); } } return arr; }
void HttpClient::proxyConnect(PNetworkStream stream, ConstStrA host, ConstStrA authorization) { if (host.find(':') == naturalNull) { return proxyConnect(stream,StringA(host+ConstStrA(":443")),authorization); } PNetworkStream nstream = new BufferedNetworkStream(stream); HttpRequest req(stream.get(), host, mCONNECT, !useHTTP10); if (!authorization.empty()) { req.setHeader(fldProxyAuthorization,authorization); } req.closeOutput(); class ResponseResult: public IHttpResponseCB { public: virtual void storeStatus(natural statusCode, ConstStrA statusMessage) { status = statusCode; statusMsg.append(statusMessage); } virtual void storeHeaderLine(ConstStrA, ConstStrA) { } natural status; AutoArray<char, SmallAlloc<32> > statusMsg; }; ResponseResult res; HttpResponse resp(stream.get(),res,HttpResponse::readHeadersNow); if (status != 200) throw HttpStatusException(THISLOCATION,host,res.status, res.statusMsg); }
std::pair<PExprNode, FilePath> FileCompiler::loadCode(ExprLocation loc, ConstStrA name) { FilePath p = loc.getFileName(); if (p.getPath().empty()) p = FilePath(ConstStrW(L"."), true); for (ConstStrA::SplitIterator iter = name.split('/'); iter.hasItems();) { ConstStrA part = iter.getNext(); if (part.empty()) { continue; } else if (part == "..") { p = p / parent; } else { p = p / part; } } const PExprNode *code = codeCache.find(p); PExprNode out; if (code) out = *code; else { out = compileFile(p); codeCache(p, out); } return std::make_pair(out, p); }
natural Server::onGET(BredyHttpSrv::IHttpRequest& req, ConstStrA vpath) { if (vpath.empty() && req.getHeaderField(req.fldUpgrade) == "websocket" ) { if (!wsenabled) return stForbidden; else return req.forwardRequestTo(&wsHandler,vpath); } else { return HttpHandler::onGET(req,vpath); } }
void HttpReqImpl::status(natural code, ConstStrA msg) { if (bHeaderSent) throw ErrorMessageException(THISLOCATION, "Headers already sent"); statusCode = code; if (msg.empty()) msg = getStatusMessage(statusCode); statusMsg = responseHdrPool.add(msg); }
void HttpReqImpl::errorPage(natural code, ConstStrA msg, ConstStrA expl) { if (!bHeaderSent) { if (msg.empty()) msg = getStatusMessage(code); SeqFileOutput f(this); if (msg.empty()) msg = getStatusMessage(code); PrintTextA print(f); //clear some headers - they can damage output //delete content length - because we will show different page responseHdrs.erase(getHeaderFieldName(fldContentLength)); //delete transfer encoding - because we need simplest encoding responseHdrs.erase(getHeaderFieldName(fldTransferEncoding)); //delete content type - because we will set to text/html responseHdrs.erase(getHeaderFieldName(fldContentType)); //delete eTag - don't store eTag with an error page responseHdrs.erase(getHeaderFieldName(fldETag)); //delete lastModified - don't store error page responseHdrs.erase(getHeaderFieldName(fldLastModified)); status(code,msg); //set new content type header(fldContentType,"text/html"); if (code == 204 || code == 304) { //set ContentLength to zero even if it is not necesery, some clients can have issue header(fldContentLength,"0"); sendHeaders(); return; } else { closeConn = true; } print("<html><head><title>%1 %2</title></head><body><h1>%1 %2</h1>") << code << msg; if (!expl.empty()) print("<pre>%1</pre>")<< expl; print("<hr>"); print("<small><em>Powered by Bredy's JsonRpcServer - C++ http & jsonrpc server - <a href=\"https://github.com/ondra-novak/jsonrpcserver\">sources available</a></em></small>"); print("</body></html>"); } else { closeConn = true; } }
LightSpeed::StringA dbOrderFromJSON(const LightSpeed::JSON::INode &nd, ConstStrA allowedSet) { using namespace LightSpeed; AutoArray<char, SmallAlloc<256> > buff; for (natural i = 0; i < nd.getEntryCount();i++) { ConstStrA fld = nd[i].getStringUtf8(); if (fld.empty() || fld == ConstStrA('^') || fld.find('`') != naturalNull) throw ErrorMessageException(THISLOCATION, "Unacceptable field name"); if (i) buff.append(ConstStrA(", ")); if (fld[0] == '^') { ConstStrA trans = findInAllowedSet(fld.offset(1),allowedSet); if (!trans.empty()) { appendField(trans, buff); buff.append(ConstStrA(" DESC")); } } else { ConstStrA trans = findInAllowedSet(fld,allowedSet); if (!trans.empty()) { appendField(trans, buff); buff.append(ConstStrA(" ASC")); } } } return ConstStrA(buff); }
ITCPServerConnHandler::Command HttpReqImpl::finishReadHeader() { TextParser<char,StaticAlloc<256> > parser; //open output and input, we need stream to show error pages outputClosed = false; inputClosed = false; isHeadMethod = ConstStrA(method) == "HEAD"; closeConn = httpMinVer == 0; //check connection HeaderValue strconn = getHeaderField(fldConnection); if (strconn.defined) { if (strconn == "close") closeConn = true; } //check content length HeaderValue strsize = getHeaderField(fldContentLength); if (strsize.defined && parser(" %u1 ", strsize)) { remainPostData = parser[1]; } else { remainPostData = 0; } //check chunked POST HeaderValue strchunked = getHeaderField(fldTransferEncoding); if (strchunked.defined != 0 && strchunked == "chunked") { chunkedPost = true; remainPostData = 0; //remainPostData is used to count chunks } else { chunkedPost = false; } //check expectation for 100-continue HeaderValue strexpect = getHeaderField(fldExpect); if (strexpect.defined != 0) { if (strexpect == "100-continue" || strexpect == "100-Continue") { bNeedContinue = true; } else { return errorPageKA(417); } } else { bNeedContinue = false; } //check host ConstStrA vpath = path; host = getHeaderField(fldHost); if (!mapHost(host, vpath)) { return errorPageKA(404); } if (vpath.empty()) { redirect("+/"); return processHandlerResponse(0); } //find handler IHttpHandler *h; curHandler = nil; natural res = callHandler( vpath, &h); if (h == 0) return errorPageKA(404); if (curHandler == nil) curHandler = h; //need this to correctly handle forward function return processHandlerResponse(res); }
ITCPServerConnHandler::Command HttpReqImpl::readHeader() { try { AutoArray<char, SmallAlloc<8192> > linebuff; NStream::Buffer &buffer = inout->getBuffer(); natural fetched = buffer.fetch(); if (fetched == 0) { errorPage(413,ConstStrA(),"Header is too large"); return ITCPServerConnHandler::cmdRemove; } natural pos = buffer.lookup(ConstBin("\r\n"),fetched); while (pos != naturalNull) { linebuff.resize(pos+2); buffer.read(linebuff.data(),pos+2); ConstStrA line = linebuff.head(pos); if (method.empty()) { if (pos == 0) return ITCPServerConnHandler::cmdWaitRead; reqBeginTime = TimeStamp::now(); reportDuration = true; cropWhite(line); ConstStrA::SplitIterator splt = line.split(' '); method = hdrPool.add(ConstStrA(splt.getNext())); path = hdrPool.add(ConstStrA(splt.getNext())); while (path.empty()) path = hdrPool.add(ConstStrA(splt.getNext())); protocol = hdrPool.add(ConstStrA(splt.getNext())); while (protocol.empty()) protocol = hdrPool.add(ConstStrA(splt.getNext())); //parse some fields TextParser<char,StaticAlloc<256> > parser; //check http version if (parser("HTTP/%u1.%u2",protocol)) { httpMajVer = (unsigned short)((natural)parser[1]); httpMinVer = (unsigned short)((natural)parser[2]); if (httpMajVer != 1 || (httpMinVer != 0 && httpMinVer != 1)) return errorPageKA(505); useHTTP11(httpMinVer == 1); } else { return errorPageKA(400,StringA(ConstStrA("Unknown protocol: ")+ protocol)); } } else if (line.empty()) { return finishReadHeader(); } else { natural dblcolon = line.find(':'); if (dblcolon == naturalNull) { errorPage(400,ConstStrA(),"line"); return ITCPServerConnHandler::cmdRemove; } ConstStrA field = line.head(dblcolon); ConstStrA value = line.offset(dblcolon+1); cropWhite(field); cropWhite(value); requestHdrs.insert(hdrPool.add(field),hdrPool.add(value)); } pos = buffer.lookup(ConstBin("\r\n")); } return ITCPServerConnHandler::cmdWaitRead; } catch (AllocatorLimitException &) { errorPage(413,ConstStrA(),"Header is too large (4096 bytes)"); return ITCPServerConnHandler::cmdRemove; } }
void HttpReqImpl::sendHeaders() { if (bHeaderSent) return; if (bNeedContinue) { remainPostData = 0; chunkedPost = false; } bNeedContinue = false; bool hasServer = false; bool hasContentType = false; bool hasTransfEnc = false; bool hasConnection = false; bool hasLength = false; bool hasDate = false; static ConstStrA contentTypeKey = getHeaderFieldName(fldContentType); static ConstStrA serverKey = getHeaderFieldName(fldServer); static ConstStrA transfEnc = getHeaderFieldName(fldTransferEncoding); static ConstStrA connectionStr = getHeaderFieldName(fldConnection); static ConstStrA contenLenStr = getHeaderFieldName(fldContentLength); static ConstStrA dateStr = getHeaderFieldName(fldDate); ConstStrA statusMsgStr = this->statusMsg; if (statusMsgStr.empty()) statusMsgStr = getStatusMessage(statusCode); PrintTextA print(*inout); print.setNL("\r\n"); print("HTTP/%1.%2 %3 %4\n") << httpMajVer << httpMinVer << statusCode << statusMsgStr; if (statusCode == 101) { hasTransfEnc = hasConnection = hasContentType = true; useChunked = false; remainPostData = naturalNull; switchedProtocol = true; TimeStamp reqEndTime = TimeStamp::now(); natural reqTime = (reqEndTime - reqBeginTime).getMilis(); logRequest(reqTime); } for (HeaderMap::Iterator iter = responseHdrs.getFwIter(); iter.hasItems();) { const HeaderMap::Entity hdrPair = iter.getNext(); if (!hasContentType && hdrPair.key == contentTypeKey) hasContentType = true; if (!hasServer && hdrPair.key == serverKey) hasServer = true; if (!hasTransfEnc && hdrPair.key == transfEnc) hasTransfEnc = !(useChunked && hdrPair.value == ConstStrA("chunked")); if (!hasConnection && hdrPair.key == connectionStr) hasConnection = true; if (!hasDate && hdrPair.key == dateStr) hasDate = true; if (!hasLength && hdrPair.key == contenLenStr) { hasLength = true; } print("%1: %2\n") << ConstStrA(hdrPair.key) << ConstStrA(hdrPair.value); } if (!hasContentType) print("%1: %2\n") << contentTypeKey << "text/html;charset=UTF-8"; if (!hasServer) print("%1: %2\n") << serverKey << serverIdent; if (hasLength) { useChunked = false; } if (!hasDate) { TimeStamp::RFC1123Time datenow = TimeStamp::now().asRFC1123Time(); print("%1: %2\n") << dateStr << ConstStrA(datenow); } if (!hasTransfEnc && useChunked && !closeConn) print("%1: %2\n") << transfEnc << "chunked"; else useChunked = false; if (!hasConnection && closeConn) print("%1: %2\n") << connectionStr << "close"; print("\n"); /* LogObject(THISLOCATION).progress("%7 - %3 %4 HTTP/%1.%2 %5 %6") << httpMajVer << httpMajVer << ConstStrA(method) << ConstStrA(path) << statusCode << statusMsgStr << getIfc<IHttpPeerInfo>().getPeerRealAddr();*/ responseHdrs.clear(); //for code 100 or 101, additional header will be next if (statusCode == 100) { //set status code 200 to simply processing reply (handler don't need to reset 100 status statusCode = 200; //unset message this->statusMsg = HdrStr(); //now, handler can exit function with status 100 - when data arrives, onData will be called } else { //header sent, prevent sending new headers bHeaderSent = true; } }
void HttpClient::createRequest(ConstStrA url, Method method) { connectionReused = false; TextParser<char, SmallAlloc<1024> > parser; if (parser("http%(?)[s]1://%(*)*2%%[a-zA-Z0-9-_.:]3/%(*)4",url)) { ConstStrA s = parser[1].str(); ConstStrA auth = parser[2].str(); ConstStrA domain_port = parser[3].str(); ConstStrA path = parser[4].str(); path = url.tail(path.length()+1); if (!s.empty() && s != "s") throw InvalidUrlException(THISLOCATION,url,"unknown protocol"); if (!auth.empty() && s.tail(1)[0] != '@') throw InvalidUrlException(THISLOCATION,url,"auth need '@' as separator"); if (domain_port.tail(1)[0] == ':') throw InvalidUrlException(THISLOCATION,url,"missing port number"); bool tls = !s.empty(); IHttpProxyProvider::Result proxyInfo; if (proxyProvider) { proxyInfo = proxyProvider->getProxy(domain_port); } if (canReuseConnection(domain_port, tls)) { try { response->skipRemainBody(); connectionReused = true; } catch (const NetworkException &) { response = nil; nstream = nil; } } if (!connectionReused) { if (tls) { if (httpsProvider == 0) { throw InvalidUrlException(THISLOCATION,url,"Https is not configured"); } PNetworkStream stream; if (proxyInfo.defined) { stream = connectSite(proxyInfo.proxyAddr,8080); proxyConnect(stream, domain_port, proxyInfo.authorization); } else { stream = connectSite(domain_port, 443); } stream = httpsProvider->connectTLS(stream,domain_port); nstream = new BufferedNetworkStream(stream); currentDomain = domain_port; currentTls = true; } else { if (proxyInfo.defined) { PNetworkStream stream; stream = connectSite(proxyInfo.proxyAddr,8080); nstream = new BufferedNetworkStream(stream); path = url; currentDomain = proxyInfo.proxyAddr; } else { PNetworkStream stream; stream = connectSite(domain_port,80); nstream = new BufferedNetworkStream(stream); currentDomain = domain_port; } } } response = nil; request = new(pool) HttpRequest(nstream.get(),path,method,!useHTTP10); if (proxyInfo.defined && !tls && !proxyInfo.authorization.empty()) { request->setHeader(fldProxyAuthorization, proxyInfo.authorization); } request->setHeader(fldHost, domain_port); request->setHeader(fldUserAgent, userAgent); if (keepAlive == false) { request->setHeader(fldConnection,"close"); } else if (useHTTP10) { request->setHeader(fldConnection,"keep-alive"); } } else { throw InvalidUrlException(THISLOCATION,url,"Parser rejected"); } }