Soap server with attachments refactoring

This commit is contained in:
Petr Bechyně
2016-11-08 15:42:52 +01:00
parent 8d033f9afc
commit 84c37b1d24
23 changed files with 511 additions and 502 deletions

View File

@ -16,6 +16,7 @@ use BeSimple\SoapCommon\Helper;
use BeSimple\SoapCommon\Mime\MultiPart as MimeMultiPart;
use BeSimple\SoapCommon\Mime\Parser as MimeParser;
use BeSimple\SoapCommon\Mime\Part as MimePart;
use BeSimple\SoapCommon\Mime\Part;
use BeSimple\SoapCommon\SoapRequest;
use BeSimple\SoapCommon\SoapRequestFilter;
use BeSimple\SoapCommon\SoapResponse;
@ -30,32 +31,17 @@ class MimeFilter implements SoapRequestFilter, SoapResponseFilter
{
public function filterRequest(SoapRequest $request, $attachmentType)
{
$attachmentsReceived = [];
$multiPartMessage = MimeParser::parseMimeMessage(
$request->getContent(),
['Content-Type' => trim($request->getContentType())]
);
$soapPart = $multiPartMessage->getMainPart();
$attachments = $multiPartMessage->getAttachments();
// check content type if it is a multipart mime message
$requestContentType = $request->getContentType();
if (stripos($requestContentType, 'multipart/related') !== false) {
// parse mime message
$headers = [
'Content-Type' => trim($requestContentType),
];
$multipart = MimeParser::parseMimeMessage($request->getContent(), $headers);
// get soap payload and update SoapResponse object
$soapPart = $multipart->getPart();
// convert href -> myhref for external references as PHP throws exception in this case
// http://svn.php.net/viewvc/php/php-src/branches/PHP_5_4/ext/soap/php_encoding.c?view=markup#l3436
$content = preg_replace('/href=(?!#)/', 'myhref=', $soapPart->getContent());
$request->setContent($content);
$request->setContentType($soapPart->getHeader('Content-Type'));
// store attachments
$attachments = $multipart->getParts(false);
foreach ($attachments as $cid => $attachment) {
$attachmentsReceived[$cid] = $attachment;
}
}
if (count($attachmentsReceived) > 0) {
$request->setAttachments($attachmentsReceived);
$request->setContent($this->sanitizePhpExceptionOnHrefs($soapPart));
$request->setContentType($soapPart->getHeader('Content-Type'));
if (count($attachments) > 0) {
$request->setAttachments($attachments);
}
return $request;
@ -65,28 +51,26 @@ class MimeFilter implements SoapRequestFilter, SoapResponseFilter
{
$attachmentsToSend = $response->getAttachments();
if (count($attachmentsToSend) > 0) {
$multipart = new MimeMultiPart();
$multipart = new MimeMultiPart('Part_' . rand(10, 15) . '_' . uniqid() . '.' . uniqid());
$soapPart = new MimePart($response->getContent(), 'text/xml', 'utf-8', MimePart::ENCODING_EIGHT_BIT);
$soapVersion = $response->getVersion();
// change content type headers for MTOM with SOAP 1.1
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');
}
// change content type headers for SOAP 1.2
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');
}
$multipart->addPart($soapPart, true);
foreach ($attachmentsToSend as $cid => $attachment) {
$multipart->addPart($attachment, false);
}
$response->setContent($multipart->getMimeMessage());
// TODO
$headers = $multipart->getHeadersForHttp();
list(, $contentType) = explode(': ', $headers[0]);
@ -95,4 +79,11 @@ class MimeFilter implements SoapRequestFilter, SoapResponseFilter
return $response;
}
private function sanitizePhpExceptionOnHrefs(Part $soapPart)
{
// convert href -> myhref for external references as PHP throws exception in this case
// http://svn.php.net/viewvc/php/php-src/branches/PHP_5_4/ext/soap/php_encoding.c?view=markup#l3436
return preg_replace('/href=(?!#)/', 'myhref=', $soapPart->getContent());
}
}

View File

@ -2,6 +2,8 @@
namespace BeSimple\SoapServer\SoapOptions;
use Exception;
class SoapServerOptions
{
const SOAP_SERVER_PERSISTENCE_NONE = 0;
@ -43,6 +45,22 @@ class SoapServerOptions
$this->persistence = $persistence;
}
public function getHandler()
{
if ($this->hasHandlerObject()) {
return $this->getHandlerObject();
} else if ($this->hasHandlerClass()) {
return $this->getHandlerClass();
} else {
throw new Exception('No HandlerClass or HandlerObject set');
}
}
public function hasHandlerClass()
{
return $this->handlerClass !== null;

View File

@ -8,9 +8,6 @@ class SoapResponse extends CommonSoapResponse
{
public function getResponseContent()
{
// set Content-Type header
header('Content-Type: ' . $this->getContentType());
return $this->getContent();
}
}

View File

@ -12,6 +12,8 @@
namespace BeSimple\SoapServer;
use BeSimple\SoapBundle\Soap\SoapAttachment;
use BeSimple\SoapCommon\Mime\Part;
use BeSimple\SoapCommon\SoapMessage;
class SoapResponseFactory
@ -23,7 +25,7 @@ class SoapResponseFactory
* @param string $location Location
* @param string $action SOAP action
* @param string $version SOAP version
* @param array $attachments SOAP attachments
* @param SoapAttachment[] $attachments SOAP attachments
*
* @return SoapResponse
*/
@ -37,6 +39,33 @@ class SoapResponseFactory
$contentType = SoapMessage::getContentTypeForVersion($version);
$response->setContentType($contentType);
if (count($attachments) > 0) {
$response->setAttachments(
self::createAttachmentParts($attachments)
);
}
return $response;
}
/**
* @param SoapAttachment[] $attachments SOAP attachments
* @return Part[]
*/
private function createAttachmentParts(array $attachments = [])
{
$parts = [];
foreach ($attachments as $attachment) {
$part = new Part(
$attachment->getContent(),
'application/pdf',
'utf-8',
Part::ENCODING_BINARY,
$attachment->getId()
);
$parts[] = $part;
}
return $parts;
}
}

