示例#1
0
	void respond(int code, const QByteArray &reason, const HttpHeaders &headers, const QByteArray &body)
	{
		if(state != WriteBody)
			return;

		HttpHeaders outHeaders = headers;
		outHeaders.removeAll("Connection");
		outHeaders.removeAll("Transfer-Encoding");
		outHeaders.removeAll("Content-Length");

		outHeaders += HttpHeader("Connection", "close");
		outHeaders += HttpHeader("Content-Length", QByteArray::number(body.size()));

		QByteArray respData = "HTTP/";
		if(version1dot0)
			respData += "1.0 ";
		else
			respData += "1.1 ";
		respData += QByteArray::number(code) + " " + reason + "\r\n";
		foreach(const HttpHeader &h, outHeaders)
			respData += h.first + ": " + h.second + "\r\n";
		respData += "\r\n";
		respData += body;

		state = WaitForWritten;
		pendingWritten += respData.size();
		sock->write(respData);
	}
示例#2
0
  void checkNewVersion(const Uuid& uuid, const std::string& extraParams, CheckUpdateDelegate* delegate)
  {
    using namespace base;
    using namespace net;

    std::string url = UPDATE_URL;
    if (!uuid.empty()) {
      url += "&uuid=";
      url += uuid;
    }
    if (!extraParams.empty()) {
      url += "&";
      url += extraParams;
    }

    HttpRequest request(url);
    HttpHeaders headers;
    headers.setHeader("User-Agent", getUserAgent());
    request.setHeaders(headers);

    std::stringstream body;
    HttpResponse response(&body);
    request.send(response);

    CheckUpdateResponse data(body.str());
    delegate->onResponse(data);
  }
示例#3
0
YETI_Result HttpEntity::set_headers(const HttpHeaders & headers)
{
    HttpHeader * header;
    header = headers.get_header(YETI_HTTP_HEADER_CONTENT_LENGTH);
    if (header != NULL) {
        m_content_length_is_known_ = true;
        YETI_LargeSize length;
        if (YETI_SUCCEEDED(header->get_value().to_integer64(length))) {
            m_content_length_ = length;
        } else {
            m_content_length_ = 0;
        }
    }

    header = headers.get_header(YETI_HTTP_HEADER_CONTENT_TYPE);
    if (header != NULL) {
        m_content_type_ = header->get_value();
    }

    header = headers.get_header(YETI_HTTP_HEADER_CONTENT_ENCODING);
    if (header != NULL) {
        m_content_encoding_ = header->get_value();
    }

    header = headers.get_header(YETI_HTTP_HEADER_TRANSFER_ENCODING);
    if (header != NULL) {
        m_transfer_encoding_ = header->get_value();
    }

    return YETI_SUCCESS;
}
示例#4
0
  void checkNewVersion(const Uuid& uuid, const std::string& extraParams, CheckUpdateDelegate* delegate)
  {
    using namespace base;
    using namespace net;

#ifndef UPDATE_URL
#define UPDATE_URL ""
#endif
#pragma message("warning: Define UPDATE_URL macro")

    std::string url = UPDATE_URL;
    if (!uuid.empty()) {
      url += "&uuid=";
      url += uuid;
    }
    if (!extraParams.empty()) {
      url += "&";
      url += extraParams;
    }

    HttpRequest request(url);
    HttpHeaders headers;
    headers.setHeader("User-Agent", getUserAgent());
    request.setHeaders(headers);

    std::stringstream body;
    HttpResponse response(&body);
    request.send(response);

    TRACE("Checking updates: %s (User-Agent: %s)\n", url.c_str(), getUserAgent().c_str());
    TRACE("Response:\n--\n%s--\n", body.str().c_str());

    CheckUpdateResponse data(body.str());
    delegate->onResponse(data);
  }
