SoapServer/Client now handle binary files correctly & large tests/fixtures update
Soap Server and Client were breaking binary files during transfer due to invalid Mime Message Parser. Now is it working fine with no errors, but the message parser is about to be rewritten into a better form.
This commit is contained in:
parent
311f9e6d08
commit
564005da93
@ -37,12 +37,12 @@ class MimeFilter implements SoapRequestFilter, SoapResponseFilter
|
||||
$soapPart = new MimePart($request->getContent(), 'text/xml', 'utf-8', MimePart::ENCODING_EIGHT_BIT);
|
||||
$soapVersion = $request->getVersion();
|
||||
|
||||
if ($soapVersion == SOAP_1_1 && $attachmentType & Helper::ATTACHMENTS_TYPE_MTOM) {
|
||||
if ($soapVersion === SOAP_1_1 && $attachmentType & Helper::ATTACHMENTS_TYPE_MTOM) {
|
||||
$multipart->setHeader('Content-Type', 'type', 'application/xop+xml');
|
||||
$multipart->setHeader('Content-Type', 'start-info', 'text/xml');
|
||||
$soapPart->setHeader('Content-Type', 'application/xop+xml');
|
||||
$soapPart->setHeader('Content-Type', 'type', 'text/xml');
|
||||
} elseif ($soapVersion == SOAP_1_2) {
|
||||
} elseif ($soapVersion === SOAP_1_2) {
|
||||
$multipart->setHeader('Content-Type', 'type', 'application/soap+xml');
|
||||
$soapPart->setHeader('Content-Type', 'application/soap+xml');
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ use BeSimple\SoapClient\Curl\CurlOptionsBuilder;
|
||||
use BeSimple\SoapClient\Curl\CurlResponse;
|
||||
use BeSimple\SoapClient\SoapOptions\SoapClientOptions;
|
||||
use BeSimple\SoapCommon\Fault\SoapFaultEnum;
|
||||
use BeSimple\SoapCommon\Fault\SoapFaultPrefixEnum;
|
||||
use BeSimple\SoapCommon\Fault\SoapFaultSourceGetter;
|
||||
use BeSimple\SoapCommon\Mime\PartFactory;
|
||||
use BeSimple\SoapCommon\SoapKernel;
|
||||
use BeSimple\SoapCommon\SoapOptions\SoapOptions;
|
||||
@ -38,8 +40,11 @@ use SoapFault;
|
||||
*/
|
||||
class SoapClient extends \SoapClient
|
||||
{
|
||||
/** @var SoapClientOptions */
|
||||
protected $soapClientOptions;
|
||||
/** @var SoapOptions */
|
||||
protected $soapOptions;
|
||||
/** @var Curl */
|
||||
private $curl;
|
||||
/** @var SoapAttachment[] */
|
||||
private $soapAttachmentsOnRequestStorage;
|
||||
@ -110,12 +115,38 @@ class SoapClient extends \SoapClient
|
||||
public function soapCall($functionName, array $arguments, array $soapAttachments = [], array $options = null, $inputHeaders = null, array &$outputHeaders = null)
|
||||
{
|
||||
$this->setSoapAttachmentsOnRequestToStorage($soapAttachments);
|
||||
$soapResponseAsObject = parent::__soapCall($functionName, $arguments, $options, $inputHeaders, $outputHeaders);
|
||||
try {
|
||||
|
||||
$soapResponse = $this->getSoapResponseFromStorage();
|
||||
$soapResponse->setResponseObject($soapResponseAsObject);
|
||||
$soapResponseAsObject = parent::__soapCall($functionName, $arguments, $options, $inputHeaders, $outputHeaders);
|
||||
$soapResponse = $this->getSoapResponseFromStorage();
|
||||
$soapResponse->setResponseObject($soapResponseAsObject);
|
||||
|
||||
return $soapResponse;
|
||||
return $soapResponse;
|
||||
|
||||
} catch (SoapFault $soapFault) {
|
||||
if (SoapFaultSourceGetter::isNativeSoapFault($soapFault)) {
|
||||
$soapResponse = $this->getSoapResponseFromStorage();
|
||||
if ($soapResponse instanceof SoapResponse) {
|
||||
$soapFault = $this->throwSoapFaultByTracing(
|
||||
SoapFaultPrefixEnum::PREFIX_PHP . '-' . $soapFault->getCode(),
|
||||
$soapFault->getMessage(),
|
||||
new SoapResponseTracingData(
|
||||
'Content-Type: ' . $soapResponse->getRequest()->getContentType(),
|
||||
$soapResponse->getRequest()->getContent(),
|
||||
'Content-Type: ' . $soapResponse->getContentType(),
|
||||
$soapResponse->getResponseContent()
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$soapFault = new SoapFault(
|
||||
SoapFaultPrefixEnum::PREFIX_PHP . '-unresolved',
|
||||
'Got SoapFault message with no response: '.$soapFault->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
throw $soapFault;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -268,7 +299,6 @@ class SoapClient extends \SoapClient
|
||||
|
||||
if ($curlResponse->curlStatusSuccess()) {
|
||||
$soapResponse = $this->returnSoapResponseByTracing(
|
||||
$this->soapClientOptions->getTrace(),
|
||||
$soapRequest,
|
||||
$curlResponse,
|
||||
$soapResponseTracingData
|
||||
@ -288,16 +318,14 @@ class SoapClient extends \SoapClient
|
||||
} else if ($curlResponse->curlStatusFailed()) {
|
||||
|
||||
return $this->throwSoapFaultByTracing(
|
||||
$this->soapClientOptions->getTrace(),
|
||||
SoapFaultEnum::SOAP_FAULT_HTTP.'-'.$curlResponse->getHttpResponseStatusCode(),
|
||||
SoapFaultPrefixEnum::PREFIX_DEFAULT.'-'.SoapFaultEnum::SOAP_FAULT_HTTP.'-'.$curlResponse->getHttpResponseStatusCode(),
|
||||
$curlResponse->getCurlErrorMessage(),
|
||||
$soapResponseTracingData
|
||||
);
|
||||
} else {
|
||||
|
||||
return $this->throwSoapFaultByTracing(
|
||||
$this->soapClientOptions->getTrace(),
|
||||
SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR,
|
||||
SoapFaultPrefixEnum::PREFIX_DEFAULT.'-'.SoapFaultEnum::SOAP_FAULT_SOAP_CLIENT_ERROR,
|
||||
'Cannot process curl response with unresolved status: ' . $curlResponse->getCurlStatus(),
|
||||
$soapResponseTracingData
|
||||
);
|
||||
@ -341,19 +369,16 @@ class SoapClient extends \SoapClient
|
||||
}
|
||||
|
||||
private function returnSoapResponseByTracing(
|
||||
$isTracingEnabled,
|
||||
SoapRequest $soapRequest,
|
||||
CurlResponse $curlResponse,
|
||||
SoapResponseTracingData $soapResponseTracingData,
|
||||
array $soapAttachments = []
|
||||
) {
|
||||
if ($isTracingEnabled === true) {
|
||||
if ($this->soapClientOptions->getTrace() === true) {
|
||||
|
||||
return SoapResponseFactory::createWithTracingData(
|
||||
$soapRequest,
|
||||
$curlResponse->getResponseBody(),
|
||||
$soapRequest->getLocation(),
|
||||
$soapRequest->getAction(),
|
||||
$soapRequest->getVersion(),
|
||||
$curlResponse->getHttpResponseContentType(),
|
||||
$soapResponseTracingData,
|
||||
$soapAttachments
|
||||
@ -372,9 +397,15 @@ class SoapClient extends \SoapClient
|
||||
}
|
||||
}
|
||||
|
||||
private function throwSoapFaultByTracing($isTracingEnabled, $soapFaultCode, $soapFaultMessage, SoapResponseTracingData $soapResponseTracingData)
|
||||
/**
|
||||
* @param string $soapFaultCode
|
||||
* @param string $soapFaultMessage
|
||||
* @param SoapResponseTracingData $soapResponseTracingData
|
||||
* @throws SoapFault
|
||||
*/
|
||||
private function throwSoapFaultByTracing($soapFaultCode, $soapFaultMessage, SoapResponseTracingData $soapResponseTracingData)
|
||||
{
|
||||
if ($isTracingEnabled === true) {
|
||||
if ($this->soapClientOptions->getTrace() === true) {
|
||||
|
||||
throw new SoapFaultWithTracingData(
|
||||
$soapFaultCode,
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace BeSimple\SoapClient;
|
||||
|
||||
use BeSimple\SoapCommon\SoapRequest;
|
||||
use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse;
|
||||
|
||||
class SoapResponse extends CommonSoapResponse
|
||||
@ -10,6 +11,8 @@ class SoapResponse extends CommonSoapResponse
|
||||
protected $responseObject;
|
||||
/** @var SoapResponseTracingData */
|
||||
protected $tracingData;
|
||||
/** @var SoapRequest */
|
||||
protected $request;
|
||||
|
||||
public function getResponseContent()
|
||||
{
|
||||
@ -40,4 +43,14 @@ class SoapResponse extends CommonSoapResponse
|
||||
{
|
||||
$this->tracingData = $tracingData;
|
||||
}
|
||||
|
||||
public function setRequest(SoapRequest $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ namespace BeSimple\SoapClient;
|
||||
|
||||
use BeSimple\SoapBundle\Soap\SoapAttachment;
|
||||
use BeSimple\SoapCommon\Mime\PartFactory;
|
||||
use BeSimple\SoapCommon\SoapRequest;
|
||||
|
||||
/**
|
||||
* SoapResponseFactory for SoapClient. Provides factory function for SoapResponse object.
|
||||
@ -50,7 +51,7 @@ class SoapResponseFactory
|
||||
$response->setContentType($contentType);
|
||||
if (count($attachments) > 0) {
|
||||
$response->setAttachments(
|
||||
self::createAttachmentParts($attachments)
|
||||
PartFactory::createAttachmentParts($attachments)
|
||||
);
|
||||
}
|
||||
|
||||
@ -60,29 +61,26 @@ class SoapResponseFactory
|
||||
/**
|
||||
* Factory method for SoapClient\SoapResponse with SoapResponseTracingData.
|
||||
*
|
||||
* @param SoapRequest $soapRequest related request object
|
||||
* @param string $content Content
|
||||
* @param string $location Location
|
||||
* @param string $action SOAP action
|
||||
* @param string $version SOAP version
|
||||
* @param string $contentType Content type header
|
||||
* @param SoapResponseTracingData $tracingData Data value object suitable for tracing SOAP traffic
|
||||
* @param SoapAttachment[] $attachments SOAP attachments
|
||||
* @return SoapResponse
|
||||
*/
|
||||
public static function createWithTracingData(
|
||||
SoapRequest $soapRequest,
|
||||
$content,
|
||||
$location,
|
||||
$action,
|
||||
$version,
|
||||
$contentType,
|
||||
SoapResponseTracingData $tracingData,
|
||||
array $attachments = []
|
||||
) {
|
||||
$response = new SoapResponse();
|
||||
$response->setRequest($soapRequest);
|
||||
$response->setContent($content);
|
||||
$response->setLocation($location);
|
||||
$response->setAction($action);
|
||||
$response->setVersion($version);
|
||||
$response->setLocation($soapRequest->getLocation());
|
||||
$response->setAction($soapRequest->getAction());
|
||||
$response->setVersion($soapRequest->getVersion());
|
||||
$response->setContentType($contentType);
|
||||
if ($tracingData !== null) {
|
||||
$response->setTracingData($tracingData);
|
||||
|
9
src/BeSimple/SoapCommon/Fault/SoapFaultPrefixEnum.php
Normal file
9
src/BeSimple/SoapCommon/Fault/SoapFaultPrefixEnum.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace BeSimple\SoapCommon\Fault;
|
||||
|
||||
class SoapFaultPrefixEnum
|
||||
{
|
||||
const PREFIX_DEFAULT = 'be';
|
||||
const PREFIX_PHP = 'php';
|
||||
}
|
25
src/BeSimple/SoapCommon/Fault/SoapFaultSourceGetter.php
Normal file
25
src/BeSimple/SoapCommon/Fault/SoapFaultSourceGetter.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace BeSimple\SoapCommon\Fault;
|
||||
|
||||
use SoapFault;
|
||||
|
||||
class SoapFaultSourceGetter
|
||||
{
|
||||
public static function isNativeSoapFault(SoapFault $soapFault)
|
||||
{
|
||||
return self::isBeSimpleSoapFault($soapFault) === false;
|
||||
}
|
||||
|
||||
public static function isBeSimpleSoapFault(SoapFault $soapFault)
|
||||
{
|
||||
$defaultPrefix = SoapFaultPrefixEnum::PREFIX_DEFAULT;
|
||||
|
||||
if (strpos($soapFault->getCode(), $defaultPrefix) === 0) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace BeSimple\SoapCommon\Mime\Boundary;
|
||||
|
||||
class MimeBoundaryAnalyser
|
||||
{
|
||||
/**
|
||||
* @param string[] $mimeMessageLines
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasMessageBoundary(array $mimeMessageLines)
|
||||
{
|
||||
foreach ($mimeMessageLines as $mimeMessageLine) {
|
||||
if (self::isMessageLineBoundary($mimeMessageLine)) {
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mimeMessageLine
|
||||
* @return bool
|
||||
*/
|
||||
public static function isMessageLineBoundary($mimeMessageLine)
|
||||
{
|
||||
return strlen($mimeMessageLine) > 0 && $mimeMessageLine[0] === "-";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mimeMessageLine
|
||||
* @param string $mimeTypeBoundary
|
||||
* @return bool
|
||||
*/
|
||||
public static function isMessageLineMiddleBoundary($mimeMessageLine, $mimeTypeBoundary)
|
||||
{
|
||||
return strcmp(trim($mimeMessageLine), '--'.$mimeTypeBoundary) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mimeMessageLine
|
||||
* @param string $mimeTypeBoundary
|
||||
* @return bool
|
||||
*/
|
||||
public static function isMessageLineLastBoundary($mimeMessageLine, $mimeTypeBoundary)
|
||||
{
|
||||
return strcmp(trim($mimeMessageLine), '--'.$mimeTypeBoundary.'--') === 0;
|
||||
}
|
||||
}
|
@ -12,9 +12,10 @@
|
||||
|
||||
namespace BeSimple\SoapCommon\Mime;
|
||||
|
||||
use BeSimple\SoapCommon\Mime\Boundary\MimeBoundaryAnalyser;
|
||||
use BeSimple\SoapCommon\Mime\Parser\ContentTypeParser;
|
||||
use BeSimple\SoapCommon\Mime\Parser\ParsedPart;
|
||||
use BeSimple\SoapCommon\Mime\Parser\ParsedPartList;
|
||||
use BeSimple\SoapCommon\Mime\Parser\ParsedPartsGetter;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
@ -25,9 +26,6 @@ use Exception;
|
||||
*/
|
||||
class Parser
|
||||
{
|
||||
const HAS_HTTP_REQUEST_HEADERS = true;
|
||||
const HAS_NO_HTTP_REQUEST_HEADERS = false;
|
||||
|
||||
/**
|
||||
* Parse the given Mime-Message and return a \BeSimple\SoapCommon\Mime\MultiPart object.
|
||||
*
|
||||
@ -39,16 +37,26 @@ class Parser
|
||||
public static function parseMimeMessage($mimeMessage, array $headers = [])
|
||||
{
|
||||
$multiPart = new MultiPart();
|
||||
$mimeMessageLines = preg_split("/(\n)/", $mimeMessage);
|
||||
$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);
|
||||
$hasHttpRequestHeaders = self::HAS_HTTP_REQUEST_HEADERS;
|
||||
$hasHttpRequestHeaders = ParsedPartsGetter::HAS_HTTP_REQUEST_HEADERS;
|
||||
} else {
|
||||
$hasHttpRequestHeaders = self::HAS_NO_HTTP_REQUEST_HEADERS;
|
||||
$hasHttpRequestHeaders = ParsedPartsGetter::HAS_NO_HTTP_REQUEST_HEADERS;
|
||||
}
|
||||
if (self::hasBoundary($mimeMessageLines)) {
|
||||
$parsedPartList = self::getPartsFromMimeMessageLines(
|
||||
if (MimeBoundaryAnalyser::hasMessageBoundary($mimeMessageLines) === true) {
|
||||
$parsedPartList = ParsedPartsGetter::getPartsFromMimeMessageLines(
|
||||
$multiPart,
|
||||
$mimeMessageLines,
|
||||
$hasHttpRequestHeaders
|
||||
@ -79,115 +87,6 @@ class Parser
|
||||
return $multiPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MultiPart $multiPart
|
||||
* @param string[] $mimeMessageLines
|
||||
* @param bool $hasHttpHeaders = self::HAS_HTTP_REQUEST_HEADERS|self::HAS_NO_HTTP_REQUEST_HEADERS
|
||||
* @return ParsedPartList
|
||||
*/
|
||||
private static function getPartsFromMimeMessageLines(
|
||||
MultiPart $multiPart,
|
||||
array $mimeMessageLines,
|
||||
$hasHttpHeaders
|
||||
) {
|
||||
$parsedParts = [];
|
||||
$contentTypeBoundary = $multiPart->getHeader('Content-Type', 'boundary');
|
||||
$contentTypeContentIdStart = $multiPart->getHeader('Content-Type', 'start');
|
||||
$currentPart = $multiPart;
|
||||
$messagePartStringContent = '';
|
||||
$inHeader = $hasHttpHeaders;
|
||||
$hitFirstBoundary = false;
|
||||
|
||||
foreach ($mimeMessageLines as $mimeMessageLine) {
|
||||
// ignore http status code and POST *
|
||||
if (substr($mimeMessageLine, 0, 5) == 'HTTP/' || substr($mimeMessageLine, 0, 4) == 'POST') {
|
||||
continue;
|
||||
}
|
||||
if (isset($currentHeader)) {
|
||||
if (isset($mimeMessageLine[0]) && ($mimeMessageLine[0] === ' ' || $mimeMessageLine[0] === "\t")) {
|
||||
$currentHeader .= $mimeMessageLine;
|
||||
continue;
|
||||
}
|
||||
if (strpos($currentHeader, ':') !== false) {
|
||||
list($headerName, $headerValue) = explode(':', $currentHeader, 2);
|
||||
$headerValueWithNoCrAtTheEnd = trim($headerValue);
|
||||
$headerValue = iconv_mime_decode($headerValueWithNoCrAtTheEnd, 0, Part::CHARSET_UTF8);
|
||||
$parsedMimeHeaders = ContentTypeParser::parseContentTypeHeader($headerName, $headerValue);
|
||||
foreach ($parsedMimeHeaders as $parsedMimeHeader) {
|
||||
$currentPart->setHeader(
|
||||
$parsedMimeHeader->getName(),
|
||||
$parsedMimeHeader->getValue(),
|
||||
$parsedMimeHeader->getSubValue()
|
||||
);
|
||||
}
|
||||
$contentTypeBoundary = $multiPart->getHeader('Content-Type', 'boundary');
|
||||
$contentTypeContentIdStart = $multiPart->getHeader('Content-Type', 'start');
|
||||
}
|
||||
unset($currentHeader);
|
||||
}
|
||||
if ($inHeader === true) {
|
||||
if (trim($mimeMessageLine) == '') {
|
||||
$inHeader = false;
|
||||
continue;
|
||||
}
|
||||
$currentHeader = $mimeMessageLine;
|
||||
continue;
|
||||
} else {
|
||||
if (self::isBoundary($mimeMessageLine)) {
|
||||
if (self::isMiddleBoundary($mimeMessageLine, $contentTypeBoundary)) {
|
||||
if ($currentPart instanceof Part) {
|
||||
$currentPartContent = self::decodeContent(
|
||||
$currentPart,
|
||||
substr($messagePartStringContent, 0, -1)
|
||||
);
|
||||
$currentPart->setContent($currentPartContent);
|
||||
// check if there is a start parameter given, if not set first part
|
||||
if ($contentTypeContentIdStart === null || $currentPart->hasContentId($contentTypeContentIdStart) === true) {
|
||||
$contentTypeContentIdStart = $currentPart->getHeader('Content-ID');
|
||||
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_MAIN);
|
||||
} else {
|
||||
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_NOT_MAIN);
|
||||
}
|
||||
}
|
||||
$currentPart = new Part();
|
||||
$hitFirstBoundary = true;
|
||||
$inHeader = true;
|
||||
$messagePartStringContent = '';
|
||||
} else if (self::isLastBoundary($mimeMessageLine, $contentTypeBoundary)) {
|
||||
$currentPartContent = self::decodeContent(
|
||||
$currentPart,
|
||||
substr($messagePartStringContent, 0, -1)
|
||||
);
|
||||
$currentPart->setContent($currentPartContent);
|
||||
// check if there is a start parameter given, if not set first part
|
||||
if ($contentTypeContentIdStart === null || $currentPart->hasContentId($contentTypeContentIdStart) === true) {
|
||||
$contentTypeContentIdStart = $currentPart->getHeader('Content-ID');
|
||||
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_MAIN);
|
||||
} else {
|
||||
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_NOT_MAIN);
|
||||
}
|
||||
$messagePartStringContent = '';
|
||||
} else {
|
||||
// else block migrated from https://github.com/progmancod/BeSimpleSoap/commit/bf9437e3bcf35c98c6c2f26aca655ec3d3514694
|
||||
// be careful to replace \r\n with \n
|
||||
$messagePartStringContent .= $mimeMessageLine . "\n";
|
||||
}
|
||||
} else {
|
||||
if ($hitFirstBoundary === false) {
|
||||
if (trim($mimeMessageLine) !== '') {
|
||||
$inHeader = true;
|
||||
$currentHeader = $mimeMessageLine;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$messagePartStringContent .= $mimeMessageLine . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ParsedPartList($parsedParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ParsedPartList $parsedPartList
|
||||
* @param MultiPart $multiPart
|
||||
@ -224,56 +123,4 @@ class Parser
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the content of a Mime part
|
||||
*
|
||||
* @param Part $part Part to add content
|
||||
* @param string $partStringContent Content to decode
|
||||
* @return string $partStringContent decodedContent
|
||||
*/
|
||||
private static function decodeContent(Part $part, $partStringContent)
|
||||
{
|
||||
$encoding = strtolower($part->getHeader('Content-Transfer-Encoding'));
|
||||
$charset = strtolower($part->getHeader('Content-Type', 'charset'));
|
||||
|
||||
if ($encoding === Part::ENCODING_BASE64) {
|
||||
$partStringContent = base64_decode($partStringContent);
|
||||
} else if ($encoding === Part::ENCODING_QUOTED_PRINTABLE) {
|
||||
$partStringContent = quoted_printable_decode($partStringContent);
|
||||
}
|
||||
|
||||
if ($charset !== Part::CHARSET_UTF8) {
|
||||
return iconv($charset, Part::CHARSET_UTF8, $partStringContent);
|
||||
}
|
||||
|
||||
return $partStringContent;
|
||||
}
|
||||
|
||||
private static function hasBoundary(array $lines)
|
||||
{
|
||||
foreach ($lines as $line) {
|
||||
if (self::isBoundary($line)) {
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function isBoundary($mimeMessageLine)
|
||||
{
|
||||
return strlen($mimeMessageLine) > 0 && $mimeMessageLine[0] === "-";
|
||||
}
|
||||
|
||||
private static function isMiddleBoundary($mimeMessageLine, $contentTypeBoundary)
|
||||
{
|
||||
return strcmp(trim($mimeMessageLine), '--'.$contentTypeBoundary) === 0;
|
||||
}
|
||||
|
||||
private static function isLastBoundary($mimeMessageLine, $contentTypeBoundary)
|
||||
{
|
||||
return strcmp(trim($mimeMessageLine), '--'.$contentTypeBoundary.'--') === 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
179
src/BeSimple/SoapCommon/Mime/Parser/ParsedPartsGetter.php
Normal file
179
src/BeSimple/SoapCommon/Mime/Parser/ParsedPartsGetter.php
Normal file
@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace BeSimple\SoapCommon\Mime\Parser;
|
||||
|
||||
use BeSimple\SoapCommon\Mime\Boundary\MimeBoundaryAnalyser;
|
||||
use BeSimple\SoapCommon\Mime\MultiPart;
|
||||
use BeSimple\SoapCommon\Mime\Part;
|
||||
use Exception;
|
||||
|
||||
class ParsedPartsGetter
|
||||
{
|
||||
const HAS_HTTP_REQUEST_HEADERS = true;
|
||||
const HAS_NO_HTTP_REQUEST_HEADERS = false;
|
||||
|
||||
/**
|
||||
* @param MultiPart $multiPart
|
||||
* @param string[] $mimeMessageLines
|
||||
* @param bool $hasHttpHeaders = self::HAS_HTTP_REQUEST_HEADERS|self::HAS_NO_HTTP_REQUEST_HEADERS
|
||||
* @return ParsedPartList
|
||||
*/
|
||||
public static function getPartsFromMimeMessageLines(
|
||||
MultiPart $multiPart,
|
||||
array $mimeMessageLines,
|
||||
$hasHttpHeaders
|
||||
) {
|
||||
$parsedParts = [];
|
||||
$contentTypeBoundary = $multiPart->getHeader('Content-Type', 'boundary');
|
||||
if ($contentTypeBoundary === null) {
|
||||
throw new Exception(
|
||||
'Unable to get Content-Type boundary from given MultiPart: ' . var_export($multiPart->getHeaders(), true)
|
||||
);
|
||||
}
|
||||
$contentTypeContentIdStart = $multiPart->getHeader('Content-Type', 'start');
|
||||
if ($contentTypeContentIdStart === null) {
|
||||
throw new Exception(
|
||||
'Unable to get Content-Type start from given MultiPart: ' . var_export($multiPart->getHeaders(), true)
|
||||
);
|
||||
}
|
||||
$currentPart = $multiPart;
|
||||
$messagePartStringContent = '';
|
||||
$inHeader = $hasHttpHeaders;
|
||||
$hitFirstBoundary = false;
|
||||
foreach ($mimeMessageLines as $mimeMessageLine) {
|
||||
if (substr($mimeMessageLine, 0, 5) === 'HTTP/' || substr($mimeMessageLine, 0, 4) === 'POST') {
|
||||
continue;
|
||||
}
|
||||
if (isset($currentHeader)) {
|
||||
if (isset($mimeMessageLine[0]) && ($mimeMessageLine[0] === ' ' || $mimeMessageLine[0] === "\t")) {
|
||||
$currentHeader .= $mimeMessageLine;
|
||||
continue;
|
||||
}
|
||||
if (strpos($currentHeader, ':') !== false) {
|
||||
list($headerName, $headerValue) = explode(':', $currentHeader, 2);
|
||||
$headerValueWithNoCrAtTheEnd = trim($headerValue);
|
||||
try {
|
||||
$headerValue = iconv_mime_decode($headerValueWithNoCrAtTheEnd, 0, Part::CHARSET_UTF8);
|
||||
} catch (Exception $e) {
|
||||
if ($hitFirstBoundary === false) {
|
||||
throw new Exception(
|
||||
'Unable to parse message: cannot parse headers before hitting the first boundary'
|
||||
);
|
||||
}
|
||||
throw new Exception(
|
||||
sprintf(
|
||||
'Unable to get header value: possible parsing message contents of %s characters in header parser: %s',
|
||||
mb_strlen($headerValueWithNoCrAtTheEnd),
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
$parsedMimeHeaders = ContentTypeParser::parseContentTypeHeader($headerName, $headerValue);
|
||||
foreach ($parsedMimeHeaders as $parsedMimeHeader) {
|
||||
$currentPart->setHeader(
|
||||
$parsedMimeHeader->getName(),
|
||||
$parsedMimeHeader->getValue(),
|
||||
$parsedMimeHeader->getSubValue()
|
||||
);
|
||||
}
|
||||
$contentTypeBoundary = $multiPart->getHeader('Content-Type', 'boundary');
|
||||
$contentTypeContentIdStart = $multiPart->getHeader('Content-Type', 'start');
|
||||
}
|
||||
unset($currentHeader);
|
||||
}
|
||||
if ($inHeader === true) {
|
||||
if (trim($mimeMessageLine) === '') {
|
||||
$inHeader = false;
|
||||
continue;
|
||||
}
|
||||
$currentHeader = $mimeMessageLine;
|
||||
continue;
|
||||
} else {
|
||||
if (MimeBoundaryAnalyser::isMessageLineBoundary($mimeMessageLine)) {
|
||||
if (MimeBoundaryAnalyser::isMessageLineMiddleBoundary($mimeMessageLine, $contentTypeBoundary)) {
|
||||
if ($currentPart instanceof Part) {
|
||||
$currentPartContent = self::decodeContent(
|
||||
$currentPart,
|
||||
substr($messagePartStringContent, 0, -1)
|
||||
);
|
||||
if ($currentPartContent[strlen($currentPartContent) - 1] === "\r") {
|
||||
// temporary hack: if there is a CRLF before any middle boundary, then the remaining CR must be removed
|
||||
$currentPartContent = substr($currentPartContent, 0, -1);
|
||||
}
|
||||
$currentPart->setContent($currentPartContent);
|
||||
// check if there is a start parameter given, if not set first part
|
||||
if ($contentTypeContentIdStart === null || $currentPart->hasContentId($contentTypeContentIdStart) === true) {
|
||||
$contentTypeContentIdStart = $currentPart->getHeader('Content-ID');
|
||||
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_MAIN);
|
||||
} else {
|
||||
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_NOT_MAIN);
|
||||
}
|
||||
}
|
||||
$currentPart = new Part();
|
||||
$hitFirstBoundary = true;
|
||||
$inHeader = true;
|
||||
$messagePartStringContent = '';
|
||||
} else if (MimeBoundaryAnalyser::isMessageLineLastBoundary($mimeMessageLine, $contentTypeBoundary)) {
|
||||
$currentPartContent = self::decodeContent(
|
||||
$currentPart,
|
||||
substr($messagePartStringContent, 0, -1)
|
||||
);
|
||||
if ($currentPartContent[strlen($currentPartContent) - 1] === "\r") {
|
||||
// temporary hack: if there is a CRLF before last boundary, then the remaining CR must be removed
|
||||
$currentPartContent = substr($currentPartContent, 0, -1);
|
||||
}
|
||||
$currentPart->setContent($currentPartContent);
|
||||
// check if there is a start parameter given, if not set first part
|
||||
if ($contentTypeContentIdStart === null || $currentPart->hasContentId($contentTypeContentIdStart) === true) {
|
||||
$contentTypeContentIdStart = $currentPart->getHeader('Content-ID');
|
||||
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_MAIN);
|
||||
} else {
|
||||
$parsedParts[] = new ParsedPart($currentPart, ParsedPart::PART_IS_NOT_MAIN);
|
||||
}
|
||||
$messagePartStringContent = '';
|
||||
} else {
|
||||
// else block migrated from https://github.com/progmancod/BeSimpleSoap/commit/bf9437e3bcf35c98c6c2f26aca655ec3d3514694
|
||||
// be careful to replace \r\n with \n
|
||||
$messagePartStringContent .= $mimeMessageLine . "\n";
|
||||
}
|
||||
} else {
|
||||
if ($hitFirstBoundary === false) {
|
||||
if (trim($mimeMessageLine) !== '') {
|
||||
$inHeader = true;
|
||||
$currentHeader = $mimeMessageLine;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$messagePartStringContent .= $mimeMessageLine . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ParsedPartList($parsedParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the content of a Mime part
|
||||
*
|
||||
* @param Part $part Part to add content
|
||||
* @param string $partStringContent Content to decode
|
||||
* @return string $partStringContent decodedContent
|
||||
*/
|
||||
private static function decodeContent(Part $part, $partStringContent)
|
||||
{
|
||||
$encoding = strtolower($part->getHeader('Content-Transfer-Encoding'));
|
||||
$charset = strtolower($part->getHeader('Content-Type', 'charset'));
|
||||
|
||||
if ($encoding === Part::ENCODING_BASE64) {
|
||||
$partStringContent = base64_decode($partStringContent);
|
||||
} else if ($encoding === Part::ENCODING_QUOTED_PRINTABLE) {
|
||||
$partStringContent = quoted_printable_decode($partStringContent);
|
||||
}
|
||||
|
||||
if ($charset !== Part::CHARSET_UTF8) {
|
||||
return iconv($charset, Part::CHARSET_UTF8, $partStringContent);
|
||||
}
|
||||
|
||||
return $partStringContent;
|
||||
}
|
||||
}
|
@ -75,6 +75,11 @@ abstract class PartHeader
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getHeaders()
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate headers.
|
||||
*
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
namespace BeSimple\SoapCommon;
|
||||
|
||||
use DOMDocument;
|
||||
|
||||
/**
|
||||
* Base class for SoapRequest and SoapResponse.
|
||||
*
|
||||
@ -73,13 +75,6 @@ abstract class SoapMessage
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
/**
|
||||
*
|
||||
* Enter description here ...
|
||||
* @var \DOMDocument
|
||||
*/
|
||||
protected $contentDomDocument = null;
|
||||
|
||||
/**
|
||||
* Message content type.
|
||||
*
|
||||
@ -170,11 +165,6 @@ abstract class SoapMessage
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
if ($this->contentDomDocument !== null) {
|
||||
$this->content = $this->contentDomDocument->saveXML();
|
||||
$this->contentDomDocument = null;
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
@ -186,9 +176,6 @@ abstract class SoapMessage
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
if (null !== $this->contentDomDocument) {
|
||||
$this->contentDomDocument->loadXML($this->content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -198,12 +185,10 @@ abstract class SoapMessage
|
||||
*/
|
||||
public function getContentDocument()
|
||||
{
|
||||
if (null === $this->contentDomDocument) {
|
||||
$this->contentDomDocument = new \DOMDocument();
|
||||
$this->contentDomDocument->loadXML($this->content);
|
||||
}
|
||||
$contentDomDocument = new DOMDocument();
|
||||
$contentDomDocument->loadXML($this->content);
|
||||
|
||||
return $this->contentDomDocument;
|
||||
return $contentDomDocument;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -204,8 +204,12 @@ class SoapServer extends \SoapServer
|
||||
$attachments = [];
|
||||
if ($soapRequest->hasAttachments()) {
|
||||
foreach ($soapRequest->getAttachments() as $attachment) {
|
||||
$fileName = $attachment->getHeader('Content-Disposition', 'filename');
|
||||
if ($fileName === null) {
|
||||
$fileName = basename($attachment->getHeader('Content-Location'));
|
||||
}
|
||||
$attachments[] = new SoapAttachment(
|
||||
$attachment->getHeader('Content-Disposition', 'filename'),
|
||||
$fileName,
|
||||
$attachment->getHeader('Content-Type'),
|
||||
$attachment->getContent()
|
||||
);
|
||||
|
@ -6,7 +6,7 @@ use BeSimple\SoapServer\SoapOptions\SoapServerOptions;
|
||||
|
||||
class SoapServerOptionsBuilder
|
||||
{
|
||||
static public function createWithDefaults($handlerClassOrObject)
|
||||
public static function createWithDefaults($handlerClassOrObject)
|
||||
{
|
||||
return new SoapServerOptions(
|
||||
$handlerClassOrObject,
|
||||
|
@ -3,6 +3,8 @@
|
||||
namespace BeSimple\SoapClient;
|
||||
|
||||
use BeSimple\SoapBundle\Soap\SoapAttachment;
|
||||
use BeSimple\SoapClient\Curl\CurlOptions;
|
||||
use BeSimple\SoapClient\SoapOptions\SoapClientOptions;
|
||||
use BeSimple\SoapCommon\ClassMap;
|
||||
use BeSimple\SoapCommon\SoapOptions\SoapOptions;
|
||||
use BeSimple\SoapCommon\SoapOptionsBuilder;
|
||||
@ -182,8 +184,14 @@ class SoapClientTest extends PHPUnit_Framework_TestCase
|
||||
self::assertContains('start="<part-', $tracingData->getLastRequestHeaders(), 'Headers should link to first MultiPart');
|
||||
self::assertContains('action="', $tracingData->getLastRequestHeaders(), 'Headers should contain SOAP action');
|
||||
self::assertEquals(
|
||||
$this->removeOneTimeData(file_get_contents(self::FIXTURES_DIR.'/soapRequestWithTwoAttachments.request')),
|
||||
$this->removeOneTimeData($tracingData->getLastRequest()),
|
||||
$this->removeOneTimeData(
|
||||
file_get_contents(
|
||||
self::FIXTURES_DIR.'/Message/Request/GetUKLocationByCounty.request.mimepart.message'
|
||||
)
|
||||
),
|
||||
$this->removeOneTimeData(
|
||||
$tracingData->getLastRequest()
|
||||
),
|
||||
'Requests must match after onetime data were removed'
|
||||
);
|
||||
}
|
||||
@ -216,7 +224,7 @@ class SoapClientTest extends PHPUnit_Framework_TestCase
|
||||
self::assertNotContains('start="<part-', $tracingData->getLastRequestHeaders(), 'Headers should link to first MultiPart');
|
||||
self::assertContains('action="', $tracingData->getLastRequestHeaders(), 'Headers should contain SOAP action');
|
||||
self::assertStringEqualsFile(
|
||||
self::FIXTURES_DIR.'/soapRequestWithNoAttachments.request',
|
||||
self::FIXTURES_DIR.'/Message/Request/GetUKLocationByCounty.request.message',
|
||||
$tracingData->getLastRequest(),
|
||||
'Requests must match'
|
||||
);
|
||||
|
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace BeSimple\SoapCommon\Mime\Boundary;
|
||||
|
||||
use PHPUnit_Framework_TestCase;
|
||||
|
||||
class MimeBoundaryAnalyserTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
const EXPECTED_HAS_BOUNDARY = true;
|
||||
const EXPECTED_HAS_NO_BOUNDARY = false;
|
||||
const EXPECTED_IS_BOUNDARY = true;
|
||||
const EXPECTED_IS_NOT_BOUNDARY = false;
|
||||
|
||||
/**
|
||||
* @dataProvider mimeMessageLinesDataProvider
|
||||
* @param string[] $mimeMessageLines
|
||||
* @param bool $expectHasBoundary
|
||||
*/
|
||||
public function testHasMessageBoundary(array $mimeMessageLines, $expectHasBoundary)
|
||||
{
|
||||
$hasMessageBoundary = MimeBoundaryAnalyser::hasMessageBoundary($mimeMessageLines);
|
||||
|
||||
self::assertEquals($expectHasBoundary, $hasMessageBoundary);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider mimeMessageLineDataProvider
|
||||
* @param string $mimeMessageLine
|
||||
* @param bool $expectIsBoundary
|
||||
*/
|
||||
public function testIsMessageLineBoundary($mimeMessageLine, $expectIsBoundary)
|
||||
{
|
||||
$isMessageBoundary = MimeBoundaryAnalyser::isMessageLineBoundary($mimeMessageLine);
|
||||
|
||||
self::assertEquals($expectIsBoundary, $isMessageBoundary);
|
||||
}
|
||||
|
||||
public function mimeMessageLinesDataProvider()
|
||||
{
|
||||
return [
|
||||
[
|
||||
[
|
||||
'',
|
||||
'mesage line -- has no boundary',
|
||||
'-- this line is a boundary',
|
||||
'',
|
||||
'',
|
||||
'-- this line is also a boundary --',
|
||||
' -- this is not a boundary'
|
||||
],
|
||||
self::EXPECTED_HAS_BOUNDARY
|
||||
],
|
||||
[
|
||||
[
|
||||
'',
|
||||
'mesage line -- has no boundary',
|
||||
'',
|
||||
'',
|
||||
' -- this is not a boundary'
|
||||
],
|
||||
self::EXPECTED_HAS_NO_BOUNDARY
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function mimeMessageLineDataProvider()
|
||||
{
|
||||
return [
|
||||
['-- this line is boundary', self::EXPECTED_IS_BOUNDARY],
|
||||
['-- this line is also a boundary --', self::EXPECTED_IS_BOUNDARY],
|
||||
['mesage line -- is not boundary', self::EXPECTED_IS_NOT_BOUNDARY],
|
||||
[' -- mesage line -- is not boundary', self::EXPECTED_IS_NOT_BOUNDARY],
|
||||
];
|
||||
}
|
||||
}
|
104
tests/BeSimple/SoapCommon/Mime/Parser/ParsedPartsGetterTest.php
Normal file
104
tests/BeSimple/SoapCommon/Mime/Parser/ParsedPartsGetterTest.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace BeSimple\SoapCommon\Mime\Parser;
|
||||
|
||||
use BeSimple\SoapCommon\Mime\MultiPart;
|
||||
use Exception;
|
||||
use PHPUnit_Framework_TestCase;
|
||||
|
||||
class ParsedPartsGetterTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
const TEST_CASE_SHOULD_FAIL = true;
|
||||
const TEST_CASE_SHOULD_NOT_FAIL = false;
|
||||
|
||||
/**
|
||||
* @dataProvider provideMimeMessageLines
|
||||
* @param MultiPart $multiPart
|
||||
* @param array $mimeMessageLines
|
||||
* @param bool $hasHttpRequestHeaders
|
||||
* @param bool $testCaseShouldFail
|
||||
* @param string|null $failedTestCaseFailMessage
|
||||
*/
|
||||
public function testGetPartsFromMimeMessageLines(
|
||||
MultiPart $multiPart,
|
||||
array $mimeMessageLines,
|
||||
$hasHttpRequestHeaders,
|
||||
$testCaseShouldFail,
|
||||
$failedTestCaseFailMessage = null
|
||||
) {
|
||||
if ($testCaseShouldFail === true) {
|
||||
$this->setExpectedException(Exception::class, $failedTestCaseFailMessage);
|
||||
}
|
||||
$parsedPartsList = ParsedPartsGetter::getPartsFromMimeMessageLines(
|
||||
$multiPart,
|
||||
$mimeMessageLines,
|
||||
$hasHttpRequestHeaders
|
||||
);
|
||||
|
||||
if ($testCaseShouldFail === false) {
|
||||
self::assertInstanceOf(ParsedPartList::class, $parsedPartsList);
|
||||
self::assertGreaterThanOrEqual(3, $parsedPartsList->getPartCount());
|
||||
self::assertTrue($parsedPartsList->hasExactlyOneMainPart());
|
||||
}
|
||||
}
|
||||
|
||||
public function provideMimeMessageLines()
|
||||
{
|
||||
$mimePartWithHeadersForSwaResponse = new MultiPart();
|
||||
$mimePartWithHeadersForSwaResponse->setHeader('Content-Type', 'boundary', 'Part_13_58e3bc35f3743.58e3bc35f376f');
|
||||
$mimePartWithHeadersForSwaResponse->setHeader('Content-Type', 'start', '<part-424dbe68-e2da-450f-9a82-cc3e82742503@response.info>');
|
||||
|
||||
$mimePartWithWrongHeadersForSwaResponse = new MultiPart();
|
||||
$mimePartWithWrongHeadersForSwaResponse->setHeader('Content-Type', 'boundary', 'non-existing');
|
||||
$mimePartWithWrongHeadersForSwaResponse->setHeader('Content-Type', 'start', '<does-not-exist>');
|
||||
|
||||
$mimePartWithHeadersForSwaRequest = new MultiPart();
|
||||
$mimePartWithHeadersForSwaRequest->setHeader('Content-Type', 'boundary', '----=_Part_6_2094841787.1482231370463');
|
||||
$mimePartWithHeadersForSwaRequest->setHeader('Content-Type', 'start', '<rootpart@soapui.org>');
|
||||
|
||||
return [
|
||||
'ParseSwaResponseWith2FilesAnd1BinaryFile' => [
|
||||
$mimePartWithHeadersForSwaResponse,
|
||||
$this->getMessageLinesFromMimeMessage(__DIR__.'/../../../../Fixtures/Message/Response/dummyServiceMethodWithOutgoingLargeSwa.response.mimepart.message'),
|
||||
ParsedPartsGetter::HAS_HTTP_REQUEST_HEADERS,
|
||||
self::TEST_CASE_SHOULD_NOT_FAIL
|
||||
],
|
||||
'ParseSwaResponseWith2FilesAnd1BinaryFileShouldFailWithNoHeaders' => [
|
||||
new MultiPart(),
|
||||
$this->getMessageLinesFromMimeMessage(__DIR__.'/../../../../Fixtures/Message/Response/dummyServiceMethodWithOutgoingLargeSwa.response.mimepart.message'),
|
||||
ParsedPartsGetter::HAS_NO_HTTP_REQUEST_HEADERS,
|
||||
self::TEST_CASE_SHOULD_FAIL,
|
||||
'Unable to get Content-Type boundary'
|
||||
],
|
||||
'ParseSwaResponseWith2FilesAnd1BinaryFileShouldFailWithWrongHeaders' => [
|
||||
$mimePartWithWrongHeadersForSwaResponse,
|
||||
$this->getMessageLinesFromMimeMessage(__DIR__.'/../../../../Fixtures/Message/Response/dummyServiceMethodWithOutgoingLargeSwa.response.mimepart.message'),
|
||||
ParsedPartsGetter::HAS_HTTP_REQUEST_HEADERS,
|
||||
self::TEST_CASE_SHOULD_FAIL,
|
||||
'cannot parse headers before hitting the first boundary'
|
||||
],
|
||||
'ParseSwaRequestWith2Files' => [
|
||||
$mimePartWithHeadersForSwaRequest,
|
||||
$this->getMessageLinesFromMimeMessage(__DIR__ . '/../../../../Fixtures/Message/Request/dummyServiceMethodWithAttachments.request.mimepart.message'),
|
||||
ParsedPartsGetter::HAS_HTTP_REQUEST_HEADERS,
|
||||
self::TEST_CASE_SHOULD_NOT_FAIL
|
||||
],
|
||||
'ParseSwaRequestWith2FilesShouldFailWithNoHeaders' => [
|
||||
new MultiPart(),
|
||||
$this->getMessageLinesFromMimeMessage(__DIR__ . '/../../../../Fixtures/Message/Request/dummyServiceMethodWithAttachments.request.mimepart.message'),
|
||||
ParsedPartsGetter::HAS_NO_HTTP_REQUEST_HEADERS,
|
||||
self::TEST_CASE_SHOULD_FAIL,
|
||||
'Unable to get Content-Type boundary'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function getMessageLinesFromMimeMessage($filePath)
|
||||
{
|
||||
if (file_exists($filePath) === false) {
|
||||
self::fail('Please, update tests data provider - file not found: '.$filePath);
|
||||
}
|
||||
|
||||
return explode("\n", file_get_contents($filePath));
|
||||
}
|
||||
}
|
@ -46,7 +46,7 @@ class SoapServerTest extends PHPUnit_Framework_TestCase
|
||||
$dummyService->getEndpoint(),
|
||||
'DummyService.dummyServiceMethod',
|
||||
'text/xml;charset=UTF-8',
|
||||
file_get_contents(self::FIXTURES_DIR.DIRECTORY_SEPARATOR.'testHandleRequest.message')
|
||||
file_get_contents(self::FIXTURES_DIR.'/Message/Request/dummyServiceMethod.message.request')
|
||||
);
|
||||
$response = $soapServer->handleRequest($request);
|
||||
|
||||
@ -74,7 +74,7 @@ class SoapServerTest extends PHPUnit_Framework_TestCase
|
||||
$dummyService->getEndpoint(),
|
||||
'DummyService.dummyServiceMethodWithAttachments',
|
||||
'text/xml;charset=UTF-8',
|
||||
file_get_contents(self::FIXTURES_DIR.DIRECTORY_SEPARATOR.'testHandleRequestWithSwa.message')
|
||||
file_get_contents(self::FIXTURES_DIR.'/Message/Request/dummyServiceMethodWithAttachments.request.message')
|
||||
);
|
||||
$response = $soapServer->handleRequest($request);
|
||||
|
||||
@ -83,7 +83,7 @@ class SoapServerTest extends PHPUnit_Framework_TestCase
|
||||
self::assertNotContains("\r\n", $response->getContent(), 'Response cannot contain CRLF line endings');
|
||||
self::assertContains('dummyServiceMethodWithAttachmentsResponse', $response->getContent());
|
||||
self::assertSame('DummyService.dummyServiceMethodWithAttachments', $response->getAction());
|
||||
self::assertFalse($response->hasAttachments(), 'Response should contain attachments');
|
||||
self::assertFalse($response->hasAttachments(), 'Response should not contain attachments');
|
||||
}
|
||||
|
||||
public function testHandleRequestWithSwaResponse()
|
||||
@ -102,7 +102,7 @@ class SoapServerTest extends PHPUnit_Framework_TestCase
|
||||
$dummyService->getEndpoint(),
|
||||
'DummyService.dummyServiceMethodWithAttachments',
|
||||
'multipart/related; type="text/xml"; start="<rootpart@soapui.org>"; boundary="----=_Part_6_2094841787.1482231370463"',
|
||||
file_get_contents(self::FIXTURES_DIR.DIRECTORY_SEPARATOR.'testHandleRequestWithSwa.mimepart.message')
|
||||
file_get_contents(self::FIXTURES_DIR.'/Message/Request/dummyServiceMethodWithAttachments.request.mimepart.message')
|
||||
);
|
||||
$response = $soapServer->handleRequest($request);
|
||||
|
||||
|
266
tests/BeSimple/SoapServerAndSoapClientCommunicationTest.php
Normal file
266
tests/BeSimple/SoapServerAndSoapClientCommunicationTest.php
Normal file
@ -0,0 +1,266 @@
|
||||
<?php
|
||||
|
||||
namespace BeSimple;
|
||||
|
||||
use BeSimple\SoapBundle\Soap\SoapAttachment;
|
||||
use BeSimple\SoapClient\SoapClientBuilder;
|
||||
use BeSimple\SoapClient\SoapClientBuilderTest;
|
||||
use BeSimple\SoapClient\SoapClientOptionsBuilder;
|
||||
use BeSimple\SoapClient\SoapFaultWithTracingData;
|
||||
use BeSimple\SoapCommon\ClassMap;
|
||||
use BeSimple\SoapCommon\SoapOptions\SoapOptions;
|
||||
use BeSimple\SoapCommon\SoapOptionsBuilder;
|
||||
use BeSimple\SoapServer\SoapServerBuilder;
|
||||
use BeSimple\SoapServer\SoapServerOptionsBuilder;
|
||||
use Fixtures\DummyService;
|
||||
use Fixtures\DummyServiceMethodWithIncomingLargeSwaRequest;
|
||||
use Fixtures\DummyServiceMethodWithOutgoingLargeSwaRequest;
|
||||
use Fixtures\GenerateTestRequest;
|
||||
use PHPUnit_Framework_TestCase;
|
||||
use SoapHeader;
|
||||
|
||||
class SoapServerAndSoapClientCommunicationTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
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;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->localWebServerProcess = popen('php -S localhost:8000 > /dev/null 2>&1 &', 'r');
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
pclose($this->localWebServerProcess);
|
||||
}
|
||||
|
||||
public function testHandleRequestWithLargeSwaResponse()
|
||||
{
|
||||
$dummyService = new DummyService();
|
||||
$classMap = new ClassMap();
|
||||
foreach ($dummyService->getClassMap() as $type => $className) {
|
||||
$classMap->add($type, $className);
|
||||
}
|
||||
$soapServerBuilder = new SoapServerBuilder();
|
||||
$soapServerOptions = SoapServerOptionsBuilder::createWithDefaults($dummyService);
|
||||
$soapOptions = SoapOptionsBuilder::createSwaWithClassMap($dummyService->getWsdlPath(), $classMap);
|
||||
$soapServer = $soapServerBuilder->build($soapServerOptions, $soapOptions);
|
||||
|
||||
$request = $soapServer->createRequest(
|
||||
$dummyService->getEndpoint(),
|
||||
'DummyService.dummyServiceMethodWithOutgoingLargeSwa',
|
||||
'text/xml;charset=UTF-8',
|
||||
file_get_contents(self::FIXTURES_DIR.'/Message/Request/dummyServiceMethodWithOutgoingLargeSwa.request.message')
|
||||
);
|
||||
$response = $soapServer->handleRequest($request);
|
||||
|
||||
file_put_contents(self::CACHE_DIR . '/content-type-soap-server-response.xml', $response->getContentType());
|
||||
file_put_contents(self::CACHE_DIR . '/multipart-message-soap-server-response.xml', $response->getContent());
|
||||
if ($response->hasAttachments() === true) {
|
||||
foreach ($response->getAttachments() as $attachment) {
|
||||
$fileName = preg_replace('/\<|\>/', '', $attachment->getContentId());
|
||||
file_put_contents(self::CACHE_DIR . DIRECTORY_SEPARATOR . 'attachment-server-response-' . $fileName, $attachment->getContent());
|
||||
|
||||
self::assertRegExp('/filename\.(docx|html|txt)/', $fileName);
|
||||
}
|
||||
} else {
|
||||
self::fail('Response should contain attachments');
|
||||
}
|
||||
|
||||
self::assertContains('dummyServiceMethodWithOutgoingLargeSwaResponse', $response->getContent());
|
||||
self::assertSame('DummyService.dummyServiceMethodWithOutgoingLargeSwa', $response->getAction());
|
||||
|
||||
self::assertEquals(
|
||||
filesize(self::LARGE_SWA_FILE),
|
||||
filesize(self::CACHE_DIR.'/attachment-server-response-filename.docx'),
|
||||
'File cannot differ after transport from SoapClient to SoapServer'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSoapCallSwaWithLargeSwaResponse()
|
||||
{
|
||||
$soapClient = $this->getSoapBuilder()->buildWithSoapHeader(
|
||||
SoapClientOptionsBuilder::createWithEndpointLocation(
|
||||
self::TEST_HTTP_URL.'/SwaSenderEndpoint.php'
|
||||
),
|
||||
SoapOptionsBuilder::createSwaWithClassMap(
|
||||
self::TEST_HTTP_URL.'/SwaSenderEndpoint.php?wsdl',
|
||||
new ClassMap([
|
||||
'GenerateTestRequest' => GenerateTestRequest::class,
|
||||
]),
|
||||
SoapOptions::SOAP_CACHE_TYPE_NONE
|
||||
),
|
||||
new SoapHeader('http://schema.testcase', 'SoapHeader', [
|
||||
'user' => 'admin',
|
||||
])
|
||||
);
|
||||
|
||||
$request = new DummyServiceMethodWithOutgoingLargeSwaRequest();
|
||||
$request->dummyAttribute = 1;
|
||||
|
||||
$soapResponse = $soapClient->soapCall('dummyServiceMethodWithOutgoingLargeSwa', [$request]);
|
||||
$attachments = $soapResponse->getAttachments();
|
||||
|
||||
self::assertContains('</dummyServiceReturn>', $soapResponse->getResponseContent());
|
||||
self::assertTrue($soapResponse->hasAttachments(), 'Response should contain attachments');
|
||||
self::assertCount(3, $attachments);
|
||||
|
||||
file_put_contents(self::CACHE_DIR . '/multipart-message-soap-client-response.xml', $soapResponse->getContent());
|
||||
foreach ($soapResponse->getAttachments() as $attachment) {
|
||||
$fileName = preg_replace('/\<|\>/', '', $attachment->getContentId());
|
||||
file_put_contents(self::CACHE_DIR . DIRECTORY_SEPARATOR . 'attachment-client-response-' . $fileName, $attachment->getContent());
|
||||
|
||||
self::assertRegExp('/filename\.(docx|html|txt)/', $fileName);
|
||||
}
|
||||
|
||||
self::assertEquals(
|
||||
filesize(self::LARGE_SWA_FILE),
|
||||
filesize(self::CACHE_DIR.'/attachment-client-response-filename.docx'),
|
||||
'File cannot differ after transport from SoapClient to SoapServer'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSoapCallWithLargeSwaRequest()
|
||||
{
|
||||
$soapClient = $this->getSoapBuilder()->buildWithSoapHeader(
|
||||
SoapClientOptionsBuilder::createWithEndpointLocation(
|
||||
self::TEST_HTTP_URL.'/SwaReceiverEndpoint.php'
|
||||
),
|
||||
SoapOptionsBuilder::createSwaWithClassMap(
|
||||
self::TEST_HTTP_URL.'/SwaReceiverEndpoint.php?wsdl',
|
||||
new ClassMap([
|
||||
'DummyServiceMethodWithIncomingLargeSwaRequest' => DummyServiceMethodWithIncomingLargeSwaRequest::class,
|
||||
]),
|
||||
SoapOptions::SOAP_CACHE_TYPE_NONE
|
||||
),
|
||||
new SoapHeader('http://schema.testcase', 'SoapHeader', [
|
||||
'user' => 'admin',
|
||||
])
|
||||
);
|
||||
|
||||
$request = new DummyServiceMethodWithIncomingLargeSwaRequest();
|
||||
$request->dummyAttribute = 1;
|
||||
|
||||
try {
|
||||
$soapResponse = $soapClient->soapCall(
|
||||
'dummyServiceMethodWithIncomingLargeSwa',
|
||||
[$request],
|
||||
[
|
||||
new SoapAttachment('filename.txt', 'text/plain', 'plaintext file'),
|
||||
new SoapAttachment('filename.html', 'text/html', '<html><body>Hello world</body></html>'),
|
||||
new SoapAttachment(
|
||||
'filename.docx',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
file_get_contents(self::LARGE_SWA_FILE)
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
self::assertContains('dummyServiceMethodWithIncomingLargeSwa', $soapResponse->getRequest()->getContent());
|
||||
self::assertContains('</dummyServiceReturn>', $soapResponse->getResponseContent());
|
||||
self::assertTrue($soapResponse->getRequest()->hasAttachments(), 'Response MUST contain attachments');
|
||||
self::assertFalse($soapResponse->hasAttachments(), 'Response MUST NOT contain attachments');
|
||||
|
||||
foreach ($soapResponse->getRequest()->getAttachments() as $attachment) {
|
||||
file_put_contents(self::CACHE_DIR . '/attachment-client-request-'.trim($attachment->getContentId(), '<>'), $attachment->getContent());
|
||||
}
|
||||
file_put_contents(self::CACHE_DIR . '/content-type-soap-client-request.xml', $soapResponse->getRequest()->getContentType());
|
||||
file_put_contents(self::CACHE_DIR.'/multipart-message-soap-client-request.xml', $soapResponse->getRequest()->getContent());
|
||||
|
||||
self::assertEquals(
|
||||
filesize(self::LARGE_SWA_FILE),
|
||||
filesize(self::CACHE_DIR.'/attachment-client-request-filename.docx'),
|
||||
'File cannot differ after transport from SoapClient to SoapServer'
|
||||
);
|
||||
|
||||
} catch (SoapFaultWithTracingData $e) {
|
||||
self::fail(
|
||||
'Endpoint did not return expected response: '.var_export($e->getSoapResponseTracingData()->getLastResponse(), true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testHandleRequestWithLargeSwaRequest()
|
||||
{
|
||||
$previousSoapClientCallContentTypeCacheFile = self::CACHE_DIR.'/content-type-soap-client-request.xml';
|
||||
$previousSoapClientCallMessageBodyCacheFile = self::CACHE_DIR.'/multipart-message-soap-client-request.xml';
|
||||
if (file_exists($previousSoapClientCallContentTypeCacheFile) === false || file_exists($previousSoapClientCallMessageBodyCacheFile) === false) {
|
||||
self::fail('Cannot load data from cache: run testSoapCallWithLargeSwaRequest to get the data.');
|
||||
}
|
||||
$previousSoapClientCallContentType = file_get_contents($previousSoapClientCallContentTypeCacheFile);
|
||||
$previousSoapClientCallMessageBody = file_get_contents($previousSoapClientCallMessageBodyCacheFile);
|
||||
|
||||
$dummyService = new DummyService();
|
||||
$classMap = new ClassMap();
|
||||
foreach ($dummyService->getClassMap() as $type => $className) {
|
||||
$classMap->add($type, $className);
|
||||
}
|
||||
$soapServerBuilder = new SoapServerBuilder();
|
||||
$soapServerOptions = SoapServerOptionsBuilder::createWithDefaults($dummyService);
|
||||
$soapOptions = SoapOptionsBuilder::createSwaWithClassMap($dummyService->getWsdlPath(), $classMap);
|
||||
$soapServer = $soapServerBuilder->build($soapServerOptions, $soapOptions);
|
||||
|
||||
$request = $soapServer->createRequest(
|
||||
$dummyService->getEndpoint(),
|
||||
'DummyService.dummyServiceMethodWithIncomingLargeSwa',
|
||||
$previousSoapClientCallContentType,
|
||||
$previousSoapClientCallMessageBody
|
||||
);
|
||||
$response = $soapServer->handleRequest($request);
|
||||
|
||||
self::assertContains('dummyServiceMethodWithIncomingLargeSwaResponse', $response->getContent());
|
||||
self::assertSame('DummyService.dummyServiceMethodWithIncomingLargeSwa', $response->getAction());
|
||||
self::assertEquals(
|
||||
filesize(self::LARGE_SWA_FILE),
|
||||
filesize(self::CACHE_DIR.'/attachment-server-request-filename.docx'),
|
||||
'File cannot differ after transport from SoapClient to SoapServer'
|
||||
);
|
||||
}
|
||||
|
||||
public function testHandleRequestWithLargeSwaRequestAndMixedCrLf()
|
||||
{
|
||||
$soapClientCallContentType = file_get_contents(self::FIXTURES_DIR.'/Message/Request/dummyServiceMethodWithIncomingLargeSwaAndMixedCrLf.contenttypeheaders');
|
||||
$soapClientCallMessageBody = file_get_contents(self::FIXTURES_DIR.'/Message/Request/dummyServiceMethodWithIncomingLargeSwaAndMixedCrLf.request.mimepart.message');
|
||||
|
||||
$dummyService = new DummyService();
|
||||
$classMap = new ClassMap();
|
||||
foreach ($dummyService->getClassMap() as $type => $className) {
|
||||
$classMap->add($type, $className);
|
||||
}
|
||||
$soapServerBuilder = new SoapServerBuilder();
|
||||
$soapServerOptions = SoapServerOptionsBuilder::createWithDefaults($dummyService);
|
||||
$soapOptions = SoapOptionsBuilder::createSwaWithClassMap($dummyService->getWsdlPath(), $classMap);
|
||||
$soapServer = $soapServerBuilder->build($soapServerOptions, $soapOptions);
|
||||
|
||||
$request = $soapServer->createRequest(
|
||||
$dummyService->getEndpoint(),
|
||||
'DummyService.dummyServiceMethodWithIncomingLargeSwa',
|
||||
$soapClientCallContentType,
|
||||
$soapClientCallMessageBody
|
||||
);
|
||||
$response = $soapServer->handleRequest($request);
|
||||
|
||||
self::assertContains('dummyServiceMethodWithIncomingLargeSwaResponse', $response->getContent());
|
||||
self::assertSame('DummyService.dummyServiceMethodWithIncomingLargeSwa', $response->getAction());
|
||||
self::assertEquals(
|
||||
filesize(self::LARGE_SWA_FILE),
|
||||
filesize(self::CACHE_DIR.'/attachment-server-request-oldfilename.docx'),
|
||||
'File cannot differ after transport from SoapClient to SoapServer'
|
||||
);
|
||||
}
|
||||
|
||||
private function getSoapBuilder()
|
||||
{
|
||||
return new SoapClientBuilder();
|
||||
}
|
||||
|
||||
public function getSoapServerBuilder()
|
||||
{
|
||||
return new SoapServerBuilder();
|
||||
}
|
||||
}
|
16
tests/Fixtures/Attachment/MessageWithAttachmentsTrait.php
Normal file
16
tests/Fixtures/Attachment/MessageWithAttachmentsTrait.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Fixtures\Attachment;
|
||||
|
||||
trait MessageWithAttachmentsTrait
|
||||
{
|
||||
/**
|
||||
* @var AttachmentCollection $attachmentCollection
|
||||
*/
|
||||
public $attachmentCollection;
|
||||
|
||||
public function hasAttachments()
|
||||
{
|
||||
return $this->attachmentCollection !== null && $this->attachmentCollection->hasAttachments();
|
||||
}
|
||||
}
|
@ -34,6 +34,10 @@ class DummyService implements AttachmentsHandlerInterface
|
||||
'DummyServiceResponseWithAttachments' => DummyServiceResponseWithAttachments::class,
|
||||
'DummyServiceRequest' => DummyServiceRequest::class,
|
||||
'DummyServiceRequestWithAttachments' => DummyServiceRequestWithAttachments::class,
|
||||
'DummyServiceMethodWithOutgoingLargeSwaRequest' => DummyServiceMethodWithOutgoingLargeSwaRequest::class,
|
||||
'DummyServiceMethodWithOutgoingLargeSwaResponse' => DummyServiceMethodWithOutgoingLargeSwaResponse::class,
|
||||
'DummyServiceMethodWithIncomingLargeSwaRequest' => DummyServiceMethodWithIncomingLargeSwaRequest::class,
|
||||
'DummyServiceMethodWithIncomingLargeSwaResponse' => DummyServiceMethodWithIncomingLargeSwaResponse::class,
|
||||
];
|
||||
}
|
||||
|
||||
@ -67,6 +71,54 @@ class DummyService implements AttachmentsHandlerInterface
|
||||
return $dummyServiceHandler->handle($dummyServiceRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DummyServiceMethodWithOutgoingLargeSwaRequest $dummyServiceRequest
|
||||
* @return DummyServiceMethodWithOutgoingLargeSwaResponse
|
||||
*/
|
||||
public function dummyServiceMethodWithOutgoingLargeSwa(DummyServiceMethodWithOutgoingLargeSwaRequest $dummyServiceRequest)
|
||||
{
|
||||
$dummyServiceHandler = new DummyServiceHandlerWithOutgoingLargeSwa();
|
||||
|
||||
$dummyServiceResponseWithAttachments = $dummyServiceHandler->handle($dummyServiceRequest);
|
||||
|
||||
if ($dummyServiceResponseWithAttachments->hasAttachments() === true) {
|
||||
$soapAttachments = [];
|
||||
foreach ($dummyServiceResponseWithAttachments->attachmentCollection->attachments as $attachment) {
|
||||
$soapAttachments[] = new SoapAttachment(
|
||||
$attachment->fileName,
|
||||
$attachment->contentType,
|
||||
$attachment->content
|
||||
);
|
||||
}
|
||||
$this->addAttachmentStorage(new RequestHandlerAttachmentsStorage($soapAttachments));
|
||||
}
|
||||
|
||||
return $dummyServiceResponseWithAttachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DummyServiceMethodWithIncomingLargeSwaRequest $dummyServiceRequest
|
||||
* @return DummyServiceMethodWithIncomingLargeSwaResponse
|
||||
*/
|
||||
public function dummyServiceMethodWithIncomingLargeSwa(DummyServiceMethodWithIncomingLargeSwaRequest $dummyServiceRequest)
|
||||
{
|
||||
$dummyServiceHandler = new DummyServiceHandlerWithIncomingLargeSwa();
|
||||
$attachmentStorageContents = $this->getAttachmentStorage()->getAttachments();
|
||||
if (count($attachmentStorageContents) > 0) {
|
||||
$attachments = [];
|
||||
foreach ($attachmentStorageContents as $soapAttachment) {
|
||||
$attachments[] = new Attachment(
|
||||
$soapAttachment->getId(),
|
||||
$soapAttachment->getType(),
|
||||
$soapAttachment->getContent()
|
||||
);
|
||||
}
|
||||
$dummyServiceRequest->attachmentCollection = new AttachmentCollection($attachments);
|
||||
}
|
||||
|
||||
return $dummyServiceHandler->handle($dummyServiceRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DummyServiceRequestWithAttachments $dummyServiceRequestWithAttachments
|
||||
* @return DummyServiceResponseWithAttachments
|
||||
|
@ -22,6 +22,26 @@
|
||||
<xsd:element name="status" type="xsd:boolean" minOccurs="1"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="DummyServiceMethodWithOutgoingLargeSwaRequest">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="dummyAttribute" type="xsd:int" minOccurs="1"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="DummyServiceMethodWithOutgoingLargeSwaResponse">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="status" type="xsd:boolean" minOccurs="1"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="DummyServiceMethodWithIncomingLargeSwaRequest">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="dummyAttribute" type="xsd:int" minOccurs="1"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="DummyServiceMethodWithIncomingLargeSwaResponse">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="status" type="xsd:boolean" minOccurs="1"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="DummyServiceRequestWithAttachments">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="dummyAttribute" type="xsd:int" minOccurs="1"/>
|
||||
@ -41,12 +61,24 @@
|
||||
<message name="DummyServiceRequest">
|
||||
<part name="request" type="tns:DummyServiceRequest"/>
|
||||
</message>
|
||||
<message name="DummyServiceMethodWithOutgoingLargeSwaRequest">
|
||||
<part name="request" type="tns:DummyServiceMethodWithOutgoingLargeSwaRequest"/>
|
||||
</message>
|
||||
<message name="DummyServiceMethodWithIncomingLargeSwaRequest">
|
||||
<part name="request" type="tns:DummyServiceMethodWithIncomingLargeSwaRequest"/>
|
||||
</message>
|
||||
<message name="DummyServiceRequestWithAttachments">
|
||||
<part name="request" type="tns:DummyServiceRequestWithAttachments"/>
|
||||
</message>
|
||||
<message name="DummyServiceResponse">
|
||||
<part name="dummyServiceReturn" type="tns:DummyServiceResponse"/>
|
||||
</message>
|
||||
<message name="DummyServiceMethodWithOutgoingLargeSwaResponse">
|
||||
<part name="dummyServiceReturn" type="tns:DummyServiceMethodWithOutgoingLargeSwaResponse"/>
|
||||
</message>
|
||||
<message name="DummyServiceMethodWithIncomingLargeSwaResponse">
|
||||
<part name="dummyServiceReturn" type="tns:DummyServiceMethodWithIncomingLargeSwaResponse"/>
|
||||
</message>
|
||||
<message name="DummyServiceResponseWithAttachments">
|
||||
<part name="dummyServiceReturn" type="tns:DummyServiceResponseWithAttachments"/>
|
||||
</message>
|
||||
@ -55,6 +87,14 @@
|
||||
<wsdl:input message="tns:DummyServiceRequest"/>
|
||||
<wsdl:output message="tns:DummyServiceResponse"/>
|
||||
</wsdl:operation>
|
||||
<wsdl:operation name="dummyServiceMethodWithOutgoingLargeSwa">
|
||||
<wsdl:input message="tns:DummyServiceMethodWithOutgoingLargeSwaRequest"/>
|
||||
<wsdl:output message="tns:DummyServiceMethodWithOutgoingLargeSwaResponse"/>
|
||||
</wsdl:operation>
|
||||
<wsdl:operation name="dummyServiceMethodWithIncomingLargeSwa">
|
||||
<wsdl:input message="tns:DummyServiceMethodWithIncomingLargeSwaRequest"/>
|
||||
<wsdl:output message="tns:DummyServiceMethodWithIncomingLargeSwaResponse"/>
|
||||
</wsdl:operation>
|
||||
<wsdl:operation name="dummyServiceMethodWithAttachments">
|
||||
<wsdl:input message="tns:DummyServiceRequestWithAttachments"/>
|
||||
<wsdl:output message="tns:DummyServiceResponseWithAttachments"/>
|
||||
@ -72,6 +112,26 @@
|
||||
<soap:body use="literal" namespace="http://schema.testcase"/>
|
||||
</wsdl:output>
|
||||
</wsdl:operation>
|
||||
<wsdl:operation name="dummyServiceMethodWithIncomingLargeSwa">
|
||||
<soap:operation soapAction="DummyService.dummyServiceMethodWithIncomingLargeSwa" style="rpc"/>
|
||||
<wsdl:input>
|
||||
<soap:header use="literal" message="tns:SoapHeader" part="SoapHeader" namespace="http://schema.testcase"/>
|
||||
<soap:body use="literal" part="request" namespace="http://schema.testcase"/>
|
||||
</wsdl:input>
|
||||
<wsdl:output>
|
||||
<soap:body use="literal" namespace="http://schema.testcase"/>
|
||||
</wsdl:output>
|
||||
</wsdl:operation>
|
||||
<wsdl:operation name="dummyServiceMethodWithOutgoingLargeSwa">
|
||||
<soap:operation soapAction="DummyService.dummyServiceMethodWithOutgoingLargeSwa" style="rpc"/>
|
||||
<wsdl:input>
|
||||
<soap:header use="literal" message="tns:SoapHeader" part="SoapHeader" namespace="http://schema.testcase"/>
|
||||
<soap:body use="literal" part="request" namespace="http://schema.testcase"/>
|
||||
</wsdl:input>
|
||||
<wsdl:output>
|
||||
<soap:body use="literal" namespace="http://schema.testcase"/>
|
||||
</wsdl:output>
|
||||
</wsdl:operation>
|
||||
<wsdl:operation name="dummyServiceMethodWithAttachments">
|
||||
<soap:operation soapAction="DummyService.dummyServiceMethodWithAttachments" style="rpc"/>
|
||||
<wsdl:input>
|
||||
|
27
tests/Fixtures/DummyServiceHandlerWithIncomingLargeSwa.php
Normal file
27
tests/Fixtures/DummyServiceHandlerWithIncomingLargeSwa.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Fixtures;
|
||||
|
||||
class DummyServiceHandlerWithIncomingLargeSwa
|
||||
{
|
||||
/**
|
||||
* @param DummyServiceMethodWithIncomingLargeSwaRequest $request
|
||||
* @return DummyServiceMethodWithIncomingLargeSwaResponse
|
||||
*/
|
||||
public function handle(DummyServiceMethodWithIncomingLargeSwaRequest $request)
|
||||
{
|
||||
if ($request->hasAttachments() === true) {
|
||||
foreach ($request->attachmentCollection->attachments as $attachment) {
|
||||
file_put_contents(
|
||||
__DIR__.'/../../cache/attachment-server-request-'.$attachment->fileName,
|
||||
$attachment->content
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$response = new DummyServiceMethodWithIncomingLargeSwaResponse();
|
||||
$response->status = true;
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
32
tests/Fixtures/DummyServiceHandlerWithOutgoingLargeSwa.php
Normal file
32
tests/Fixtures/DummyServiceHandlerWithOutgoingLargeSwa.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Fixtures;
|
||||
|
||||
use BeSimple\SoapServerAndSoapClientCommunicationTest;
|
||||
use Fixtures\Attachment\Attachment;
|
||||
use Fixtures\Attachment\AttachmentCollection;
|
||||
|
||||
class DummyServiceHandlerWithOutgoingLargeSwa
|
||||
{
|
||||
/**
|
||||
* @param DummyServiceMethodWithOutgoingLargeSwaRequest $request
|
||||
* @return DummyServiceMethodWithOutgoingLargeSwaResponse
|
||||
*/
|
||||
public function handle(DummyServiceMethodWithOutgoingLargeSwaRequest $request)
|
||||
{
|
||||
$response = new DummyServiceMethodWithOutgoingLargeSwaResponse();
|
||||
$response->status = true;
|
||||
|
||||
$response->attachmentCollection = new AttachmentCollection([
|
||||
new Attachment('filename.txt', 'text/plain', 'plaintext file'),
|
||||
new Attachment('filename.html', 'text/html', '<html><body>Hello world</body></html>'),
|
||||
new Attachment(
|
||||
'filename.docx',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
file_get_contents(SoapServerAndSoapClientCommunicationTest::LARGE_SWA_FILE)
|
||||
),
|
||||
]);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Fixtures;
|
||||
|
||||
use Fixtures\Attachment\MessageWithAttachmentsTrait;
|
||||
|
||||
class DummyServiceMethodWithIncomingLargeSwaRequest
|
||||
{
|
||||
use MessageWithAttachmentsTrait;
|
||||
|
||||
/**
|
||||
* @var int $dummyAttribute
|
||||
*/
|
||||
public $dummyAttribute;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Fixtures;
|
||||
|
||||
class DummyServiceMethodWithIncomingLargeSwaResponse
|
||||
{
|
||||
/**
|
||||
* @var bool $status
|
||||
*/
|
||||
public $status;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Fixtures;
|
||||
|
||||
class DummyServiceMethodWithOutgoingLargeSwaRequest
|
||||
{
|
||||
/**
|
||||
* @var int $dummyAttribute
|
||||
*/
|
||||
public $dummyAttribute;
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Fixtures;
|
||||
|
||||
use Fixtures\Attachment\MessageWithAttachmentsTrait;
|
||||
|
||||
class DummyServiceMethodWithOutgoingLargeSwaResponse
|
||||
{
|
||||
use MessageWithAttachmentsTrait;
|
||||
|
||||
/**
|
||||
* @var bool $status
|
||||
*/
|
||||
public $status;
|
||||
}
|
@ -2,10 +2,12 @@
|
||||
|
||||
namespace Fixtures;
|
||||
|
||||
use Fixtures\Attachment\AttachmentCollection;
|
||||
use Fixtures\Attachment\MessageWithAttachmentsTrait;
|
||||
|
||||
class DummyServiceRequestWithAttachments
|
||||
{
|
||||
use MessageWithAttachmentsTrait;
|
||||
|
||||
/**
|
||||
* @var int $dummyAttribute
|
||||
*/
|
||||
@ -15,14 +17,4 @@ class DummyServiceRequestWithAttachments
|
||||
* @var bool $includeAttachments
|
||||
*/
|
||||
public $includeAttachments;
|
||||
|
||||
/**
|
||||
* @var AttachmentCollection $attachmentCollection
|
||||
*/
|
||||
public $attachmentCollection;
|
||||
|
||||
public function hasAttachments()
|
||||
{
|
||||
return $this->attachmentCollection !== null && $this->attachmentCollection->hasAttachments();
|
||||
}
|
||||
}
|
||||
|
@ -2,22 +2,14 @@
|
||||
|
||||
namespace Fixtures;
|
||||
|
||||
use Fixtures\Attachment\AttachmentCollection;
|
||||
use Fixtures\Attachment\MessageWithAttachmentsTrait;
|
||||
|
||||
class DummyServiceResponseWithAttachments
|
||||
{
|
||||
use MessageWithAttachmentsTrait;
|
||||
|
||||
/**
|
||||
* @var bool $status
|
||||
*/
|
||||
public $status;
|
||||
|
||||
/**
|
||||
* @var AttachmentCollection $attachmentCollection
|
||||
*/
|
||||
public $attachmentCollection;
|
||||
|
||||
public function hasAttachments()
|
||||
{
|
||||
return $this->attachmentCollection !== null && $this->attachmentCollection->hasAttachments();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
multipart/related; type="application/soap+xml"; charset=utf-8; boundary="urn:uuid:340c4456-d650-4ddb-ae83-b13cf6077326"; start="<urn:uuid:c0d48506-8780-410c-b06a-52b6bbbefa5b>"; action="DummyService.dummyServiceMethodWithIncomingLargeSwa"
|
Binary file not shown.
@ -0,0 +1,14 @@
|
||||
<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:dummyServiceMethodWithOutgoingLargeSwa>
|
||||
<request>
|
||||
<dummyAttribute>1</dummyAttribute>
|
||||
</request>
|
||||
</sch:dummyServiceMethodWithOutgoingLargeSwa>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://schema.testcase">
|
||||
<SOAP-ENV:Body>
|
||||
<ns1:dummyServiceMethodWithIncomingLargeSwaRequest>
|
||||
<dummyServiceReturn>
|
||||
<status>true</status>
|
||||
</dummyServiceReturn>
|
||||
</ns1:dummyServiceMethodWithIncomingLargeSwaRequest>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
Binary file not shown.
BIN
tests/Fixtures/large-test-file.docx
Normal file
BIN
tests/Fixtures/large-test-file.docx
Normal file
Binary file not shown.
15
tests/SwaReceiverEndpoint.php
Normal file
15
tests/SwaReceiverEndpoint.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
const FIXTURES_DIR = __DIR__.'/Fixtures';
|
||||
const CACHE_DIR = __DIR__.'/../cache';
|
||||
|
||||
if (isset($_GET['wsdl'])) {
|
||||
header('Content-type: text/xml');
|
||||
echo file_get_contents(FIXTURES_DIR.'/DummyService.wsdl');
|
||||
exit;
|
||||
}
|
||||
|
||||
header('Content-type: text/xml');
|
||||
echo file_get_contents(FIXTURES_DIR.'/Message/Response/dummyServiceMethodWithIncomingLargeSwa.response.message');
|
22
tests/SwaSenderEndpoint.php
Normal file
22
tests/SwaSenderEndpoint.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
const FIXTURES_DIR = __DIR__ . '/Fixtures';
|
||||
|
||||
if (isset($_GET['wsdl'])) {
|
||||
header('Content-type: text/xml');
|
||||
echo file_get_contents(FIXTURES_DIR.'/DummyService.wsdl');
|
||||
exit;
|
||||
}
|
||||
$contentTypeFromCache = __DIR__.'/../cache/content-type-soap-server-response.xml';
|
||||
$multiPartMessageFromCache = __DIR__.'/../cache/multipart-message-soap-server-response.xml';
|
||||
|
||||
if (file_exists($contentTypeFromCache) === false || file_exists($multiPartMessageFromCache) === false) {
|
||||
$soapServer = new \SoapServer(FIXTURES_DIR.'/DummyService.wsdl');
|
||||
$soapServer->fault(
|
||||
911,
|
||||
'Cannot load data from cache: run soap server testHandleRequestWithLargeSwaResponse to get the data.'
|
||||
);
|
||||
}
|
||||
|
||||
header('Content-type: '.file_get_contents($contentTypeFromCache));
|
||||
echo file_get_contents($multiPartMessageFromCache);
|
Loading…
Reference in New Issue
Block a user