View File

@ -12,10 +12,13 @@
namespace BeSimple\SoapServer;
use BeSimple\SoapBundle\Soap\SoapAttachment;
use BeSimple\SoapCommon\AttachmentsHandler;
use BeSimple\SoapCommon\SoapKernel;
use BeSimple\SoapCommon\SoapOptions\SoapOptions;
use BeSimple\SoapCommon\SoapRequest;
use BeSimple\SoapCommon\SoapRequestFactory;
use BeSimple\SoapCommon\Storage\RequestHandlerAttachmentsStorage;
use BeSimple\SoapServer\SoapOptions\SoapServerOptions;
use BeSimple\SoapCommon\Converter\MtomTypeConverter;
use BeSimple\SoapCommon\Converter\SwaTypeConverter;
@ -44,9 +47,6 @@ class SoapServer extends \SoapServer
*/
public function __construct(SoapServerOptions $soapServerOptions, SoapOptions $soapOptions)
{
if ($soapOptions->hasAttachments()) {
$soapOptions = $this->configureTypeConverters($soapOptions);
}
$this->soapVersion = $soapOptions->getSoapVersion();
$this->soapServerOptions = $soapServerOptions;
$this->soapOptions = $soapOptions;
@ -60,6 +60,7 @@ class SoapServer extends \SoapServer
/**
* Custom handle method to be able to modify the SOAP messages.
*
* @deprecated Please, use createRequest + handleRequest methods
* @param string $requestUrl
* @param string $soapAction
* @param string $requestContent = null
@ -67,15 +68,9 @@ class SoapServer extends \SoapServer
*/
public function handle($requestUrl, $soapAction, $requestContent = null)
{
try {
return $this->getSoapResponse($requestUrl, $soapAction, $requestContent)->getResponseContent();
} catch (\SoapFault $fault) {
$this->fault($fault->faultcode, $fault->faultstring);
return self::SOAP_SERVER_REQUEST_FAILED;
}
return $this->handleRequest(
$this->createRequest($requestUrl, $soapAction, $requestContent)
);
}
/**
@ -83,20 +78,42 @@ class SoapServer extends \SoapServer
*
* @param string $requestUrl
* @param string $soapAction
* @param string $requestContentType
* @param string $requestContent = null
* @return SoapResponse
* @return SoapRequest
*/
public function getSoapResponse($requestUrl, $soapAction, $requestContent = null)
public function createRequest($requestUrl, $soapAction, $requestContentType, $requestContent = null)
{
$soapRequest = SoapRequestFactory::create(
$requestUrl,
$soapAction,
$this->soapVersion,
$requestContentType,
$requestContent
);
$soapResponse = $this->handleSoapRequest($soapRequest);
$soapKernel = new SoapKernel();
if ($this->soapOptions->hasAttachments()) {
$soapRequest = $soapKernel->filterRequest(
$soapRequest,
$this->getAttachmentFilters(),
$this->soapOptions->getAttachmentType()
);
}
return $soapResponse;
return $soapRequest;
}
public function handleRequest(SoapRequest $soapRequest)
{
try {
return $this->handleSoapRequest($soapRequest);
} catch (\SoapFault $fault) {
$this->fault($fault->faultcode, $fault->faultstring);
return self::SOAP_SERVER_REQUEST_FAILED;
}
}
/**
@ -110,33 +127,87 @@ class SoapServer extends \SoapServer
*/
private function handleSoapRequest(SoapRequest $soapRequest)
{
$soapKernel = new SoapKernel();
/** @var AttachmentsHandler $handler */
$handler = $this->soapServerOptions->getHandler();
if ($this->soapOptions->hasAttachments()) {
$soapRequest = $soapKernel->filterRequest($soapRequest, $this->getFilters(), $this->soapOptions->getAttachmentType());
$this->injectAttachmentStorage($handler, $soapRequest, $this->soapOptions->getAttachmentType());
}
ob_start();
parent::handle($soapRequest->getContent());
$response = ob_get_clean();
$nativeSoapServerResponse = ob_get_clean();
$attachments = [];
if ($this->soapOptions->hasAttachments()) {
$attachments = $handler->getAttachmentsFromStorage();
}
// Remove headers added by SoapServer::handle() method
header_remove('Content-Length');
header_remove('Content-Type');
$soapResponse = SoapResponseFactory::create(
$response,
return $this->createResponse(
$soapRequest->getLocation(),
$soapRequest->getAction(),
$soapRequest->getVersion()
$soapRequest->getVersion(),
$nativeSoapServerResponse,
$attachments
);
}
/**
* @param string $requestLocation
* @param string $soapAction
* @param string $soapVersion
* @param string|null $responseContent
* @param SoapAttachment[] $attachments
* @return SoapResponse
*/
private function createResponse($requestLocation, $soapAction, $soapVersion, $responseContent = null, $attachments = [])
{
$soapResponse = SoapResponseFactory::create(
$responseContent,
$requestLocation,
$soapAction,
$soapVersion,
$attachments
);
$soapKernel = new SoapKernel();
if ($this->soapOptions->hasAttachments()) {
$soapResponse = $soapKernel->filterResponse($soapResponse, $this->getFilters(), $this->soapOptions->getAttachmentType());
$soapResponse = $soapKernel->filterResponse(
$soapResponse,
$this->getAttachmentFilters(),
$this->soapOptions->getAttachmentType()
);
}
return $soapResponse;
}
private function injectAttachmentStorage(AttachmentsHandler $handler, SoapRequest $soapRequest, $attachmentType)
{
$attachments = [];
if ($soapRequest->hasAttachments()) {
foreach ($soapRequest->getAttachments() as $attachment) {
$attachments[] = new SoapAttachment(
$attachment->getHeader('Content-Disposition', 'filename'),
$attachmentType,
$attachment->getContent()
);
}
}
$handler->addAttachmentStorage(new RequestHandlerAttachmentsStorage($attachments));
}
/**
* Legacy code: TypeConverters should be resolved in SoapServer::__construct()
* To be removed if all tests pass
*
* @deprecated
* @param SoapOptions $soapOptions
* @return SoapOptions
*/
private function configureTypeConverters(SoapOptions $soapOptions)
{
if ($soapOptions->getAttachmentType() !== SoapOptions::SOAP_ATTACHMENTS_TYPE_BASE64) {
@ -152,7 +223,7 @@ class SoapServer extends \SoapServer
return $soapOptions;
}
private function getFilters()
private function getAttachmentFilters()
{
$filters = [];
if ($this->soapOptions->getAttachmentType() !== SoapOptions::SOAP_ATTACHMENTS_TYPE_BASE64) {

View File

@ -31,7 +31,7 @@ class XmlMimeFilter implements SoapResponseFilter
{
}
public function filterResponse(SoapResponse $response)
public function filterResponse(SoapResponse $response, $attachmentType)
{
$dom = $response->getContentDocument();