public void Register(string contentType, ResponseSerializerDelegate responseSerializer,
                             StreamDeserializerDelegate streamDeserializer)
        {
            if (contentType.IsNullOrEmpty())
                throw new ArgumentNullException("contentType");

            var parts = contentType.Split('/');
            var format = parts[parts.Length - 1];
            this.ContentTypeFormats[format] = contentType;

            this.ContentTypeResponseSerializers[contentType] = responseSerializer;
            SetContentTypeDeserializer(contentType, streamDeserializer);
        }
        public void Register(string contentType, ResponseSerializerDelegate responseSerializer,
                             StreamDeserializerDelegate streamDeserializer)
        {
            if (contentType.IsNullOrEmpty())
                throw new ArgumentNullException("contentType");

            var parts = contentType.Split('/');
            var format = parts[parts.Length - 1];
            this.ContentTypeFormats[format] = contentType;

            this.ContentTypeResponseSerializers[contentType] = responseSerializer;
            SetContentTypeDeserializer(contentType, streamDeserializer);
        }
        /// <summary>
        /// Writes to response.
        /// Response headers are customizable by implementing IHasOptions an returning Dictionary of Http headers.
        /// </summary>
        /// <param name="response">The response.</param>
        /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
        /// <param name="defaultAction">The default action.</param>
        /// <param name="request">The serialization context.</param>
        /// <param name="bodyPrefix">Add prefix to response body if any</param>
        /// <param name="bodySuffix">Add suffix to response body if any</param>
        /// <returns></returns>
        public static Task <bool> WriteToResponse(this IResponse response, object result, ResponseSerializerDelegate defaultAction, IRequest request, byte[] bodyPrefix, byte[] bodySuffix)
        {
            using (Profiler.Current.Step("Writing to Response"))
            {
                var defaultContentType = request.ResponseContentType;
                try
                {
                    if (result == null)
                    {
                        response.EndRequestWithNoContent();
                        return(TrueTask);
                    }

                    ApplyGlobalResponseHeaders(response);

                    var httpResult = result as IHttpResult;
                    if (httpResult != null)
                    {
                        if (httpResult.RequestContext == null)
                        {
                            httpResult.RequestContext = request;
                        }

                        var paddingLength = bodyPrefix != null ? bodyPrefix.Length : 0;
                        if (bodySuffix != null)
                        {
                            paddingLength += bodySuffix.Length;
                        }

                        httpResult.PaddingLength = paddingLength;

                        var httpError = httpResult as IHttpError;
                        if (httpError != null)
                        {
                            response.Dto = httpError.CreateErrorResponse();
                            if (response.HandleCustomErrorHandler(request,
                                                                  defaultContentType, httpError.Status, response.Dto))
                            {
                                return(TrueTask);
                            }
                        }

                        response.Dto = response.Dto ?? httpResult.GetDto();

                        response.StatusCode        = httpResult.Status;
                        response.StatusDescription = httpResult.StatusDescription ?? httpResult.StatusCode.ToString();
                        if (string.IsNullOrEmpty(httpResult.ContentType))
                        {
                            httpResult.ContentType = defaultContentType;
                        }
                        response.ContentType = httpResult.ContentType;
                    }
                    else
                    {
                        response.Dto = result;
                    }

                    /* Mono Error: Exception: Method not found: 'System.Web.HttpResponse.get_Headers' */
                    var responseOptions = result as IHasOptions;
                    if (responseOptions != null)
                    {
                        //Reserving options with keys in the format 'xx.xxx' (No Http headers contain a '.' so its a safe restriction)
                        const string reservedOptions = ".";

                        foreach (var responseHeaders in responseOptions.Options)
                        {
                            if (responseHeaders.Key.Contains(reservedOptions))
                            {
                                continue;
                            }
                            if (responseHeaders.Key == HttpHeaders.ContentLength)
                            {
                                response.SetContentLength(long.Parse(responseHeaders.Value));
                                continue;
                            }

                            Log.DebugFormat("Setting Custom HTTP Header: {0}: {1}", responseHeaders.Key, responseHeaders.Value);
                            response.AddHeader(responseHeaders.Key, responseHeaders.Value);
                        }
                    }

                    var disposableResult = result as IDisposable;
                    if (WriteToOutputStream(response, result, bodyPrefix, bodySuffix))
                    {
                        response.Flush(); //required for Compression
                        if (disposableResult != null)
                        {
                            disposableResult.Dispose();
                        }
                        return(TrueTask);
                    }

                    if (httpResult != null)
                    {
                        result = httpResult.Response;
                    }

                    //ContentType='text/html' is the default for a HttpResponse
                    //Do not override if another has been set
                    if (response.ContentType == null || response.ContentType == MimeTypes.Html)
                    {
                        response.ContentType = defaultContentType;
                    }
                    if (bodyPrefix != null && response.ContentType.IndexOf(MimeTypes.Json, StringComparison.InvariantCultureIgnoreCase) >= 0)
                    {
                        response.ContentType = MimeTypes.JavaScript;
                    }

                    if (HostContext.Config.AppendUtf8CharsetOnContentTypes.Contains(response.ContentType))
                    {
                        response.ContentType += ContentFormat.Utf8Suffix;
                    }

                    var responseText = result as string;
                    if (responseText != null)
                    {
                        if (bodyPrefix != null)
                        {
                            response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                        }
                        WriteTextToResponse(response, responseText, defaultContentType);
                        if (bodySuffix != null)
                        {
                            response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                        }
                        return(TrueTask);
                    }

                    if (defaultAction == null)
                    {
                        throw new ArgumentNullException("defaultAction", String.Format(
                                                            "As result '{0}' is not a supported responseType, a defaultAction must be supplied",
                                                            (result != null ? result.GetType().GetOperationName() : "")));
                    }

                    if (bodyPrefix != null)
                    {
                        response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                    }
                    if (result != null)
                    {
                        defaultAction(request, result, response);
                    }
                    if (bodySuffix != null)
                    {
                        response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                    }

                    if (disposableResult != null)
                    {
                        disposableResult.Dispose();
                    }

                    return(FalseTask);
                }
                catch (Exception originalEx)
                {
                    HostContext.RaiseUncaughtException(request, response, request.OperationName, originalEx);

                    if (!HostContext.Config.WriteErrorsToResponse)
                    {
                        return(originalEx.AsTaskException <bool>());
                    }

                    var errorMessage = String.Format(
                        "Error occured while Processing Request: [{0}] {1}", originalEx.GetType().GetOperationName(), originalEx.Message);

                    try
                    {
                        if (!response.IsClosed)
                        {
                            response.WriteErrorToResponse(
                                request,
                                defaultContentType,
                                request.OperationName,
                                errorMessage,
                                originalEx,
                                (int)HttpStatusCode.InternalServerError);
                        }
                    }
                    catch (Exception writeErrorEx)
                    {
                        //Exception in writing to response should not hide the original exception
                        Log.Info("Failed to write error to response: {0}", writeErrorEx);
                        return(originalEx.AsTaskException <bool>());
                    }
                    return(TrueTask);
                }
                finally
                {
                    response.EndRequest(skipHeaders: true);
                }
            }
        }
 public static Task <bool> WriteToResponse(this IResponse httpRes, object result, ResponseSerializerDelegate serializer, IRequest serializationContext)
 {
     return(httpRes.WriteToResponse(result, serializer, serializationContext, null, null));
 }
        /// <summary>
        /// Writes to response.
        /// Response headers are customizable by implementing IHasOptions an returning Dictionary of Http headers.
        /// </summary>
        /// <param name="response">The response.</param>
        /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
        /// <param name="defaultAction">The default action.</param>
        /// <param name="serializerCtx">The serialization context.</param>
        /// <param name="bodyPrefix">Add prefix to response body if any</param>
        /// <param name="bodySuffix">Add suffix to response body if any</param>
        /// <returns></returns>
        public static bool WriteToResponse(this IHttpResponse response, object result, ResponseSerializerDelegate defaultAction, IRequestContext serializerCtx, byte[] bodyPrefix, byte[] bodySuffix)
        {
            using (Profiler.Current.Step("Writing to Response"))
            {
                var defaultContentType = serializerCtx.ResponseContentType;
                try
                {
                    if (result == null) return true;

                    ApplyGlobalResponseHeaders(response);

                    var httpResult = result as IHttpResult;
                    var disposableResult = result as IDisposable;
                    if (httpResult != null)
                    {
                        response.StatusCode = httpResult.Status;
                        response.StatusDescription = httpResult.StatusDescription ?? httpResult.StatusCode.ToString();
                        if (String.IsNullOrEmpty(httpResult.ContentType))
                        {
                            httpResult.ContentType = defaultContentType;
                        }
                        response.ContentType = httpResult.ContentType;
                    }

                    /* Mono Error: Exception: Method not found: 'System.Web.HttpResponse.get_Headers' */
                    var responseOptions = result as IHasOptions;
                    if (responseOptions != null)
                    {
                        //Reserving options with keys in the format 'xx.xxx' (No Http headers contain a '.' so its a safe restriction)
                        const string reservedOptions = ".";

                        foreach (var responseHeaders in responseOptions.Options)
                        {
                            if (responseHeaders.Key.Contains(reservedOptions)) continue;

                            Log.DebugFormat("Setting Custom HTTP Header: {0}: {1}", responseHeaders.Key, responseHeaders.Value);
                            response.AddHeader(responseHeaders.Key, responseHeaders.Value);
                        }
                    }

                    if (WriteToOutputStream(response, result, bodyPrefix, bodySuffix))
                    {
                        response.Flush(); //required for Compression
                        if (disposableResult != null) disposableResult.Dispose();
                        return true;
                    }

                    if (httpResult != null)
                    {
                        result = httpResult.Response;
                    }

                    //ContentType='text/html' is the default for a HttpResponse
                    //Do not override if another has been set
                    if (response.ContentType == null || response.ContentType == ContentType.Html)
                    {
                        response.ContentType = defaultContentType;
                    }
                    if (bodyPrefix != null && response.ContentType.IndexOf(ContentType.Json) >= 0)
                    {
                        response.ContentType = ContentType.JavaScript;
                    }

                    if (EndpointHost.Config.AppendUtf8CharsetOnContentTypes.Contains(response.ContentType))
                    {
                        response.ContentType += ContentType.Utf8Suffix;
                    }

                    var responseText = result as string;
                    if (responseText != null)
                    {
                        if (bodyPrefix != null) response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                        WriteTextToResponse(response, responseText, defaultContentType);
                        if (bodySuffix != null) response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                        return true;
                    }

                    if (defaultAction == null)
                    {
                        throw new ArgumentNullException("defaultAction", String.Format(
                        "As result '{0}' is not a supported responseType, a defaultAction must be supplied",
                        (result != null ? result.GetType().Name : "")));
                    }

                    if (bodyPrefix != null) response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                    if (result != null) defaultAction(serializerCtx, result, response);
                    if (bodySuffix != null) response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);

                    if (disposableResult != null) disposableResult.Dispose();

                    return false;
                }
                catch (Exception originalEx)
                {
                    //TM: It would be good to handle 'remote end dropped connection' problems here. Arguably they should at least be suppressible via configuration

                    //DB: Using standard ServiceStack configuration method
                    if (!EndpointHost.Config.WriteErrorsToResponse) throw;

                    var errorMessage = String.Format(
                    "Error occured while Processing Request: [{0}] {1}", originalEx.GetType().Name, originalEx.Message);

                    Log.Error(errorMessage, originalEx);

                    var operationName = result != null
                                        ? result.GetType().Name.Replace("Response", "")
                                        : "OperationName";

                    try
                    {
                        if (!response.IsClosed)
                        {
                            response.WriteErrorToResponse(defaultContentType, operationName, errorMessage, originalEx);
                        }
                    }
                    catch (Exception writeErrorEx)
                    {
                        //Exception in writing to response should not hide the original exception
                        Log.Info("Failed to write error to response: {0}", writeErrorEx);
                        throw originalEx;
                    }
                    return true;
                }
                finally
                {
                    response.EndServiceStackRequest(skipHeaders:true);
                }
            }
        }
 public static bool WriteToResponse(this IHttpResponse httpRes, object result, ResponseSerializerDelegate serializer, IRequestContext serializationContext)
 {
     return httpRes.WriteToResponse(result, serializer, serializationContext, null, null);
 }
        /// <summary>
        /// Writes to response.
        /// Response headers are customizable by implementing IHasOptions an returning Dictionary of Http headers.
        /// </summary>
        /// <param name="response">The response.</param>
        /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
        /// <param name="defaultAction">The default action.</param>
        /// <param name="serializerCtx">The serialization context.</param>
        /// <param name="bodyPrefix">Add prefix to response body if any</param>
        /// <param name="bodySuffix">Add suffix to response body if any</param>
        /// <returns></returns>
        public static bool WriteToResponse(this IHttpResponse response, object result, ResponseSerializerDelegate defaultAction, IRequestContext serializerCtx, byte[] bodyPrefix, byte[] bodySuffix)
        {
            using (Profiler.Current.Step("Writing to Response"))
            {
                var defaultContentType = serializerCtx.ResponseContentType;
                try
                {
                    if (result == null)
                    {
                        response.EndHttpRequestWithNoContent();
                        return(true);
                    }

                    ApplyGlobalResponseHeaders(response);

                    var httpResult = result as IHttpResult;
                    if (httpResult != null)
                    {
                        if (httpResult.RequestContext == null)
                        {
                            httpResult.RequestContext = serializerCtx;
                        }

                        var httpError = httpResult as IHttpError;
                        if (httpError != null)
                        {
                            if (response.HandleCustomErrorHandler(serializerCtx.Get <IHttpRequest>(),
                                                                  defaultContentType, httpError.Status, httpError.ToErrorResponse()))
                            {
                                return(true);
                            }
                        }

                        response.StatusCode        = httpResult.Status;
                        response.StatusDescription = httpResult.StatusDescription ?? httpResult.StatusCode.ToString();
                        if (string.IsNullOrEmpty(httpResult.ContentType))
                        {
                            httpResult.ContentType = defaultContentType;
                        }
                        response.ContentType = httpResult.ContentType;
                    }

                    /* Mono Error: Exception: Method not found: 'System.Web.HttpResponse.get_Headers' */
                    var responseOptions = result as IHasOptions;
                    if (responseOptions != null)
                    {
                        //Reserving options with keys in the format 'xx.xxx' (No Http headers contain a '.' so its a safe restriction)
                        const string reservedOptions = ".";

                        foreach (var responseHeaders in responseOptions.Options)
                        {
                            if (responseHeaders.Key.Contains(reservedOptions))
                            {
                                continue;
                            }

                            Log.DebugFormat("Setting Custom HTTP Header: {0}: {1}", responseHeaders.Key, responseHeaders.Value);
                            response.AddHeader(responseHeaders.Key, responseHeaders.Value);
                        }
                    }

                    var disposableResult = result as IDisposable;
                    if (WriteToOutputStream(response, result, bodyPrefix, bodySuffix))
                    {
                        response.Flush(); //required for Compression
                        if (disposableResult != null)
                        {
                            disposableResult.Dispose();
                        }
                        return(true);
                    }

                    if (httpResult != null)
                    {
                        result = httpResult.Response;
                    }

                    //ContentType='text/html' is the default for a HttpResponse
                    //Do not override if another has been set
                    if (response.ContentType == null || response.ContentType == ContentType.Html)
                    {
                        response.ContentType = defaultContentType;
                    }
                    if (bodyPrefix != null && response.ContentType.IndexOf(ContentType.Json, StringComparison.InvariantCultureIgnoreCase) >= 0)
                    {
                        response.ContentType = ContentType.JavaScript;
                    }

                    if (EndpointHost.Config.AppendUtf8CharsetOnContentTypes.Contains(response.ContentType))
                    {
                        response.ContentType += ContentType.Utf8Suffix;
                    }

                    var responseText = result as string;
                    if (responseText != null)
                    {
                        if (bodyPrefix != null)
                        {
                            response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                        }
                        WriteTextToResponse(response, responseText, defaultContentType);
                        if (bodySuffix != null)
                        {
                            response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                        }
                        return(true);
                    }

                    if (defaultAction == null)
                    {
                        throw new ArgumentNullException("defaultAction", String.Format(
                                                            "As result '{0}' is not a supported responseType, a defaultAction must be supplied",
                                                            (result != null ? result.GetType().Name : "")));
                    }

                    if (bodyPrefix != null)
                    {
                        response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                    }
                    if (result != null)
                    {
                        defaultAction(serializerCtx, result, response);
                    }
                    if (bodySuffix != null)
                    {
                        response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                    }

                    if (disposableResult != null)
                    {
                        disposableResult.Dispose();
                    }

                    return(false);
                }
                catch (Exception originalEx)
                {
                    //TM: It would be good to handle 'remote end dropped connection' problems here. Arguably they should at least be suppressible via configuration

                    //DB: Using standard ServiceStack configuration method
                    if (!EndpointHost.Config.WriteErrorsToResponse)
                    {
                        throw;
                    }

                    var errorMessage = String.Format(
                        "Error occured while Processing Request: [{0}] {1}", originalEx.GetType().Name, originalEx.Message);

                    Log.Error(errorMessage, originalEx);

                    var operationName = result != null
                        ? result.GetType().Name.Replace("Response", "")
                        : "OperationName";

                    try
                    {
                        if (!response.IsClosed)
                        {
                            response.WriteErrorToResponse(
                                serializerCtx.Get <IHttpRequest>(),
                                defaultContentType,
                                operationName,
                                errorMessage,
                                originalEx,
                                (int)HttpStatusCode.InternalServerError);
                        }
                    }
                    catch (Exception writeErrorEx)
                    {
                        //Exception in writing to response should not hide the original exception
                        Log.Info("Failed to write error to response: {0}", writeErrorEx);
                        throw originalEx;
                    }
                    return(true);
                }
                finally
                {
                    response.EndServiceStackRequest(skipHeaders: true);
                }
            }
        }
        /// <summary>
        /// Writes to response.
        /// Response headers are customizable by implementing IHasOptions an returning Dictionary of Http headers.
        /// </summary>
        /// <param name="response">The response.</param>
        /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
        /// <param name="defaultAction">The default action.</param>
        /// <param name="request">The serialization context.</param>
        /// <param name="bodyPrefix">Add prefix to response body if any</param>
        /// <param name="bodySuffix">Add suffix to response body if any</param>
        /// <returns></returns>
        public static Task<bool> WriteToResponse(this IResponse response, object result, ResponseSerializerDelegate defaultAction, IRequest request, byte[] bodyPrefix, byte[] bodySuffix)
        {
            using (Profiler.Current.Step("Writing to Response"))
            {
                var defaultContentType = request.ResponseContentType;
                try
                {
                    if (result == null)
                    {
                        response.EndRequestWithNoContent();
                        return TypeConstants.TrueTask;
                    }

                    ApplyGlobalResponseHeaders(response);

                    IDisposable resultScope = null;

                    var httpResult = result as IHttpResult;
                    if (httpResult != null)
                    {
                        if (httpResult.ResultScope != null)
                            resultScope = httpResult.ResultScope();

                        if (httpResult.RequestContext == null)
                            httpResult.RequestContext = request;

                        var paddingLength = bodyPrefix != null ? bodyPrefix.Length : 0;
                        if (bodySuffix != null)
                            paddingLength += bodySuffix.Length;

                        httpResult.PaddingLength = paddingLength;

                        var httpError = httpResult as IHttpError;
                        if (httpError != null)
                        {
                            response.Dto = httpError.CreateErrorResponse();
                            if (response.HandleCustomErrorHandler(request,
                                defaultContentType, httpError.Status, response.Dto))
                            {
                                return TypeConstants.TrueTask;
                            }
                        }

                        response.Dto = response.Dto ?? httpResult.GetDto();

                        response.StatusCode = httpResult.Status;
                        response.StatusDescription = (httpResult.StatusDescription ?? httpResult.StatusCode.ToString()).Localize(request);
                        if (string.IsNullOrEmpty(httpResult.ContentType))
                        {
                            httpResult.ContentType = defaultContentType;
                        }
                        response.ContentType = httpResult.ContentType;

                        if (httpResult.Cookies != null)
                        {
                            foreach (var cookie in httpResult.Cookies)
                            {
                                response.SetCookie(cookie);
                            }
                        }
                    }
                    else
                    {
                        response.Dto = result;
                    }

                    /* Mono Error: Exception: Method not found: 'System.Web.HttpResponse.get_Headers' */
                    var responseOptions = result as IHasOptions;
                    if (responseOptions != null)
                    {
                        //Reserving options with keys in the format 'xx.xxx' (No Http headers contain a '.' so its a safe restriction)
                        const string reservedOptions = ".";

                        foreach (var responseHeaders in responseOptions.Options)
                        {
                            if (responseHeaders.Key.Contains(reservedOptions)) continue;
                            if (responseHeaders.Key == HttpHeaders.ContentLength)
                            {
                                response.SetContentLength(long.Parse(responseHeaders.Value));
                                continue;
                            }

                            if (Log.IsDebugEnabled)
                                Log.DebugFormat("Setting Custom HTTP Header: {0}: {1}", responseHeaders.Key, responseHeaders.Value);

                            if (Env.IsMono && responseHeaders.Key.EqualsIgnoreCase(HttpHeaders.ContentType))
                            {
                                response.ContentType = responseHeaders.Value;
                            }
                            else
                            {
                                response.AddHeader(responseHeaders.Key, responseHeaders.Value);
                            }
                        }
                    }

                    //ContentType='text/html' is the default for a HttpResponse
                    //Do not override if another has been set
                    if (response.ContentType == null || response.ContentType == MimeTypes.Html)
                    {
                        response.ContentType = defaultContentType;
                    }
                    if (bodyPrefix != null && response.ContentType.IndexOf(MimeTypes.Json, StringComparison.InvariantCultureIgnoreCase) >= 0)
                    {
                        response.ContentType = MimeTypes.JavaScript;
                    }

                    if (HostContext.Config.AppendUtf8CharsetOnContentTypes.Contains(response.ContentType))
                    {
                        response.ContentType += ContentFormat.Utf8Suffix;
                    }

                    using (resultScope)
                    using (HostContext.Config.AllowJsConfig ? JsConfig.CreateScope(request.QueryString[Keywords.JsConfig]) : null)
                    {
                        var disposableResult = result as IDisposable;
                        if (WriteToOutputStream(response, result, bodyPrefix, bodySuffix))
                        {
                            response.Flush(); //required for Compression
                            if (disposableResult != null) disposableResult.Dispose();
                            return TypeConstants.TrueTask;
                        }

                        if (httpResult != null)
                            result = httpResult.Response;

                        var responseText = result as string;
                        if (responseText != null)
                        {
                            if (bodyPrefix != null) response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);

                            if (response.ContentType == null || response.ContentType == MimeTypes.Html)
                                response.ContentType = defaultContentType;
                            response.Write(responseText);

                            if (bodySuffix != null) response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                            return TypeConstants.TrueTask;
                        }

                        if (defaultAction == null)
                        {
                            throw new ArgumentNullException("defaultAction", String.Format(
                                "As result '{0}' is not a supported responseType, a defaultAction must be supplied",
                                (result != null ? result.GetType().GetOperationName() : "")));
                        }

                        if (bodyPrefix != null)
                            response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);

                        if (result != null)
                            defaultAction(request, result, response);

                        if (bodySuffix != null)
                            response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);

                        if (disposableResult != null)
                            disposableResult.Dispose();
                    }

                    return TypeConstants.FalseTask;
                }
                catch (Exception originalEx)
                {
                    return HandleResponseWriteException(originalEx, request, response, defaultContentType);
                }
                finally
                {
                    response.EndRequest(skipHeaders: true);
                }
            }
        }
        /// <summary>
        /// Writes to response.
        /// Response headers are customizable by implementing IHasOptions an returning Dictionary of Http headers.
        /// </summary>
        /// <param name="response">The response.</param>
        /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
        /// <param name="defaultAction">The default action.</param>
        /// <param name="request">The serialization context.</param>
        /// <param name="bodyPrefix">Add prefix to response body if any</param>
        /// <param name="bodySuffix">Add suffix to response body if any</param>
        /// <returns></returns>
        public static Task<bool> WriteToResponse(this IResponse response, object result, ResponseSerializerDelegate defaultAction, IRequest request, byte[] bodyPrefix, byte[] bodySuffix)
        {
            using (Profiler.Current.Step("Writing to Response"))
            {
                var defaultContentType = request.ResponseContentType;
                try
                {
                    if (result == null)
                    {
                        response.EndRequestWithNoContent();
                        return TrueTask;
                    }

                    ApplyGlobalResponseHeaders(response);

                    var httpResult = result as IHttpResult;
                    if (httpResult != null)
                    {
                        if (httpResult.RequestContext == null)
                        {
                            httpResult.RequestContext = request;
                        }

                        var paddingLength = bodyPrefix != null ? bodyPrefix.Length : 0;
                        if (bodySuffix != null)
                            paddingLength += bodySuffix.Length;

                        httpResult.PaddingLength = paddingLength;

                        var httpError = httpResult as IHttpError;
                        if (httpError != null)
                        {
                            response.Dto = httpError.CreateErrorResponse();
                            if (response.HandleCustomErrorHandler(request,
                                defaultContentType, httpError.Status, response.Dto))
                            {
                                return TrueTask;
                            }
                        }

                        response.Dto = response.Dto ?? httpResult.GetDto();

                        response.StatusCode = httpResult.Status;
                        response.StatusDescription = httpResult.StatusDescription ?? httpResult.StatusCode.ToString();
                        if (string.IsNullOrEmpty(httpResult.ContentType))
                        {
                            httpResult.ContentType = defaultContentType;
                        }
                        response.ContentType = httpResult.ContentType;
                    }
                    else
                    {
                        response.Dto = result;
                    }

                    /* Mono Error: Exception: Method not found: 'System.Web.HttpResponse.get_Headers' */
                    var responseOptions = result as IHasOptions;
                    if (responseOptions != null)
                    {
                        //Reserving options with keys in the format 'xx.xxx' (No Http headers contain a '.' so its a safe restriction)
                        const string reservedOptions = ".";

                        foreach (var responseHeaders in responseOptions.Options)
                        {
                            if (responseHeaders.Key.Contains(reservedOptions)) continue;
                            if (responseHeaders.Key == HttpHeaders.ContentLength)
                            {
                                response.SetContentLength(long.Parse(responseHeaders.Value));
                                continue;
                            }

                            Log.DebugFormat("Setting Custom HTTP Header: {0}: {1}", responseHeaders.Key, responseHeaders.Value);
                            response.AddHeader(responseHeaders.Key, responseHeaders.Value);
                        }
                    }

                    var disposableResult = result as IDisposable;
                    if (WriteToOutputStream(response, result, bodyPrefix, bodySuffix))
                    {
                        response.Flush(); //required for Compression
                        if (disposableResult != null) disposableResult.Dispose();
                        return TrueTask;
                    }

                    if (httpResult != null)
                    {
                        result = httpResult.Response;
                    }

                    //ContentType='text/html' is the default for a HttpResponse
                    //Do not override if another has been set
                    if (response.ContentType == null || response.ContentType == MimeTypes.Html)
                    {
                        response.ContentType = defaultContentType;
                    }
                    if (bodyPrefix != null && response.ContentType.IndexOf(MimeTypes.Json, StringComparison.InvariantCultureIgnoreCase) >= 0)
                    {
                        response.ContentType = MimeTypes.JavaScript;
                    }

                    if (HostContext.Config.AppendUtf8CharsetOnContentTypes.Contains(response.ContentType))
                    {
                        response.ContentType += ContentFormat.Utf8Suffix;
                    }

                    var responseText = result as string;
                    if (responseText != null)
                    {                        
                        if (bodyPrefix != null) response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                        WriteTextToResponse(response, responseText, defaultContentType);
                        if (bodySuffix != null) response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                        return TrueTask;
                    }

                    if (defaultAction == null)
                    {
                        throw new ArgumentNullException("defaultAction", String.Format(
                        "As result '{0}' is not a supported responseType, a defaultAction must be supplied",
                        (result != null ? result.GetType().GetOperationName() : "")));
                    }

                    if (bodyPrefix != null) response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                    if (result != null) defaultAction(request, result, response);
                    if (bodySuffix != null) response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);

                    if (disposableResult != null) disposableResult.Dispose();

                    return FalseTask;
                }
                catch (Exception originalEx)
                {
                    HostContext.RaiseUncaughtException(request, response, request.OperationName, originalEx);

                    if (!HostContext.Config.WriteErrorsToResponse) 
                        return originalEx.AsTaskException<bool>();

                    var errorMessage = String.Format(
                    "Error occured while Processing Request: [{0}] {1}", originalEx.GetType().GetOperationName(), originalEx.Message);

                    try
                    {
                        if (!response.IsClosed)
                        {
                            response.WriteErrorToResponse(
                                request,
                                defaultContentType,
                                request.OperationName,
                                errorMessage,
                                originalEx,
                                (int)HttpStatusCode.InternalServerError);
                        }
                    }
                    catch (Exception writeErrorEx)
                    {
                        //Exception in writing to response should not hide the original exception
                        Log.Info("Failed to write error to response: {0}", writeErrorEx);
                        return originalEx.AsTaskException<bool>();
                    }
                    return TrueTask;
                }
                finally
                {
                    response.EndRequest(skipHeaders: true);
                }
            }
        }
