Example #1
0
        private static ErrorHandler CreateHandler(IDataService service, AstoriaRequestMessage requestMessage, Exception exception, Version defaultResponseVersion)
        {
            Debug.Assert(service != null, "service != null");
            Debug.Assert(service.Configuration != null, "service.Configuration != null");

            string  acceptableContentTypes     = null;
            string  requestAcceptCharsetHeader = null;
            Version responseVersion            = defaultResponseVersion;

            if (requestMessage != null)
            {
                acceptableContentTypes     = requestMessage.GetAcceptableContentTypes();
                requestAcceptCharsetHeader = requestMessage.GetRequestAcceptCharsetHeader();

                try
                {
                    Version maxProtocolVersion = service.Configuration.DataServiceBehavior.MaxProtocolVersion.ToVersion();
                    requestMessage.InitializeRequestVersionHeaders(maxProtocolVersion);
                    responseVersion = VersionUtil.GetResponseVersionForError(requestMessage.GetAcceptableContentTypes(), requestMessage.RequestMaxVersion, maxProtocolVersion);
                }
                catch (Exception e)
                {
                    if (!CommonUtil.IsCatchableExceptionType(e))
                    {
                        throw;
                    }

                    // Ignore exceptions as we should use the default response version.
                }
            }

            return(new ErrorHandler(exception, service.Configuration.UseVerboseErrors, responseVersion, acceptableContentTypes, requestAcceptCharsetHeader));
        }
