示例#1
0
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;
}
示例#2
0
	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();
	}
示例#5
0
	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));
		}
	}
示例#6
0
	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;
	}
示例#7
0
	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();
	}
示例#10
0
   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);
   }
示例#11
0
   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);
   }
示例#12
0
	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();
	}
示例#13
0
	void HttpClientHandler::sendResponse(HttpResponseHeader & hdr)
	{
		setResponseHeaders(hdr);
	//	Out(SYS_WEB|LOG_DEBUG) << "Sending response " << hdr.toString() << endl;
		output_buffer.append(hdr.toString().toUtf8());
		sendOutputBuffer();
	}
示例#14
0
	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;
		}
	}
示例#15
0
	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;
	}
示例#16
0
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;
}
示例#17
0
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();
}
示例#18
0
/**
 * 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;
}
示例#19
0
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();
}
示例#20
0
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);
         }
      }
   }
}