/** * Write in specific format * * @param DataService &$dataService Dataservice * @param RequestDescription &$requestDescription Request description object * @param Object &$odataModelInstance OData model instance * @param String $responseContentType Content type of the response * @param String $responseFormat Output format * * @return nothing */ public static function write(DataService &$dataService, RequestDescription &$requestDescription, &$odataModelInstance, $responseContentType, $responseFormat) { $responseBody = null; $dataServiceVersion = $requestDescription->getResponseDataServiceVersion(); if ($responseFormat == ResponseFormat::METADATA_DOCUMENT) { // /$metadata $writer = new MetadataWriter($dataService->getMetadataQueryProviderWrapper()); $responseBody = $writer->writeMetadata(); $dataServiceVersion = $writer->getDataServiceVersion(); } else { if ($responseFormat == ResponseFormat::TEXT) { // /Customer('ALFKI')/CompanyName/$value // /Customers/$count $responseBody = utf8_encode($requestDescription->getTargetResult()); } else { if ($responseFormat == ResponseFormat::BINARY) { // Binary property or media resource $targetKind = $requestDescription->getTargetKind(); if ($targetKind == RequestTargetKind::MEDIA_RESOURCE) { $eTag = $dataService->getStreamProvider()->getStreamETag($requestDescription->getTargetResult(), $requestDescription->getResourceStreamInfo()); $dataService->getHost()->setResponseETag($eTag); $responseBody = $dataService->getStreamProvider()->getReadStream($requestDescription->getTargetResult(), $requestDescription->getResourceStreamInfo()); } else { $responseBody = $requestDescription->getTargetResult(); } if (is_null($responseContentType)) { $responseContentType = ODataConstants::MIME_APPLICATION_OCTETSTREAM; } } else { $writer = null; $absoluteServiceUri = $dataService->getHost()->getAbsoluteServiceUri()->getUrlAsString(); if ($responseFormat == ResponseFormat::ATOM || $responseFormat == ResponseFormat::PLAIN_XML) { if (is_null($odataModelInstance)) { $writer = new \ODataProducer\Writers\ServiceDocument\Atom\ServiceDocumentWriter($dataService->getMetadataQueryProviderWrapper(), $absoluteServiceUri); } else { $isPostV1 = $requestDescription->getResponseDataServiceVersion()->compare(new Version(1, 0)) == 1; $writer = new ODataWriter($absoluteServiceUri, $isPostV1, 'atom'); } } else { if ($responseFormat == ResponseFormat::JSON) { if (is_null($odataModelInstance)) { $writer = new \ODataProducer\Writers\ServiceDocument\Json\ServiceDocumentWriter($dataService->getMetadataQueryProviderWrapper(), $absoluteServiceUri); } else { $isPostV1 = $requestDescription->getResponseDataServiceVersion()->compare(new Version(1, 0)) == 1; $writer = new ODataWriter($absoluteServiceUri, $isPostV1, 'json'); } } } $responseBody = $writer->writeRequest($odataModelInstance); } } } $dataService->getHost()->setResponseStatusCode(HttpStatus::CODE_OK); $dataService->getHost()->setResponseContentType($responseContentType); $dataService->getHost()->setResponseVersion($dataServiceVersion->toString() . ';'); $dataService->getHost()->setResponseCacheControl(ODataConstants::HTTPRESPONSE_HEADER_CACHECONTROL_NOCACHE); $dataService->getHost()->getWebOperationContext()->outgoingResponse()->setStream($responseBody); }
/** * Process the given request Uri and creates an instance of * RequestDescription from the processed uri. * * @param Url &$absoluteRequestUri The absolute request uri. * @param DataService &$dataService Reference to the data service * instance. * * @return RequestDescription * * @throws ODataException If any exception occurs while processing the segments * or incase of any version incompatibility. */ public static function process(Url &$absoluteRequestUri, DataService &$dataService) { $absoluteRequestUri = $dataService->getHost()->getAbsoluteRequestUri(); $absoluteServiceUri = $dataService->getHost()->getAbsoluteServiceUri(); $requestUriSegments = array_slice($absoluteRequestUri->getSegments(), $absoluteServiceUri->getSegmentCount()); $segmentDescriptors = null; try { $segmentDescriptors = SegmentParser::parseRequestUriSegements($requestUriSegments, $dataService->getMetadataQueryProviderWrapper(), true); } catch (ODataException $odataException) { throw $odataException; } $requestDescription = new RequestDescription($segmentDescriptors, $absoluteRequestUri); $requestTargetKind = $requestDescription->getTargetKind(); if ($requestTargetKind != RequestTargetKind::METADATA && $requestTargetKind != RequestTargetKind::BATCH && $requestTargetKind != RequestTargetKind::SERVICE_DIRECTORY) { if ($requestTargetKind != RequestTargetKind::PRIMITIVE_VALUE && $requestTargetKind != RequestTargetKind::MEDIA_RESOURCE) { $requestDescription->setContainerName($requestDescription->getIdentifier()); } else { // http://odata/NW.svc/Orders/$count // http://odata/NW.svc/Orders(123)/Customer/CustomerID/$value // http://odata/NW.svc/Employees(1)/$value // http://odata/NW.svc/Employees(1)/ThumbNail_48X48/$value $requestDescription->setContainerName($segmentDescriptors[count($segmentDescriptors) - 2]->getIdentifier()); } if ($requestDescription->getIdentifier() === ODataConstants::URI_COUNT_SEGMENT) { if (!$dataService->getServiceConfiguration()->getAcceptCountRequests()) { ODataException::createBadRequestError(Messages::dataServiceConfigurationCountNotAccepted()); } $requestDescription->setRequestCountOption(RequestCountOption::VALUE_ONLY); // use of $count requires request DataServiceVersion // and MaxDataServiceVersion // greater than or equal to 2.0 $requestDescription->raiseResponseVersion(2, 0, $dataService); $requestDescription->raiseMinimumVersionRequirement(2, 0, $dataService); } else { if ($requestDescription->isNamedStream()) { $requestDescription->raiseMinimumVersionRequirement(3, 0, $dataService); } else { if ($requestDescription->getTargetKind() == RequestTargetKind::RESOURCE) { if (!$requestDescription->isLinkUri()) { $resourceSetWrapper = $requestDescription->getTargetResourceSetWrapper(); //assert($resourceSetWrapper != null) $hasNamedStream = $resourceSetWrapper->hasNamedStreams($dataService->getMetadataQueryProviderWrapper()); $hasBagProperty = $resourceSetWrapper->hasBagProperty($dataService->getMetadataQueryProviderWrapper()); if ($hasNamedStream || $hasBagProperty) { $requestDescription->raiseResponseVersion(3, 0, $dataService); } } } else { if ($requestDescription->getTargetKind() == RequestTargetKind::BAG) { $requestDescription->raiseResponseVersion(3, 0, $dataService); } } } } } return $requestDescription; }
/** * Checks whether client request contains any odata query options. * * @return void * * @throws ODataException Throws bad request error if client request * includes any odata query option. */ private function _checkForEmptyQueryArguments() { $dataServiceHost = $this->_dataService->getHost(); if (!is_null($dataServiceHost->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_FILTER)) || !is_null($dataServiceHost->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_EXPAND)) || !is_null($dataServiceHost->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_INLINECOUNT)) || !is_null($dataServiceHost->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_ORDERBY)) || !is_null($dataServiceHost->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_SELECT)) || !is_null($dataServiceHost->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_SKIP)) || !is_null($dataServiceHost->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_SKIPTOKEN)) || !is_null($dataServiceHost->getQueryStringItem(ODataConstants::HTTPQUERY_STRING_TOP))) { ODataException::createBadRequestError(Messages::queryProcessorNoQueryOptionsApplicable()); } }
/** * Builds the string corresponding to query parameters for top level results * (result set identified by the resource path) to be put in next page link. * * @return string/NULL string representing the query parameters in the URI * query parameter format, NULL if there * is no query parameters * required for the next link of top level result set. */ protected function getNextPageLinkQueryParametersForRootResourceSet() { $queryParameterString = null; foreach (array(ODataConstants::HTTPQUERY_STRING_FILTER, ODataConstants::HTTPQUERY_STRING_EXPAND, ODataConstants::HTTPQUERY_STRING_ORDERBY, ODataConstants::HTTPQUERY_STRING_INLINECOUNT, ODataConstants::HTTPQUERY_STRING_SELECT) as $queryOption) { $value = $this->dataService->getHost()->getQueryStringItem($queryOption); if (!is_null($value)) { if (!is_null($queryParameterString)) { $queryParameterString = $queryParameterString . '&'; } $queryParameterString .= $queryOption . '=' . $value; } } $topCountValue = $this->requestDescription->getTopOptionCount(); if (!is_null($topCountValue)) { $remainingCount = $topCountValue - $this->requestDescription->getTopCount(); if (!is_null($queryParameterString)) { $queryParameterString .= '&'; } $queryParameterString .= ODataConstants::HTTPQUERY_STRING_TOP . '=' . $remainingCount; } if (!is_null($queryParameterString)) { $queryParameterString .= '&'; } return $queryParameterString; }
/** * Common function to handle exceptions in the data service. * * @param Exception $exception exception occured * @param DataService &$dataService dataservice * * @return nothing */ public static function handleException($exception, DataService &$dataService) { $acceptTypesText = $dataService->getHost()->getRequestAccept(); $responseContentType = null; try { $responseContentType = HttpProcessUtility::selectMimeType($acceptTypesText, array(ODataConstants::MIME_APPLICATION_XML, ODataConstants::MIME_APPLICATION_JSON)); } catch (HttpHeaderFailure $exception) { $exception = new ODataException($exception->getMessage(), $exception->getStatusCode()); } catch (\Exception $exception) { // Never come here } if (is_null($responseContentType)) { $responseContentType = ODataConstants::MIME_APPLICATION_XML; } if (!$exception instanceof ODataException) { $exception = new ODataException($exception->getMessage(), HttpStatus::CODE_INTERNAL_SERVER_ERROR); } $dataService->getHost()->setResponseVersion(ODataConstants::DATASERVICEVERSION_1_DOT_0 . ';'); // At this point all kind of exceptions will be converted //to 'ODataException' if ($exception->getStatusCode() == HttpStatus::CODE_NOT_MODIFIED) { $dataService->getHost()->setResponseStatusCode(HttpStatus::CODE_NOT_MODIFIED); } else { $dataService->getHost()->setResponseStatusCode($exception->getStatusCode()); $dataService->getHost()->setResponseContentType($responseContentType); $responseBody = null; if (strcasecmp($responseContentType, ODataConstants::MIME_APPLICATION_XML) == 0) { $responseBody = AtomODataWriter::serializeException($exception, true); } else { $responseBody = JsonODataWriter::serializeException($exception, true); } $dataService->getHost()->getWebOperationContext()->outgoingResponse()->setStream($responseBody); } }
/** * Pushes a segment for the current navigation property being written out. * Note: Refer 'ObjectModelSerializerNotes.txt' for more details about * 'Segment Stack' and this method. * Note: Calls to this method should be balanced with calls to popSegment. * * @param ResourceProperty &$resourceProperty Current navigation property * being written out * * @return true if a segment was pushed, false otherwise * * @throws InvalidOperationException If this function invoked with non-navigation * property instance. */ private function _pushSegmentForNavigationProperty(ResourceProperty &$resourceProperty) { if ($resourceProperty->getTypeKind() == ResourceTypeKind::ENTITY) { $this->assert(!empty($this->_segmentNames), '!is_empty($this->_segmentNames'); $currentResourceSetWrapper = $this->_getCurrentResourceSetWrapper(); $currentResourceType = $currentResourceSetWrapper->getResourceType(); $currentResourceSetWrapper = $this->_dataService->getMetadataQueryProviderWrapper()->getResourceSetWrapperForNavigationProperty($currentResourceSetWrapper, $currentResourceType, $resourceProperty); $this->assert(!is_null($currentResourceSetWrapper), '!null($currentResourceSetWrapper)'); return $this->_pushSegment($resourceProperty->getName(), $currentResourceSetWrapper); } else { throw new InvalidOperationException('pushSegmentForNavigationProperty should not be called with non-entity type'); } }
/** * Gets the response format for the requested resource. * * @param RequestDescription &$requestDescription The request submitted by * client and it's execution * result. * @param UriProcessor &$uriProcessor The reference to the * UriProcessor. * @param DataService &$dataService Reference to the data * service instance * @param string &$responseContentType On Return, this will hold * the response content-type, a null value means the requested resource * is named stream and IDSSP2::getStreamContentType returned null. * * @return ResponseFormat The format in which response needs to be serialized. * * @throws ODataException, HttpHeaderFailure */ public static function getResponseFormat(RequestDescription &$requestDescription, UriProcessor &$uriProcessor, DataService &$dataService, &$responseContentType) { // The Accept request-header field specifies media types which are // acceptable for the response $requestAcceptText = $dataService->getHost()->getRequestAccept(); $responseFormat = ResponseFormat::UNSUPPORTED; $requestTargetKind = $requestDescription->getTargetKind(); if ($requestDescription->isLinkUri()) { $requestTargetKind = RequestTargetKind::LINK; } if ($requestTargetKind == RequestTargetKind::METADATA) { $responseContentType = HttpProcessUtility::selectMimeType($requestAcceptText, array(ODataConstants::MIME_APPLICATION_XML)); if (!is_null($responseContentType)) { $responseFormat = ResponseFormat::METADATA_DOCUMENT; } } else { if ($requestTargetKind == RequestTargetKind::SERVICE_DIRECTORY) { $responseContentType = HttpProcessUtility::selectMimeType($requestAcceptText, array(ODataConstants::MIME_APPLICATION_XML, ODataConstants::MIME_APPLICATION_ATOMSERVICE, ODataConstants::MIME_APPLICATION_JSON)); if (!is_null($responseContentType)) { $responseFormat = self::_getContentFormat($responseContentType); } } else { if ($requestTargetKind == RequestTargetKind::PRIMITIVE_VALUE) { $supportedResponseMimeTypes = array(ODataConstants::MIME_TEXTPLAIN); $responseFormat = ResponseFormat::TEXT; if ($requestDescription->getIdentifier() != '$count') { $projectedProperty = $requestDescription->getProjectedProperty(); self::assert(!is_null($projectedProperty), '!is_null($projectedProperty)'); $type = $projectedProperty->getInstanceType(); self::assert(!is_null($type) && array_search('ODataProducer\\Providers\\Metadata\\Type\\IType', class_implements($type)) !== false, '!is_null($type) && array_search(\'ODataProducer\\Providers\\Metadata\\Type\\IType\', class_implements($type)) !== false'); if ($type instanceof Binary) { $supportedResponseMimeTypes = array(ODataConstants::MIME_APPLICATION_OCTETSTREAM); $responseFormat = ResponseFormat::BINARY; } } $responseContentType = HttpProcessUtility::selectMimeType($requestAcceptText, $supportedResponseMimeTypes); if (is_null($responseContentType)) { $responseFormat = ResponseFormat::UNSUPPORTED; } } else { if ($requestTargetKind == RequestTargetKind::PRIMITIVE || $requestTargetKind == RequestTargetKind::COMPLEX_OBJECT || $requestTargetKind == RequestTargetKind::BAG || $requestTargetKind == RequestTargetKind::LINK) { $responseContentType = HttpProcessUtility::selectMimeType($requestAcceptText, array(ODataConstants::MIME_APPLICATION_XML, ODataConstants::MIME_TEXTXML, ODataConstants::MIME_APPLICATION_JSON)); if (!is_null($responseContentType)) { $responseFormat = self::_getContentFormat($responseContentType); } } else { if ($requestTargetKind == RequestTargetKind::RESOURCE) { $responseContentType = HttpProcessUtility::selectMimeType($requestAcceptText, array(ODataConstants::MIME_APPLICATION_ATOM, ODataConstants::MIME_APPLICATION_JSON)); if (!is_null($responseContentType)) { $responseFormat = self::_getContentFormat($responseContentType); } } else { if ($requestTargetKind == RequestTargetKind::MEDIA_RESOURCE) { $responseFormat = ResponseFormat::BINARY; if ($requestDescription->isNamedStream() || $requestDescription->getTargetResourceType()->isMediaLinkEntry()) { $streamInfo = $requestDescription->getResourceStreamInfo(); //Execute the query as we need media resource instance for //further processing $uriProcessor->execute(); $requestDescription->setExecuted(); // DSSW::getStreamContentType can throw error in 2 cases // 1. If the required stream implementation not found // 2. If IDSSP::getStreamContentType returns NULL for MLE $contentType = $dataService->getStreamProvider()->getStreamContentType($requestDescription->getTargetResult(), $streamInfo); if (!is_null($contentType)) { $responseContentType = HttpProcessUtility::selectMimeType($requestAcceptText, array($contentType)); if (is_null($responseContentType)) { $responseFormat = ResponseFormat::UNSUPPORTED; } } else { // For NamedStream StreamWrapper::getStreamContentType // can return NULL if the requested named stream has not // yet been uploaded. But for an MLE if // IDSSP::getStreamContentType // returns NULL then StreamWrapper will throw error $responseContentType = null; } } else { ODataException::createBadRequestError(Messages::badRequestInvalidUriForMediaResource($dataService->getHost()->getAbsoluteRequestUri()->getUrlAsString())); } } } } } } } if ($responseFormat == ResponseFormat::UNSUPPORTED) { throw new ODataException(Messages::dataServiceExceptionUnsupportedMediaType(), 415); } return $responseFormat; }
/** * This function is used to perform following checking (validation) * for capability negotiation. * (1) Check client request's 'DataServiceVersion' header value is * less than or equal to the minimum version required to intercept * the response * (2) Check client request's 'MaxDataServiceVersion' header value is * less than or equal to the version of protocol required to generate * the response * (3) Check the configured maximum protocol version is less than or equal * to the version of protocol required to generate the response * In addition to these checking, this function is also responsible for * initializing the properties respresenting 'DataServiceVersion' and * 'MaxDataServiceVersion'. * * @param RequestDescription $requestDescription The request description object * @param DataService $dataService The Service to check * * @return void * * @throws ODataException If any of the above 3 check fails. */ public static function checkVersion(RequestDescription $requestDescription, DataService $dataService) { if (is_null($requestDescription->_requestDataServiceVersion)) { $version = $dataService->getHost()->getRequestVersion(); //'DataServiceVersion' header not present in the request, so use //default value as the maximum version number that the server can //interpret. if (is_null($version)) { $knownVersions = $requestDescription::getKnownDataServiceVersions(); $version = $knownVersions[count($knownVersions) - 1]; } else { $version = $requestDescription::_validateAndGetVersion($version, ODataConstants::ODATAVERSIONHEADER); } $requestDescription->_requestDataServiceVersion = $version; } if (is_null($requestDescription->_requestMaxDataServiceVersion)) { $version = $dataService->getHost()->getRequestMaxVersion(); //'MaxDataServiceVersion' header not present in the request, so use //default value as the maximum version number that the server can //interpret. if (is_null($version)) { $knownVersions = $requestDescription::getKnownDataServiceVersions(); $version = $knownVersions[count($knownVersions) - 1]; } else { $version = $requestDescription::_validateAndGetVersion($version, ODataConstants::ODATAMAXVERSIONHEADER); } $requestDescription->_requestMaxDataServiceVersion = $version; } if ($requestDescription->_requestDataServiceVersion->compare($requestDescription->_minimumRequiredClientVersion) < 0) { ODataException::createBadRequestError(Messages::requestDescriptionDataServiceVersionTooLow($requestDescription->_requestDataServiceVersion->toString(), $requestDescription->_minimumRequiredClientVersion->toString())); } if ($requestDescription->_requestMaxDataServiceVersion->compare($requestDescription->_responseDataServiceVersion) < 0) { ODataException::createBadRequestError(Messages::requestDescriptionDataServiceVersionTooLow($requestDescription->_requestMaxDataServiceVersion->toString(), $requestDescription->_responseDataServiceVersion->toString())); } $configuration = $dataService->getServiceConfiguration(); $maxConfiguredProtocolVersion = $configuration->getMaxDataServiceVersionObject(); if ($maxConfiguredProtocolVersion->compare($requestDescription->_responseDataServiceVersion) < 0) { ODataException::createBadRequestError(Messages::requestDescriptionResponseVersionIsBiggerThanProtocolVersion($requestDescription->_responseDataServiceVersion->toString(), $maxConfiguredProtocolVersion->toString())); } }
/** * Check whether implementor modified content type or etag header * if so throw InvalidOperationException. * * @param string $methodName NAme of the method * * @return void * * @throws InvalidOperationException */ private function _verifyContentTypeOrETagModified($methodName) { if ($this->_responseContentType !== $this->_dataService->getHost()->getResponseContentType() || $this->_responseETag !== $this->_dataService->getHost()->getResponseETag()) { throw new InvalidOperationException(Messages::dataServiceStreamProviderWrapperMustNotSetContentTypeAndEtag($methodName)); } }