Example #1
0
        /// <summary>
        /// Writes the error with fallback logic for XML cases where the writer is in an error state and a new writer must be created.
        /// </summary>
        /// <param name="messageWriter">The message writer.</param>
        /// <param name="encoding">The encoding to use for the error if we have to fallback.</param>
        /// <param name="responseStream">The response stream to write to in the fallback case.</param>
        /// <param name="args">The args for the error.</param>
        /// <param name="error">The error to write.</param>
        /// <param name="messageWriterBuilder">MessageWriterBuilder to use if a new ODataMessageWriter needs to be constructed.</param>
        private static void WriteErrorWithFallbackForXml(ODataMessageWriter messageWriter, Encoding encoding, Stream responseStream, HandleExceptionArgs args, ODataError error, MessageWriterBuilder messageWriterBuilder)
        {
            Debug.Assert(args != null, "args != null");
#if DEBUG
            Debug.Assert(args.ProcessExceptionWasCalled, "ProcessException was not called by the time we tried to serialze this error message with ODataLib.");
#endif

            if (messageWriter != null)
            {
                try
                {
                    // If the XmlWriter inside the ODataMessageWriter had entered Error state, ODataMessageWriter.WriteError would throw an InvalidOperationException
                    // when we try to write to it. Note that XmlWriter doesn't always throw an XmlException when it enters Error state.
                    // The right thing to do is we don't write any more because at this point we don't know what's been written to the underlying
                    // stream. However we still should flush the writer to make sure that all the content that was written but is sitting in the buffers actually appears
                    // in the stream before writing the instream error. Otherwise the buffer will be flushed when disposing the writer later and we would end up with
                    // either content written after the instream error (this would also result in having the Xml declaration in the middle of the payload -
                    // [Astoria-ODataLib-Integration] In-stream errors due to XmlExceptions are written out backwards (error before partial valid payload)) or,
                    // hypothetically, the instream error in the middle of the other content that was already partially written. For example we can end up with a payload that
                    // looks like <element attr="val<m:error... The XmlReader would not be able to parse the error payload in this case. Disposing the writer will flush the buffer.
                    // It is fine to do it since the writer is not usable at this point anyways. Also note that the writer will be disposed more than once (e.g. in finally block
                    // in ResponseBodySerializer) but only the first call will have any effect.
                    // However since in the versions we shipped we always create a new XmlWriter to serialize the error payload when the existing
                    // one is in error state, we will continue to do the same to avoid introducing any breaking change here.
                    messageWriter.WriteError(error, args.UseVerboseErrors);
                }
                catch (ODataException e)
                {
                    // Yikes, ODataLib threw while writing the error. This tends to happen if the service author did something invalid during custom
                    // error handling, such as add an custom instance annotation to the error payload. In this dire case, we treat it almost like
                    // an in-stream error, and abort the previous writing. We write out the new error. Note that this will produce an invalid payload like
                    // the situation noted above with XmlWriter errors.
                    WebUtil.Dispose(messageWriter);
                    messageWriterBuilder.SetMessageForErrorInError();
                    var        newErrorWriter = messageWriterBuilder.CreateWriter();
                    ODataError errorWhileWritingOtherError = new ODataError()
                    {
                        ErrorCode  = "500",
                        InnerError = new ODataInnerError(e),
                        Message    = Strings.ErrorHandler_ErrorWhileWritingError
                    };

                    newErrorWriter.WriteError(errorWhileWritingOtherError, args.UseVerboseErrors);
                }
                catch (InvalidOperationException)
                {
                    Debug.Assert(ContentTypeUtil.IsNotJson(args.ResponseContentType), "Should never get here for JSON responses");
                    WebUtil.Dispose(messageWriter);

                    // if either an InvalidOperationException was encountered (see comment above) or the message writer was null, write the error out manually.
                    Debug.Assert(responseStream != null, "responseStream != null");
                    using (XmlWriter xmlWriter = XmlWriter.Create(responseStream, XmlUtil.CreateXmlWriterSettings(encoding)))
                    {
                        ErrorUtils.WriteXmlError(xmlWriter, error, args.UseVerboseErrors, MaxInnerErrorDepth);
                    }
                }
            }
        }
        /// <summary>
        /// Validates that we can read or write a message with the given content-type value.
        /// </summary>
        /// <param name="contentType">The content-type value in question.</param>
        private static void ValidateContentType(string contentType)
        {
            if (string.IsNullOrEmpty(contentType))
            {
                return;
            }

            // Ideally ODataLib should have a public API to get the ODataFormat from the content-type header.
            // Unfortunately since that's not available, we will process the content-type value to determine if the format is JSON Light.
            string mime;

            ContentTypeUtil.ReadContentType(contentType, out mime);
        }
