Ejemplo n.º 1
0
	void respondOk(ZhttpRequest *req, const QVariant &data, const QByteArray &prefix = QByteArray(), const QByteArray &jsonpCallback = QByteArray())
	{
		HttpHeaders headers;
		if(!jsonpCallback.isEmpty())
			headers += HttpHeader("Content-Type", "application/javascript");
		else
			headers += HttpHeader("Content-Type", "text/plain");

		QJson::Serializer serializer;
		QByteArray body;
		if(data.isValid())
			body = serializer.serialize(data);
		if(!prefix.isEmpty())
			body.prepend(prefix);

		if(!jsonpCallback.isEmpty())
		{
			QByteArray encBody = serializer.serialize(QString::fromUtf8(body));
			body = "/**/" + jsonpCallback + '(' + encBody + ");\n";
		}
		else if(!body.isEmpty())
			body += "\n"; // newline is required

		respond(req, 200, "OK", headers, body);
	}
Ejemplo n.º 2
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);
	}
Ejemplo n.º 3
0
	void respond(int code, const QString &status, const QByteArray &body)
	{
		HttpHeaders headers;
		headers += HttpHeader("Content-Type", "text/plain");
		headers += HttpHeader("Content-Length", QByteArray::number(body.size()));

		respond(code, status.toUtf8(), headers, body);
	}
Ejemplo n.º 4
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");
	}
Ejemplo n.º 5
0
	void respond(int code, const QByteArray &reason, const QString &body)
	{
		HttpHeaders headers;
		headers += HttpHeader("Content-Type", "text/plain");

		respond(code, reason, headers, body.toUtf8());
	}
Ejemplo n.º 6
0
HttpHeader HttpHeader::Builder::build() const {
    std::string headerStr;

    switch (m_requestType) {
    case eRequestType::HTTP_REQUEST_GET: {
        headerStr += "GET ";
        headerStr += m_requestPath;
        headerStr += " HTTP/1.1\r\n";
        break;
    }
    case eRequestType::HTTP_REQUEST_POST: {
        headerStr += "POST ";
        headerStr += m_requestPath;
        headerStr += " HTTP/1.1\r\n";
        break;
    }
    case eRequestType::HTTP_RESPONSE: {
        headerStr += "HTTP/1.1 ";
        headerStr += StatusString(m_statusCode);
        headerStr += "\r\n";
        break;
    }

    case eRequestType::UNKNOWN: {
        return HttpHeader();
    }
    }

    headerStr += Joiner().on("\r\n").join(m_headers.begin(), m_headers.end());
    headerStr += "\r\n\r\n";

    HttpHeader rVal;
    rVal.parsePartial(core::memory::ConstBlob((u8 *) headerStr.c_str(), headerStr.length()));
    return rVal;
}
Ejemplo n.º 7
0
	void respondOk(ZhttpRequest *req, const QString &str, const QByteArray &jsonpCallback = QByteArray())
	{
		HttpHeaders headers;
		if(!jsonpCallback.isEmpty())
			headers += HttpHeader("Content-Type", "application/javascript");
		else
			headers += HttpHeader("Content-Type", "text/plain");

		QByteArray body;
		if(!jsonpCallback.isEmpty())
		{
			QByteArray encBody = serializeJsonString(str);
			body = "/**/" + jsonpCallback + '(' + encBody + ");\n";
		}
		else
			body = str.toUtf8();

		respond(req, 200, "OK", headers, body);
	}
