HttpResponseExtensionsInternal.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. //Copyright (c) Service Stack LLC. All Rights Reserved.
  2. //License: https://raw.github.com/ServiceStack/ServiceStack/master/license.txt
  3. using System;
  4. using System.IO;
  5. using System.Net;
  6. using System.Threading.Tasks;
  7. using System.Collections.Generic;
  8. using System.Threading;
  9. using MediaBrowser.Model.Services;
  10. using ServiceStack.Host;
  11. namespace ServiceStack
  12. {
  13. public static class HttpResponseExtensionsInternal
  14. {
  15. public static async Task<bool> WriteToOutputStream(IResponse response, object result)
  16. {
  17. var asyncStreamWriter = result as IAsyncStreamWriter;
  18. if (asyncStreamWriter != null)
  19. {
  20. await asyncStreamWriter.WriteToAsync(response.OutputStream, CancellationToken.None).ConfigureAwait(false);
  21. return true;
  22. }
  23. var streamWriter = result as IStreamWriter;
  24. if (streamWriter != null)
  25. {
  26. streamWriter.WriteTo(response.OutputStream);
  27. return true;
  28. }
  29. var stream = result as Stream;
  30. if (stream != null)
  31. {
  32. WriteTo(stream, response.OutputStream);
  33. return true;
  34. }
  35. var bytes = result as byte[];
  36. if (bytes != null)
  37. {
  38. response.ContentType = "application/octet-stream";
  39. response.SetContentLength(bytes.Length);
  40. response.OutputStream.Write(bytes, 0, bytes.Length);
  41. return true;
  42. }
  43. return false;
  44. }
  45. public static long WriteTo(Stream inStream, Stream outStream)
  46. {
  47. var memoryStream = inStream as MemoryStream;
  48. if (memoryStream != null)
  49. {
  50. memoryStream.WriteTo(outStream);
  51. return memoryStream.Position;
  52. }
  53. var data = new byte[4096];
  54. long total = 0;
  55. int bytesRead;
  56. while ((bytesRead = inStream.Read(data, 0, data.Length)) > 0)
  57. {
  58. outStream.Write(data, 0, bytesRead);
  59. total += bytesRead;
  60. }
  61. return total;
  62. }
  63. /// <summary>
  64. /// End a ServiceStack Request with no content
  65. /// </summary>
  66. public static void EndRequestWithNoContent(this IResponse httpRes)
  67. {
  68. if (httpRes.StatusCode == (int)HttpStatusCode.OK)
  69. {
  70. httpRes.StatusCode = (int)HttpStatusCode.NoContent;
  71. }
  72. httpRes.SetContentLength(0);
  73. }
  74. public static Task WriteToResponse(this IResponse httpRes, MediaBrowser.Model.Services.IRequest httpReq, object result)
  75. {
  76. if (result == null)
  77. {
  78. httpRes.EndRequestWithNoContent();
  79. return Task.FromResult(true);
  80. }
  81. var httpResult = result as IHttpResult;
  82. if (httpResult != null)
  83. {
  84. httpResult.RequestContext = httpReq;
  85. httpReq.ResponseContentType = httpResult.ContentType ?? httpReq.ResponseContentType;
  86. var httpResSerializer = ContentTypes.Instance.GetResponseSerializer(httpReq.ResponseContentType);
  87. return httpRes.WriteToResponse(httpResult, httpResSerializer, httpReq);
  88. }
  89. var serializer = ContentTypes.Instance.GetResponseSerializer(httpReq.ResponseContentType);
  90. return httpRes.WriteToResponse(result, serializer, httpReq);
  91. }
  92. private static object GetDto(object response)
  93. {
  94. if (response == null) return null;
  95. var httpResult = response as IHttpResult;
  96. return httpResult != null ? httpResult.Response : response;
  97. }
  98. /// <summary>
  99. /// Writes to response.
  100. /// Response headers are customizable by implementing IHasHeaders an returning Dictionary of Http headers.
  101. /// </summary>
  102. /// <param name="response">The response.</param>
  103. /// <param name="result">Whether or not it was implicity handled by ServiceStack's built-in handlers.</param>
  104. /// <param name="defaultAction">The default action.</param>
  105. /// <param name="request">The serialization context.</param>
  106. /// <returns></returns>
  107. public static async Task WriteToResponse(this IResponse response, object result, Action<IRequest, object, IResponse> defaultAction, MediaBrowser.Model.Services.IRequest request)
  108. {
  109. var defaultContentType = request.ResponseContentType;
  110. if (result == null)
  111. {
  112. response.EndRequestWithNoContent();
  113. return;
  114. }
  115. var httpResult = result as IHttpResult;
  116. if (httpResult != null)
  117. {
  118. if (httpResult.RequestContext == null)
  119. httpResult.RequestContext = request;
  120. response.Dto = response.Dto ?? GetDto(httpResult);
  121. response.StatusCode = httpResult.Status;
  122. response.StatusDescription = httpResult.StatusDescription ?? httpResult.StatusCode.ToString();
  123. if (string.IsNullOrEmpty(httpResult.ContentType))
  124. {
  125. httpResult.ContentType = defaultContentType;
  126. }
  127. response.ContentType = httpResult.ContentType;
  128. if (httpResult.Cookies != null)
  129. {
  130. var httpRes = response as IHttpResponse;
  131. if (httpRes != null)
  132. {
  133. foreach (var cookie in httpResult.Cookies)
  134. {
  135. httpRes.SetCookie(cookie);
  136. }
  137. }
  138. }
  139. }
  140. else
  141. {
  142. response.Dto = result;
  143. }
  144. /* Mono Error: Exception: Method not found: 'System.Web.HttpResponse.get_Headers' */
  145. var responseOptions = result as IHasHeaders;
  146. if (responseOptions != null)
  147. {
  148. //Reserving options with keys in the format 'xx.xxx' (No Http headers contain a '.' so its a safe restriction)
  149. const string reservedOptions = ".";
  150. foreach (var responseHeaders in responseOptions.Headers)
  151. {
  152. if (responseHeaders.Key.Contains(reservedOptions)) continue;
  153. if (responseHeaders.Key == "Content-Length")
  154. {
  155. response.SetContentLength(long.Parse(responseHeaders.Value));
  156. continue;
  157. }
  158. response.AddHeader(responseHeaders.Key, responseHeaders.Value);
  159. }
  160. }
  161. //ContentType='text/html' is the default for a HttpResponse
  162. //Do not override if another has been set
  163. if (response.ContentType == null || response.ContentType == "text/html")
  164. {
  165. response.ContentType = defaultContentType;
  166. }
  167. if (new HashSet<string> { "application/json", }.Contains(response.ContentType))
  168. {
  169. response.ContentType += "; charset=utf-8";
  170. }
  171. var disposableResult = result as IDisposable;
  172. var writeToOutputStreamResult = await WriteToOutputStream(response, result).ConfigureAwait(false);
  173. if (writeToOutputStreamResult)
  174. {
  175. response.Flush(); //required for Compression
  176. if (disposableResult != null) disposableResult.Dispose();
  177. return;
  178. }
  179. if (httpResult != null)
  180. result = httpResult.Response;
  181. var responseText = result as string;
  182. if (responseText != null)
  183. {
  184. if (response.ContentType == null || response.ContentType == "text/html")
  185. response.ContentType = defaultContentType;
  186. response.Write(responseText);
  187. return;
  188. }
  189. if (defaultAction == null)
  190. {
  191. throw new ArgumentNullException("defaultAction", String.Format(
  192. "As result '{0}' is not a supported responseType, a defaultAction must be supplied",
  193. (result != null ? result.GetType().GetOperationName() : "")));
  194. }
  195. if (result != null)
  196. defaultAction(request, result, response);
  197. if (disposableResult != null)
  198. disposableResult.Dispose();
  199. }
  200. }
  201. }