3 Commits

Author SHA1 Message Date
ecffdc18fd Minor bugfixes of SoapFault handling
SoapFault is now properly prefixed so that SoapFaultSourceGetter identifies them correctly, missing exceptions option is now processed in SoapServer
2017-05-10 09:15:27 +02:00
b45202f40a Project composer description update 2017-04-26 13:00:40 +02:00
ab83642f06 Parser fix: single line MimeMessages are now parsed correctly 2017-04-06 15:51:29 +02:00
11 changed files with 189 additions and 24 deletions

View File

@ -1,7 +1,7 @@
{
"name": "tuscanicz/soap",
"type": "library",
"description": "A fork of the abandoned besimple/soap package with added support for Symfony 3.0",
"description": "A largely refactored besimple/soap used to build SOAP and WSDL based web services. This fork fixes a lot of errors and provides better API, robust, stable and modern codebase.",
"keywords": ["soap", "soap server", "soap client"],
"license": "MIT",
"authors": [

View File

@ -318,14 +318,14 @@ class SoapClient extends \SoapClient
} else if ($curlResponse->curlStatusFailed()) {
return $this->throwSoapFaultByTracing(
SoapFaultPrefixEnum::PREFIX_DEFAULT.'-'.SoapFaultEnum::SOAP_FAULT_HTTP.'-'.$curlResponse->getHttpResponseStatusCode(),
SoapFaultEnum::SOAP_FAULT_HTTP.'-'.$curlResponse->getHttpResponseStatusCode(),
$curlResponse->getCurlErrorMessage(),
$soapResponseTracingData
);
} else {
return $this->throwSoapFaultByTracing(
SoapFaultPrefixEnum::PREFIX_DEFAULT.'-'.SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR,
SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR,
'Cannot process curl response with unresolved status: ' . $curlResponse->getCurlStatus(),
$soapResponseTracingData
);

View File

@ -4,7 +4,7 @@ namespace BeSimple\SoapCommon\Fault;
class SoapFaultEnum
{
const SOAP_FAULT_WSDL = 'wsdl';
const SOAP_FAULT_HTTP = 'http';
const SOAP_FAULT_SOAP_CLIENT_ERROR = 'soap-client-error';
const SOAP_FAULT_WSDL = SoapFaultPrefixEnum::PREFIX_DEFAULT.'-'.'wsdl';
const SOAP_FAULT_HTTP = SoapFaultPrefixEnum::PREFIX_DEFAULT.'-'.'http';
const SOAP_FAULT_SOAP_CLIENT_ERROR = SoapFaultPrefixEnum::PREFIX_DEFAULT.'-'.'soap-client-error';
}

View File

@ -13,13 +13,13 @@ class SoapFaultSourceGetter
public static function isBeSimpleSoapFault(SoapFault $soapFault)
{
$defaultPrefix = SoapFaultPrefixEnum::PREFIX_DEFAULT;
$nativeSoapFaultPrefix = SoapFaultPrefixEnum::PREFIX_DEFAULT.'-';
if (strpos($soapFault->getCode(), $defaultPrefix) === 0) {
if (strpos($soapFault->faultcode, $nativeSoapFaultPrefix) === 0) {
return false;
return true;
}
return true;
return false;
}
}

View File

@ -21,6 +21,7 @@ class MimeBoundaryAnalyser
}
/**
* @todo: This method is not reliable at all
* @param string $mimeMessageLine
* @return bool
*/

View File

@ -29,25 +29,16 @@ class Parser
/**
* Parse the given Mime-Message and return a \BeSimple\SoapCommon\Mime\MultiPart object.
*
* @param string $mimeMessage Mime message string
* @param string[] $headers array(string=>string) of header elements (e.g. coming from http request)
*
* @return \BeSimple\SoapCommon\Mime\MultiPart
* @param string $mimeMessage Mime message string
* @param string[] $headers array(string=>string) of header elements (e.g. coming from http request)
* @return MultiPart
*/
public static function parseMimeMessage($mimeMessage, array $headers = [])
{
$multiPart = new MultiPart();
$mimeMessageLines = explode("\n", $mimeMessage);
$mimeMessageLineCount = count($mimeMessageLines);
if ($mimeMessageLineCount <= 1) {
throw new Exception(
sprintf(
'Cannot process message of %d characters: got unexpectable low number of lines: %s',
mb_strlen($mimeMessage),
(string)$mimeMessageLineCount
)
);
}
// add given headers, e.g. coming from HTTP headers
if (count($headers) > 0) {
self::setMultiPartHeaders($multiPart, $headers);
@ -56,6 +47,15 @@ class Parser
$hasHttpRequestHeaders = ParsedPartsGetter::HAS_NO_HTTP_REQUEST_HEADERS;
}
if (MimeBoundaryAnalyser::hasMessageBoundary($mimeMessageLines) === true) {
if ($mimeMessageLineCount <= 1) {
throw new Exception(
sprintf(
'Cannot parse MultiPart message of %d characters: got unexpectable low number of lines: %s',
mb_strlen($mimeMessage),
(string)$mimeMessageLineCount
)
);
}
$parsedPartList = ParsedPartsGetter::getPartsFromMimeMessageLines(
$multiPart,
$mimeMessageLines,

View File

@ -121,6 +121,7 @@ class SoapServerOptions
{
$optionsAsArray = [
'keep_alive' => $this->isKeepAlive(),
'exceptions' => $this->isExceptions(),
];
return $optionsAsArray;

View File

@ -0,0 +1,64 @@
<?php
namespace BeSimple\SoapCommon\Fault;
use PHPUnit_Framework_TestCase;
use SoapFault;
class SoapFaultSourceGetterTest extends PHPUnit_Framework_TestCase
{
const FIXTURES_DIR = __DIR__ . '/../../Fixtures';
/**
* @param SoapFault $soapFault
* @dataProvider provideNativeSoapFaults
*/
public function testWithNativeSoapFault(SoapFault $soapFault)
{
self::assertTrue(SoapFaultSourceGetter::isNativeSoapFault($soapFault));
self::assertFalse(SoapFaultSourceGetter::isBeSimpleSoapFault($soapFault));
}
/**
* @param SoapFault $soapFault
* @dataProvider provideBeSimpleSoapFaults
*/
public function testWithBeSimpleSoapFault(SoapFault $soapFault)
{
self::assertFalse(SoapFaultSourceGetter::isNativeSoapFault($soapFault));
self::assertTrue(SoapFaultSourceGetter::isBeSimpleSoapFault($soapFault));
}
public function provideNativeSoapFaults()
{
return [
[$this->getNativeSoapFaultFromClient()],
// @todo: add more test cases for Soap Server \SoapFault
];
}
public function provideBeSimpleSoapFaults()
{
return [
[new SoapFault(SoapFaultEnum::SOAP_FAULT_HTTP, 'HTTP Connection error')],
[new SoapFault(SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR, 'SOAP Client error')],
[new SoapFault(SoapFaultEnum::SOAP_FAULT_WSDL, 'WSDL error')],
];
}
/**
* @return SoapFault
*/
private function getNativeSoapFaultFromClient()
{
try {
$soapClient = @new \SoapClient('non-existing-wsdl-throwing-soapfault');
$soapClient->__call('no-function', []);
} catch (SoapFault $e) {
return $e;
}
self::fail('Cannot generate native PHP SoapFault from Client, please review the test');
}
}

View File

@ -0,0 +1,99 @@
<?php
namespace BeSimple\SoapCommon\Mime;
use Exception;
use PHPUnit_Framework_TestCase;
class ParserTest extends PHPUnit_Framework_TestCase
{
const TEST_CASE_SHOULD_FAIL = true;
const TEST_CASE_SHOULD_NOT_FAIL = false;
/**
* @dataProvider provideMimeMessages
* @param string $mimeMessage
* @param string[] $headers
* @param bool $testCaseShouldFail
* @param string|null $failedTestCaseFailMessage
*/
public function testParseMimeMessage(
$mimeMessage,
array $headers,
$testCaseShouldFail,
$failedTestCaseFailMessage = null
) {
if ($testCaseShouldFail === true) {
$this->setExpectedException(Exception::class, $failedTestCaseFailMessage);
}
$mimeMessage = Parser::parseMimeMessage($mimeMessage, $headers);
if ($testCaseShouldFail === false) {
self::assertInstanceOf(MultiPart::class, $mimeMessage);
self::assertInstanceOf(Part::class, $mimeMessage->getMainPart());
}
}
public function provideMimeMessages()
{
return [
'ParseRequest' => [
$this->getMimeMessageFromFile(__DIR__.'/../../../Fixtures/Message/Request/dummyServiceMethod.message.request'),
[],
self::TEST_CASE_SHOULD_NOT_FAIL
],
'ParseRequestOneLiner' => [
$this->getMimeMessageFromFile(__DIR__.'/../../../Fixtures/Message/Request/dummyServiceMethod.oneliner.message.request'),
[],
self::TEST_CASE_SHOULD_NOT_FAIL
],
'ParseSwaResponseWith2FilesAnd1BinaryFile' => [
$this->getMimeMessageFromFile(__DIR__.'/../../../Fixtures/Message/Response/dummyServiceMethodWithOutgoingLargeSwa.response.mimepart.message'),
[
'Content-Type' => 'multipart/related;'.
' type="application/soap+xml"; charset=utf-8;'.
' boundary=Part_13_58e3bc35f3743.58e3bc35f376f;'.
' start="<part-424dbe68-e2da-450f-9a82-cc3e82742503@response.info>"'
],
self::TEST_CASE_SHOULD_NOT_FAIL
],
'ParseSwaResponseWith2FilesAnd1BinaryFileShouldFailWithNoHeaders' => [
$this->getMimeMessageFromFile(__DIR__.'/../../../Fixtures/Message/Response/dummyServiceMethodWithOutgoingLargeSwa.response.mimepart.message'),
[],
self::TEST_CASE_SHOULD_FAIL,
'Unable to get Content-Type boundary'
],
'ParseSwaResponseWith2FilesAnd1BinaryFileShouldFailWithWrongHeaders' => [
$this->getMimeMessageFromFile(__DIR__.'/../../../Fixtures/Message/Response/dummyServiceMethodWithOutgoingLargeSwa.response.mimepart.message'),
[
'Content-Type' => 'multipart/related; type="application/soap+xml"; charset=utf-8; boundary=DOES_NOT_EXIST; start="<non-existing>"'
],
self::TEST_CASE_SHOULD_FAIL,
'cannot parse headers before hitting the first boundary'
],
'ParseSwaRequestWith2Files' => [
$this->getMimeMessageFromFile(__DIR__ . '/../../../Fixtures/Message/Request/dummyServiceMethodWithAttachments.request.mimepart.message'),
[
'Content-Type' => 'multipart/related; type="application/soap+xml"; charset=utf-8; boundary=----=_Part_6_2094841787.1482231370463; start="<rootpart@soapui.org>"'
],
self::TEST_CASE_SHOULD_NOT_FAIL
],
'ParseSwaRequestWith2FilesShouldFailWithNoHeaders' => [
$this->getMimeMessageFromFile(__DIR__ . '/../../../Fixtures/Message/Request/dummyServiceMethodWithAttachments.request.mimepart.message'),
[],
self::TEST_CASE_SHOULD_FAIL,
'Unable to get Content-Type boundary'
],
];
}
private function getMimeMessageFromFile($filePath)
{
if (file_exists($filePath) === false) {
self::fail('Please, update tests data provider - file not found: '.$filePath);
}
return file_get_contents($filePath);
}
}

View File

@ -24,7 +24,6 @@ class SoapServerAndSoapClientCommunicationTest extends PHPUnit_Framework_TestCas
const CACHE_DIR = __DIR__ . '/../../cache';
const FIXTURES_DIR = __DIR__ . '/../Fixtures';
const TEST_HTTP_URL = 'http://localhost:8000/tests';
const TEST_LOCAL_WSDL_UK = SoapClientBuilderTest::TEST_LOCAL_WSDL_UK;
const LARGE_SWA_FILE = self::FIXTURES_DIR.'/large-test-file.docx';
private $localWebServerProcess;

View File

@ -0,0 +1 @@
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://schema.testcase"><soapenv:Header><sch:SoapHeader><user>admin</user></sch:SoapHeader></soapenv:Header><soapenv:Body><sch:dummyServiceMethod><request><dummyAttribute>1</dummyAttribute></request></sch:dummyServiceMethod></soapenv:Body></soapenv:Envelope>