Example #1
0
 public virtual void SetContentType(string contentType)
 {
     if (!contentType.StartsWith("multipart/"))
     {
         throw new ArgumentException("contentType must start with multipart/");
     }
     multipartReader        = new MultipartReader(contentType, this);
     attachmentsByName      = new Dictionary <string, BlobStoreWriter>();
     attachmentsByMd5Digest = new Dictionary <string, BlobStoreWriter>();
 }
		public virtual void SetContentType(string contentType)
		{
			if (!contentType.StartsWith("multipart/"))
			{
				throw new ArgumentException("contentType must start with multipart/");
			}
			multipartReader = new MultipartReader(contentType, this);
			attachmentsByName = new Dictionary<string, BlobStoreWriter>();
			attachmentsByMd5Digest = new Dictionary<string, BlobStoreWriter>();
		}
Example #3
0
 public void SetContentType(String contentType)
 {
     if (!contentType.StartsWith("multipart/", StringComparison.InvariantCultureIgnoreCase))
     {
         throw new ArgumentException("contentType must start with multipart/");
     }
     multipartReader        = new MultipartReader(contentType, this);
     attachmentsByName      = new Dictionary <String, BlobStoreWriter>();
     attachmentsByMd5Digest = new Dictionary <String, BlobStoreWriter>();
 }
 public void SetContentType(String contentType)
 {
     if (contentType.StartsWith("multipart/", StringComparison.InvariantCultureIgnoreCase))
     {
         multipartReader        = new MultipartReader(contentType, this);
         attachmentsByName      = new Dictionary <String, BlobStoreWriter>();
         attachmentsByMd5Digest = new Dictionary <String, BlobStoreWriter>();
     }
     else if (contentType == null ||
              contentType.StartsWith("application/json", StringComparison.Ordinal) ||
              contentType.StartsWith("text/plain", StringComparison.Ordinal))
     {
         // No multipart, so no attachments. Body is pure JSON. (We allow text/plain because CouchDB
         // sends JSON responses using the wrong content-type.)
     }
     else
     {
         throw new ArgumentException("contentType must start with multipart/");
     }
 }
 public virtual void SetContentType(string contentType)
 {
     if (contentType.StartsWith("multipart/"))
     {
         multipartReader        = new MultipartReader(contentType, this);
         attachmentsByName      = new Dictionary <string, BlobStoreWriter>();
         attachmentsByMd5Digest = new Dictionary <string, BlobStoreWriter>();
     }
     else
     {
         if (contentType == null || contentType.StartsWith("application/json") || contentType
             .StartsWith("text/plain"))
         {
         }
         else
         {
             // No multipart, so no attachments. Body is pure JSON. (We allow text/plain because CouchDB
             // sends JSON responses using the wrong content-type.)
             throw new ArgumentException("contentType must start with multipart/");
         }
     }
 }
 public void SetContentType(String contentType)
 {
     if (contentType == null ||
         contentType.StartsWith("application/json", StringComparison.Ordinal) ||
         contentType.StartsWith("text/plain", StringComparison.Ordinal))
     {
         // No multipart, so no attachments. Body is pure JSON. (We allow text/plain because CouchDB
         // sends JSON responses using the wrong content-type.)
         jsonBuffer = new List <byte>();
     }
     else if (contentType.StartsWith("multipart/", StringComparison.InvariantCultureIgnoreCase))
     {
         multipartReader     = new MultipartReader(contentType, this);
         attachmentsByName   = new Dictionary <String, BlobStoreWriter>();
         attachmentsByDigest = new Dictionary <String, BlobStoreWriter>();
     }
     else
     {
         Log.To.Sync.E(TAG, "Invalid contentType in SetContentType ({0}); does not start with multipart/, throwing...",
                       contentType);
         throw new ArgumentException("Does not start with multipart/", "contentType");
     }
 }
 /// <exception cref="System.Exception"></exception>
 public virtual void TestSearchFor()
 {
     string testString = new string("\r\n\r\n");
     byte[] testStringBytes = Sharpen.Runtime.GetBytesForString(testString, Sharpen.Extensions.GetEncoding
         ("UTF-8"));
     MultipartReader reader = new MultipartReader("multipart/related;boundary=X", null
         );
     reader.AppendData(testStringBytes);
     Range r = reader.SearchFor(testStringBytes, 0);
     NUnit.Framework.Assert.AreEqual(0, r.GetLocation());
     NUnit.Framework.Assert.AreEqual(4, r.GetLength());
     Range r2 = reader.SearchFor(Sharpen.Runtime.GetBytesForString(new string("nomatch"
         ), Sharpen.Extensions.GetEncoding("UTF-8")), 0);
     NUnit.Framework.Assert.AreEqual(-1, r2.GetLocation());
     NUnit.Framework.Assert.AreEqual(0, r2.GetLength());
 }
        protected override internal Task ExecuteRequest(HttpClient httpClient, HttpRequestMessage request)
        {
            object fullBody = null;
            Exception error = null;
            HttpResponseMessage response = null;
            if (_tokenSource.IsCancellationRequested)
            {
                RespondWithResult(fullBody, new Exception(string.Format("{0}: Request {1} has been aborted", this, request)), response);
                var tcs = new TaskCompletionSource<bool>();
                tcs.SetCanceled();
                return tcs.Task;
            }

            Log.D(Tag + ".ExecuteRequest", "Sending request: {0}", request);
            var requestTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_tokenSource.Token);
            var retVal = httpClient.SendAsync(request, requestTokenSource.Token);
            retVal.ConfigureAwait(false).GetAwaiter().OnCompleted(() => {
                requestTokenSource.Dispose();
                try {
                    response = retVal.Result;
                } catch(Exception e) {
                    var err = (e is AggregateException) ? e.InnerException : e;
                    Log.E(Tag, "Unhandled Exception", err);
                    error = err;
                    RespondWithResult(fullBody, err, response);
                    return;
                }

                try
                {
                    if (response == null)
                    {
                        Log.E(Tag, "Didn't get response for {0}", request);

                        error = new HttpRequestException();
                        RespondWithResult(fullBody, error, response);
                    }
                    else if (!response.IsSuccessStatusCode)
                    {
                        HttpStatusCode status = response.StatusCode;

                        Log.E(Tag, "Got error status: {0} for {1}.  Reason: {2}", status.GetStatusCode(), request, response.ReasonPhrase);
                        error = new HttpResponseException(status);

                        RespondWithResult(fullBody, error, response);
                    }
                    else
                    {
                        Log.D(Tag, "Processing response: {0}", response);
                        var entity = response.Content;
                        var contentTypeHeader = entity.Headers.ContentType;
                        Stream inputStream = null;
                        if (contentTypeHeader != null && contentTypeHeader.ToString().Contains("multipart/"))
                        {
                            Log.V(Tag, "contentTypeHeader = {0}", contentTypeHeader.ToString());
                            try
                            {
                                _topReader = new MultipartReader(contentTypeHeader.ToString(), this);
                                inputStream = entity.ReadAsStreamAsync().Result;
                                const int bufLen = 1024;
                                var buffer = new byte[bufLen];
                                var numBytesRead = 0;
                                while ((numBytesRead = inputStream.Read(buffer, 0, bufLen)) > 0)
                                {
                                    if (numBytesRead != bufLen)
                                    {
                                        var bufferToAppend = new Couchbase.Lite.Util.ArraySegment<byte>(buffer, 0, numBytesRead).ToArray();
                                        _topReader.AppendData(bufferToAppend);
                                    }
                                    else
                                    {
                                        _topReader.AppendData(buffer);
                                    }
                                }
                                _topReader.Finished();
                                RespondWithResult(fullBody, error, response);
                            }
                            finally
                            {
                                try
                                {
                                    inputStream.Close();
                                }
                                catch (IOException)
                                {
                                }
                            }
                        }
                        else
                        {
                            Log.V(Tag, "contentTypeHeader is not multipart = {0}", contentTypeHeader.ToString());
                            if (entity != null)
                            {
                                try
                                {
                                    inputStream = entity.ReadAsStreamAsync().Result;
                                    fullBody = Manager.GetObjectMapper().ReadValue<object>(inputStream);
                                    RespondWithResult(fullBody, error, response);
                                }
                                finally
                                {
                                    try
                                    {
                                        inputStream.Close();
                                    }
                                    catch (IOException)
                                    {
                                    }
                                }
                            }
                        }
                    }
                }
                catch (AggregateException e)
                {
                    var err = e.InnerException;
                    Log.E(Tag, "Unhandled Exception", err);
                    error = err;
                    RespondWithResult(fullBody, err, response);
                }
                catch (IOException e)
                {
                    Log.E(Tag, "IO Exception", e);
                    error = e;
                    RespondWithResult(fullBody, e, response);
                }
                catch (Exception e)
                {
                    Log.E(Tag, "ExecuteRequest Exception: ", e);
                    error = e;
                    RespondWithResult(fullBody, e, response);
                }
                finally {
                    response.Dispose();
                }
            });

            return retVal;
        }
        public void TestGZipped()
        {
            var mime = GetType().Assembly.GetManifestResourceStream("MultipartStars.mime").ReadAllBytes();
            var reader = new MultipartReader("multipart/related; boundary=\"BOUNDARY\"", this);
            reader.AppendData(mime);
            Assert.IsTrue(reader.Finished);

            CollectionAssert.AreEquivalent(new Dictionary<string, object> {
                { "Content-Encoding", "gzip" },
                { "Content-Length", "24" },
                { "Content-Type", "star-bellies" }
            }, _headerList[0]);

            var stars = _partList[0].Decompress();
            Assert.AreEqual(Enumerable.Repeat((byte)'*', 100), stars);
        }
        public void TestSimple()
        {
            var mime = Encoding.UTF8.GetBytes("--BOUNDARY\r\nFoo: Bar\r\n Header : Val ue \r\n\r\npart the first\r\n--BOUNDARY  \r\n\r\n2nd part\r\n--BOUNDARY--");
            var expectedParts = new List<IEnumerable<byte>> {
                Encoding.UTF8.GetBytes("part the first"),
                Encoding.UTF8.GetBytes("2nd part")
            };

            var expectedHeaders = new List<IDictionary<string, object>> {
                new Dictionary<string, object> {
                    { "Foo", "Bar" },
                    { "Header", "Val ue" }
                },
                new Dictionary<string, object>()
            };

            for (int chunkSize = 1; chunkSize <= mime.Length; ++chunkSize) {
                WriteDebug("--- chunkSize = {0}", chunkSize);
                Reset();
                var reader = new MultipartReader("multipart/related; boundary=\"BOUNDARY\"", this);
                Assert.IsFalse(reader.Finished);

                Range r = new Range(0, 0);
                do {
                    Assert.IsTrue(r.Location < mime.Length, "Parser didn't stop at end");
                    r.Length = Math.Min(chunkSize, mime.Length - r.Location);
                    var sublist = new Couchbase.Lite.Util.ArraySegment<byte>(mime, r.Location, r.Length);
                    reader.AppendData(sublist);
                    r.Location += chunkSize;
                } while(!reader.Finished);
            }

            Assert.AreEqual(expectedHeaders, _headerList);
            Assert.AreEqual(expectedParts, _partList);
        }
        public void TestTypes()
        {
            var reader = new MultipartReader("multipart/related; boundary=\"BOUNDARY\"", null);
            Assert.AreEqual(Encoding.UTF8.GetBytes("\r\n--BOUNDARY"), reader.Boundary);

            reader = new MultipartReader("multipart/related; boundary=BOUNDARY", null);
            Assert.AreEqual(Encoding.UTF8.GetBytes("\r\n--BOUNDARY"), reader.Boundary);

            Assert.Throws<ArgumentException>(() => reader = new MultipartReader("multipart/related; boundary=\"BOUNDARY", null));

            reader = new MultipartReader("multipart/related; boundary=X", null);
            Assert.AreEqual(Encoding.UTF8.GetBytes("\r\n--X"), reader.Boundary);
        }
 public virtual void TestParseContentType()
 {
     Encoding utf8 = Sharpen.Extensions.GetEncoding("UTF-8");
     Dictionary<string, byte[]> contentTypes = new Dictionary<string, byte[]>();
     contentTypes.Put("multipart/related; boundary=\"BOUNDARY\"", Sharpen.Runtime.GetBytesForString
         (new string("\r\n--BOUNDARY"), utf8));
     contentTypes.Put("multipart/related; boundary=BOUNDARY", Sharpen.Runtime.GetBytesForString
         (new string("\r\n--BOUNDARY"), utf8));
     contentTypes.Put("multipart/related;boundary=X", Sharpen.Runtime.GetBytesForString
         (new string("\r\n--X"), utf8));
     foreach (string contentType in contentTypes.Keys)
     {
         MultipartReaderDelegate delegate_ = null;
         MultipartReader reader = new MultipartReader(contentType, delegate_);
         byte[] expectedBoundary = (byte[])contentTypes.Get(contentType);
         byte[] boundary = reader.GetBoundary();
         NUnit.Framework.Assert.IsTrue(Arrays.Equals(boundary, expectedBoundary));
     }
     try
     {
         MultipartReaderDelegate delegate_ = null;
         MultipartReader reader = new MultipartReader("multipart/related; boundary=\"BOUNDARY"
             , delegate_);
         NUnit.Framework.Assert.IsTrue("Should not have gotten here, above lines should have thrown exception"
             , false);
     }
     catch (Exception)
     {
     }
 }
        private Task ExecuteRequest(CouchbaseLiteHttpClient httpClient, HttpRequestMessage request)
        {
            object fullBody = null;
            Exception error = null;
            HttpResponseMessage response = null;
            if (_tokenSource.IsCancellationRequested) {
                RespondWithResult(fullBody, new Exception(string.Format("{0}: Request {1} has been aborted", this, request)), response);
                var tcs = new TaskCompletionSource<bool>();
                tcs.SetCanceled();
                return tcs.Task;
            }
                
            request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
            request.Headers.Add("X-Accept-Part-Encoding", "gzip");

            Log.To.Sync.V(Tag, "Sending request: {0}", request);
            var requestTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_tokenSource.Token);
            httpClient.Authenticator = Authenticator;
            return httpClient.SendAsync(request, requestTokenSource.Token).ContinueWith(t =>
            {
                requestTokenSource.Dispose();
                try {
                    response = t.Result;
                } catch(Exception e) {
                    var err = Misc.Flatten(e).First();
                    Log.To.Sync.E(Tag, "Unhandled exception while getting bulk documents", err);
                    error = err;
                    RespondWithResult(fullBody, err, response);
                    return;
                }

                try {
                    if (response == null) {
                        Log.To.Sync.I(Tag, "Didn't get response for {0}", request);

                        error = new HttpRequestException();
                        RespondWithResult(fullBody, error, response);
                    } else if (!response.IsSuccessStatusCode)  {
                        HttpStatusCode status = response.StatusCode;

                        Log.To.Sync.I(Tag, "Got error status: {0} for {1}.  Reason: {2}", status.GetStatusCode(), request, response.ReasonPhrase);
                        error = new HttpResponseException(status);

                        RespondWithResult(fullBody, error, response);
                    } else {
                        Log.To.Sync.D(Tag, "Processing response: {0}", response);
                        var entity = response.Content;
                        var contentTypeHeader = entity.Headers.ContentType;
                        Stream inputStream = null;
                        if (contentTypeHeader != null && contentTypeHeader.ToString().Contains("multipart/"))
                        {
                            Log.To.Sync.D(Tag, "contentTypeHeader = {0}", contentTypeHeader.ToString());
                            try {
                                _topReader = new MultipartReader(contentTypeHeader.ToString(), this);
                                inputStream = entity.ReadAsStreamAsync().Result;
                                const int bufLen = 1024;
                                var buffer = new byte[bufLen];
                                var numBytesRead = 0;
                                while ((numBytesRead = inputStream.Read(buffer, 0, bufLen)) > 0) {
                                    if (numBytesRead != bufLen) {
                                        var bufferToAppend = new Couchbase.Lite.Util.ArraySegment<byte>(buffer, 0, numBytesRead).ToArray();
                                        _topReader.AppendData(bufferToAppend);
                                    } else {
                                        _topReader.AppendData(buffer);
                                    }
                                }

                                RespondWithResult(fullBody, error, response);
                            } finally {
                                try { 
                                    inputStream.Close();
                                } catch (IOException) { }
                            }
                        } else {
                            Log.To.Sync.D(Tag, "contentTypeHeader is not multipart = {0}", contentTypeHeader.ToString());
                            if (entity != null) {
                                try {
                                    inputStream = entity.ReadAsStreamAsync().Result;
                                    fullBody = Manager.GetObjectMapper().ReadValue<object>(inputStream);
                                    RespondWithResult(fullBody, error, response);
                                } finally {
                                    try {
                                        inputStream.Close();
                                    } catch (IOException) {  }
                                }
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    var err = (e is AggregateException) ? e.InnerException : e;
                    Log.To.Sync.E(Tag, "Exception while processing bulk download response", err);
                    error = err;
                    RespondWithResult(fullBody, err, response);
                }
            });
        }
        protected override internal void ExecuteRequest(HttpClient httpClient, HttpRequestMessage request)
        {
            object fullBody = null;
            Exception error = null;
            HttpResponseMessage response = null;
            try
            {
                if (_tokenSource.IsCancellationRequested)
                {
                    RespondWithResult(fullBody, new Exception(string.Format("{0}: Request {1} has been aborted", this, request)), response);
                    return;
                }
            }
            catch (AggregateException e)
            {
                var err = e.InnerException;
                Log.E(Tag, "Unhandled Exception", err);
                error = err;
                RespondWithResult(fullBody, err, response);
                return;
            }
            catch (IOException e)
            {
                Log.E(Tag, "IO Exception", e);
                error = e;
                RespondWithResult(fullBody, e, response);
                return;
            }
            catch (Exception e)
            {
                Log.E(Tag, "ExecuteRequest Exception: ", e);
                error = e;
                RespondWithResult(fullBody, e, response);
                return;
            }

            try
            {
                Log.D(Tag + ".ExecuteRequest", "Sending request: {0}", request);
                var requestTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_tokenSource.Token);
                var responseTask = httpClient.SendAsync(request, requestTokenSource.Token);
                if (!responseTask.Wait((Int32)ManagerOptions.Default.RequestTimeout.TotalMilliseconds, requestTokenSource.Token))
                {
                    Log.E(Tag, "Response task timed out: {0}, {1}, {2}", responseTask, TaskScheduler.Current, Description());
                    throw new HttpResponseException(HttpStatusCode.RequestTimeout);
                }
                requestTokenSource.Dispose();
                response = responseTask.Result;
            }
            catch (AggregateException e)
            {
                var err = e.InnerException;
                Log.E(Tag, "Unhandled Exception: {0}, {1}", TaskScheduler.Current, Description());
                Log.E(Tag, "Unhandled Exception at Line 129 or 130", err);
                error = err;
                RespondWithResult(fullBody, err, response);
                return;
            }
            catch (IOException e)
            {
                Log.E(Tag, "IO Exception", e);
                error = e;
                RespondWithResult(fullBody, e, response);
                return;
            }
            catch (Exception e)
            {
                Log.E(Tag, "ExecuteRequest Exception: ", e);
                error = e;
                RespondWithResult(fullBody, e, response);
                return;
            }

            try
            {
                if (response == null)
                {
                    Log.E(Tag, "Didn't get response for {0}", request);

                    error = new HttpRequestException();
                    RespondWithResult(fullBody, error, response);
                }
                else if (!response.IsSuccessStatusCode)
                {
                    HttpStatusCode status = response.StatusCode;

                    Log.E(Tag, "Got error status: {0} for {1}.  Reason: {2}", status.GetStatusCode(), request, response.ReasonPhrase);
                    error = new HttpResponseException(status);

                    RespondWithResult(fullBody, error, response);
                }
                else
                {
                    Log.D(Tag, "Processing response: {0}", response);
                    var entity = response.Content;
                    var contentTypeHeader = entity.Headers.ContentType;
                    Stream inputStream = null;
                    if (contentTypeHeader != null && contentTypeHeader.ToString().Contains("multipart/"))
                    {
                        Log.V(Tag, "contentTypeHeader = {0}", contentTypeHeader.ToString());
                        try
                        {
                            _topReader = new MultipartReader(contentTypeHeader.ToString(), this);
                            inputStream = entity.ReadAsStreamAsync().Result;
                            const int bufLen = 1024;
                            var buffer = new byte[bufLen];
                            var numBytesRead = 0;
                            while ((numBytesRead = inputStream.Read(buffer, 0, bufLen)) > 0)
                            {
                                if (numBytesRead != bufLen)
                                {
                                    var bufferToAppend = new Couchbase.Lite.Util.ArraySegment<byte>(buffer, 0, numBytesRead).ToArray();
                                    _topReader.AppendData(bufferToAppend);
                                }
                                else
                                {
                                    _topReader.AppendData(buffer);
                                }
                            }
                            _topReader.Finished();
                            RespondWithResult(fullBody, error, response);
                        }
                        finally
                        {
                            try
                            {
                                inputStream.Close();
                            }
                            catch (IOException)
                            {
                            }
                        }
                    }
                    else
                    {
                        Log.V(Tag, "contentTypeHeader is not multipart = {0}", contentTypeHeader.ToString());
                        if (entity != null)
                        {
                            try
                            {
                                inputStream = entity.ReadAsStreamAsync().Result;
                                fullBody = Manager.GetObjectMapper().ReadValue<object>(inputStream);
                                RespondWithResult(fullBody, error, response);
                            }
                            finally
                            {
                                try
                                {
                                    inputStream.Close();
                                }
                                catch (IOException)
                                {
                                }
                            }
                        }
                    }
                }
            }
            catch (AggregateException e)
            {
                var err = e.InnerException;
                Log.E(Tag, "Unhandled Exception", err);
                error = err;
                RespondWithResult(fullBody, err, response);
            }
            catch (IOException e)
            {
                Log.E(Tag, "IO Exception", e);
                error = e;
                RespondWithResult(fullBody, e, response);
            }
            catch (Exception e)
            {
                Log.E(Tag, "ExecuteRequest Exception: ", e);
                error = e;
                RespondWithResult(fullBody, e, response);
            }
        }
 // expected exception
 public virtual void TestParseHeaders()
 {
     string testString = new string("\r\nFoo: Bar\r\n Header : Val ue ");
     MultipartReader reader = new MultipartReader("multipart/related;boundary=X", null
         );
     reader.ParseHeaders(testString);
     NUnit.Framework.Assert.AreEqual(reader.headers.Keys.Count, 2);
 }
        public void SetHeaders(IDictionary<string, string> headers)
        {
            var contentType = headers.Get("Content-Type");
            if (contentType != null && contentType.StartsWith("multipart/")) {
                // Multipart, so initialize the parser:
                Log.V(TAG, "    Has attachments, {0}", contentType);
                try {
                    multipartReader = new MultipartReader(contentType, this);
                } catch (ArgumentException e) {
                    throw new CouchbaseLiteException(e, StatusCode.NotAcceptable);
                }

                attachmentsByName = new Dictionary<string, BlobStoreWriter>();
                attachmentsBySHA1Digest = new Dictionary<string, BlobStoreWriter>();
                return;
            } else if (contentType == null || contentType.StartsWith("application/json") ||
                contentType.StartsWith("text/plain")) {
                // No multipart, so no attachments. Body is pure JSON. (We allow text/plain because CouchDB
                // sends JSON responses using the wrong content-type.)
                StartJsonBuffer(headers);
                return;
            }

            throw new CouchbaseLiteException(StatusCode.NotAcceptable); 
        }
 public virtual void SetContentType(string contentType)
 {
     if (contentType.StartsWith("multipart/"))
     {
         multipartReader = new MultipartReader(contentType, this);
         attachmentsByName = new Dictionary<string, BlobStoreWriter>();
         attachmentsByMd5Digest = new Dictionary<string, BlobStoreWriter>();
     }
     else
     {
         if (contentType == null || contentType.StartsWith("application/json") || contentType
             .StartsWith("text/plain"))
         {
         }
         else
         {
             // No multipart, so no attachments. Body is pure JSON. (We allow text/plain because CouchDB
             // sends JSON responses using the wrong content-type.)
             throw new ArgumentException("contentType must start with multipart/");
         }
     }
 }
        public void SetContentType(String contentType)
        {

            if (contentType.StartsWith ("multipart/", StringComparison.InvariantCultureIgnoreCase))
            {
                multipartReader = new MultipartReader(contentType, this);
                attachmentsByName = new Dictionary<String, BlobStoreWriter>();
                attachmentsByMd5Digest = new Dictionary<String, BlobStoreWriter>();
            } else if (contentType == null 
                || contentType.StartsWith("application/json", StringComparison.Ordinal)
                || contentType.StartsWith("text/plain", StringComparison.Ordinal)) {
                // No multipart, so no attachments. Body is pure JSON. (We allow text/plain because CouchDB
                // sends JSON responses using the wrong content-type.)
            } else {
                throw new ArgumentException("contentType must start with multipart/");
            }
        }
 private void ReaderOperationWithMime(byte[] mime, string part1ExpectedStr, string
      part2ExpectedStr, int recommendedChunkSize)
 {
     Encoding utf8 = Sharpen.Extensions.GetEncoding("UTF-8");
     // if the caller passes in a special chunksize, which is not equal to mime.length, then
     // lets test the algorithm _only_ at that chunksize.  otherwise, test it at every chunksize
     // between 1 and mime.length.  (this is needed because when testing with a very large mime value,
     // the test takes too long to test at every single chunk size)
     int chunkSize = 1;
     if (recommendedChunkSize != mime.Length)
     {
         chunkSize = recommendedChunkSize;
     }
     for (; chunkSize <= recommendedChunkSize; ++chunkSize)
     {
         ByteArrayInputStream mimeInputStream = new ByteArrayInputStream(mime);
         MultipartReaderTest.TestMultipartReaderDelegate delegate_ = new MultipartReaderTest.TestMultipartReaderDelegate
             (this);
         string contentType = "multipart/related; boundary=\"BOUNDARY\"";
         MultipartReader reader = new MultipartReader(contentType, delegate_);
         NUnit.Framework.Assert.IsFalse(reader.Finished());
         int location = 0;
         int length = 0;
         do
         {
             NUnit.Framework.Assert.IsTrue("Parser didn't stop at end", location < mime.Length
                 );
             length = Math.Min(chunkSize, (mime.Length - location));
             byte[] bytesRead = new byte[length];
             mimeInputStream.Read(bytesRead, 0, length);
             reader.AppendData(bytesRead);
             location += chunkSize;
         }
         while (!reader.Finished());
         NUnit.Framework.Assert.AreEqual(delegate_.partList.Count, 2);
         NUnit.Framework.Assert.AreEqual(delegate_.headersList.Count, 2);
         byte[] part1Expected = Sharpen.Runtime.GetBytesForString(part1ExpectedStr, utf8);
         byte[] part2Expected = Sharpen.Runtime.GetBytesForString(part2ExpectedStr, utf8);
         ByteArrayBuffer part1 = delegate_.partList[0];
         ByteArrayBuffer part2 = delegate_.partList[1];
         NUnit.Framework.Assert.IsTrue(Arrays.Equals(part1.ToByteArray(), part1Expected));
         NUnit.Framework.Assert.IsTrue(Arrays.Equals(part2.ToByteArray(), part2Expected));
         IDictionary<string, string> headers1 = delegate_.headersList[0];
         NUnit.Framework.Assert.IsTrue(headers1.ContainsKey("Foo"));
         NUnit.Framework.Assert.AreEqual(headers1.Get("Foo"), "Bar");
         NUnit.Framework.Assert.IsTrue(headers1.ContainsKey("Header"));
         NUnit.Framework.Assert.AreEqual(headers1.Get("Header"), "Val ue");
     }
 }