Ejemplo n.º 8
0
bool CHttpServer::call_ruby_method(String const &uri, String const &body, String& strReply)
{
    Route route;
    if (!dispatch(uri, route)) 
        return false;

    HeaderList headers;
    headers.addElement(HttpHeader("Content-Type","application/x-www-form-urlencoded"));
    VALUE req = create_request_hash(route.application, route.model, route.action, route.id,
                                    "POST", uri, String(), headers, body);
    VALUE data = callFramework(req);
    strReply = String(getStringFromValue(data), getStringLenFromValue(data));
    rho_ruby_releaseValue(data);

    return true;
}
Ejemplo n.º 9
0
void CHttpServer::call_ruby_proc( rho::String const &query, String const &body )
{
    unsigned long valProc = 0;
    convertFromStringA( query.c_str(), valProc );

    HeaderList headers;
    headers.addElement(HttpHeader("Content-Type","application/x-www-form-urlencoded"));
    VALUE req = create_request_hash("", "", "", "", "POST", "", String(), headers, body);
    addHashToHash(req,"proc",valProc);

    VALUE data = callFramework(req);
    String strReply = String(getStringFromValue(data), getStringLenFromValue(data));
    rho_ruby_releaseValue(data);

    send_response(strReply);
}
Ejemplo n.º 10
0
	bool processHeaderData(const QByteArray &headerData)
	{
		QList<QByteArray> lines;
		int at = 0;
		while(at < headerData.size())
		{
			int end = headerData.indexOf("\n", at);
			assert(end != -1);

			if(end > at && headerData[end - 1] == '\r')
				lines += headerData.mid(at, end - at - 1);
			else
				lines += headerData.mid(at, end - at);
			at = end + 1;
		}

		if(lines.isEmpty())
			return false;

		QByteArray requestLine = lines[0];

		at = requestLine.indexOf(' ');
		if(at == -1)
			return false;

		method = QString::fromLatin1(requestLine.mid(0, at));
		if(method.isEmpty())
			return false;

		++at;
		int end = requestLine.indexOf(' ', at);
		if(end == -1)
			return false;

		uri = requestLine.mid(at, end - at);

		QByteArray versionStr = requestLine.mid(end + 1);
		if(versionStr == "HTTP/1.0")
			version1dot0 = true;

		for(int n = 1; n < lines.count(); ++n)
		{
			const QByteArray &line = lines[n];
			end = line.indexOf(':');
			if(end == -1)
				continue;

			// skip first space
			at = end + 1;
			if(at < line.length() && line[at] == ' ')
				++at;

			QByteArray name = line.mid(0, end);
			QByteArray val = line.mid(at);

			reqHeaders += HttpHeader(name, val);
		}

		//log_debug("httpserver: IN method=[%s] uri=[%s] 1.1=%s", qPrintable(method), uri.data(), version1dot0 ? "no" : "yes");
		//foreach(const HttpHeader &h, reqHeaders)
		//	log_debug("httpserver:   [%s] [%s]", h.first.data(), h.second.data());
		log_debug("httpserver: IN %s %s", qPrintable(method), uri.data());

		return true;
	}
