HttpRequestMessageProperty.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. //----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //----------------------------------------------------------------------------
  4. namespace System.ServiceModel.Channels
  5. {
  6. using System.Collections.Generic;
  7. using System.Net;
  8. using System.Net.Http;
  9. using System.Net.Http.Headers;
  10. using System.Runtime;
  11. public sealed class HttpRequestMessageProperty : IMessageProperty, IMergeEnabledMessageProperty
  12. {
  13. TraditionalHttpRequestMessageProperty traditionalProperty;
  14. HttpRequestMessageBackedProperty httpBackedProperty;
  15. bool initialCopyPerformed;
  16. bool useHttpBackedProperty;
  17. public HttpRequestMessageProperty()
  18. : this((IHttpHeaderProvider)null)
  19. {
  20. }
  21. internal HttpRequestMessageProperty(IHttpHeaderProvider httpHeaderProvider)
  22. {
  23. this.traditionalProperty = new TraditionalHttpRequestMessageProperty(httpHeaderProvider);
  24. this.useHttpBackedProperty = false;
  25. }
  26. internal HttpRequestMessageProperty(HttpRequestMessage httpRequestMessage)
  27. {
  28. this.httpBackedProperty = new HttpRequestMessageBackedProperty(httpRequestMessage);
  29. this.useHttpBackedProperty = true;
  30. }
  31. public static string Name
  32. {
  33. get { return "httpRequest"; }
  34. }
  35. public WebHeaderCollection Headers
  36. {
  37. get
  38. {
  39. return this.useHttpBackedProperty ?
  40. this.httpBackedProperty.Headers :
  41. this.traditionalProperty.Headers;
  42. }
  43. }
  44. public string Method
  45. {
  46. get
  47. {
  48. return this.useHttpBackedProperty ?
  49. this.httpBackedProperty.Method :
  50. this.traditionalProperty.Method;
  51. }
  52. set
  53. {
  54. if (value == null)
  55. {
  56. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
  57. }
  58. if (this.useHttpBackedProperty)
  59. {
  60. this.httpBackedProperty.Method = value;
  61. }
  62. else
  63. {
  64. this.traditionalProperty.Method = value;
  65. }
  66. }
  67. }
  68. public string QueryString
  69. {
  70. get
  71. {
  72. return this.useHttpBackedProperty ?
  73. this.httpBackedProperty.QueryString :
  74. this.traditionalProperty.QueryString;
  75. }
  76. set
  77. {
  78. if (value == null)
  79. {
  80. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
  81. }
  82. if (this.useHttpBackedProperty)
  83. {
  84. this.httpBackedProperty.QueryString = value;
  85. }
  86. else
  87. {
  88. this.traditionalProperty.QueryString = value;
  89. }
  90. }
  91. }
  92. public bool SuppressEntityBody
  93. {
  94. get
  95. {
  96. return this.useHttpBackedProperty ?
  97. this.httpBackedProperty.SuppressEntityBody :
  98. this.traditionalProperty.SuppressEntityBody;
  99. }
  100. set
  101. {
  102. if (this.useHttpBackedProperty)
  103. {
  104. this.httpBackedProperty.SuppressEntityBody = value;
  105. }
  106. else
  107. {
  108. this.traditionalProperty.SuppressEntityBody = value;
  109. }
  110. }
  111. }
  112. private HttpRequestMessage HttpRequestMessage
  113. {
  114. get
  115. {
  116. if (this.useHttpBackedProperty)
  117. {
  118. return this.httpBackedProperty.HttpRequestMessage;
  119. }
  120. return null;
  121. }
  122. }
  123. internal static HttpRequestMessage GetHttpRequestMessageFromMessage(Message message)
  124. {
  125. HttpRequestMessage httpRequestMessage = null;
  126. HttpRequestMessageProperty property = message.Properties.GetValue<HttpRequestMessageProperty>(HttpRequestMessageProperty.Name);
  127. if (property != null)
  128. {
  129. httpRequestMessage = property.HttpRequestMessage;
  130. if (httpRequestMessage != null)
  131. {
  132. httpRequestMessage.CopyPropertiesFromMessage(message);
  133. message.EnsureReadMessageState();
  134. }
  135. }
  136. return httpRequestMessage;
  137. }
  138. IMessageProperty IMessageProperty.CreateCopy()
  139. {
  140. if (!this.useHttpBackedProperty ||
  141. !this.initialCopyPerformed)
  142. {
  143. this.initialCopyPerformed = true;
  144. return this;
  145. }
  146. return this.httpBackedProperty.CreateTraditionalRequestMessageProperty();
  147. }
  148. bool IMergeEnabledMessageProperty.TryMergeWithProperty(object propertyToMerge)
  149. {
  150. // The ImmutableDispatchRuntime will merge MessageProperty instances from the
  151. // OperationContext (that were created before the response message was created) with
  152. // MessageProperty instances on the message itself. The message's version of the
  153. // HttpRequestMessageProperty may hold a reference to an HttpRequestMessage, and this
  154. // cannot be discarded, so values from the OperationContext's property must be set on
  155. // the message's version without completely replacing the message's property.
  156. if (this.useHttpBackedProperty)
  157. {
  158. HttpRequestMessageProperty requestProperty = propertyToMerge as HttpRequestMessageProperty;
  159. if (requestProperty != null)
  160. {
  161. if (!requestProperty.useHttpBackedProperty)
  162. {
  163. this.httpBackedProperty.MergeWithTraditionalProperty(requestProperty.traditionalProperty);
  164. requestProperty.traditionalProperty = null;
  165. requestProperty.httpBackedProperty = this.httpBackedProperty;
  166. requestProperty.useHttpBackedProperty = true;
  167. }
  168. return true;
  169. }
  170. }
  171. return false;
  172. }
  173. internal interface IHttpHeaderProvider
  174. {
  175. void CopyHeaders(WebHeaderCollection headers);
  176. }
  177. private class TraditionalHttpRequestMessageProperty
  178. {
  179. public const string DefaultMethod = "POST";
  180. public const string DefaultQueryString = "";
  181. WebHeaderCollection headers;
  182. IHttpHeaderProvider httpHeaderProvider;
  183. string method;
  184. public TraditionalHttpRequestMessageProperty(IHttpHeaderProvider httpHeaderProvider)
  185. {
  186. this.httpHeaderProvider = httpHeaderProvider;
  187. this.method = DefaultMethod;
  188. this.QueryString = DefaultQueryString;
  189. }
  190. public WebHeaderCollection Headers
  191. {
  192. get
  193. {
  194. if (this.headers == null)
  195. {
  196. this.headers = new WebHeaderCollection();
  197. if (this.httpHeaderProvider != null)
  198. {
  199. this.httpHeaderProvider.CopyHeaders(this.headers);
  200. this.httpHeaderProvider = null;
  201. }
  202. }
  203. return this.headers;
  204. }
  205. }
  206. public string Method
  207. {
  208. get
  209. {
  210. return this.method;
  211. }
  212. set
  213. {
  214. this.method = value;
  215. this.HasMethodBeenSet = true;
  216. }
  217. }
  218. public bool HasMethodBeenSet { get; private set; }
  219. public string QueryString { get; set; }
  220. public bool SuppressEntityBody { get; set; }
  221. }
  222. private class HttpRequestMessageBackedProperty
  223. {
  224. private HttpHeadersWebHeaderCollection headers;
  225. public HttpRequestMessageBackedProperty(HttpRequestMessage httpRequestMessage)
  226. {
  227. Fx.Assert(httpRequestMessage != null, "The 'httpRequestMessage' property should never be null.");
  228. this.HttpRequestMessage = httpRequestMessage;
  229. }
  230. public HttpRequestMessage HttpRequestMessage { get; private set; }
  231. public WebHeaderCollection Headers
  232. {
  233. get
  234. {
  235. if (this.headers == null)
  236. {
  237. this.headers = new HttpHeadersWebHeaderCollection(this.HttpRequestMessage);
  238. }
  239. return this.headers;
  240. }
  241. }
  242. public string Method
  243. {
  244. get
  245. {
  246. return this.HttpRequestMessage.Method.Method;
  247. }
  248. set
  249. {
  250. this.HttpRequestMessage.Method = new HttpMethod(value);
  251. }
  252. }
  253. public string QueryString
  254. {
  255. get
  256. {
  257. string query = this.HttpRequestMessage.RequestUri.Query;
  258. return query.Length > 0 ? query.Substring(1) : string.Empty;
  259. }
  260. set
  261. {
  262. UriBuilder uriBuilder = new UriBuilder(this.HttpRequestMessage.RequestUri);
  263. uriBuilder.Query = value;
  264. this.HttpRequestMessage.RequestUri = uriBuilder.Uri;
  265. }
  266. }
  267. public bool SuppressEntityBody
  268. {
  269. get
  270. {
  271. HttpContent content = this.HttpRequestMessage.Content;
  272. if (content != null)
  273. {
  274. long? contentLength = content.Headers.ContentLength;
  275. if (!contentLength.HasValue ||
  276. (contentLength.HasValue && contentLength.Value > 0))
  277. {
  278. return false;
  279. }
  280. }
  281. return true;
  282. }
  283. set
  284. {
  285. HttpContent content = this.HttpRequestMessage.Content;
  286. if (value && content != null &&
  287. (!content.Headers.ContentLength.HasValue ||
  288. content.Headers.ContentLength.Value > 0))
  289. {
  290. HttpContent newContent = new ByteArrayContent(EmptyArray<byte>.Instance);
  291. foreach (KeyValuePair<string, IEnumerable<string>> header in content.Headers)
  292. {
  293. newContent.Headers.AddHeaderWithoutValidation(header);
  294. }
  295. this.HttpRequestMessage.Content = newContent;
  296. content.Dispose();
  297. }
  298. else if (!value && content == null)
  299. {
  300. this.HttpRequestMessage.Content = new ByteArrayContent(EmptyArray<byte>.Instance);
  301. }
  302. }
  303. }
  304. public HttpRequestMessageProperty CreateTraditionalRequestMessageProperty()
  305. {
  306. HttpRequestMessageProperty copiedProperty = new HttpRequestMessageProperty();
  307. copiedProperty.Headers.Add(this.Headers);
  308. if (this.Method != TraditionalHttpRequestMessageProperty.DefaultMethod)
  309. {
  310. copiedProperty.Method = this.Method;
  311. }
  312. copiedProperty.QueryString = this.QueryString;
  313. copiedProperty.SuppressEntityBody = this.SuppressEntityBody;
  314. return copiedProperty;
  315. }
  316. public void MergeWithTraditionalProperty(TraditionalHttpRequestMessageProperty propertyToMerge)
  317. {
  318. if (propertyToMerge.HasMethodBeenSet)
  319. {
  320. this.Method = propertyToMerge.Method;
  321. }
  322. if (propertyToMerge.QueryString != TraditionalHttpRequestMessageProperty.DefaultQueryString)
  323. {
  324. this.QueryString = propertyToMerge.QueryString;
  325. }
  326. this.SuppressEntityBody = propertyToMerge.SuppressEntityBody;
  327. WebHeaderCollection headersToMerge = propertyToMerge.Headers;
  328. foreach (string headerKey in headersToMerge.AllKeys)
  329. {
  330. this.Headers[headerKey] = headersToMerge[headerKey];
  331. }
  332. }
  333. }
  334. }
  335. }