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); }
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); }
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); }
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"); }
void respond(int code, const QByteArray &reason, const QString &body) { HttpHeaders headers; headers += HttpHeader("Content-Type", "text/plain"); respond(code, reason, headers, body.toUtf8()); }
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; }
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); }
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; }
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); }
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; }
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; }
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(); }
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"); } }
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'); }
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'); }
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()); }
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)); }
// 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; }
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); } }
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); } }
void HttpEntity::setHeader(const http::HeaderName& headerName, const QString& value, bool merge) { setHeader(HttpHeader(headerName, value), merge); }