Exemplo n.º 1
0
        private static void LogApiCallCounter(HttpRequestMessage request, string opName)
        {
            if (string.IsNullOrWhiteSpace(opName))
            {
                // OpName was not provided, so try to figure it out
                if (request.RequestUri.Query.Contains("comp=list") &&
                    request.Method == HttpMethod.Get)
                {
                    opName = "ListContainers";
                }
            }

            if (opName == null)
            {
                string message = string.Format(
                    "Failed to determine counter op name from Uri {0}",
                    request.RequestUri);

#if DEBUG
                throw new Exception(message);
#else
                Logger.Warning(message);
                return;
#endif
            }

            CounterManager.LogSyncJobCounter(
                Constants.CounterNames.ApiCall,
                1,
                new CounterDimension(Constants.DimensionNames.ApiCallName, opName));
        }
Exemplo n.º 2
0
        private static void LogApiCallCounter(HttpRequestMessage request)
        {
            // A backblaze API request should have the following formats:
            // https://<domain>/b2api/v1/b2_authorize_account
            // https://<domain>/b2api/v1/b2_upload_file/foo/bar
            // We want to isolate the 'b2_' segment and extract the operation name

            // Find the segment containing "b2api/"
            var segments = request.RequestUri.Segments;
            int idx      = segments.IndexOf("b2api/", true);

            if (idx < 0)
            {
                throw new Exception("Failed to locate 'b2api' segment in URI " + request.RequestUri);
            }

            // The op name will be 2 segments after the b2api/ segment. Ensure that it exists
            if (segments.Length <= idx + 2)
            {
                throw new Exception("URI " + request.RequestUri + " is not formatted correctly (idx=" + idx + ")");
            }

            // Isoate the segment
            string opNameSegment = segments[idx + 2];

            // Ensure it starts with b2_ in case there is a strange call
            Pre.Assert(opNameSegment.StartsWith("b2_"));

            CounterManager.LogSyncJobCounter(
                Constants.CounterNames.ApiCall,
                1,
                new CounterDimension(Constants.DimensionNames.ApiCallName, opNameSegment.Trim('/')));
        }
Exemplo n.º 3
0
        public async Task CancelUploadSession(OneDriveUploadSession session)
        {
            // If the session is already cancelled, nothing to do
            if (session.State == OneDriveFileUploadState.Cancelled)
            {
                return;
            }

            CounterManager.LogSyncJobCounter(
                Constants.CounterNames.ApiCall,
                1,
                new CounterDimension(
                    Constants.DimensionNames.OperationName,
                    "CancelUploadSession"));

            HttpRequestMessage  request  = new HttpRequestMessage(HttpMethod.Delete, session.UploadUrl);
            HttpResponseMessage response = await this.SendOneDriveRequest(request).ConfigureAwait(false);

            if (response.StatusCode == HttpStatusCode.NoContent)
            {
                session.State = OneDriveFileUploadState.Cancelled;
                return;
            }

            Logger.Warning(
                "Failed to cancel upload session for file {0} (ParenId={1})",
                session.ItemName,
                session.ParentId);

            LogRequest(request, this.oneDriveHttpClient.BaseAddress, true);
            LogResponse(response, true);

            throw new OneDriveHttpException("Failed to cancel the upload session.", response.StatusCode);
        }
Exemplo n.º 4
0
        // See https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_createuploadsession
        public async Task <OneDriveUploadSession> CreateUploadSession(string parentItemId, string name, long length)
        {
            if (string.IsNullOrWhiteSpace(parentItemId))
            {
                throw new ArgumentNullException(nameof(parentItemId));
            }

            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException(nameof(name));
            }

            // TODO: Check for the maximum file size limit
            if (length <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            if (this.uploadSessions.Any(s => s.ParentId == parentItemId && s.ItemName == name))
            {
                throw new InvalidOperationException("An upload session for this item already exists.");
            }

            HttpRequestMessage request = new HttpRequestMessage(
                HttpMethod.Post,
                string.Format("/v1.0/drive/items/{0}:/{1}:/upload.createSession", parentItemId, HttpUtility.UrlEncode(name)));

            CounterManager.LogSyncJobCounter(
                Constants.CounterNames.ApiCall,
                1,
                new CounterDimension(
                    Constants.DimensionNames.OperationName,
                    "CreateUploadSession"));

            HttpResponseMessage response = await this.SendOneDriveRequest(request).ConfigureAwait(false);

            JObject responseObject = await response.Content.ReadAsJObjectAsync().ConfigureAwait(false);

            var newSession = new OneDriveUploadSession(
                parentItemId,
                name,
                responseObject["uploadUrl"].Value <string>(),
                responseObject["expirationDateTime"].Value <DateTime>(),
                length);

            Logger.Info(
                "Created OneDrive upload session with parentItemId={0}, name={1}, expirationDateTime={2}",
                parentItemId,
                name,
                newSession.ExpirationDateTime);

            this.uploadSessions.Add(newSession);

            return(newSession);
        }