Beispiel #10
0
        /// <summary>
        /// Writes to response.
        /// Response headers are customizable by implementing IHasOptions an returning Dictionary of Http headers.
        /// </summary>
        /// <param name="response">The response.</param>
        /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
        /// <param name="defaultAction">The default action.</param>
        /// <param name="serializerCtx">The serialization context.</param>
        /// <param name="bodyPrefix">Add prefix to response body if any</param>
        /// <param name="bodySuffix">Add suffix to response body if any</param>
        /// <returns></returns>
        public static bool WriteToResponse(this IHttpResponse response, object result, ResponseSerializerDelegate defaultAction, IRequestContext serializerCtx, byte[] bodyPrefix, byte[] bodySuffix)
        {
            var defaultContentType = serializerCtx.ResponseContentType;

            try
            {
                if (result == null)
                {
                    return(true);
                }

                foreach (var globalResponseHeader in EndpointHost.Config.GlobalResponseHeaders)
                {
                    response.AddHeader(globalResponseHeader.Key, globalResponseHeader.Value);
                }

                var httpResult = result as IHttpResult;
                if (httpResult != null)
                {
                    response.StatusCode = (int)httpResult.StatusCode;
                    if (string.IsNullOrEmpty(httpResult.ContentType))
                    {
                        httpResult.ContentType = defaultContentType;
                    }
                    response.ContentType = httpResult.ContentType;
                }

                /* Mono Error: Exception: Method not found: 'System.Web.HttpResponse.get_Headers' */
                var responseOptions = result as IHasOptions;
                if (responseOptions != null)
                {
                    //Reserving options with keys in the format 'xx.xxx' (No Http headers contain a '.' so its a safe restriction)
                    const string reservedOptions = ".";

                    foreach (var responseHeaders in responseOptions.Options)
                    {
                        if (responseHeaders.Key.Contains(reservedOptions))
                        {
                            continue;
                        }

                        Log.DebugFormat("Setting Custom HTTP Header: {0}: {1}", responseHeaders.Key, responseHeaders.Value);
                        response.AddHeader(responseHeaders.Key, responseHeaders.Value);
                    }
                }

                if (WriteToOutputStream(response.OutputStream, result))
                {
                    return(true);
                }

                if (httpResult != null)
                {
                    result = httpResult.Response;
                }

                //ContentType='text/html' is the default for a HttpResponse
                //Do not override if another has been set
                if (response.ContentType == null || response.ContentType == ContentType.Html)
                {
                    response.ContentType = defaultContentType;
                }

                var responseText = result as string;
                if (responseText != null)
                {
                    if (bodyPrefix != null)
                    {
                        response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                    }
                    WriteTextToResponse(response, responseText, defaultContentType);
                    if (bodySuffix != null)
                    {
                        response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                    }
                    return(true);
                }

                if (defaultAction == null)
                {
                    throw new ArgumentNullException("defaultAction", string.Format(
                                                        "As result '{0}' is not a supported responseType, a defaultAction must be supplied",
                                                        result.GetType().Name));
                }

                if (bodyPrefix != null)
                {
                    response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                }
                defaultAction(serializerCtx, result, response);
                if (bodySuffix != null)
                {
                    response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                }

                return(false);
            }
            catch (Exception ex)
            {
                var errorMessage = string.Format("Error occured while Processing Request: [{0}] {1}",
                                                 ex.GetType().Name, ex.Message);
                Log.Error(errorMessage, ex);

                var operationName = result != null
                                        ? result.GetType().Name.Replace("Response", "")
                                        : "OperationName";

                response.WriteErrorToResponse(defaultContentType, operationName, errorMessage, ex);
                return(true);
            }
            finally
            {
                response.Close();
            }
        }
        /// <summary>
        /// Writes to response.
        /// Response headers are customizable by implementing IHasOptions an returning Dictionary of Http headers.
        /// </summary>
        /// <param name="response">The response.</param>
        /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
        /// <param name="defaultAction">The default action.</param>
        /// <param name="request">The serialization context.</param>
        /// <param name="bodyPrefix">Add prefix to response body if any</param>
        /// <param name="bodySuffix">Add suffix to response body if any</param>
        /// <returns></returns>
        public static Task <bool> WriteToResponse(this IResponse response, object result, ResponseSerializerDelegate defaultAction, IRequest request, byte[] bodyPrefix, byte[] bodySuffix)
        {
            using (Profiler.Current.Step("Writing to Response"))
            {
                var defaultContentType = request.ResponseContentType;
                try
                {
                    if (result == null)
                    {
                        response.EndRequestWithNoContent();
                        return(TypeConstants.TrueTask);
                    }

                    ApplyGlobalResponseHeaders(response);

                    IDisposable resultScope = null;

                    var httpResult = result as IHttpResult;
                    if (httpResult != null)
                    {
                        if (httpResult.ResultScope != null)
                        {
                            resultScope = httpResult.ResultScope();
                        }

                        if (httpResult.RequestContext == null)
                        {
                            httpResult.RequestContext = request;
                        }

                        var paddingLength = bodyPrefix?.Length ?? 0;
                        if (bodySuffix != null)
                        {
                            paddingLength += bodySuffix.Length;
                        }

                        httpResult.PaddingLength = paddingLength;

                        var httpError = httpResult as IHttpError;
                        if (httpError != null)
                        {
                            response.Dto = httpError.CreateErrorResponse();
                            if (response.HandleCustomErrorHandler(request,
                                                                  defaultContentType, httpError.Status, response.Dto))
                            {
                                return(TypeConstants.TrueTask);
                            }
                        }

                        response.Dto = response.Dto ?? httpResult.GetDto();

                        response.StatusCode        = httpResult.Status;
                        response.StatusDescription = (httpResult.StatusDescription ?? httpResult.StatusCode.ToString()).Localize(request);
                        if (string.IsNullOrEmpty(httpResult.ContentType))
                        {
                            httpResult.ContentType = defaultContentType;
                        }
                        response.ContentType = httpResult.ContentType;

                        if (httpResult.Cookies != null)
                        {
                            foreach (var cookie in httpResult.Cookies)
                            {
                                response.SetCookie(cookie);
                            }
                        }
                    }
                    else
                    {
                        response.Dto = result;
                    }

                    /* Mono Error: Exception: Method not found: 'System.Web.HttpResponse.get_Headers' */
                    var responseOptions = result as IHasOptions;
                    if (responseOptions != null)
                    {
                        //Reserving options with keys in the format 'xx.xxx' (No Http headers contain a '.' so its a safe restriction)
                        const string reservedOptions = ".";

                        foreach (var responseHeaders in responseOptions.Options)
                        {
                            if (responseHeaders.Key.Contains(reservedOptions))
                            {
                                continue;
                            }
                            if (responseHeaders.Key == HttpHeaders.ContentLength)
                            {
                                response.SetContentLength(long.Parse(responseHeaders.Value));
                                continue;
                            }

                            if (Log.IsDebugEnabled)
                            {
                                Log.Debug($"Setting Custom HTTP Header: {responseHeaders.Key}: {responseHeaders.Value}");
                            }

                            if (Env.IsMono && responseHeaders.Key.EqualsIgnoreCase(HttpHeaders.ContentType))
                            {
                                response.ContentType = responseHeaders.Value;
                            }
                            else
                            {
                                response.AddHeader(responseHeaders.Key, responseHeaders.Value);
                            }
                        }
                    }

                    //ContentType='text/html' is the default for a HttpResponse
                    //Do not override if another has been set
                    if (response.ContentType == null || response.ContentType == MimeTypes.Html)
                    {
                        response.ContentType = defaultContentType;
                    }
                    if (bodyPrefix != null && response.ContentType.IndexOf(MimeTypes.Json, StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        response.ContentType = MimeTypes.JavaScript;
                    }

                    if (HostContext.Config.AppendUtf8CharsetOnContentTypes.Contains(response.ContentType))
                    {
                        response.ContentType += ContentFormat.Utf8Suffix;
                    }

                    using (resultScope)
                        using (HostContext.Config.AllowJsConfig ? JsConfig.CreateScope(request.QueryString[Keywords.JsConfig]) : null)
                        {
                            var disposableResult = result as IDisposable;
                            if (WriteToOutputStream(response, result, bodyPrefix, bodySuffix))
                            {
                                response.Flush(); //required for Compression
                                disposableResult?.Dispose();
                                return(TypeConstants.TrueTask);
                            }

                            if (httpResult != null)
                            {
                                result = httpResult.Response;
                            }

                            var responseText = result as string;
                            if (responseText != null)
                            {
                                if (bodyPrefix != null)
                                {
                                    response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                                }

                                if (response.ContentType == null || response.ContentType == MimeTypes.Html)
                                {
                                    response.ContentType = defaultContentType;
                                }
                                response.Write(responseText);

                                if (bodySuffix != null)
                                {
                                    response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                                }
                                return(TypeConstants.TrueTask);
                            }

                            if (defaultAction == null)
                            {
                                throw new ArgumentNullException(nameof(defaultAction),
                                                                $"As result '{(result != null ? result.GetType().GetOperationName() : "")}' is not a supported responseType, a defaultAction must be supplied");
                            }

                            if (bodyPrefix != null)
                            {
                                response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                            }

                            if (result != null)
                            {
                                defaultAction(request, result, response);
                            }

                            if (bodySuffix != null)
                            {
                                response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                            }

                            disposableResult?.Dispose();
                        }

                    return(TypeConstants.FalseTask);
                }
                catch (Exception originalEx)
                {
                    return(HandleResponseWriteException(originalEx, request, response, defaultContentType));
                }
                finally
                {
                    response.EndRequest(skipHeaders: true);
                }
            }
        }
        /// <summary>
        /// Writes to response.
        /// Response headers are customizable by implementing IHasOptions an returning Dictionary of Http headers.
        /// </summary>
        /// <param name="response">The response.</param>
        /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
        /// <param name="defaultAction">The default action.</param>
        /// <param name="serializerCtx">The serialization context.</param>
        /// <param name="bodyPrefix">Add prefix to response body if any</param>
        /// <param name="bodySuffix">Add suffix to response body if any</param>
        /// <returns></returns>
        public static bool WriteToResponse(this IHttpResponse response, object result, ResponseSerializerDelegate defaultAction, IRequestContext serializerCtx, byte[] bodyPrefix, byte[] bodySuffix)
        {
            var defaultContentType = serializerCtx.ResponseContentType;
            try
            {
                if (result == null) return true;

                foreach (var globalResponseHeader in EndpointHost.Config.GlobalResponseHeaders)
                {
                    response.AddHeader(globalResponseHeader.Key, globalResponseHeader.Value);
                }

                var httpResult = result as IHttpResult;
                if (httpResult != null)
                {
                    response.StatusCode = (int)httpResult.StatusCode;
                    if (string.IsNullOrEmpty(httpResult.ContentType))
                    {
                        httpResult.ContentType = defaultContentType;
                    }
                    response.ContentType = httpResult.ContentType;
                }

                /* Mono Error: Exception: Method not found: 'System.Web.HttpResponse.get_Headers' */
                var responseOptions = result as IHasOptions;
                if (responseOptions != null)
                {
                    //Reserving options with keys in the format 'xx.xxx' (No Http headers contain a '.' so its a safe restriction)
                    const string reservedOptions = ".";

                    foreach (var responseHeaders in responseOptions.Options)
                    {
                        if (responseHeaders.Key.Contains(reservedOptions)) continue;

                        Log.DebugFormat("Setting Custom HTTP Header: {0}: {1}", responseHeaders.Key, responseHeaders.Value);
                        response.AddHeader(responseHeaders.Key, responseHeaders.Value);
                    }
                }

                if (WriteToOutputStream(response.OutputStream, result))
                {
                    return true;
                }

                if (httpResult != null)
                {
                    result = httpResult.Response;
                }

                //ContentType='text/html' is the default for a HttpResponse
                //Do not override if another has been set
                if (response.ContentType == null || response.ContentType == ContentType.Html)
                {
                    response.ContentType = defaultContentType;
                }

                var responseText = result as string;
                if (responseText != null)
                {
                    if (bodyPrefix != null) response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                    WriteTextToResponse(response, responseText, defaultContentType);
                    if (bodySuffix != null) response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                    return true;
                }

                if (defaultAction == null)
                {
                    throw new ArgumentNullException("defaultAction", string.Format(
                        "As result '{0}' is not a supported responseType, a defaultAction must be supplied",
                        result.GetType().Name));
                }

                if (bodyPrefix != null) response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                defaultAction(serializerCtx, result, response);
                if (bodySuffix != null) response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);

                return false;
            }
            catch (Exception ex)
            {
                var errorMessage = string.Format("Error occured while Processing Request: [{0}] {1}",
                    ex.GetType().Name, ex.Message);
                Log.Error(errorMessage, ex);

                var operationName = result != null
                    ? result.GetType().Name.Replace("Response", "")
                    : "OperationName";

                response.WriteErrorToResponse(defaultContentType, operationName, errorMessage, ex);
                return true;
            }
            finally
            {
                response.Close();
            }
        }
 public static Task <bool> WriteToResponse(this IResponse httpRes, object result, ResponseSerializerDelegate serializer, IRequest serializationContext, CancellationToken token = default(CancellationToken))
 {
     return(httpRes.WriteToResponse(result, serializer, serializationContext, null, null, token));
 }
        /// <summary>
        /// Writes to response.
        /// Response headers are customizable by implementing IHasOptions an returning Dictionary of Http headers.
        /// </summary>
        /// <param name="response">The response.</param>
        /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
        /// <param name="defaultAction">The default action.</param>
        /// <param name="serializerCtx">The serialization context.</param>
        /// <param name="bodyPrefix">Add prefix to response body if any</param>
        /// <param name="bodySuffix">Add suffix to response body if any</param>
        /// <returns></returns>
        public static bool WriteToResponse(this IHttpResponse response, IHttpRequest request, object result, ResponseSerializerDelegate defaultAction, IRequestContext serializerCtx, byte[] bodyPrefix, byte[] bodySuffix)
        {
            var defaultContentType = serializerCtx.ResponseContentType;
            AckCodeType ack = AckCodeType.Success;
            bool completed = true;
            try
            {
                if (result == null)
                {
                    response.EndRequestWithNoContent();
                    return true;
                }

                ApplyGlobalResponseHeaders(response);

                /* Mono Error: Exception: Method not found: 'System.Web.HttpResponse.get_Headers' */
                var responseOptions = result as IHasOptions;
                if (responseOptions != null)
                {
                    //Reserving options with keys in the format 'xx.xxx' (No Http headers contain a '.' so its a safe restriction)
                    const string reservedOptions = ".";

                    foreach (var responseHeaders in responseOptions.Options)
                    {
                        if (responseHeaders.Key.Contains(reservedOptions)) continue;

                        Log.Debug(string.Format("Setting Custom HTTP Header: {0}: {1}", responseHeaders.Key, responseHeaders.Value),
                            new Dictionary<string, string>() 
                            {
                                {"ErrorCode", "FXD300061"}
                            });
                        response.AddHeader(responseHeaders.Key, responseHeaders.Value);
                    }
                }

                var disposableResult = result as IDisposable;

                //ContentType='text/html' is the default for a HttpResponse
                //Do not override if another has been set
                if (response.ContentType == null || response.ContentType == ContentType.Html)
                {
                    response.ContentType = defaultContentType;
                }
                if (bodyPrefix != null && response.ContentType.IndexOf(ContentType.Json, StringComparison.InvariantCultureIgnoreCase) >= 0)
                {
                    response.ContentType = ContentType.JavaScript;
                }

                if (EndpointHost.Config.AppendUtf8CharsetOnContentTypes.Contains(response.ContentType))
                {
                    response.ContentType += ContentType.Utf8Suffix;
                }

                var responseText = result as string;
                if (responseText != null)
                {
                    if (bodyPrefix != null) response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                    WriteTextToResponse(response, responseText, defaultContentType);
                    if (bodySuffix != null) response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                    return true;
                }

                var commonResponseDto = result as IHasResponseStatus;
                if (commonResponseDto != null)
                {
                    // defensive programming
                    if (commonResponseDto.ResponseStatus == null)
                    {
                        commonResponseDto.ResponseStatus = new ResponseStatusType();
                    }
                    commonResponseDto.ResponseStatus.Timestamp = DateTime.Now;
                    // TODO add version

                    // post ack check, in case developer forget to set ack according to error status 
                    bool hasError = false;
                    if (commonResponseDto.ResponseStatus.Ack == AckCodeType.Success && commonResponseDto.ResponseStatus.Errors.Count > 0)
                    {
                        foreach (ErrorDataType error in commonResponseDto.ResponseStatus.Errors)
                        {
                            if (error.SeverityCode == SeverityCodeType.Error)
                            {
                                hasError = true;
                                break;
                            }
                        }
                        if (hasError)
                        {
                            commonResponseDto.ResponseStatus.Ack = AckCodeType.Failure;
                        }
                    }

                    ack = commonResponseDto.ResponseStatus.Ack;

                    AddRequestInfoToResponseStatus(serializerCtx.Get<IHttpRequest>(), commonResponseDto);
                }

                // Defensive programming, in normal case, we should not see GenericErrorResponseType here
                // In case any exception, we set http status code to trigger SOA C# client side WebServiceException
                var genericErrorResponseDto = result as GenericErrorResponseType;
                if (genericErrorResponseDto != null)
                {
                    response.StatusCode = (int)HttpStatusCode.InternalServerError;
                    ack = AckCodeType.Failure;
                }

                if (defaultAction == null)
                {
                    throw new ArgumentNullException("defaultAction", String.Format(
                    "As result '{0}' is not a supported responseType, a defaultAction must be supplied",
                    (result != null ? result.GetType().Name : "")));
                }

                if (EndpointHost.Config.ServiceManager.MetadataMap[request.ServicePath].UseChunkedTransferEncoding)
                {
                    response.AddHeader(ServiceUtils.ResponseStatusHttpHeaderKey, ack.ToString());
                    response.UseChunkedTransferEncoding();
                }

                if (bodyPrefix != null) response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                if (result != null)
                {
                    try
                    {
                        defaultAction(serializerCtx, result, response);
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                    finally
                    {
                       //response.SerializationTimeInMillis = serializeTransaction.Transaction.DurationInMillis;
                    }
                }
                if (bodySuffix != null) response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                
                // Record response size
                response.ExecutionResult.ResponseSize = response.OutputStream.Length;

                if (disposableResult != null) disposableResult.Dispose();

                return false;
            }
            catch (Exception originalEx)
            {
                ack = AckCodeType.Failure;

                bool usedChunkedTransferEncoding = response.UsedChunkedTransferEncoding();
                var errorMessage = string.Format("Error occured while {0}: [{1}] {2}",
                    usedChunkedTransferEncoding ? "using chunked transfer encoding" : "processing request", originalEx.GetType().Name, originalEx.Message);
                Log.Error(errorMessage, originalEx, new Dictionary<string, string>(){ { "ErrorCode", "FXD300010" } });

                //TM: It would be good to handle 'remote end dropped connection' problems here. Arguably they should at least be suppressible via configuration

                //DB: Using standard ServiceStack configuration method

                if (!EndpointHost.Config.WriteErrorsToResponse)
                {
                    completed = false;
                    throw;
                }

                if (response.IsClosed)
                    return true;
                
                if (usedChunkedTransferEncoding)
                    return true;

                try
                {
                    response.WriteErrorToResponse(serializerCtx.Get<IHttpRequest>(), defaultContentType, originalEx);
                    return true;
                }
                catch (Exception writeErrorEx)
                {
                    //Exception in writing to response should not hide the original exception
                    Log.Error("Failed to write error to response: " + writeErrorEx.Message, writeErrorEx, new Dictionary<string, string>(){ { "ErrorCode", "FXD300010" } });
                    completed = false;
                    throw originalEx;
                }
            }
            finally
            {
                if (!response.UsedChunkedTransferEncoding())
                    response.AddHeader(ServiceUtils.ResponseStatusHttpHeaderKey, ack.ToString());

                if (completed)
                    response.LogRequest(request);

                response.EndRequest(true);
            }
        }
        public static bool WriteToResponse(this IHttpResponse httpRes, IHttpRequest httpReq, object result, ResponseSerializerDelegate serializer, byte[] bodyPrefix, byte[] bodySuffix)
        {
            if (result == null)
            {
                httpRes.LogRequest(httpReq);
                httpRes.EndRequestWithNoContent();
                return true;
            }

            var serializationContext = new HttpRequestContext(httpReq, httpRes, result);

            return httpRes.WriteToResponse(httpReq, result, serializer, serializationContext, bodyPrefix, bodySuffix);
        }
        /// <summary>
        /// Writes to response.
        /// Response headers are customizable by implementing IHasOptions an returning Dictionary of Http headers.
        /// </summary>
        /// <param name="response">The response.</param>
        /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
        /// <param name="defaultAction">The default action.</param>
        /// <param name="serializerCtx">The serialization context.</param>
        /// <param name="bodyPrefix">Add prefix to response body if any</param>
        /// <param name="bodySuffix">Add suffix to response body if any</param>
        /// <returns></returns>
        public static bool WriteToResponse(this IHttpResponse response, object result, ResponseSerializerDelegate defaultAction, IRequestContext serializerCtx, byte[] bodyPrefix, byte[] bodySuffix)
        {
            var defaultContentType = serializerCtx.ResponseContentType;
            try
            {
                if (result == null) return true;

                foreach (var globalResponseHeader in EndpointHost.Config.GlobalResponseHeaders)
                {
                    response.AddHeader(globalResponseHeader.Key, globalResponseHeader.Value);
                }

                var httpResult = result as IHttpResult;
                if (httpResult != null)
                {
                    response.StatusCode = (int)httpResult.StatusCode;
                    response.StatusDescription = httpResult.StatusDescription ?? httpResult.StatusCode.ToString();
                    if (string.IsNullOrEmpty(httpResult.ContentType))
                    {
                        httpResult.ContentType = defaultContentType;
                    }
                    response.ContentType = httpResult.ContentType;
                }

                /* Mono Error: Exception: Method not found: 'System.Web.HttpResponse.get_Headers' */
                var responseOptions = result as IHasOptions;
                if (responseOptions != null)
                {
                    //Reserving options with keys in the format 'xx.xxx' (No Http headers contain a '.' so its a safe restriction)
                    const string reservedOptions = ".";

                    foreach (var responseHeaders in responseOptions.Options)
                    {
                        if (responseHeaders.Key.Contains(reservedOptions)) continue;

                        Log.DebugFormat("Setting Custom HTTP Header: {0}: {1}", responseHeaders.Key, responseHeaders.Value);
                        response.AddHeader(responseHeaders.Key, responseHeaders.Value);
                    }
                }

                if (WriteToOutputStream(response.OutputStream, result))
                {
                    return true;
                }

                if (httpResult != null)
                {
                    result = httpResult.Response;
                }

                //ContentType='text/html' is the default for a HttpResponse
                //Do not override if another has been set
                if (response.ContentType == null || response.ContentType == ContentType.Html)
                {
                    response.ContentType = defaultContentType;
                }

                var responseText = result as string;
                if (responseText != null)
                {
                    if (bodyPrefix != null) response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                    WriteTextToResponse(response, responseText, defaultContentType);
                    if (bodySuffix != null) response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);
                    return true;
                }

                if (defaultAction == null)
                {
                    throw new ArgumentNullException("defaultAction", string.Format(
                        "As result '{0}' is not a supported responseType, a defaultAction must be supplied",
                        result.GetType().Name));
                }

                if (bodyPrefix != null) response.OutputStream.Write(bodyPrefix, 0, bodyPrefix.Length);
                defaultAction(serializerCtx, result, response);
                if (bodySuffix != null) response.OutputStream.Write(bodySuffix, 0, bodySuffix.Length);

                return false;
            }
            catch (Exception ex)
            {
                //TM: It would be good to handle 'remote end dropped connection' problems here. Arguably they should at least be suppressible via configuration

                //default value 'true' to be consistent with the way SS worked before this change
                bool writeErrorToResponse = ServiceStack.Configuration.ConfigUtils.GetAppSetting<bool>(ServiceStack.Configuration.Keys.WriteErrorsToResponse, true);

                if(!writeErrorToResponse) {
                    throw;
                }
                var errorMessage = string.Format("Error occured while Processing Request: [{0}] {1}",
                    ex.GetType().Name, ex.Message);
                Log.Error(errorMessage, ex);

                var operationName = result != null
                    ? result.GetType().Name.Replace("Response", "")
                    : "OperationName";

                try {
                    if(!response.IsClosed) {
                        response.WriteErrorToResponse(defaultContentType, operationName, errorMessage, ex);
                    }
                }
                catch(Exception WriteErrorEx) {
                    //Exception in writing to response should not hide the original exception
                    Log.Info("Failed to write error to response: {0}", WriteErrorEx);
                    //rethrow the original exception
                    throw ex;
                }
                return true;
            }
            finally
            {
                response.Close();
            }
        }