示例#5
0
	void setup(const QUrl &_uri, const HttpHeaders &_headers, const QHostAddress &connectAddr = QHostAddress(), int connectPort = -1, int _maxRedirects = -1, const QString &connectHostToTrust = QString())
	{
		assert(!method.isEmpty());

		QUrl uri = _uri;
		if(connectPort != -1)
			uri.setPort(connectPort);
		else if(uri.port() == -1)
		{
			if(uri.scheme() == "https")
				uri.setPort(443);
			else
				uri.setPort(80);
		}

		HttpHeaders headers = _headers;

		checkHosts += uri.host(QUrl::FullyEncoded);

		if(!connectAddr.isNull())
		{
			curl_slist_free_all(dnsCache);

			if(!connectHostToTrust.isEmpty())
				checkHosts += connectHostToTrust;

			QByteArray cacheEntry = uri.host(QUrl::FullyEncoded).toUtf8() + ':' + QByteArray::number(uri.port()) + ':' + connectAddr.toString().toUtf8();
			dnsCache = curl_slist_append(dnsCache, cacheEntry.data());
			curl_easy_setopt(easy, CURLOPT_RESOLVE, dnsCache);
		}

		curl_easy_setopt(easy, CURLOPT_URL, uri.toEncoded().data());

		bool chunked = false;
		if(headers.contains("Content-Length"))
		{
			curl_off_t content_len = (curl_off_t)headers.get("Content-Length").toLongLong();
			/*if(method == "POST")
				curl_easy_setopt(easy, CURLOPT_POSTFIELDSIZE_LARGE, content_len);
			else*/
				curl_easy_setopt(easy, CURLOPT_INFILESIZE_LARGE, content_len);

			// curl will set this for us
			headers.removeAll("Content-Length");
		}
		else
		{
			if(expectBody)
				chunked = true;
			else if(alwaysSetBody)
				curl_easy_setopt(easy, CURLOPT_INFILESIZE_LARGE, (curl_off_t)0);
		}

		curl_slist_free_all(headersList);
		foreach(const HttpHeader &h, headers)
		{
			QByteArray i = h.first + ": " + h.second;
			headersList = curl_slist_append(headersList, i.data());
		}
 bool HttpContentNegociation::Apply(const HttpHeaders& headers)
 {
   HttpHeaders::const_iterator accept = headers.find("accept");
   if (accept != headers.end())
   {
     return Apply(accept->second);
   }
   else
   {
     return Apply("*/*");
   }
 }
示例#7
0
	void applyHeaders(const HttpHeaders &in, HttpHeaders *out)
	{
		*out += HttpHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");

		QByteArray origin;
		if(in.contains("Origin"))
			origin = in.get("Origin");
		else
			origin = "*";
		*out += HttpHeader("Access-Control-Allow-Origin", origin);
		*out += HttpHeader("Access-Control-Allow-Credentials", "true");
	}
示例#8
0
Response* FakeConnection::getDocument(const std::string& uri) {
  TIMED_FUNC(FakeConnection_getDocument);
  LOG(DEBUG) << "  Entering FakeConnection::getDocument";

  LOG(DEBUG) << "    Fetching document with URI: " << uri;
  std::map<std::string,IDocumentContent*>::iterator it;
  it = mImpl->documents.find(uri);
  std::string ct("");
  std::string mime("");
  if (mImpl->documents.end() != it) {
    LOG(DEBUG) << "    Found document with URI: " << uri;
    IDocumentContent* docPtr = it->second;
    ct = docPtr->getContent();
    mime = docPtr->getMimeType();
  }
  //IDocumentContent& value = mImpl->documents[uri.c_str()];

  //LOG(DEBUG) << "    pointer value: " << value;

  Response* response = new Response;
  LOG(DEBUG) << "  Setting response code";


  LOG(DEBUG) << "  Setting response content ptr";
  //std::unique_ptr<std::string> content(new std::string(ct));
  //response->setContent(std::move(content));
  response->setContent(new std::string(ct));
  LOG(DEBUG) << "  Setting response headers";
  if (0 == ct.compare("")) {
    // not found
    response->setResponseCode(ResponseCode::NOT_FOUND);
    response->setResponseType(ResponseType::UNKNOWN_TYPE);
  } else {
    // found
    HttpHeaders headers;
    headers.setHeader("Content-type",mime);
    response->setResponseHeaders(headers);
    response->setResponseCode(ResponseCode::OK);
    if (0 == mime.compare("application/json")) {
      response->setResponseType(ResponseType::JSON);
    } else if (0 == mime.compare("application/xml") ) {
      response->setResponseType(ResponseType::XML);
    } else if (0 == mime.compare("text/plain")) {
      response->setResponseType(ResponseType::TEXT);
    } else {
      response->setResponseType(ResponseType::BINARY);
    }
  }

  LOG(DEBUG) << "  returning response ";
  return response;
}
示例#9
0
void Logger::logFromClient(std::string &name, HttpHeaders &headers) {
  std::string message = "Got POST (";
  message.append(name);
  message.append(") :\n");
  message.append(headers.getPostData());

  log4cpp::Category::getInstance("sslsniff").info(message);
}
示例#10
0
  void setHeaders(const HttpHeaders& headers)
  {
    if (m_headerlist) {
      curl_slist_free_all(m_headerlist);
      m_headerlist = NULL;
    }

    std::string tmp;
    for (HttpHeaders::const_iterator it=headers.begin(), end=headers.end(); it!=end; ++it) {
      tmp = it->first;
      tmp += ": ";
      tmp += it->second;

      m_headerlist = curl_slist_append(m_headerlist, tmp.c_str());
    }

    curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_headerlist);
  }