Example #3
0
        /// <summary>Writes multiple top-level elements, possibly none.</summary>
        /// <param name="expanded">Expanded results for elements.</param>
        /// <param name="elements">Enumerator for elements to write.</param>
        protected override void WriteTopLevelElements(IExpandedResult expanded, QueryResultInfo elements)
        {
            Debug.Assert(
                !this.RequestDescription.IsSingleResult,
                "!this.RequestDescription.IsSingleResult -- otherwise WriteTopLevelElement should have been called");

            if (this.RequestDescription.LinkUri)
            {
                bool needPop = this.PushSegmentForRoot();
                this.WriteLinkCollection(elements);
                this.PopSegmentName(needPop);
            }
            else
            {
                MetadataProviderEdmModel model     = this.Service.Provider.GetMetadataProviderEdmModel();
                OperationWrapper         operation = this.RequestDescription.LastSegmentInfo.Operation;

                IEdmOperation edmOperation = model.GetRelatedOperation(operation);
                Debug.Assert(edmOperation != null, "edmOperation != null");

                IEdmCollectionTypeReference collectionType = (IEdmCollectionTypeReference)edmOperation.ReturnType;
                bool isJsonLightResponse = ContentTypeUtil.IsResponseMediaTypeJsonLight(this.Service, /*isEntryOrFeed*/ false);
                this.collectionWriter = this.writer.CreateODataCollectionWriter(isJsonLightResponse ? null : collectionType.ElementType());

                ODataCollectionStart collectionStart = new ODataCollectionStart {
                    Name = this.ComputeContainerName()
                };
                collectionStart.SetSerializationInfo(new ODataCollectionStartSerializationInfo {
                    CollectionTypeName = collectionType.FullName()
                });

                this.collectionWriter.WriteStart(collectionStart);
                while (elements.HasMoved)
                {
                    object       element      = elements.Current;
                    ResourceType resourceType = element == null ?
                                                this.RequestDescription.TargetResourceType : WebUtil.GetResourceType(this.Provider, element);
                    if (resourceType == null)
                    {
                        throw new InvalidOperationException(Microsoft.OData.Service.Strings.Serializer_UnsupportedTopLevelType(element.GetType()));
                    }

                    this.collectionWriter.WriteItem(this.GetPropertyValue(XmlConstants.XmlCollectionItemElementName, resourceType, element, false /*openProperty*/).FromODataValue());
                    elements.MoveNext();
                }

                this.collectionWriter.WriteEnd();
                this.collectionWriter.Flush();
            }
        }
Example #4
0
        /// <summary>
        /// Initializes a new instance of <see cref="ODataMessageReaderDeserializer"/>.
        /// </summary>
        /// <param name="update">true if we're reading an update operation; false if not.</param>
        /// <param name="dataService">Data service for which the deserializer will act.</param>
        /// <param name="tracker">Tracker to use for modifications.</param>
        /// <param name="requestDescription">The request description to use.</param>
        /// <param name="enableODataServerBehavior">If true, the message reader settings will use the ODataServer behavior;
        /// if false, the message reader settings will use the default behavior.</param>
        internal ODataMessageReaderDeserializer(bool update, IDataService dataService, UpdateTracker tracker, RequestDescription requestDescription, bool enableODataServerBehavior)
            : base(update, dataService, tracker, requestDescription)
        {
            AstoriaRequestMessage requestMessage = dataService.OperationContext.RequestMessage;

            // WCF DS needs to treat content type */* as ATOM payload, so check for it here and override the content type header
            if (ContentTypeUtil.CompareMimeType(requestMessage.ContentType, XmlConstants.MimeAny))
            {
                requestMessage.ContentType = XmlConstants.MimeApplicationAtom;
            }

            this.messageReader = new ODataMessageReader(
                requestMessage,
                WebUtil.CreateMessageReaderSettings(dataService, enableODataServerBehavior),
                dataService.Provider.GetMetadataProviderEdmModel());
        }
Example #5
0
        /// <summary>
        /// Gets the response version for an error payload.
        /// </summary>
        /// <param name="acceptableContentTypes">A comma-separated list of client-supported MIME Accept types.</param>
        /// <param name="requestMaxVersion">The OData-MaxVersion of the request.</param>
        /// <param name="maxProtocolVersion">The max protocol version as specified in the config.</param>
        /// <returns>The response version to be used for an error payload.</returns>
        /// <remarks>
        /// This function is specific to exceptions. For V1 and V2, we will still return
        /// RequestDescription.DataServiceDefaultResponseVersion. This helps avoid breaking changes. For V3, we return
        /// what is in the request header, provided the version is valid.
        /// </remarks>
        internal static Version GetResponseVersionForError(string acceptableContentTypes, Version requestMaxVersion, Version maxProtocolVersion)
        {
            Debug.Assert(maxProtocolVersion != null, "maxProtocolVersion != null");

            Version responseVersion = DataServiceDefaultResponseVersion;

            Version effectiveMaxResponseVersion = GetEffectiveMaxResponseVersion(maxProtocolVersion, requestMaxVersion);

            if (ContentTypeUtil.IsResponseMediaTypeJsonLight(acceptableContentTypes, false /*entityTarget*/, effectiveMaxResponseVersion))
            {
                Debug.Assert(effectiveMaxResponseVersion >= Version4Dot0, "effectiveMaxResponseVersion should be at least Version3Dot0 to match JSON Light.");
                responseVersion = Version4Dot0;
            }

            return(responseVersion);
        }
