IHTTPStream & WinHttpStream::connect() { if (method == ConstStrA("GET") || method == ConstStrA("HEAD")) setState(stReadResponse); else setState(stWriteRequest); return *this; }
void HttpReqImpl::logRequest(natural reqTime) { LogObject(THISLOCATION).progress("%7 - %10 %3 %4 HTTP/%1.%2 %5 %6 %8 (%9 ms)") << httpMajVer << httpMajVer << ConstStrA(method) << ConstStrA(path) << statusCode << statusMsg << getIfc<IHttpPeerInfo>().getPeerRealAddr() << requestName << reqTime << host; }
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); }
ITCPServerConnHandler::Command HttpReqImpl::onData(NStream& stream, EventType eventType, natural reason) { inout = &stream; try { if (curHandler != nil) { evType = eventType; wakeUpReason = reason; natural res = curHandler->onData(*this); return processHandlerResponse(res); } else if (eventType == evDataReady) { //only evDataReady is expected evType = evRequest; return readHeader(); } else { //otherwise it is invalid return ITCPServerConnHandler::cmdRemove; } } catch (HttpStatusException &e) { LogObject lg(THISLOCATION); lg.note("Status set by exception: %1") << e.getMessage(); useHTTP11(false); if (inout != 0) errorPage(e.getStatus(),e.getStatusMsg().getUtf8(),ConstStrA()); return ITCPServerConnHandler::cmdRemove; } catch (LightSpeed::NotImplementedExcption &e) { LogObject lg(THISLOCATION); lg.note("Http unhandled exception: %1") << e.getMessage(); useHTTP11(false); if (inout != 0) errorPage(501,ConstStrA(),e.getMessage().getUtf8()); return ITCPServerConnHandler::cmdRemove; } catch (std::exception &e) { LogObject lg(THISLOCATION); lg.note("Http unhandled exception: %1") << e.what(); useHTTP11(false); if (inout != 0) errorPage(500,ConstStrA(),e.what()); return ITCPServerConnHandler::cmdRemove; } catch (...) { LogObject lg(THISLOCATION); lg.note("Http unhandled exception: ...") ; useHTTP11(false); if (inout != 0) errorPage(500,ConstStrA()); return ITCPServerConnHandler::cmdRemove; } }
bool IniConfigT::Section::get(natural &val, const ConstStrA &var, natural index) const {try { const Str *r = getValue(var,index); if (r) { parser(" %1 ",ConstStrA(*r)); val = parser[1]; } return r != 0; } catch (...) { Exception::rethrow(THISLOCATION);throw; }}
natural WinHttpStream::write( const void *buffer, natural size ) { postBuffer.append(ArrayRef<const byte>(reinterpret_cast<const byte *>(buffer),size)); if (method == ConstStrA("GET")) method = ConstStrA("POST"); /* if (curState == stIdle) { setState(stWriteRequest); } TMWatch _w(*this); DWORD rd; BOOL res = InternetWriteFile(hHTTPConn,buffer,size,&rd); DWORD err = GetLastError(); if (res == FALSE) { throw FileMsgException(THISLOCATION,err,url,"Write failed"); }*/ return size; }
IniConfigT::Iterator::Iterator(const FieldMap &container, ConstStrA section, ConstStrA prefix) :innerIter(container.seek(SectionField(section,ConstStrA()))) { if (innerIter.hasItems()) { const FieldMap::Entity &e = innerIter.peek(); if (e.key.section == section) { this->section = e.key.section; if (e.key.field.head(prefix.length()) == prefix) { prefix = e.key.field.head(prefix.length()); } } } }
integer ServiceApp::onMessage(ConstStrA command, const Args & args, SeqFileOutput output) { if (command == ConstStrA(restartCmd) || command == ConstStrA(startForeCmd)) { return onMessage(startCmd,args,output); } if (command == ConstStrA(stopCmd)) { stop(); return 0; } if (command == ConstStrA(statusCmd)) { PrintTextA print(output); print("Service is running\n"); return 0; } if (command == ConstStrA(startCmd)) return 0; if (command == ConstStrA("restartDaemon")) { instance->restartDaemon(); } return integerNull; }
ConstValue LocalView::searchOneKey(const ConstValue &key) const { KeyToValue::Iterator iter = keyToValueMap.seek(KeyAndDocId(key, ConstStrA())); Container res = json.array(); while (iter.hasItems()) { const KeyToValue::KeyValue &kv = iter.getNext(); if (compareJson(key, kv.key.key) != cmpResultEqual) break; res.add(json("id",kv.key.docId) ("key",kv.key.key) ("value",kv.value.value) ("doc",kv.value.doc)); } return res; }
void HttpReqImpl::redirect(ConstStrA url, int code) { if (code == 0) { if (url.head(1) == ConstStrA('+')) code = 301; else code = 307; } TextParser<char> parser; if (parser("%[a-z]0://%",url)) { header(fldLocation, url); } else { StringA absurl = getAbsoluteUrl(url); header(fldLocation, absurl); } header(fldContentLength, "0"); status(code); sendHeaders(); }
bool IniConfigT::Section::get(bool &val, const ConstStrA &var, natural index) const {try { const Str *r = getValue(var,index); if (r) { if (*r == ConstStrA("true") || *r == ConstStrA("yes") || *r == ConstStrA("on")) val = true; else if (*r == ConstStrA("false") || *r == ConstStrA("no") || *r == ConstStrA("off")) val = false; else { natural v; parser(" %1 ",ConstStrA(*r)); v = parser[1]; val = v == 0?false:true; } } return r != 0; } catch (...) { Exception::rethrow(THISLOCATION);throw; }}
static void couchStoreAndRetrieveAttachment(PrintTextA &a) { CouchDB db(getTestCouch()); db.use(DATABASENAME); Document doc = db.newDocument(".data"); db.uploadAttachment(doc,"testAttachment","text/plain",[](SeqFileOutput wr) { SeqTextOutA txtwr(wr); ConstStrA sentence("The quick brown fox jumps over the lazy dog"); txtwr.blockWrite(sentence,true); }); Document doc2 = db.retrieveDocument(doc.getID()); AttachmentData data = db.downloadAttachment(doc2,"testAttachment"); a("%1-%2") << data.contentType << ConstStrA(reinterpret_cast<const char *>(data.data()),data.length()); }
void HttpReqImpl::clear() { method = ConstStrA(); requestHdrs.clear(); responseHdrs.clear(); hdrPool.clear(); responseHdrPool.clear(); statusMsg = HdrStr(); curHandler = nil; bHeaderSent = false; isHeadMethod = false; useChunked = false; inout = 0; remainPostData = 0; chunkedPost = false; requestContext = nil; statusCode=200; postBodyLimit = naturalNull; switchedProtocol = false; requestName = HdrStr(); }
ConstStrA WinHttpStream::getHeader( ConstStrA field ) { class FillHdrMap: public IEnumHeaders { public: HeaderMap &hdrmap; FillHdrMap(HeaderMap &hdrmap):hdrmap(hdrmap) {} virtual bool operator()(ConstStrA field, ConstStrA value) { hdrmap.insert(field, value); return false; } }; if (curState != stReadResponse) { setState(stReadResponse); hdrmap.clear(); FillHdrMap x(hdrmap); enumHeaders(x); } StringA *k = hdrmap.find(StringAI(field)); if (k == 0) return ConstStrA(); else return *k; }
bool ServiceApp::processRequest(const void *request) { const char *p = reinterpret_cast<const char*>(request); natural count = *p++; ConstStrA command(p); p += command.length() + 1; AutoArray<String,StaticAlloc<256> > params; AutoArray<ConstStrW,StaticAlloc<256> > wparams; for(natural i = 0;i < count;i++){ ConstStrA param(p); p += param.length() + 1; params.add(String(param)); wparams.add(ConstStrW(params[i])); } AutoArray<byte> output; output.resize(instance->getReplyMaxSize()); SeqOutputBuffer fakeout(output.data() + sizeof (integer), output.length() - sizeof (integer)); fakeout.setStaticObj(); integer res = -1; bool stop = false; SeqFileOutput out(&fakeout); try { res = onMessage(command, wparams, out); stop = command == ConstStrA(stopCmd); } catch(std::exception & e){ TextOut<SeqFileOutput> msg(out); msg("%1") << e.what(); res = -1; } try { *reinterpret_cast<integer*>(output.data()) = res; instance->sendReply(output.data(), fakeout.length() + sizeof (integer)); } catch(...){ } return !stop; }
static ConstStrA getStatusMessage(natural status) { char buff[3]; if (status >= 100 && status <= 999) { buff[0] = status / 100 + '0'; buff[1] = (status / 10) % 10 + '0'; buff[2] = status % 10 + '0'; ConstStrA srchText(buff, 3); natural l = 0, h = countof(statusMessages); while (l < h) { natural m = (l + h) / 2; ConstStrA msg = statusMessages[m]; ConstStrA code = msg.head(3); if (code > srchText) h = m; else if (code < srchText) l = m + 1; else return msg.offset(4); } } return ConstStrA("Unknown status code"); }
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; } }
integer ServiceApp::startCommand(ConstStrA command, const Args & args, SeqFileOutput &serr) { if (command == ConstStrA(startCmd)) { integer x = validateService(args,serr); if (x) return x; createInstanceFile(); x = onMessage(command,args,serr); if (x) return x; x = initService(args,serr); if (x) return x; serr = nil; instance->enterDaemonMode(restartDelaySec); return startService(); } else if (command == ConstStrA(startForeCmd)) { integer x = validateService(args,serr); if (x) return x; createInstanceFile(); x = onMessage(command,args,serr); if (x) return x; x = initService(args,serr); if (x) return x; return startService(); } else if (command == ConstStrA(restartCmd)){ integer x = validateService(args,serr); if (x) return x; try { instance->open(); postMessage(stopCmd,Args(), serr); instance->waitForTerminate(timeout); } catch (ProgInstance::NotRunningException &) { } catch (ProgInstance::TimeoutException &) { instance->terminate(); instance->waitForTerminate(timeout); } createInstanceFile(); x = onMessage(command,args,serr); if (x) return x; x = initService(args,serr); if (x) return x; serr = nil; instance->enterDaemonMode(restartDelaySec); return startService(); } else if (command == ConstStrA(stopCmd)) { try { instance->open(); integer res = postMessage(command,args, serr); if (res == 0) instance->waitForTerminate(timeout); return res; } catch (ProgInstance::TimeoutException &) { instance->terminate(); return 0; } } else if (command == ConstStrA(waitCmd)) { natural timeout = naturalNull; if (!args.empty()) { TextParser<wchar_t> parser; if (parser(L" %u1 ",args[0])) timeout = parser[1]; } instance->open(); instance->waitForTerminate(timeout); } else if (command == ConstStrA(testCmd)) { integer x = validateService(args, serr); return x; } else if (command == ConstStrA(runTestsCmd)) { if (args.empty()) return Singleton<TestCollector>::getInstance().runTests(ConstStrA(), serr); else if (args[0] == ConstStrW(L"list")) { SeqTextOutA out(serr); TextOut<SeqTextOutA> print(out); print("%1\n")<<(Singleton<TestCollector>::getInstance().listTests()); return 0; } else return Singleton<TestCollector>::getInstance().runTests(args[1], serr); } else { instance->open(); return postMessage(command,args, serr); } return 0; }
HeaderValue HttpReqImpl::getHeaderField(ConstStrA field) const { const HdrStr *val = requestHdrs.find(field); if (val == 0) return HeaderValue(); else return HeaderValue(ConstStrA(*val)); }
IniConfigT::Section IniConfigT::Section::openSection(const ConstStrA &name) const{ return Section(owner,StringA(section+ConstStrA(".")+name),ConstStrA()); }
ConstValue LocalView::searchRange(const ConstValue &startKey, const ConstValue &endKey, natural groupLevel, bool descending, natural offset, natural limit, ConstStrA offsetDoc, bool excludeEnd) const { Shared _(lock); KeyAndDocId startK(startKey,offsetDoc); KeyAndDocId endK(endKey,excludeEnd?ConstStrA():ConstStrA("\xEF\xBF\xBF\xEF\xBF\xBF\xEF\xBF\xBF\xEF\xBF\xBF")); KeyAndDocId *seekPos; KeyAndDocId *stopPos; Direction::Type dir; if (descending) { seekPos = &endK; stopPos = &startK; dir = Direction::backward; } else { seekPos = &startK; stopPos = &endK; dir = Direction::forward; } Optional<KeyToValue::Iterator> iter; if (seekPos->key != null) { iter = keyToValueMap.seek(*seekPos,0,dir); } else { if (descending) iter = keyToValueMap.getBkIter(); else iter = keyToValueMap.getFwIter(); } Optional<KeyToValue::Iterator> iend; if (stopPos->key != null) { bool found; iend = keyToValueMap.seek(*stopPos,&found,dir); } natural whLimit = naturalNull - offset < limit? offset+limit:naturalNull; Container rows = json.array(); ConstValue grows; while (iter->hasItems() && (iend == null || iend.value() != iter.value()) && whLimit > 0) { const KeyToValue::KeyValue &kv = iter->getNext(); if (offset == 0) rows.add(json("id",kv.key.docId) ("key",kv.key.key) ("value",kv.value.value) ("doc",kv.value.doc)); else offset--; whLimit --; } if (groupLevel != naturalNull) { if (groupLevel == 0) { grows = runReduce(rows); } else { Container res; Container collect; ConstValue lastKey = null; res = json.array(); collect = json.array(); rows->enumEntries(JSON::IEntryEnum::lambda([&](const ConstValue &val, ConstStrA, natural){ ConstValue key = val["key"]; if (!canGroupKeys(key, lastKey)) { if (lastKey != null) { ConstValue z = runReduce(collect); res.add(json("key",lastKey)("value", z)); } lastKey = sliceKey(key, groupLevel, json); collect.clear(); } collect.add(val); return false; })); if (!collect.empty()) { ConstValue z = runReduce(collect); res.add(json("key",lastKey)("value", z)); } grows = res; } } else { grows = rows; } return json("rows",grows)("total_rows",keyToValueMap.length()); }
bool WinHttpStream::canWrite() const { return (method != ConstStrA("GET") && method != ConstStrA("HEAD")) || hHTTPConn == 0; }
ITCPServerConnHandler::Command HttpReqImpl::errorPageKA(natural code, ConstStrA expl) { errorPage(code,ConstStrA(),expl); finish(); 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; } }
ConstStrA::Iterator IniConfigT::errorIncludeCB(ConstStrA) { return ConstStrA().getFwIter(); }