示例#11
0
  /*
   * This function serves as a callback for response headers read by libcurl
   *
   * The libcurl documentation at
   * http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTHEADERFUNCTION
   * says that the callback is called once for each header and only complete
   * header line are passed. So we do not need to handle multi-line headers
   * ourselves.
   *
   * Please see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html
   *   The first response header returned will always be "Status-line",
   * Also see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
   */
  static size_t headers_callback(void *buffer, size_t size, size_t nmemb, void *userp) {
    char *buf = reinterpret_cast<char*>(buffer);
    HttpHeaders *pHeaders = reinterpret_cast<HttpHeaders*>(userp);
    size_t result = 0u;
    if (pHeaders != NULL) {
      /*
       * Note: std::string is capable of storing binary stream data and can
       * store "\0" as normal character (witout terminating string).
       *
       * - Until C++11, it is not guaranteed to be contiguously stored though.
       * - Use caution when using .c_str(), if string contains "\0", use .data() instead
       *
       * Since binary data can be stored in std::string, unicode characters
       * can be present But beware of using .length() or .size(), since they
       * will return number of bytes storage (i.e., char) needed to store the
       * string, and not actual number of characters.
       */
      std::string s = "";
      s.append(buf, size * nmemb);
      result = size * nmemb;

      // There can be 3 different cases, each should be handled differently:

      // Case 1: Check for last header line: CRLF,
      //         http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html
      if (s.size() == 2u && s[0] == '\r' && s[1] == '\n')
        return result;

      // Case 2: If it is the first header, the it must be a status Line.
      // For all other cases, statusLine must be populated with some non-zero length string
      if (pHeaders->getStatusLine().length() == 0u) {
        pHeaders->setStatusLine(HttpHelperUtils::stripWhitespaces(s));
        return result;
      }

      // Case 3: This is a usual message header, of form
      // message-header = field-name ":" [ field-value ]
      // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
      pHeaders->appendHeaderString(s);
    }

    return result;
  }
