void KDSoapServerSocket::slotReadyRead() { if (!m_socketEnabled) return; //qDebug() << this << QThread::currentThread() << "slotReadyRead!"; QByteArray buf(2048, ' '); qint64 nread = -1; while (nread != 0) { nread = read(buf.data(), buf.size()); if (nread < 0) { qDebug() << "Error reading from server socket:" << errorString(); return; } m_requestBuffer += buf.left(nread); } //qDebug() << "KDSoapServerSocket: request:" << m_requestBuffer; QByteArray receivedHttpHeaders, receivedData; const bool splitOK = splitHeadersAndData(m_requestBuffer, receivedHttpHeaders, receivedData); if (!splitOK) { //qDebug() << "Incomplete SOAP request, wait for more data"; //incomplete request, wait for more data return; } QMap<QByteArray, QByteArray> httpHeaders; httpHeaders = parseHeaders(receivedHttpHeaders); if (m_doDebug) { qDebug() << "headers received:" << receivedHttpHeaders; qDebug() << httpHeaders; qDebug() << "data received:" << receivedData; } const QByteArray contentLength = httpHeaders.value("content-length"); if (receivedData.size() < contentLength.toInt()) return; // incomplete request, wait for more data m_requestBuffer.clear(); const QByteArray requestType = httpHeaders.value("_requestType"); if (requestType != "GET" && requestType != "POST") { qWarning() << "Unknown HTTP request:" << requestType; //handleError(replyMsg, "Client.Data", QString::fromLatin1("Invalid request type '%1', should be GET or POST").arg(QString::fromLatin1(requestType.constData()))); //sendReply(0, replyMsg); const QByteArray methodNotAllowed = "HTTP/1.1 405 Method Not Allowed\r\nAllow: GET POST\r\nContent-Length: 0\r\n\r\n"; write(methodNotAllowed); return; } const QString path = QString::fromLatin1(httpHeaders.value("_path").constData()); KDSoapServerAuthInterface* serverAuthInterface = qobject_cast<KDSoapServerAuthInterface *>(m_serverObject); if (serverAuthInterface) { QByteArray authValue = httpHeaders.value("Authorization"); if (authValue.isEmpty()) authValue = httpHeaders.value("authorization"); // as sent by Qt-4.5 if (!serverAuthInterface->handleHttpAuth(authValue, path)) { // send auth request (Qt supports basic, ntlm and digest) const QByteArray unauthorized = "HTTP/1.1 401 Authorization Required\r\nWWW-Authenticate: Basic realm=\"example\"\r\nContent-Length: 0\r\n\r\n"; write(unauthorized); return; } } KDSoapServer* server = m_owner->server(); KDSoapMessage replyMsg; replyMsg.setUse(server->use()); KDSoapServerObjectInterface* serverObjectInterface = qobject_cast<KDSoapServerObjectInterface *>(m_serverObject); if (!serverObjectInterface) { const QString error = QString::fromLatin1("Server object %1 does not implement KDSoapServerObjectInterface!").arg(QString::fromLatin1(m_serverObject->metaObject()->className())); handleError(replyMsg, "Server.ImplementationError", error); sendReply(0, replyMsg); return; } else { serverObjectInterface->setServerSocket(this); } if (requestType == "GET") { if (path == server->wsdlPathInUrl() && handleWsdlDownload()) { return; } else if (handleFileDownload(serverObjectInterface, path)) { return; } // See http://www.ibm.com/developerworks/xml/library/x-tipgetr/ // We could implement it, but there's no SOAP request, just a query in the URL, // which we'd have to pass to a different virtual than processRequest. handleError(replyMsg, "Client.Data", QString::fromLatin1("Support for GET requests not implemented yet.")); sendReply(0, replyMsg); return; } //parse message KDSoapMessage requestMsg; KDSoapHeaders requestHeaders; KDSoapMessageReader reader; KDSoapMessageReader::XmlError err = reader.xmlToMessage(receivedData, &requestMsg, &m_messageNamespace, &requestHeaders); if (err == KDSoapMessageReader::PrematureEndOfDocumentError) { //qDebug() << "Incomplete SOAP message, wait for more data"; // This should never happen, since we check for content-size above. return; } //TODO handle parse errors? // check soap version and extract soapAction header QByteArray soapAction; const QByteArray contentType = httpHeaders.value("content-type"); if (contentType.startsWith("text/xml")) { // SOAP 1.1 soapAction = httpHeaders.value("soapaction"); // The SOAP standard allows quotation marks around the SoapAction, so we have to get rid of these. if (soapAction.startsWith('\"')) soapAction = soapAction.mid(1, soapAction.length() - 2); } else if (contentType.startsWith("application/soap+xml")) { // SOAP 1.2 // Example: application/soap+xml;charset=utf-8;action=ActionHex const QList<QByteArray> parts = contentType.split(';'); Q_FOREACH(const QByteArray& part, parts) { if (part.startsWith("action=")) { soapAction = part.mid(strlen("action=")); } } } m_method = requestMsg.name(); if (!replyMsg.isFault()) { makeCall(serverObjectInterface, requestMsg, replyMsg, requestHeaders, soapAction, path); } if (serverObjectInterface && m_delayedResponse) { // Delayed response. Disable the socket to make sure we don't handle another call at the same time. setSocketEnabled(false); } else { sendReply(serverObjectInterface, replyMsg); } }
void shouldWriteAProperSoapMessageWithRightsAddressingProperties() { // GIVEN HttpServerThread server(emptyResponse(), HttpServerThread::Public); KDSoapClientInterface client(server.endPoint(), "http://www.ecerami.com/wsdl/HelloService"); KDSoapMessage message; KDSoapMessageAddressingProperties map; // with some message addressing properties map.setAction("sayHello"); map.setDestination("http://www.ecerami.com/wsdl/HelloService"); map.setSourceEndpointAddress("http://www.ecerami.com/wsdl/source"); map.setFaultEndpoint(KDSoapEndpointReference("http://www.ecerami.com/wsdl/fault")); map.setMessageID("uuid:e197db59-0982-4c9c-9702-4234d204f7f4"); map.setReplyEndpointAddress(KDSoapMessageAddressingProperties::predefinedAddressToString(KDSoapMessageAddressingProperties::Anonymous)); // two relationships related to previous message KDSoapMessageRelationship::Relationship relationship("uuid:http://www.ecerami.com/wsdl/someUniqueString"); // no type means implicit Reply KDSoapMessageRelationship::Relationship relationshipBis("uuid:http://www.ecerami.com/wsdl/someUniqueStringBis", "CustomTypeReply"); map.addRelationship(relationship); map.addRelationship(relationshipBis); // some reference parameters... // one with a value KDSoapValue refParam("myReferenceParameter", "ReferencParameterContent"); map.addReferenceParameter(refParam); // an other one, with children KDSoapValue childOne("myReferenceParameterChildOne", "ChildOneContent"); KDSoapValue childTwo("myReferenceParameterChildTwo", "ChildTwoContent"); KDSoapValueList childrenList; childrenList << childOne << childTwo; KDSoapValue refParamWithChildren("myReferenceParameterWithChildren", childrenList); map.addReferenceParameter(refParamWithChildren); // some metadata KDSoapValueList metadataContainer; KDSoapValue metadata("myMetadata", "MetadataContent"); metadataContainer << metadata; KDSoapValue child("myMetadataBisChild", "MetadataBisChildContent"); KDSoapValueList childList; childList << child; KDSoapValue metadataBis("myMetadataBis", childList); map.setMetadata(metadataContainer); map.addMetadata(metadataBis); // and some request content const QString action = QString::fromLatin1("sayHello"); message.setUse(KDSoapMessage::EncodedUse); message.addArgument(QString::fromLatin1("msg"), QVariant::fromValue(QString("HelloContentMessage")), KDSoapNamespaceManager::xmlSchema2001(), QString::fromLatin1("string")); message.setNamespaceUri(QString::fromLatin1("http://www.ecerami.com/wsdl/HelloService.wsdl")); // WHEN message.setMessageAddressingProperties(map); KDSoapMessage reply = client.call(QLatin1String("sayHello"), message, action); // THEN QVERIFY(xmlBufferCompare(server.receivedData(), expectedSoapMessage())); }
void testRequestXml() // this tests the serialization of KDSoapValue[List] in KDSoapClientInterface { HttpServerThread server(emptyResponse(), HttpServerThread::Public); KDSoapClientInterface client(server.endPoint(), countryMessageNamespace()); KDSoapMessage message; message.setUse(KDSoapMessage::EncodedUse); // write out types explicitely // Test simpletype element message.addArgument(QString::fromLatin1("testString"), QString::fromUtf8("Hello Klarälvdalens")); // Test simpletype extended to have an attribute KDSoapValue val(QString::fromLatin1("val"), 5, countryMessageNamespace(), QString::fromLatin1("MyVal")); val.childValues().attributes().append(KDSoapValue(QString::fromLatin1("attr"), QString::fromLatin1("attrValue"))); message.arguments().append(val); // Test complextype with child elements and attributes KDSoapValueList valueList; KDSoapValue orderperson(QString::fromLatin1("orderperson"), QString::fromLatin1("someone"), countryMessageNamespace(), QString::fromLatin1("Person")); valueList.append(orderperson); valueList.attributes().append(KDSoapValue(QString::fromLatin1("attr"), QString::fromLatin1("attrValue"))); message.addArgument(QString::fromLatin1("order"), valueList, countryMessageNamespace(), QString::fromLatin1("MyOrder")); // Code from documentation QRect rect(0, 0, 100, 200); KDSoapValueList rectArgument; rectArgument.addArgument(QLatin1String("x"), rect.x()); rectArgument.addArgument(QLatin1String("y"), rect.y()); rectArgument.addArgument(QLatin1String("width"), rect.width()); rectArgument.addArgument(QLatin1String("height"), rect.height()); message.addArgument(QLatin1String("rect"), rectArgument); // NOTE: type information is missing; setQualified() is missing too. const QString XMLSchemaNS = KDSoapNamespaceManager::xmlSchema2001(); // Test array KDSoapValueList arrayContents; arrayContents.setArrayType(XMLSchemaNS, QString::fromLatin1("string")); arrayContents.append(KDSoapValue(QString::fromLatin1("item"), QString::fromLatin1("kdab"), XMLSchemaNS, QString::fromLatin1("string"))); arrayContents.append(KDSoapValue(QString::fromLatin1("item"), QString::fromLatin1("rocks"), XMLSchemaNS, QString::fromLatin1("string"))); message.addArgument(QString::fromLatin1("testArray"), arrayContents, QString::fromLatin1("http://schemas.xmlsoap.org/soap/encoding/"), QString::fromLatin1("Array")); // Add a header KDSoapMessage header1; header1.setUse(KDSoapMessage::EncodedUse); header1.addArgument(QString::fromLatin1("header1"), QString::fromLatin1("headerValue")); KDSoapHeaders headers; headers << header1; client.call(QLatin1String("test"), message, QString::fromLatin1("MySoapAction"), headers); // Check what we sent QByteArray expectedRequestXml = QByteArray(xmlEnvBegin11()) + " xmlns:n1=\"http://www.kdab.com/xml/MyWsdl/\"" "><soap:Header>" "<n1:header1 xsi:type=\"xsd:string\">headerValue</n1:header1>" "</soap:Header>"; const QByteArray expectedRequestBody = QByteArray("<soap:Body>" "<n1:test>" "<testString xsi:type=\"xsd:string\">Hello Klarälvdalens</testString>" "<val xsi:type=\"n1:MyVal\" attr=\"attrValue\">5</val>" "<order xsi:type=\"n1:MyOrder\" attr=\"attrValue\"><orderperson xsi:type=\"n1:Person\">someone</orderperson></order>" "<rect><x xsi:type=\"xsd:int\">0</x><y xsi:type=\"xsd:int\">0</y><width xsi:type=\"xsd:int\">100</width><height xsi:type=\"xsd:int\">200</height></rect>" "<testArray xsi:type=\"soap-enc:Array\" soap-enc:arrayType=\"xsd:string[2]\">" "<item xsi:type=\"xsd:string\">kdab</item>" "<item xsi:type=\"xsd:string\">rocks</item>" "</testArray>" "</n1:test>" "</soap:Body>") + xmlEnvEnd() + '\n'; // added by QXmlStreamWriter::writeEndDocument QVERIFY(xmlBufferCompare(server.receivedData(), expectedRequestXml + expectedRequestBody)); // Now using persistent headers server.resetReceivedBuffers(); client.setHeader(QLatin1String("header1"), header1); client.call(QLatin1String("test"), message, QString::fromLatin1("MySoapAction")); QVERIFY(xmlBufferCompare(server.receivedData(), expectedRequestXml + expectedRequestBody)); // Now remove the persistent header (using setHeader + empty message) server.resetReceivedBuffers(); client.setHeader(QLatin1String("header1"), KDSoapMessage()); client.call(QLatin1String("test"), message, QString::fromLatin1("MySoapAction")); const QByteArray expectedRequestXmlNoHeader = QByteArray(xmlEnvBegin11()) + " xmlns:n1=\"http://www.kdab.com/xml/MyWsdl/\"" "><soap:Header/>"; // the empty element does not matter QVERIFY(xmlBufferCompare(server.receivedData(), expectedRequestXmlNoHeader + expectedRequestBody)); }