public async Task PutAsync_ReturnOK()
        {
            var responseString = "";

            using (var httpResponseMessage = new HttpResponseMessage())
                using (var uploadStream = new MemoryStream(new byte[100]))
                    using (var responseStream = new MemoryStream(Encoding.UTF8.GetBytes(responseString)))
                        using (var streamContent = new StreamContent(responseStream))
                        {
                            httpResponseMessage.Content    = streamContent;
                            httpResponseMessage.StatusCode = HttpStatusCode.OK;
                            const string AuthorizationHeaderName  = "Authorization";
                            const string AuthorizationHeaderValue = "token";
                            var          sessionUrl = "https://api.onedrive.com/v1.0/up/123";
                            var          item       = new Item()
                            {
                                Name = "uploaded"
                            };

                            this.serializer.Setup(s => s.DeserializeObject <Item>(It.Is <string>(str => str.Equals(responseString))))
                            .Returns(item);

                            this.httpProvider.Setup(
                                provider => provider.SendAsync(
                                    It.Is <HttpRequestMessage>(
                                        request => request.RequestUri.ToString().Equals(sessionUrl)),
                                    HttpCompletionOption.ResponseContentRead,
                                    CancellationToken.None)).Returns(Task.FromResult <HttpResponseMessage>(httpResponseMessage));

                            this.authenticationProvider.Setup(
                                provider => provider.AuthenticateRequestAsync(
                                    It.IsAny <HttpRequestMessage>())).Returns(
                                (HttpRequestMessage msg) =>
                            {
                                msg.Headers.Add(AuthorizationHeaderName, AuthorizationHeaderValue);
                                return(Task.FromResult(0));
                            });

                            UploadChunkRequest uploadRequest = new UploadChunkRequest(sessionUrl, this.oneDriveClient, null, 0, 100, 200);

                            var result = await uploadRequest.PutAsync(uploadStream);

                            Assert.IsNotNull(result.ItemResponse, "result item is null");
                            Assert.AreEqual(item, result.ItemResponse, "result returned is not expected");
                            // make sure no auth header is added to the upload request
                            Assert.IsFalse(uploadRequest.Headers.Contains(new HeaderOption(AuthorizationHeaderName, AuthorizationHeaderValue)));
                        }
        }
        public virtual async Task <UploadChunkResult> GetChunkRequestResponseAsync(UploadChunkRequest request, byte[] readBuffer, ICollection <Exception> exceptionTrackingList)
        {
            var firstAttempt = true;

            this.uploadStream.Seek(request.RangeBegin, SeekOrigin.Begin);
            await this.uploadStream.ReadAsync(readBuffer, 0, request.RangeLength).ConfigureAwait(false);

            while (true)
            {
                using (var requestBodyStream = new MemoryStream(request.RangeLength))
                {
                    await requestBodyStream.WriteAsync(readBuffer, 0, request.RangeLength).ConfigureAwait(false);

                    requestBodyStream.Seek(0, SeekOrigin.Begin);

                    try
                    {
                        return(await request.PutAsync(requestBodyStream).ConfigureAwait(false));
                    }
                    catch (ServiceException exception)
                    {
                        if (exception.IsMatch("generalException") || exception.IsMatch("timeout"))
                        {
                            if (firstAttempt)
                            {
                                firstAttempt = false;
                                exceptionTrackingList.Add(exception);
                            }
                            else
                            {
                                throw;
                            }
                        }
                        else if (exception.IsMatch("invalidRange"))
                        {
                            // Succeeded previously, but nothing to return right now
                            return(new UploadChunkResult());
                        }
                        else
                        {
                            throw;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Get the series of requests needed to complete the upload session. Call <see cref="UpdateSessionStatusAsync"/>
        /// first to update the internal session information.
        /// </summary>
        /// <param name="options">Options to be applied to each request.</param>
        /// <returns>All requests currently needed to complete the upload session.</returns>
        public virtual IEnumerable <UploadChunkRequest> GetUploadChunkRequests(IEnumerable <Option> options = null)
        {
            foreach (var range in this.rangesRemaining)
            {
                var currentRangeBegins = range.Item1;

                while (currentRangeBegins <= range.Item2)
                {
                    var nextChunkSize = NextChunkSize(currentRangeBegins, range.Item2);
                    var uploadRequest = new UploadChunkRequest(
                        this.Session.UploadUrl,
                        this.client,
                        options,
                        currentRangeBegins,
                        currentRangeBegins + nextChunkSize - 1,
                        this.totalUploadLength);

                    yield return(uploadRequest);

                    currentRangeBegins += nextChunkSize;
                }
            }
        }
        /// <summary>
        /// Get the series of requests needed to complete the upload session. Call <see cref="UpdateSessionStatusAsync"/>
        /// first to update the internal session information.
        /// </summary>
        /// <param name="options">Options to be applied to each request.</param>
        /// <returns>All requests currently needed to complete the upload session.</returns>
        public virtual IEnumerable<UploadChunkRequest> GetUploadChunkRequests(IEnumerable<Option> options = null)
        {
            foreach (var range in this.rangesRemaining)
            {
                var currentRangeBegins = range.Item1;

                while (currentRangeBegins <= range.Item2)
                {
                    var nextChunkSize = NextChunkSize(currentRangeBegins, range.Item2);
                    var uploadRequest = new UploadChunkRequest(
                        this.Session.UploadUrl,
                        this.client,
                        options,
                        currentRangeBegins,
                        currentRangeBegins + nextChunkSize - 1,
                        this.totalUploadLength);
                    
                    yield return uploadRequest;

                    currentRangeBegins += nextChunkSize;
                }
            }
        }
        public virtual async Task<UploadChunkResult> GetChunkRequestResponseAsync(UploadChunkRequest request, byte[] readBuffer, ICollection<Exception> exceptionTrackingList)
        {
            var firstAttempt = true;
            this.uploadStream.Seek(request.RangeBegin, SeekOrigin.Begin);
            await this.uploadStream.ReadAsync(readBuffer, 0, request.RangeLength).ConfigureAwait(false);

            while (true)
            {
                using (var requestBodyStream = new MemoryStream(request.RangeLength))
                {
                    await requestBodyStream.WriteAsync(readBuffer, 0, request.RangeLength).ConfigureAwait(false);
                    requestBodyStream.Seek(0, SeekOrigin.Begin);

                    try
                    {
                        return await request.PutAsync(requestBodyStream).ConfigureAwait(false);
                    }
                    catch (ServiceException exception)
                    {
                        if (exception.IsMatch("generalException") || exception.IsMatch("timeout"))
                        {
                            if (firstAttempt)
                            {
                                firstAttempt = false;
                                exceptionTrackingList.Add(exception);
                            }
                            else
                            {
                                throw;
                            }
                        }
                        else if (exception.IsMatch("invalidRange"))
                        {
                            // Succeeded previously, but nothing to return right now
                            return new UploadChunkResult();
                        }
                        else
                        {
                            throw;
                        }
                    }
                }
            }
        }