示例#12
0
Response* FakeConnection::search(const SearchDescription& desc) {
  TIMED_FUNC(FakeConnection_search);
  LOG(DEBUG) << "  Entering FakeConnection::search";

  Response* response = new Response;

  response->setResponseCode(ResponseCode::OK);
  HttpHeaders headers;
  headers.setHeader("Content-type","application/json");
  response->setResponseHeaders(headers);
  std::ostringstream cos;
  cos <<
      "{\"response\": {" <<
        "\"start\": 1, \"page-length\": ";

  // return 10 or documents.length, whichever is smaller
  if (mImpl->documents.size() < 10) {
    cos << mImpl->documents.size();
  } else {
    cos << 10;
  }
  cos << ", \"results\": [";

  // loop over documents in map
  std::map<std::string,IDocumentContent*>::iterator it;
  int count = 0;
  for (it = mImpl->documents.begin();it != mImpl->documents.end() && count < 10;++it) {
    ++count; // iterate first as MarkLogic responses are 1 indexed, not 0 indexed
    if (count > 1) {
      cos << ",";
    }
    cos << "{\"index\": " << count << " , \"uri\": \"" << (it->first)
        << "\", \"score\": \"43648\" \"confidence\":\"0.418356\" \"fitness\":\"0.50947\"}";
  }

  cos << "] } }";
  //std::unique_ptr<std::string> cPtr(new std::string(cos.str()));
  //response->setContent(std::move(cPtr));
  response->setContent(new std::string(cos.str()));
  response->setResponseType(ResponseType::JSON);

  return response;
}
void HTTPSBridge::buildRequestFromHeaders(HttpHeaders &headers, std::string &request) {
  std::ostringstream requestStream;
  requestStream << headers.getMethod()  << " "
	  << headers.getRequest() << " "
	  << "HTTP/1.0\r\n";
      
  std::map<std::string,std::string>::iterator iter;
  std::map<std::string,std::string>& headersMap = headers.getHeaders();
  for( iter = headersMap.begin(); iter != headersMap.end(); ++iter ) {
    std::string key   = iter->first;
    std::string value = iter->second;

    Util::trimString(key);
    Util::trimString(value);
    
    if (key != "Accept-Encoding" && key != "Connection" && key != "Keep-Alive")
      requestStream << key << ": " << value << "\r\n";
  }

  requestStream << "Connection: Close" << "\r\n\r\n";
  if (headers.isPost()) requestStream << headers.getPostData();

  request = requestStream.str();
}
示例#14
0
	void start(const QVariant &vrequest, Mode mode)
	{
		outSeq = 0;

		ZurlRequestPacket request;
		if(!request.fromVariant(vrequest))
		{
			log_warning("failed to parse zurl request");

			QVariantHash vhash = vrequest.toHash();
			rid = vhash.value("id").toByteArray();
			assert(!rid.isEmpty()); // app layer ensures this
			receiver = vhash.value("sender").toByteArray();
			bool cancel = vhash.value("cancel").toBool();
			if(!receiver.isEmpty() && !cancel)
			{
				QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "bad-request"));
			}
			else
			{
				cleanup();
				QMetaObject::invokeMethod(q, "finished", Qt::QueuedConnection);
			}

			return;
		}

		rid = request.id;
		receiver = request.sender;
		outCredits = 0;
		userData = request.userData;
		quiet = false;
		sentHeader = false;
		stuffToRead = false;
		bytesReceived = 0;

		ignorePolicies = request.ignorePolicies;

		// streaming only allowed on streaming interface
		if(mode == Worker::Stream)
			outStream = request.stream;
		else
			outStream = false;

		// some required fields
		if(request.method.isEmpty() || request.uri.isEmpty())
		{
			log_warning("missing request method or missing uri");

			QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "bad-request"));
			return;
		}

		log_info("IN id=%s, %s %s", request.id.data(), qPrintable(request.method), request.uri.toEncoded().data());

		// inbound streaming must start with sequence number of 0
		if(mode == Worker::Stream && request.more && request.seq != 0)
		{
			log_warning("streamed input must start with seq 0");

			QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "bad-request"));
			return;
		}

		// fire and forget
		if(mode == Worker::Stream && receiver.isEmpty())
			quiet = true;

		// can't use these two together
		if(mode == Worker::Single && request.more)
		{
			log_warning("cannot use streamed input on router interface");

			QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "bad-request"));
			return;
		}

		bodySent = false;

		inSeq = request.seq;

		if(!isAllowed(request.uri.host()) || (!request.connectHost.isEmpty() && !isAllowed(request.connectHost)))
		{
			QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "policy-violation"));
			return;
		}

		hreq = new HttpRequest(dns, this);
		connect(hreq, SIGNAL(nextAddress(const QHostAddress &)), SLOT(req_nextAddress(const QHostAddress &)));
		connect(hreq, SIGNAL(readyRead()), SLOT(req_readyRead()));
		connect(hreq, SIGNAL(bytesWritten(int)), SLOT(req_bytesWritten(int)));
		connect(hreq, SIGNAL(error()), SLOT(req_error()));
		maxResponseSize = request.maxSize;
		if(!request.connectHost.isEmpty())
			hreq->setConnectHost(request.connectHost);

		hreq->setIgnoreTlsErrors(request.ignoreTlsErrors);

		if(request.credits != -1)
			outCredits += request.credits;

		HttpHeaders headers = request.headers;
		// ensure content-length (or overwrite it, if not streaming input)
		if((request.method == "POST" || request.method == "PUT") && (!headers.contains("content-length") || !request.more))
			headers += HttpHeader("Content-Length", QByteArray::number(request.body.size()));

		timer = new QTimer(this);
		connect(timer, SIGNAL(timeout()), SLOT(timer_timeout()));
		timer->setSingleShot(true);
		timer->start(config->sessionTimeout * 1000);

		hreq->start(request.method, request.uri, headers);

		// note: unlike follow-up requests, the initial request is assumed to have a body.
		//   if no body field is present, we act as if it is present but empty.

		if(!request.body.isEmpty())
		{
			if(request.more && !request.headers.contains("content-length"))
			{
				log_warning("streamed input requires content-length");
				QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "length-required"));
				return;
			}

			hreq->writeBody(request.body);
		}

		if(!request.more)
		{
			bodySent = true;
			hreq->endBody();
		}
		else
		{
			// send cts
			ZurlResponsePacket resp;
			resp.credits = config->sessionBufferSize;
			writeResponse(resp);
		}
	}