Example #6
0
        /// <summary>
        /// Gets the encoding for error serialization based on the accept charset header.
        /// </summary>
        /// <param name="requestAcceptCharsetHeader">The request accept charset header.</param>
        /// <returns>The encoding to use.</returns>
        private static Encoding GetEncodingForError(string requestAcceptCharsetHeader)
        {
            Encoding encoding = null;

            if (requestAcceptCharsetHeader != null)
            {
                try
                {
                    encoding = ContentTypeUtil.EncodingFromAcceptCharset(requestAcceptCharsetHeader);
                }
                catch (DataServiceException)
                {
                    // Ignore formatting erros in Accept-Charset and rely on text.
                }
            }

            return(encoding ?? ContentTypeUtil.FallbackEncoding);
        }
Example #7
0
        /// <summary>
        /// Gets the content type for error serialization based on the accept header and version.
        /// </summary>
        /// <param name="requestAcceptHeader">The accept header value.</param>
        /// <param name="responseVersion">The response version.</param>
        /// <returns>The content type to use for the error response.</returns>
        private static string GetErrorResponseContentType(string requestAcceptHeader, Version responseVersion)
        {
            string contentType = null;

            if (requestAcceptHeader != null)
            {
                try
                {
                    contentType = ContentTypeUtil.SelectResponseMediaType(requestAcceptHeader, false /*entityTarget*/, responseVersion);
                }
                catch (DataServiceException)
                {
                    // Ignore formatting errors in Accept and rely on text.
                }
            }

            // TODO: change fallback type to JSON
            return(contentType ?? XmlConstants.MimeApplicationXml);
        }
Example #8
0
        /// <summary>
        /// Validates that we can read or write a message with the given content-type value.
        /// </summary>
        /// <param name="contentType">The content-type value in question.</param>
        /// <param name="isParameterPayload">true if the writer is intended to for a parameter payload, false otherwise.</param>
        private void ValidateContentType(string contentType, bool isParameterPayload)
        {
            if (string.IsNullOrEmpty(contentType))
            {
                return;
            }

            // Ideally ODataLib should have a public API to get the ODataFormat from the content-type header.
            // Unfortunately since that's not available, we will process the content-type value to determine if the format is JSON Light.
            string mime;

            ContentTypeUtil.MediaParameter[] parameters = ContentTypeUtil.ReadContentType(contentType, out mime);

            if (MimeApplicationJson.Equals(mime, StringComparison.OrdinalIgnoreCase))
            {
                // If the MDSV is < V3, application/json means JSON Verbose. Otherwise application/json;odata=verbose is JSON Verbose.
                if (this.context.MaxProtocolVersion < DataServiceProtocolVersion.V3 ||
                    parameters != null &&
                    parameters.Any(p => p.Name.Equals(XmlConstants.MimeODataParameterName, StringComparison.OrdinalIgnoreCase) &&
                                   p.Value.Equals(MimeODataParameterVerboseValue, StringComparison.OrdinalIgnoreCase)))
                {
                    // Parameter payloads do not have an Atom representation.
                    // By default we use JSON Verbose except for UseJson() is called for JSON Light.
                    if (!isParameterPayload)
                    {
                        ThrowNotSupportedExceptionForJsonVerbose(contentType);
                    }

                    Debug.Assert(
                        this.context.MaxProtocolVersion >= DataServiceProtocolVersion.V3,
                        "DataServiceVersion must be V3 or higher for parameter payloads.");
                }
                else
                {
                    // If the response is in JSON Light, a service model is required to read or write it.
                    if (this.ServiceModel == null)
                    {
                        ThrowInvalidOperationExceptionForJsonLightWithoutModel();
                    }
                }
            }
        }
        /// <summary>
        /// Ensures that the media type has been parsed and that the parameters list has been populated.
        /// This is done lazily to avoid parsing them if GetParameterValue is never called.
        /// </summary>
        private void PopulateMediaTypeParameters()
        {
            if (this.mediaTypeParameters != null)
            {
                return;
            }

            if (this.rawMediaType != null)
            {
                string   mime;
                Encoding encoding;
                this.mediaTypeParameters = ContentTypeUtil.ReadContentType(this.rawMediaType, out mime, out encoding);
            }

            // if we had no media type or it had no parameters, create an empty list
            if (this.mediaTypeParameters == null)
            {
                this.mediaTypeParameters = new ContentTypeUtil.MediaParameter[0];
            }
        }