Exemplo n.º 5
0
        public async Task SendUploadFragment(OneDriveUploadSession uploadSession, byte[] fragmentBuffer, long offset)
        {
            switch (uploadSession.State)
            {
            case OneDriveFileUploadState.NotStarted:
                uploadSession.State = OneDriveFileUploadState.InProgress;
                break;

            case OneDriveFileUploadState.Completed:
                throw new OneDriveException("Cannot upload fragment to completed upload session.");

            case OneDriveFileUploadState.Faulted:
                throw new OneDriveException("Cannot upload fragment to faulted upload session.");

            case OneDriveFileUploadState.Cancelled:
                throw new OneDriveException("Cannot upload fragment to cancelled upload session.");
            }

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Put, uploadSession.UploadUrl)
            {
                Content = new ByteArrayContent(fragmentBuffer)
            };

            request.Content.Headers.ContentLength = fragmentBuffer.LongLength;
            request.Content.Headers.ContentRange  = new ContentRangeHeaderValue(
                offset,
                offset + fragmentBuffer.Length - 1,
                uploadSession.Length);

            CounterManager.LogSyncJobCounter(
                Constants.CounterNames.ApiCall,
                1,
                new CounterDimension(
                    Constants.DimensionNames.OperationName,
                    "SendUploadFragment"));

            var response = await this.SendOneDriveRequest(request).ConfigureAwait(false);

            if (!response.IsSuccessStatusCode)
            {
                uploadSession.State = OneDriveFileUploadState.Faulted;
            }

            if (response.StatusCode == HttpStatusCode.Created || response.StatusCode == HttpStatusCode.OK)
            {
                // 201 indicates that the upload is complete.
                uploadSession.State = OneDriveFileUploadState.Completed;
                uploadSession.Item  = await response.Content.ReadAsJsonAsync <Item>().ConfigureAwait(false);

                this.uploadSessions.Remove(uploadSession);
            }
        }
Exemplo n.º 6
0
        public async Task <Uri> GetDownloadUriForItem(string id)
        {
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "/v1.0/drive/items/" + id + "/content");

            CounterManager.LogSyncJobCounter(
                Constants.CounterNames.ApiCall,
                1,
                new CounterDimension(
                    Constants.DimensionNames.OperationName,
                    "GetDownloadUriForItem"));

            var response = await this.SendOneDriveRequest(request, this.oneDriveHttpClientNoRedirect).ConfigureAwait(false);

            return(response.Headers.Location);
        }
Exemplo n.º 7
0
        private async Task <OneDriveResponse <T> > GetOneDriveItemSet <T>(string requestUri)
        {
            CounterManager.LogSyncJobCounter(
                Constants.CounterNames.ApiCall,
                1,
                new CounterDimension(
                    Constants.DimensionNames.OperationName,
                    "GetItemSet"));

            // Send the request to OneDrive and get the response.
            var response = await this.SendOneDriveRequest(new HttpRequestMessage(HttpMethod.Get, requestUri)).ConfigureAwait(false);

            // Request was successful. Read the content returned.
            string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

            return(JsonConvert.DeserializeObject <OneDriveResponse <T> >(content));
        }
