25 #include <QMetaMethod>
27 #include <QVarLengthArray>
29 static const char s_forbidden[] =
"HTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\n\r\n";
40 , m_serverObject(serverObject)
41 , m_delayedResponse(
false)
42 , m_socketEnabled(
true)
43 , m_receivedData(
false)
48 connect(
this, &QIODevice::readyRead,
this, &KDSoapServerSocket::slotReadyRead);
49 m_doDebug = qEnvironmentVariableIsSet(
"KDSOAP_DEBUG");
64 sourceBuffer.setData(headerData);
65 sourceBuffer.open(QIODevice::ReadOnly);
67 const QList<QByteArray> firstLine = sourceBuffer.readLine().split(
' ');
68 if (firstLine.count() < 3) {
69 qDebug() <<
"Malformed HTTP request:" << firstLine;
72 const QByteArray &requestType = firstLine.at(0);
73 headersMap.insert(
"_requestType", requestType);
79 const QByteArray arg1 = firstLine.at(1);
80 const int queryPos = arg1.indexOf(
'?');
81 const QByteArray path = queryPos >= 0 ? arg1.left(queryPos) : arg1;
82 const QByteArray query = queryPos >= 0 ? arg1.mid(queryPos) : QByteArray();
84 const QByteArray cleanedPath = QDir::cleanPath(QString::fromUtf8(path)).toUtf8();
85 headersMap.insert(
"_path", cleanedPath + query);
87 const QByteArray &httpVersion = firstLine.at(2);
88 headersMap.insert(
"_httpVersion", httpVersion);
90 while (!sourceBuffer.atEnd()) {
91 const QByteArray line = sourceBuffer.readLine();
92 const int pos = line.indexOf(
':');
94 qDebug() <<
"Malformed HTTP header:" << line;
96 const QByteArray header = line.left(pos).toLower();
97 const QByteArray value = line.mid(pos + 1).trimmed();
99 headersMap.insert(header, value);
108 const int sep = request.indexOf(
"\r\n\r\n");
112 header = request.left(sep);
113 data = request.mid(sep + 4);
119 if (bar.startsWith(
'\"') && bar.endsWith(
'\"')) {
120 return bar.mid(1, bar.length() - 2);
126 static QByteArray
httpResponseHeaders(
bool fault,
const QByteArray &contentType,
int responseDataSize, QObject *serverObject)
128 QByteArray httpResponse;
129 httpResponse.reserve(50);
132 httpResponse +=
"HTTP/1.1 500 Internal Server Error\r\n";
133 }
else if (responseDataSize == 0) {
134 httpResponse +=
"HTTP/1.1 204 No Content\r\n";
136 httpResponse +=
"HTTP/1.1 200 OK\r\n";
139 httpResponse +=
"Content-Type: ";
140 httpResponse += contentType;
141 httpResponse +=
"\r\nContent-Length: ";
142 httpResponse += QByteArray::number(responseDataSize);
143 httpResponse +=
"\r\n";
146 if (serverObjectInterface) {
149 httpResponse += headerItem.m_name;
150 httpResponse +=
": ";
151 httpResponse += headerItem.m_value;
152 httpResponse +=
"\r\n";
156 httpResponse +=
"\r\n";
160 void KDSoapServerSocket::slotReadyRead()
162 if (!m_socketEnabled) {
168 if (!m_receivedData) {
169 m_receivedData =
true;
175 QByteArray buf(2048,
' ');
178 nread = read(buf.data(), buf.size());
180 qDebug() <<
"Error reading from server socket:" << errorString();
183 m_requestBuffer += buf.left(nread);
184 m_bytesReceived += nread;
189 if (m_httpHeaders.isEmpty()) {
191 QByteArray receivedHttpHeaders, receivedData;
192 const bool splitOK =
splitHeadersAndData(m_requestBuffer, receivedHttpHeaders, receivedData);
200 m_requestBuffer = receivedData;
201 m_bytesReceived = receivedData.size();
203 if (rawXmlInterface) {
205 serverObjectInterface->setServerSocket(
this);
206 m_useRawXML = rawXmlInterface->
newRequest(m_httpHeaders.value(
"_requestType"), m_httpHeaders);
211 qDebug() <<
"headers:" << m_httpHeaders;
212 qDebug() <<
"data received:" << m_requestBuffer;
215 if (m_httpHeaders.value(
"transfer-encoding") !=
"chunked") {
218 m_requestBuffer.clear();
221 const QByteArray contentLength = m_httpHeaders.value(
"content-length");
222 if (m_bytesReceived < contentLength.toInt()) {
229 handleRequest(m_httpHeaders, m_requestBuffer);
233 while (m_chunkStart >= 0) {
234 const int nextEOL = m_requestBuffer.indexOf(
"\r\n", m_chunkStart);
238 const QByteArray chunkSizeStr = m_requestBuffer.mid(m_chunkStart, nextEOL - m_chunkStart);
241 int chunkSize = chunkSizeStr.toInt(&ok, 16);
243 const QByteArray badRequest =
"HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n";
247 if (chunkSize == 0) {
248 m_requestBuffer = m_requestBuffer.mid(nextEOL);
252 if (nextEOL + 2 + chunkSize + 2 >= m_requestBuffer.size()) {
255 const QByteArray chunk = m_requestBuffer.mid(nextEOL + 2, chunkSize);
259 m_decodedRequestBuffer += chunk;
261 m_chunkStart = nextEOL + 2 + chunkSize + 2;
264 if (!m_requestBuffer.contains(
"\r\n\r\n")) {
270 handleRequest(m_httpHeaders, m_decodedRequestBuffer);
272 m_decodedRequestBuffer.clear();
275 m_requestBuffer.clear();
276 m_httpHeaders.clear();
277 m_receivedData =
false;
280 void KDSoapServerSocket::handleRequest(
const QMap<QByteArray, QByteArray> &httpHeaders,
const QByteArray &receivedData)
282 const QByteArray requestType = httpHeaders.value(
"_requestType");
283 const QString path = QString::fromLatin1(httpHeaders.value(
"_path").constData());
285 if (!path.startsWith(QLatin1String(
"/"))) {
292 if (serverAuthInterface) {
293 const QByteArray authValue = httpHeaders.value(
"authorization");
294 if (!serverAuthInterface->handleHttpAuth(authValue, path)) {
296 const QByteArray unauthorized =
297 "HTTP/1.1 401 Authorization Required\r\nWWW-Authenticate: Basic realm=\"example\"\r\nContent-Length: 0\r\n\r\n";
303 if (requestType !=
"GET" && requestType !=
"POST") {
305 QByteArray customVerbRequestAnswer;
306 if (serverCustomRequest && serverCustomRequest->
processCustomVerbRequest(requestType, receivedData, httpHeaders, customVerbRequestAnswer)) {
307 write(customVerbRequestAnswer);
310 qWarning() <<
"Unknown HTTP request:" << requestType;
313 const QByteArray methodNotAllowed =
"HTTP/1.1 405 Method Not Allowed\r\nAllow: GET POST\r\nContent-Length: 0\r\n\r\n";
314 write(methodNotAllowed);
324 if (!serverObjectInterface) {
325 const QString error = QString::fromLatin1(
"Server object %1 does not implement KDSoapServerObjectInterface!")
326 .arg(QString::fromLatin1(m_serverObject->metaObject()->className()));
327 handleError(replyMsg,
"Server.ImplementationError", error);
331 serverObjectInterface->setServerSocket(
this);
334 if (requestType ==
"GET") {
335 if (path == server->
wsdlPathInUrl() && handleWsdlDownload()) {
337 }
else if (handleFileDownload(serverObjectInterface, path)) {
344 handleError(replyMsg,
"Client.Data", QString::fromLatin1(
"Support for GET requests not implemented yet."));
361 QByteArray soapAction;
362 const QByteArray contentType = httpHeaders.value(
"content-type");
363 if (contentType.startsWith(
"text/xml")) {
365 soapAction = httpHeaders.value(
"soapaction");
369 }
else if (contentType.startsWith(
"application/soap+xml")) {
372 const QList<QByteArray> parts = contentType.split(
';');
373 for (
const QByteArray &part : qAsConst(parts)) {
374 if (part.trimmed().startsWith(
"action=")) {
375 soapAction =
stripQuotes(part.mid(part.indexOf(
'=') + 1));
380 m_method = requestMsg.
name();
383 makeCall(serverObjectInterface, requestMsg, replyMsg, requestHeaders, soapAction, path);
386 if (serverObjectInterface && m_delayedResponse) {
388 setSocketEnabled(
false);
390 sendReply(serverObjectInterface, replyMsg);
394 bool KDSoapServerSocket::handleWsdlDownload()
397 const QString wsdlFile = server->
wsdlFile();
399 if (wf.open(QIODevice::ReadOnly)) {
401 const QByteArray responseText = wf.readAll();
402 const QByteArray response =
httpResponseHeaders(
false,
"application/xml", responseText.size(), m_serverObject);
412 QByteArray contentType;
415 const QByteArray notFound =
"HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n";
419 if (!device->open(QIODevice::ReadOnly)) {
424 const QByteArray response =
httpResponseHeaders(
false, contentType, device->size(), m_serverObject);
426 qDebug() <<
"KDSoapServerSocket: file download response" << response;
428 qint64 written = write(response);
429 Q_ASSERT(written == response.size());
432 char block[4096] = {0};
434 while (!device->atEnd()) {
435 const qint64 in = device->read(block,
sizeof(block));
440 if (in != write(block, in)) {
455 void KDSoapServerSocket::writeXML(
const QByteArray &xmlResponse,
bool isFault)
457 const QByteArray httpHeaders =
httpResponseHeaders(isFault,
"text/xml", xmlResponse.size(),
460 qDebug() <<
"KDSoapServerSocket: writing" << httpHeaders << xmlResponse;
462 qint64 written = write(httpHeaders);
463 if (written != httpHeaders.size()) {
464 qWarning() <<
"Only wrote" << written <<
"out of" << httpHeaders.size() <<
"bytes of HTTP headers. Error:" << errorString();
466 written = write(xmlResponse);
467 if (written != xmlResponse.size()) {
468 qWarning() <<
"Only wrote" << written <<
"out of" << xmlResponse.size() <<
"bytes of response. Error:" << errorString();
474 const bool isFault = replyMsg.
isFault();
476 QByteArray xmlResponse;
477 if (!replyMsg.
isNil()) {
481 QString responseName = isFault ? QString::fromLatin1(
"Fault") : replyMsg.
name();
482 if (responseName.isEmpty()) {
483 responseName = m_method;
485 QString responseNamespace = m_messageNamespace;
487 if (serverObjectInterface) {
488 responseHeaders = serverObjectInterface->responseHeaders();
489 if (!serverObjectInterface->responseNamespace().isEmpty()) {
490 responseNamespace = serverObjectInterface->responseNamespace();
494 xmlResponse = msgWriter.
messageToXml(replyMsg, responseName, responseHeaders, QMap<QString, KDSoapMessage>());
497 writeXML(xmlResponse, isFault);
507 server->log(
"FAULT " + m_method.toLatin1() +
" -- " + replyMsg.
faultAsString().toUtf8() +
'\n');
509 server->log(
"CALL " + m_method.toLatin1() +
'\n');
517 sendReply(serverObjectInterface, replyMsg);
518 m_delayedResponse =
false;
519 setSocketEnabled(
true);
524 m_delayedResponse =
true;
527 void KDSoapServerSocket::handleError(
KDSoapMessage &replyMsg,
const char *errorCode,
const QString &error)
529 qWarning(
"%s", qPrintable(error));
535 const KDSoapHeaders &requestHeaders,
const QByteArray &soapAction,
const QString &path)
537 Q_ASSERT(serverObjectInterface);
543 replyMsg = requestMsg;
544 handleError(replyMsg,
"Client.Data", QString::fromLatin1(
"Request was a fault"));
548 serverObjectInterface->setRequestHeaders(requestHeaders, soapAction);
551 if (path != server->
path()) {
554 serverObjectInterface->
processRequest(requestMsg, replyMsg, soapAction);
556 if (serverObjectInterface->
hasFault()) {
559 serverObjectInterface->storeFaultAttributes(replyMsg);
566 void KDSoapServerSocket::setSocketEnabled(
bool enabled)
568 if (m_socketEnabled == enabled) {
572 m_socketEnabled = enabled;
578 #include "moc_KDSoapServerSocket_p.cpp"
static const char s_forbidden[]
static QByteArray httpResponseHeaders(bool fault, const QByteArray &contentType, int responseDataSize, QObject *serverObject)
static HeadersMap parseHeaders(const QByteArray &headerData)
QMap< QByteArray, QByteArray > HeadersMap
static QByteArray stripQuotes(const QByteArray &bar)
static bool splitHeadersAndData(const QByteArray &request, QByteArray &header, QByteArray &data)
XmlError xmlToMessage(const QByteArray &data, KDSoapMessage *pParsedMessage, QString *pMessageNamespace, KDSoapHeaders *pRequestHeaders, KDSoap::SoapVersion soapVersion) const
@ PrematureEndOfDocumentError
QByteArray messageToXml(const KDSoapMessage &message, const QString &method, const KDSoapHeaders &headers, const QMap< QString, KDSoapMessage > &persistentHeaders, const KDSoapAuthentication &authentication=KDSoapAuthentication()) const
void setMessageNamespace(const QString &ns)
void createFaultMessage(const QString &faultCode, const QString &faultText, KDSoap::SoapVersion soapVersion)
void setFault(bool fault)
QString faultAsString() const
virtual bool processCustomVerbRequest(const QByteArray &requestType, const QByteArray &requestData, const QMap< QByteArray, QByteArray > &httpHeaders, QByteArray &customAnswer)
virtual HttpResponseHeaderItems additionalHttpResponseHeaderItems() const
QVector< HttpResponseHeaderItem > HttpResponseHeaderItems
virtual void processRequestWithPath(const KDSoapMessage &request, KDSoapMessage &response, const QByteArray &soapAction, const QString &path)
virtual void processRequest(const KDSoapMessage &request, KDSoapMessage &response, const QByteArray &soapAction)
virtual QIODevice * processFileRequest(const QString &path, QByteArray &contentType)
virtual void endRequest()
virtual void processXML(const QByteArray &xmlChunk)
virtual bool newRequest(const QByteArray &requestType, const QMap< QByteArray, QByteArray > &httpHeaders)
void sendDelayedReply(KDSoapServerObjectInterface *serverObjectInterface, const KDSoapMessage &replyMsg)
void socketDeleted(KDSoapServerSocket *)
KDSoapServerSocket(KDSoapSocketList *owner, QObject *serverObject)
void sendReply(KDSoapServerObjectInterface *serverObjectInterface, const KDSoapMessage &replyMsg)
void setResponseDelayed()
KDSoapMessage::Use use() const
QString wsdlPathInUrl() const
LogLevel logLevel() const
KDSoapServer * server() const
void increaseConnectionCount()