bool Message::receiveContent(HttpResponse* response) { bool rval = true; // check status code for error HttpConnection* hc = response->getConnection(); HttpResponseHeader* header = response->getHeader(); if(header->getStatusCode() >= 400) { // receive content as a DynamicObject (to be converted to an exception) if(mDynamicObject.isNull()) { mDynamicObject = DynamicObject(); } else { mDynamicObject->clear(); } rval = receiveContentObject(hc, header, mDynamicObject); if(rval) { // create Exception from DynamicObject, return false ExceptionRef e = Exception::convertToException(mDynamicObject); Exception::set(e); rval = false; } } else { rval = receiveContent(hc, header); } return rval; }
void HttpClientHandler::executePHPScript( PhpInterface* php_iface, HttpResponseHeader & hdr, const QString & php_exe, const QString & php_file, const QMap<QString,QString> & args) { // Out(SYS_WEB|LOG_DEBUG) << "Launching PHP script " << php_file << endl; php = new PhpHandler(php_exe,php_iface); if (!php->executeScript(php_file,args)) { QString data = QString(HTTP_500_ERROR).arg("Failed to launch PHP executable !"); hdr.setResponseCode(500); hdr.setValue("Content-Length",QString::number(data.utf8().length())); QTextStream os(client); os.setEncoding( QTextStream::UnicodeUTF8 ); os << hdr.toString(); os << data; state = WAITING_FOR_REQUEST; } else { php_response_hdr = hdr; connect(php,SIGNAL(finished()),this,SLOT(onPHPFinished())); state = PROCESSING_PHP; } }
bool HttpClientHandler::sendFile(HttpResponseHeader & hdr,const QString & full_path) { // Out(SYS_WEB|LOG_DEBUG) << "Sending file " << full_path << endl; setResponseHeaders(hdr); // first look in cache MMapFile* c = srv->cacheLookup(full_path); if (!c) { // not in cache so load it c = new MMapFile(); if (!c->open(full_path,QIODevice::ReadOnly)) { delete c; Out(SYS_WEB|LOG_DEBUG) << "Failed to open file " << full_path << endl; return false; } srv->insertIntoCache(full_path,c); } // Out(SYS_WEB|LOG_DEBUG) << "HTTP header : " << endl; // Out(SYS_WEB|LOG_DEBUG) << hdr.toString() << endl; QByteArray data((const char*)c->getDataPointer(),c->getSize()); hdr.setValue("Content-Length",QString::number(data.size())); output_buffer.append(hdr.toString().toUtf8()); output_buffer.append(data); sendOutputBuffer(); // Out(SYS_WEB|LOG_DEBUG) << "Finished sending " << full_path << " (" << written << " bytes)" << endl; return true; }
void HttpClientHandler::send(HttpResponseHeader & hdr,const QByteArray & data) { setResponseHeaders(hdr); hdr.setValue("Content-Length",QString::number(data.length())); output_buffer.append(hdr.toString().toUtf8()); output_buffer.append(data); sendOutputBuffer(); }
void HttpServer::setDefaultResponseHeaders(HttpResponseHeader & hdr,const QString & content_type,bool with_session_info) { hdr.setValue("Server","KTorrent/" KT_VERSION_MACRO); hdr.setValue("Date",DateTimeToString(QDateTime::currentDateTime(Qt::UTC),false)); hdr.setValue("Content-Type",content_type); hdr.setValue("Connection","keep-alive"); if (with_session_info && session.sessionId && session.logged_in) { hdr.setValue("Set-Cookie",QString("KT_SESSID=%1").arg(session.sessionId)); } }
void HttpClientHandler::send404(HttpResponseHeader & hdr,const QString & path) { // Out(SYS_WEB|LOG_DEBUG) << "Sending 404 " << path << endl; QString data = HTTP_404_ERROR; hdr.setValue("Content-Length",QString::number(data.length())); QTextStream os(client); os.setEncoding( QTextStream::UnicodeUTF8 ); os << hdr.toString(); os << data; }
void HttpClientHandler::send500(HttpResponseHeader & hdr) { // Out(SYS_WEB|LOG_DEBUG) << "Sending 500 " << endl; QString data = QString(HTTP_500_ERROR).arg("An internal server error occured !"); hdr.setValue("Content-Length",QString::number(data.length())); QTextStream os(client); os.setEncoding( QTextStream::UnicodeUTF8 ); os << hdr.toString(); os << data; }
void HttpClientHandler::send404(HttpResponseHeader & hdr,const QString & path) { setResponseHeaders(hdr); // Out(SYS_WEB|LOG_DEBUG) << "Sending 404 " << path << endl; QString data = QString(HTTP_404_ERROR).arg(path); hdr.setValue("Content-Length",QString::number(data.length())); output_buffer.append(hdr.toString().toUtf8()); output_buffer.append(data.toUtf8()); sendOutputBuffer(); }
void HttpClientHandler::send500(HttpResponseHeader & hdr,const QString & error) { setResponseHeaders(hdr); // Out(SYS_WEB|LOG_DEBUG) << "Sending 500 " << endl; QString err = i18n("An internal server error occurred: %1",error); QString data = QString(HTTP_500_ERROR).arg(err); hdr.setValue("Content-Length",QString::number(data.length())); output_buffer.append(hdr.toString().toUtf8()); output_buffer.append(data.toUtf8()); sendOutputBuffer(); }
virtual void handleRegexRequest(ServiceChannel* ch) { // send 200 OK HttpResponseHeader* h = ch->getResponse()->getHeader(); h->setStatus(200, "OK"); //h->setField("Content-Length", 0); h->setField("Transfer-Encoding", "chunked"); h->setField("Connection", "close"); ch->getResponse()->sendHeader(); ByteArrayInputStream bais(mRegexContent, strlen(mRegexContent)); ch->getResponse()->sendBody(&bais); }
virtual void handleRegexRequest2(ServiceChannel* ch) { // send 200 OK HttpResponseHeader* h = ch->getResponse()->getHeader(); h->setStatus(200, "OK"); //h->setField("Content-Length", 0); h->setField("Transfer-Encoding", "chunked"); h->setField("Connection", "close"); ch->getResponse()->sendHeader(); string out = JsonWriter::writeToString(ch->getHandlerInfo()); ByteArrayInputStream bais(out.c_str(), out.length()); ch->getResponse()->sendBody(&bais); }
void HttpClientHandler::sendResponse(const HttpResponseHeader & hdr) { // Out(SYS_WEB|LOG_DEBUG) << "Sending response " << hdr.toString() << endl; QTextStream os(client); os.setEncoding( QTextStream::UnicodeUTF8 ); os << hdr.toString(); }
void HttpClientHandler::sendResponse(HttpResponseHeader & hdr) { setResponseHeaders(hdr); // Out(SYS_WEB|LOG_DEBUG) << "Sending response " << hdr.toString() << endl; output_buffer.append(hdr.toString().toUtf8()); sendOutputBuffer(); }
void HttpClientHandler::setResponseHeaders(HttpResponseHeader& hdr) { if (shouldClose()) { if (header.majorVersion() == 1 && header.minorVersion() == 0) return; else hdr.setValue("Connection","close"); } else { if (header.majorVersion() == 1 && header.minorVersion() == 0) hdr.setValue("Connection","Keep-Alive"); else return; } }
bool HttpClientHandler::sendFile(HttpResponseHeader & hdr,const QString & full_path) { // Out(SYS_WEB|LOG_DEBUG) << "Sending file " << full_path << endl; // first look in cache MMapFile* c = srv->cacheLookup(full_path); if (!c) { // not in cache so load it c = new MMapFile(); if (!c->open(full_path,MMapFile::READ)) { delete c; Out(SYS_WEB|LOG_DEBUG) << "Failed to open file " << full_path << endl; return false; } srv->insertIntoCache(full_path,c); } hdr.setValue("Content-Length",QString::number(c->getSize())); // Out(SYS_WEB|LOG_DEBUG) << "HTTP header : " << endl; // Out(SYS_WEB|LOG_DEBUG) << hdr.toString() << endl; QCString d = hdr.toString().utf8(); client->writeBlock(d.data(),d.length()); Uint32 written = 0; Uint32 total = c->getSize(); const char* data = (const char*)c->getDataPointer(); while (written < total) { Uint32 w = client->writeBlock(data + written,total - written); written += w; } client->flush(); // Out(SYS_WEB|LOG_DEBUG) << "Finished sending " << full_path << " (" << written << " bytes)" << endl; return true; }
bool SampleService::sendm3u(BtpAction* action, DynamicObject& fileInfos) { bool rval = true; // build m3u file string content; content.append("#EXTM3U\n"); // create play list char temp[22]; FileInfoIterator i = fileInfos.getIterator(); while(i->hasNext()) { FileInfo& fi = i->next(); Media& media = fi["media"]; int sampleLength = media["sampleRange"][1]->getUInt32() - media["sampleRange"][0]->getUInt32(); snprintf(temp, 22, "%u", (sampleLength < 0 ? 0 : sampleLength)); string artist; if(media["contributors"]->hasMember("Performer")) { artist = media["contributors"]["Performer"][0]["name"]->getString(); } else if(media["contributors"]->hasMember("Artist")) { artist = media["contributors"]["Artist"][0]["name"]->getString(); } content.append("#EXTINF:"); content.append(temp); content.push_back(','); content.append(artist); content.append(" - "); content.append(media["title"]->getString()); content.push_back('\n'); // FIXME: get host from configuration or something // needs to be public IP that remote side can access DynamicObject vars; action->getResourceQuery(vars); if(!vars->hasMember("nodeuser")) { BM_ID_SET(vars["nodeuser"], mNode->getDefaultUserId()); } HttpRequestHeader* header = action->getRequest()->getHeader(); string host = header->getFieldValue("X-Forwarded-Host"); if(host.length() == 0) { host = header->getFieldValue("Host"); } content.append("http://"); content.append(host); content.append("/api/3.0/sales/samples/file/"); content.append(fi["mediaId"]->getString()); content.push_back('/'); content.append(fi["id"]->getString()); content.push_back('\n'); } // set up response header HttpResponseHeader* header = action->getResponse()->getHeader(); header->setField("Content-Type", "audio/x-mpegurl"); header->setField( "Content-Disposition", "attachment; filename=bitmunk-sample.m3u"); // create content input stream ByteBuffer b(content.length()); b.put(content.c_str(), content.length(), false); ByteArrayInputStream bais(&b); // send sample action->getResponse()->getHeader()->setStatus(200, "OK"); action->sendResult(&bais); return rval; }
void HttpConnectionServicer::serviceConnection(Connection* c) { // wrap connection, set default timeouts to 30 seconds HttpConnection hc(c, false); hc.setReadTimeout(30000); hc.setWriteTimeout(30000); // monitor connection if(!mConnectionMonitor.isNull()) { mConnectionMonitor->beforeServicingConnection(&hc); } // create request HttpRequest* request = hc.createRequest(); HttpRequestHeader* reqHeader = request->getHeader(); // create response HttpResponse* response = request->createResponse(); HttpResponseHeader* resHeader = response->getHeader(); // handle keep-alive (HTTP/1.1 keep-alive is on by default) bool keepAlive = true; bool noerror = true; while(keepAlive && noerror) { // set defaults resHeader->setVersion("HTTP/1.1"); resHeader->setDate(); resHeader->setField("Server", mServerName); // monitor request waiting if(!mConnectionMonitor.isNull()) { mConnectionMonitor->beforeRequest(&hc); } // receive request header if((noerror = request->receiveHeader())) { // begin new request state hc.getRequestState()->beginRequest(); // monitor received request if(!mConnectionMonitor.isNull()) { mConnectionMonitor->beforeServicingRequest( &hc, request, response); } // do request modification if(mRequestModifier != NULL) { mRequestModifier->modifyRequest(request); } // check http version bool version10 = (strcmp(reqHeader->getVersion(), "HTTP/1.0") == 0); bool version11 = (strcmp(reqHeader->getVersion(), "HTTP/1.1") == 0); // only version 1.0 and 1.1 supported if(version10 || version11) { // set response version according to request version resHeader->setVersion(reqHeader->getVersion()); // use proxy'd host field if one was used // else use host field if one was used string host; if(reqHeader->getField("X-Forwarded-Host", host) || reqHeader->getField("Host", host)) { resHeader->setField("Host", host); } // get connection header string connHeader; if(reqHeader->getField("Connection", connHeader)) { if(strcasecmp(connHeader.c_str(), "close") == 0) { keepAlive = false; } else if(strcasecmp(connHeader.c_str(), "keep-alive") == 0) { keepAlive = true; } } else if(version10) { // if HTTP/1.0 and no keep-alive header, keep-alive is off keepAlive = false; } // get request path and normalize it const char* inPath = reqHeader->getPath(); char outPath[strlen(inPath) + 2]; HttpRequestServicer::normalizePath(inPath, outPath); // find appropriate request servicer for path HttpRequestServicer* hrs = NULL; // find secure/non-secure servicer hrs = findRequestServicer(host, outPath, hc.isSecure()); if(hrs != NULL) { // service request hrs->serviceRequest(request, response); // turn off keep-alive if response has close connection field if(keepAlive) { if(resHeader->getField("Connection", connHeader) && strcasecmp(connHeader.c_str(), "close") == 0) { keepAlive = false; } } // if servicer closed connection, turn off keep-alive if(keepAlive && c->isClosed()) { keepAlive = false; } } else { // no servicer, so send 404 Not Found const char* html = "<html><body><h2>404 Not Found</h2></body></html>"; resHeader->setStatus(404, "Not Found"); resHeader->setField("Content-Type", "text/html"); resHeader->setField("Content-Length", 48); resHeader->setField("Connection", "close"); if((noerror = response->sendHeader())) { ByteArrayInputStream is(html, 48); noerror = response->sendBody(&is); } } } else { // send 505 HTTP Version Not Supported const char* html = "<html><body>" "<h2>505 HTTP Version Not Supported</h2>" "</body></html>"; resHeader->setStatus(505, "HTTP Version Not Supported"); resHeader->setField("Content-Type", "text/html"); resHeader->setField("Content-Length", 65); resHeader->setField("Connection", "close"); if((noerror = response->sendHeader())) { ByteArrayInputStream is(html, 65); noerror = response->sendBody(&is); } } // monitor serviced request if(!mConnectionMonitor.isNull()) { mConnectionMonitor->afterServicingRequest( &hc, request, response); } } else { // begin new request state hc.getRequestState()->beginRequest(); // monitor request error if(!mConnectionMonitor.isNull()) { mConnectionMonitor->beforeRequestError( &hc, request, response); } // exception occurred while receiving header ExceptionRef e = Exception::get(); // if no header then drop through and close connection if(e->isType("monarch.http.NoHeader")) { keepAlive = false; } // for bad header or request set 400 else if(e->isType("monarch.http.BadHeader") || e->isType("monarch.http.BadRequest")) { // send 400 Bad Request const char* html = "<html><body><h2>400 Bad Request</h2></body></html>"; response->getHeader()->setStatus(400, "Bad Request"); response->getHeader()->setField("Content-Type", "text/html"); response->getHeader()->setField("Content-Length", 50); response->getHeader()->setField("Connection", "close"); if(response->sendHeader()) { ByteArrayInputStream is(html, 50); response->sendBody(&is); } } // if the exception was an interruption, then send a 503 else if(e->isType("monarch.io.InterruptedException") || e->isType("monarch.rt.Interrupted")) { // send 503 Service Unavailable const char* html = "<html><body><h2>503 Service Unavailable</h2></body></html>"; resHeader->setStatus(503, "Service Unavailable"); resHeader->setField("Content-Type", "text/html"); resHeader->setField("Content-Length", 58); resHeader->setField("Connection", "close"); if((noerror = response->sendHeader())) { ByteArrayInputStream is(html, 58); noerror = response->sendBody(&is); } } // if the exception was not a socket error then send an internal // server error response else if(!e->isType("monarch.net.Socket", true)) { // send 500 Internal Server Error const char* html = "<html><body><h2>500 Internal Server Error</h2></body></html>"; resHeader->setStatus(500, "Internal Server Error"); resHeader->setField("Content-Type", "text/html"); resHeader->setField("Content-Length", 60); resHeader->setField("Connection", "close"); if((noerror = response->sendHeader())) { ByteArrayInputStream is(html, 60); noerror = response->sendBody(&is); } } else { // log socket error if(e->getDetails()->hasMember("error")) { // build error string string error; DynamicObjectIterator i = e->getDetails()["error"].getIterator(); while(i->hasNext()) { error.append(i->next()->getString()); if(i->hasNext()) { error.push_back(','); } } MO_CAT_ERROR(MO_HTTP_CAT, "Connection error: ['%s','%s','%s']", e->getMessage(), e->getType(), error.c_str()); } else { MO_CAT_ERROR(MO_HTTP_CAT, "Connection error: ['%s','%s']", e->getMessage(), e->getType()); } } // monitor request error if(!mConnectionMonitor.isNull()) { mConnectionMonitor->afterRequestError( &hc, request, response, e); } } // monitor request if(!mConnectionMonitor.isNull()) { mConnectionMonitor->afterRequest(&hc); } if(keepAlive && noerror) { // set keep-alive timeout (defaults to 5 minutes) hc.setReadTimeout(1000 * 60 * 5); // clear request and response header fields reqHeader->clearFields(); resHeader->clearFields(); resHeader->clearStatus(); } } // clean up request and response delete request; delete response; // monitor connection if(!mConnectionMonitor.isNull()) { mConnectionMonitor->afterServicingConnection(&hc); } // close connection hc.close(); }
/** * A UPnP discover response is an HTTP based response to a UPnP discover * request that uses the Simple Service Discovery Protocol (SSDP). It provides * information about a discovered UPnP device. * * The following is taken from: * * http://www.upnp.org/download/UPnPDA10_20000613.htm * * ----------------------------------------------------------------------- * HTTP/1.1 200 OK * CACHE-CONTROL: max-age = seconds until advertisement expires * DATE: when response was generated * EXT: * LOCATION: URL for UPnP description for root device * SERVER: OS/version UPnP/1.0 product/version * ST: search target * USN: advertisement UUID * ----------------------------------------------------------------------- * Headers: * * CACHE-CONTROL * Required. Must have max-age directive that specifies number of seconds the * advertisement is valid. After this duration, control points should assume * the device (or service) is no longer available. Should be > 1800 seconds * (30 minutes). Specified by UPnP vendor. Integer. * * DATE * Recommended. When response was generated. RFC 1123 date. * * EXT * Required. Confirms that the MAN header was understood. * (Header only; no value.) * * LOCATION * Required. Contains a URL to the UPnP description of the root device. In * some unmanaged networks, host of this URL may contain an IP address * (versus a domain name). Specified by UPnP vendor. Single URL. * * SERVER * Required. Concatenation of OS name, OS version, UPnP/1.0, product name, * and product version. Specified by UPnP vendor. String. * * ST * Required header defined by SSDP. Search Target. Single URI. If ST header * in request was, * * ssdp:all * Respond 3+2d+k times for a root device with d embedded devices and s * embedded services but only k distinct service types. Value for ST header * must be the same as for the NT header in NOTIFY messages with ssdp:alive. * (See above.) Single URI. * * upnp:rootdevice * Respond once for root device. Must be upnp:rootdevice. Single URI. * * uuid:device-UUID * Respond once for each device, root or embedded. Must be uuid:device-UUID. * Device UUID specified by UPnP vendor. Single URI. * * urn:schemas-upnp-org:device:deviceType:v * Respond once for each device, root or embedded. Must be * urn:schemas-upnp-org:device:deviceType:v. Device type and version defined * by UPnP Forum working committee. * * urn:schemas-upnp-org:service:serviceType:v * Respond once for each service. Must be * urn:schemas-upnp-org:service:serviceType:v. Service type and version defined * by UPnP Forum working committee. * * USN * Required header defined by SSDP. Unique Service Name. (See list of required * values for USN header in NOTIFY with ssdp:alive above.) Single URI. * ----------------------------------------------------------------------- */ static Device parseDevice(const char* response) { Device rval(NULL); // parse ssdp response as an http response header HttpResponseHeader header; if(header.parse(response)) { // ensure all necessary header fields are present if(header.hasField("cache-control") && header.hasField("ext") && header.hasField("location") && header.hasField("server") && header.hasField("st") && header.hasField("usn")) { // create UPnP device from response rval = Device(); // get device information rval["server"] = header.getFieldValue("server").c_str(); rval["location"] = header.getFieldValue("location").c_str(); rval["usn"] = header.getFieldValue("location").c_str(); } } return rval; }
static void runHttpHeaderTest(TestRunner& tr) { tr.group("HttpHeader"); tr.test("Bicapitalization"); { // test bicapitalization of http headers const char* tests[] = { "", "", "a", "A", "-", "-", "a--a", "A--A", "-aa-", "-Aa-", "-aa", "-Aa", "aa-", "Aa-", "aaa-zzz", "Aaa-Zzz", "ThIs-a-BICaPitAlized-hEADer", "This-A-Bicapitalized-Header", "Message-ID", "Message-Id", NULL }; for(int i = 0; tests[i] != NULL; i +=2) { char* bic = strdup(tests[i]); HttpHeader::biCapitalize(bic); assertStrCmp(bic, tests[i+1]); free(bic); } } tr.passIfNoException(); tr.test("HttpRequestHeader parse"); { HttpRequestHeader header; header.setDate(); header.setMethod("GET"); header.setPath("/"); header.setVersion("HTTP/1.1"); header.setField("host", "localhost:80"); header.setField("Content-Type", "text/html"); header.setField("Connection", "close"); string date; string expect; expect.append("GET / HTTP/1.1\r\n"); expect.append("Connection: close\r\n"); expect.append("Content-Type: text/html\r\n"); expect.append("Date: "); header.getField("Date", date); expect.append(date); expect.append("\r\n"); expect.append("Host: localhost:80\r\n"); expect.append("\r\n"); string str = header.toString(); assertStrCmp(str.c_str(), expect.c_str()); HttpRequestHeader header2; header2.parse(str); string str2 = header2.toString(); assertStrCmp(str2.c_str(), expect.c_str()); } tr.passIfNoException(); tr.test("HttpResponseHeader parse"); { HttpResponseHeader header; header.setDate(); header.setVersion("HTTP/1.1"); header.setStatus(404, "Not Found"); header.setField("host", "localhost:80"); header.setField("Content-Type", "text/html"); header.setField("Connection", "close"); string date; string expect; expect.append("HTTP/1.1 404 Not Found\r\n"); expect.append("Connection: close\r\n"); expect.append("Content-Type: text/html\r\n"); expect.append("Date: "); header.getField("Date", date); expect.append(date); expect.append("\r\n"); expect.append("Host: localhost:80\r\n"); expect.append("\r\n"); string str = header.toString(); assertStrCmp(str.c_str(), expect.c_str()); HttpResponseHeader header2; header2.parse(str); string str2 = header2.toString(); assertStrCmp(str2.c_str(), expect.c_str()); } tr.passIfNoException(); tr.test("Multiple fields with same name"); { HttpResponseHeader header; header.setDate(); header.setVersion("HTTP/1.1"); header.setStatus(404, "Not Found"); header.setField("host", "localhost:80"); header.setField("Content-Type", "text/html"); header.setField("Connection", "close"); header.addField("Set-Cookie", "cookie1=value1; max-age=0; path=/"); header.addField("Set-Cookie", "cookie2=value2; max-age=0; path=/"); header.addField("Set-Cookie", "cookie3=value3; max-age=0; path=/"); string date; string expect; expect.append("HTTP/1.1 404 Not Found\r\n"); expect.append("Connection: close\r\n"); expect.append("Content-Type: text/html\r\n"); expect.append("Date: "); header.getField("Date", date); expect.append(date); expect.append("\r\n"); expect.append("Host: localhost:80\r\n"); expect.append("Set-Cookie: cookie1=value1; max-age=0; path=/\r\n"); expect.append("Set-Cookie: cookie2=value2; max-age=0; path=/\r\n"); expect.append("Set-Cookie: cookie3=value3; max-age=0; path=/\r\n"); expect.append("\r\n"); string str = header.toString(); assertStrCmp(str.c_str(), expect.c_str()); HttpResponseHeader header2; header2.parse(str); string str2 = header2.toString(); assertStrCmp(str2.c_str(), expect.c_str()); } tr.passIfNoException(); tr.test("hasContentType"); { HttpResponseHeader header; header.clearFields(); header.setField("Content-Type", "text/html"); assert(header.hasContentType("text/html")); header.clearFields(); header.setField("Content-Type", "text/html; params"); assert(header.hasContentType("text/html")); header.clearFields(); header.setField("Content-Type", "text/html; params"); assert(!header.hasContentType("text/plain")); } tr.passIfNoException(); tr.ungroup(); }
bool SampleService::sendFile(BtpAction* action, FileInfo& fi) { bool rval = false; // FIXME: only audio supported at this time // FIXME: get content type for file info instead of "extension" const char* extension = fi["extension"]->getString(); if((rval = (strcmp(extension, "mp3") == 0))) { // ensure file exists, get sample range for media File file(fi["path"]->getString()); Media media; BM_ID_SET(media["id"], BM_MEDIA_ID(fi["mediaId"])); if((rval = (file->exists() && getSampleRange(media)))) { // set up response header HttpResponseHeader* header = action->getResponse()->getHeader(); header->setField("Content-Type", "audio/mpeg"); header->setField( "Content-Disposition", "attachment; filename=bitmunk-sample.mp3"); // calculate content length for sample for HTTP/1.0 if(strcmp( action->getRequest()->getHeader()->getVersion(), "HTTP/1.0") == 0) { // check for cached sample content length int64_t contentLength = 0; if(media->hasMember("sampleContentLength")) { contentLength = media["sampleContentLength"]->getUInt64(); } else { // create time parser to produce sample MpegAudioTimeParser matp; matp.addTimeSet( media["sampleRange"][0]->getUInt32(), media["sampleRange"][1]->getUInt32()); // FIXME: build a method into the catalog that will return // the content-length for a sample // read data, strip id3 tag, parse mp3 data, add new id3 tag // get content length Id3v2Tag tag(media); Id3v2TagWriter stripper(NULL); Id3v2TagWriter embedder( &tag, false, tag.getFrameSource(), false); FileInputStream fis(file); MutatorInputStream strip(&fis, false, &stripper, false); MutatorInputStream parse(&strip, false, &matp, false); MutatorInputStream embed(&parse, false, &embedder, false); int64_t numBytes; while((numBytes = embed.skip(file->getLength())) > 0) { contentLength += numBytes; } embed.close(); if(numBytes != -1) { // cache content length media["sampleContentLength"] = contentLength; } } // replace chunked encoding with content length header->setField("Connection", "close"); header->removeField("Transfer-Encoding"); header->removeField("TE"); header->setField("Content-Length", contentLength); } // create time parser to produce sample MpegAudioTimeParser matp; matp.addTimeSet( media["sampleRange"][0]->getUInt32(), media["sampleRange"][1]->getUInt32()); // read data, strip id3 tag, embed new id3 tag, produce sample Id3v2Tag tag(media); Id3v2TagWriter stripper(NULL); Id3v2TagWriter embedder( &tag, false, tag.getFrameSource(), false); FileInputStream fis(file); MutatorInputStream strip(&fis, false, &stripper, false); MutatorInputStream parse(&strip, false, &matp, false); MutatorInputStream embed(&parse, false, &embedder, false); // send sample, remove any special encoding to prevent compression action->getRequest()->getHeader()->removeField("Accept-Encoding"); action->getResponse()->getHeader()->setStatus(200, "OK"); action->sendResult(&embed); // close stream embed.close(); } } return rval; }
void ProxyResourceHandler::operator()(BtpAction* action) { // get request host HttpRequestHeader* hrh = action->getRequest()->getHeader(); string host = hrh->getFieldValue("X-Forwarded-Host"); if(host.length() == 0) { host = hrh->getFieldValue("Host"); } // find a rule Rule* rule = findRule(action, host); if(rule == NULL) { // delegate to rest resource handler RestResourceHandler::operator()(action); } else { // get URL to proxy or redirect to UrlRef url = rule->url; // get url host string urlHost; HttpResponse* res = action->getResponse(); bool secure = res->getConnection()->isSecure(); // if URL has no host, reuse incoming host if(url->getHost().length() == 0) { urlHost = host; } // if URL has no port or uses a default port number, only use URL host else if( (url->getPort() == 0) || (secure && url->getPort() == 443) || (!secure && url->getPort() == 80)) { urlHost = url->getHost(); } // use URL host and port else { urlHost = url->getHostAndPort(); } // handle 0.0.0.0 (any host) by replacing it with the request host if(strncmp(urlHost.c_str(), "0.0.0.0", 8) == 0) { // 0.0.0.0 is 8 chars long urlHost.replace(0, 8, host.substr(0, host.find(':')).c_str()); } // rewrite the request path if it does not match URL path string path = hrh->getPath(); if(strcmp(path.c_str(), url->getPath().c_str()) != 0) { // check for path wildcard if(strcmp(rule->path, "*") == 0) { // since a wildcard is used, prepend the URL path to the // resource (if the url path isn't '/') string urlPath = url->getPath(); if(urlPath.length() > 1) { path.insert(0, url->getPath().c_str()); } } else { // replace the part of the resource that matched the proxy // rule with the rewrite path from the proxy URL path.replace(0, strlen(rule->path), url->getPath().c_str()); } } // do redirect if appropriate if(rule->type == Rule::Redirect) { // set response code HttpResponseHeader* header = res->getHeader(); if(rule->permanent) { header->setStatus(301, "Moved Permanently"); } else { header->setStatus(302, "Found"); } // build new location url bool secure = res->getConnection()->isSecure(); header->setField("Location", StringTools::format("%s://%s%s", secure ? "https" : "http", urlHost.c_str(), path.c_str())); action->sendResult(); } // do proxy else if(rule->type == Rule::Proxy) { // get client-side request HttpRequest* req = action->getRequest(); // do path rewrite hrh->setPath(path.c_str()); // add X-Forwarded headers hrh->appendFieldValue("X-Forwarded-For", req->getConnection()->getRemoteAddress()->toString(true).c_str()); hrh->appendFieldValue("X-Forwarded-Host", hrh->getFieldValue("Host").c_str()); hrh->appendFieldValue("X-Forwarded-Server", SocketTools::getHostname().c_str()); // rewrite host if rule specifies it if(rule->rewriteHost) { hrh->setField("Host", urlHost.c_str()); } // do proxy: MO_CAT_INFO(BM_NODE_CAT, "ProxyResourceHandler proxying %s%s => %s%s", host.c_str(), hrh->getPath(), urlHost.c_str(), path.c_str()); MO_CAT_DEBUG(BM_NODE_CAT, "ProxyResourceHandler request header for %s%s => %s%s:\n%s", host.c_str(), hrh->getPath(), urlHost.c_str(), path.c_str(), hrh->toString().c_str()); // get a connection BtpClient* btpc = mNode->getMessenger()->getBtpClient(); HttpConnection* conn = btpc->createConnection(false, &(*url)); if(conn == NULL) { // send service unavailable HttpResponseHeader* header = action->getResponse()->getHeader(); header->setStatus(503, "Service Unavailable"); string content = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n" "<html><head>\n" "<title>503 Service Unavailable</title>\n" "</head><body>\n" "<h1>Service Unavailable</h1>\n" "<p>The service was not available.</p>\n" "</body></html>"; ByteBuffer b(content.length()); b.put(content.c_str(), content.length(), false); ByteArrayInputStream bais(&b); action->sendResult(&bais); } else { // proxy the client's request and receive server's header (by // writing it into the client's response header) HttpResponse* res = action->getResponse(); if(_proxyHttp(req->getHeader(), req->getConnection(), conn) && conn->receiveHeader(res->getHeader())) { // proxy the server's response, consider result sent _proxyHttp(res->getHeader(), conn, req->getConnection()); action->setResultSent(true); } // close connection conn->close(); // clean up delete conn; } if(!action->isResultSent()) { // send exception (client's fault if code < 500) ExceptionRef e = Exception::get(); action->sendException(e, e->getCode() < 500); } } } }