Example #2
0
        /// <summary>
        /// Create a new instance of ODataMessageWriterSettings for batch requests.
        /// </summary>
        /// <param name="dataService">Data service instance.</param>
        /// <returns>An instance of a message writer with the appropriate settings.</returns>
        internal static MessageWriterBuilder ForBatch(IDataService dataService)
        {
            Uri     serviceUri      = dataService.OperationContext.RequestMessage.AbsoluteServiceUri;
            Version responseVersion = VersionUtil.GetEffectiveMaxResponseVersion(dataService.OperationContext.RequestMessage.RequestMaxVersion, dataService.Configuration.DataServiceBehavior.MaxProtocolVersion.ToVersion());

            MessageWriterBuilder messageWriterBuilder = new MessageWriterBuilder(serviceUri, responseVersion, dataService, dataService.OperationContext.ResponseMessage, null /*model*/);

            // Astoria does not do content negotiation for the top level batch payload at all in V1/V2
            // Hence passing */* as the accept header value by default.

            string contentType = XmlConstants.MimeAny;

            if (dataService.OperationContext.RequestMessage != null &&
                string.CompareOrdinal(
                    XmlConstants.ODataVersion4Dot0,
                    dataService.OperationContext.RequestMessage.GetHeader(XmlConstants.HttpODataVersion)) == 0)
            {
                // For V4, batch request & response payload can be in Json format
                string accept = dataService.OperationContext.RequestMessage.GetHeader(XmlConstants.HttpAccept);

                if (accept != null && accept.StartsWith(XmlConstants.MimeApplicationJson))
                {
                    contentType = accept;
                }
            }
            messageWriterBuilder.WriterSettings.SetContentType(contentType, null /*acceptableCharSets*/);

            return(messageWriterBuilder);
        }
        /// <summary>
        /// Create a new instance of ODataMessageWriterSettings for batch requests.
        /// </summary>
        /// <param name="dataService">Data service instance.</param>
        /// <returns>An instance of a message writer with the appropriate settings.</returns>
        internal static MessageWriterBuilder ForBatch(IDataService dataService)
        {
            Uri     serviceUri      = dataService.OperationContext.RequestMessage.AbsoluteServiceUri;
            Version responseVersion = VersionUtil.GetEffectiveMaxResponseVersion(dataService.OperationContext.RequestMessage.RequestMaxVersion, dataService.Configuration.DataServiceBehavior.MaxProtocolVersion.ToVersion());

            MessageWriterBuilder messageWriterBuilder = new MessageWriterBuilder(serviceUri, responseVersion, dataService, dataService.OperationContext.ResponseMessage, null /*model*/);

            // Astoria does not do content negotiation for the top level batch payload at all in V1/V2
            // Hence passing */* as the accept header value.
            messageWriterBuilder.WriterSettings.SetContentType(XmlConstants.MimeAny, null /*acceptableCharSets*/);

            return(messageWriterBuilder);
        }
        /// <summary>
        /// Tries to create a type name segment if the given identifier refers to a known type.
        /// </summary>
        /// <param name="previous">previous segment info.</param>
        /// <param name="segment">The segment being interpreted.</param>
        /// <param name="typeNameSegment">The type name segment, if one was created.</param>
        /// <returns>Whether or not a type segment was created for the identifier.</returns>
        private bool TryCreateTypeNameSegment(SegmentInfo previous, ODataPathSegment segment, out SegmentInfo typeNameSegment)
        {
            var typeSegment = segment as TypeSegment;

            if (typeSegment == null || previous.TargetResourceSet == null)
            {
                typeNameSegment = null;
                return(false);
            }

            ResourceType targetResourceType = MetadataProviderUtils.GetResourceType(typeSegment);

            // if the new type segment prevents any results from possibly being returned, then short-circuit and throw a 404.
            ResourceType previousResourceType = previous.TargetResourceType;

            Debug.Assert(previousResourceType != null, "previous.TargetResourceType != null");
            if (!targetResourceType.IsAssignableFrom(previousResourceType) && !previousResourceType.IsAssignableFrom(targetResourceType))
            {
                throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_InvalidTypeIdentifier_UnrelatedType(targetResourceType.FullName, previousResourceType.FullName));
            }

            // Since we allow derived navigation properties or named streams in V1/V2, the server will generate edit links and navigation links with type segment in it.
            // Hence we need to be able to process type segment in the request even when the server MPV is set to V1/V2. But we do not want to expose new functionality
            // like filtering collections based on type, etc on V1/V2 servers. Hence only checking for MPV to be v3 or greater if the previous segment is a collection
            if (!previous.SingleResult)
            {
                VersionUtil.CheckMaxProtocolVersion(VersionUtil.Version4Dot0, this.maxProtocolVersion);
            }

            typeNameSegment = new SegmentInfo
            {
                Identifier         = targetResourceType.FullName,
                Operation          = previous.Operation,
                TargetKind         = previous.TargetKind,
                TargetSource       = previous.TargetSource,
                TargetResourceType = targetResourceType,
                SingleResult       = previous.SingleResult,
                TargetResourceSet  = previous.TargetResourceSet,
                ProjectedProperty  = previous.ProjectedProperty,
                Key = previous.Key,
                RequestExpression       = previous.RequestExpression,
                RequestEnumerable       = previous.RequestEnumerable,
                IsTypeIdentifierSegment = true
            };

            return(true);
        }
