void cvolsm_NameToObject ( qcom_sGet *get ) { pwr_tStatus sts; gdb_sObject *op = NULL; cdh_sParseName ParseName; cdh_sParseName *pn; net_sNameToObject *mp = get->data; gdb_AssumeUnlocked; pn = cdh_ParseName(&sts, &ParseName, mp->poid, mp->name, 0); if (pn == NULL) { respondError(get, pwr_cNObjid, sts); return; } gdb_ScopeLock { /* Don't allow mount translation. */ op = vol_NameToObject(&sts, pn, gdb_mLo_owned, mp->trans & ~vol_mTrans_mount); if (op == NULL) { respondError(get, pwr_cNObjid, sts); } else { respondObject(get, op, mp->lcount, mp->rcount); } } gdb_ScopeUnlock; }
void startRetry() { connect(zhttpRequest, SIGNAL(error()), SLOT(zhttpRequest_error())); connect(zhttpRequest, SIGNAL(paused()), SLOT(zhttpRequest_paused())); state = WaitingForResponse; bool isHttps = (requestData.uri.scheme() == "https"); QString host = requestData.uri.host(); QByteArray encPath = requestData.uri.path(QUrl::FullyEncoded).toUtf8(); // look up the route route = domainMap->entry(DomainMap::Http, isHttps, host, encPath); if(route.isNull()) { log_warning("requestsession: %p %s has 0 routes", q, qPrintable(host)); state = WaitingForResponse; respondError(502, "Bad Gateway", QString("No route for host: %1").arg(host)); return; } log_debug("proxysession: %p %s has %d routes", q, qPrintable(host), route.targets.count()); }
void handleSocket(Session *s) { if(s->path == "/websocket") { s->pending = true; pendingSessions += s; emit q->sessionReady(); return; } else { QList<QByteArray> parts = s->path.mid(1).split('/'); if(parts.count() == 3) { QByteArray sid = parts[1]; QByteArray lastPart = parts[2]; s->sid = sid; s->lastPart = lastPart; s->pending = true; pendingSessions += s; emit q->sessionReady(); return; } respondError(s->sock, 404, "Not Found", "Not Found"); } }
ConnCheckWorker::ConnCheckWorker(ZrpcRequest *req, ZrpcManager *proxyControlClient, StatsManager *stats, QObject *parent) : Deferred(parent), req_(req) { req_->setParent(this); QVariantHash args = req_->args(); if(!args.contains("ids") || args["ids"].type() != QVariant::List) { respondError("bad-request"); return; } QVariantList vids = args["ids"].toList(); foreach(const QVariant &vid, vids) { if(vid.type() != QVariant::ByteArray) { respondError("bad-request"); return; } cids_ += QString::fromUtf8(vid.toByteArray()); } foreach(const QString &cid, cids_) { if(!stats->checkConnection(cid.toUtf8())) missing_ += cid; } if(!missing_.isEmpty()) { // ask the proxy about any cids we don't know about Deferred *d = ControlRequest::connCheck(proxyControlClient, missing_, this); connect(d, SIGNAL(finished(const DeferredResult &)), SLOT(proxyConnCheck_finished(const DeferredResult &))); return; } doFinish(); }
void processRequestInput(Session *s) { s->reqBody += s->req->readBody(MAX_REQUEST_BODY - s->reqBody.size() + 1); if(s->reqBody.size() > MAX_REQUEST_BODY) { respondError(s->req, 400, "Bad Request", "Request too large."); return; } if(s->req->isInputFinished()) handleRequest(s); }
void cvolsm_OidToObject ( qcom_sGet *get ) { pwr_tStatus sts; gdb_sObject *op; net_sOidToObject *mp = get->data; gdb_AssumeUnlocked; gdb_ScopeLock { op = hash_Search(&sts, gdbroot->oid_ht, &mp->oid); if (op == NULL || !op->l.flags.b.isOwned) { respondError(get, pwr_cNObjid, GDH__NOSUCHOBJ); } else { respondObject(get, op, mp->lcount, mp->rcount); } } gdb_ScopeUnlock; }
// emits signals, but safe to delete after void trySend() { QPointer<QObject> self = this; stuffToRead = false; ZurlResponsePacket resp; if(!sentHeader) { resp.code = hreq->responseCode(); resp.reason = hreq->responseStatus(); resp.headers = hreq->responseHeaders(); sentHeader = true; } // note: we always set body, even if empty if(outStream) { // note: we skip credits handling if quiet mode QByteArray buf; if(!quiet) buf = hreq->readResponseBody(outCredits); else buf = hreq->readResponseBody(); // all if(!buf.isEmpty()) { if(maxResponseSize != -1 && bytesReceived + buf.size() > maxResponseSize) { respondError("max-size-exceeded"); return; } } if(!buf.isNull()) resp.body = buf; else resp.body = ""; bytesReceived += resp.body.size(); if(!quiet) outCredits -= resp.body.size(); resp.more = (hreq->bytesAvailable() > 0 || !hreq->isFinished()); if(hreq->bytesAvailable() > 0) stuffToRead = true; } else { if(!inbuf.isNull()) { resp.body = inbuf; inbuf.clear(); } else resp.body = ""; } writeResponse(resp); if(!self) return; if(!resp.more) { cleanup(); emit q->finished(); } }
void respondCannotAccept() { respondError(500, "Internal Server Error", "Accept service unavailable."); }
void respondBadRequest(const QString &errorString) { respondError(400, "Bad Request", errorString); }
void processIncomingRequest() { if(state == Prefetching) { if(prefetchSize > 0) in += zhttpRequest->readBody(prefetchSize - in.size()); if(in.size() >= prefetchSize || zhttpRequest->isInputFinished()) { // we've read enough body to start inspection disconnect(zhttpRequest, SIGNAL(readyRead()), this, SLOT(zhttpRequest_readyRead())); state = Inspecting; requestData.body = in.toByteArray(); bool truncated = (!zhttpRequest->isInputFinished() || zhttpRequest->bytesAvailable() > 0); assert(!inspectRequest); if(inspectManager) { inspectRequest = new InspectRequest(inspectManager, this); if(inspectChecker->isInterfaceAvailable()) { connect(inspectRequest, SIGNAL(finished()), SLOT(inspectRequest_finished())); inspectChecker->watch(inspectRequest); inspectRequest->start(requestData, truncated, route.session); } else { inspectChecker->watch(inspectRequest); inspectChecker->give(inspectRequest); inspectRequest->start(requestData, truncated, route.session); inspectRequest = 0; } } if(!inspectRequest) { log_debug("inspect not available"); QMetaObject::invokeMethod(this, "doInspectError", Qt::QueuedConnection); } } } else if(state == Receiving) { in += zhttpRequest->readBody(MAX_SHARED_REQUEST_BODY - in.size()); if(in.size() >= MAX_SHARED_REQUEST_BODY || zhttpRequest->isInputFinished()) { // we've read as much as we can for now. if there is still // more to read, then the engine will notice this and // disallow sharing before passing to proxysession. at that // point, proxysession will read the remainder of the data disconnect(zhttpRequest, SIGNAL(readyRead()), this, SLOT(zhttpRequest_readyRead())); state = WaitingForResponse; requestData.body = in.take(); emit q->inspected(idata); } } else if(state == ReceivingForAccept) { QByteArray buf = zhttpRequest->readBody(); if(in.size() + buf.size() > MAX_ACCEPT_REQUEST_BODY) { respondError(413, "Request Entity Too Large", QString("Body must not exceed %1 bytes").arg(MAX_ACCEPT_REQUEST_BODY)); return; } in += buf; if(zhttpRequest->isInputFinished()) { if(acceptManager) zhttpRequest->pause(); else respondCannotAccept(); } } }
/** Responds with messages that are in the json queue for this clients session * If the queue is empty, it doesnt send anything and returns false. * if it does respond with SOMETHING, even an error-response, it returns true. * Use abort to send an empty resonse without looking at the queue at all. * When longpoll is set to false, it always responds with data, even if the queue is empty. In this case if wont affect a waiting longpoll netid. */ bool respondJsonQueue(bool abort=false, bool longpoll=true) { string jsonStr; if (abort) { //to abort we need to reply with an empty json array: DEB(id << " cancelling longpoll"); jsonStr="[]"; httpSessionMan.endGet(id, mAuthCookie); } else { if (longpoll) { ThttpCookie authCookieClone=0; try { authCookieClone=mHeaders["x-synapse-authcookie-clone"]; } catch(...){ }; //check if there are messages in the queue, based on the authcookie from the current request: //This function will change authCookie if neccesary and fill jsonStr! httpSessionMan.getJsonQueue(id, mAuthCookie, jsonStr, authCookieClone); //authcookie was probably expired, respond with error if (!mAuthCookie) { respondError(400, "Session is expired, or session limit reached."); return(true); } } else { //we DO want to respond with something if its there, but we dont want to do long polling httpSessionMan.getJsonQueue(mAuthCookie, jsonStr); if (jsonStr=="") jsonStr="[]"; } } if (jsonStr=="") { //nothing to reply (yet), return false return(false); } else { //send headers Cvar extraHeaders; extraHeaders["Content-Length"]=jsonStr.length(); extraHeaders["Cache-Control"]="no-cache"; extraHeaders["Content-Type"]="application/json"; extraHeaders["X-Synapse-Authcookie"]=mAuthCookie; sendHeaders(200, extraHeaders); //write the json queue sendData(asio::buffer(jsonStr)); return(true); } }
void respondBadRequest(const QString &body) { respondError(400, "Bad Request", body); }
void handleRequest(Session *s) { QString method = s->req->requestMethod(); log_debug("sockjs request: path=[%s], asUri=[%s]", s->path.data(), s->asUri.toEncoded().data()); if(method == "OPTIONS") { respondEmpty(s->req); } else if(method == "GET" && s->path == "/info") { quint32 x = (quint32)qrand(); QVariantMap out; out["websocket"] = true; out["origins"] = QVariantList() << QString("*:*"); out["cookie_needed"] = false; out["entropy"] = x; respondOk(s->req, out); } else if(method == "GET" && s->path.startsWith("/iframe") && s->path.endsWith(".html")) { HttpHeaders headers; headers += HttpHeader("ETag", iframeHtmlEtag); QByteArray ifNoneMatch = s->req->requestHeaders().get("If-None-Match"); if(ifNoneMatch == iframeHtmlEtag) { respond(s->req, 304, "Not Modified", headers, QByteArray()); } else { headers += HttpHeader("Content-Type", "text/html; charset=UTF-8"); headers += HttpHeader("Cache-Control", "public, max-age=31536000"); respond(s->req, 200, "OK", headers, iframeHtml); } } else { QList<QByteArray> parts = s->path.mid(1).split('/'); if(parts.count() == 3) { QByteArray sid = parts[1]; QByteArray lastPart = parts[2]; Session *existing = sessionsById.value(sid); if(existing) { if(existing->ext) { // give to external session ZhttpRequest *req = s->req; QByteArray body = s->reqBody.toByteArray(); QByteArray jsonpCallback = s->jsonpCallback; s->req->disconnect(this); s->req = 0; removeSession(s); existing->ext->handleRequest(req, jsonpCallback, lastPart, body); } else { if(existing->closeValue.isValid()) { respondOk(s->req, existing->closeValue, "c", s->jsonpCallback); } else { QVariantList out; out += 2010; out += QString("Another connection still open"); respondOk(s->req, out, "c", s->jsonpCallback); } } return; } if((method == "POST" && lastPart == "xhr") || ((method == "GET" || method == "POST") && lastPart == "jsonp")) { if(lastPart == "jsonp" && s->jsonpCallback.isEmpty()) { respondError(s->req, 400, "Bad Request", "Bad Request"); return; } s->sid = sid; s->lastPart = lastPart; sessionsById.insert(s->sid, s); s->pending = true; pendingSessions += s; emit q->sessionReady(); return; } } respondError(s->req, 404, "Not Found", "Not Found"); } }
// Received new data: void received(int id, asio::streambuf &readBuffer, std::size_t bytesTransferred) { string dataStr(boost::asio::buffer_cast<const char*>(readBuffer.data()), readBuffer.size()); string error; //we're expecting a new request: if (mState==REQUEST || mState==WAIT_LONGPOLL) { //if there is still an outstanding longpoll, cancel it: if (mState==WAIT_LONGPOLL) { respond(true); } //resize data to first delimiter: dataStr.resize(dataStr.find(delimiter)+delimiter.length()); readBuffer.consume(dataStr.length()); DEB(id << " got http REQUEST: \n" << dataStr); //determine the kind of request: smatch what; if (!regex_search( dataStr, what, boost::regex("^(GET|POST) ([^? ]*)([^ ]*) HTTP/(1..)$") )) { error="Cant parse request."; } else { mRequestType=what[1]; mRequestUrl=what[2]; mRequestQuery=what[3]; mRequestVersion=what[4]; DEB("REQUEST query: " << mRequestQuery); //create a regex iterator for http headers mHeaders.clear(); boost::sregex_iterator headerI( dataStr.begin(), dataStr.end(), boost::regex("^([[:alnum:]-]*): (.*?)$") ); //parse http headers while (headerI!=sregex_iterator()) { string header=(*headerI)[1].str(); string value=(*headerI)[2].str(); //headers handling is lowercase in synapse! transform(header.begin(), header.end(), header.begin(), ::tolower); mHeaders[header]=value; headerI++; } //this header MUST be set on longpoll requests: //if the client doesnt has one yet, httpSessionMan will assign a new one. try { mAuthCookie=mHeaders["x-synapse-authcookie"]; } catch(...) { mAuthCookie=0; } //proceed based on requestType: //a GET or empty POST: if (mRequestType=="GET" || (int)mHeaders["content-length"]==0) { if (respond()) { mState=REQUEST; } else { DEB(id << " is now waiting for longpoll results"); mState=WAIT_LONGPOLL; } return; } //a POST with content: else { if ( (int)mHeaders["content-length"]<0 || (int)mHeaders["content-length"] > config["maxContent"] ) { error="Invalid Content-Length"; } else { //change state to read the contents of the POST: mState=CONTENT; return; } } } } else //we're expecting the contents of a POST request. if (mState==CONTENT) { if (readBuffer.size() < mHeaders["content-length"]) { error="Didn't receive enough content-bytes!"; DEB(id << " ERROR: Expected " << mHeaders["content-length"].str() << " bytes, but only got: " << bytesTransferred); } else { dataStr.resize(mHeaders["content-length"]); readBuffer.consume(mHeaders["content-length"]); DEB(id << " got http CONTENT with length=" << dataStr.size() << ": \n" << dataStr); //POST to the special send url? if (mRequestUrl=="/synapse/send") { error=httpSessionMan.sendMessage(mAuthCookie, dataStr); if (error=="") { //DONT:respond with jsondata if we have something in the queue anyways //DONT:respondJsonQueue(false,false); //we cant do this, because then the order of messages isnt garanteed. so just respond with a boring empty string: respondString(200, ""); } else respondError(400, error); mState=REQUEST; return; } else { //ignore whatever is posted, and just respond normally: DEB(id << " ignored POST content"); if (respond()) mState=REQUEST; else mState=WAIT_LONGPOLL; return; } } } //ERROR(id << " had error while processing http data: " << error); error="Bad request: "+error; respondError(400, error); doDisconnect(); return; }
void Config::request(ServerRequest::ptr request, Access access) { const std::string &method = request->request().requestLine.method; if (method == POST) { if (access != READWRITE) { respondError(request, FORBIDDEN); return; } if (request->request().entity.contentType.type != "application" || request->request().entity.contentType.subtype != "x-www-form-urlencoded") { respondError(request, UNSUPPORTED_MEDIA_TYPE); return; } Stream::ptr requestStream = request->requestStream(); requestStream.reset(new LimitedStream(requestStream, 65536)); MemoryStream requestBody; transferStream(requestStream, requestBody); std::string queryString; queryString.resize(requestBody.buffer().readAvailable()); requestBody.buffer().copyOut(&queryString[0], requestBody.buffer().readAvailable()); bool failed = false; URI::QueryString qs(queryString); for (URI::QueryString::const_iterator it = qs.begin(); it != qs.end(); ++it) { ConfigVarBase::ptr var = Mordor::Config::lookup(it->first); if (var && !var->fromString(it->second)) failed = true; } if (failed) { respondError(request, HTTP::FORBIDDEN, "One or more new values were not accepted"); return; } // Fall through } if (method == GET || method == HEAD || method == POST) { Format format = HTML; URI::QueryString qs; if (request->request().requestLine.uri.queryDefined()) qs = request->request().requestLine.uri.queryString(); URI::QueryString::const_iterator it = qs.find("alt"); if (it != qs.end() && it->second == "json") format = JSON; // TODO: use Accept to indicate JSON switch (format) { case HTML: { request->response().status.status = OK; request->response().entity.contentType = MediaType("text", "html"); if (method == HEAD) { if (request->request().requestLine.ver == Version(1, 1) && isAcceptable(request->request().request.te, "chunked", true)) { request->response().general.transferEncoding.push_back("chunked"); } return; } Stream::ptr response = request->responseStream(); response.reset(new BufferedStream(response)); response->write("<html><body><table>\n", 20); Mordor::Config::visit(boost::bind(access == READWRITE ? &eachConfigVarHTMLWrite : &eachConfigVarHTML, _1, response)); response->write("</table></body></html>", 22); response->close(); break; } case JSON: { JSON::Object root; Mordor::Config::visit(boost::bind(&eachConfigVarJSON, _1, boost::ref(root))); std::ostringstream os; os << root; std::string str = os.str(); request->response().status.status = OK; request->response().entity.contentType = MediaType("application", "json"); request->response().entity.contentLength = str.size(); if (method != HEAD) { request->responseStream()->write(str.c_str(), str.size()); request->responseStream()->close(); } break; } default: MORDOR_NOTREACHED(); } } else { respondError(request, METHOD_NOT_ALLOWED); } }
void respondLengthRequired(const QString &body) { respondError(411, "Length Required", body); }
void handleRequest(ZhttpRequest *_req, const QByteArray &jsonpCallback, const QByteArray &lastPart, const QByteArray &body) { connect(_req, SIGNAL(bytesWritten(int)), SLOT(req_bytesWritten(int))); connect(_req, SIGNAL(error()), SLOT(req_error())); if(lastPart == "xhr" || lastPart == "jsonp") { if(req) { QVariantList out; out += 2010; out += QString("Another connection still open"); requests.insert(_req, new RequestItem(_req, jsonpCallback, RequestItem::Background, true)); respondOk(_req, out, "c", jsonpCallback); return; } if(peerClosed) { QVariantList out; out += 3000; out += QString("Client already closed connection"); requests.insert(_req, new RequestItem(_req, jsonpCallback, RequestItem::Background, true)); respondOk(_req, out, "c", jsonpCallback); return; } req = _req; requests.insert(req, new RequestItem(req, jsonpCallback, RequestItem::Receive)); keepAliveTimer->start(KEEPALIVE_TIMEOUT * 1000); tryWrite(); } else if(lastPart == "xhr_send" || lastPart == "jsonp_send") { // only allow one outstanding send request at a time if(findFirstSendRequest()) { requests.insert(_req, new RequestItem(_req, jsonpCallback, RequestItem::Background, true)); respondError(_req, 400, "Bad Request", "Already sending"); return; } QByteArray param; if(_req->requestMethod() == "POST") { if(lastPart == "xhr_send") { // assume json param = body; } else // jsonp_send { // assume form encoded foreach(const QByteArray &kv, body.split('&')) { int at = kv.indexOf('='); if(at == -1) continue; if(QUrl::fromPercentEncoding(kv.mid(0, at)) == "d") { param = QUrl::fromPercentEncoding(kv.mid(at + 1)).toUtf8(); break; } } } } else // GET { QUrlQuery query(_req->requestUri()); param = query.queryItemValue("d").toUtf8(); } QJsonParseError error; QJsonDocument doc = QJsonDocument::fromJson(param, &error); if(error.error != QJsonParseError::NoError || !doc.isArray()) { requests.insert(_req, new RequestItem(_req, jsonpCallback, RequestItem::Background, true)); respondError(_req, 400, "Bad Request", "Payload expected"); return; } QVariantList messages = doc.array().toVariantList(); QList<Frame> frames; int bytes = 0; foreach(const QVariant &vmessage, messages) { if(vmessage.type() != QVariant::String) { requests.insert(_req, new RequestItem(_req, jsonpCallback, RequestItem::Background, true)); respondError(_req, 400, "Bad Request", "Payload expected"); return; } QByteArray data = vmessage.toString().toUtf8(); if(data.size() > BUFFER_SIZE) { requests.insert(_req, new RequestItem(_req, jsonpCallback, RequestItem::Background, true)); respondError(_req, 400, "Bad Request", "Message too large"); return; } frames += Frame(Frame::Text, data, false); bytes += data.size(); } if(frames.isEmpty()) { requests.insert(_req, new RequestItem(_req, jsonpCallback, RequestItem::Background, true)); respondOk(_req, QString("ok"), jsonpCallback); return; } RequestItem *ri = new RequestItem(_req, jsonpCallback, RequestItem::Send); requests.insert(_req, ri); ri->sendFrames = frames; ri->sendBytes = bytes; tryRead(); }
static void respondObject ( qcom_sGet *get, gdb_sObject *op, pwr_tInt32 lcount, pwr_tInt32 rcount ) { pwr_tStatus sts; qcom_sPut put; gdb_sObject *pop; net_sObjectR *rsp; net_sGobject *gop; net_sGobject *go[net_cObjectMaxCount]; pwr_tUInt32 count; pwr_tUInt32 pcount; pwr_tUInt32 size; pwr_tInt32 i; pool_sQlink *sol; gdb_sObject *sop; gdb_AssumeLocked; /* The parents must be first in the message, so the receiver can link the cache objects. They must be in top-down-order. */ pop = op; pcount = 0; count = 0; while (pop->g.oid.oix != pwr_cNObjectIx && count < net_cObjectMaxCount - 1) { pop = pool_Address(NULL, gdbroot->pool, pop->l.por); go[count++] = &pop->g; } pcount = count; pop = pool_Address(NULL, gdbroot->pool, op->l.por); if (pop != NULL) { /* Left siblings. (At most lcount of them.) */ for ( i = 0, sol = pool_Qpred(NULL, gdbroot->pool, &op->u.n.sib_ll); i < lcount && sol != &pop->u.n.sib_lh && count < net_cObjectMaxCount - 1; i++, sol = pool_Qpred(NULL, gdbroot->pool, sol) ) { sop = pool_Qitem(sol, gdb_sObject, u.n.sib_ll); go[count++] = &sop->g; } /* Right siblings. (At most rcount of them.) */ for ( i = 0, sol = pool_Qsucc(NULL, gdbroot->pool, &op->u.n.sib_ll); i < rcount && sol != &pop->u.n.sib_lh && count < net_cObjectMaxCount - 1; i++, sol = pool_Qsucc(NULL, gdbroot->pool, sol) ) { sop = pool_Qitem(sol, gdb_sObject, u.n.sib_ll); go[count++] = &sop->g; } } /* Build response message. */ size = sizeof(net_sObjectR) + count * sizeof(net_sGobject); rsp = net_Alloc(&sts, &put, size, net_eMsg_objectR); if (rsp == NULL) { printf("NETH: could not allocate pams buffer for Cache send response, sts: %d\n", sts); respondError(get, op->g.oid, sts); return; } gop = &rsp->g[0]; /* Copy parent objects. */ for (i = pcount - 1; i >= 0; i--) *gop++ = *(go[i]); /* Copy target object. */ *gop++ = op->g; /* Copy sibling objects. */ for (i = pcount; i < count; i++) *gop++ = *(go[i]); rsp->sts = sts; rsp->oid = op->g.oid; rsp->count = count + 1; if (!net_Reply(&sts, get, &put, 0)) errh_Bugcheck(sts, "net_Reply"); }
/** Respond by sending a file relative to the wwwdir/ */ bool respondFile(string path) { Cvar extraHeaders; //FIXME: do a better way of checking/securing the path. Inode verification? Or compare with a filelist? if (path.find("..")!=string::npos) { respondError(403, "Path contains illegal characters"); return(true); } //default path? if (path=="/") { path=config["indexFile"].str(); } string localPath; localPath="wwwdir/"+path; struct stat statResults; int statError=stat(localPath.c_str(), &statResults); if (statError) { respondError(404, "Error while statting or file not found: " + path); return(true); } if (! (S_ISREG(statResults.st_mode)) ) { respondError(404, "Path " + path + " is not a regular file"); return(true); } ifstream inputFile(localPath.c_str(), std::ios::in | std::ios::binary); if (!inputFile.good() ) { respondError(404, "Error while opening " + path ); return(true); } //determine content-type smatch what; if (regex_search( path, what, boost::regex("([^.]*$)") )) { string extention=what[1]; if (config["contentTypes"].isSet(extention)) { extraHeaders["content-type"]=config["contentTypes"][extention]; DEB("Content type of ." << extention << " is " << config["contentTypes"][extention].str()); } else { WARNING("Cannot determine content-type of ." << extention << " files"); } } //determine filesize inputFile.seekg (0, ios::end); int fileSize=inputFile.tellg(); inputFile.seekg (0, ios::beg); extraHeaders["Content-Length"]=fileSize; //enable browser caching with the right headers: extraHeaders["Cache-Control"]="public, max-age=290304000"; getHttpDate(extraHeaders["Date"].str()); sendHeaders(200, extraHeaders); DEB(id << " sending CONTENT of " << path); char buf[4096]; //TODO: is there a better way to do this? int sendSize=0; while (inputFile.good()) { inputFile.read(buf ,sizeof(buf)); if (!sendData(asio::buffer(buf, inputFile.gcount()))) { break; } sendSize+=inputFile.gcount(); } if (sendSize!=fileSize) { ERROR(id << " error during file transfer, disconnecting"); doDisconnect(); } return true; }