TextMessageEncoder.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.ServiceModel.Channels
  5. {
  6. using System.Globalization;
  7. using System.IO;
  8. using System.Net.Mime;
  9. using System.Runtime;
  10. using System.Runtime.Diagnostics;
  11. using System.ServiceModel.Diagnostics;
  12. using System.ServiceModel.Diagnostics.Application;
  13. using System.Text;
  14. using System.Xml;
  15. class TextMessageEncoderFactory : MessageEncoderFactory
  16. {
  17. TextMessageEncoder messageEncoder;
  18. internal static ContentEncoding[] Soap11Content = GetContentEncodingMap(MessageVersion.Soap11WSAddressing10);
  19. internal static ContentEncoding[] Soap12Content = GetContentEncodingMap(MessageVersion.Soap12WSAddressing10);
  20. internal static ContentEncoding[] SoapNoneContent = GetContentEncodingMap(MessageVersion.None);
  21. internal const string Soap11MediaType = "text/xml";
  22. internal const string Soap12MediaType = "application/soap+xml";
  23. const string XmlMediaType = "application/xml";
  24. public TextMessageEncoderFactory(MessageVersion version, Encoding writeEncoding, int maxReadPoolSize, int maxWritePoolSize, XmlDictionaryReaderQuotas quotas)
  25. {
  26. messageEncoder = new TextMessageEncoder(version, writeEncoding, maxReadPoolSize, maxWritePoolSize, quotas);
  27. }
  28. public override MessageEncoder Encoder
  29. {
  30. get { return messageEncoder; }
  31. }
  32. public override MessageVersion MessageVersion
  33. {
  34. get { return messageEncoder.MessageVersion; }
  35. }
  36. public int MaxWritePoolSize
  37. {
  38. get { return messageEncoder.MaxWritePoolSize; }
  39. }
  40. public int MaxReadPoolSize
  41. {
  42. get { return messageEncoder.MaxReadPoolSize; }
  43. }
  44. public static Encoding[] GetSupportedEncodings()
  45. {
  46. Encoding[] supported = TextEncoderDefaults.SupportedEncodings;
  47. Encoding[] enc = new Encoding[supported.Length];
  48. Array.Copy(supported, enc, supported.Length);
  49. return enc;
  50. }
  51. public XmlDictionaryReaderQuotas ReaderQuotas
  52. {
  53. get
  54. {
  55. return messageEncoder.ReaderQuotas;
  56. }
  57. }
  58. internal static string GetMediaType(MessageVersion version)
  59. {
  60. string mediaType = null;
  61. if (version.Envelope == EnvelopeVersion.Soap12)
  62. {
  63. mediaType = TextMessageEncoderFactory.Soap12MediaType;
  64. }
  65. else if (version.Envelope == EnvelopeVersion.Soap11)
  66. {
  67. mediaType = TextMessageEncoderFactory.Soap11MediaType;
  68. }
  69. else if (version.Envelope == EnvelopeVersion.None)
  70. {
  71. mediaType = TextMessageEncoderFactory.XmlMediaType;
  72. }
  73. else
  74. {
  75. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
  76. SR.GetString(SR.EnvelopeVersionNotSupported, version.Envelope)));
  77. }
  78. return mediaType;
  79. }
  80. internal static string GetContentType(string mediaType, Encoding encoding)
  81. {
  82. return String.Format(CultureInfo.InvariantCulture, "{0}; charset={1}", mediaType, TextEncoderDefaults.EncodingToCharSet(encoding));
  83. }
  84. static ContentEncoding[] GetContentEncodingMap(MessageVersion version)
  85. {
  86. Encoding[] readEncodings = TextMessageEncoderFactory.GetSupportedEncodings();
  87. string media = GetMediaType(version);
  88. ContentEncoding[] map = new ContentEncoding[readEncodings.Length];
  89. for (int i = 0; i < readEncodings.Length; i++)
  90. {
  91. ContentEncoding contentEncoding = new ContentEncoding();
  92. contentEncoding.contentType = GetContentType(media, readEncodings[i]);
  93. contentEncoding.encoding = readEncodings[i];
  94. map[i] = contentEncoding;
  95. }
  96. return map;
  97. }
  98. internal static Encoding GetEncodingFromContentType(string contentType, ContentEncoding[] contentMap)
  99. {
  100. if (contentType == null)
  101. {
  102. return null;
  103. }
  104. // Check for known/expected content types
  105. for (int i = 0; i < contentMap.Length; i++)
  106. {
  107. if (contentMap[i].contentType == contentType)
  108. {
  109. return contentMap[i].encoding;
  110. }
  111. }
  112. // then some heuristic matches (since System.Mime.ContentType is a performance hit)
  113. // start by looking for a parameter.
  114. // If none exists, we don't have an encoding
  115. int semiColonIndex = contentType.IndexOf(';');
  116. if (semiColonIndex == -1)
  117. {
  118. return null;
  119. }
  120. // optimize for charset being the first parameter
  121. int charsetValueIndex = -1;
  122. // for Indigo scenarios, we'll have "; charset=", so check for the c
  123. if ((contentType.Length > semiColonIndex + 11) // need room for parameter + charset + '='
  124. && contentType[semiColonIndex + 2] == 'c'
  125. && string.Compare("charset=", 0, contentType, semiColonIndex + 2, 8, StringComparison.OrdinalIgnoreCase) == 0)
  126. {
  127. charsetValueIndex = semiColonIndex + 10;
  128. }
  129. else
  130. {
  131. // look for charset= somewhere else in the message
  132. int paramIndex = contentType.IndexOf("charset=", semiColonIndex + 1, StringComparison.OrdinalIgnoreCase);
  133. if (paramIndex != -1)
  134. {
  135. // validate there's only whitespace or semi-colons beforehand
  136. for (int i = paramIndex - 1; i >= semiColonIndex; i--)
  137. {
  138. if (contentType[i] == ';')
  139. {
  140. charsetValueIndex = paramIndex + 8;
  141. break;
  142. }
  143. if (contentType[i] == '\n')
  144. {
  145. if (i == semiColonIndex || contentType[i - 1] != '\r')
  146. {
  147. break;
  148. }
  149. i--;
  150. continue;
  151. }
  152. if (contentType[i] != ' '
  153. && contentType[i] != '\t')
  154. {
  155. break;
  156. }
  157. }
  158. }
  159. }
  160. string charSet;
  161. Encoding enc;
  162. // we have a possible charset value. If it's easy to parse, do so
  163. if (charsetValueIndex != -1)
  164. {
  165. // get the next semicolon
  166. semiColonIndex = contentType.IndexOf(';', charsetValueIndex);
  167. if (semiColonIndex == -1)
  168. {
  169. charSet = contentType.Substring(charsetValueIndex);
  170. }
  171. else
  172. {
  173. charSet = contentType.Substring(charsetValueIndex, semiColonIndex - charsetValueIndex);
  174. }
  175. // and some minimal quote stripping
  176. if (charSet.Length > 2 && charSet[0] == '"' && charSet[charSet.Length - 1] == '"')
  177. {
  178. charSet = charSet.Substring(1, charSet.Length - 2);
  179. }
  180. Fx.Assert(charSet == (new ContentType(contentType)).CharSet,
  181. "CharSet parsing failed to correctly parse the ContentType header.");
  182. if (TryGetEncodingFromCharSet(charSet, out enc))
  183. {
  184. return enc;
  185. }
  186. }
  187. // our quick heuristics failed. fall back to System.Net
  188. try
  189. {
  190. ContentType parsedContentType = new ContentType(contentType);
  191. charSet = parsedContentType.CharSet;
  192. }
  193. catch (FormatException e)
  194. {
  195. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.GetString(SR.EncoderBadContentType), e));
  196. }
  197. if (TryGetEncodingFromCharSet(charSet, out enc))
  198. return enc;
  199. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.GetString(SR.EncoderUnrecognizedCharSet, charSet)));
  200. }
  201. internal static bool TryGetEncodingFromCharSet(string charSet, out Encoding encoding)
  202. {
  203. encoding = null;
  204. if (charSet == null || charSet.Length == 0)
  205. return true;
  206. return TextEncoderDefaults.TryGetEncoding(charSet, out encoding);
  207. }
  208. internal class ContentEncoding
  209. {
  210. internal string contentType;
  211. internal Encoding encoding;
  212. }
  213. class TextMessageEncoder : MessageEncoder, ITraceSourceStringProvider
  214. {
  215. int maxReadPoolSize;
  216. int maxWritePoolSize;
  217. // Double-checked locking pattern requires volatile for read/write synchronization
  218. volatile SynchronizedPool<XmlDictionaryWriter> streamedWriterPool;
  219. volatile SynchronizedPool<XmlDictionaryReader> streamedReaderPool;
  220. volatile SynchronizedPool<UTF8BufferedMessageData> bufferedReaderPool;
  221. volatile SynchronizedPool<TextBufferedMessageWriter> bufferedWriterPool;
  222. volatile SynchronizedPool<RecycledMessageState> recycledStatePool;
  223. object thisLock;
  224. string contentType;
  225. string mediaType;
  226. Encoding writeEncoding;
  227. MessageVersion version;
  228. bool optimizeWriteForUTF8;
  229. const int maxPooledXmlReadersPerMessage = 2;
  230. XmlDictionaryReaderQuotas readerQuotas;
  231. XmlDictionaryReaderQuotas bufferedReadReaderQuotas;
  232. OnXmlDictionaryReaderClose onStreamedReaderClose;
  233. ContentEncoding[] contentEncodingMap;
  234. public TextMessageEncoder(MessageVersion version, Encoding writeEncoding, int maxReadPoolSize, int maxWritePoolSize, XmlDictionaryReaderQuotas quotas)
  235. {
  236. if (version == null)
  237. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("version");
  238. if (writeEncoding == null)
  239. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("writeEncoding");
  240. TextEncoderDefaults.ValidateEncoding(writeEncoding);
  241. this.writeEncoding = writeEncoding;
  242. optimizeWriteForUTF8 = IsUTF8Encoding(writeEncoding);
  243. thisLock = new object();
  244. this.version = version;
  245. this.maxReadPoolSize = maxReadPoolSize;
  246. this.maxWritePoolSize = maxWritePoolSize;
  247. this.readerQuotas = new XmlDictionaryReaderQuotas();
  248. quotas.CopyTo(this.readerQuotas);
  249. this.bufferedReadReaderQuotas = EncoderHelpers.GetBufferedReadQuotas(this.readerQuotas);
  250. this.onStreamedReaderClose = new OnXmlDictionaryReaderClose(ReturnStreamedReader);
  251. this.mediaType = TextMessageEncoderFactory.GetMediaType(version);
  252. this.contentType = TextMessageEncoderFactory.GetContentType(mediaType, writeEncoding);
  253. if (version.Envelope == EnvelopeVersion.Soap12)
  254. {
  255. contentEncodingMap = TextMessageEncoderFactory.Soap12Content;
  256. }
  257. else if (version.Envelope == EnvelopeVersion.Soap11)
  258. {
  259. contentEncodingMap = TextMessageEncoderFactory.Soap11Content;
  260. }
  261. else if (version.Envelope == EnvelopeVersion.None)
  262. {
  263. contentEncodingMap = TextMessageEncoderFactory.SoapNoneContent;
  264. }
  265. else
  266. {
  267. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
  268. SR.GetString(SR.EnvelopeVersionNotSupported, version.Envelope)));
  269. }
  270. }
  271. static bool IsUTF8Encoding(Encoding encoding)
  272. {
  273. return encoding.WebName == "utf-8";
  274. }
  275. public override string ContentType
  276. {
  277. get { return contentType; }
  278. }
  279. public int MaxWritePoolSize
  280. {
  281. get { return maxWritePoolSize; }
  282. }
  283. public int MaxReadPoolSize
  284. {
  285. get { return maxReadPoolSize; }
  286. }
  287. public XmlDictionaryReaderQuotas ReaderQuotas
  288. {
  289. get
  290. {
  291. return readerQuotas;
  292. }
  293. }
  294. public override string MediaType
  295. {
  296. get { return mediaType; }
  297. }
  298. public override MessageVersion MessageVersion
  299. {
  300. get { return version; }
  301. }
  302. object ThisLock
  303. {
  304. get { return thisLock; }
  305. }
  306. internal override bool IsCharSetSupported(string charSet)
  307. {
  308. Encoding tmp;
  309. return TextEncoderDefaults.TryGetEncoding(charSet, out tmp);
  310. }
  311. public override bool IsContentTypeSupported(string contentType)
  312. {
  313. if (contentType == null)
  314. {
  315. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contentType");
  316. }
  317. if (base.IsContentTypeSupported(contentType))
  318. {
  319. return true;
  320. }
  321. // we support a few extra content types for "none"
  322. if (MessageVersion == MessageVersion.None)
  323. {
  324. const string rss1MediaType = "text/xml";
  325. const string rss2MediaType = "application/rss+xml";
  326. const string atomMediaType = "application/atom+xml";
  327. const string htmlMediaType = "text/html";
  328. if (IsContentTypeSupported(contentType, rss1MediaType, rss1MediaType))
  329. {
  330. return true;
  331. }
  332. if (IsContentTypeSupported(contentType, rss2MediaType, rss2MediaType))
  333. {
  334. return true;
  335. }
  336. if (IsContentTypeSupported(contentType, htmlMediaType, atomMediaType))
  337. {
  338. return true;
  339. }
  340. if (IsContentTypeSupported(contentType, atomMediaType, atomMediaType))
  341. {
  342. return true;
  343. }
  344. // application/xml checked by base method
  345. }
  346. return false;
  347. }
  348. public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
  349. {
  350. if (bufferManager == null)
  351. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("bufferManager"));
  352. if (TD.TextMessageDecodingStartIsEnabled())
  353. {
  354. TD.TextMessageDecodingStart();
  355. }
  356. Message message;
  357. UTF8BufferedMessageData messageData = TakeBufferedReader();
  358. messageData.Encoding = GetEncodingFromContentType(contentType, this.contentEncodingMap);
  359. messageData.Open(buffer, bufferManager);
  360. RecycledMessageState messageState = messageData.TakeMessageState();
  361. if (messageState == null)
  362. messageState = new RecycledMessageState();
  363. message = new BufferedMessage(messageData, messageState);
  364. message.Properties.Encoder = this;
  365. if (TD.MessageReadByEncoderIsEnabled() && buffer != null)
  366. {
  367. TD.MessageReadByEncoder(
  368. EventTraceActivityHelper.TryExtractActivity(message, true),
  369. buffer.Count,
  370. this);
  371. }
  372. if (MessageLogger.LogMessagesAtTransportLevel)
  373. MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportReceive);
  374. return message;
  375. }
  376. public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
  377. {
  378. if (stream == null)
  379. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("stream"));
  380. if (TD.TextMessageDecodingStartIsEnabled())
  381. {
  382. TD.TextMessageDecodingStart();
  383. }
  384. XmlReader reader = TakeStreamedReader(stream, GetEncodingFromContentType(contentType, this.contentEncodingMap));
  385. Message message = Message.CreateMessage(reader, maxSizeOfHeaders, version);
  386. message.Properties.Encoder = this;
  387. if (TD.StreamedMessageReadByEncoderIsEnabled())
  388. {
  389. TD.StreamedMessageReadByEncoder(EventTraceActivityHelper.TryExtractActivity(message, true));
  390. }
  391. if (MessageLogger.LogMessagesAtTransportLevel)
  392. MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportReceive);
  393. return message;
  394. }
  395. public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
  396. {
  397. if (message == null)
  398. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("message"));
  399. if (bufferManager == null)
  400. throw TraceUtility.ThrowHelperError(new ArgumentNullException("bufferManager"), message);
  401. if (maxMessageSize < 0)
  402. throw TraceUtility.ThrowHelperError(new ArgumentOutOfRangeException("maxMessageSize", maxMessageSize,
  403. SR.GetString(SR.ValueMustBeNonNegative)), message);
  404. if (messageOffset < 0 || messageOffset > maxMessageSize)
  405. throw TraceUtility.ThrowHelperError(new ArgumentOutOfRangeException("messageOffset", messageOffset,
  406. SR.GetString(SR.ValueMustBeInRange, 0, maxMessageSize)), message);
  407. ThrowIfMismatchedMessageVersion(message);
  408. EventTraceActivity eventTraceActivity = null;
  409. if (TD.TextMessageEncodingStartIsEnabled())
  410. {
  411. eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
  412. TD.TextMessageEncodingStart(eventTraceActivity);
  413. }
  414. message.Properties.Encoder = this;
  415. TextBufferedMessageWriter messageWriter = TakeBufferedWriter();
  416. ArraySegment<byte> messageData = messageWriter.WriteMessage(message, bufferManager, messageOffset, maxMessageSize);
  417. ReturnMessageWriter(messageWriter);
  418. if (TD.MessageWrittenByEncoderIsEnabled() && messageData != null)
  419. {
  420. TD.MessageWrittenByEncoder(
  421. eventTraceActivity ?? EventTraceActivityHelper.TryExtractActivity(message),
  422. messageData.Count,
  423. this);
  424. }
  425. if (MessageLogger.LogMessagesAtTransportLevel)
  426. {
  427. XmlDictionaryReader xmlDictionaryReader = XmlDictionaryReader.CreateTextReader(messageData.Array, messageData.Offset, messageData.Count, null, XmlDictionaryReaderQuotas.Max, null);
  428. MessageLogger.LogMessage(ref message, xmlDictionaryReader, MessageLoggingSource.TransportSend);
  429. }
  430. return messageData;
  431. }
  432. public override void WriteMessage(Message message, Stream stream)
  433. {
  434. if (message == null)
  435. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("message"));
  436. if (stream == null)
  437. throw TraceUtility.ThrowHelperError(new ArgumentNullException("stream"), message);
  438. ThrowIfMismatchedMessageVersion(message);
  439. EventTraceActivity eventTraceActivity = null;
  440. if (TD.TextMessageEncodingStartIsEnabled())
  441. {
  442. eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
  443. TD.TextMessageEncodingStart(eventTraceActivity);
  444. }
  445. message.Properties.Encoder = this;
  446. XmlDictionaryWriter xmlWriter = TakeStreamedWriter(stream);
  447. if (optimizeWriteForUTF8)
  448. {
  449. message.WriteMessage(xmlWriter);
  450. }
  451. else
  452. {
  453. xmlWriter.WriteStartDocument();
  454. message.WriteMessage(xmlWriter);
  455. xmlWriter.WriteEndDocument();
  456. }
  457. xmlWriter.Flush();
  458. ReturnStreamedWriter(xmlWriter);
  459. if (TD.StreamedMessageWrittenByEncoderIsEnabled())
  460. {
  461. TD.StreamedMessageWrittenByEncoder(eventTraceActivity ?? EventTraceActivityHelper.TryExtractActivity(message));
  462. }
  463. if (MessageLogger.LogMessagesAtTransportLevel)
  464. MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportSend);
  465. }
  466. public override IAsyncResult BeginWriteMessage(Message message, Stream stream, AsyncCallback callback, object state)
  467. {
  468. if (message == null)
  469. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("message"));
  470. if (stream == null)
  471. throw TraceUtility.ThrowHelperError(new ArgumentNullException("stream"), message);
  472. ThrowIfMismatchedMessageVersion(message);
  473. message.Properties.Encoder = this;
  474. return new WriteMessageAsyncResult(message, stream, this, callback, state);
  475. }
  476. public override void EndWriteMessage(IAsyncResult result)
  477. {
  478. WriteMessageAsyncResult.End(result);
  479. }
  480. class WriteMessageAsyncResult : AsyncResult
  481. {
  482. static AsyncCompletion onWriteMessage = new AsyncCompletion(OnWriteMessage);
  483. Message message;
  484. TextMessageEncoder textEncoder;
  485. XmlDictionaryWriter xmlWriter;
  486. EventTraceActivity eventTraceActivity;
  487. public WriteMessageAsyncResult(Message message, Stream stream, TextMessageEncoder textEncoder, AsyncCallback callback, object state)
  488. : base(callback, state)
  489. {
  490. this.message = message;
  491. this.textEncoder = textEncoder;
  492. this.xmlWriter = textEncoder.TakeStreamedWriter(stream);
  493. this.eventTraceActivity = null;
  494. if (TD.TextMessageEncodingStartIsEnabled())
  495. {
  496. this.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
  497. TD.TextMessageEncodingStart(this.eventTraceActivity);
  498. }
  499. if (!textEncoder.optimizeWriteForUTF8)
  500. {
  501. xmlWriter.WriteStartDocument();
  502. }
  503. IAsyncResult result = message.BeginWriteMessage(this.xmlWriter, PrepareAsyncCompletion(onWriteMessage), this);
  504. if (SyncContinue(result))
  505. {
  506. this.Complete(true);
  507. }
  508. }
  509. static bool OnWriteMessage(IAsyncResult result)
  510. {
  511. WriteMessageAsyncResult thisPtr = (WriteMessageAsyncResult)result.AsyncState;
  512. return thisPtr.HandleWriteMessage(result);
  513. }
  514. bool HandleWriteMessage(IAsyncResult result)
  515. {
  516. message.EndWriteMessage(result);
  517. if (!textEncoder.optimizeWriteForUTF8)
  518. {
  519. this.xmlWriter.WriteEndDocument();
  520. }
  521. xmlWriter.Flush(); // blocking call
  522. textEncoder.ReturnStreamedWriter(this.xmlWriter);
  523. if (TD.MessageWrittenAsynchronouslyByEncoderIsEnabled())
  524. {
  525. TD.MessageWrittenAsynchronouslyByEncoder(
  526. this.eventTraceActivity ?? EventTraceActivityHelper.TryExtractActivity(message));
  527. }
  528. if (MessageLogger.LogMessagesAtTransportLevel)
  529. MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportSend);
  530. return true;
  531. }
  532. public static void End(IAsyncResult result)
  533. {
  534. AsyncResult.End<WriteMessageAsyncResult>(result);
  535. }
  536. }
  537. XmlDictionaryWriter TakeStreamedWriter(Stream stream)
  538. {
  539. if (streamedWriterPool == null)
  540. {
  541. lock (ThisLock)
  542. {
  543. if (streamedWriterPool == null)
  544. {
  545. streamedWriterPool = new SynchronizedPool<XmlDictionaryWriter>(maxWritePoolSize);
  546. }
  547. }
  548. }
  549. XmlDictionaryWriter xmlWriter = streamedWriterPool.Take();
  550. if (xmlWriter == null)
  551. {
  552. xmlWriter = XmlDictionaryWriter.CreateTextWriter(stream, this.writeEncoding, false);
  553. if (TD.WritePoolMissIsEnabled())
  554. {
  555. TD.WritePoolMiss(xmlWriter.GetType().Name);
  556. }
  557. }
  558. else
  559. {
  560. ((IXmlTextWriterInitializer)xmlWriter).SetOutput(stream, this.writeEncoding, false);
  561. }
  562. return xmlWriter;
  563. }
  564. void ReturnStreamedWriter(XmlWriter xmlWriter)
  565. {
  566. xmlWriter.Close();
  567. streamedWriterPool.Return((XmlDictionaryWriter)xmlWriter);
  568. }
  569. TextBufferedMessageWriter TakeBufferedWriter()
  570. {
  571. if (bufferedWriterPool == null)
  572. {
  573. lock (ThisLock)
  574. {
  575. if (bufferedWriterPool == null)
  576. {
  577. bufferedWriterPool = new SynchronizedPool<TextBufferedMessageWriter>(maxWritePoolSize);
  578. }
  579. }
  580. }
  581. TextBufferedMessageWriter messageWriter = bufferedWriterPool.Take();
  582. if (messageWriter == null)
  583. {
  584. messageWriter = new TextBufferedMessageWriter(this);
  585. if (TD.WritePoolMissIsEnabled())
  586. {
  587. TD.WritePoolMiss(messageWriter.GetType().Name);
  588. }
  589. }
  590. return messageWriter;
  591. }
  592. void ReturnMessageWriter(TextBufferedMessageWriter messageWriter)
  593. {
  594. bufferedWriterPool.Return(messageWriter);
  595. }
  596. XmlReader TakeStreamedReader(Stream stream, Encoding enc)
  597. {
  598. if (streamedReaderPool == null)
  599. {
  600. lock (ThisLock)
  601. {
  602. if (streamedReaderPool == null)
  603. {
  604. streamedReaderPool = new SynchronizedPool<XmlDictionaryReader>(maxReadPoolSize);
  605. }
  606. }
  607. }
  608. XmlDictionaryReader xmlReader = streamedReaderPool.Take();
  609. if (xmlReader == null)
  610. {
  611. xmlReader = XmlDictionaryReader.CreateTextReader(stream, enc, this.readerQuotas, null);
  612. if (TD.ReadPoolMissIsEnabled())
  613. {
  614. TD.ReadPoolMiss(xmlReader.GetType().Name);
  615. }
  616. }
  617. else
  618. {
  619. ((IXmlTextReaderInitializer)xmlReader).SetInput(stream, enc, this.readerQuotas, onStreamedReaderClose);
  620. }
  621. return xmlReader;
  622. }
  623. void ReturnStreamedReader(XmlDictionaryReader xmlReader)
  624. {
  625. streamedReaderPool.Return(xmlReader);
  626. }
  627. XmlDictionaryWriter CreateWriter(Stream stream)
  628. {
  629. return XmlDictionaryWriter.CreateTextWriter(stream, writeEncoding, false);
  630. }
  631. UTF8BufferedMessageData TakeBufferedReader()
  632. {
  633. if (bufferedReaderPool == null)
  634. {
  635. lock (ThisLock)
  636. {
  637. if (bufferedReaderPool == null)
  638. {
  639. bufferedReaderPool = new SynchronizedPool<UTF8BufferedMessageData>(maxReadPoolSize);
  640. }
  641. }
  642. }
  643. UTF8BufferedMessageData messageData = bufferedReaderPool.Take();
  644. if (messageData == null)
  645. {
  646. messageData = new UTF8BufferedMessageData(this, maxPooledXmlReadersPerMessage);
  647. if (TD.ReadPoolMissIsEnabled())
  648. {
  649. TD.ReadPoolMiss(messageData.GetType().Name);
  650. }
  651. }
  652. return messageData;
  653. }
  654. void ReturnBufferedData(UTF8BufferedMessageData messageData)
  655. {
  656. bufferedReaderPool.Return(messageData);
  657. }
  658. SynchronizedPool<RecycledMessageState> RecycledStatePool
  659. {
  660. get
  661. {
  662. if (recycledStatePool == null)
  663. {
  664. lock (ThisLock)
  665. {
  666. if (recycledStatePool == null)
  667. {
  668. recycledStatePool = new SynchronizedPool<RecycledMessageState>(maxReadPoolSize);
  669. }
  670. }
  671. }
  672. return recycledStatePool;
  673. }
  674. }
  675. string ITraceSourceStringProvider.GetSourceString()
  676. {
  677. return base.GetTraceSourceString();
  678. }
  679. static readonly byte[] xmlDeclarationStartText = { (byte)'<', (byte)'?', (byte)'x', (byte)'m', (byte)'l' };
  680. static readonly byte[] version10Text = { (byte)'v', (byte)'e', (byte)'r', (byte)'s', (byte)'i', (byte)'o', (byte)'n', (byte)'=', (byte)'"', (byte)'1', (byte)'.', (byte)'0', (byte)'"' };
  681. static readonly byte[] encodingText = { (byte)'e', (byte)'n', (byte)'c', (byte)'o', (byte)'d', (byte)'i', (byte)'n', (byte)'g', (byte)'=' };
  682. class UTF8BufferedMessageData : BufferedMessageData
  683. {
  684. TextMessageEncoder messageEncoder;
  685. Pool<XmlDictionaryReader> readerPool;
  686. OnXmlDictionaryReaderClose onClose;
  687. Encoding encoding;
  688. const int additionalNodeSpace = 1024;
  689. public UTF8BufferedMessageData(TextMessageEncoder messageEncoder, int maxReaderPoolSize)
  690. : base(messageEncoder.RecycledStatePool)
  691. {
  692. this.messageEncoder = messageEncoder;
  693. readerPool = new Pool<XmlDictionaryReader>(maxReaderPoolSize);
  694. onClose = new OnXmlDictionaryReaderClose(OnXmlReaderClosed);
  695. }
  696. internal Encoding Encoding
  697. {
  698. set
  699. {
  700. this.encoding = value;
  701. }
  702. }
  703. public override MessageEncoder MessageEncoder
  704. {
  705. get { return messageEncoder; }
  706. }
  707. public override XmlDictionaryReaderQuotas Quotas
  708. {
  709. get { return messageEncoder.bufferedReadReaderQuotas; }
  710. }
  711. protected override void OnClosed()
  712. {
  713. messageEncoder.ReturnBufferedData(this);
  714. }
  715. protected override XmlDictionaryReader TakeXmlReader()
  716. {
  717. ArraySegment<byte> buffer = this.Buffer;
  718. XmlDictionaryReader xmlReader = readerPool.Take();
  719. if (xmlReader == null)
  720. {
  721. xmlReader = XmlDictionaryReader.CreateTextReader(buffer.Array, buffer.Offset, buffer.Count, this.encoding, this.Quotas, onClose);
  722. if (TD.ReadPoolMissIsEnabled())
  723. {
  724. TD.ReadPoolMiss(xmlReader.GetType().Name);
  725. }
  726. }
  727. else
  728. {
  729. ((IXmlTextReaderInitializer)xmlReader).SetInput(buffer.Array, buffer.Offset, buffer.Count, this.encoding, this.Quotas, onClose);
  730. }
  731. return xmlReader;
  732. }
  733. protected override void ReturnXmlReader(XmlDictionaryReader xmlReader)
  734. {
  735. if (xmlReader != null)
  736. {
  737. readerPool.Return(xmlReader);
  738. }
  739. }
  740. }
  741. class TextBufferedMessageWriter : BufferedMessageWriter
  742. {
  743. TextMessageEncoder messageEncoder;
  744. XmlDictionaryWriter writer;
  745. public TextBufferedMessageWriter(TextMessageEncoder messageEncoder)
  746. {
  747. this.messageEncoder = messageEncoder;
  748. }
  749. protected override void OnWriteStartMessage(XmlDictionaryWriter writer)
  750. {
  751. if (!messageEncoder.optimizeWriteForUTF8)
  752. writer.WriteStartDocument();
  753. }
  754. protected override void OnWriteEndMessage(XmlDictionaryWriter writer)
  755. {
  756. if (!messageEncoder.optimizeWriteForUTF8)
  757. writer.WriteEndDocument();
  758. }
  759. protected override XmlDictionaryWriter TakeXmlWriter(Stream stream)
  760. {
  761. if (messageEncoder.optimizeWriteForUTF8)
  762. {
  763. XmlDictionaryWriter returnedWriter = writer;
  764. if (returnedWriter == null)
  765. {
  766. returnedWriter = XmlDictionaryWriter.CreateTextWriter(stream, messageEncoder.writeEncoding, false);
  767. }
  768. else
  769. {
  770. writer = null;
  771. ((IXmlTextWriterInitializer)returnedWriter).SetOutput(stream, messageEncoder.writeEncoding, false);
  772. }
  773. return returnedWriter;
  774. }
  775. else
  776. {
  777. return messageEncoder.CreateWriter(stream);
  778. }
  779. }
  780. protected override void ReturnXmlWriter(XmlDictionaryWriter writer)
  781. {
  782. writer.Close();
  783. if (messageEncoder.optimizeWriteForUTF8)
  784. {
  785. if (this.writer == null)
  786. this.writer = writer;
  787. }
  788. }
  789. }
  790. }
  791. }
  792. }