Example #5
0
        /// <summary>
        /// update the request version header, if it is not specified.
        /// </summary>
        /// <param name="maxProtocolVersion">protocol version as specified in the config.</param>
        internal void InitializeRequestVersionHeaders(Version maxProtocolVersion)
        {
            if (this.requestVersionHeadersInitialized)
            {
                return;
            }

            this.requestVersionHeadersInitialized = true;

            Debug.Assert(this.requestVersion == null, "this.requestVersion == null");
            Debug.Assert(this.RequestMaxVersion == null, "this.RequestMaxVersion == null");

            Version maxRequestVersionAllowed = GetMaxRequestVersionAllowed(maxProtocolVersion);

            // read the request version headers from the underlying host
            this.requestVersionString = this.host.RequestVersion;
            this.RequestVersion       = ValidateVersionHeader(XmlConstants.HttpODataVersion, this.requestVersionString);
            this.RequestMaxVersion    = ValidateVersionHeader(XmlConstants.HttpODataMaxVersion, this.host.RequestMaxVersion);

            // If the request version is not specified.
            if (this.requestVersion == null)
            {
                // In request headers, if the OData-Version header is not specified and OData-MaxVersion is specified,
                // we should ideally set the OData-Version header to whatever the value was specified in OData-MaxVersion
                // header has.
                if (this.RequestMaxVersion != null)
                {
                    // set the OData-Version to minimum of OData-MaxVersion and ProtocolVersion.
                    this.RequestVersion = (this.RequestMaxVersion < maxProtocolVersion) ? this.RequestMaxVersion : maxProtocolVersion;
                }
                else
                {
                    // If both request DSV and request MaxDSV is not specified, then set the request DSV to the MPV
                    this.RequestVersion = maxProtocolVersion;
                }

                this.requestVersionString = this.RequestVersion.ToString(2);
            }
            else
            {
                if (this.RequestVersion > maxRequestVersionAllowed)
                {
                    throw DataServiceException.CreateBadRequestError(Strings.DataService_RequestVersionMustBeLessThanMPV(this.RequestVersion, maxProtocolVersion));
                }

                // Verify that the request DSV is a known version number.
                if (!VersionUtil.IsKnownRequestVersion(this.RequestVersion))
                {
                    string message = Strings.DataService_InvalidRequestVersion(
                        this.RequestVersion.ToString(2),
                        KnownODataVersionsToString(maxRequestVersionAllowed));
                    throw DataServiceException.CreateBadRequestError(message);
                }
            }

            // Initialize the request OData-MaxVersion if not specified.
            if (this.RequestMaxVersion == null)
            {
                this.RequestMaxVersion = maxProtocolVersion;
            }
            else if (this.RequestMaxVersion < VersionUtil.DataServiceDefaultResponseVersion)
            {
                // We need to make sure the MaxDSV is at least 1.0. This was the V1 behavior.
                // Verified that this was checked both in batch and non-batch cases.
                string message = Strings.DataService_MaxDSVTooLow(
                    this.RequestMaxVersion.ToString(2),
                    VersionUtil.DataServiceDefaultResponseVersion.Major,
                    VersionUtil.DataServiceDefaultResponseVersion.Minor);
                throw DataServiceException.CreateBadRequestError(message);
            }
        }
