File: System\Data\Services\ResponseBodyWriter.cs
Project: ndp\fx\src\DataWeb\Server\System.Data.Services.csproj (System.Data.Services)
//---------------------------------------------------------------------
// <copyright file="ResponseBodyWriter.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <summary>
//      Provides a base class for DataWeb services.
// </summary>
//
// @owner  Microsoft
//---------------------------------------------------------------------
 
namespace System.Data.Services
{
    using System;
    using System.Collections;
    using System.Data.Services.Providers;
    using System.Data.Services.Serializers;
    using System.Diagnostics;
    using System.IO;
    using System.Text;
 
    /// <summary>
    /// Use this class to encapsulate writing the body of the outgoing response 
    /// for a data request.
    /// </summary>
    internal class ResponseBodyWriter
    {
        /// <summary>Encoding, if available.</summary>
        private readonly Encoding encoding;
 
        /// <summary>Whether <see cref="queryResults"/> has already moved.</summary>
        private readonly bool hasMoved;
 
        /// <summary>Host for the request being processed.</summary>
        private readonly IDataService service;
 
        /// <summary>Enumerator for results.</summary>
        private readonly IEnumerator queryResults;
 
        /// <summary>Description of request made to the system.</summary>
        private readonly RequestDescription requestDescription;
 
        /// <summary>Content format for response.</summary>
        private readonly ContentFormat responseFormat;
 
        /// <summary>If the target is a Media Resource, this holds the read stream for the Media Resource.</summary>
        private Stream mediaResourceStream;
 
        /// <summary>Initializes a new <see cref="ResponseBodyWriter"/> that can write the body of a response.</summary>
        /// <param name="encoding">Encoding, if available.</param>
        /// <param name="hasMoved">Whether <paramref name="queryResults"/> has already moved.</param>
        /// <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="responseFormat">Content format for response.</param>
        internal ResponseBodyWriter(
            Encoding encoding,
            bool hasMoved,
            IDataService service,
            IEnumerator queryResults,
            RequestDescription requestDescription,
            ContentFormat responseFormat)
        {
            Debug.Assert(responseFormat != ContentFormat.Unknown, "responseFormat != ContentFormat.Unknown");
            this.encoding = encoding;
            this.hasMoved = hasMoved;
            this.service = service;
            this.queryResults = queryResults;
            this.requestDescription = requestDescription;
            this.responseFormat = responseFormat;
 
            if (this.requestDescription.TargetKind == RequestTargetKind.MediaResource)
            {
                // Note that GetReadStream will set the ResponseETag before it returns
                this.mediaResourceStream = service.StreamProvider.GetReadStream(this.queryResults.Current, this.service.OperationContext);
            }
        }
 
        /// <summary>Gets the absolute URI to the service.</summary>
        internal Uri AbsoluteServiceUri
        {
            get { return this.service.OperationContext.AbsoluteServiceUri; }
        }
 
        /// <summary>Gets the <see cref="IDataServiceHost"/> for this response.</summary>
        internal DataServiceHostWrapper Host
        {
            get { return this.service.OperationContext.Host; }
        }
 
        /// <summary>Gets the <see cref="DataServiceProviderWrapper"/> for this response.</summary>
        internal DataServiceProviderWrapper Provider
        {
            get { return this.service.Provider; }
        }
 
