public RemoteMultipartRequest(ScheduledExecutorService workExecutor, HttpClientFactory
			 clientFactory, string method, Uri url, MultipartEntity multiPart, IDictionary<string
			, object> requestHeaders, RemoteRequestCompletionBlock onCompletion) : base(workExecutor
			, clientFactory, method, url, null, requestHeaders, onCompletion)
		{
			this.multiPart = multiPart;
		}
		public RemoteRequest(ScheduledExecutorService workExecutor, HttpClientFactory clientFactory
			, string method, Uri url, object body, IDictionary<string, object> requestHeaders
			, RemoteRequestCompletionBlock onCompletion)
		{
			this.clientFactory = clientFactory;
			this.method = method;
			this.url = url;
			this.body = body;
			this.onCompletion = onCompletion;
			this.workExecutor = workExecutor;
			this.requestHeaders = requestHeaders;
		}
        internal void SendAsyncMultipartRequest(HttpMethod method, String relativePath, MultipartContent multiPartEntity, RemoteRequestCompletionBlock completionHandler)
        {
            Uri url = null;
            try
            {
                var urlStr = BuildRelativeURLString(relativePath);
                url = new Uri(urlStr);
            }
			#if PORTABLE
			catch (FormatException e)
			#else
            catch (UriFormatException e)
			#endif
            {
                throw new ArgumentException("Invalid URI format.", e);
            }

            var message = new HttpRequestMessage(method, url);
            message.Content = multiPartEntity;
            message.Headers.Add("Accept", "*/*");

            PreemptivelySetAuthCredentials(message);

            var client = clientFactory.GetHttpClient();

            client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, CancellationTokenSource.Token)
                .ContinueWith(response=> {
                    if (response.Status != TaskStatus.RanToCompletion)
                    {
                        Log.E(Tag, "SendAsyncRequest did not run to completion.", response.Exception);
                        return null;
                    }
                    return response.Result.Content.ReadAsStreamAsync();
                }, CancellationTokenSource.Token)
                .ContinueWith(response=> {
                    if (response.Status != TaskStatus.RanToCompletion)
                    {
                        Log.E(Tag, "SendAsyncRequest did not run to completion.", response.Exception);
                    } else if (response.Result.Result == null || response.Result.Result.Length == 0)
                    {
                        Log.E(Tag, "Server returned an empty response.", response.Exception);
                    }
                    if (completionHandler != null) {
                        var mapper = Manager.GetObjectMapper();
                        var fullBody = mapper.ReadValue<Object>(response.Result.Result);
                        completionHandler (fullBody, response.Exception);
                    }
                }, CancellationTokenSource.Token);
        }
        internal void SendAsyncMultipartDownloaderRequest(HttpMethod method, string relativePath, object body, Database db, RemoteRequestCompletionBlock onCompletion)
        {
            try
            {
                var urlStr = BuildRelativeURLString(relativePath);
                var url = new Uri(urlStr);

                var message = new HttpRequestMessage(method, url);
                message.Headers.Add("Accept", "*/*");
                AddRequestHeaders(message);

                var httpClient = clientFactory.GetHttpClient();
                httpClient.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, CancellationTokenSource.Token)
                    .ContinueWith(new Action<Task<HttpResponseMessage>>(responseMessage=> {
                        object fullBody = null;
                        Exception error = null;
                        try
                        {
                            var response = responseMessage.Result;
                            // add in cookies to global store
							CouchbaseLiteHttpClientFactory.Instance.AddCookies(clientFactory.HttpHandler.CookieContainer.GetCookies(url), url);
                               
                            var status = response.StatusCode;
                            if ((Int32)status.GetStatusCode() >= 300)
                            {
                                Log.E(Database.Tag, "Got error " + Sharpen.Extensions.ToString(status.GetStatusCode
                                    ()));
                                Log.E(Database.Tag, "Request was for: " + message);
                                Log.E(Database.Tag, "Status reason: " + response.ReasonPhrase);
								error = new HttpResponseException(status); //, response.ReasonPhrase);
                            }
                        else
                        {
                            var entity = response.Content;
                            var contentTypeHeader = response.Content.Headers.ContentType;
                            InputStream inputStream = null;
                            if (contentTypeHeader != null && contentTypeHeader.ToString().Contains("multipart/related"))
                            {
                                try
                                {
                                    var reader = new MultipartDocumentReader(responseMessage.Result, LocalDatabase);
                                    reader.SetContentType(contentTypeHeader.MediaType);

                                    var inputStreamTask = entity.ReadAsStreamAsync();
                                    inputStreamTask.Wait(90000, CancellationTokenSource.Token);
                                    
                                    const int bufLen = 1024;
                                    var buffer = new byte[bufLen];
                                    
                                    int numBytesRead = 0;
                                    while ((numBytesRead = inputStream.Read(buffer)) != -1)
                                    {
                                        if (numBytesRead != bufLen)
                                        {
                                            var bufferToAppend = new ArraySegment<Byte>(buffer, 0, numBytesRead).Array;
                                            reader.AppendData(bufferToAppend);
                                        }
                                        else
                                        {
                                            reader.AppendData(buffer);
                                        }
                                    }

                                    reader.Finish();
                                    fullBody = reader.GetDocumentProperties();

                                    if (onCompletion != null)
                                        onCompletion(fullBody, error);
                                }
                                finally
                                {
                                    try
                                    {
                                        inputStream.Close();
                                    }
                                    catch (IOException)
                                    {
                                            // NOTE: swallow?
                                    }
                                }
                            }
                            else
                            {
                                if (entity != null)
                                {
                                    try
                                    {
                                        var readTask = entity.ReadAsStreamAsync();
                                        readTask.Wait(); // TODO: This should be scaled based on content length.
                                        inputStream = readTask.Result;
                                        fullBody = Manager.GetObjectMapper().ReadValue<Object>(inputStream);
                                        if (onCompletion != null)
                                            onCompletion(fullBody, error);
                                    }
                                    catch (Exception ex)
                                    {
                                        Log.E(Tag, ex.Message);
                                    }
                                    finally
                                    {
                                        try
                                        {
                                            inputStream.Close();
                                        }
                                        catch (IOException)
                                        {
                                        }
                                    }
                                }
                            }
                        }
                    }
                    catch (ProtocolViolationException e)
                    {
                        Log.E(Database.Tag, "client protocol exception", e);
                        error = e;
                    }
                    catch (IOException e)
                    {
                        Log.E(Database.Tag, "io exception", e);
                        error = e;
                    }
                    }));
            }
			#if PORTABLE
			catch (FormatException e)
			#else
            catch (UriFormatException e)
			#endif
            {
                Log.E(Database.Tag, "Malformed URL for async request", e);
            }
        }
        internal void SendAsyncRequest(HttpMethod method, Uri url, Object body, RemoteRequestCompletionBlock completionHandler)
        {
            var message = new HttpRequestMessage(method, url);
            var mapper = Manager.GetObjectMapper();
            if (body != null)
            {
                var bytes = mapper.WriteValueAsBytes(body).ToArray();
                var byteContent = new ByteArrayContent(bytes);
                message.Content = byteContent;
            }
            message.Headers.Add("Accept", new[] { "multipart/related", "application/json" });

            PreemptivelySetAuthCredentials(message);

            var client = clientFactory.GetHttpClient();
            client.CancelPendingRequests();
            client.SendAsync(message, HttpCompletionOption.ResponseHeadersRead, CancellationTokenSource.Token)
                .ContinueWith(response => {
                    if (response.Status != TaskStatus.RanToCompletion)
                    {
                        Log.E(Tag, "SendAsyncRequest did not run to completion.", response.Exception);
                        return null;
                    }
                    return response.Result;
                }, CancellationTokenSource.Token)
				.ContinueWith(response => {
                    if (completionHandler != null) {
                        var fullBody = mapper.ReadValue<Object>(response.Result.Content.ReadAsStreamAsync().Result);

                        Exception error = response.Exception;
                        if (error == null && !response.Result.IsSuccessStatusCode)
                        {
                            error = new HttpResponseException(response.Result.StatusCode); 
                        }

						//JON D - assuming here you meant to send the error instance back to the completonHandler
						// otherwise you wouldn't have taken care to create a HttpResponseException
						completionHandler (fullBody, error);
						//completionHandler (fullBody, response.Exception);
                    }
                });
        }
        internal void SendAsyncRequest(HttpMethod method, string relativePath, object body, RemoteRequestCompletionBlock completionHandler)
        {
            try
            {
                var urlStr = BuildRelativeURLString(relativePath);
                var url = new Uri(urlStr);
                SendAsyncRequest(method, url, body, completionHandler);
            }
			#if PORTABLE
			catch (FormatException e)
			#else
            catch (UriFormatException e)
			#endif
            {
                Log.E(Tag, "Malformed URL for async request", e);
            }
        }
 internal HttpRequestMessage SendAsyncRequest(HttpMethod method, string relativePath, object body, RemoteRequestCompletionBlock completionHandler, bool ignoreCancel = false)
 {
     try {
         var url = _baseUrl.Append(relativePath);
         return SendAsyncRequest(method, url, body, completionHandler, ignoreCancel);
     } catch(UriFormatException e) {
         throw Misc.CreateExceptionAndLog(Log.To.Sync, e, Tag, "Malformed URL for async request");
     } catch(Exception e) {
         throw Misc.CreateExceptionAndLog(Log.To.Sync, e, Tag, "Error sending async request {0}",
             new SecureLogString(relativePath, LogMessageSensitivity.PotentiallyInsecure));
     }
 }
        internal void SendAsyncMultipartRequest(HttpMethod method, String relativePath, MultipartContent multiPartEntity, RemoteRequestCompletionBlock completionHandler)
        {
            Uri url = null;
            try {
                var urlStr = BuildRelativeURLString(relativePath);
                url = new Uri(urlStr);
            } catch (UriFormatException e) {
                throw new ArgumentException("Invalid URI format.", e);
            }

            var message = new HttpRequestMessage(method, url);
            message.Content = multiPartEntity;
            message.Headers.Add("Accept", "*/*");

            var client = clientFactory.GetHttpClient(false);

            var authHeader = AuthUtils.GetAuthenticationHeaderValue(Authenticator, message.RequestUri);
            if (authHeader != null) {
                client.DefaultRequestHeaders.Authorization = authHeader;
            }

            client.SendAsync(message, CancellationTokenSource.Token).ContinueWith(response=> 
            {
                multiPartEntity.Dispose();
                if (response.Status != TaskStatus.RanToCompletion)
                {
                    Log.E(TAG, "SendAsyncRequest did not run to completion.", response.Exception);
                    client.Dispose();
                    return null;
                }
                if ((Int32)response.Result.StatusCode > 300) {
                    LastError = new HttpResponseException(response.Result.StatusCode);
                    Log.E(TAG, "Server returned HTTP Error", LastError);
                    client.Dispose();
                    return null;
                }
                return response.Result.Content.ReadAsStreamAsync();
            }, CancellationTokenSource.Token).ContinueWith(response=> 
            {
                try {
                    var hasEmptyResult = response.Result == null || response.Result.Result == null || response.Result.Result.Length == 0;
                    if (response.Status != TaskStatus.RanToCompletion) {
                        Log.E (TAG, "SendAsyncRequest did not run to completion.", response.Exception);
                    } else if (hasEmptyResult) {
                        Log.E (TAG, "Server returned an empty response.", response.Exception ?? LastError);
                    }

                    if (completionHandler != null) {
                        object fullBody = null;
                        if (!hasEmptyResult) {
                            var mapper = Manager.GetObjectMapper();
                            fullBody = mapper.ReadValue<Object> (response.Result.Result);
                        }

                        completionHandler (fullBody, response.Exception);
                    }
                } finally {
                    client.Dispose();
                }
            }, CancellationTokenSource.Token);
        }
        internal void SendAsyncMultipartDownloaderRequest(HttpMethod method, string relativePath, object body, Database db, RemoteRequestCompletionBlock onCompletion)
        {
            try {
                var urlStr = BuildRelativeURLString(relativePath);
                var url = new Uri(urlStr);

                var message = new HttpRequestMessage(method, url);
                message.Headers.Add("Accept", "*/*");
                AddRequestHeaders(message);

                var client = clientFactory.GetHttpClient(false);
                var challengeResponseAuth = Authenticator as IChallengeResponseAuthenticator;
                if (challengeResponseAuth != null) {
                    var authHandler = clientFactory.Handler as DefaultAuthHandler;
                    if (authHandler != null) {
                        authHandler.Authenticator = challengeResponseAuth;
                    }

                    challengeResponseAuth.PrepareWithRequest(message);
                }

                var authHeader = AuthUtils.GetAuthenticationHeaderValue(Authenticator, message.RequestUri);
                if (authHeader != null) {
                    client.DefaultRequestHeaders.Authorization = authHeader;
                }

                client.SendAsync(message, CancellationTokenSource.Token).ContinueWith(new Action<Task<HttpResponseMessage>>(responseMessage =>
                {
                    object fullBody = null;
                    Exception error = null;
                    try {
                        var response = responseMessage.Result;
                        // add in cookies to global store
                        //CouchbaseLiteHttpClientFactory.Instance.AddCookies(clientFactory.HttpHandler.CookieContainer.GetCookies(url));

                        var status = response.StatusCode;
                        if ((Int32)status.GetStatusCode() >= 300) {
                            Log.E(TAG, "Got error " + Sharpen.Extensions.ToString(status.GetStatusCode()));
                            Log.E(TAG, "Request was for: " + message);
                            Log.E(TAG, "Status reason: " + response.ReasonPhrase);
                            error = new WebException(response.ReasonPhrase);
                        } else {
                            var entity = response.Content;
                            var contentTypeHeader = response.Content.Headers.ContentType;
                            InputStream inputStream = null;
                            if (contentTypeHeader != null && contentTypeHeader.ToString().Contains("multipart/related")) {
                                try {
                                    var reader = new MultipartDocumentReader(LocalDatabase);
                                    var contentType = contentTypeHeader.ToString();
                                    reader.SetContentType(contentType);

                                    var inputStreamTask = entity.ReadAsStreamAsync();
                                    //inputStreamTask.Wait(90000, CancellationTokenSource.Token);
                                    inputStream = inputStreamTask.Result;

                                    const int bufLen = 1024;
                                    var buffer = new byte[bufLen];

                                    int numBytesRead = 0;
                                    while ((numBytesRead = inputStream.Read(buffer)) != -1) {
                                        if (numBytesRead != bufLen) {
                                            var bufferToAppend = new Couchbase.Lite.Util.ArraySegment<Byte>(buffer, 0, numBytesRead);
                                            reader.AppendData(bufferToAppend);
                                        } else {
                                            reader.AppendData(buffer);
                                        }
                                    }

                                    reader.Finish();
                                    fullBody = reader.GetDocumentProperties();

                                    if (onCompletion != null) {
                                        onCompletion(fullBody, error);
                                    }
                                } catch (Exception ex) {
                                    Log.E(TAG, "SendAsyncMultipartDownloaderRequest has an error occurred.", ex);
                                } finally {
                                    try {
                                        inputStream.Close();
                                    } catch (Exception) { }
                                }
                            } else {
                                if (entity != null) {
                                    try {
                                        var readTask = entity.ReadAsStreamAsync();
                                        //readTask.Wait(); // TODO: This should be scaled based on content length.
                                        inputStream = readTask.Result;
                                        fullBody = Manager.GetObjectMapper().ReadValue<Object>(inputStream);
                                        if (onCompletion != null)
                                            onCompletion(fullBody, error);
                                    } catch (Exception ex) {
                                        Log.E(TAG, "SendAsyncMultipartDownloaderRequest has an error occurred.", ex);
                                    } finally {
                                        try {
                                            inputStream.Close();
                                        } catch (Exception) { }
                                    }
                                }
                            }
                        }
                    } catch (System.Net.ProtocolViolationException e) {
                        Log.E(TAG, "client protocol exception", e);
                        error = e;
                    } catch (IOException e) {
                        Log.E(TAG, "IO Exception", e);
                        error = e;
                    } finally {
                        client.Dispose();
                    }
                }), WorkExecutor.Scheduler);
            } catch (UriFormatException e) {
                Log.E(TAG, "Malformed URL for async request", e);
            }
        }
        internal HttpRequestMessage SendAsyncRequest(HttpMethod method, Uri url, object body, RemoteRequestCompletionBlock completionHandler, bool ignoreCancel)
        {
            var message = new HttpRequestMessage(method, url);
            var mapper = Manager.GetObjectMapper();
            message.Headers.Add("Accept", new[] { "multipart/related", "application/json" });

            var bytes = default(byte[]);
            if(body != null) {
                bytes = mapper.WriteValueAsBytes(body).ToArray();
                var byteContent = new ByteArrayContent(bytes);
                message.Content = byteContent;
                message.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            }

            var token = ignoreCancel ? CancellationToken.None : _remoteRequestCancellationSource.Token;
            Log.To.Sync.V(Tag, "{0} - Sending {1} request to: {2}", _id, method, new SecureLogUri(url));
            var client = default(CouchbaseLiteHttpClient);
            if(!_client.AcquireFor(TimeSpan.FromSeconds(1), out client)) {
                Log.To.Sync.I(Tag, "Client is disposed, aborting request to {0}", new SecureLogUri(url));
                return null;
            }

            client.Authenticator = Authenticator;
            var t = client.SendAsync(message, token).ContinueWith(response =>
            {
                try {
                    HttpResponseMessage result = null;
                    Exception error = null;
                    if(!response.IsFaulted && !response.IsCanceled) {
                        result = response.Result;
                        UpdateServerType(result);
                    } else if(response.IsFaulted) {
                        Log.To.Sync.W(Tag, String.Format("Http Message failed to send, or got error response, " +
                            "passing to callback... {0}, ",
                            new SecureLogUri(message.RequestUri)), response.Exception);
                        if(bytes != null) {
                            try {
                                Log.To.Sync.W(Tag, "\tFailed content: {0}", new SecureLogString(bytes, LogMessageSensitivity.PotentiallyInsecure));
                            } catch(ObjectDisposedException) { }
                        }
                    }

                    if(completionHandler != null) {
                        object fullBody = null;

                        try {
                            if(response.Status != TaskStatus.RanToCompletion) {
                                Log.To.Sync.V(Tag, "SendAsyncRequest did not run to completion.");
                            }

                            if(response.IsCanceled) {
                                error = new WebException("SendAsyncRequest was cancelled", System.Net.WebExceptionStatus.RequestCanceled);
                            } else {
                                error = Misc.Flatten(response.Exception).FirstOrDefault();
                            }

                            if(error == null) {
                                if(!result.IsSuccessStatusCode) {
                                    result = response.Result;
                                    error = new HttpResponseException(result.StatusCode);
                                }
                            }

                            if(error == null) {
                                var content = result.Content;
                                if(content != null) {
                                    fullBody = mapper.ReadValue<object>(content.ReadAsStreamAsync().Result);
                                }

                                error = null;
                            }
                        } catch(Exception e) {
                            error = e;
                            Log.To.Sync.W(Tag, "SendAsyncRequest got an exception while processing response, " +
                                "passing it on to the callback.", e);
                        }

                        completionHandler(fullBody, error);
                    }

                    if(result != null) {
                        result.Dispose();
                    }
                } finally {
                    Task dummy;
                    _requests.TryRemove(message, out dummy);
                    message.Dispose();
                }
            }, token);

            _requests.AddOrUpdate(message, k => t, (k, v) => t);
            return message;
        }
 internal void SendAsyncRequest(HttpMethod method, string relativePath, object body, RemoteRequestCompletionBlock completionHandler, CancellationTokenSource requestTokenSource = null)
 {
     try {
         var urlStr = BuildRelativeURLString(relativePath);
         var url = new Uri(urlStr);
         SendAsyncRequest(method, url, body, completionHandler, requestTokenSource);
     } catch (UriFormatException e) {
         Log.E(TAG, "Malformed URL for async request", e);
         throw;
     } catch (Exception e) {
         Log.E(TAG, "Unhandled exception", e);
         throw;
     }
 }
        internal void SendAsyncRequest(HttpMethod method, Uri url, Object body, RemoteRequestCompletionBlock completionHandler, CancellationTokenSource requestTokenSource = null)
        {
            var message = new HttpRequestMessage(method, url);
            var mapper = Manager.GetObjectMapper();
            message.Headers.Add("Accept", new[] { "multipart/related", "application/json" });

            var client = clientFactory.GetHttpClient(false);
            var challengeResponseAuth = Authenticator as IChallengeResponseAuthenticator;
            if (challengeResponseAuth != null) {
                var authHandler = clientFactory.Handler as DefaultAuthHandler;
                if (authHandler != null) {
                    authHandler.Authenticator = challengeResponseAuth;
                }

                challengeResponseAuth.PrepareWithRequest(message);
            }

            var authHeader = AuthUtils.GetAuthenticationHeaderValue(Authenticator, message.RequestUri);
            if (authHeader != null) {
                client.DefaultRequestHeaders.Authorization = authHeader;
            }

            if (body != null) {
                var bytes = mapper.WriteValueAsBytes(body).ToArray();
                var byteContent = new ByteArrayContent(bytes);
                message.Content = byteContent;
                message.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
            }

            var token = requestTokenSource != null 
                ? requestTokenSource.Token
                : CancellationTokenSource.Token;

            Log.D(TAG, "Sending async {0} request to: {1}", method, url);
            var t = client.SendAsync(message, token) .ContinueWith(response =>
            {
                try {
                    lock(_requests) {
                        _requests.Remove(message);
                    }

                    HttpResponseMessage result = null;
                    Exception error = null;
                    if (!response.IsFaulted && !response.IsCanceled) {
                        result = response.Result;
                        UpdateServerType(result);
                    } else if(response.IsFaulted) {
                        error = response.Exception.InnerException;
                        Log.E(TAG, "Http Message failed to send: {0}", message);
                        Log.E(TAG, "Http exception", response.Exception.InnerException);
                        if (message.Content != null) {
                            Log.E(TAG, "\tFailed content: {0}", message.Content.ReadAsStringAsync().Result);
                        }
                    }

                    if (completionHandler != null) {
                        object fullBody = null;

                        try {
                            if (response.Status != TaskStatus.RanToCompletion) {
                                Log.D(TAG, "SendAsyncRequest did not run to completion.", response.Exception);
                            }

                            if(response.IsCanceled) {
                                error = new Exception("SendAsyncRequest Task has been canceled.");
                            } else  {
                                error = error is AggregateException
                                    ? response.Exception.Flatten()
                                    : response.Exception;
                            }

                            if (error == null) {
                                if (!result.IsSuccessStatusCode) {
                                    result = response.Result;
                                    error = new HttpResponseException(result.StatusCode);
                                }
                            }

                            if (error == null) {
                                var content = result.Content;
                                if (content != null) {
                                    fullBody = mapper.ReadValue<object>(content.ReadAsStreamAsync().Result);
                                }

                                error = null;
                            }
                        } catch (Exception e) {
                            error = e;
                            Log.E(TAG, "SendAsyncRequest has an error occurred.", e);
                        }

                        completionHandler(fullBody, error);
                    }

                    return result;
                } finally {
                    client.Dispose();
                }
            }, token, TaskContinuationOptions.None, WorkExecutor.Scheduler);

            lock(_requests) {
                _requests[message] = t;
            }
        }
 public RemoteRequest(ScheduledExecutorService workExecutor, HttpClientFactory clientFactory
     , string method, Uri url, object body, Database db, IDictionary<string, object> 
     requestHeaders, RemoteRequestCompletionBlock onCompletion)
 {
     this.clientFactory = clientFactory;
     this.method = method;
     this.url = url;
     this.body = body;
     this.onCompletion = onCompletion;
     this.workExecutor = workExecutor;
     this.requestHeaders = requestHeaders;
     this.db = db;
     this.request = CreateConcreteRequest();
     Log.V(Log.TagSync, "%s: RemoteRequest created, url: %s", this, url);
 }
 public virtual void SetOnPreCompletion(RemoteRequestCompletionBlock onPreCompletion
     )
 {
     this.onPreCompletion = onPreCompletion;
 }
        internal void SendAsyncMultipartRequest(HttpMethod method, string relativePath, MultipartContent multiPartEntity, RemoteRequestCompletionBlock completionHandler)
        {
            Uri url = null;
            try {
                url = _baseUrl.Append(relativePath);
            } catch(UriFormatException) {
                Log.To.Sync.E(Tag, "Invalid path received for request: {0}, throwing...",
                    new SecureLogString(relativePath, LogMessageSensitivity.PotentiallyInsecure));
                throw new ArgumentException("Invalid path", "relativePath");
            }

            var message = new HttpRequestMessage(method, url);
            message.Content = multiPartEntity;
            message.Headers.Add("Accept", "*/*");

            var client = default(CouchbaseLiteHttpClient);
            if(!_client.AcquireFor(TimeSpan.FromSeconds(1), out client)) {
                Log.To.Sync.I(Tag, "Client is disposed, aborting request to {0}", new SecureLogString(relativePath, LogMessageSensitivity.PotentiallyInsecure));
                return;
            }

            var _lastError = default(Exception);
            client.Authenticator = Authenticator;
            var t = client.SendAsync(message, _cancellationTokenSource.Token).ContinueWith(response =>
            {
                multiPartEntity.Dispose();
                if(response.Status != TaskStatus.RanToCompletion) {
                    _lastError = response.Exception;
                    Log.To.Sync.W(Tag, "SendAsyncRequest did not run to completion, returning null...");
                    return Task.FromResult((Stream)null);
                }
                if((int)response.Result.StatusCode > 300) {
                    _lastError = new HttpResponseException(response.Result.StatusCode);
                    Log.To.Sync.W(Tag, "Server returned HTTP Error, returning null...");
                    return Task.FromResult((Stream)null);
                }
                return response.Result.Content.ReadAsStreamAsync();
            }, _cancellationTokenSource.Token).ContinueWith(response =>
            {
                try {
                    var hasEmptyResult = response.Result == null || response.Result.Result == null || response.Result.Result.Length == 0;
                    if(response.Status != TaskStatus.RanToCompletion) {
                        Log.To.Sync.W(Tag, "SendAsyncRequest phase two did not run to completion, continuing...");
                    } else if(hasEmptyResult) {
                        Log.To.Sync.W(Tag, "Server returned an empty response, continuing...");
                    }

                    if(completionHandler != null) {
                        object fullBody = null;
                        if(!hasEmptyResult) {
                            var mapper = Manager.GetObjectMapper();
                            fullBody = mapper.ReadValue<Object>(response.Result.Result);
                        }

                        completionHandler(fullBody, response.Exception ?? _lastError);
                    }
                } finally {
                    Task dummy;
                    _requests.TryRemove(message, out dummy);
                }
            }, _cancellationTokenSource.Token);
            _requests.TryAdd(message, t);
        }
        internal void SendAsyncMultipartDownloaderRequest(HttpMethod method, string relativePath, object body, Database db, RemoteRequestCompletionBlock onCompletion)
        {
            try {
                var url = _baseUrl.Append(relativePath);

                var message = new HttpRequestMessage(method, url);
                message.Headers.Add("Accept", "*/*");
                AddRequestHeaders(message);

                var client = default(CouchbaseLiteHttpClient);
                if(!_client.AcquireFor(TimeSpan.FromSeconds(1), out client)) {
                    Log.To.Sync.I(Tag, "Client is disposed, aborting request to {0}", new SecureLogString(relativePath, LogMessageSensitivity.PotentiallyInsecure));
                    return;
                }

                client.Authenticator = Authenticator;
                var request = client.SendAsync(message, _cancellationTokenSource.Token).ContinueWith(new Action<Task<HttpResponseMessage>>(responseMessage =>
                {
                    object fullBody = null;
                    Exception error = null;
                    try {
                        if(responseMessage.IsFaulted) {
                            error = responseMessage.Exception.InnerException;
                            if(onCompletion != null) {
                                onCompletion(null, error);
                            }

                            return;
                        }

                        var response = responseMessage.Result;
                        // add in cookies to global store
                        //CouchbaseLiteHttpClientFactory.Instance.AddCoIokies(clientFactory.HttpHandler.CookieContainer.GetCookies(url));

                        var status = response.StatusCode;
                        if((Int32)status.GetStatusCode() >= 300) {
                            Log.To.Sync.W(Tag, "Got error {0}", status.GetStatusCode());
                            Log.To.Sync.W(Tag, "Request was for: " + message);
                            Log.To.Sync.W(Tag, "Status reason: " + response.ReasonPhrase);
                            Log.To.Sync.W(Tag, "Passing error onto callback...");
                            error = new HttpResponseException(status);
                            if(onCompletion != null) {
                                onCompletion(null, error);
                            }
                        } else {
                            var entity = response.Content;
                            var contentTypeHeader = response.Content.Headers.ContentType;
                            Stream inputStream = null;
                            if(contentTypeHeader != null && contentTypeHeader.ToString().Contains("multipart/related")) {
                                try {
                                    var reader = new MultipartDocumentReader(db);
                                    var contentType = contentTypeHeader.ToString();
                                    reader.SetContentType(contentType);

                                    var inputStreamTask = entity.ReadAsStreamAsync();
                                    //inputStreamTask.Wait(90000, CancellationTokenSource.Token);
                                    inputStream = inputStreamTask.Result;

                                    const int bufLen = 1024;
                                    var buffer = new byte[bufLen];

                                    int numBytesRead = 0;
                                    while((numBytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0) {
                                        if(numBytesRead != bufLen) {
                                            var bufferToAppend = new Couchbase.Lite.Util.ArraySegment<Byte>(buffer, 0, numBytesRead);
                                            reader.AppendData(bufferToAppend);
                                        } else {
                                            reader.AppendData(buffer);
                                        }
                                    }

                                    reader.Finish();
                                    fullBody = reader.GetDocumentProperties();

                                    if(onCompletion != null) {
                                        onCompletion(fullBody, error);
                                    }
                                } catch(Exception ex) {
                                    Log.To.Sync.W(Tag, "SendAsyncMultipartDownloaderRequest got an exception, aborting...", ex);
                                } finally {
                                    try {
                                        inputStream.Close();
                                    } catch(Exception) { }
                                }
                            } else {
                                if(entity != null) {
                                    try {
                                        var readTask = entity.ReadAsStreamAsync();
                                        //readTask.Wait(); // TODO: This should be scaled based on content length.
                                        inputStream = readTask.Result;
                                        fullBody = Manager.GetObjectMapper().ReadValue<Object>(inputStream);
                                        if(onCompletion != null)
                                            onCompletion(fullBody, error);
                                    } catch(Exception ex) {
                                        Log.To.Sync.W(Tag, "SendAsyncMultipartDownloaderRequest got an exception, aborting...", ex);
                                    } finally {
                                        try {
                                            inputStream.Close();
                                        } catch(Exception) { }
                                    }
                                }
                            }
                        }
                    } catch(Exception e) {
                        Log.To.Sync.W(Tag, "Got exception during SendAsyncMultipartDownload, aborting...");
                        error = e;
                    } finally {
                        Task dummy;
                        _requests.TryRemove(message, out dummy);
                        responseMessage.Result.Dispose();
                    }
                }), _workExecutor.Scheduler);
                _requests.TryAdd(message, request);
            } catch(UriFormatException e) {
                Log.To.Sync.W(Tag, "Malformed URL for async request, aborting...", e);
            }
        }
 public RemoteMultipartDownloaderRequest(ScheduledExecutorService workExecutor, HttpClientFactory
      clientFactory, string method, Uri url, object body, Database db, IDictionary<string
     , object> requestHeaders, RemoteRequestCompletionBlock onCompletion) : base(workExecutor
     , clientFactory, method, url, body, db, requestHeaders, onCompletion)
 {
     this.db = db;
 }