< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Xml.MtomBinaryData
Assembly: CoreWCF.Primitives
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Xml/XmlMtomWriter.cs
Line coverage
100%
Covered lines: 12
Uncovered lines: 0
Coverable lines: 12
Total lines: 1382
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%11100%
.ctor(...)100%11100%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.Primitives/src/CoreWCF/Xml/XmlMtomWriter.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Globalization;
 7using System.IO;
 8using System.Text;
 9using System.Threading;
 10using System.Threading.Tasks;
 11using System.Xml;
 12using System.Xml.XPath;
 13
 14namespace CoreWCF.Xml
 15{
 16    internal interface IXmlMtomWriterInitializer
 17    {
 18        void SetOutput(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo, string boundary, string s
 19    }
 20
 21    internal class XmlMtomWriter : XmlDictionaryWriter, IXmlMtomWriterInitializer, IAsyncXmlWriter
 22    {
 23        public static XmlDictionaryWriter Create(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo)
 24
 25        public static XmlDictionaryWriter Create(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo,
 26        {
 27            XmlMtomWriter writer = new XmlMtomWriter();
 28            writer.SetOutput(stream, encoding, maxSizeInBytes, startInfo, boundary, startUri, writeMessageHeaders, ownsS
 29            return writer;
 30        }
 31
 32        // Maximum number of bytes that are inlined as base64 data without being MTOM-optimized as xop:Include
 33        private const int MaxInlinedBytes = 767;  // 768 will be the first MIMEd length
 34
 35        private int _maxSizeInBytes;
 36        private XmlDictionaryWriter _writer;
 37        private XmlDictionaryWriter _infosetWriter;
 38        private MimeWriter _mimeWriter;
 39        private Encoding _encoding;
 40        private bool _isUTF8;
 41        private string _contentID;
 42        private string _contentType;
 43        private string _initialContentTypeForRootPart;
 44        private string _initialContentTypeForMimeMessage;
 45        private MemoryStream _contentTypeStream;
 46        private List<MimePart> _mimeParts;
 47        private IList<MtomBinaryData> _binaryDataChunks;
 48        private int _depth;
 49        private int _totalSizeOfMimeParts;
 50        private int _sizeOfBufferedBinaryData;
 51        private char[] _chars;
 52        private byte[] _bytes;
 53        private bool _isClosed;
 54        private bool _ownsStream;
 55
 56        public XmlMtomWriter()
 57        {
 58        }
 59
 60        public void SetOutput(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo, string boundary, s
 61        {
 62            if (encoding == null)
 63                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(encoding));
 64            if (maxSizeInBytes < 0)
 65                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(maxSize
 66            _maxSizeInBytes = maxSizeInBytes;
 67            _encoding = encoding;
 68            _isUTF8 = IsUTF8Encoding(encoding);
 69            Initialize(stream, startInfo, boundary, startUri, writeMessageHeaders, ownsStream);
 70        }
 71
 72        private XmlDictionaryWriter Writer
 73        {
 74            get
 75            {
 76                if (!IsInitialized)
 77                {
 78                    Initialize();
 79                }
 80                return _writer;
 81            }
 82        }
 83
 84        private bool IsInitialized => (_initialContentTypeForRootPart == null);
 85
 86        private void Initialize(Stream stream, string startInfo, string boundary, string startUri, bool writeMessageHead
 87        {
 88            if (stream == null)
 89                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(stream));
 90            if (startInfo == null)
 91                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(startInfo));
 92            if (boundary == null)
 93                boundary = GetBoundaryString();
 94            if (startUri == null)
 95                startUri = GenerateUriForMimePart(0);
 96            if (!MailBnfHelper.IsValidMimeBoundary(boundary))
 97                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.Format(SR.MtomBoundar
 98
 99            _ownsStream = ownsStream;
 100            _isClosed = false;
 101            _depth = 0;
 102            _totalSizeOfMimeParts = 0;
 103            _sizeOfBufferedBinaryData = 0;
 104            _binaryDataChunks = null;
 105            _contentType = null;
 106            _contentTypeStream = null;
 107            _contentID = startUri;
 108            if (_mimeParts != null)
 109                _mimeParts.Clear();
 110            _mimeWriter = new MimeWriter(stream, boundary);
 111            _initialContentTypeForRootPart = GetContentTypeForRootMimePart(_encoding, startInfo);
 112            if (writeMessageHeaders)
 113                _initialContentTypeForMimeMessage = GetContentTypeForMimeMessage(boundary, startUri, startInfo);
 114        }
 115
 116        private void Initialize()
 117        {
 118            if (_isClosed)
 119                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.XmlWriterClos
 120
 121            if (_initialContentTypeForRootPart != null)
 122            {
 123                if (_initialContentTypeForMimeMessage != null)
 124                {
 125                    _mimeWriter.StartPreface();
 126                    _mimeWriter.WriteHeader(MimeGlobals.MimeVersionHeader, MimeGlobals.DefaultVersion);
 127                    _mimeWriter.WriteHeader(MimeGlobals.ContentTypeHeader, _initialContentTypeForMimeMessage);
 128                    _initialContentTypeForMimeMessage = null;
 129                }
 130
 131                WriteMimeHeaders(_contentID, _initialContentTypeForRootPart, _isUTF8 ? MimeGlobals.Encoding8bit : MimeGl
 132
 133                Stream infosetContentStream = _mimeWriter.GetContentStreamAsync().GetAwaiter().GetResult();
 134                IXmlTextWriterInitializer initializer = _writer as IXmlTextWriterInitializer;
 135                if (initializer == null)
 136                    _writer = XmlDictionaryWriter.CreateTextWriter(infosetContentStream, _encoding, _ownsStream);
 137                else
 138                    initializer.SetOutput(infosetContentStream, _encoding, _ownsStream);
 139
 140                _contentID = null;
 141                _initialContentTypeForRootPart = null;
 142            }
 143        }
 144
 145        private static string GetBoundaryString() => MimeBoundaryGenerator.Next();
 146
 147        internal static bool IsUTF8Encoding(Encoding encoding) => encoding.WebName == "utf-8";
 148
 149        private static string GetContentTypeForMimeMessage(string boundary, string startUri, string startInfo)
 150        {
 151            StringBuilder contentTypeBuilder = new StringBuilder(
 152                string.Format(CultureInfo.InvariantCulture, "{0}/{1};{2}=\"{3}\";{4}=\"{5}\"",
 153                    MtomGlobals.MediaType, MtomGlobals.MediaSubtype,
 154                    MtomGlobals.TypeParam, MtomGlobals.XopType,
 155                    MtomGlobals.BoundaryParam, boundary));
 156
 157            if (startUri != null && startUri.Length > 0)
 158                contentTypeBuilder.AppendFormat(CultureInfo.InvariantCulture, ";{0}=\"<{1}>\"", MtomGlobals.StartParam, 
 159
 160            if (startInfo != null && startInfo.Length > 0)
 161                contentTypeBuilder.AppendFormat(CultureInfo.InvariantCulture, ";{0}=\"{1}\"", MtomGlobals.StartInfoParam
 162
 163            return contentTypeBuilder.ToString();
 164        }
 165
 166        private static string GetContentTypeForRootMimePart(Encoding encoding, string startInfo)
 167        {
 168            string contentType = string.Format(CultureInfo.InvariantCulture, "{0};{1}={2}", MtomGlobals.XopType, MtomGlo
 169
 170            if (startInfo != null)
 171                contentType = string.Format(CultureInfo.InvariantCulture, "{0};{1}=\"{2}\"", contentType, MtomGlobals.Ty
 172
 173            return contentType;
 174        }
 175
 176        private static string CharSet(Encoding enc)
 177        {
 178            string name = enc.WebName;
 179            if (string.Compare(name, Encoding.UTF8.WebName, StringComparison.OrdinalIgnoreCase) == 0)
 180                return name;
 181            if (string.Compare(name, Encoding.Unicode.WebName, StringComparison.OrdinalIgnoreCase) == 0)
 182                return "utf-16LE";
 183            if (string.Compare(name, Encoding.BigEndianUnicode.WebName, StringComparison.OrdinalIgnoreCase) == 0)
 184                return "utf-16BE";
 185            return name;
 186        }
 187
 188        public override void WriteStartElement(string prefix, string localName, string ns)
 189        {
 190            WriteBase64InlineIfPresent();
 191            ThrowIfElementIsXOPInclude(prefix, localName, ns);
 192            Writer.WriteStartElement(prefix, localName, ns);
 193            _depth++;
 194        }
 195
 196        public override async Task WriteStartElementAsync(string prefix, string localName, string ns)
 197        {
 198            await WriteBase64InlineIfPresentAsync();
 199            ThrowIfElementIsXOPInclude(prefix, localName, ns);
 200            await Writer.WriteStartElementAsync(prefix, localName, ns);
 201            _depth++;
 202        }
 203
 204        public override void WriteStartElement(string prefix, XmlDictionaryString localName, XmlDictionaryString ns)
 205        {
 206            if (localName == null)
 207                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(localName));
 208
 209            WriteBase64InlineIfPresent();
 210            ThrowIfElementIsXOPInclude(prefix, localName.Value, ns == null ? null : ns.Value);
 211            Writer.WriteStartElement(prefix, localName, ns);
 212            _depth++;
 213        }
 214
 215        private void ThrowIfElementIsXOPInclude(string prefix, string localName, string ns)
 216        {
 217            if (ns == null)
 218            {
 219                // This check isn't as thorough as on .NET Framework due to use of internal api's.
 220                // The scenario that this won't catch is when the XopIncludeNamespace has been added
 221                // multiple times with different prefixes. I don't believe that will be the case as
 222                // any attempt to add it with a different prefix would hit this code path anyway.
 223                var xopPrefix = Writer.LookupPrefix(MtomGlobals.XopIncludeNamespace);
 224                if (xopPrefix != null && xopPrefix == prefix)
 225                    ns = MtomGlobals.XopIncludeNamespace;
 226            }
 227
 228            if (localName == MtomGlobals.XopIncludeLocalName && ns == MtomGlobals.XopIncludeNamespace)
 229                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomDataMustNotC
 230        }
 231
 232        public override void WriteEndElement()
 233        {
 234            WriteXOPInclude();
 235            Writer.WriteEndElement();
 236            _depth--;
 237            WriteXOPBinaryParts();
 238        }
 239
 240        public override async Task WriteEndElementAsync()
 241        {
 242            await WriteXOPIncludeAsync();
 243            await Writer.WriteEndElementAsync();
 244            _depth--;
 245            await WriteXOPBinaryPartsAsync();
 246        }
 247
 248        public override void WriteFullEndElement()
 249        {
 250            WriteXOPInclude();
 251            Writer.WriteFullEndElement();
 252            _depth--;
 253            WriteXOPBinaryParts();
 254        }
 255
 256        public override void WriteValue(IStreamProvider value)
 257        {
 258            if (value == null)
 259                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(value)));
 260
 261            if (Writer.WriteState == WriteState.Element)
 262            {
 263                if (_binaryDataChunks == null)
 264                {
 265                    _binaryDataChunks = new List<MtomBinaryData>();
 266                    _contentID = GenerateUriForMimePart((_mimeParts == null) ? 1 : _mimeParts.Count + 1);
 267                }
 268                _binaryDataChunks.Add(new MtomBinaryData(value));
 269            }
 270            else
 271                Writer.WriteValue(value);
 272        }
 273
 274        public override Task WriteValueAsync(IStreamProvider value)
 275        {
 276            if (value == null)
 277                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(value)));
 278
 279            if (Writer.WriteState == WriteState.Element)
 280            {
 281                if (_binaryDataChunks == null)
 282                {
 283                    _binaryDataChunks = new List<MtomBinaryData>();
 284                    _contentID = GenerateUriForMimePart((_mimeParts == null) ? 1 : _mimeParts.Count + 1);
 285                }
 286                _binaryDataChunks.Add(new MtomBinaryData(value));
 287                return Task.CompletedTask;
 288            }
 289            else
 290                return Writer.WriteValueAsync(value);
 291        }
 292
 293        public override void WriteBase64(byte[] buffer, int index, int count)
 294        {
 295            if (Writer.WriteState == WriteState.Element)
 296            {
 297                if (buffer == null)
 298                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException(nameof(buffer)))
 299
 300                // Not checking upper bound because it will be caught by "count".  This is what XmlTextWriter does.
 301                if (index < 0)
 302                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(ind
 303
 304                if (count < 0)
 305                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(cou
 306                if (count > buffer.Length - index)
 307                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(cou
 308
 309                if (_binaryDataChunks == null)
 310                {
 311                    _binaryDataChunks = new List<MtomBinaryData>();
 312                    _contentID = GenerateUriForMimePart((_mimeParts == null) ? 1 : _mimeParts.Count + 1);
 313                }
 314
 315                int totalSize = ValidateSizeOfMessage(_maxSizeInBytes, 0, _totalSizeOfMimeParts);
 316                totalSize += ValidateSizeOfMessage(_maxSizeInBytes, totalSize, _sizeOfBufferedBinaryData);
 317                totalSize += ValidateSizeOfMessage(_maxSizeInBytes, totalSize, count);
 318                _sizeOfBufferedBinaryData += count;
 319                _binaryDataChunks.Add(new MtomBinaryData(buffer, index, count));
 320            }
 321            else
 322                Writer.WriteBase64(buffer, index, count);
 323        }
 324
 325        internal static int ValidateSizeOfMessage(int maxSize, int offset, int size)
 326        {
 327            if (size > (maxSize - offset))
 328                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.Format(SR.MtomExceededMaxS
 329            return size;
 330        }
 331
 332        private void WriteBase64InlineIfPresent()
 333        {
 334            if (_binaryDataChunks != null)
 335            {
 336                WriteBase64Inline();
 337            }
 338        }
 339
 340        private Task WriteBase64InlineIfPresentAsync()
 341        {
 342            if (_binaryDataChunks != null)
 343            {
 344                return WriteBase64InlineAsync();
 345            }
 346            else
 347            {
 348                return Task.CompletedTask;
 349            }
 350        }
 351
 352        private void WriteBase64Inline()
 353        {
 354            foreach (MtomBinaryData data in _binaryDataChunks)
 355            {
 356                if (data.type == MtomBinaryDataType.Provider)
 357                {
 358                    Writer.WriteValue(data.provider);
 359                }
 360                else
 361                {
 362                    Writer.WriteBase64(data.chunk, 0, data.chunk.Length);
 363                }
 364            }
 365            _sizeOfBufferedBinaryData = 0;
 366            _binaryDataChunks = null;
 367            _contentType = null;
 368            _contentID = null;
 369        }
 370
 371        private async Task WriteBase64InlineAsync()
 372        {
 373            foreach (MtomBinaryData data in _binaryDataChunks)
 374            {
 375                if (data.type == MtomBinaryDataType.Provider)
 376                {
 377                    await Writer.WriteValueAsync(data.provider);
 378                }
 379                else
 380                {
 381                    await Writer.WriteBase64Async(data.chunk, 0, data.chunk.Length);
 382                }
 383            }
 384            _sizeOfBufferedBinaryData = 0;
 385            _binaryDataChunks = null;
 386            _contentType = null;
 387            _contentID = null;
 388        }
 389
 390        private void WriteXOPInclude()
 391        {
 392            if (_binaryDataChunks == null)
 393                return;
 394
 395            bool inline = true;
 396            long size = 0;
 397            foreach (MtomBinaryData data in _binaryDataChunks)
 398            {
 399                long len = data.Length;
 400                if (len < 0 || len > (MaxInlinedBytes - size))
 401                {
 402                    inline = false;
 403                    break;
 404                }
 405                size += len;
 406            }
 407
 408            if (inline)
 409                WriteBase64Inline();
 410            else
 411            {
 412                if (_mimeParts == null)
 413                    _mimeParts = new List<MimePart>();
 414
 415                MimePart mimePart = new MimePart(_binaryDataChunks, _contentID, _contentType, MimeGlobals.EncodingBinary
 416                _mimeParts.Add(mimePart);
 417
 418                _totalSizeOfMimeParts += ValidateSizeOfMessage(_maxSizeInBytes, _totalSizeOfMimeParts, mimePart.sizeInBy
 419                _totalSizeOfMimeParts += ValidateSizeOfMessage(_maxSizeInBytes, _totalSizeOfMimeParts, _mimeWriter.GetBo
 420
 421                Writer.WriteStartElement(MtomGlobals.XopIncludePrefix, MtomGlobals.XopIncludeLocalName, MtomGlobals.XopI
 422                Writer.WriteStartAttribute(MtomGlobals.XopIncludeHrefLocalName, MtomGlobals.XopIncludeHrefNamespace);
 423                Writer.WriteValue(string.Format(CultureInfo.InvariantCulture, "{0}{1}", MimeGlobals.ContentIDScheme, _co
 424                Writer.WriteEndAttribute();
 425                Writer.WriteEndElement();
 426                _binaryDataChunks = null;
 427                _sizeOfBufferedBinaryData = 0;
 428                _contentType = null;
 429                _contentID = null;
 430            }
 431        }
 432
 433        private async Task WriteXOPIncludeAsync()
 434        {
 435            if (_binaryDataChunks == null)
 436                return;
 437
 438            bool inline = true;
 439            long size = 0;
 440            foreach (MtomBinaryData data in _binaryDataChunks)
 441            {
 442                long len = data.Length;
 443                if (len < 0 || len > (MaxInlinedBytes - size))
 444                {
 445                    inline = false;
 446                    break;
 447                }
 448                size += len;
 449            }
 450
 451            if (inline)
 452                await WriteBase64InlineAsync();
 453            else
 454            {
 455                if (_mimeParts == null)
 456                    _mimeParts = new List<MimePart>();
 457
 458                MimePart mimePart = new MimePart(_binaryDataChunks, _contentID, _contentType, MimeGlobals.EncodingBinary
 459                _mimeParts.Add(mimePart);
 460
 461                _totalSizeOfMimeParts += ValidateSizeOfMessage(_maxSizeInBytes, _totalSizeOfMimeParts, mimePart.sizeInBy
 462                _totalSizeOfMimeParts += ValidateSizeOfMessage(_maxSizeInBytes, _totalSizeOfMimeParts, _mimeWriter.GetBo
 463
 464                await Writer.WriteStartElementAsync(MtomGlobals.XopIncludePrefix, MtomGlobals.XopIncludeLocalName, MtomG
 465                Writer.WriteStartAttribute(MtomGlobals.XopIncludeHrefLocalName, MtomGlobals.XopIncludeHrefNamespace);
 466                Writer.WriteString(string.Format(CultureInfo.InvariantCulture, "{0}{1}", MimeGlobals.ContentIDScheme, _c
 467                Writer.WriteEndAttribute();
 468                await Writer.WriteEndElementAsync();
 469                _binaryDataChunks = null;
 470                _sizeOfBufferedBinaryData = 0;
 471                _contentType = null;
 472                _contentID = null;
 473            }
 474        }
 475
 476        public static string GenerateUriForMimePart(int index) => string.Format(CultureInfo.InvariantCulture, "http://te
 477
 478        private void WriteXOPBinaryParts()
 479        {
 480            if (_depth > 0 || _mimeWriter.WriteState == MimeWriterState.Closed)
 481                return;
 482
 483            if (Writer.WriteState != WriteState.Closed)
 484                Writer.Flush();
 485
 486            if (_mimeParts != null)
 487            {
 488                foreach (MimePart part in _mimeParts)
 489                {
 490                    WriteMimeHeaders(part.contentID, part.contentType, part.contentTransferEncoding);
 491                    Stream s = _mimeWriter.GetContentStream();
 492                    int bufferSize = 65536;
 493                    Stream stream = null;
 494                    foreach (MtomBinaryData data in part.binaryData)
 495                    {
 496                        if (data.type == MtomBinaryDataType.Provider)
 497                        {
 498                            stream = data.provider.GetStream();
 499                            if (stream == null)
 500                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.XmlInvalid
 501
 502                            stream.CopyTo(s, bufferSize);
 503
 504                            data.provider.ReleaseStream(stream);
 505                        }
 506                        else
 507                        {
 508                            s.Write(data.chunk, 0, data.chunk.Length);
 509                        }
 510                    }
 511                }
 512                _mimeParts.Clear();
 513            }
 514            _mimeWriter.Close();
 515        }
 516
 517        private async Task WriteXOPBinaryPartsAsync()
 518        {
 519            if (_depth > 0 || _mimeWriter.WriteState == MimeWriterState.Closed)
 520                return;
 521
 522            if (Writer.WriteState != WriteState.Closed)
 523                await Writer.FlushAsync();
 524
 525            if (_mimeParts != null)
 526            {
 527                foreach (MimePart part in _mimeParts)
 528                {
 529                    await WriteMimeHeadersAsync(part.contentID, part.contentType, part.contentTransferEncoding);
 530                    Stream s = await _mimeWriter.GetContentStreamAsync();
 531                    int bufferSize = 65536;
 532                    Stream stream = null;
 533                    foreach (MtomBinaryData data in part.binaryData)
 534                    {
 535                        if (data.type == MtomBinaryDataType.Provider)
 536                        {
 537                            stream = data.provider.GetStream();
 538                            if (stream == null)
 539                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.XmlInvalid
 540
 541                            await stream.CopyToAsync(s, bufferSize);
 542
 543                            data.provider.ReleaseStream(stream);
 544                        }
 545                        else
 546                        {
 547                            await s.WriteAsync(data.chunk, 0, data.chunk.Length);
 548                        }
 549                    }
 550                }
 551                _mimeParts.Clear();
 552            }
 553            await _mimeWriter.CloseAsync();
 554        }
 555
 556        private void WriteMimeHeaders(string contentID, string contentType, string contentTransferEncoding)
 557        {
 558            _mimeWriter.StartPart();
 559            if (contentID != null)
 560                _mimeWriter.WriteHeader(MimeGlobals.ContentIDHeader, string.Format(CultureInfo.InvariantCulture, "<{0}>"
 561            if (contentTransferEncoding != null)
 562                _mimeWriter.WriteHeader(MimeGlobals.ContentTransferEncodingHeader, contentTransferEncoding);
 563            if (contentType != null)
 564                _mimeWriter.WriteHeader(MimeGlobals.ContentTypeHeader, contentType);
 565        }
 566
 567        private async Task WriteMimeHeadersAsync(string contentID, string contentType, string contentTransferEncoding)
 568        {
 569            await _mimeWriter.StartPartAsync();
 570            if (contentID != null)
 571                _mimeWriter.WriteHeader(MimeGlobals.ContentIDHeader, string.Format(CultureInfo.InvariantCulture, "<{0}>"
 572            if (contentTransferEncoding != null)
 573                _mimeWriter.WriteHeader(MimeGlobals.ContentTransferEncodingHeader, contentTransferEncoding);
 574            if (contentType != null)
 575                _mimeWriter.WriteHeader(MimeGlobals.ContentTypeHeader, contentType);
 576        }
 577
 578        public override void Close()
 579        {
 580            if (!_isClosed)
 581            {
 582                _isClosed = true;
 583                if (IsInitialized)
 584                {
 585                    WriteXOPInclude();
 586                    if (Writer.WriteState == WriteState.Element ||
 587                        Writer.WriteState == WriteState.Attribute ||
 588                        Writer.WriteState == WriteState.Content)
 589                    {
 590                        Writer.WriteEndDocument();
 591                    }
 592                    Writer.Flush();
 593                    _depth = 0;
 594                    WriteXOPBinaryParts();
 595                    Writer.Close();
 596                }
 597            }
 598        }
 599
 600        private void CheckIfStartContentTypeAttribute(string localName, string ns)
 601        {
 602            if (localName != null && localName == MtomGlobals.MimeContentTypeLocalName
 603                && ns != null && (ns == MtomGlobals.MimeContentTypeNamespace200406 || ns == MtomGlobals.MimeContentTypeN
 604            {
 605                _contentTypeStream = new MemoryStream();
 606                this._infosetWriter = Writer;
 607                _writer = XmlDictionaryWriter.CreateBinaryWriter(_contentTypeStream);
 608                Writer.WriteStartElement("Wrapper");
 609                Writer.WriteStartAttribute(localName, ns);
 610            }
 611        }
 612
 613        private void CheckIfEndContentTypeAttribute()
 614        {
 615            if (_contentTypeStream != null)
 616            {
 617                Writer.WriteEndAttribute();
 618                Writer.WriteEndElement();
 619                Writer.Flush();
 620                _contentTypeStream.Position = 0;
 621                XmlReader contentTypeReader = XmlDictionaryReader.CreateBinaryReader(_contentTypeStream, null, XmlDictio
 622                while (contentTypeReader.Read())
 623                {
 624                    if (contentTypeReader.IsStartElement("Wrapper"))
 625                    {
 626                        _contentType = contentTypeReader.GetAttribute(MtomGlobals.MimeContentTypeLocalName, MtomGlobals.
 627                        if (_contentType == null)
 628                        {
 629                            _contentType = contentTypeReader.GetAttribute(MtomGlobals.MimeContentTypeLocalName, MtomGlob
 630                        }
 631                        break;
 632                    }
 633                }
 634                _writer = _infosetWriter;
 635                this._infosetWriter = null;
 636                _contentTypeStream = null;
 637                if (_contentType != null)
 638                    Writer.WriteString(_contentType);
 639            }
 640        }
 641
 642        public override void Flush()
 643        {
 644            if (IsInitialized)
 645                Writer.Flush();
 646        }
 647
 648        public override Task FlushAsync()
 649        {
 650            if (IsInitialized)
 651                return Writer.FlushAsync();
 652            else
 653                return Task.CompletedTask;
 654        }
 655
 656        public override string LookupPrefix(string ns) => Writer.LookupPrefix(ns);
 657
 658        public override XmlWriterSettings Settings => Writer.Settings;
 659
 660        public override void WriteAttributes(XmlReader reader, bool defattr) => Writer.WriteAttributes(reader, defattr);
 661
 662        public override void WriteBinHex(byte[] buffer, int index, int count)
 663        {
 664            WriteBase64InlineIfPresent();
 665            Writer.WriteBinHex(buffer, index, count);
 666        }
 667
 668        public override void WriteCData(string text)
 669        {
 670            WriteBase64InlineIfPresent();
 671            Writer.WriteCData(text);
 672        }
 673
 674        public override void WriteCharEntity(char ch)
 675        {
 676            WriteBase64InlineIfPresent();
 677            Writer.WriteCharEntity(ch);
 678        }
 679
 680        public override void WriteChars(char[] buffer, int index, int count)
 681        {
 682            WriteBase64InlineIfPresent();
 683            Writer.WriteChars(buffer, index, count);
 684        }
 685
 686        public override void WriteComment(string text)
 687        {
 688            // Don't write comments after the document element
 689            if (_depth == 0 && _mimeWriter.WriteState == MimeWriterState.Closed)
 690                return;
 691
 692            WriteBase64InlineIfPresent();
 693            Writer.WriteComment(text);
 694        }
 695
 696        public override void WriteDocType(string name, string pubid, string sysid, string subset)
 697        {
 698            WriteBase64InlineIfPresent();
 699            Writer.WriteDocType(name, pubid, sysid, subset);
 700        }
 701
 702        public override void WriteEndAttribute()
 703        {
 704            CheckIfEndContentTypeAttribute();
 705            Writer.WriteEndAttribute();
 706        }
 707
 708        public override void WriteEndDocument()
 709        {
 710            WriteXOPInclude();
 711            Writer.WriteEndDocument();
 712            _depth = 0;
 713            WriteXOPBinaryParts();
 714        }
 715
 716        public override void WriteEntityRef(string name)
 717        {
 718            WriteBase64InlineIfPresent();
 719            Writer.WriteEntityRef(name);
 720        }
 721
 722        public override void WriteName(string name)
 723        {
 724            WriteBase64InlineIfPresent();
 725            Writer.WriteName(name);
 726        }
 727
 728        public override void WriteNmToken(string name)
 729        {
 730            WriteBase64InlineIfPresent();
 731            Writer.WriteNmToken(name);
 732        }
 733
 734        protected override void WriteTextNode(XmlDictionaryReader reader, bool attribute)
 735        {
 736            Type type = reader.ValueType;
 737            if (type == typeof(string))
 738            {
 739                if (reader.CanReadValueChunk)
 740                {
 741                    if (_chars == null)
 742                    {
 743                        _chars = new char[256];
 744                    }
 745                    int count;
 746                    while ((count = reader.ReadValueChunk(_chars, 0, _chars.Length)) > 0)
 747                    {
 748                        WriteChars(_chars, 0, count);
 749                    }
 750                }
 751                else
 752                {
 753                    WriteString(reader.Value);
 754                }
 755                if (!attribute)
 756                {
 757                    reader.Read();
 758                }
 759            }
 760            else if (type == typeof(byte[]))
 761            {
 762                if (reader.CanReadBinaryContent)
 763                {
 764                    // Its best to read in buffers that are a multiple of 3 so we don't break base64 boundaries when con
 765                    if (_bytes == null)
 766                    {
 767                        _bytes = new byte[384];
 768                    }
 769                    int count;
 770                    while ((count = reader.ReadValueAsBase64(_bytes, 0, _bytes.Length)) > 0)
 771                    {
 772                        WriteBase64(_bytes, 0, count);
 773                    }
 774                }
 775                else
 776                {
 777                    WriteString(reader.Value);
 778                }
 779                if (!attribute)
 780                {
 781                    reader.Read();
 782                }
 783            }
 784            else
 785            {
 786                base.WriteTextNode(reader, attribute);
 787            }
 788        }
 789
 790        public override void WriteNode(XPathNavigator navigator, bool defattr)
 791        {
 792            WriteBase64InlineIfPresent();
 793            Writer.WriteNode(navigator, defattr);
 794        }
 795
 796        public override void WriteProcessingInstruction(string name, string text)
 797        {
 798            WriteBase64InlineIfPresent();
 799            Writer.WriteProcessingInstruction(name, text);
 800        }
 801
 802        public override void WriteQualifiedName(string localName, string namespaceUri)
 803        {
 804            WriteBase64InlineIfPresent();
 805            Writer.WriteQualifiedName(localName, namespaceUri);
 806        }
 807
 808        public override void WriteRaw(char[] buffer, int index, int count)
 809        {
 810            WriteBase64InlineIfPresent();
 811            Writer.WriteRaw(buffer, index, count);
 812        }
 813
 814        public override void WriteRaw(string data)
 815        {
 816            WriteBase64InlineIfPresent();
 817            Writer.WriteRaw(data);
 818        }
 819
 820        public override void WriteStartAttribute(string prefix, string localName, string ns)
 821        {
 822            Writer.WriteStartAttribute(prefix, localName, ns);
 823            CheckIfStartContentTypeAttribute(localName, ns);
 824        }
 825
 826        public override void WriteStartAttribute(string prefix, XmlDictionaryString localName, XmlDictionaryString ns)
 827        {
 828            Writer.WriteStartAttribute(prefix, localName, ns);
 829            if (localName != null && ns != null)
 830                CheckIfStartContentTypeAttribute(localName.Value, ns.Value);
 831        }
 832
 833        public override void WriteStartDocument() => Writer.WriteStartDocument();
 834
 835        public override void WriteStartDocument(bool standalone) => Writer.WriteStartDocument(standalone);
 836
 837        public override WriteState WriteState => Writer.WriteState;
 838
 839        public override void WriteString(string text)
 840        {
 841            // Don't write whitespace after the document element
 842            if (_depth == 0 && _mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(text))
 843                return;
 844
 845            WriteBase64InlineIfPresent();
 846            Writer.WriteString(text);
 847        }
 848
 849        public override void WriteString(XmlDictionaryString value)
 850        {
 851            // Don't write whitespace after the document element
 852            if (_depth == 0 && _mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(value.Value
 853                return;
 854
 855            WriteBase64InlineIfPresent();
 856            Writer.WriteString(value);
 857        }
 858
 859        public override void WriteSurrogateCharEntity(char lowChar, char highChar)
 860        {
 861            WriteBase64InlineIfPresent();
 862            Writer.WriteSurrogateCharEntity(lowChar, highChar);
 863        }
 864
 865        public override void WriteWhitespace(string whitespace)
 866        {
 867            // Don't write whitespace after the document element
 868            if (_depth == 0 && _mimeWriter.WriteState == MimeWriterState.Closed)
 869                return;
 870
 871            WriteBase64InlineIfPresent();
 872            Writer.WriteWhitespace(whitespace);
 873        }
 874
 875        public override void WriteValue(object value)
 876        {
 877            IStreamProvider sp = value as IStreamProvider;
 878            if (sp != null)
 879            {
 880                WriteValue(sp);
 881            }
 882            else
 883            {
 884                WriteBase64InlineIfPresent();
 885                Writer.WriteValue(value);
 886            }
 887        }
 888
 889        public override void WriteValue(string value)
 890        {
 891            // Don't write whitespace after the document element
 892            if (_depth == 0 && _mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(value))
 893                return;
 894
 895            WriteBase64InlineIfPresent();
 896            Writer.WriteValue(value);
 897        }
 898
 899        public override void WriteValue(bool value)
 900        {
 901            WriteBase64InlineIfPresent();
 902            Writer.WriteValue(value);
 903        }
 904
 905        public override void WriteValue(DateTime value)
 906        {
 907            WriteBase64InlineIfPresent();
 908            Writer.WriteValue(value);
 909        }
 910
 911        public override void WriteValue(double value)
 912        {
 913            WriteBase64InlineIfPresent();
 914            Writer.WriteValue(value);
 915        }
 916
 917        public override void WriteValue(int value)
 918        {
 919            WriteBase64InlineIfPresent();
 920            Writer.WriteValue(value);
 921        }
 922
 923        public override void WriteValue(long value)
 924        {
 925            WriteBase64InlineIfPresent();
 926            Writer.WriteValue(value);
 927        }
 928
 929        public override void WriteValue(XmlDictionaryString value)
 930        {
 931            // Don't write whitespace after the document element
 932            if (_depth == 0 && _mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(value.Value
 933                return;
 934
 935            WriteBase64InlineIfPresent();
 936            Writer.WriteValue(value);
 937        }
 938
 939        public override void WriteXmlnsAttribute(string prefix, string ns) => Writer.WriteXmlnsAttribute(prefix, ns);
 940
 941        public override void WriteXmlnsAttribute(string prefix, XmlDictionaryString ns) => Writer.WriteXmlnsAttribute(pr
 942
 943        public override string XmlLang => Writer.XmlLang;
 944
 945        public override XmlSpace XmlSpace => Writer.XmlSpace;
 946
 947        private static class MimeBoundaryGenerator
 948        {
 949            private static long id;
 950            private static string prefix;
 951
 952            static MimeBoundaryGenerator()
 953            {
 954                prefix = string.Concat(Guid.NewGuid().ToString(), "+id=");
 955            }
 956
 957            internal static string Next()
 958            {
 959                long nextId = Interlocked.Increment(ref id);
 960                return string.Format(CultureInfo.InvariantCulture, "{0}{1}", prefix, nextId);
 961            }
 962        }
 963
 964        private class MimePart
 965        {
 966            internal IList<MtomBinaryData> binaryData;
 967            internal string contentID;
 968            internal string contentType;
 969            internal string contentTransferEncoding;
 970            internal int sizeInBytes;
 971
 972            internal MimePart(IList<MtomBinaryData> binaryData, string contentID, string contentType, string contentTran
 973            {
 974                this.binaryData = binaryData;
 975                this.contentID = contentID;
 976                this.contentType = contentType ?? MtomGlobals.DefaultContentTypeForBinary;
 977                this.contentTransferEncoding = contentTransferEncoding;
 978                sizeInBytes = GetSize(contentID, contentType, contentTransferEncoding, sizeOfBufferedBinaryData, maxSize
 979            }
 980
 981            private static int GetSize(string contentID, string contentType, string contentTransferEncoding, int sizeOfB
 982            {
 983                int size = XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, 0, MimeGlobals.CRLF.Length * 3);
 984                if (contentTransferEncoding != null)
 985                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, MimeWriter.GetHeaderSize(MimeGloba
 986                if (contentType != null)
 987                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, MimeWriter.GetHeaderSize(MimeGloba
 988                if (contentID != null)
 989                {
 990                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, MimeWriter.GetHeaderSize(MimeGloba
 991                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, 2); // include '<' and '>'
 992                }
 993                size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, sizeOfBufferedBinaryData);
 994                return size;
 995            }
 996        }
 997    }
 998
 999
 1000    internal static class MtomGlobals
 1001    {
 1002        internal static string XopIncludeLocalName = "Include";
 1003        internal static string XopIncludeNamespace = "http://www.w3.org/2004/08/xop/include";
 1004        internal static string XopIncludePrefix = "xop";
 1005        internal static string XopIncludeHrefLocalName = "href";
 1006        internal static string XopIncludeHrefNamespace = string.Empty;
 1007        internal static string MediaType = "multipart";
 1008        internal static string MediaSubtype = "related";
 1009        internal static string BoundaryParam = "boundary";
 1010        internal static string TypeParam = "type";
 1011        internal static string XopMediaType = "application";
 1012        internal static string XopMediaSubtype = "xop+xml";
 1013        internal static string XopType = "application/xop+xml";
 1014        internal static string StartParam = "start";
 1015        internal static string StartInfoParam = "start-info";
 1016        internal static string ActionParam = "action";
 1017        internal static string CharsetParam = "charset";
 1018        internal static string MimeContentTypeLocalName = "contentType";
 1019        internal static string MimeContentTypeNamespace200406 = "http://www.w3.org/2004/06/xmlmime";
 1020        internal static string MimeContentTypeNamespace200505 = "http://www.w3.org/2005/05/xmlmime";
 1021        internal static string DefaultContentTypeForBinary = "application/octet-stream";
 1022    }
 1023
 1024    internal static class MimeGlobals
 1025    {
 1026        internal static string MimeVersionHeader = "MIME-Version";
 1027        internal static string DefaultVersion = "1.0";
 1028        internal static string ContentIDScheme = "cid:";
 1029        internal static string ContentIDHeader = "Content-ID";
 1030        internal static string ContentTypeHeader = "Content-Type";
 1031        internal static string ContentTransferEncodingHeader = "Content-Transfer-Encoding";
 1032        internal static string EncodingBinary = "binary";
 1033        internal static string Encoding8bit = "8bit";
 1034        internal static byte[] COLONSPACE = new byte[] { (byte)':', (byte)' ' };
 1035        internal static byte[] DASHDASH = new byte[] { (byte)'-', (byte)'-' };
 1036        internal static byte[] CRLF = new byte[] { (byte)'\r', (byte)'\n' };
 1037        // Per RFC2045, preceding CRLF sequence is part of the boundary. MIME boundary tags begin with --
 1038        internal static byte[] BoundaryPrefix = new byte[] { (byte)'\r', (byte)'\n', (byte)'-', (byte)'-' };
 1039    }
 1040
 1041    internal enum MimeWriterState
 1042    {
 1043        Start,
 1044        StartPreface,
 1045        StartPart,
 1046        Header,
 1047        Content,
 1048        Closed,
 1049    }
 1050
 1051    internal class MimeWriter
 1052    {
 1053        private Stream stream;
 1054        private byte[] boundaryBytes;
 1055        private MimeWriterState state;
 1056        private BufferedWrite bufferedWrite;
 1057        private Stream contentStream;
 1058
 1059        internal MimeWriter(Stream stream, string boundary)
 1060        {
 1061            if (stream == null)
 1062                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(stream));
 1063            if (boundary == null)
 1064                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(boundary));
 1065
 1066            this.stream = stream;
 1067            boundaryBytes = MimeWriter.GetBoundaryBytes(boundary);
 1068            state = MimeWriterState.Start;
 1069            bufferedWrite = new BufferedWrite();
 1070        }
 1071
 1072        internal static int GetHeaderSize(string name, string value, int maxSizeInBytes)
 1073        {
 1074            if (name == null)
 1075                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(name));
 1076            if (value == null)
 1077                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value));
 1078
 1079            int size = XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, 0, MimeGlobals.COLONSPACE.Length + MimeGlobal
 1080            size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, name.Length);
 1081            size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, value.Length);
 1082            return size;
 1083        }
 1084
 1085        internal static byte[] GetBoundaryBytes(string boundary)
 1086        {
 1087            byte[] boundaryBytes = new byte[boundary.Length + MimeGlobals.BoundaryPrefix.Length];
 1088            for (int i = 0; i < MimeGlobals.BoundaryPrefix.Length; i++)
 1089                boundaryBytes[i] = MimeGlobals.BoundaryPrefix[i];
 1090            Encoding.ASCII.GetBytes(boundary, 0, boundary.Length, boundaryBytes, MimeGlobals.BoundaryPrefix.Length);
 1091            return boundaryBytes;
 1092        }
 1093
 1094        internal MimeWriterState WriteState => state;
 1095
 1096        internal int GetBoundarySize() => boundaryBytes.Length;
 1097
 1098        internal void StartPreface()
 1099        {
 1100            if (state != MimeWriterState.Start)
 1101                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR.Mim
 1102
 1103            state = MimeWriterState.StartPreface;
 1104        }
 1105
 1106        internal async Task StartPartAsync()
 1107        {
 1108            switch (state)
 1109            {
 1110                case MimeWriterState.StartPart:
 1111                case MimeWriterState.Closed:
 1112                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR
 1113                default:
 1114                    break;
 1115            }
 1116
 1117            state = MimeWriterState.StartPart;
 1118
 1119            if (contentStream != null)
 1120            {
 1121                await contentStream.FlushAsync();
 1122                contentStream = null;
 1123            }
 1124
 1125            bufferedWrite.Write(boundaryBytes);
 1126            bufferedWrite.Write(MimeGlobals.CRLF);
 1127        }
 1128
 1129        internal void StartPart()
 1130        {
 1131            switch (state)
 1132            {
 1133                case MimeWriterState.StartPart:
 1134                case MimeWriterState.Closed:
 1135                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR
 1136                default:
 1137                    break;
 1138            }
 1139
 1140            state = MimeWriterState.StartPart;
 1141
 1142            if (contentStream != null)
 1143            {
 1144                contentStream.Flush();
 1145                contentStream = null;
 1146            }
 1147
 1148            bufferedWrite.Write(boundaryBytes);
 1149            bufferedWrite.Write(MimeGlobals.CRLF);
 1150        }
 1151
 1152        internal void Close()
 1153        {
 1154            switch (state)
 1155            {
 1156                case MimeWriterState.Closed:
 1157                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR
 1158                default:
 1159                    break;
 1160            }
 1161
 1162            state = MimeWriterState.Closed;
 1163
 1164            if (contentStream != null)
 1165            {
 1166                contentStream.Flush();
 1167                contentStream = null;
 1168            }
 1169
 1170            bufferedWrite.Write(boundaryBytes);
 1171            bufferedWrite.Write(MimeGlobals.DASHDASH);
 1172            bufferedWrite.Write(MimeGlobals.CRLF);
 1173
 1174            Flush();
 1175        }
 1176
 1177        internal async Task CloseAsync()
 1178        {
 1179            switch (state)
 1180            {
 1181                case MimeWriterState.Closed:
 1182                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR
 1183                default:
 1184                    break;
 1185            }
 1186
 1187            state = MimeWriterState.Closed;
 1188
 1189            if (contentStream != null)
 1190            {
 1191                await contentStream.FlushAsync();
 1192                contentStream = null;
 1193            }
 1194
 1195            bufferedWrite.Write(boundaryBytes);
 1196            bufferedWrite.Write(MimeGlobals.DASHDASH);
 1197            bufferedWrite.Write(MimeGlobals.CRLF);
 1198
 1199            await FlushAsync();
 1200        }
 1201
 1202        private async Task FlushAsync()
 1203        {
 1204            if (bufferedWrite.Length > 0)
 1205            {
 1206                await stream.WriteAsync(bufferedWrite.GetBuffer(), 0, bufferedWrite.Length);
 1207                bufferedWrite.Reset();
 1208            }
 1209        }
 1210
 1211        private void Flush()
 1212        {
 1213            if (bufferedWrite.Length > 0)
 1214            {
 1215                stream.Write(bufferedWrite.GetBuffer(), 0, bufferedWrite.Length);
 1216                bufferedWrite.Reset();
 1217            }
 1218        }
 1219
 1220        internal void WriteHeader(string name, string value)
 1221        {
 1222            if (name == null)
 1223                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(name));
 1224            if (value == null)
 1225                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value));
 1226
 1227            switch (state)
 1228            {
 1229                case MimeWriterState.Start:
 1230                case MimeWriterState.Content:
 1231                case MimeWriterState.Closed:
 1232                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR
 1233                default:
 1234                    break;
 1235            }
 1236
 1237            state = MimeWriterState.Header;
 1238
 1239            bufferedWrite.Write(name);
 1240            bufferedWrite.Write(MimeGlobals.COLONSPACE);
 1241            bufferedWrite.Write(value);
 1242            bufferedWrite.Write(MimeGlobals.CRLF);
 1243        }
 1244
 1245        internal async Task<Stream> GetContentStreamAsync()
 1246        {
 1247            switch (state)
 1248            {
 1249                case MimeWriterState.Start:
 1250                case MimeWriterState.Content:
 1251                case MimeWriterState.Closed:
 1252                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR
 1253                default:
 1254                    break;
 1255            }
 1256
 1257            state = MimeWriterState.Content;
 1258            bufferedWrite.Write(MimeGlobals.CRLF);
 1259            await FlushAsync();
 1260            contentStream = stream;
 1261            return contentStream;
 1262        }
 1263
 1264        internal Stream GetContentStream()
 1265        {
 1266            switch (state)
 1267            {
 1268                case MimeWriterState.Start:
 1269                case MimeWriterState.Content:
 1270                case MimeWriterState.Closed:
 1271                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.Format(SR
 1272                default:
 1273                    break;
 1274            }
 1275
 1276            state = MimeWriterState.Content;
 1277            bufferedWrite.Write(MimeGlobals.CRLF);
 1278            Flush();
 1279            contentStream = stream;
 1280            return contentStream;
 1281        }
 1282    }
 1283
 1284    internal class BufferedWrite
 1285    {
 1286        private byte[] buffer;
 1287        private int offset;
 1288
 1289        internal BufferedWrite()
 1290            : this(256)
 1291        {
 1292        }
 1293
 1294        internal BufferedWrite(int initialSize)
 1295        {
 1296            buffer = new byte[initialSize];
 1297        }
 1298
 1299        private void EnsureBuffer(int count)
 1300        {
 1301            int currSize = buffer.Length;
 1302            if (count > currSize - offset)
 1303            {
 1304                int newSize = currSize;
 1305                do
 1306                {
 1307                    if (newSize == int.MaxValue)
 1308                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SR.WriteBufferOverflo
 1309                    newSize = (newSize < int.MaxValue / 2) ? newSize * 2 : int.MaxValue;
 1310                }
 1311                while (count > newSize - offset);
 1312                byte[] newBuffer = new byte[newSize];
 1313                Buffer.BlockCopy(buffer, 0, newBuffer, 0, offset);
 1314                buffer = newBuffer;
 1315            }
 1316        }
 1317
 1318        internal int Length => offset;
 1319
 1320        internal byte[] GetBuffer() => buffer;
 1321
 1322        internal void Reset() => offset = 0;
 1323
 1324        internal void Write(byte[] value) => Write(value, 0, value.Length);
 1325
 1326        internal void Write(byte[] value, int index, int count)
 1327        {
 1328            EnsureBuffer(count);
 1329            Buffer.BlockCopy(value, index, buffer, offset, count);
 1330            offset += count;
 1331        }
 1332
 1333        internal void Write(string value) => Write(value, 0, value.Length);
 1334
 1335        internal void Write(string value, int index, int count)
 1336        {
 1337            EnsureBuffer(count);
 1338            for (int i = 0; i < count; i++)
 1339            {
 1340                char c = value[index + i];
 1341                if ((ushort)c > 0xFF)
 1342                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.Format(SR.MimeHeade
 1343                buffer[offset + i] = (byte)c;
 1344            }
 1345            offset += count;
 1346        }
 1347    }
 1348
 1349    internal enum MtomBinaryDataType { Provider, Segment }
 1350
 1351    internal class MtomBinaryData
 1352    {
 1353        internal MtomBinaryDataType type;
 1354
 1355        internal IStreamProvider provider;
 1356        internal byte[] chunk;
 1357
 11358        internal MtomBinaryData(IStreamProvider provider)
 1359        {
 11360            type = MtomBinaryDataType.Provider;
 11361            this.provider = provider;
 11362        }
 1363
 101364        internal MtomBinaryData(byte[] buffer, int offset, int count)
 1365        {
 101366            type = MtomBinaryDataType.Segment;
 101367            chunk = new byte[count];
 101368            Buffer.BlockCopy(buffer, offset, chunk, 0, count);
 101369        }
 1370
 1371        internal long Length
 1372        {
 1373            get
 1374            {
 111375                if (type == MtomBinaryDataType.Segment)
 101376                    return chunk.Length;
 1377
 11378                return -1;
 1379            }
 1380        }
 1381    }
 1382}