Ejemplo n.º 11
0
bool CHttpServer::receive_request(ByteVector &request)
{
	if (verbose) RAWTRACE("Receiving request...");

	ByteVector r;
    char buf[BUF_SIZE];
    int attempts = 0;
    for(;;) {
        if (verbose) RAWTRACE("Read portion of data from socket...");
        int n = recv(m_sock, &buf[0], sizeof(buf), 0);
        //RAWTRACE1("RECV: %d", n);
        if (n == -1) {
            int e = RHO_NET_ERROR_CODE;
            if (verbose) RAWTRACE1("RECV ERROR: %d", e);
#if !defined(WINDOWS_PLATFORM)
            if (e == EINTR)
                continue;
#else
			if (e == WSAEINTR)
				continue;
#endif

#if defined(OS_WP8) || (defined(RHODES_QT_PLATFORM) && defined(OS_WINDOWS_DESKTOP)) || defined(OS_WINCE)
            if (e == EAGAIN || e == WSAEWOULDBLOCK) {
#else
            if (e == EAGAIN) {
#endif
                if (!r.empty())
                    break;
                
                if(++attempts > (HTTP_EAGAIN_TIMEOUT*10))
                {
                    if (verbose) RAWLOG_ERROR("Error when receiving data from socket. Client does not send data for " HTTP_EAGAIN_TIMEOUT_STR " sec. Cancel recieve.");
                    return false;
                }

                fd_set fds;
                FD_ZERO(&fds);
                FD_SET(m_sock, &fds);
                timeval tv = {0};
				tv.tv_usec = 100000;//100 MS
                select(m_sock + 1, &fds, 0, 0, &tv);
                continue;
            }
            
            if (verbose) RAWLOG_ERROR1("Error when receiving data from socket: %d", e);
            return false;
        }
        
        if (n == 0) {
            if(!r.empty()) {
                if (verbose) RAWTRACE("Client closed connection gracefully");
                break;
            } else {
                if (verbose) RAWLOG_ERROR("Connection gracefully closed before we receive any data");
                return false;
            }
        } else {
            if (verbose) RAWTRACE1("Actually read %d bytes", n);
            r.insert(r.end(), &buf[0], &buf[0] + n);
        }
    }
    
    if (!r.empty()) {
        request.insert(request.end(), r.begin(), r.end());
        if ( !rho_conf_getBool("log_skip_post") ) {
            String strRequest(request.begin(),request.end());
            if (verbose) RAWTRACE1("Received request:\n%s", strRequest.c_str());
        }
    }
    return true;
}

bool CHttpServer::send_response_impl(String const &data, bool continuation)
{
#ifdef OS_MACOSX
    if ( m_localResponseWriter != 0 ) {
      m_localResponseWriter->writeResponse( data );
      return true;
    }
#endif

    if (verbose) {
        if (continuation)
            if (verbose) RAWTRACE("Send continuation data...");
        else
            if (verbose) RAWTRACE("Sending response...");
    }
    
    // First of all, make socket blocking
#if defined(WINDOWS_PLATFORM)
    unsigned long optval = 0;
        if(::ioctlsocket(m_sock, FIONBIO, &optval) == SOCKET_ERROR) {
        RAWLOG_ERROR1("Can not set blocking socket mode: %d", RHO_NET_ERROR_CODE);
        return false;
    }
#else
    int flags = fcntl(m_sock, F_GETFL);
    if (flags == -1) {
        if (verbose) RAWLOG_ERROR1("Can not get current socket mode: %d", errno);
        return false;
    }
    if (fcntl(m_sock, F_SETFL, flags & ~O_NONBLOCK) == -1) {
        if (verbose) RAWLOG_ERROR1("Can not set blocking socket mode: %d", errno);
        return false;
    }
#endif
    
    size_t pos = 0;
    for(; pos < data.size();) {
        int n = send(m_sock, data.c_str() + pos, data.size() - pos, 0);
        if (n == -1) {
            int e = RHO_NET_ERROR_CODE;
#if !defined(WINDOWS_PLATFORM)
            if (e == EINTR)
                continue;
#endif
            
            if (verbose) RAWLOG_ERROR1("Can not send response data: %d", e);
            return false;
        }
        
        if (n == 0)
            break;
        
        pos += n;
    }
    
    //String dbg_response = response.size() > 100 ? response.substr(0, 100) : response;
    //RAWTRACE2("Sent response:\n%s%s", dbg_response.c_str(), response.size() > 100 ? "..." : "   ");
    if (continuation) {
        if (verbose) RAWTRACE1("Sent response body: %d bytes", data.size());
    }
    else if ( !rho_conf_getBool("log_skip_post") ) {
        if (verbose) RAWTRACE1("Sent response (only headers displayed):\n%s", data.c_str());
    }

    return true;
}

bool CHttpServer::send_response(String const &response, bool redirect)
{
#ifdef OS_ANDROID
    if (redirect) {
        CAutoPtr<IRhoThreadImpl> ptrThread = rho_get_RhoClassFactory()->createThreadImpl();
        ptrThread->sleep(20);
    }
#endif
    return send_response_impl(response, false);
}

String CHttpServer::create_response(String const &reason)
{
    return create_response(reason, "");
}

String CHttpServer::create_response(String const &reason, HeaderList const &headers)
{
    return create_response(reason, headers, "");
}

String CHttpServer::create_response(String const &reason, String const &body)
{
    return create_response(reason, HeaderList(), body);
}

String CHttpServer::create_response(String const &reason, HeaderList const &hdrs, String const &body)
{
    String response = "HTTP/1.1 ";
    response += reason;
    response += "\r\n";
    
    char buf[50];
    snprintf(buf, sizeof(buf), "%d", m_port);
    
    HeaderList headers;
    headers.push_back(Header("Host", String("127.0.0.1:") + buf));
    headers.push_back(Header("Connection", "close"));
    headers.push_back(HttpHeader("Access-Control-Allow-Origin", "*"));
    std::copy(hdrs.begin(), hdrs.end(), std::back_inserter(headers));
    
    for(HeaderList::const_iterator it = headers.begin(), lim = headers.end();
        it != lim; ++it) {
        response += it->name;
        response += ": ";
        response += it->value;
        response += "\r\n";
    }
    
    response += "\r\n";
    
    response += body;
    
    return response;
}
Ejemplo n.º 12
0
	void add(RequestSession *rs)
	{
		assert(addAllowed);
		assert(!route.isNull());

		SessionItem *si = new SessionItem;
		si->rs = rs;
		si->rs->setParent(this);

		if(!sessionItems.isEmpty())
			shared = true;

		sessionItems += si;
		sessionItemsBySession.insert(rs, si);
		connect(rs, &RequestSession::bytesWritten, this, &Private::rs_bytesWritten);
		connect(rs, &RequestSession::errorResponding, this, &Private::rs_errorResponding);
		connect(rs, &RequestSession::finished, this, &Private::rs_finished);
		connect(rs, &RequestSession::paused, this, &Private::rs_paused);

		if(state == Stopped)
		{
			isHttps = rs->isHttps();

			requestData = rs->requestData();
			requestBody += requestData.body;
			requestData.body.clear();

			if(!route.asHost.isEmpty())
				ProxyUtil::applyHost(&requestData.uri, route.asHost);

			QByteArray path = requestData.uri.path(QUrl::FullyEncoded).toUtf8();

			if(route.pathRemove > 0)
				path = path.mid(route.pathRemove);

			if(!route.pathPrepend.isEmpty())
				path = route.pathPrepend + path;

			requestData.uri.setPath(QString::fromUtf8(path), QUrl::StrictMode);

			QByteArray sigIss;
			QByteArray sigKey;
			if(!route.sigIss.isEmpty() && !route.sigKey.isEmpty())
			{
				sigIss = route.sigIss;
				sigKey = route.sigKey;
			}
			else
			{
				sigIss = defaultSigIss;
				sigKey = defaultSigKey;
			}

			targets = route.targets;

			foreach(const HttpHeader &h, route.headers)
			{
				requestData.headers.removeAll(h.first);
				requestData.headers += HttpHeader(h.first, h.second);
			}

			bool intReq = false;

			if(!rs->isRetry())
			{
				inRequest = rs->request();

				connect(inRequest, &ZhttpRequest::readyRead, this, &Private::inRequest_readyRead);
				connect(inRequest, &ZhttpRequest::error, this, &Private::inRequest_error);

				requestBody += inRequest->readBody();

				intReq = inRequest->passthroughData().isValid();
			}

			trustedClient = ProxyUtil::manipulateRequestHeaders("proxysession", q, &requestData, defaultUpstreamKey, route, sigIss, sigKey, acceptXForwardedProtocol, useXForwardedProtocol, xffTrustedRule, xffRule, origHeadersNeedMark, rs->peerAddress(), idata, !intReq);

			state = Requesting;
			buffering = true;

			if(trustedClient || intReq)
				passthrough = true;

			initialRequestBody = requestBody.toByteArray();

			if(requestBody.size() > MAX_ACCEPT_REQUEST_BODY)
			{
				requestBody.clear();
				buffering = false;
			}

			tryNextTarget();
		}
Ejemplo n.º 13
0
	void handleRequest(Session *s)
	{
		QString method = s->req->requestMethod();
		log_debug("sockjs request: path=[%s], asUri=[%s]", s->path.data(), s->asUri.toEncoded().data());

		if(method == "OPTIONS")
		{
			respondEmpty(s->req);
		}
		else if(method == "GET" && s->path == "/info")
		{
			quint32 x = (quint32)qrand();

			QVariantMap out;
			out["websocket"] = true;
			out["origins"] = QVariantList() << QString("*:*");
			out["cookie_needed"] = false;
			out["entropy"] = x;
			respondOk(s->req, out);
		}
		else if(method == "GET" && s->path.startsWith("/iframe") && s->path.endsWith(".html"))
		{
			HttpHeaders headers;
			headers += HttpHeader("ETag", iframeHtmlEtag);

			QByteArray ifNoneMatch = s->req->requestHeaders().get("If-None-Match");
			if(ifNoneMatch == iframeHtmlEtag)
			{
				respond(s->req, 304, "Not Modified", headers, QByteArray());
			}
			else
			{
				headers += HttpHeader("Content-Type", "text/html; charset=UTF-8");
				headers += HttpHeader("Cache-Control", "public, max-age=31536000");
				respond(s->req, 200, "OK", headers, iframeHtml);
			}
		}
		else
		{
			QList<QByteArray> parts = s->path.mid(1).split('/');
			if(parts.count() == 3)
			{
				QByteArray sid = parts[1];
				QByteArray lastPart = parts[2];

				Session *existing = sessionsById.value(sid);
				if(existing)
				{
					if(existing->ext)
					{
						// give to external session
						ZhttpRequest *req = s->req;
						QByteArray body = s->reqBody.toByteArray();
						QByteArray jsonpCallback = s->jsonpCallback;
						s->req->disconnect(this);
						s->req = 0;
						removeSession(s);

						existing->ext->handleRequest(req, jsonpCallback, lastPart, body);
					}
					else
					{
						if(existing->closeValue.isValid())
						{
							respondOk(s->req, existing->closeValue, "c", s->jsonpCallback);
						}
						else
						{
							QVariantList out;
							out += 2010;
							out += QString("Another connection still open");
							respondOk(s->req, out, "c", s->jsonpCallback);
						}
					}
					return;
				}

				if((method == "POST" && lastPart == "xhr") || ((method == "GET" || method == "POST") && lastPart == "jsonp"))
				{
					if(lastPart == "jsonp" && s->jsonpCallback.isEmpty())
					{
						respondError(s->req, 400, "Bad Request", "Bad Request");
						return;
					}

					s->sid = sid;
					s->lastPart = lastPart;
					sessionsById.insert(s->sid, s);
					s->pending = true;
					pendingSessions += s;
					emit q->sessionReady();
					return;
				}
			}

			respondError(s->req, 404, "Not Found", "Not Found");
		}
	}
Ejemplo n.º 14
0
	void respondError(ZWebSocket *sock, int code, const QByteArray &reason, const QString &message)
	{
		HttpHeaders headers;
		headers += HttpHeader("Content-Type", "text/plain");
		sock->respondError(code, reason, headers, message.toUtf8() + '\n');
	}
Ejemplo n.º 15
0
	void respondError(ZhttpRequest *req, int code, const QByteArray &reason, const QString &message)
	{
		HttpHeaders headers;
		headers += HttpHeader("Content-Type", "text/plain");
		respond(req, code, reason, headers, message.toUtf8() + '\n');
	}
Ejemplo n.º 16
0
	void respondEmpty(ZhttpRequest *req)
	{
		HttpHeaders headers;
		headers += HttpHeader("Content-Type", "text/plain"); // workaround FF issue. see sockjs spec.
		respond(req, 204, "No Content", headers, QByteArray());
	}
Ejemplo n.º 17
0
bool M2RequestPacket::fromByteArray(const QByteArray &in)
{
	int start = 0;
	int end = in.indexOf(' ');
	if(end == -1)
		return false;

	sender = in.mid(start, end - start);

	start = end + 1;
	end = in.indexOf(' ', start);
	if(end == -1)
		return false;

	id = in.mid(start, end - start);

	start = end + 1;
	end = in.indexOf(' ', start);
	if(end == -1)
		return false;

	QByteArray path_only = in.mid(start, end - start);

	start = end + 1;
	TnetString::Type type;
	int offset, size;
	if(!TnetString::check(in, start, &type, &offset, &size))
		return false;

	if(type != TnetString::Hash && type != TnetString::ByteArray)
		return false;

	bool ok;
	QVariant vheaders = TnetString::toVariant(in, start, type, offset, size, &ok);
	if(!ok)
		return false;

	QVariantMap headersMap;
	if(type == TnetString::Hash)
	{
		headersMap = vheaders.toMap();
	}
	else // ByteArray
	{
		QJson::Parser parser;
		vheaders = parser.parse(vheaders.toByteArray(), &ok);
		if(!ok)
			return false;

		headersMap = vheaders.toMap();
	}

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

		if(vit.value().type() != QVariant::String)
			return false;

		m2headers[vit.key()] = vit.value().toString().toUtf8();
	}

	start = offset + size + 1;
	if(!TnetString::check(in, start, &type, &offset, &size))
		return false;

	if(type != TnetString::ByteArray)
		return false;

	body = TnetString::toByteArray(in, start, offset, size, &ok);
	if(!ok)
		return false;

	scheme = m2headers.value("URL_SCHEME");

	QByteArray m2method = m2headers.value("METHOD");

	if(m2method == "JSON")
	{
		QJson::Parser parser;
		QVariant vdata = parser.parse(body, &ok);
		if(!ok)
			return false;

		if(vdata.type() != QVariant::Map)
			return false;

		QVariantMap data = vdata.toMap();
		if(!data.contains("type") || data["type"].type() != QVariant::String)
			return false;

		QString type = data["type"].toString();
		if(type != "disconnect")
			return false;

		isDisconnect = true;
		return true;
	}

	method = QString::fromLatin1(m2method);
	path = m2headers.value("URI");

	QByteArray uploadStartRaw = m2headers.value("x-mongrel2-upload-start");
	QByteArray uploadDoneRaw = m2headers.value("x-mongrel2-upload-done");
	if(!uploadDoneRaw.isEmpty())
	{
		// these headers must match for the packet to be valid. not
		//   sure why mongrel2 can't enforce this for us but whatever
		if(uploadStartRaw != uploadDoneRaw)
			return false;

		uploadFile = QString::fromUtf8(uploadDoneRaw);
		uploadDone = true;
	}
	else if(!uploadStartRaw.isEmpty())
	{
		uploadFile = QString::fromUtf8(uploadStartRaw);
	}

	QSet<QString> skipHeaders;
	skipHeaders += "x-mongrel2-upload-start";
	skipHeaders += "x-mongrel2-upload-done";

	headers.clear();
	QMapIterator<QString, QByteArray> it(m2headers);
	while(it.hasNext())
	{
		it.next();

		QString key = it.key();
		if(isAllCaps(key) || skipHeaders.contains(key))
			continue;

		headers += HttpHeader(makeMixedCaseHeader(key).toLatin1(), it.value());
	}

	return true;
}
void
HttpHeaders::addHeader(const std::string& key, const std::string& value)
{
  m_headers.push_back(HttpHeader(key, value));
}
Ejemplo n.º 19
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;
	}
Ejemplo n.º 20
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);
		}
	}
Ejemplo n.º 21
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);
		}
	}
Ejemplo n.º 22
0
void HttpEntity::setHeader(const http::HeaderName& headerName, const QString& value, bool merge) {
	setHeader(HttpHeader(headerName, value), merge);
}