Exemplo n.º 8
0
        public async Task <Item> CreateFolderAsync(ItemContainer parent, string name)
        {
            string requestUri;

            // Build the request specific to the parent
            if (parent.IsItem)
            {
                requestUri = string.Format("/v1.0/drive/items/{0}/children", parent.Item.Id);
            }
            else
            {
                requestUri = string.Format("/v1.0/drives/{0}/root/children", parent.Drive.Id);
            }

            Item newFolder = new Item
            {
                Name   = name,
                Folder = new FolderFacet()
            };

            string jsonContent = JsonConvert.SerializeObject(newFolder, new JsonSerializerSettings()
            {
                DefaultValueHandling = DefaultValueHandling.Ignore
            });

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUri);

            request.Content = new StringContent(jsonContent, Encoding.UTF8, "application/json");

            CounterManager.LogSyncJobCounter(
                Constants.CounterNames.ApiCall,
                1,
                new CounterDimension(
                    Constants.DimensionNames.OperationName,
                    "CreateFolder"));

            HttpResponseMessage response = await this.SendOneDriveRequest(request).ConfigureAwait(false);

            return(await response.Content.ReadAsJsonAsync <Item>().ConfigureAwait(false));
        }
Exemplo n.º 9
0
        public async Task <HttpResponseMessage> DownloadFileFragment(Uri downloadUri, int offset, int length)
        {
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, downloadUri);

            request.Headers.Range = new RangeHeaderValue(offset * length, ((offset + 1) * length) - 1);

            CounterManager.LogSyncJobCounter(
                Constants.CounterNames.ApiCall,
                1,
                new CounterDimension(
                    Constants.DimensionNames.OperationName,
                    "DownloadFileFragment"));

            var response = await this.oneDriveHttpClient.SendAsync(request).ConfigureAwait(false);

            if (!response.IsSuccessStatusCode)
            {
                throw new OneDriveHttpException(response.ReasonPhrase, response.StatusCode);
            }

            return(response);
        }
Exemplo n.º 10
0
        public async Task <Item> CreateItem(ItemContainer parent, string name)
        {
            string             uri            = string.Format("/v1.0/drive/items/{0}:/{1}:/[email protected]=fail", parent.Item.Id, HttpUtility.UrlEncode(name));
            HttpContent        requestContent = new StringContent(string.Empty, Encoding.ASCII, "text/plain");
            HttpRequestMessage request        = new HttpRequestMessage(HttpMethod.Put, uri)
            {
                Content = requestContent
            };

            CounterManager.LogSyncJobCounter(
                Constants.CounterNames.ApiCall,
                1,
                new CounterDimension(
                    Constants.DimensionNames.OperationName,
                    "CreateItem"));

            var response = await this.SendOneDriveRequest(request).ConfigureAwait(false);

            // Request was successful. Read the content returned.
            string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

            return(JsonConvert.DeserializeObject <Item>(content));
        }