Example #10
0
        /// <summary>
        /// Creates an ODataWriter for writing an entry or a feed.
        /// </summary>
        /// <param name="forFeed">true when writing a feed; false when writing an entry.</param>
        /// <returns>The ODataWriter to use for writing the feed or entry.</returns>
        private DataServiceODataWriter CreateODataWriter(bool forFeed)
        {
            IEdmModel model = this.Service.Provider.GetMetadataProviderEdmModel();

            Debug.Assert(model != null, "model != null");

            IEdmEntitySet  entitySet  = null;
            IEdmEntityType entityType = null;

            if (!ContentTypeUtil.IsResponseMediaTypeJsonLight(this.Service, /*isEntryOrFeed*/ true))
            {
                entitySet  = WebUtil.GetEntitySet(this.Service.Provider, model, this.RequestDescription.TargetResourceSet);
                entityType = (IEdmEntityType)model.FindType(this.RequestDescription.TargetResourceType.FullName);
            }

            ODataWriter odataWriter = forFeed
                ? this.messageWriter.CreateODataFeedWriter(entitySet, entityType)
                : this.messageWriter.CreateODataEntryWriter(entitySet, entityType);

            return(this.Service.CreateODataWriterWrapper(odataWriter));
        }
        /// <summary>
        /// Validates that we can read or write a message with the given content-type value.
        /// </summary>
        /// <param name="contentType">The content-type value in question.</param>
        /// <param name="isParameterPayload">true if the writer is intended to for a parameter payload, false otherwise.</param>
        /// <param name="isResponse">true if content-type header value is from response, false otherwise.</param>
        private void ValidateContentType(string contentType, bool isParameterPayload, bool isResponse)
        {
            if (string.IsNullOrEmpty(contentType))
            {
                return;
            }

            // Ideally ODataLib should have a public API to get the ODataFormat from the content-type header.
            // Unfortunately since that's not available, we will process the content-type value to determine if the format is JSON Light.
            string mime;

            ContentTypeUtil.ReadContentType(contentType, out mime);

            if (MimeApplicationJson.Equals(mime, StringComparison.OrdinalIgnoreCase))
            {
                if (isResponse && this.UsingAtom)
                {
                    ThrowInvalidOperationExceptionForJsonLightWithoutModel();
                }
            }
        }
Example #12
0
        /// <summary>
        /// Gets the EDM model from the service.
        /// </summary>
        /// <returns>The EDM model or null.</returns>
        private IEdmModel GetModelFromService()
        {
            Debug.Assert(this.service != null, "this.service != null");

            // DEVNOTE: Its unclear why this check for OperationContext being non-null is needed,
            // or what it has to do with the model. It was refactored from another place, and more
            // investigation is needed.
            if (this.service.OperationContext != null)
            {
                Debug.Assert(this.requestDescription != null, "this.requestDescription != null");
                bool isEntryOrFeed = this.requestDescription.TargetKind == RequestTargetKind.Resource;
                if (!ContentTypeUtil.IsResponseMediaTypeJsonLight(this.service, isEntryOrFeed))
                {
                    Debug.Assert(this.service.Provider != null, "this.service.Provider != null");
                    MetadataProviderEdmModel metadataProviderEdmModel = this.service.Provider.GetMetadataProviderEdmModel();
                    Debug.Assert(metadataProviderEdmModel.Mode == MetadataProviderEdmModelMode.Serialization, "Model expected to be in serialization mode.");
                    return(metadataProviderEdmModel);
                }
            }

            return(null);
        }
Example #13
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);
            }
        }
Example #14
0
 public void UnqualifiedJsonShouldBeConsideredJsonLightIfMaxVersionIsGreaterThan3()
 {
     ContentTypeUtil.IsResponseMediaTypeJsonLight("application/json", true, new Version(5, 0)).Should().BeTrue();
 }
Example #15
0
        public void HttpProcessUtilityReadMediaTypeTest()
        {
            MediaTypeTest[] tests = new MediaTypeTest[]
            {
                new MediaTypeTest()
                {
                    InputText = "", ExceptionExpected = true
                },
                new MediaTypeTest()
                {
                    InputText = "text", ExceptionExpected = true
                },
                new MediaTypeTest()
                {
                    InputText = "text/", ExceptionExpected = true
                },
                new MediaTypeTest()
                {
                    InputText = "text /plain", ExceptionExpected = true
                },
                new MediaTypeTest()
                {
                    InputText = "text/ plain", ExceptionExpected = true
                },
                new MediaTypeTest()
                {
                    InputText = "text/plain a=b", ExceptionExpected = true
                },
                new MediaTypeTest()
                {
                    InputText = "text/plain;a", ExceptionExpected = true
                },
                new MediaTypeTest()
                {
                    InputText = "text/plain;a =b", ExceptionExpected = true
                },
                new MediaTypeTest()
                {
                    InputText = "text/plain;a= b", ExceptionExpected = true
                },
                new MediaTypeTest()
                {
                    InputText = "text/plain;;", ExceptionExpected = true
                },
                new MediaTypeTest()
                {
                    InputText = "text/plain;\"a\"=b;", ExceptionExpected = true
                },
                new MediaTypeTest()
                {
                    InputText = "text/plain;a=b=c; cc=dd ", ExceptionExpected = true
                },
            };

            foreach (var test in tests)
            {
                string mimeType   = null;
                Action testAction = () => ContentTypeUtil.ReadContentType(test.InputText, out mimeType);
                testAction.ShouldThrow <Exception>();
            }
        }
