WebMessageFormatter.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. //
  2. // WebMessageFormatter.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. // Atsushi Enomoto <[email protected]>
  7. //
  8. // Copyright (C) 2008,2009 Novell, Inc (http://www.novell.com)
  9. // Copyright (C) 2011 Xamarin, Inc (http://xamarin.com)
  10. //
  11. // Permission is hereby granted, free of charge, to any person obtaining
  12. // a copy of this software and associated documentation files (the
  13. // "Software"), to deal in the Software without restriction, including
  14. // without limitation the rights to use, copy, modify, merge, publish,
  15. // distribute, sublicense, and/or sell copies of the Software, and to
  16. // permit persons to whom the Software is furnished to do so, subject to
  17. // the following conditions:
  18. //
  19. // The above copyright notice and this permission notice shall be
  20. // included in all copies or substantial portions of the Software.
  21. //
  22. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  25. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  26. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  27. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  28. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29. //
  30. using System;
  31. using System.Collections.Generic;
  32. using System.Globalization;
  33. using System.IO;
  34. using System.Reflection;
  35. using System.Runtime.Serialization;
  36. using System.Runtime.Serialization.Json;
  37. using System.ServiceModel;
  38. using System.ServiceModel.Channels;
  39. using System.ServiceModel.Description;
  40. using System.ServiceModel.Web;
  41. using System.Text;
  42. using System.Xml;
  43. #if NET_2_1
  44. using XmlObjectSerializer = System.Object;
  45. #endif
  46. namespace System.ServiceModel.Dispatcher
  47. {
  48. // This set of classes is to work as message formatters for
  49. // WebHttpBehavior. There are couple of aspects to differentiate
  50. // implementations:
  51. // - request/reply and client/server
  52. // by WebMessageFormatter hierarchy
  53. // - WebClientMessageFormatter - for client
  54. // - RequestClientFormatter - for request
  55. // - ReplyClientFormatter - for response
  56. // - WebDispatchMessageFormatter - for server
  57. // - RequestDispatchFormatter - for request
  58. // - ReplyDispatchFormatter - for response
  59. //
  60. // FIXME: below items need more work
  61. // - HTTP method differences
  62. // - GET (WebGet)
  63. // - POST (other way)
  64. // - output format: Stream, JSON, XML ...
  65. internal abstract class WebMessageFormatter
  66. {
  67. OperationDescription operation;
  68. ServiceEndpoint endpoint;
  69. QueryStringConverter converter;
  70. WebHttpBehavior behavior;
  71. UriTemplate template;
  72. WebAttributeInfo info = null;
  73. public WebMessageFormatter (OperationDescription operation, ServiceEndpoint endpoint, QueryStringConverter converter, WebHttpBehavior behavior)
  74. {
  75. this.operation = operation;
  76. this.endpoint = endpoint;
  77. this.converter = converter;
  78. this.behavior = behavior;
  79. ApplyWebAttribute ();
  80. #if !NET_2_1
  81. // This is a hack for WebScriptEnablingBehavior
  82. var jqc = converter as JsonQueryStringConverter;
  83. if (jqc != null)
  84. BodyName = jqc.CustomWrapperName;
  85. #endif
  86. }
  87. void ApplyWebAttribute ()
  88. {
  89. MethodInfo mi = operation.SyncMethod ?? operation.BeginMethod;
  90. object [] atts = mi.GetCustomAttributes (typeof (WebGetAttribute), false);
  91. if (atts.Length > 0)
  92. info = ((WebGetAttribute) atts [0]).Info;
  93. atts = mi.GetCustomAttributes (typeof (WebInvokeAttribute), false);
  94. if (atts.Length > 0)
  95. info = ((WebInvokeAttribute) atts [0]).Info;
  96. if (info == null)
  97. info = new WebAttributeInfo ();
  98. template = info.BuildUriTemplate (Operation, GetMessageDescription (MessageDirection.Input));
  99. }
  100. public string BodyName { get; set; }
  101. public WebHttpBehavior Behavior {
  102. get { return behavior; }
  103. }
  104. public WebAttributeInfo Info {
  105. get { return info; }
  106. }
  107. public WebMessageBodyStyle BodyStyle {
  108. get { return info.IsBodyStyleSetExplicitly ? info.BodyStyle : behavior.DefaultBodyStyle; }
  109. }
  110. public bool IsRequestBodyWrapped {
  111. get {
  112. switch (BodyStyle) {
  113. case WebMessageBodyStyle.Wrapped:
  114. case WebMessageBodyStyle.WrappedRequest:
  115. return true;
  116. }
  117. return BodyName != null;
  118. }
  119. }
  120. public bool IsResponseBodyWrapped {
  121. get {
  122. switch (BodyStyle) {
  123. case WebMessageBodyStyle.Wrapped:
  124. case WebMessageBodyStyle.WrappedResponse:
  125. return true;
  126. }
  127. return BodyName != null;
  128. }
  129. }
  130. public OperationDescription Operation {
  131. get { return operation; }
  132. }
  133. public QueryStringConverter Converter {
  134. get { return converter; }
  135. }
  136. public ServiceEndpoint Endpoint {
  137. get { return endpoint; }
  138. }
  139. public UriTemplate UriTemplate {
  140. get { return template; }
  141. }
  142. protected WebContentFormat ToContentFormat (WebMessageFormat src, object result)
  143. {
  144. if (result is Stream)
  145. return WebContentFormat.Raw;
  146. switch (src) {
  147. case WebMessageFormat.Xml:
  148. return WebContentFormat.Xml;
  149. case WebMessageFormat.Json:
  150. return WebContentFormat.Json;
  151. }
  152. throw new SystemException ("INTERNAL ERROR: should not happen");
  153. }
  154. protected string GetMediaTypeString (WebContentFormat fmt)
  155. {
  156. switch (fmt) {
  157. case WebContentFormat.Raw:
  158. return "application/octet-stream";
  159. case WebContentFormat.Json:
  160. return "application/json";
  161. case WebContentFormat.Xml:
  162. default:
  163. return "application/xml";
  164. }
  165. }
  166. protected void CheckMessageVersion (MessageVersion messageVersion)
  167. {
  168. if (messageVersion == null)
  169. throw new ArgumentNullException ("messageVersion");
  170. if (!MessageVersion.None.Equals (messageVersion))
  171. throw new ArgumentException (String.Format ("Only MessageVersion.None is supported. {0} is not.", messageVersion));
  172. }
  173. protected MessageDescription GetMessageDescription (MessageDirection dir)
  174. {
  175. foreach (MessageDescription md in operation.Messages)
  176. if (md.Direction == dir)
  177. return md;
  178. throw new SystemException ("INTERNAL ERROR: no corresponding message description for the specified direction: " + dir);
  179. }
  180. protected XmlObjectSerializer GetSerializer (WebContentFormat msgfmt, bool isWrapped, MessagePartDescription part)
  181. {
  182. if (part.Type == typeof (void))
  183. return null; // no serialization should be done.
  184. switch (msgfmt) {
  185. case WebContentFormat.Xml:
  186. if (xml_serializer == null)
  187. xml_serializer = isWrapped ? new DataContractSerializer (part.Type, part.Name, part.Namespace) : new DataContractSerializer (part.Type);
  188. return xml_serializer;
  189. case WebContentFormat.Json:
  190. // FIXME: after name argument they are hack
  191. if (json_serializer == null)
  192. #if MOONLIGHT
  193. json_serializer = new DataContractJsonSerializer (part.Type);
  194. #else
  195. json_serializer = isWrapped ? new DataContractJsonSerializer (part.Type, BodyName ?? part.Name, null, 0x100000, false, null, true) : new DataContractJsonSerializer (part.Type);
  196. #endif
  197. return json_serializer;
  198. default:
  199. throw new NotImplementedException (msgfmt.ToString ());
  200. }
  201. }
  202. XmlObjectSerializer xml_serializer, json_serializer;
  203. protected object DeserializeObject (XmlObjectSerializer serializer, Message message, MessageDescription md, bool isWrapped, WebContentFormat fmt)
  204. {
  205. // FIXME: handle ref/out parameters
  206. var reader = message.GetReaderAtBodyContents ();
  207. reader.MoveToContent ();
  208. bool wasEmptyElement = reader.IsEmptyElement;
  209. if (isWrapped) {
  210. if (fmt == WebContentFormat.Json)
  211. reader.ReadStartElement ("root", String.Empty); // note that the wrapper name is passed to the serializer.
  212. else
  213. reader.ReadStartElement (md.Body.WrapperName, md.Body.WrapperNamespace);
  214. }
  215. var ret = (serializer == null) ? null : ReadObjectBody (serializer, reader);
  216. if (isWrapped && !wasEmptyElement)
  217. reader.ReadEndElement ();
  218. return ret;
  219. }
  220. protected object ReadObjectBody (XmlObjectSerializer serializer, XmlReader reader)
  221. {
  222. #if NET_2_1
  223. return (serializer is DataContractJsonSerializer) ?
  224. ((DataContractJsonSerializer) serializer).ReadObject (reader) :
  225. ((DataContractSerializer) serializer).ReadObject (reader, true);
  226. #else
  227. return serializer.ReadObject (reader, true);
  228. #endif
  229. }
  230. internal class RequestClientFormatter : WebClientMessageFormatter
  231. {
  232. public RequestClientFormatter (OperationDescription operation, ServiceEndpoint endpoint, QueryStringConverter converter, WebHttpBehavior behavior)
  233. : base (operation, endpoint, converter, behavior)
  234. {
  235. }
  236. public override object DeserializeReply (Message message, object [] parameters)
  237. {
  238. throw new NotSupportedException ();
  239. }
  240. }
  241. internal class ReplyClientFormatter : WebClientMessageFormatter
  242. {
  243. public ReplyClientFormatter (OperationDescription operation, ServiceEndpoint endpoint, QueryStringConverter converter, WebHttpBehavior behavior)
  244. : base (operation, endpoint, converter, behavior)
  245. {
  246. }
  247. public override Message SerializeRequest (MessageVersion messageVersion, object [] parameters)
  248. {
  249. throw new NotSupportedException ();
  250. }
  251. }
  252. #if !NET_2_1
  253. internal class RequestDispatchFormatter : WebDispatchMessageFormatter
  254. {
  255. public RequestDispatchFormatter (OperationDescription operation, ServiceEndpoint endpoint, QueryStringConverter converter, WebHttpBehavior behavior)
  256. : base (operation, endpoint, converter, behavior)
  257. {
  258. }
  259. public override Message SerializeReply (MessageVersion messageVersion, object [] parameters, object result)
  260. {
  261. throw new NotSupportedException ();
  262. }
  263. }
  264. internal class ReplyDispatchFormatter : WebDispatchMessageFormatter
  265. {
  266. public ReplyDispatchFormatter (OperationDescription operation, ServiceEndpoint endpoint, QueryStringConverter converter, WebHttpBehavior behavior)
  267. : base (operation, endpoint, converter, behavior)
  268. {
  269. }
  270. public override void DeserializeRequest (Message message, object [] parameters)
  271. {
  272. throw new NotSupportedException ();
  273. }
  274. }
  275. #endif
  276. internal abstract class WebClientMessageFormatter : WebMessageFormatter, IClientMessageFormatter
  277. {
  278. IClientMessageFormatter default_formatter;
  279. protected WebClientMessageFormatter (OperationDescription operation, ServiceEndpoint endpoint, QueryStringConverter converter, WebHttpBehavior behavior)
  280. : base (operation, endpoint, converter, behavior)
  281. {
  282. }
  283. public virtual Message SerializeRequest (MessageVersion messageVersion, object [] parameters)
  284. {
  285. if (parameters == null)
  286. throw new ArgumentNullException ("parameters");
  287. CheckMessageVersion (messageVersion);
  288. var c = new Dictionary<string,string> ();
  289. MessageDescription md = GetMessageDescription (MessageDirection.Input);
  290. Message ret;
  291. Uri to;
  292. object msgpart = null;
  293. for (int i = 0; i < parameters.Length; i++) {
  294. var p = md.Body.Parts [i];
  295. string name = p.Name.ToUpper (CultureInfo.InvariantCulture);
  296. if (UriTemplate.PathSegmentVariableNames.Contains (name) ||
  297. UriTemplate.QueryValueVariableNames.Contains (name))
  298. c.Add (name, parameters [i] != null ? Converter.ConvertValueToString (parameters [i], parameters [i].GetType ()) : null);
  299. else {
  300. // FIXME: bind as a message part
  301. if (msgpart == null)
  302. msgpart = parameters [i];
  303. else
  304. throw new NotImplementedException (String.Format ("More than one parameters including {0} that are not contained in the URI template {1} was found.", p.Name, UriTemplate));
  305. }
  306. }
  307. ret = Message.CreateMessage (messageVersion, (string) null, msgpart);
  308. to = UriTemplate.BindByName (Endpoint.Address.Uri, c);
  309. ret.Headers.To = to;
  310. var hp = new HttpRequestMessageProperty ();
  311. hp.Method = Info.Method;
  312. WebMessageFormat msgfmt = Info.IsResponseFormatSetExplicitly ? Info.ResponseFormat : Behavior.DefaultOutgoingResponseFormat;
  313. var contentFormat = ToContentFormat (msgfmt, msgpart);
  314. string mediaType = GetMediaTypeString (contentFormat);
  315. // FIXME: get encoding from somewhere
  316. hp.Headers ["Content-Type"] = mediaType + "; charset=utf-8";
  317. #if !NET_2_1
  318. if (WebOperationContext.Current != null)
  319. WebOperationContext.Current.OutgoingRequest.Apply (hp);
  320. #endif
  321. // FIXME: set hp.SuppressEntityBody for some cases.
  322. ret.Properties.Add (HttpRequestMessageProperty.Name, hp);
  323. var wp = new WebBodyFormatMessageProperty (ToContentFormat (Info.IsRequestFormatSetExplicitly ? Info.RequestFormat : Behavior.DefaultOutgoingRequestFormat, null));
  324. ret.Properties.Add (WebBodyFormatMessageProperty.Name, wp);
  325. return ret;
  326. }
  327. public virtual object DeserializeReply (Message message, object [] parameters)
  328. {
  329. if (parameters == null)
  330. throw new ArgumentNullException ("parameters");
  331. CheckMessageVersion (message.Version);
  332. #if !NET_2_1
  333. if (OperationContext.Current != null) {
  334. // Set response in the context
  335. OperationContext.Current.IncomingMessage = message;
  336. }
  337. #endif
  338. if (message.IsEmpty)
  339. return null; // empty message, could be returned by HttpReplyChannel.
  340. string pname = WebBodyFormatMessageProperty.Name;
  341. if (!message.Properties.ContainsKey (pname))
  342. throw new SystemException ("INTERNAL ERROR: it expects WebBodyFormatMessageProperty existence");
  343. var wp = (WebBodyFormatMessageProperty) message.Properties [pname];
  344. var fmt = wp != null ? wp.Format : WebContentFormat.Xml;
  345. var md = GetMessageDescription (MessageDirection.Output);
  346. var serializer = GetSerializer (wp.Format, IsResponseBodyWrapped, md.Body.ReturnValue);
  347. var ret = DeserializeObject (serializer, message, md, IsResponseBodyWrapped, fmt);
  348. return ret;
  349. }
  350. }
  351. internal class WrappedBodyWriter : BodyWriter
  352. {
  353. public WrappedBodyWriter (object value, XmlObjectSerializer serializer, string name, string ns, WebContentFormat fmt)
  354. : base (true)
  355. {
  356. this.name = name;
  357. this.ns = ns;
  358. this.value = value;
  359. this.serializer = serializer;
  360. this.fmt = fmt;
  361. }
  362. WebContentFormat fmt;
  363. string name, ns;
  364. object value;
  365. XmlObjectSerializer serializer;
  366. #if !NET_2_1
  367. protected override BodyWriter OnCreateBufferedCopy (int maxBufferSize)
  368. {
  369. return new WrappedBodyWriter (value, serializer, name, ns, fmt);
  370. }
  371. #endif
  372. protected override void OnWriteBodyContents (XmlDictionaryWriter writer)
  373. {
  374. switch (fmt) {
  375. case WebContentFormat.Raw:
  376. WriteRawContents (writer);
  377. break;
  378. case WebContentFormat.Json:
  379. WriteJsonBodyContents (writer);
  380. break;
  381. case WebContentFormat.Xml:
  382. WriteXmlBodyContents (writer);
  383. break;
  384. }
  385. }
  386. void WriteRawContents (XmlDictionaryWriter writer)
  387. {
  388. throw new NotSupportedException ("Some unsupported sequence of writing operation occured. It is likely a missing feature.");
  389. }
  390. void WriteJsonBodyContents (XmlDictionaryWriter writer)
  391. {
  392. if (name != null) {
  393. writer.WriteStartElement ("root");
  394. writer.WriteAttributeString ("type", "object");
  395. }
  396. WriteObject (serializer, writer, value);
  397. if (name != null)
  398. writer.WriteEndElement ();
  399. }
  400. void WriteXmlBodyContents (XmlDictionaryWriter writer)
  401. {
  402. if (name != null)
  403. writer.WriteStartElement (name, ns);
  404. WriteObject (serializer, writer, value);
  405. if (name != null)
  406. writer.WriteEndElement ();
  407. }
  408. void WriteObject (XmlObjectSerializer serializer, XmlDictionaryWriter writer, object value)
  409. {
  410. #if NET_2_1
  411. if (serializer is DataContractJsonSerializer)
  412. ((DataContractJsonSerializer) serializer).WriteObject (writer, value);
  413. else
  414. ((DataContractSerializer) serializer).WriteObject (writer, value);
  415. #else
  416. serializer.WriteObject (writer, value);
  417. #endif
  418. }
  419. }
  420. #if !NET_2_1
  421. internal abstract class WebDispatchMessageFormatter : WebMessageFormatter, IDispatchMessageFormatter
  422. {
  423. protected WebDispatchMessageFormatter (OperationDescription operation, ServiceEndpoint endpoint, QueryStringConverter converter, WebHttpBehavior behavior)
  424. : base (operation, endpoint, converter, behavior)
  425. {
  426. }
  427. public virtual Message SerializeReply (MessageVersion messageVersion, object [] parameters, object result)
  428. {
  429. try {
  430. return SerializeReplyCore (messageVersion, parameters, result);
  431. } finally {
  432. if (WebOperationContext.Current != null)
  433. OperationContext.Current.Extensions.Remove (WebOperationContext.Current);
  434. }
  435. }
  436. Message SerializeReplyCore (MessageVersion messageVersion, object [] parameters, object result)
  437. {
  438. // parameters could be null.
  439. // result could be null. For Raw output, it becomes no output.
  440. CheckMessageVersion (messageVersion);
  441. MessageDescription md = GetMessageDescription (MessageDirection.Output);
  442. // FIXME: use them.
  443. // var dcob = Operation.Behaviors.Find<DataContractSerializerOperationBehavior> ();
  444. // XmlObjectSerializer xos = dcob.CreateSerializer (result.GetType (), md.Body.WrapperName, md.Body.WrapperNamespace, null);
  445. // var xsob = Operation.Behaviors.Find<XmlSerializerOperationBehavior> ();
  446. // XmlSerializer [] serializers = XmlSerializer.FromMappings (xsob.GetXmlMappings ().ToArray ());
  447. WebMessageFormat msgfmt = Info.IsResponseFormatSetExplicitly ? Info.ResponseFormat : Behavior.DefaultOutgoingResponseFormat;
  448. XmlObjectSerializer serializer = null;
  449. // FIXME: serialize ref/out parameters as well.
  450. string name = null, ns = null;
  451. switch (msgfmt) {
  452. case WebMessageFormat.Xml:
  453. serializer = GetSerializer (WebContentFormat.Xml, IsResponseBodyWrapped, md.Body.ReturnValue);
  454. name = IsResponseBodyWrapped ? md.Body.WrapperName : null;
  455. ns = IsResponseBodyWrapped ? md.Body.WrapperNamespace : null;
  456. break;
  457. case WebMessageFormat.Json:
  458. serializer = GetSerializer (WebContentFormat.Json, IsResponseBodyWrapped, md.Body.ReturnValue);
  459. name = IsResponseBodyWrapped ? (BodyName ?? md.Body.ReturnValue.Name) : null;
  460. ns = String.Empty;
  461. break;
  462. }
  463. var contentFormat = ToContentFormat (msgfmt, result);
  464. string mediaType = GetMediaTypeString (contentFormat);
  465. Message ret = contentFormat == WebContentFormat.Raw ? new RawMessage ((Stream) result) : Message.CreateMessage (MessageVersion.None, null, new WrappedBodyWriter (result, serializer, name, ns, contentFormat));
  466. // Message properties
  467. var hp = new HttpResponseMessageProperty ();
  468. // FIXME: get encoding from somewhere
  469. hp.Headers ["Content-Type"] = mediaType + "; charset=utf-8";
  470. // apply user-customized HTTP results via WebOperationContext.
  471. if (WebOperationContext.Current != null) // this formatter must be available outside ServiceHost.
  472. WebOperationContext.Current.OutgoingResponse.Apply (hp);
  473. // FIXME: fill some properties if required.
  474. ret.Properties.Add (HttpResponseMessageProperty.Name, hp);
  475. var wp = new WebBodyFormatMessageProperty (contentFormat);
  476. ret.Properties.Add (WebBodyFormatMessageProperty.Name, wp);
  477. return ret;
  478. }
  479. public virtual void DeserializeRequest (Message message, object [] parameters)
  480. {
  481. if (parameters == null)
  482. throw new ArgumentNullException ("parameters");
  483. CheckMessageVersion (message.Version);
  484. IncomingWebRequestContext iwc = null;
  485. if (OperationContext.Current != null) {
  486. OperationContext.Current.Extensions.Add (new WebOperationContext (OperationContext.Current));
  487. iwc = WebOperationContext.Current.IncomingRequest;
  488. }
  489. var wp = message.Properties [WebBodyFormatMessageProperty.Name] as WebBodyFormatMessageProperty;
  490. var fmt = wp != null ? wp.Format : WebContentFormat.Xml;
  491. Uri to = message.Headers.To;
  492. UriTemplateMatch match = to == null ? null : UriTemplate.Match (Endpoint.Address.Uri, to);
  493. if (match != null && iwc != null)
  494. iwc.UriTemplateMatch = match;
  495. MessageDescription md = GetMessageDescription (MessageDirection.Input);
  496. for (int i = 0; i < parameters.Length; i++) {
  497. var p = md.Body.Parts [i];
  498. string name = p.Name.ToUpperInvariant ();
  499. if (fmt == WebContentFormat.Raw && p.Type == typeof (Stream)) {
  500. var rmsg = (RawMessage) message;
  501. parameters [i] = rmsg.Stream;
  502. } else {
  503. var str = match.BoundVariables [name];
  504. if (str != null)
  505. parameters [i] = Converter.ConvertStringToValue (str, p.Type);
  506. else {
  507. if (info.Method != "GET") {
  508. var serializer = GetSerializer (fmt, IsRequestBodyWrapped, p);
  509. parameters [i] = DeserializeObject (serializer, message, md, IsRequestBodyWrapped, fmt);
  510. }
  511. // for GET Uri template parameters, there is no <anyType xsi:nil='true' />. So just skip the member.
  512. }
  513. }
  514. }
  515. }
  516. }
  517. #endif
  518. internal class RawMessage : Message
  519. {
  520. public RawMessage (Stream stream)
  521. {
  522. this.Stream = stream;
  523. headers = new MessageHeaders (MessageVersion.None);
  524. properties = new MessageProperties ();
  525. }
  526. public override MessageVersion Version {
  527. get { return MessageVersion.None; }
  528. }
  529. MessageHeaders headers;
  530. public override MessageHeaders Headers {
  531. get { return headers; }
  532. }
  533. MessageProperties properties;
  534. public override MessageProperties Properties {
  535. get { return properties; }
  536. }
  537. public Stream Stream { get; private set; }
  538. protected override void OnWriteBodyContents (XmlDictionaryWriter writer)
  539. {
  540. writer.WriteString ("-- message body is raw binary --");
  541. }
  542. protected override MessageBuffer OnCreateBufferedCopy (int maxBufferSize)
  543. {
  544. var ms = Stream as MemoryStream;
  545. if (ms == null) {
  546. ms = new MemoryStream ();
  547. #if NET_4_0 || NET_2_1
  548. Stream.CopyTo (ms);
  549. #else
  550. byte [] tmp = new byte [0x1000];
  551. int size;
  552. do {
  553. size = Stream.Read (tmp, 0, tmp.Length);
  554. ms.Write (tmp, 0, size);
  555. } while (size > 0);
  556. #endif
  557. this.Stream = ms;
  558. }
  559. return new RawMessageBuffer (ms.ToArray (), headers, properties);
  560. }
  561. }
  562. internal class RawMessageBuffer : MessageBuffer
  563. {
  564. byte [] buffer;
  565. MessageHeaders headers;
  566. MessageProperties properties;
  567. public RawMessageBuffer (byte [] buffer, MessageHeaders headers, MessageProperties properties)
  568. {
  569. this.buffer = buffer;
  570. this.headers = new MessageHeaders (headers);
  571. this.properties = new MessageProperties (properties);
  572. }
  573. public override int BufferSize {
  574. get { return buffer.Length; }
  575. }
  576. public override void Close ()
  577. {
  578. }
  579. public override Message CreateMessage ()
  580. {
  581. var msg = new RawMessage (new MemoryStream (buffer));
  582. msg.Headers.CopyHeadersFrom (headers);
  583. msg.Properties.CopyProperties (properties);
  584. return msg;
  585. }
  586. }
  587. }
  588. }