示例#15
0
int CGnutellaSocket::ParseHandshake()
{
	const char* p = m_strHandshake.c_str();
	const char* pend = p+m_strHandshake.length();

	if (!strncmp(p, "GNUTELLA/0.6", 12))
	{
		p += 12;
		p++;
		long code = strtol(p, (char**)&p, 10);
		p++;

		StringA codemsg;
		while (p < pend)
		{
			if (p[0] == '\r' && p[1] == '\n')
			{
				break;
			}

			codemsg += *p;
			p++;
		}

		if (*p++ != '\r') ASSERT(0);
		if (*p++ != '\n') ASSERT(0);

	//	HttpHeaders headers;
		m_headers.AddHeaders(p);

		/*
		while (p < pend)
		{
			std::string header;
			while (p < pend)
			{
				if (p[0] == '\r' && p[1] == '\n')
				{
					break;
				}

				header += *p;
				p++;
			}

			if (*p++ != '\r') ASSERT(0);
			if (*p++ != '\n') ASSERT(0);

			m_headers.push_back(header);
		}
		ASSERT(*p == 0);
		*/

		return code;
	}
	else
	{
		ASSERT(0);
		return 0;
	}
}
示例#16
0
	// return true if jsonp applied
	bool tryApplyJsonp(const DomainMap::JsonpConfig &config, bool *ok, QString *errorMessage)
	{
		*ok = true;

		// must be a GET
		if(requestData.method != "GET")
			return false;

		QString callbackParam = QString::fromUtf8(config.callbackParam);
		if(callbackParam.isEmpty())
			callbackParam = "callback";

		QString bodyParam;
		if(!config.bodyParam.isEmpty())
			bodyParam = QString::fromUtf8(config.bodyParam);

		QUrl uri = requestData.uri;
		QUrlQuery query(uri);

		// two ways to activate JSON-P:
		//   1) callback param present
		//   2) default callback specified in configuration and body param present
		if(!query.hasQueryItem(callbackParam) &&
			(config.defaultCallback.isEmpty() || bodyParam.isEmpty() || !query.hasQueryItem(bodyParam)))
		{
			return false;
		}

		QByteArray callback;
		if(query.hasQueryItem(callbackParam))
		{
			callback = parsePercentEncoding(query.queryItemValue(callbackParam, QUrl::FullyEncoded).toUtf8());
			if(callback.isEmpty())
			{
				log_debug("requestsession: id=%s invalid callback parameter, rejecting", rid.second.data());
				*ok = false;
				*errorMessage = "Invalid callback parameter.";
				return false;
			}

			query.removeAllQueryItems(callbackParam);
		}
		else
			callback = config.defaultCallback;

		QString method;
		if(query.hasQueryItem("_method"))
		{
			method = QString::fromLatin1(parsePercentEncoding(query.queryItemValue("_method", QUrl::FullyEncoded).toUtf8()));
			if(!validMethod(method))
			{
				log_debug("requestsession: id=%s invalid _method parameter, rejecting", rid.second.data());
				*ok = false;
				*errorMessage = "Invalid _method parameter.";
				return false;
			}

			query.removeAllQueryItems("_method");
		}

		HttpHeaders headers;
		if(query.hasQueryItem("_headers"))
		{
			QJsonParseError e;
			QJsonDocument doc = QJsonDocument::fromJson(parsePercentEncoding(query.queryItemValue("_headers", QUrl::FullyEncoded).toUtf8()), &e);
			if(e.error != QJsonParseError::NoError || !doc.isObject())
			{
				log_debug("requestsession: id=%s invalid _headers parameter, rejecting", rid.second.data());
				*ok = false;
				*errorMessage = "Invalid _headers parameter.";
				return false;
			}

			QVariantMap headersMap = doc.object().toVariantMap();

			QMapIterator<QString, QVariant> vit(headersMap);
			while(vit.hasNext())
			{
				vit.next();

				if(vit.value().type() != QVariant::String)
				{
					log_debug("requestsession: id=%s invalid _headers parameter, rejecting", rid.second.data());
					*ok = false;
					*errorMessage = "Invalid _headers parameter.";
					return false;
				}

				QByteArray key = vit.key().toUtf8();

				// ignore some headers that we explicitly set later on
				if(qstricmp(key.data(), "host") == 0)
					continue;
				if(qstricmp(key.data(), "accept") == 0)
					continue;

				headers += HttpHeader(key, vit.value().toString().toUtf8());
			}

			query.removeAllQueryItems("_headers");
		}

		QByteArray body;
		if(!bodyParam.isEmpty())
		{
			if(query.hasQueryItem(bodyParam))
			{
				body = parsePercentEncoding(query.queryItemValue(bodyParam, QUrl::FullyEncoded).toUtf8());
				if(body.isNull())
				{
					log_debug("requestsession: id=%s invalid body parameter, rejecting", rid.second.data());
					*ok = false;
					*errorMessage = "Invalid body parameter.";
					return false;
				}

				headers.removeAll("Content-Type");
				headers += HttpHeader("Content-Type", "application/json");

				query.removeAllQueryItems(bodyParam);
			}
		}
		else
		{
			if(query.hasQueryItem("_body"))
			{
				body = parsePercentEncoding(query.queryItemValue("_body").toUtf8());
				if(body.isNull())
				{
					log_debug("requestsession: id=%s invalid _body parameter, rejecting", rid.second.data());
					*ok = false;
					*errorMessage = "Invalid _body parameter.";
					return false;
				}

				query.removeAllQueryItems("_body");
			}
		}

		uri.setQuery(query);

		// if we have no query items anymore, strip the '?'
		if(query.isEmpty())
		{
			QByteArray tmp = uri.toEncoded();
			if(tmp.length() > 0 && tmp[tmp.length() - 1] == '?')
			{
				tmp.truncate(tmp.length() - 1);
				uri = QUrl::fromEncoded(tmp, QUrl::StrictMode);
			}
		}

		if(method.isEmpty())
			method = config.defaultMethod;

		requestData.method = method;

		requestData.uri = uri;

		headers += HttpHeader("Host", uri.host().toUtf8());
		headers += HttpHeader("Accept", "*/*");

		// carry over the rest of the headers
		foreach(const HttpHeader &h, requestData.headers)
		{
			if(qstricmp(h.first.data(), "host") == 0)
				continue;
			if(qstricmp(h.first.data(), "accept") == 0)
				continue;

			headers += h;
		}

		requestData.headers = headers;
		in += body;

		jsonpCallback = callback;
		jsonpExtendedResponse = (config.mode == DomainMap::JsonpConfig::Extended);

		return true;
	}