Example #16
0
 public void JsonWithNoMetadataShouldBeConsideredJson()
 {
     ContentTypeUtil.IsNotJson("application/json").Should().BeFalse();
 }
Example #17
0
        /// <summary>
        /// process the batch response
        /// </summary>
        /// <returns>an instance of the DataServiceResponse, containing individual operation responses for this batch request.</returns>
        private DataServiceResponse HandleBatchResponse()
        {
            bool batchMessageReaderOwned = true;

            try
            {
                if ((this.batchResponseMessage == null) || (this.batchResponseMessage.StatusCode == (int)HttpStatusCode.NoContent))
                {   // we always expect a response to our batch POST request
                    throw Error.InvalidOperation(Strings.Batch_ExpectedResponse(1));
                }

                Func <Stream> getResponseStream = () => this.ResponseStream;

                // We are not going to use the responseVersion returned from this call, as the $batch request itself doesn't apply versioning
                // of the responses on the root level. The responses are versioned on the part level. (Note that the version on the $batch level
                // is actually used to version the batch itself, but we for now we only recognize a single version so to keep it backward compatible
                // we don't check this here. Also note that the HandleResponse method will verify that we can support the version, that is it's
                // lower than the highest version we understand).
                Version responseVersion;
                BaseSaveResult.HandleResponse(
                    this.RequestInfo,
                    (HttpStatusCode)this.batchResponseMessage.StatusCode,               // statusCode
                    this.batchResponseMessage.GetHeader(XmlConstants.HttpODataVersion), // responseVersion
                    getResponseStream,                                                  // getResponseStream
                    true,                                                               // throwOnFailure
                    out responseVersion);

                if (this.ResponseStream == null)
                {
                    Error.ThrowBatchExpectedResponse(InternalError.NullResponseStream);
                }

                // Create the message and the message reader.
                this.batchResponseMessage = new HttpWebResponseMessage(new HeaderCollection(this.batchResponseMessage), this.batchResponseMessage.StatusCode, getResponseStream);
                ODataMessageReaderSettings messageReaderSettings = this.RequestInfo.GetDeserializationInfo(/*mergeOption*/ null).ReadHelper.CreateSettings();

                // No need to pass in any model to the batch reader.
                this.batchMessageReader = new ODataMessageReader(this.batchResponseMessage, messageReaderSettings);
                ODataBatchReader batchReader;
                try
                {
                    batchReader = this.batchMessageReader.CreateODataBatchReader();
                }
                catch (ODataContentTypeException contentTypeException)
                {
                    string    mime;
                    Encoding  encoding;
                    Exception inner = contentTypeException;
                    ContentTypeUtil.ReadContentType(this.batchResponseMessage.GetHeader(XmlConstants.HttpContentType), out mime, out encoding);
                    if (String.Equals(XmlConstants.MimeTextPlain, mime))
                    {
                        inner = GetResponseText(
                            this.batchResponseMessage.GetStream,
                            (HttpStatusCode)this.batchResponseMessage.StatusCode);
                    }

                    throw Error.InvalidOperation(Strings.Batch_ExpectedContentType(this.batchResponseMessage.GetHeader(XmlConstants.HttpContentType)), inner);
                }

                DataServiceResponse response = this.HandleBatchResponseInternal(batchReader);

                // In case of successful processing of at least the beginning of the batch, the message reader is owned by the returned response
                // (or rather by the IEnumerable of operation responses inside it).
                // It will be disposed once the operation responses are enumerated (since the IEnumerator should be disposed once used).
                // In that case we must NOT dispose it here, since that enumeration can exist long after we return from this method.
                batchMessageReaderOwned = false;

                return(response);
            }
            catch (DataServiceRequestException)
            {
                throw;
            }
            catch (InvalidOperationException ex)
            {
                HeaderCollection headers     = new HeaderCollection(this.batchResponseMessage);
                int statusCode               = this.batchResponseMessage == null ? (int)HttpStatusCode.InternalServerError : (int)this.batchResponseMessage.StatusCode;
                DataServiceResponse response = new DataServiceResponse(headers, statusCode, new OperationResponse[0], this.IsBatchRequest);
                throw new DataServiceRequestException(Strings.DataServiceException_GeneralError, ex, response);
            }
            finally
            {
                if (batchMessageReaderOwned)
                {
                    Util.Dispose(ref this.batchMessageReader);
                }
            }
        }