Exemplo n.º 11
0
        /// <summary>
        /// Transfer data from the source stream to the destination stream. This method also performs the necessary
        /// data transforms (throttling, hashing, encrypting, etc) as the data is streamed. This allows a the
        /// source stream to only be read a single time while performing multiple operations on the data.
        /// </summary>
        /// <param name="sourceStream">The stream that the data is read from</param>
        /// <param name="destinationStream">The stream that the data is written to</param>
        /// <returns>(async) The result of the transfer</returns>
        private async Task <TransferResult> TransferDataWithTransformsAsync(
            Stream sourceStream,
            Stream destinationStream)
        {
            SHA1Cng sha1 = null;
            MD5Cng  md5  = null;

            try
            {
                // By default, we will compute the SHA1 and MD5 hashes of the file as it is streamed.
                sha1 = new SHA1Cng();
                md5  = new MD5Cng();

                // Allocate the buffer that will be used to transfer the data. The source stream will be read one
                // suffer-size as a time, then written to the destination.
                byte[] buffer       = new byte[TransferBufferSize];
                long   readTotal    = 0;
                long   writtenTotal = 0;

                while (true)
                {
                    // If we are using a throttling manager, get the necessary number of tokens to
                    // transfer the data
                    if (this.throttlingManager != null)
                    {
                        int tokens = 0;
                        while (true)
                        {
                            // Get the number of tokens needed. We will require tokens equaling the number of
                            // bytes to be transferred. This is a non-blocking calling and will return between
                            // 0 and the number of requested tokens.
                            tokens += this.throttlingManager.GetTokens(TransferBufferSize - tokens);
                            if (tokens >= TransferBufferSize)
                            {
                                // We have enough tokens to transfer the buffer
                                break;
                            }

                            // We don't (yet) have enough tokens, so wait for a short duration and try again
                            await Task.Delay(10, this.cancellationToken).ConfigureAwait(false);
                        }
                    }

                    // Read data from the source adapter
                    int read = sourceStream.Read(buffer, 0, buffer.Length);

                    CounterManager.LogSyncJobCounter(
                        "SyncJob/BytesRead",
                        read);

                    // Increment the total number of bytes read from the source adapter
                    readTotal += read;
                    int bytesWritten;

                    if (read < buffer.Length)
                    {
                        // Compute the last part of the SHA1 and MD5 hashes (this finished the algorithm's work).
                        sha1.TransformFinalBlock(buffer, 0, read);
                        md5.TransformFinalBlock(buffer, 0, read);

                        if (this.encryptionManager != null)
                        {
                            bytesWritten = this.encryptionManager.TransformFinalBlock(buffer, 0, read);
                        }
                        else
                        {
                            destinationStream.Write(buffer, 0, read);
                            destinationStream.Flush();
                            bytesWritten = buffer.Length;
                        }

                        CounterManager.LogSyncJobCounter(
                            "SyncJob/BytesWritten",
                            read);

                        writtenTotal += bytesWritten;

                        // Increment the total number of bytes written to the desination adapter
                        this.bytesCompleted += read;

                        this.progressChanged(new CopyProgressInfo(this.bytesCompleted, this.updateInfo));

                        // Read the end of the stream
                        break;
                    }

                    // Pass the data through the required hashing algorithms.
                    sha1.TransformBlock(buffer, 0, read, buffer, 0);
                    md5.TransformBlock(buffer, 0, read, buffer, 0);

                    // Write the data to the destination adapter
                    if (this.encryptionManager != null)
                    {
                        bytesWritten = this.encryptionManager.TransformBlock(buffer, 0, read);
                    }
                    else
                    {
                        destinationStream.Write(buffer, 0, read);
                        bytesWritten = buffer.Length;
                    }

                    CounterManager.LogSyncJobCounter(
                        "SyncJob/BytesWritten",
                        read);

                    writtenTotal += bytesWritten;

                    // Increment the total number of bytes written to the desination adapter
                    this.bytesCompleted += read;

                    if (this.syncProgressUpdateStopwatch.ElapsedMilliseconds > 100)
                    {
                        this.progressChanged(new CopyProgressInfo(this.bytesCompleted, this.updateInfo));

                        // After reporting the number of bytes copied for this file, set back to 0 so that we are only
                        // reporting the number of bytes sync we last invoked the callback.
                        this.bytesCompleted = 0;

                        this.syncProgressUpdateStopwatch.Restart();
                    }
                }

                TransferResult result = new TransferResult
                {
                    BytesRead    = readTotal,
                    BytesWritten = writtenTotal
                };

                if (this.encryptionManager != null)
                {
                    if (this.encryptionManager.Mode == EncryptionMode.Encrypt)
                    {
                        result.Sha1Hash            = sha1.Hash;
                        result.Md5Hash             = md5.Hash;
                        result.TransformedSha1Hash = this.encryptionManager.Sha1Hash;
                        result.TransformedMd5Hash  = this.encryptionManager.Md5Hash;
                    }
                    else
                    {
                        result.TransformedSha1Hash = sha1.Hash;
                        result.TransformedMd5Hash  = md5.Hash;
                        result.Sha1Hash            = this.encryptionManager.Sha1Hash; // The SHA1 hash of the data written by the encryption manager
                        result.Md5Hash             = this.encryptionManager.Md5Hash;
                    }
                }
                else
                {
                    result.Sha1Hash = sha1.Hash;
                    result.Md5Hash  = md5.Hash;
                }

                return(result);
            }
            finally
            {
                sha1?.Dispose();
                md5?.Dispose();
            }
        }