Example #6
0
        /// <summary>Initializes a new <see cref="ResponseBodyWriter"/> that can write the body of a response.</summary>
        /// <param name="service">Service for the request being processed.</param>
        /// <param name="queryResults">Enumerator for results.</param>
        /// <param name="requestDescription">Description of request made to the system.</param>
        /// <param name="actualResponseMessageWhoseHeadersMayBeOverridden">IODataResponseMessage instance for the response.</param>
        internal ResponseBodyWriter(
            IDataService service,
            QueryResultInfo queryResults,
            RequestDescription requestDescription,
            IODataResponseMessage actualResponseMessageWhoseHeadersMayBeOverridden)
        {
            Debug.Assert(service != null, "service != null");
            Debug.Assert(requestDescription != null, "requestDescription != null");
            Debug.Assert(actualResponseMessageWhoseHeadersMayBeOverridden != null, "actualResponseMessageWhoseHeadersMayBeOverridden != null");

            this.service            = service;
            this.queryResults       = queryResults;
            this.requestDescription = requestDescription;
            this.actualResponseMessageWhoseHeadersMayBeOverridden = actualResponseMessageWhoseHeadersMayBeOverridden;

            Debug.Assert(this.PayloadKind != ODataPayloadKind.Unsupported, "payloadKind != ODataPayloadKind.Unsupported");

            this.encoding = ContentTypeUtil.EncodingFromAcceptCharset(this.service.OperationContext.RequestMessage.GetRequestAcceptCharsetHeader());

            if (this.PayloadKind == ODataPayloadKind.Entry ||
                this.PayloadKind == ODataPayloadKind.Feed ||
                this.PayloadKind == ODataPayloadKind.Property ||
                this.PayloadKind == ODataPayloadKind.Collection ||
                this.PayloadKind == ODataPayloadKind.EntityReferenceLink ||
                this.PayloadKind == ODataPayloadKind.EntityReferenceLinks ||
                this.PayloadKind == ODataPayloadKind.Error ||
                this.PayloadKind == ODataPayloadKind.ServiceDocument ||
                this.PayloadKind == ODataPayloadKind.Parameter)
            {
                AstoriaRequestMessage requestMessage = service.OperationContext.RequestMessage;
                IODataResponseMessage responseMessageOnOperationContext = service.OperationContext.ResponseMessage;

                Version effectiveMaxResponseVersion = VersionUtil.GetEffectiveMaxResponseVersion(service.Configuration.DataServiceBehavior.MaxProtocolVersion.ToVersion(), requestMessage.RequestMaxVersion);
                bool    isEntityOrFeed = this.PayloadKind == ODataPayloadKind.Entry || this.PayloadKind == ODataPayloadKind.Feed;
                if (ContentTypeUtil.IsResponseMediaTypeJsonLight(requestMessage.GetAcceptableContentTypes(), isEntityOrFeed, effectiveMaxResponseVersion))
                {
                    // If JSON light 'wins', then bump the version to V3.
                    requestDescription.VerifyAndRaiseResponseVersion(VersionUtil.Version4Dot0, service);
                    responseMessageOnOperationContext.SetHeader(XmlConstants.HttpODataVersion, XmlConstants.ODataVersion4Dot0 + ";");
                }
            }

            if (this.requestDescription.TargetKind == RequestTargetKind.MediaResource)
            {
                Debug.Assert(this.PayloadKind == ODataPayloadKind.BinaryValue, "payloadKind == ODataPayloadKind.BinaryValue");

                // Note that GetReadStream will set the ResponseETag before it returns
                this.mediaResourceStream = service.StreamProvider.GetReadStream(
                    this.queryResults.Current,
                    this.requestDescription.StreamProperty,
                    this.service.OperationContext);
            }
            else if (this.PayloadKind != ODataPayloadKind.BinaryValue)
            {
                IEdmModel model;
                if (this.PayloadKind == ODataPayloadKind.MetadataDocument)
                {
                    model = MetadataSerializer.PrepareModelForSerialization(this.service.Provider, this.service.Configuration);
                }
                else
                {
                    model = this.GetModelFromService();
                }

                // Create the message writer using which the response needs to be written.
                this.messageWriterBuilder = MessageWriterBuilder.ForNormalRequest(
                    this.service,
                    this.requestDescription,
                    this.actualResponseMessageWhoseHeadersMayBeOverridden,
                    model);

                this.messageWriter = this.messageWriterBuilder.CreateWriter();

                try
                {
                    // Make sure all the headers are written before the method returns.
                    this.contentFormat = ODataUtils.SetHeadersForPayload(this.messageWriter, this.PayloadKind);
                }
                catch (ODataContentTypeException contentTypeException)
                {
                    throw new DataServiceException(415, null, Strings.DataServiceException_UnsupportedMediaType, null, contentTypeException);
                }

                Debug.Assert(requestDescription.ResponseFormat != null, "Response format should already have been determined.");
                Debug.Assert(ReferenceEquals(this.contentFormat, requestDescription.ResponseFormat.Format), "Response format in request description did not match format when writing.");

                if (this.PayloadKind == ODataPayloadKind.Value && !String.IsNullOrEmpty(this.requestDescription.MimeType))
                {
                    this.actualResponseMessageWhoseHeadersMayBeOverridden.SetHeader(XmlConstants.HttpContentType, this.requestDescription.MimeType);
                }

                // EPM is currently removed, but this doesn't seem to be used for EPM only. The old comment was saying:
                // In astoria, there is a bug in V1/V2 that while computing response version, we did not take
                // epm into account. Hence while creating the writer, we need to pass the RequestDescription.ActualResponseVersion
                // so that ODataLib can do the correct payload validation. But we need to write the response version without
                // the epm into the response headers because of backward-compat issue. Hence over-writing the response version
                // header with the wrong version value.
                string responseVersion = this.requestDescription.ResponseVersion.ToString() + ";";
                this.actualResponseMessageWhoseHeadersMayBeOverridden.SetHeader(XmlConstants.HttpODataVersion, responseVersion);
            }
        }