Example #18
0
 public void AtomShouldNotBeConsideredJsonLight()
 {
     ContentTypeUtil.IsResponseMediaTypeJsonLight("application/atom+xml", true, V4).Should().BeFalse();
 }
Example #19
0
 public void CheckForBeingJsonShouldBeCaseInsensitive()
 {
     ContentTypeUtil.IsNotJson("aPPLiCATioN/jSOn").Should().BeFalse();
     ContentTypeUtil.IsNotJson("aPPLiCATioN/ATom+XMl").Should().BeTrue();
 }
Example #20
0
 public void CheckForBeingJsonLightShouldBeCaseInsensitive()
 {
     ContentTypeUtil.IsResponseMediaTypeJsonLight("AppLICation/JSoN;ODatA.MeTaDAtA=FulL", true, V4).Should().BeTrue();
 }
Example #21
0
        public static string GetResourceUrl(string assemblyName, string resourceName, bool shorturl)
        {
            string extension = StringUtil.GetExtension(resourceName);

            string url     = null;
            string version = string.Empty;

            IArea site = AreaConfig.Instance;

            switch (extension)
            {
            case ".css":
                url     = Web.Utility.FormatCssUrl(site, "_res.aspx?r=");
                version = AreaConfig.Instance.CssVersion;
                break;

            case ".js":
                url     = Web.Utility.FormatJsUrl(site, "_res.aspx?r=");
                version = AreaConfig.Instance.JsVersion;
                break;

            default:
                url = StringUtil.CombinUrl(site.VirtualPath, "_res.aspx?r=");
                break;
            }

            if (shorturl)
            {
                assemblyName = assemblyName.Substring(PRENAMESPACE.Length);
                resourceName = resourceName.Substring(PRENAMESPACE.Length).Substring(assemblyName.Length + 1);
            }

            url += Convert.ToBase64String(Encoding.ASCII.GetBytes(resourceName)) +
                   "&t=" +
                   Convert.ToBase64String(Encoding.ASCII.GetBytes(assemblyName));

            string contentType = ContentTypeUtil.GetContentType(extension);

            // short the url
            if (contentType == "text/css")
            {
                contentType = "0";
            }
            else if (contentType == "text/javascript")
            {
                contentType = "1";
            }

            url += ("&z=" + contentType);

            if (StringUtil.HasText(version) && site != null && !site.CombineCss)
            {
                url += ("&v=" + version);
            }

            if (shorturl)
            {
                url += "&su=1";
            }

            return(url);
        }
Example #22
0
 public void AtomShouldNotBeConsideredJson()
 {
     ContentTypeUtil.IsNotJson("application/atom+xml").Should().BeTrue();
 }
Example #23
0
 public void NullContentTypeShouldNotBeConsideredJson()
 {
     ContentTypeUtil.IsNotJson(null).Should().BeTrue();
 }
Example #24
0
 public void JsonWithNoMetadataShouldBeConsideredJsonLight()
 {
     ContentTypeUtil.IsResponseMediaTypeJsonLight("application/json;odata.metadata=none", true, V4).Should().BeTrue();
 }
Example #25
0
 public void UnqualifiedJsonShouldBeConsideredJsonLightForNonEntityPayloadsIfMaxVersionIs3()
 {
     ContentTypeUtil.IsResponseMediaTypeJsonLight("application/json", false, V4).Should().BeTrue();
 }