        /// <summary>Writes the request body to the specified <see cref="Stream"/>.</summary>
        /// <param name="stream">Stream to write to.</param>
        internal void Write(Stream stream)
        {
            Debug.Assert(stream != null, "stream != null");
            IExceptionWriter exceptionWriter = null;
 
            try
            {
                switch (this.responseFormat)
                {
                    case ContentFormat.Binary:
                        Debug.Assert(
                            this.requestDescription.TargetKind == RequestTargetKind.OpenPropertyValue ||
                            this.requestDescription.TargetKind == RequestTargetKind.PrimitiveValue ||
                            this.requestDescription.TargetKind == RequestTargetKind.MediaResource,
                            this.requestDescription.TargetKind + " is PrimitiveValue or OpenPropertyValue or StreamPropertyValue");
 
                        BinarySerializer binarySerializer = new BinarySerializer(stream);
                        exceptionWriter = binarySerializer;
                        if (this.requestDescription.TargetKind == RequestTargetKind.MediaResource)
                        {
                            Debug.Assert(this.mediaResourceStream != null, "this.mediaResourceStream != null");
 
                            binarySerializer.WriteRequest(this.mediaResourceStream, this.service.StreamProvider.StreamBufferSize);
                        }
                        else
                        {
                            binarySerializer.WriteRequest(this.queryResults.Current);
                        }
 
                        break;
 
                    case ContentFormat.Text:
                        Debug.Assert(
                            this.requestDescription.TargetKind == RequestTargetKind.OpenPropertyValue ||
                            this.requestDescription.TargetKind == RequestTargetKind.PrimitiveValue,
                            this.requestDescription.TargetKind + " is PrimitiveValue or OpenPropertyValue");
 
                        TextSerializer textSerializer = new TextSerializer(stream, this.encoding);
                        exceptionWriter = textSerializer;
                        textSerializer.WriteRequest(this.queryResults.Current);
                        break;
 
                    case ContentFormat.Atom:
                    case ContentFormat.Json:
                    case ContentFormat.PlainXml:
                        Debug.Assert(this.requestDescription.TargetKind != RequestTargetKind.PrimitiveValue, "this.requestDescription.TargetKind != RequestTargetKind.PrimitiveValue");
                        Debug.Assert(this.requestDescription.TargetKind != RequestTargetKind.OpenPropertyValue, "this.requestDescription.TargetKind != RequestTargetKind.OpenPropertyValue");
                        Debug.Assert(this.requestDescription.TargetKind != RequestTargetKind.Metadata, "this.requestDescription.TargetKind != RequestTargetKind.Metadata");
 
                        if (this.requestDescription.TargetKind == RequestTargetKind.ServiceDirectory)
                        {
                            if (this.responseFormat == ContentFormat.Json)
                            {
                                JsonServiceDocumentSerializer serviceSerializer = new JsonServiceDocumentSerializer(stream, this.Provider, this.encoding);
                                exceptionWriter = serviceSerializer;
                                serviceSerializer.WriteRequest();
                                break;
                            }
                            else
                            {
                                AtomServiceDocumentSerializer serviceSerializer = new AtomServiceDocumentSerializer(stream, this.AbsoluteServiceUri, this.Provider, this.encoding);
                                exceptionWriter = serviceSerializer;
                                serviceSerializer.WriteRequest(this.service);
                            }
                        }
                        else
                        {
                            Serializer serializer;
                            if (ContentFormat.Json == this.responseFormat)
                            {
                                serializer = new JsonSerializer(this.requestDescription, stream, this.AbsoluteServiceUri, this.service, this.encoding, this.Host.ResponseETag);
                            }
                            else if (ContentFormat.PlainXml == this.responseFormat)
                            {
                                serializer = new PlainXmlSerializer(this.requestDescription, this.AbsoluteServiceUri, this.service, stream, this.encoding);
                            }
                            else
                            {
                                Debug.Assert(
                                    this.requestDescription.TargetKind == RequestTargetKind.OpenProperty ||
                                    this.requestDescription.TargetKind == RequestTargetKind.Resource,
                                    "TargetKind " + this.requestDescription.TargetKind + " == Resource || OpenProperty -- POX should have handled it otherwise.");
                                serializer = new SyndicationSerializer(
                                    this.requestDescription,
                                    this.AbsoluteServiceUri,
                                    this.service,
                                    stream,
                                    this.encoding,
                                    this.Host.ResponseETag,
                                    new Atom10FormatterFactory());
                            }
 
                            exceptionWriter = serializer;
                            Debug.Assert(exceptionWriter != null, "this.exceptionWriter != null");
                            serializer.WriteRequest(this.queryResults, this.hasMoved);
                        }
 
                        break;
 
                    default:
                        Debug.Assert(
                            this.responseFormat == ContentFormat.MetadataDocument,
                            "responseFormat(" + this.responseFormat + ") == ContentFormat.MetadataDocument -- otherwise exception should have been thrown before");
                        Debug.Assert(this.requestDescription.TargetKind == RequestTargetKind.Metadata, "this.requestDescription.TargetKind == RequestTargetKind.Metadata");
                        MetadataSerializer metadataSerializer = new MetadataSerializer(stream, this.AbsoluteServiceUri, this.Provider, this.encoding);
                        exceptionWriter = metadataSerializer;
                        metadataSerializer.WriteRequest(this.service);
                        break;
                }
            }
            catch (Exception exception)
            {
                if (!WebUtil.IsCatchableExceptionType(exception))
                {
                    throw;
                }
 
                // Only JSON and XML are supported.
                string contentType = (this.responseFormat == ContentFormat.Json) ? XmlConstants.MimeApplicationJson : XmlConstants.MimeApplicationXml;
                ErrorHandler.HandleDuringWritingException(exception, this.service, contentType, exceptionWriter);
            }
            finally
            {
                WebUtil.Dispose(this.queryResults);
                WebUtil.Dispose(this.mediaResourceStream);
            }
        }
    }
}