示例#17
0
	void start(const QVariant &vrequest, Mode mode)
	{
		outSeq = 0;
		outCredits = 0;
		quiet = false;

		ZhttpRequestPacket request;
		if(!request.fromVariant(vrequest))
		{
			log_warning("failed to parse zurl request");

			QVariantHash vhash = vrequest.toHash();
			rid = vhash.value("id").toByteArray();
			toAddress = vhash.value("from").toByteArray();
			QByteArray type = vhash.value("type").toByteArray();
			if(!toAddress.isEmpty() && type != "error" && type != "cancel")
			{
				QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "bad-request"));
			}
			else
			{
				cleanup();
				QMetaObject::invokeMethod(q, "finished", Qt::QueuedConnection);
			}

			return;
		}

		rid = request.id;
		toAddress = request.from;
		userData = request.userData;
		sentHeader = false;
		stuffToRead = false;
		bytesReceived = 0;

		ignorePolicies = request.ignorePolicies;

		if(request.uri.isEmpty())
		{
			log_warning("missing request uri");

			QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "bad-request"));
			return;
		}

		QString scheme = request.uri.scheme();
		if(scheme == "https" || scheme == "http")
		{
			transport = HttpTransport;
		}
		else if(scheme == "wss" || scheme == "ws")
		{
			transport = WebSocketTransport;
		}
		else
		{
			log_warning("unsupported scheme");

			QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "bad-request"));
			return;
		}

		if(transport == WebSocketTransport && mode != Worker::Stream)
		{
			log_warning("websocket must be used from stream interface");

			QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "bad-request"));
			return;
		}

		int defaultPort;
		if(scheme == "https" || scheme == "wss")
			defaultPort = 443;
		else // http || wss
			defaultPort = 80;

		HttpHeaders headers = request.headers;

		if(transport == HttpTransport)
		{
			// fire and forget
			if(mode == Worker::Stream && (rid.isEmpty() || toAddress.isEmpty()))
				quiet = true;

			// streaming only allowed on streaming interface
			if(mode == Worker::Stream)
				outStream = request.stream;
			else
				outStream = false;

			if(request.method.isEmpty())
			{
				log_warning("missing request method");

				QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "bad-request"));
				return;
			}

			log_info("IN id=%s, %s %s", request.id.data(), qPrintable(request.method), request.uri.toEncoded().data());

			// inbound streaming must start with sequence number of 0
			if(mode == Worker::Stream && request.more && request.seq != 0)
			{
				log_warning("streamed input must start with seq 0");

				QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "bad-request"));
				return;
			}

			// can't use these two together
			if(mode == Worker::Single && request.more)
			{
				log_warning("cannot use streamed input on router interface");

				QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "bad-request"));
				return;
			}

			bodySent = false;

			inSeq = request.seq;

			if(!isAllowed(request.uri.host()) || (!request.connectHost.isEmpty() && !isAllowed(request.connectHost)))
			{
				QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "policy-violation"));
				return;
			}

			QByteArray hostHeader = request.uri.host().toUtf8();

			// only tack on the port if it isn't being overridden
			int port = request.uri.port(defaultPort);
			if(request.connectPort == -1 && port != defaultPort)
				hostHeader += ":" + QByteArray::number(port);

			headers.removeAll("Host");
			headers += HttpHeader("Host", hostHeader);

			hreq = new HttpRequest(dns, this);
			connect(hreq, SIGNAL(nextAddress(const QHostAddress &)), SLOT(req_nextAddress(const QHostAddress &)));
			connect(hreq, SIGNAL(readyRead()), SLOT(req_readyRead()));
			connect(hreq, SIGNAL(bytesWritten(int)), SLOT(req_bytesWritten(int)));
			connect(hreq, SIGNAL(error()), SLOT(req_error()));

			maxResponseSize = request.maxSize;
			sessionTimeout = request.timeout;

			if(!request.connectHost.isEmpty())
				hreq->setConnectHost(request.connectHost);
			if(request.connectPort != -1)
				request.uri.setPort(request.connectPort);

			hreq->setIgnoreTlsErrors(request.ignoreTlsErrors);
			if(request.followRedirects)
				hreq->setFollowRedirects(8);

			if(request.credits != -1)
				outCredits += request.credits;
		}
		else // WebSocketTransport
		{
			log_info("IN id=%s, %s", request.id.data(), request.uri.toEncoded().data());

			// inbound streaming must start with sequence number of 0
			if(request.seq != 0)
			{
				log_warning("websocket input must start with seq 0");

				QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "bad-request"));
				return;
			}

			if(toAddress.isEmpty())
			{
				log_warning("websocket input must provide from address");

				QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "bad-request"));
				return;
			}

			inSeq = request.seq;

			if(!isAllowed(request.uri.host()) || (!request.connectHost.isEmpty() && !isAllowed(request.connectHost)))
			{
				QMetaObject::invokeMethod(this, "respondError", Qt::QueuedConnection, Q_ARG(QByteArray, "policy-violation"));
				return;
			}

			QByteArray hostHeader = request.uri.host().toUtf8();

			// only tack on the port if it isn't being overridden
			int port = request.uri.port(defaultPort);
			if(request.connectPort == -1 && port != defaultPort)
				hostHeader += ":" + QByteArray::number(port);

			headers.removeAll("Host");
			headers += HttpHeader("Host", hostHeader);

			ws = new WebSocket(dns, this);
			connect(ws, SIGNAL(nextAddress(const QHostAddress &)), SLOT(req_nextAddress(const QHostAddress &)));
			connect(ws, SIGNAL(connected()), SLOT(ws_connected()));
			connect(ws, SIGNAL(readyRead()), SLOT(ws_readyRead()));
			connect(ws, SIGNAL(framesWritten(int)), SLOT(ws_framesWritten(int)));
			connect(ws, SIGNAL(peerClosing()), SLOT(ws_peerClosing()));
			connect(ws, SIGNAL(closed()), SLOT(ws_closed()));
			connect(ws, SIGNAL(error()), SLOT(ws_error()));

			if(!request.connectHost.isEmpty())
				ws->setConnectHost(request.connectHost);
			if(request.connectPort != -1)
				request.uri.setPort(request.connectPort);

			ws->setIgnoreTlsErrors(request.ignoreTlsErrors);
			ws->setMaxFrameSize(config->sessionBufferSize);

			if(request.credits != -1)
				outCredits += request.credits;
		}

		httpActivityTimer = new QTimer(this);
		connect(httpActivityTimer, SIGNAL(timeout()), SLOT(httpActivity_timeout()));
		httpActivityTimer->setSingleShot(true);
		httpActivityTimer->start(config->activityTimeout * 1000);

		if(sessionTimeout != -1)
		{
			httpSessionTimer = new QTimer(this);
			connect(httpSessionTimer, SIGNAL(timeout()), SLOT(httpSession_timeout()));
			httpSessionTimer->setSingleShot(true);
			httpSessionTimer->start(sessionTimeout);
		}

		if(transport == WebSocketTransport || (transport == HttpTransport && mode == Worker::Stream))
		{
			expireTimer = new QTimer(this);
			connect(expireTimer, SIGNAL(timeout()), SLOT(expire_timeout()));
			expireTimer->setSingleShot(true);
			expireTimer->start(SESSION_EXPIRE);

			keepAliveTimer = new QTimer(this);
			connect(keepAliveTimer, SIGNAL(timeout()), SLOT(keepAlive_timeout()));
			keepAliveTimer->start(SESSION_EXPIRE / 2);
		}

		if(transport == HttpTransport)
		{
			if(!request.body.isEmpty() && !request.more && !headers.contains("Content-Length"))
				headers += HttpHeader("Content-Length", QByteArray::number(request.body.size()));

			bool hasOrMightHaveBody = (!request.body.isEmpty() || request.more);

			hreq->start(request.method, request.uri, headers, hasOrMightHaveBody);

			if(hasOrMightHaveBody)
			{
				if(!request.body.isEmpty())
					hreq->writeBody(request.body);

				if(!request.more)
				{
					bodySent = true;
					hreq->endBody();
				}
			}
			else
				bodySent = true;

			if(mode == Stream)
			{
				if(request.more)
				{
					// send cts
					ZhttpResponsePacket resp;
					resp.type = ZhttpResponsePacket::Credit;
					resp.credits = config->sessionBufferSize;
					writeResponse(resp);
				}
				else
				{
					// send ack
					ZhttpResponsePacket resp;
					resp.type = ZhttpResponsePacket::KeepAlive;
					writeResponse(resp);
				}
			}
		}
		else // WebSocketTransport
		{
			ws->start(request.uri, headers);
		}
	}