Example #26
0
        private MaterializeAtom ReadPropertyFromRawData(ClientPropertyAnnotation property)
        {
            DataServiceContext context = (DataServiceContext)this.Source;

            bool merging = context.ApplyingChanges;

            try
            {
                context.ApplyingChanges = true;

                // if this is the data property for a media entry, what comes back
                // is the raw value (no markup)
#if ASTORIA_OPEN_OBJECT
                object openProps = null;
#endif
                string   mimeType    = null;
                Encoding encoding    = null;
                Type     elementType = property.EntityCollectionItemType ?? property.NullablePropertyType;
                IList    results     = (IList)Activator.CreateInstance(typeof(List <>).MakeGenericType(elementType));
                ContentTypeUtil.ReadContentType(this.ContentType, out mimeType, out encoding);

                using (Stream responseStream = this.GetResponseStream())
                {
                    // special case byte[], and for everything else let std conversion kick-in
                    if (property.PropertyType == typeof(byte[]))
                    {
                        int    total  = checked ((int)this.ContentLength);
                        byte[] buffer = null;
                        if (total >= 0)
                        {
                            buffer = LoadPropertyResult.ReadByteArrayWithContentLength(responseStream, total);
                        }
                        else
                        {
                            buffer = LoadPropertyResult.ReadByteArrayChunked(responseStream);
                        }

                        results.Add(buffer);
#if ASTORIA_OPEN_OBJECT
                        property.SetValue(this.entity, buffer, this.propertyName, ref openProps, false);
#else
                        property.SetValue(this.entity, buffer, this.propertyName, false);
#endif
                    }
                    else
                    {
                        // responseStream will disposed, StreamReader doesn't need to dispose of it.
                        StreamReader reader         = new StreamReader(responseStream, encoding);
                        object       convertedValue = property.PropertyType == typeof(string) ?
                                                      reader.ReadToEnd() :
                                                      ClientConvert.ChangeType(reader.ReadToEnd(), property.PropertyType);
                        results.Add(convertedValue);
#if ASTORIA_OPEN_OBJECT
                        property.SetValue(this.entity, convertedValue, this.propertyName, ref openProps, false);
#else
                        property.SetValue(this.entity, convertedValue, this.propertyName, false);
#endif
                    }
                }

#if ASTORIA_OPEN_OBJECT
                Debug.Assert(openProps == null, "These should not be set in this path");
#endif
                if (property.MimeTypeProperty != null)
                {
                    // an implication of this 3rd-arg-null is that mime type properties cannot be open props
#if ASTORIA_OPEN_OBJECT
                    property.MimeTypeProperty.SetValue(this.entity, mimeType, null, ref openProps, false);
                    Debug.Assert(openProps == null, "These should not be set in this path");
#else
                    property.MimeTypeProperty.SetValue(this.entity, mimeType, null, false);
#endif
                }

                return(MaterializeAtom.CreateWrapper(context, results));
            }
            finally
            {
                context.ApplyingChanges = merging;
            }
        }
Example #27
0
        public void ValidHttpProcessUtilityReadMediaTypeTest()
        {
            MediaTypeTest[] tests = new MediaTypeTest[]
            {
                new MediaTypeTest()
                {
                    InputText = "text/plain", MediaType = "text/plain"
                },
                new MediaTypeTest()
                {
                    InputText = "text/plain ", MediaType = "text/plain", OutputText = "text/plain"
                },
                new MediaTypeTest()
                {
                    InputText = "text/plain;", MediaType = "text/plain", OutputText = "text/plain"
                },
                new MediaTypeTest()
                {
                    InputText  = "text/plain;a=b", MediaType = "text/plain", OutputText = "text/plain;a=b",
                    Parameters = new ContentTypeUtil.MediaParameter[] { new ContentTypeUtil.MediaParameter("a", "b", false) }
                },
                new MediaTypeTest()
                {
                    InputText  = "text/plain;a=b; cc=dd ", MediaType = "text/plain", OutputText = "text/plain;a=b;cc=dd",
                    Parameters = new ContentTypeUtil.MediaParameter[] {
                        new ContentTypeUtil.MediaParameter("a", "b", false),
                        new ContentTypeUtil.MediaParameter("cc", "dd", false)
                    }
                },
                new MediaTypeTest()
                {
                    InputText  = "text/plain;a=", MediaType = "text/plain",
                    Parameters = new ContentTypeUtil.MediaParameter[] { new ContentTypeUtil.MediaParameter("a", "", false) }
                },
                new MediaTypeTest()
                {
                    InputText  = "text/plain;a=\"b\"", MediaType = "text/plain",
                    Parameters = new ContentTypeUtil.MediaParameter[] { new ContentTypeUtil.MediaParameter("a", "b", true) }
                },
                new MediaTypeTest() // its weird that we did not fail when no name was specified
                {
                    InputText  = "text/plain;=b", MediaType = "text/plain",
                    Parameters = new ContentTypeUtil.MediaParameter[] { new ContentTypeUtil.MediaParameter("", "b", false) }
                },
                new MediaTypeTest()
                {
                    InputText  = "text/plain;a=", MediaType = "text/plain",
                    Parameters = new ContentTypeUtil.MediaParameter[] { new ContentTypeUtil.MediaParameter("a", "", false) }
                },
            };
            foreach (var test in tests)
            {
                string mimeType = null;
                ContentTypeUtil.MediaParameter[] parameters = null;
                parameters = ContentTypeUtil.ReadContentType(test.InputText, out mimeType);

                Assert.AreEqual(mimeType, test.MediaType, "Media types match.");
                Assert.AreEqual(parameters != null, test.Parameters != null, "Parameter nullability matches.");
                if (parameters != null)
                {
                    for (int i = 0; i < parameters.Length; i++)
                    {
                        Assert.AreEqual(parameters[i].Name, test.Parameters[i].Name, "Parameters name do not match");
                        Assert.AreEqual(parameters[i].Value, test.Parameters[i].Value, "Parameters value do not match");
                        Assert.AreEqual(parameters[i].GetOriginalValue(), test.Parameters[i].GetOriginalValue(), "Parameters original value do not match");
                        Assert.AreEqual(test.OutputText ?? test.InputText, ContentTypeUtil.WriteContentType(mimeType, parameters));
                    }
                }
            }
        }
