files.com 1.1.812 → 1.1.814
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/_VERSION +1 -1
- data/doc/Files/APIConnectionError.html +1 -1
- data/doc/Files/APIError.html +1 -1
- data/doc/Files/AccountAlreadyExistsError.html +1 -1
- data/doc/Files/AccountLineItem.html +1 -1
- data/doc/Files/AccountOverdueError.html +1 -1
- data/doc/Files/Action.html +1 -1
- data/doc/Files/ActionLog.html +1 -1
- data/doc/Files/ActionNotificationExport.html +1 -1
- data/doc/Files/ActionNotificationExportResult.html +1 -1
- data/doc/Files/AdditionalAuthenticationRequiredError.html +1 -1
- data/doc/Files/AgentNode.html +1 -1
- data/doc/Files/AgentNodeConnection.html +1 -1
- data/doc/Files/AgentNodeInstance.html +1 -1
- data/doc/Files/AgentPushUpdate.html +1 -1
- data/doc/Files/AgentPushUpdateBlockedError.html +1 -1
- data/doc/Files/AgentUnavailableError.html +1 -1
- data/doc/Files/AgentUpgradeRequiredError.html +1 -1
- data/doc/Files/AiAssistantPersonality.html +1 -1
- data/doc/Files/AiTask.html +1 -1
- data/doc/Files/AiTaskCannotBeRunManuallyError.html +1 -1
- data/doc/Files/AlreadyCompletedError.html +1 -1
- data/doc/Files/Api.html +20 -13
- data/doc/Files/ApiClient/RequestLogContext.html +29 -29
- data/doc/Files/ApiClient/TransferSettings.html +721 -0
- data/doc/Files/ApiClient.html +756 -204
- data/doc/Files/ApiKey.html +1 -1
- data/doc/Files/ApiKeyIsDisabledError.html +1 -1
- data/doc/Files/ApiKeyIsPathRestrictedError.html +1 -1
- data/doc/Files/ApiKeyNotFoundError.html +1 -1
- data/doc/Files/ApiKeyOnlyForDesktopAppError.html +1 -1
- data/doc/Files/ApiKeyOnlyForFileOperationsError.html +1 -1
- data/doc/Files/ApiKeyOnlyForMobileAppError.html +1 -1
- data/doc/Files/ApiKeyOnlyForOfficeIntegrationError.html +1 -1
- data/doc/Files/ApiKeySessionsNotSupportedError.html +1 -1
- data/doc/Files/ApiRequestLog.html +1 -1
- data/doc/Files/App.html +1 -1
- data/doc/Files/As2IncomingMessage.html +1 -1
- data/doc/Files/As2OutgoingMessage.html +1 -1
- data/doc/Files/As2Partner.html +1 -1
- data/doc/Files/As2Station.html +1 -1
- data/doc/Files/AttachmentTooLargeError.html +1 -1
- data/doc/Files/AuthenticationError.html +1 -1
- data/doc/Files/AuthenticationRequiredError.html +1 -1
- data/doc/Files/Auto.html +1 -1
- data/doc/Files/Automation.html +1 -1
- data/doc/Files/AutomationAuthoringSchema.html +1 -1
- data/doc/Files/AutomationCannotBeRunManuallyError.html +1 -1
- data/doc/Files/AutomationExecutionNode.html +1 -1
- data/doc/Files/AutomationLog.html +1 -1
- data/doc/Files/AutomationRun.html +1 -1
- data/doc/Files/AutomationsUnavailableError.html +1 -1
- data/doc/Files/BadRequestError.html +1 -1
- data/doc/Files/BandwidthSnapshot.html +1 -1
- data/doc/Files/Behavior.html +1 -1
- data/doc/Files/BehaviorNotAllowedOnRemoteServerError.html +1 -1
- data/doc/Files/BillingInformationHiddenError.html +1 -1
- data/doc/Files/BillingPermissionRequiredError.html +1 -1
- data/doc/Files/BufferedUploadDisabledForThisDestinationError.html +1 -1
- data/doc/Files/Bundle.html +1 -1
- data/doc/Files/BundleAction.html +1 -1
- data/doc/Files/BundleDownload.html +1 -1
- data/doc/Files/BundleMaximumUsesReachedError.html +1 -1
- data/doc/Files/BundleNotification.html +1 -1
- data/doc/Files/BundleOnlyAllowsPreviewsError.html +1 -1
- data/doc/Files/BundleOperationRequiresSubfolderError.html +1 -1
- data/doc/Files/BundlePath.html +1 -1
- data/doc/Files/BundlePathNotFoundError.html +1 -1
- data/doc/Files/BundlePermissionRequiredError.html +1 -1
- data/doc/Files/BundleRecipient.html +1 -1
- data/doc/Files/BundleRegistration.html +1 -1
- data/doc/Files/BundleRegistrationCodeFailedError.html +1 -1
- data/doc/Files/BundleRegistrationNotFoundError.html +1 -1
- data/doc/Files/CannotAdministerHigherLevelUserError.html +1 -1
- data/doc/Files/CannotDownloadDirectoryError.html +1 -1
- data/doc/Files/CannotLoginWhileUsingKeyError.html +1 -1
- data/doc/Files/CantActForOtherUserError.html +1 -1
- data/doc/Files/CantMoveWithMultipleLocationsError.html +1 -1
- data/doc/Files/ChatMessage.html +1 -1
- data/doc/Files/ChatSession.html +1 -1
- data/doc/Files/ChildSiteManagementPolicy.html +1 -1
- data/doc/Files/Clickwrap.html +1 -1
- data/doc/Files/CodeNotFoundError.html +1 -1
- data/doc/Files/ConfigurationLockedPathError.html +1 -1
- data/doc/Files/ContactAdminForPasswordChangeHelpError.html +1 -1
- data/doc/Files/CouldNotCreateParentError.html +1 -1
- data/doc/Files/CustomDomain.html +1 -1
- data/doc/Files/DatetimeParseError.html +1 -1
- data/doc/Files/DesktopConfigurationProfile.html +1 -1
- data/doc/Files/DestinationExistsError.html +1 -1
- data/doc/Files/DestinationFolderLimitedError.html +1 -1
- data/doc/Files/DestinationParentConflictError.html +1 -1
- data/doc/Files/DestinationParentDoesNotExistError.html +1 -1
- data/doc/Files/DestinationSameError.html +1 -1
- data/doc/Files/DestinationSiteMismatchError.html +1 -1
- data/doc/Files/DirectConnectionInfo.html +1 -1
- data/doc/Files/DnsRecord.html +1 -1
- data/doc/Files/DoesNotSupportSortingError.html +1 -1
- data/doc/Files/DuplicateShareRecipientError.html +1 -1
- data/doc/Files/EmailIncomingMessage.html +1 -1
- data/doc/Files/EmailLog.html +1 -1
- data/doc/Files/Error.html +2 -2
- data/doc/Files/Errors.html +1 -1
- data/doc/Files/EventChannel.html +1 -1
- data/doc/Files/EventDeliveryAttempt.html +1 -1
- data/doc/Files/EventRecord.html +1 -1
- data/doc/Files/EventSubscription.html +1 -1
- data/doc/Files/EventTarget.html +1 -1
- data/doc/Files/ExavaultApiRequestLog.html +1 -1
- data/doc/Files/ExceededRuntimeLimitError.html +1 -1
- data/doc/Files/Expectation.html +1 -1
- data/doc/Files/ExpectationAlreadyHasOpenWindowError.html +1 -1
- data/doc/Files/ExpectationEvaluation.html +1 -1
- data/doc/Files/ExpectationIncident.html +1 -1
- data/doc/Files/ExpectationNotManualTriggerError.html +1 -1
- data/doc/Files/ExpiredPrivateKeyError.html +1 -1
- data/doc/Files/ExpiredPublicKeyError.html +1 -1
- data/doc/Files/ExportFailureError.html +1 -1
- data/doc/Files/ExportNotReadyError.html +1 -1
- data/doc/Files/ExternalEvent.html +1 -1
- data/doc/Files/FailedToChangePasswordError.html +1 -1
- data/doc/Files/File.html +2019 -1515
- data/doc/Files/FileAction.html +1 -1
- data/doc/Files/FileActionError.html +1 -1
- data/doc/Files/FileComment.html +1 -1
- data/doc/Files/FileCommentReaction.html +1 -1
- data/doc/Files/FileLockedError.html +1 -1
- data/doc/Files/FileMigration.html +1 -1
- data/doc/Files/FileMigrationLog.html +1 -1
- data/doc/Files/FileNotFoundError.html +1 -1
- data/doc/Files/FileNotUploadedError.html +1 -1
- data/doc/Files/FilePendingProcessingError.html +1 -1
- data/doc/Files/FileProcessingErrorError.html +1 -1
- data/doc/Files/FileTooBigToDecryptError.html +1 -1
- data/doc/Files/FileTooBigToEncryptError.html +1 -1
- data/doc/Files/FileUploadNotFoundError.html +1 -1
- data/doc/Files/FileUploadPart.html +1 -1
- data/doc/Files/FileUploadedToWrongRegionError.html +1 -1
- data/doc/Files/FileUtils.html +1 -1
- data/doc/Files/FilenameTooLongError.html +1 -1
- data/doc/Files/FilesAgentFailedAuthorizationError.html +1 -1
- data/doc/Files/FilesNativeStorageOverloadedError.html +1 -1
- data/doc/Files/Folder.html +1 -1
- data/doc/Files/FolderAdminOrBillingPermissionRequiredError.html +1 -1
- data/doc/Files/FolderAdminPermissionRequiredError.html +1 -1
- data/doc/Files/FolderLockedError.html +1 -1
- data/doc/Files/FolderMustNotBeAFileError.html +1 -1
- data/doc/Files/FolderNotEmptyError.html +1 -1
- data/doc/Files/FoldersNotAllowedError.html +1 -1
- data/doc/Files/FormField.html +1 -1
- data/doc/Files/FormFieldSet.html +1 -1
- data/doc/Files/FtpActionLog.html +1 -1
- data/doc/Files/FullPermissionRequiredError.html +1 -1
- data/doc/Files/GpgKey.html +1 -1
- data/doc/Files/Group.html +1 -1
- data/doc/Files/GroupNotFoundError.html +1 -1
- data/doc/Files/GroupUser.html +1 -1
- data/doc/Files/History.html +1 -1
- data/doc/Files/HistoryExport.html +1 -1
- data/doc/Files/HistoryExportResult.html +1 -1
- data/doc/Files/HistoryPermissionRequiredError.html +1 -1
- data/doc/Files/HistoryUnavailableError.html +1 -1
- data/doc/Files/HolidayCalendar.html +1 -1
- data/doc/Files/HolidayRegion.html +1 -1
- data/doc/Files/Image.html +1 -1
- data/doc/Files/InAppAiAssistantUnavailableError.html +1 -1
- data/doc/Files/InboundS3Log.html +1 -1
- data/doc/Files/InboxNotFoundError.html +1 -1
- data/doc/Files/InboxRecipient.html +1 -1
- data/doc/Files/InboxRegistration.html +1 -1
- data/doc/Files/InboxRegistrationCodeFailedError.html +1 -1
- data/doc/Files/InboxUpload.html +1 -1
- data/doc/Files/InsufficientPermissionForParamsError.html +1 -1
- data/doc/Files/InsufficientPermissionForSiteError.html +1 -1
- data/doc/Files/IntegrationCentricProfile.html +1 -1
- data/doc/Files/InternalGeneralErrorError.html +1 -1
- data/doc/Files/InvalidBodyError.html +1 -1
- data/doc/Files/InvalidBundleCodeError.html +1 -1
- data/doc/Files/InvalidCredentialsError.html +1 -1
- data/doc/Files/InvalidCursorError.html +1 -1
- data/doc/Files/InvalidCursorTypeForSortError.html +1 -1
- data/doc/Files/InvalidEtagsError.html +1 -1
- data/doc/Files/InvalidFileTypeError.html +1 -1
- data/doc/Files/InvalidFilenameError.html +1 -1
- data/doc/Files/InvalidFilterAliasCombinationError.html +1 -1
- data/doc/Files/InvalidFilterFieldError.html +1 -1
- data/doc/Files/InvalidFilterParamError.html +1 -1
- data/doc/Files/InvalidFilterParamFormatError.html +1 -1
- data/doc/Files/InvalidFilterParamValueError.html +1 -1
- data/doc/Files/InvalidInputEncodingError.html +1 -1
- data/doc/Files/InvalidInterfaceError.html +1 -1
- data/doc/Files/InvalidOauthError.html +1 -1
- data/doc/Files/InvalidOauthProviderError.html +1 -1
- data/doc/Files/InvalidOrExpiredCodeError.html +1 -1
- data/doc/Files/InvalidParameterError.html +5 -1
- data/doc/Files/InvalidPathError.html +1 -1
- data/doc/Files/InvalidPriorityColorError.html +1 -1
- data/doc/Files/InvalidRangeError.html +1 -1
- data/doc/Files/InvalidReturnToUrlError.html +1 -1
- data/doc/Files/InvalidSearchQueryError.html +1 -1
- data/doc/Files/InvalidSessionError.html +1 -1
- data/doc/Files/InvalidSiteError.html +1 -1
- data/doc/Files/InvalidSortFieldError.html +1 -1
- data/doc/Files/InvalidSortFilterCombinationError.html +1 -1
- data/doc/Files/InvalidUploadOffsetError.html +1 -1
- data/doc/Files/InvalidUploadPartGapError.html +1 -1
- data/doc/Files/InvalidUploadPartSizeError.html +1 -1
- data/doc/Files/InvalidUsernameOrPasswordError.html +1 -1
- data/doc/Files/InvalidWorkspaceIdHeaderError.html +1 -1
- data/doc/Files/InvalidZipFileError.html +1 -1
- data/doc/Files/Invoice.html +1 -1
- data/doc/Files/InvoiceLineItem.html +1 -1
- data/doc/Files/IpAddress.html +1 -1
- data/doc/Files/KeyLifecycleRule.html +1 -1
- data/doc/Files/List.html +1 -1
- data/doc/Files/Lock.html +1 -1
- data/doc/Files/LockOperationBusyError.html +1 -1
- data/doc/Files/LockedOutError.html +1 -1
- data/doc/Files/LockoutRegionMismatchError.html +1 -1
- data/doc/Files/MetadataCategory.html +1 -1
- data/doc/Files/MetadataNotSupportedOnRemotesError.html +1 -1
- data/doc/Files/MethodNotAllowedError.html +1 -1
- data/doc/Files/MigrationInProgressError.html +1 -1
- data/doc/Files/MissingParameterError.html +1 -1
- data/doc/Files/ModelSaveErrorError.html +1 -1
- data/doc/Files/MoverAccessDeniedError.html +1 -1
- data/doc/Files/MoverPackageRequiredError.html +1 -1
- data/doc/Files/MultipleProcessingErrorsError.html +1 -1
- data/doc/Files/MultipleSortParamsNotAllowedError.html +1 -1
- data/doc/Files/MustAuthenticateWithApiKeyError.html +1 -1
- data/doc/Files/NeedAdminPermissionForInboxError.html +1 -1
- data/doc/Files/NestedNotFoundError.html +1 -1
- data/doc/Files/NoAccountForSiteError.html +1 -1
- data/doc/Files/NoValidInputParamsError.html +1 -1
- data/doc/Files/NonAdminsMustQueryByFolderOrPathError.html +1 -1
- data/doc/Files/NotAllowedToCreateBundleError.html +1 -1
- data/doc/Files/NotAuthenticatedError.html +1 -1
- data/doc/Files/NotAuthorizedError.html +1 -1
- data/doc/Files/NotEnqueuableSyncError.html +1 -1
- data/doc/Files/NotFoundError.html +1 -1
- data/doc/Files/NotImplementedError.html +1 -1
- data/doc/Files/Notification.html +1 -1
- data/doc/Files/OffsetUploadNotAllowedWithMalwareScanningError.html +1 -1
- data/doc/Files/OneTimePasswordIncorrectError.html +1 -1
- data/doc/Files/OutboundConnectionLog.html +1 -1
- data/doc/Files/PartNumberTooLargeError.html +1 -1
- data/doc/Files/Partner.html +1 -1
- data/doc/Files/PartnerChannel.html +1 -1
- data/doc/Files/PartnerChannelTemplate.html +1 -1
- data/doc/Files/PartnerConnection.html +1 -1
- data/doc/Files/PartnerSite.html +1 -1
- data/doc/Files/PartnerSiteRequest.html +1 -1
- data/doc/Files/PasswordChangeNotRequiredError.html +1 -1
- data/doc/Files/PasswordChangeRequiredError.html +1 -1
- data/doc/Files/PathCannotHaveTrailingWhitespaceError.html +1 -1
- data/doc/Files/PathTooLongError.html +1 -1
- data/doc/Files/PathUtil.html +1 -1
- data/doc/Files/Payment.html +1 -1
- data/doc/Files/PaymentLineItem.html +1 -1
- data/doc/Files/PaymentMethodErrorError.html +1 -1
- data/doc/Files/PendingWorkEvent.html +1 -1
- data/doc/Files/Permission.html +1 -1
- data/doc/Files/PlanNotFoundError.html +1 -1
- data/doc/Files/Preview.html +1 -1
- data/doc/Files/PreviewOnlyPermissionCannotDownloadError.html +1 -1
- data/doc/Files/ProcessingFailureError.html +1 -1
- data/doc/Files/PublicHostingRequestLog.html +1 -1
- data/doc/Files/PublicIpAddress.html +1 -1
- data/doc/Files/PublicKey.html +1 -1
- data/doc/Files/RateLimitedError.html +1 -1
- data/doc/Files/ReadOnlySessionError.html +1 -1
- data/doc/Files/ReadPermissionRequiredError.html +1 -1
- data/doc/Files/ReauthenticationFailedError.html +1 -1
- data/doc/Files/ReauthenticationFailedFinalError.html +1 -1
- data/doc/Files/ReauthenticationNeededActionError.html +1 -1
- data/doc/Files/ReauthenticationNeededFieldsError.html +1 -1
- data/doc/Files/ReauthenticationRateLimitedError.html +1 -1
- data/doc/Files/RecaptchaFailedError.html +1 -1
- data/doc/Files/RecipientAlreadySharedError.html +1 -1
- data/doc/Files/RemoteBandwidthSnapshot.html +1 -1
- data/doc/Files/RemoteDesktopDebugLoggingDisabledError.html +1 -1
- data/doc/Files/RemoteEntryReadOnlyError.html +1 -1
- data/doc/Files/RemoteMountBackend.html +1 -1
- data/doc/Files/RemoteServer.html +1 -1
- data/doc/Files/RemoteServerConfigurationFile.html +1 -1
- data/doc/Files/RemoteServerCredential.html +1 -1
- data/doc/Files/RemoteServerErrorError.html +1 -1
- data/doc/Files/Request.html +1 -1
- data/doc/Files/RequestBodyTooLargeError.html +1 -1
- data/doc/Files/RequestParamsContainInvalidCharacterError.html +1 -1
- data/doc/Files/RequestParamsInvalidError.html +1 -1
- data/doc/Files/RequestParamsRequiredError.html +1 -1
- data/doc/Files/ResourceBelongsToParentSiteError.html +1 -1
- data/doc/Files/ResourceLockedError.html +1 -1
- data/doc/Files/Response.html +1 -1
- data/doc/Files/Restore.html +1 -1
- data/doc/Files/RootFolderBehaviorSiteAdminRequiredError.html +1 -1
- data/doc/Files/RootFolderBehaviorSkipSiteAdminRequiredError.html +1 -1
- data/doc/Files/Schedule.html +1 -1
- data/doc/Files/ScheduledExport.html +1 -1
- data/doc/Files/ScimLog.html +1 -1
- data/doc/Files/SearchAllOnChildPathError.html +1 -1
- data/doc/Files/SearchUnavailableError.html +1 -1
- data/doc/Files/Secret.html +1 -1
- data/doc/Files/SelfManagedRequiredError.html +1 -1
- data/doc/Files/ServiceUnavailableError.html +1 -1
- data/doc/Files/Session.html +1 -1
- data/doc/Files/SettingsChange.html +1 -1
- data/doc/Files/SftpActionLog.html +1 -1
- data/doc/Files/SftpHostKey.html +1 -1
- data/doc/Files/ShareGroup.html +1 -1
- data/doc/Files/ShareGroupMember.html +1 -1
- data/doc/Files/SiemHttpDestination.html +1 -1
- data/doc/Files/SiemHttpDestinationEvent.html +1 -1
- data/doc/Files/Site.html +1 -1
- data/doc/Files/SiteAdminOrPartnerAdminPermissionRequiredError.html +1 -1
- data/doc/Files/SiteAdminOrWorkspaceAdminOrFolderAdminPermissionRequiredError.html +1 -1
- data/doc/Files/SiteAdminOrWorkspaceAdminOrPartnerAdminOrFolderAdminPermissionRequiredError.html +1 -1
- data/doc/Files/SiteAdminOrWorkspaceAdminOrPartnerAdminPermissionRequiredError.html +1 -1
- data/doc/Files/SiteAdminOrWorkspaceAdminPermissionRequiredError.html +1 -1
- data/doc/Files/SiteAdminRequiredError.html +1 -1
- data/doc/Files/SiteConfigurationError.html +1 -1
- data/doc/Files/SiteDisabledError.html +1 -1
- data/doc/Files/SiteFilesAreImmutableError.html +1 -1
- data/doc/Files/SiteNotFoundError.html +1 -1
- data/doc/Files/SiteSubdomainRedirect.html +1 -1
- data/doc/Files/SiteWasRemovedError.html +1 -1
- data/doc/Files/SizableIO.html +43 -41
- data/doc/Files/Snapshot.html +1 -1
- data/doc/Files/SsoEvent.html +1 -1
- data/doc/Files/SsoStrategy.html +1 -1
- data/doc/Files/Status.html +1 -1
- data/doc/Files/Style.html +1 -1
- data/doc/Files/SubfolderLockedError.html +1 -1
- data/doc/Files/Sync.html +1 -1
- data/doc/Files/SyncInProgressError.html +1 -1
- data/doc/Files/SyncLog.html +1 -1
- data/doc/Files/SyncRun.html +1 -1
- data/doc/Files/SyncRunLiveTransfer.html +1 -1
- data/doc/Files/SystemProfiler.html +1 -1
- data/doc/Files/TooManyConcurrentLoginsError.html +1 -1
- data/doc/Files/TooManyConcurrentRequestsError.html +1 -1
- data/doc/Files/TooManyLoginAttemptsError.html +1 -1
- data/doc/Files/TooManyRequestsError.html +1 -1
- data/doc/Files/TooManySharesError.html +1 -1
- data/doc/Files/Transfer/BufferLimitError.html +175 -0
- data/doc/Files/Transfer/ByteBudget/Reservation.html +301 -0
- data/doc/Files/Transfer/ByteBudget.html +748 -0
- data/doc/Files/Transfer/CancelledError.html +235 -0
- data/doc/Files/Transfer/Checkpoint.html +1371 -0
- data/doc/Files/Transfer/CheckpointError.html +173 -0
- data/doc/Files/Transfer/ClientTransport/ClientResponse.html +661 -0
- data/doc/Files/Transfer/ClientTransport/Reader.html +530 -0
- data/doc/Files/Transfer/ClientTransport.html +675 -0
- data/doc/Files/Transfer/Clock.html +189 -0
- data/doc/Files/Transfer/Control.html +667 -0
- data/doc/Files/Transfer/Controller/Attempt.html +465 -0
- data/doc/Files/Transfer/Controller.html +1166 -0
- data/doc/Files/Transfer/Coordinator/Timing.html +568 -0
- data/doc/Files/Transfer/Coordinator.html +534 -0
- data/doc/Files/Transfer/DownloadCoordinator/FileSink.html +976 -0
- data/doc/Files/Transfer/DownloadCoordinator/IoSink.html +1168 -0
- data/doc/Files/Transfer/DownloadCoordinator/Piece.html +875 -0
- data/doc/Files/Transfer/DownloadCoordinator.html +728 -0
- data/doc/Files/Transfer/FileSlots.html +480 -0
- data/doc/Files/Transfer/FrontendResponseError.html +235 -0
- data/doc/Files/Transfer/Holds.html +505 -0
- data/doc/Files/Transfer/HttpTransport.html +579 -0
- data/doc/Files/Transfer/Job.html +2338 -0
- data/doc/Files/Transfer/Options.html +924 -0
- data/doc/Files/Transfer/PausedError.html +319 -0
- data/doc/Files/Transfer/Policy.html +1642 -0
- data/doc/Files/Transfer/RequestPermits.html +160 -0
- data/doc/Files/Transfer/SourceChangedError.html +235 -0
- data/doc/Files/Transfer/StorageResponse.html +546 -0
- data/doc/Files/Transfer/StorageRetry.html +1098 -0
- data/doc/Files/Transfer/UploadCoordinator/Grant.html +1048 -0
- data/doc/Files/Transfer/UploadCoordinator/Part.html +1048 -0
- data/doc/Files/Transfer/UploadCoordinator/Region.html +295 -0
- data/doc/Files/Transfer/UploadCoordinator/Segments.html +299 -0
- data/doc/Files/Transfer/UploadCoordinator/Source.html +725 -0
- data/doc/Files/Transfer/UploadCoordinator/StreamSource.html +664 -0
- data/doc/Files/Transfer/UploadCoordinator.html +546 -0
- data/doc/Files/Transfer/UploadPlan.html +522 -0
- data/doc/Files/Transfer/VersionChangedError.html +235 -0
- data/doc/Files/Transfer.html +134 -0
- data/doc/Files/TrialExpiredError.html +1 -1
- data/doc/Files/TrialLockedError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationCodeAlreadySentError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationCountryBlacklistedError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationErrorError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationGeneralErrorError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationMethodUnsupportedErrorError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationRequiredError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationSetupExpiredError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationUnsubscribedRecipientError.html +1 -1
- data/doc/Files/URI.html +1 -1
- data/doc/Files/UnrecognizedSortIndexError.html +1 -1
- data/doc/Files/UnsupportedCurrencyError.html +1 -1
- data/doc/Files/UnsupportedHttpResponseFormatError.html +1 -1
- data/doc/Files/UnsupportedMediaTypeError.html +1 -1
- data/doc/Files/UpdatesNotAllowedForRemotesError.html +1 -1
- data/doc/Files/UploadsUnavailableError.html +1 -1
- data/doc/Files/UsageByTopLevelDir.html +1 -1
- data/doc/Files/UsageDailySnapshot.html +1 -1
- data/doc/Files/UsageSnapshot.html +1 -1
- data/doc/Files/User.html +1 -1
- data/doc/Files/UserAdditionalEmailRecipient.html +1 -1
- data/doc/Files/UserCipherUse.html +1 -1
- data/doc/Files/UserIdInvalidError.html +1 -1
- data/doc/Files/UserIdOnUserEndpointError.html +1 -1
- data/doc/Files/UserIdWithoutSiteAdminError.html +1 -1
- data/doc/Files/UserLifecycleRule.html +1 -1
- data/doc/Files/UserNotFoundError.html +1 -1
- data/doc/Files/UserRequest.html +1 -1
- data/doc/Files/UserRequestsEnabledRequiredError.html +1 -1
- data/doc/Files/UserRequiredError.html +1 -1
- data/doc/Files/UserSecurityEvent.html +1 -1
- data/doc/Files/UserSftpClientUse.html +1 -1
- data/doc/Files/Util/LogSettings.html +1 -1
- data/doc/Files/Util.html +1 -1
- data/doc/Files/WebDavActionLog.html +1 -1
- data/doc/Files/WebhookTest.html +1 -1
- data/doc/Files/Workspace.html +1 -1
- data/doc/Files/WriteAndBundlePermissionRequiredError.html +1 -1
- data/doc/Files/WritePermissionRequiredError.html +1 -1
- data/doc/Files/ZipListEntry.html +1 -1
- data/doc/Files.html +116 -116
- data/doc/_index.html +314 -4
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +1 -1
- data/doc/index.html +1 -1
- data/doc/method_list.html +4703 -2415
- data/doc/top-level-namespace.html +1 -1
- data/docs/file.md +346 -0
- data/lib/files.com/api.rb +6 -1
- data/lib/files.com/api_client.rb +220 -32
- data/lib/files.com/models/file.rb +124 -17
- data/lib/files.com/sizable_io.rb +1 -0
- data/lib/files.com/transfer/budgets.rb +123 -0
- data/lib/files.com/transfer/checkpoint.rb +113 -0
- data/lib/files.com/transfer/clock.rb +14 -0
- data/lib/files.com/transfer/controller.rb +549 -0
- data/lib/files.com/transfer/download_coordinator.rb +906 -0
- data/lib/files.com/transfer/errors.rb +100 -0
- data/lib/files.com/transfer/http_transport.rb +337 -0
- data/lib/files.com/transfer/job.rb +778 -0
- data/lib/files.com/transfer/options.rb +126 -0
- data/lib/files.com/transfer/policy.rb +91 -0
- data/lib/files.com/transfer/storage_retry.rb +169 -0
- data/lib/files.com/transfer/upload_coordinator.rb +657 -0
- data/lib/files.com/transfer/upload_plan.rb +157 -0
- data/lib/files.com.rb +13 -0
- data/spec/lib/api_client_spec.rb +125 -3
- data/spec/models/file_transfer_spec.rb +70 -4
- data/spec/support/transfer_server.rb +113 -14
- data/spec/transfer/adaptive_download_spec.rb +1102 -0
- data/spec/transfer/adaptive_upload_spec.rb +1602 -0
- data/spec/transfer/concurrency_controller_spec.rb +382 -0
- data/spec/workspace_id_spec.rb +19 -14
- metadata +59 -1
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Files
|
|
4
|
+
# Adaptive transfers: uploads and downloads that send their parts concurrently and tune how many are in flight to
|
|
5
|
+
# what the link sustains, within the limits the caller sets. They are opt-in, through the transfer: option of
|
|
6
|
+
# Files::File.upload_file, Files::File.upload_io (with a known size), #upload_file, #download_file and
|
|
7
|
+
# #download_content (see docs/file.md); without it every transfer works as it always has.
|
|
8
|
+
module Transfer
|
|
9
|
+
# Raised by a transfer whose Control was cancelled, once the requests it had in flight have settled. Nothing is
|
|
10
|
+
# finalized after it.
|
|
11
|
+
class CancelledError < Error
|
|
12
|
+
def initialize(message = "The transfer was cancelled")
|
|
13
|
+
super
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Raised by a transfer whose Control was paused, once the requests it had in flight have settled. checkpoint
|
|
18
|
+
# resumes it in this process with Files::File.resume_upload or Files::File.resume_download; it is nil when what was
|
|
19
|
+
# transferred cannot be continued: a transfer paused before it started, an upload from a caller's stream, which
|
|
20
|
+
# cannot be read again, or a download into a caller's IO that can neither start over nor report where its bytes
|
|
21
|
+
# began. A resume paused before its turn, or before it finished checking the local file or destination, gives back
|
|
22
|
+
# the checkpoint it was given, still usable.
|
|
23
|
+
class PausedError < Error
|
|
24
|
+
attr_reader :checkpoint
|
|
25
|
+
|
|
26
|
+
def initialize(checkpoint = nil, message = "The transfer was paused")
|
|
27
|
+
super(message)
|
|
28
|
+
@checkpoint = checkpoint
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# A part needs more memory than the transfer's buffer_bytes allows. It is raised before anything is read for that
|
|
33
|
+
# part, so raise buffer_bytes or transfer without transfer:. An adaptive download through a caller's client raises
|
|
34
|
+
# it, before it sends a content request or writes anything, when the client's adapter is not one it relies on to
|
|
35
|
+
# stream a response
|
|
36
|
+
# through Faraday's on_data (Faraday's net_http or net_http_persistent), and refuses with it a 2xx response such an
|
|
37
|
+
# adapter still read whole.
|
|
38
|
+
class BufferLimitError < InvalidParameterError; end
|
|
39
|
+
|
|
40
|
+
# The remote file changed while it was downloading: storage refused a range for the version the download began
|
|
41
|
+
# with, or answered with another version.
|
|
42
|
+
class VersionChangedError < Error
|
|
43
|
+
def initialize(message = "The remote file changed during the download")
|
|
44
|
+
super
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# A response that came from the Files.com web app rather than file storage, such as a sign-in page. Nothing from
|
|
49
|
+
# it is written.
|
|
50
|
+
class FrontendResponseError < Error
|
|
51
|
+
def initialize(message = "The transfer was answered by the Files.com web app instead of file storage")
|
|
52
|
+
super
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# The local file being uploaded changed or ended early while its parts were being sent, or a caller's stream ended
|
|
57
|
+
# before the size it was given. The upload is not finalized.
|
|
58
|
+
class SourceChangedError < Error
|
|
59
|
+
def initialize(message = "The local file changed during the upload")
|
|
60
|
+
super
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# A checkpoint that cannot resume: it came from another process, another resume is using it, a resume already
|
|
65
|
+
# started from it, or the local file, the destination (including the bytes already written to it, which must be
|
|
66
|
+
# read back to be checked) or the remote file is no longer the one it was taken from. Nothing is sent, written or
|
|
67
|
+
# truncated before it is raised.
|
|
68
|
+
class CheckpointError < InvalidParameterError; end
|
|
69
|
+
|
|
70
|
+
# A request that failed before a whole response arrived. retryable? is false for a TLS failure, which repeating
|
|
71
|
+
# cannot fix. Coordinators raise original (downloads) or an APIConnectionError (uploads) in its place, as the
|
|
72
|
+
# transfers without transfer: do.
|
|
73
|
+
class NetworkFailure < StandardError
|
|
74
|
+
attr_reader :original
|
|
75
|
+
|
|
76
|
+
def initialize(original, retryable: true)
|
|
77
|
+
super("Transfer request failed (#{original.class})")
|
|
78
|
+
@original = original
|
|
79
|
+
@retryable = retryable
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def retryable?
|
|
83
|
+
@retryable
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Carries an error from the caller's source or destination through the transport unchanged, so it is never taken
|
|
88
|
+
# for a network failure and retried.
|
|
89
|
+
class Passthrough < StandardError
|
|
90
|
+
attr_reader :error
|
|
91
|
+
|
|
92
|
+
def initialize(error)
|
|
93
|
+
super("Transfer source or destination failed (#{error.class})")
|
|
94
|
+
@error = error
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
private_constant :NetworkFailure, :Passthrough
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Files
|
|
4
|
+
module Transfer
|
|
5
|
+
# A storage response: its status, its headers as lowercase names each with its values, and its body: what the
|
|
6
|
+
# success handler returned, or at most the first 64 KiB of any other response, with truncated true when that
|
|
7
|
+
# response had more.
|
|
8
|
+
StorageResponse = Struct.new(:status, :headers, :body, :truncated, keyword_init: true) do
|
|
9
|
+
def header(name)
|
|
10
|
+
headers.fetch(name, []).first
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Sends a job's content requests with Net::HTTP over connections each worker thread owns, reused for its next
|
|
15
|
+
# request to the same origin. It never follows a redirect or retries by itself, verifies TLS certificates and host
|
|
16
|
+
# names, and uses Files.proxy, or the proxy environment variables without it, with Files.open_timeout and
|
|
17
|
+
# Files.read_timeout (for each read and each write) as they were when the job began. It carries no Files.com
|
|
18
|
+
# credentials: a signed URL is its own authority.
|
|
19
|
+
class HttpTransport
|
|
20
|
+
ERROR_BODY_BYTES = 64 * 1024
|
|
21
|
+
KEEP_ALIVE_SECONDS = 30
|
|
22
|
+
RETRYABLE_ERRORS = [ IOError, SystemCallError, SocketError, Timeout::Error, Net::ProtocolError, Net::HTTPBadResponse ].freeze
|
|
23
|
+
|
|
24
|
+
# Whether client sends requests just as this transport does: the SDK's own ApiClient with no request handling or
|
|
25
|
+
# logging of its own (ApiClient#default_transfer_behavior?), over a connection ApiClient.build_default_conn made
|
|
26
|
+
# with this transport's own proxy, its user and password included, or, as this transport, with none, and with
|
|
27
|
+
# nothing about it changed since (ApiClient.unchanged_default_conn?). Any other client, such as one made after
|
|
28
|
+
# Files.proxy changed, keeps its connection and its settings, such as a minimum TLS version, an adapter
|
|
29
|
+
# configuration block or its proxy, its remote_request and its logging for the transfer, one request at a time,
|
|
30
|
+
# through ClientTransport.
|
|
31
|
+
def compatible?(client)
|
|
32
|
+
return false unless client.respond_to?(:default_transfer_behavior?) && client.default_transfer_behavior?
|
|
33
|
+
|
|
34
|
+
ApiClient.unchanged_default_conn?(client.conn, @proxy_identity)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def initialize
|
|
38
|
+
proxy = Files.proxy
|
|
39
|
+
@proxy_identity = ApiClient.proxy_identity(proxy)
|
|
40
|
+
@proxy = @proxy_identity && Faraday::ProxyOptions.from(proxy)
|
|
41
|
+
@open_timeout = Files.open_timeout
|
|
42
|
+
@read_timeout = Files.read_timeout
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def serialized?
|
|
46
|
+
false
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Sends one request on the calling thread over its connections, a Hash that thread owns, and returns its
|
|
50
|
+
# StorageResponse. body is a String, or an object whose read(length, buffer) gives length bytes in all, streamed
|
|
51
|
+
# without holding them. With a block, a 2xx response is passed to it with its body unread, and what it returns is
|
|
52
|
+
# the response's body; the block reads it as it arrives. An error from the block is raised as it is, unless it is a
|
|
53
|
+
# network error, which is raised, as one before the response, as a NetworkFailure. timing, when given, is told as
|
|
54
|
+
# the send starts (connection setup included), as the response headers arrive, and once the block, or the read of
|
|
55
|
+
# any other response's body, has finished.
|
|
56
|
+
def perform(method, url, headers, connections, body: nil, length: nil, timing: nil, &on_success)
|
|
57
|
+
uri = ::URI.parse(url)
|
|
58
|
+
request = Net::HTTPGenericRequest.new(method.to_s.upcase, !body.nil?, true, uri.request_uri, headers)
|
|
59
|
+
if body.is_a?(String)
|
|
60
|
+
request.body = body
|
|
61
|
+
elsif body
|
|
62
|
+
request.body_stream = body
|
|
63
|
+
request.content_length = length
|
|
64
|
+
end
|
|
65
|
+
result = nil
|
|
66
|
+
timing&.sending
|
|
67
|
+
connection(uri, connections).request(request) do |response|
|
|
68
|
+
timing&.headers
|
|
69
|
+
kept, truncated = on_success && response.is_a?(Net::HTTPSuccess) ? [ on_success.call(response), false ] : limited_body(response)
|
|
70
|
+
timing&.body
|
|
71
|
+
result = StorageResponse.new(status: response.code.to_i, headers: response.to_hash, body: kept, truncated: truncated)
|
|
72
|
+
end
|
|
73
|
+
result
|
|
74
|
+
rescue OpenSSL::SSL::SSLError => e
|
|
75
|
+
drop(connections, uri)
|
|
76
|
+
raise NetworkFailure.new(e, retryable: false), cause: nil
|
|
77
|
+
rescue *RETRYABLE_ERRORS => e
|
|
78
|
+
drop(connections, uri)
|
|
79
|
+
raise NetworkFailure.new(e), cause: nil
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def close_all(connections)
|
|
83
|
+
connections.each_value { |http| finish(http) }
|
|
84
|
+
connections.clear
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private def connection(uri, connections)
|
|
88
|
+
key = [ uri.scheme, uri.hostname, uri.port ]
|
|
89
|
+
http = connections[key]
|
|
90
|
+
return http if http&.started?
|
|
91
|
+
|
|
92
|
+
http = build_http(uri)
|
|
93
|
+
http.start
|
|
94
|
+
connections[key] = http
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private def build_http(uri)
|
|
98
|
+
http = if @proxy
|
|
99
|
+
Net::HTTP.new(uri.hostname, uri.port, @proxy.uri.hostname, @proxy.uri.port, @proxy.user, @proxy.password)
|
|
100
|
+
else
|
|
101
|
+
Net::HTTP.new(uri.hostname, uri.port)
|
|
102
|
+
end
|
|
103
|
+
http.use_ssl = uri.scheme == "https"
|
|
104
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER if http.use_ssl?
|
|
105
|
+
http.open_timeout = @open_timeout
|
|
106
|
+
http.read_timeout = @read_timeout
|
|
107
|
+
http.write_timeout = @read_timeout
|
|
108
|
+
http.max_retries = 0
|
|
109
|
+
http.keep_alive_timeout = KEEP_ALIVE_SECONDS
|
|
110
|
+
http
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# The first ERROR_BODY_BYTES of a response's body, which is read to its end, and whether it had more.
|
|
114
|
+
private def limited_body(response)
|
|
115
|
+
kept = String.new(encoding: Encoding::BINARY)
|
|
116
|
+
truncated = false
|
|
117
|
+
response.read_body do |chunk|
|
|
118
|
+
room = ERROR_BODY_BYTES - kept.bytesize
|
|
119
|
+
truncated ||= chunk.bytesize > room
|
|
120
|
+
kept << chunk.byteslice(0, room) if room.positive?
|
|
121
|
+
end
|
|
122
|
+
[ kept, truncated ]
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private def drop(connections, uri)
|
|
126
|
+
finish(connections.delete([ uri.scheme, uri.hostname, uri.port ])) if uri
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private def finish(http)
|
|
130
|
+
http.finish if http&.started?
|
|
131
|
+
rescue IOError, SystemCallError
|
|
132
|
+
nil
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Sends a transfer's content requests through the caller's own client, which HttpTransport cannot stand in for:
|
|
137
|
+
# one with its own adapter, middleware, request handling (an overridden remote_request), logger, or connection
|
|
138
|
+
# settings, such as a minimum TLS version or an adapter configuration block. Each request goes through the client's
|
|
139
|
+
# remote_request exactly once (ApiClient.single_attempt), so the client's hooks, logging, settings and errors still
|
|
140
|
+
# apply while every retry stays the transfer's own, counted and admitted like any other. It sends one request at a
|
|
141
|
+
# time, since such a client may not be safe to share between threads, and whole-String bodies, since its adapter
|
|
142
|
+
# may not stream them.
|
|
143
|
+
#
|
|
144
|
+
# A response a success handler reads is streamed to it as it arrives, through Faraday's on_data, so nothing of it is
|
|
145
|
+
# held here beyond what the handler keeps; any other response keeps its first 64 KiB, and says whether it had more.
|
|
146
|
+
# The handler runs in a Fiber of its own, with the client ApiClient#request made active for the thread that sent the
|
|
147
|
+
# request, so what it calls, such as a caller's progress callback, makes its SDK calls with it. A download relies
|
|
148
|
+
# on that streaming, so
|
|
149
|
+
# it goes through a client only when the client's adapter is one that streams (streams?), and refuses any other
|
|
150
|
+
# before it requests or writes anything; an upload's responses are small, and any adapter carries them. A 2xx
|
|
151
|
+
# response such an adapter still read whole is refused, never handed on. A body shorter than its headers said is a
|
|
152
|
+
# network failure, as through HttpTransport. The job's diagnostics say when a transfer uses it.
|
|
153
|
+
class ClientTransport
|
|
154
|
+
# The adapters a download relies on to stream: as their pinned versions, faraday-net_http 3.0 and
|
|
155
|
+
# faraday-net_http_persistent 2.1, do, each saves a response's status and headers in env, then hands on each chunk
|
|
156
|
+
# of its body to on_data as Net::HTTP reads it, holding none of it, with any options or configuration block given.
|
|
157
|
+
STREAMING_ADAPTERS = %w[Faraday::Adapter::NetHttp Faraday::Adapter::NetHttpPersistent].freeze
|
|
158
|
+
NOT_STREAMING = "The download client's adapter cannot stream a response through Faraday's on_data, so an adaptive download " \
|
|
159
|
+
"cannot keep it within buffer_bytes; give the client Faraday's net_http or net_http_persistent adapter, or " \
|
|
160
|
+
"download without transfer:"
|
|
161
|
+
ADAPTER_BUFFERED = "The download client's adapter did not stream the response through Faraday's on_data, so the transfer cannot " \
|
|
162
|
+
"keep it within buffer_bytes; give the client an adapter that streams, or download without transfer:"
|
|
163
|
+
|
|
164
|
+
# Whether client's adapter is exactly one of STREAMING_ADAPTERS, so a download can go through it.
|
|
165
|
+
def self.streams?(client)
|
|
166
|
+
conn = client.respond_to?(:conn) ? client.conn : nil
|
|
167
|
+
conn.is_a?(Faraday::Connection) && STREAMING_ADAPTERS.include?(conn.builder.adapter&.name)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# A streamed 2xx response the client hands over, given to a success handler as HttpTransport gives it a
|
|
171
|
+
# Net::HTTPResponse; its body arrives through a Reader's Fiber.
|
|
172
|
+
class ClientResponse
|
|
173
|
+
attr_reader :code
|
|
174
|
+
|
|
175
|
+
def initialize(status, headers)
|
|
176
|
+
@code = status.to_s
|
|
177
|
+
@headers = headers
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def [](name)
|
|
181
|
+
@headers.fetch(name.to_s.downcase, []).first
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def key?(name)
|
|
185
|
+
@headers.key?(name.to_s.downcase)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def to_hash
|
|
189
|
+
@headers
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def content_length
|
|
193
|
+
value = self["content-length"]
|
|
194
|
+
value&.match?(/\A\d+\z/) ? value.to_i : nil
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def chunked?
|
|
198
|
+
self["transfer-encoding"].to_s.downcase.include?("chunked")
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def read_body
|
|
202
|
+
while (chunk = Fiber.yield)
|
|
203
|
+
yield chunk
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Faraday's on_data for one request. It runs a success handler in a Fiber that waits for each chunk as it arrives,
|
|
209
|
+
# and keeps the first 64 KiB of any other response.
|
|
210
|
+
class Reader
|
|
211
|
+
def initialize(on_success, timing)
|
|
212
|
+
@on_success = on_success
|
|
213
|
+
@timing = timing
|
|
214
|
+
@fiber = nil
|
|
215
|
+
@result = nil
|
|
216
|
+
@kept = String.new(encoding: Encoding::BINARY)
|
|
217
|
+
@truncated = false
|
|
218
|
+
@streamed = false
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def streamed?
|
|
222
|
+
@streamed
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Whether a response other than a success had more than the bytes kept.
|
|
226
|
+
def truncated?
|
|
227
|
+
@truncated
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# A chunk, the bytes so far and, since Faraday 2.5, the request's env, which holds the response's status and
|
|
231
|
+
# headers before its first chunk.
|
|
232
|
+
def call(chunk, _received, env = nil)
|
|
233
|
+
unless @streamed
|
|
234
|
+
@streamed = true
|
|
235
|
+
@timing&.headers
|
|
236
|
+
raise BufferLimitError, ADAPTER_BUFFERED if env.nil? || env.status.nil?
|
|
237
|
+
|
|
238
|
+
start(env.status.to_i, env.response_headers)
|
|
239
|
+
end
|
|
240
|
+
if @fiber
|
|
241
|
+
@fiber.resume(chunk.b) if @fiber.alive? && !chunk.empty?
|
|
242
|
+
else
|
|
243
|
+
room = HttpTransport::ERROR_BODY_BYTES - @kept.bytesize
|
|
244
|
+
@truncated ||= chunk.bytesize > room
|
|
245
|
+
@kept << chunk.b.byteslice(0, room) if room.positive?
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# After the response: what the success handler returned once it read the end of the body, or the bytes kept.
|
|
250
|
+
def result
|
|
251
|
+
@fiber.resume(nil) while @fiber&.alive?
|
|
252
|
+
@fiber ? @result : @kept
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# The handler's Fiber would not see the client ApiClient#request made active on this thread, since Thread#[] is
|
|
256
|
+
# per Fiber, so that client is made active in it too.
|
|
257
|
+
private def start(status, headers)
|
|
258
|
+
return unless status.between?(200, 299)
|
|
259
|
+
|
|
260
|
+
response = ClientResponse.new(status, ClientTransport.names(headers))
|
|
261
|
+
client = Thread.current[:files_api_client]
|
|
262
|
+
@fiber = Fiber.new do
|
|
263
|
+
Thread.current[:files_api_client] = client
|
|
264
|
+
@result = @on_success.call(response)
|
|
265
|
+
end
|
|
266
|
+
@fiber.resume
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Lowercase header names, each with its value; Faraday joins a repeated header's values with ", ".
|
|
271
|
+
def self.names(headers)
|
|
272
|
+
(headers || {}).each_with_object({}) { |(name, value), all| all[name.to_s.downcase] = [ value.to_s ] }
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def initialize(client)
|
|
276
|
+
@client = client
|
|
277
|
+
@lock = Mutex.new
|
|
278
|
+
@open_timeout = Files.open_timeout
|
|
279
|
+
@read_timeout = Files.read_timeout
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def serialized?
|
|
283
|
+
true
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# As HttpTransport#perform, through the client's remote_request.
|
|
287
|
+
def perform(method, url, headers, _connections, body: nil, length: nil, timing: nil, &on_success)
|
|
288
|
+
raise ArgumentError, "a client transfer sends a String body of #{length} bytes" unless body.nil? || body.is_a?(String)
|
|
289
|
+
|
|
290
|
+
@lock.synchronize do
|
|
291
|
+
reader = on_success && Reader.new(on_success, timing)
|
|
292
|
+
timing&.sending
|
|
293
|
+
begin
|
|
294
|
+
response = ApiClient.single_attempt do
|
|
295
|
+
@client.remote_request(method.to_s.downcase.to_sym, url, headers, body) do |request|
|
|
296
|
+
request.options.open_timeout = @open_timeout
|
|
297
|
+
request.options.timeout = @read_timeout
|
|
298
|
+
request.options.on_data = reader.method(:call).to_proc if reader
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
timing&.headers
|
|
302
|
+
respond(reader, response.status, response.headers, response.body).tap { timing&.body }
|
|
303
|
+
rescue Faraday::SSLError => e
|
|
304
|
+
raise NetworkFailure.new(e, retryable: false), cause: nil
|
|
305
|
+
rescue Faraday::Error => e
|
|
306
|
+
raise NetworkFailure.new(e), cause: nil unless e.response
|
|
307
|
+
|
|
308
|
+
timing&.headers
|
|
309
|
+
respond(reader, e.response[:status], e.response[:headers], e.response[:body]).tap { timing&.body }
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
rescue IOError => e
|
|
313
|
+
raise NetworkFailure.new(e), cause: nil
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def close_all(_connections); end
|
|
317
|
+
|
|
318
|
+
# A success handler gets only a streamed body: a 2xx response the adapter read whole has already been held, so it
|
|
319
|
+
# is refused rather than handed on. Any other response keeps its first 64 KiB, and says whether it had more.
|
|
320
|
+
private def respond(reader, status, headers, body)
|
|
321
|
+
names = ClientTransport.names(headers)
|
|
322
|
+
status = status.to_i
|
|
323
|
+
if reader&.streamed?
|
|
324
|
+
kept = reader.result
|
|
325
|
+
truncated = reader.truncated?
|
|
326
|
+
elsif reader && status.between?(200, 299)
|
|
327
|
+
raise BufferLimitError, ADAPTER_BUFFERED
|
|
328
|
+
else
|
|
329
|
+
body = body.to_s
|
|
330
|
+
kept = body.byteslice(0, HttpTransport::ERROR_BODY_BYTES).b
|
|
331
|
+
truncated = body.bytesize > HttpTransport::ERROR_BODY_BYTES
|
|
332
|
+
end
|
|
333
|
+
StorageResponse.new(status: status, headers: names, body: kept, truncated: truncated)
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
end
|