Example #28
0
        /// <summary>
        /// Check to see if the resource to be inserted is a media descriptor, and if so
        /// setup a POST request for the media content first and turn the rest of
        /// the operation into a PUT to update the rest of the properties.
        /// </summary>
        /// <param name="entityDescriptor">The resource to check/process</param>
        /// <returns>An instance of ODataRequestMessage to do POST to the media resource</returns>
        private ODataRequestMessageWrapper CheckAndProcessMediaEntryPost(EntityDescriptor entityDescriptor)
        {
            // TODO: Revisit the design of how media link entries are handled during update
            ClientEdmModel       model = this.RequestInfo.Model;
            ClientTypeAnnotation type  = model.GetClientTypeAnnotation(model.GetOrCreateEdmType(entityDescriptor.Entity.GetType()));

            if (!type.IsMediaLinkEntry && !entityDescriptor.IsMediaLinkEntry)
            {
                // this is not a media link descriptor, process normally
                return(null);
            }

            if (type.MediaDataMember == null && entityDescriptor.SaveStream == null)
            {
                // The entity is marked as MLE but we don't have the content property
                //   and the user didn't set the save stream.
                throw Error.InvalidOperation(Strings.Context_MLEWithoutSaveStream(type.ElementTypeName));
            }

            Debug.Assert(
                (type.MediaDataMember != null && entityDescriptor.SaveStream == null) ||
                (type.MediaDataMember == null && entityDescriptor.SaveStream != null),
                "Only one way of specifying the MR content is allowed.");

            ODataRequestMessageWrapper mediaRequest = null;

            if (type.MediaDataMember != null)
            {
                string contentType   = null;
                int    contentLength = 0;

                if (type.MediaDataMember.MimeTypeProperty == null)
                {
                    contentType = XmlConstants.MimeApplicationOctetStream;
                }
                else
                {
                    object mimeTypeValue = type.MediaDataMember.MimeTypeProperty.GetValue(entityDescriptor.Entity);
                    String mimeType      = mimeTypeValue != null?mimeTypeValue.ToString() : null;

                    if (String.IsNullOrEmpty(mimeType))
                    {
                        throw Error.InvalidOperation(
                                  Strings.Context_NoContentTypeForMediaLink(
                                      type.ElementTypeName,
                                      type.MediaDataMember.MimeTypeProperty.PropertyName));
                    }

                    contentType = mimeType;
                }

                object value = type.MediaDataMember.GetValue(entityDescriptor.Entity);
                if (value == null)
                {
                    this.mediaResourceRequestStream = null;
                }
                else
                {
                    byte[] buffer = value as byte[];
                    if (buffer == null)
                    {
                        string   mime;
                        Encoding encoding;
                        ContentTypeUtil.ReadContentType(contentType, out mime, out encoding);

                        if (encoding == null)
                        {
                            encoding     = Encoding.UTF8;
                            contentType += XmlConstants.MimeTypeUtf8Encoding;
                        }

                        buffer = encoding.GetBytes(ClientConvert.ToString(value));
                    }

                    contentLength = buffer.Length;

                    // Need to specify that the buffer is publicly visible as we need to access it later on
                    this.mediaResourceRequestStream = new MemoryStream(buffer, 0, buffer.Length, false, true);
                }

                HeaderCollection headers = new HeaderCollection();
                headers.SetHeader(XmlConstants.HttpContentLength, contentLength.ToString(CultureInfo.InvariantCulture));
                headers.SetHeader(XmlConstants.HttpContentType, contentType);

                mediaRequest = this.CreateMediaResourceRequest(
                    entityDescriptor.GetResourceUri(this.RequestInfo.BaseUriResolver, false /*queryLink*/),
                    XmlConstants.HttpMethodPost,
                    Util.ODataVersion4,
                    type.MediaDataMember == null, // sendChunked
                    true,                         // applyResponsePreference
                    headers,
                    entityDescriptor);
            }
            else
            {
                HeaderCollection headers = new HeaderCollection();
                this.SetupMediaResourceRequest(headers, entityDescriptor.SaveStream, null /*etag*/);

                mediaRequest = this.CreateMediaResourceRequest(
                    entityDescriptor.GetResourceUri(this.RequestInfo.BaseUriResolver, false /*queryLink*/),
                    XmlConstants.HttpMethodPost,
                    Util.ODataVersion4,
                    type.MediaDataMember == null, // sendChunked
                    true,                         // applyResponsePreference
                    headers,
                    entityDescriptor);
            }

            // Convert the insert into an update for the media link descriptor we just created
            // (note that the identity still needs to be fixed up on the resbox once
            // the response comes with the 'location' header; that happens during processing
            // of the response in SavedResource())
            entityDescriptor.State = EntityStates.Modified;

            return(mediaRequest);
        }
Example #29
0
 public void UnspecifiedJsonShouldBeConsideredJson()
 {
     ContentTypeUtil.IsNotJson("application/json").Should().BeFalse();
 }