files.com 1.1.813 → 1.1.815

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.
Files changed (465) hide show
  1. checksums.yaml +4 -4
  2. data/_VERSION +1 -1
  3. data/doc/Files/APIConnectionError.html +1 -1
  4. data/doc/Files/APIError.html +1 -1
  5. data/doc/Files/AccountAlreadyExistsError.html +1 -1
  6. data/doc/Files/AccountLineItem.html +1 -1
  7. data/doc/Files/AccountOverdueError.html +1 -1
  8. data/doc/Files/Action.html +1 -1
  9. data/doc/Files/ActionLog.html +1 -1
  10. data/doc/Files/ActionNotificationExport.html +1 -1
  11. data/doc/Files/ActionNotificationExportResult.html +1 -1
  12. data/doc/Files/AdditionalAuthenticationRequiredError.html +1 -1
  13. data/doc/Files/AgentNode.html +1 -1
  14. data/doc/Files/AgentNodeConnection.html +1 -1
  15. data/doc/Files/AgentNodeInstance.html +1 -1
  16. data/doc/Files/AgentPushUpdate.html +1 -1
  17. data/doc/Files/AgentPushUpdateBlockedError.html +1 -1
  18. data/doc/Files/AgentUnavailableError.html +1 -1
  19. data/doc/Files/AgentUpgradeRequiredError.html +1 -1
  20. data/doc/Files/AiAssistantPersonality.html +1 -1
  21. data/doc/Files/AiTask.html +1 -1
  22. data/doc/Files/AiTaskCannotBeRunManuallyError.html +1 -1
  23. data/doc/Files/AlreadyCompletedError.html +1 -1
  24. data/doc/Files/Api.html +20 -13
  25. data/doc/Files/ApiClient/RequestLogContext.html +29 -29
  26. data/doc/Files/ApiClient/TransferSettings.html +721 -0
  27. data/doc/Files/ApiClient.html +756 -204
  28. data/doc/Files/ApiKey.html +1 -1
  29. data/doc/Files/ApiKeyIsDisabledError.html +1 -1
  30. data/doc/Files/ApiKeyIsPathRestrictedError.html +1 -1
  31. data/doc/Files/ApiKeyNotFoundError.html +1 -1
  32. data/doc/Files/ApiKeyOnlyForDesktopAppError.html +1 -1
  33. data/doc/Files/ApiKeyOnlyForFileOperationsError.html +1 -1
  34. data/doc/Files/ApiKeyOnlyForMobileAppError.html +1 -1
  35. data/doc/Files/ApiKeyOnlyForOfficeIntegrationError.html +1 -1
  36. data/doc/Files/ApiKeySessionsNotSupportedError.html +1 -1
  37. data/doc/Files/ApiRequestLog.html +1 -1
  38. data/doc/Files/App.html +1 -1
  39. data/doc/Files/As2IncomingMessage.html +1 -1
  40. data/doc/Files/As2OutgoingMessage.html +1 -1
  41. data/doc/Files/As2Partner.html +1 -1
  42. data/doc/Files/As2Station.html +1 -1
  43. data/doc/Files/AttachmentTooLargeError.html +1 -1
  44. data/doc/Files/AuthenticationError.html +1 -1
  45. data/doc/Files/AuthenticationRequiredError.html +1 -1
  46. data/doc/Files/Auto.html +1 -1
  47. data/doc/Files/Automation.html +1 -1
  48. data/doc/Files/AutomationAuthoringSchema.html +1 -1
  49. data/doc/Files/AutomationCannotBeRunManuallyError.html +1 -1
  50. data/doc/Files/AutomationExecutionNode.html +1 -1
  51. data/doc/Files/AutomationLog.html +1 -1
  52. data/doc/Files/AutomationRun.html +1 -1
  53. data/doc/Files/AutomationsUnavailableError.html +1 -1
  54. data/doc/Files/BadRequestError.html +1 -1
  55. data/doc/Files/BandwidthSnapshot.html +1 -1
  56. data/doc/Files/Behavior.html +1 -1
  57. data/doc/Files/BehaviorNotAllowedOnRemoteServerError.html +1 -1
  58. data/doc/Files/BillingInformationHiddenError.html +1 -1
  59. data/doc/Files/BillingPermissionRequiredError.html +1 -1
  60. data/doc/Files/BufferedUploadDisabledForThisDestinationError.html +1 -1
  61. data/doc/Files/Bundle.html +1 -1
  62. data/doc/Files/BundleAction.html +1 -1
  63. data/doc/Files/BundleDownload.html +1 -1
  64. data/doc/Files/BundleMaximumUsesReachedError.html +1 -1
  65. data/doc/Files/BundleNotification.html +1 -1
  66. data/doc/Files/BundleOnlyAllowsPreviewsError.html +1 -1
  67. data/doc/Files/BundleOperationRequiresSubfolderError.html +1 -1
  68. data/doc/Files/BundlePath.html +1 -1
  69. data/doc/Files/BundlePathNotFoundError.html +1 -1
  70. data/doc/Files/BundlePermissionRequiredError.html +1 -1
  71. data/doc/Files/BundleRecipient.html +1 -1
  72. data/doc/Files/BundleRegistration.html +1 -1
  73. data/doc/Files/BundleRegistrationCodeFailedError.html +1 -1
  74. data/doc/Files/BundleRegistrationNotFoundError.html +1 -1
  75. data/doc/Files/CannotAdministerHigherLevelUserError.html +1 -1
  76. data/doc/Files/CannotDownloadDirectoryError.html +1 -1
  77. data/doc/Files/CannotLoginWhileUsingKeyError.html +1 -1
  78. data/doc/Files/CantActForOtherUserError.html +1 -1
  79. data/doc/Files/CantMoveWithMultipleLocationsError.html +1 -1
  80. data/doc/Files/ChatMessage.html +1 -1
  81. data/doc/Files/ChatSession.html +1 -1
  82. data/doc/Files/ChildSiteManagementPolicy.html +1 -1
  83. data/doc/Files/Clickwrap.html +1 -1
  84. data/doc/Files/CodeNotFoundError.html +1 -1
  85. data/doc/Files/ConfigurationLockedPathError.html +1 -1
  86. data/doc/Files/ContactAdminForPasswordChangeHelpError.html +1 -1
  87. data/doc/Files/CouldNotCreateParentError.html +1 -1
  88. data/doc/Files/CustomDomain.html +1 -1
  89. data/doc/Files/DatetimeParseError.html +1 -1
  90. data/doc/Files/DesktopConfigurationProfile.html +1 -1
  91. data/doc/Files/DestinationExistsError.html +1 -1
  92. data/doc/Files/DestinationFolderLimitedError.html +1 -1
  93. data/doc/Files/DestinationParentConflictError.html +1 -1
  94. data/doc/Files/DestinationParentDoesNotExistError.html +1 -1
  95. data/doc/Files/DestinationSameError.html +1 -1
  96. data/doc/Files/DestinationSiteMismatchError.html +1 -1
  97. data/doc/Files/DirectConnectionInfo.html +1 -1
  98. data/doc/Files/DnsRecord.html +1 -1
  99. data/doc/Files/DoesNotSupportSortingError.html +1 -1
  100. data/doc/Files/DuplicateShareRecipientError.html +1 -1
  101. data/doc/Files/EmailIncomingMessage.html +1 -1
  102. data/doc/Files/EmailLog.html +1 -1
  103. data/doc/Files/Error.html +2 -2
  104. data/doc/Files/Errors.html +1 -1
  105. data/doc/Files/EventChannel.html +1 -1
  106. data/doc/Files/EventDeliveryAttempt.html +1 -1
  107. data/doc/Files/EventRecord.html +1 -1
  108. data/doc/Files/EventSubscription.html +1 -1
  109. data/doc/Files/EventTarget.html +1 -1
  110. data/doc/Files/ExavaultApiRequestLog.html +1 -1
  111. data/doc/Files/ExceededRuntimeLimitError.html +1 -1
  112. data/doc/Files/Expectation.html +1 -1
  113. data/doc/Files/ExpectationAlreadyHasOpenWindowError.html +1 -1
  114. data/doc/Files/ExpectationEvaluation.html +1 -1
  115. data/doc/Files/ExpectationIncident.html +1 -1
  116. data/doc/Files/ExpectationNotManualTriggerError.html +1 -1
  117. data/doc/Files/ExpiredPrivateKeyError.html +1 -1
  118. data/doc/Files/ExpiredPublicKeyError.html +1 -1
  119. data/doc/Files/ExportFailureError.html +1 -1
  120. data/doc/Files/ExportNotReadyError.html +1 -1
  121. data/doc/Files/ExternalEvent.html +1 -1
  122. data/doc/Files/FailedToChangePasswordError.html +1 -1
  123. data/doc/Files/File.html +2019 -1515
  124. data/doc/Files/FileAction.html +1 -1
  125. data/doc/Files/FileActionError.html +1 -1
  126. data/doc/Files/FileComment.html +1 -1
  127. data/doc/Files/FileCommentReaction.html +1 -1
  128. data/doc/Files/FileLockedError.html +1 -1
  129. data/doc/Files/FileMigration.html +1 -1
  130. data/doc/Files/FileMigrationLog.html +1 -1
  131. data/doc/Files/FileNotFoundError.html +1 -1
  132. data/doc/Files/FileNotUploadedError.html +1 -1
  133. data/doc/Files/FilePendingProcessingError.html +1 -1
  134. data/doc/Files/FileProcessingErrorError.html +1 -1
  135. data/doc/Files/FileTooBigToDecryptError.html +1 -1
  136. data/doc/Files/FileTooBigToEncryptError.html +1 -1
  137. data/doc/Files/FileUploadNotFoundError.html +1 -1
  138. data/doc/Files/FileUploadPart.html +1 -1
  139. data/doc/Files/FileUploadedToWrongRegionError.html +1 -1
  140. data/doc/Files/FileUtils.html +1 -1
  141. data/doc/Files/FilenameTooLongError.html +1 -1
  142. data/doc/Files/FilesAgentFailedAuthorizationError.html +1 -1
  143. data/doc/Files/FilesNativeStorageOverloadedError.html +1 -1
  144. data/doc/Files/Folder.html +1 -1
  145. data/doc/Files/FolderAdminOrBillingPermissionRequiredError.html +1 -1
  146. data/doc/Files/FolderAdminPermissionRequiredError.html +1 -1
  147. data/doc/Files/FolderLockedError.html +1 -1
  148. data/doc/Files/FolderMustNotBeAFileError.html +1 -1
  149. data/doc/Files/FolderNotEmptyError.html +1 -1
  150. data/doc/Files/FoldersNotAllowedError.html +1 -1
  151. data/doc/Files/FormField.html +1 -1
  152. data/doc/Files/FormFieldSet.html +1 -1
  153. data/doc/Files/FtpActionLog.html +1 -1
  154. data/doc/Files/FullPermissionRequiredError.html +1 -1
  155. data/doc/Files/GpgKey.html +1 -1
  156. data/doc/Files/Group.html +1 -1
  157. data/doc/Files/GroupNotFoundError.html +1 -1
  158. data/doc/Files/GroupUser.html +1 -1
  159. data/doc/Files/History.html +1 -1
  160. data/doc/Files/HistoryExport.html +1 -1
  161. data/doc/Files/HistoryExportResult.html +1 -1
  162. data/doc/Files/HistoryPermissionRequiredError.html +1 -1
  163. data/doc/Files/HistoryUnavailableError.html +1 -1
  164. data/doc/Files/HolidayCalendar.html +1 -1
  165. data/doc/Files/HolidayRegion.html +1 -1
  166. data/doc/Files/Image.html +1 -1
  167. data/doc/Files/InAppAiAssistantUnavailableError.html +1 -1
  168. data/doc/Files/InboundS3Log.html +1 -1
  169. data/doc/Files/InboxNotFoundError.html +1 -1
  170. data/doc/Files/InboxRecipient.html +1 -1
  171. data/doc/Files/InboxRegistration.html +1 -1
  172. data/doc/Files/InboxRegistrationCodeFailedError.html +1 -1
  173. data/doc/Files/InboxUpload.html +1 -1
  174. data/doc/Files/InsufficientPermissionForParamsError.html +1 -1
  175. data/doc/Files/InsufficientPermissionForSiteError.html +1 -1
  176. data/doc/Files/IntegrationCentricProfile.html +1 -1
  177. data/doc/Files/InternalGeneralErrorError.html +1 -1
  178. data/doc/Files/InvalidBodyError.html +1 -1
  179. data/doc/Files/InvalidBundleCodeError.html +1 -1
  180. data/doc/Files/InvalidCredentialsError.html +1 -1
  181. data/doc/Files/InvalidCursorError.html +1 -1
  182. data/doc/Files/InvalidCursorTypeForSortError.html +1 -1
  183. data/doc/Files/InvalidEtagsError.html +1 -1
  184. data/doc/Files/InvalidFileTypeError.html +1 -1
  185. data/doc/Files/InvalidFilenameError.html +1 -1
  186. data/doc/Files/InvalidFilterAliasCombinationError.html +1 -1
  187. data/doc/Files/InvalidFilterFieldError.html +1 -1
  188. data/doc/Files/InvalidFilterParamError.html +1 -1
  189. data/doc/Files/InvalidFilterParamFormatError.html +1 -1
  190. data/doc/Files/InvalidFilterParamValueError.html +1 -1
  191. data/doc/Files/InvalidInputEncodingError.html +1 -1
  192. data/doc/Files/InvalidInterfaceError.html +1 -1
  193. data/doc/Files/InvalidOauthError.html +1 -1
  194. data/doc/Files/InvalidOauthProviderError.html +1 -1
  195. data/doc/Files/InvalidOrExpiredCodeError.html +1 -1
  196. data/doc/Files/InvalidParameterError.html +5 -1
  197. data/doc/Files/InvalidPathError.html +1 -1
  198. data/doc/Files/InvalidPriorityColorError.html +1 -1
  199. data/doc/Files/InvalidRangeError.html +1 -1
  200. data/doc/Files/InvalidReturnToUrlError.html +1 -1
  201. data/doc/Files/InvalidSearchQueryError.html +1 -1
  202. data/doc/Files/InvalidSessionError.html +1 -1
  203. data/doc/Files/InvalidSiteError.html +1 -1
  204. data/doc/Files/InvalidSortFieldError.html +1 -1
  205. data/doc/Files/InvalidSortFilterCombinationError.html +1 -1
  206. data/doc/Files/InvalidUploadOffsetError.html +1 -1
  207. data/doc/Files/InvalidUploadPartGapError.html +1 -1
  208. data/doc/Files/InvalidUploadPartSizeError.html +1 -1
  209. data/doc/Files/InvalidUsernameOrPasswordError.html +1 -1
  210. data/doc/Files/InvalidWorkspaceIdHeaderError.html +1 -1
  211. data/doc/Files/InvalidZipFileError.html +1 -1
  212. data/doc/Files/Invoice.html +1 -1
  213. data/doc/Files/InvoiceLineItem.html +1 -1
  214. data/doc/Files/IpAddress.html +1 -1
  215. data/doc/Files/KeyLifecycleRule.html +1 -1
  216. data/doc/Files/List.html +1 -1
  217. data/doc/Files/Lock.html +1 -1
  218. data/doc/Files/LockOperationBusyError.html +1 -1
  219. data/doc/Files/LockedOutError.html +1 -1
  220. data/doc/Files/LockoutRegionMismatchError.html +1 -1
  221. data/doc/Files/MetadataCategory.html +1 -1
  222. data/doc/Files/MetadataNotSupportedOnRemotesError.html +1 -1
  223. data/doc/Files/MethodNotAllowedError.html +1 -1
  224. data/doc/Files/MigrationInProgressError.html +1 -1
  225. data/doc/Files/MissingParameterError.html +1 -1
  226. data/doc/Files/ModelSaveErrorError.html +1 -1
  227. data/doc/Files/MoverAccessDeniedError.html +1 -1
  228. data/doc/Files/MoverPackageRequiredError.html +1 -1
  229. data/doc/Files/MultipleProcessingErrorsError.html +1 -1
  230. data/doc/Files/MultipleSortParamsNotAllowedError.html +1 -1
  231. data/doc/Files/MustAuthenticateWithApiKeyError.html +1 -1
  232. data/doc/Files/NeedAdminPermissionForInboxError.html +1 -1
  233. data/doc/Files/NestedNotFoundError.html +1 -1
  234. data/doc/Files/NoAccountForSiteError.html +1 -1
  235. data/doc/Files/NoValidInputParamsError.html +1 -1
  236. data/doc/Files/NonAdminsMustQueryByFolderOrPathError.html +1 -1
  237. data/doc/Files/NotAllowedToCreateBundleError.html +1 -1
  238. data/doc/Files/NotAuthenticatedError.html +1 -1
  239. data/doc/Files/NotAuthorizedError.html +1 -1
  240. data/doc/Files/NotEnqueuableSyncError.html +1 -1
  241. data/doc/Files/NotFoundError.html +1 -1
  242. data/doc/Files/NotImplementedError.html +1 -1
  243. data/doc/Files/Notification.html +1 -1
  244. data/doc/Files/OffsetUploadNotAllowedWithMalwareScanningError.html +1 -1
  245. data/doc/Files/OneTimePasswordIncorrectError.html +1 -1
  246. data/doc/Files/OutboundConnectionLog.html +1 -1
  247. data/doc/Files/PartNumberTooLargeError.html +1 -1
  248. data/doc/Files/Partner.html +1 -1
  249. data/doc/Files/PartnerChannel.html +1 -1
  250. data/doc/Files/PartnerChannelTemplate.html +1 -1
  251. data/doc/Files/PartnerConnection.html +1 -1
  252. data/doc/Files/PartnerSite.html +1 -1
  253. data/doc/Files/PartnerSiteRequest.html +1 -1
  254. data/doc/Files/PasswordChangeNotRequiredError.html +1 -1
  255. data/doc/Files/PasswordChangeRequiredError.html +1 -1
  256. data/doc/Files/PathCannotHaveTrailingWhitespaceError.html +1 -1
  257. data/doc/Files/PathTooLongError.html +1 -1
  258. data/doc/Files/PathUtil.html +1 -1
  259. data/doc/Files/Payment.html +1 -1
  260. data/doc/Files/PaymentLineItem.html +1 -1
  261. data/doc/Files/PaymentMethodErrorError.html +1 -1
  262. data/doc/Files/PendingWorkEvent.html +1 -1
  263. data/doc/Files/Permission.html +1 -1
  264. data/doc/Files/PlanNotFoundError.html +1 -1
  265. data/doc/Files/Preview.html +1 -1
  266. data/doc/Files/PreviewOnlyPermissionCannotDownloadError.html +1 -1
  267. data/doc/Files/ProcessingFailureError.html +1 -1
  268. data/doc/Files/PublicHostingRequestLog.html +1 -1
  269. data/doc/Files/PublicIpAddress.html +1 -1
  270. data/doc/Files/PublicKey.html +1 -1
  271. data/doc/Files/RateLimitedError.html +1 -1
  272. data/doc/Files/ReadOnlySessionError.html +1 -1
  273. data/doc/Files/ReadPermissionRequiredError.html +1 -1
  274. data/doc/Files/ReauthenticationFailedError.html +1 -1
  275. data/doc/Files/ReauthenticationFailedFinalError.html +1 -1
  276. data/doc/Files/ReauthenticationNeededActionError.html +1 -1
  277. data/doc/Files/ReauthenticationNeededFieldsError.html +1 -1
  278. data/doc/Files/ReauthenticationRateLimitedError.html +1 -1
  279. data/doc/Files/RecaptchaFailedError.html +1 -1
  280. data/doc/Files/RecipientAlreadySharedError.html +1 -1
  281. data/doc/Files/RemoteBandwidthSnapshot.html +1 -1
  282. data/doc/Files/RemoteDesktopDebugLoggingDisabledError.html +1 -1
  283. data/doc/Files/RemoteEntryReadOnlyError.html +1 -1
  284. data/doc/Files/RemoteMountBackend.html +1 -1
  285. data/doc/Files/RemoteServer.html +1 -1
  286. data/doc/Files/RemoteServerConfigurationFile.html +1 -1
  287. data/doc/Files/RemoteServerCredential.html +1 -1
  288. data/doc/Files/RemoteServerErrorError.html +1 -1
  289. data/doc/Files/Request.html +1 -1
  290. data/doc/Files/RequestBodyTooLargeError.html +1 -1
  291. data/doc/Files/RequestParamsContainInvalidCharacterError.html +1 -1
  292. data/doc/Files/RequestParamsInvalidError.html +1 -1
  293. data/doc/Files/RequestParamsRequiredError.html +1 -1
  294. data/doc/Files/ResourceBelongsToParentSiteError.html +1 -1
  295. data/doc/Files/ResourceLockedError.html +1 -1
  296. data/doc/Files/Response.html +1 -1
  297. data/doc/Files/Restore.html +1 -1
  298. data/doc/Files/RootFolderBehaviorSiteAdminRequiredError.html +1 -1
  299. data/doc/Files/RootFolderBehaviorSkipSiteAdminRequiredError.html +1 -1
  300. data/doc/Files/Schedule.html +1 -1
  301. data/doc/Files/ScheduledExport.html +1 -1
  302. data/doc/Files/ScimLog.html +1 -1
  303. data/doc/Files/SearchAllOnChildPathError.html +1 -1
  304. data/doc/Files/SearchUnavailableError.html +1 -1
  305. data/doc/Files/Secret.html +1 -1
  306. data/doc/Files/SelfManagedRequiredError.html +1 -1
  307. data/doc/Files/ServiceUnavailableError.html +1 -1
  308. data/doc/Files/Session.html +1 -1
  309. data/doc/Files/SettingsChange.html +1 -1
  310. data/doc/Files/SftpActionLog.html +1 -1
  311. data/doc/Files/SftpHostKey.html +1 -1
  312. data/doc/Files/ShareGroup.html +1 -1
  313. data/doc/Files/ShareGroupMember.html +1 -1
  314. data/doc/Files/SiemHttpDestination.html +1 -1
  315. data/doc/Files/SiemHttpDestinationEvent.html +1 -1
  316. data/doc/Files/Site.html +911 -731
  317. data/doc/Files/SiteAdminOrPartnerAdminPermissionRequiredError.html +1 -1
  318. data/doc/Files/SiteAdminOrWorkspaceAdminOrFolderAdminPermissionRequiredError.html +1 -1
  319. data/doc/Files/SiteAdminOrWorkspaceAdminOrPartnerAdminOrFolderAdminPermissionRequiredError.html +1 -1
  320. data/doc/Files/SiteAdminOrWorkspaceAdminOrPartnerAdminPermissionRequiredError.html +1 -1
  321. data/doc/Files/SiteAdminOrWorkspaceAdminPermissionRequiredError.html +1 -1
  322. data/doc/Files/SiteAdminRequiredError.html +1 -1
  323. data/doc/Files/SiteConfigurationError.html +1 -1
  324. data/doc/Files/SiteDisabledError.html +1 -1
  325. data/doc/Files/SiteFilesAreImmutableError.html +1 -1
  326. data/doc/Files/SiteNotFoundError.html +1 -1
  327. data/doc/Files/SiteSubdomainRedirect.html +1 -1
  328. data/doc/Files/SiteWasRemovedError.html +1 -1
  329. data/doc/Files/SizableIO.html +1 -1
  330. data/doc/Files/Snapshot.html +1 -1
  331. data/doc/Files/SsoEvent.html +1 -1
  332. data/doc/Files/SsoStrategy.html +1 -1
  333. data/doc/Files/Status.html +1 -1
  334. data/doc/Files/Style.html +1 -1
  335. data/doc/Files/SubfolderLockedError.html +1 -1
  336. data/doc/Files/Sync.html +1 -1
  337. data/doc/Files/SyncInProgressError.html +1 -1
  338. data/doc/Files/SyncLog.html +1 -1
  339. data/doc/Files/SyncRun.html +1 -1
  340. data/doc/Files/SyncRunLiveTransfer.html +1 -1
  341. data/doc/Files/SystemProfiler.html +1 -1
  342. data/doc/Files/TooManyConcurrentLoginsError.html +1 -1
  343. data/doc/Files/TooManyConcurrentRequestsError.html +1 -1
  344. data/doc/Files/TooManyLoginAttemptsError.html +1 -1
  345. data/doc/Files/TooManyRequestsError.html +1 -1
  346. data/doc/Files/TooManySharesError.html +1 -1
  347. data/doc/Files/Transfer/BufferLimitError.html +175 -0
  348. data/doc/Files/Transfer/ByteBudget/Reservation.html +301 -0
  349. data/doc/Files/Transfer/ByteBudget.html +748 -0
  350. data/doc/Files/Transfer/CancelledError.html +235 -0
  351. data/doc/Files/Transfer/Checkpoint.html +1371 -0
  352. data/doc/Files/Transfer/CheckpointError.html +173 -0
  353. data/doc/Files/Transfer/ClientTransport/ClientResponse.html +661 -0
  354. data/doc/Files/Transfer/ClientTransport/Reader.html +530 -0
  355. data/doc/Files/Transfer/ClientTransport.html +675 -0
  356. data/doc/Files/Transfer/Clock.html +189 -0
  357. data/doc/Files/Transfer/Control.html +667 -0
  358. data/doc/Files/Transfer/Controller/Attempt.html +465 -0
  359. data/doc/Files/Transfer/Controller.html +1166 -0
  360. data/doc/Files/Transfer/Coordinator/Timing.html +568 -0
  361. data/doc/Files/Transfer/Coordinator.html +534 -0
  362. data/doc/Files/Transfer/DownloadCoordinator/FileSink.html +976 -0
  363. data/doc/Files/Transfer/DownloadCoordinator/IoSink.html +1168 -0
  364. data/doc/Files/Transfer/DownloadCoordinator/Piece.html +875 -0
  365. data/doc/Files/Transfer/DownloadCoordinator.html +728 -0
  366. data/doc/Files/Transfer/FileSlots.html +480 -0
  367. data/doc/Files/Transfer/FrontendResponseError.html +235 -0
  368. data/doc/Files/Transfer/Holds.html +505 -0
  369. data/doc/Files/Transfer/HttpTransport.html +579 -0
  370. data/doc/Files/Transfer/Job.html +2338 -0
  371. data/doc/Files/Transfer/Options.html +924 -0
  372. data/doc/Files/Transfer/PausedError.html +319 -0
  373. data/doc/Files/Transfer/Policy.html +1642 -0
  374. data/doc/Files/Transfer/RequestPermits.html +160 -0
  375. data/doc/Files/Transfer/SourceChangedError.html +235 -0
  376. data/doc/Files/Transfer/StorageResponse.html +546 -0
  377. data/doc/Files/Transfer/StorageRetry.html +1098 -0
  378. data/doc/Files/Transfer/UploadCoordinator/Grant.html +1048 -0
  379. data/doc/Files/Transfer/UploadCoordinator/Part.html +1048 -0
  380. data/doc/Files/Transfer/UploadCoordinator/Region.html +295 -0
  381. data/doc/Files/Transfer/UploadCoordinator/Segments.html +299 -0
  382. data/doc/Files/Transfer/UploadCoordinator/Source.html +725 -0
  383. data/doc/Files/Transfer/UploadCoordinator/StreamSource.html +664 -0
  384. data/doc/Files/Transfer/UploadCoordinator.html +546 -0
  385. data/doc/Files/Transfer/UploadPlan.html +522 -0
  386. data/doc/Files/Transfer/VersionChangedError.html +235 -0
  387. data/doc/Files/Transfer.html +134 -0
  388. data/doc/Files/TrialExpiredError.html +1 -1
  389. data/doc/Files/TrialLockedError.html +1 -1
  390. data/doc/Files/TwoFactorAuthenticationCodeAlreadySentError.html +1 -1
  391. data/doc/Files/TwoFactorAuthenticationCountryBlacklistedError.html +1 -1
  392. data/doc/Files/TwoFactorAuthenticationErrorError.html +1 -1
  393. data/doc/Files/TwoFactorAuthenticationGeneralErrorError.html +1 -1
  394. data/doc/Files/TwoFactorAuthenticationMethodUnsupportedErrorError.html +1 -1
  395. data/doc/Files/TwoFactorAuthenticationRequiredError.html +1 -1
  396. data/doc/Files/TwoFactorAuthenticationSetupExpiredError.html +1 -1
  397. data/doc/Files/TwoFactorAuthenticationUnsubscribedRecipientError.html +1 -1
  398. data/doc/Files/URI.html +1 -1
  399. data/doc/Files/UnrecognizedSortIndexError.html +1 -1
  400. data/doc/Files/UnsupportedCurrencyError.html +1 -1
  401. data/doc/Files/UnsupportedHttpResponseFormatError.html +1 -1
  402. data/doc/Files/UnsupportedMediaTypeError.html +1 -1
  403. data/doc/Files/UpdatesNotAllowedForRemotesError.html +1 -1
  404. data/doc/Files/UploadsUnavailableError.html +1 -1
  405. data/doc/Files/UsageByTopLevelDir.html +1 -1
  406. data/doc/Files/UsageDailySnapshot.html +1 -1
  407. data/doc/Files/UsageSnapshot.html +1 -1
  408. data/doc/Files/User.html +597 -386
  409. data/doc/Files/UserAdditionalEmailRecipient.html +1 -1
  410. data/doc/Files/UserCipherUse.html +1 -1
  411. data/doc/Files/UserIdInvalidError.html +1 -1
  412. data/doc/Files/UserIdOnUserEndpointError.html +1 -1
  413. data/doc/Files/UserIdWithoutSiteAdminError.html +1 -1
  414. data/doc/Files/UserLifecycleRule.html +1 -1
  415. data/doc/Files/UserNotFoundError.html +1 -1
  416. data/doc/Files/UserRequest.html +1 -1
  417. data/doc/Files/UserRequestsEnabledRequiredError.html +1 -1
  418. data/doc/Files/UserRequiredError.html +1 -1
  419. data/doc/Files/UserSecurityEvent.html +1 -1
  420. data/doc/Files/UserSftpClientUse.html +1 -1
  421. data/doc/Files/Util/LogSettings.html +1 -1
  422. data/doc/Files/Util.html +1 -1
  423. data/doc/Files/WebDavActionLog.html +1 -1
  424. data/doc/Files/WebhookTest.html +1 -1
  425. data/doc/Files/Workspace.html +1 -1
  426. data/doc/Files/WriteAndBundlePermissionRequiredError.html +1 -1
  427. data/doc/Files/WritePermissionRequiredError.html +1 -1
  428. data/doc/Files/ZipListEntry.html +1 -1
  429. data/doc/Files.html +116 -116
  430. data/doc/_index.html +314 -4
  431. data/doc/class_list.html +1 -1
  432. data/doc/file.README.html +1 -1
  433. data/doc/index.html +1 -1
  434. data/doc/method_list.html +5368 -3048
  435. data/doc/top-level-namespace.html +1 -1
  436. data/docs/file.md +346 -0
  437. data/docs/site.md +10 -0
  438. data/docs/user.md +8 -0
  439. data/lib/files.com/api.rb +6 -1
  440. data/lib/files.com/api_client.rb +220 -32
  441. data/lib/files.com/models/file.rb +124 -17
  442. data/lib/files.com/models/site.rb +16 -0
  443. data/lib/files.com/models/user.rb +19 -0
  444. data/lib/files.com/transfer/budgets.rb +123 -0
  445. data/lib/files.com/transfer/checkpoint.rb +113 -0
  446. data/lib/files.com/transfer/clock.rb +14 -0
  447. data/lib/files.com/transfer/controller.rb +549 -0
  448. data/lib/files.com/transfer/download_coordinator.rb +906 -0
  449. data/lib/files.com/transfer/errors.rb +100 -0
  450. data/lib/files.com/transfer/http_transport.rb +337 -0
  451. data/lib/files.com/transfer/job.rb +778 -0
  452. data/lib/files.com/transfer/options.rb +126 -0
  453. data/lib/files.com/transfer/policy.rb +91 -0
  454. data/lib/files.com/transfer/storage_retry.rb +169 -0
  455. data/lib/files.com/transfer/upload_coordinator.rb +657 -0
  456. data/lib/files.com/transfer/upload_plan.rb +157 -0
  457. data/lib/files.com.rb +13 -0
  458. data/spec/lib/api_client_spec.rb +125 -3
  459. data/spec/models/file_transfer_spec.rb +53 -4
  460. data/spec/support/transfer_server.rb +113 -14
  461. data/spec/transfer/adaptive_download_spec.rb +1102 -0
  462. data/spec/transfer/adaptive_upload_spec.rb +1602 -0
  463. data/spec/transfer/concurrency_controller_spec.rb +382 -0
  464. data/spec/workspace_id_spec.rb +19 -14
  465. metadata +59 -1
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Files
4
+ module Transfer
5
+ # Lets another thread stop an adaptive transfer. cancel and pause stop new requests at once; the transfer then
6
+ # waits for the requests already in flight, releases the memory and connections it holds and raises
7
+ # CancelledError or PausedError. The first of the two wins, and a Control stays stopped: resume with a new one.
8
+ class Control
9
+ def initialize
10
+ @lock = Mutex.new
11
+ @state = :running
12
+ @listeners = []
13
+ end
14
+
15
+ def cancel
16
+ change(:cancelled)
17
+ end
18
+
19
+ def pause
20
+ change(:paused)
21
+ end
22
+
23
+ def cancelled?
24
+ @state == :cancelled
25
+ end
26
+
27
+ def paused?
28
+ @state == :paused
29
+ end
30
+
31
+ # :running, :cancelled or :paused.
32
+ attr_reader :state
33
+
34
+ # A listener's wake is called after each change, so jobs waiting on this control notice it at once.
35
+ def subscribe(listener)
36
+ @lock.synchronize { @listeners << listener }
37
+ end
38
+
39
+ def unsubscribe(listener)
40
+ @lock.synchronize { @listeners.delete(listener) }
41
+ end
42
+
43
+ private def change(state)
44
+ listeners = @lock.synchronize do
45
+ return false unless @state == :running
46
+
47
+ @state = state
48
+ @listeners.dup
49
+ end
50
+ listeners.each(&:wake)
51
+ true
52
+ end
53
+ end
54
+
55
+ # What an adaptive transfer may use. The transfer: option takes these as a Hash, true for all the defaults, an
56
+ # Options or a Job that several transfers share.
57
+ #
58
+ # concurrency:: :automatic (the default) tunes the requests in flight from what the link sustains; an Integer from
59
+ # 1 to 256 keeps exactly that many.
60
+ # startup:: With :automatic, :default starts an upload to cloud storage with one request and grows while each
61
+ # step pays off; :aggressive starts at 32. Downloads and direct destinations ignore it.
62
+ # max_concurrency:: With :automatic, the most requests in flight, from 1 to 256. The default is 64 for cloud
63
+ # storage and downloads and 16 for direct destinations, which it can only lower.
64
+ # buffer_bytes:: The most bytes held in memory at once for parts waiting to be sent or written, at least 65,536.
65
+ # The default is 256 MiB.
66
+ # max_files:: How many files a shared Job transfers at once; more wait their turn. The default is 4.
67
+ # control:: A Control that cancels or pauses the transfer.
68
+ class Options
69
+ MAX_CONCURRENCY = 256
70
+ DEFAULT_BUFFER_BYTES = 256 * 1024 * 1024
71
+ MIN_BUFFER_BYTES = 64 * 1024
72
+ DEFAULT_MAX_FILES = 4
73
+ KEYS = %i[concurrency startup max_concurrency buffer_bytes max_files control].freeze
74
+ STARTUPS = %i[default aggressive].freeze
75
+
76
+ attr_reader :concurrency, :startup, :max_concurrency, :buffer_bytes, :max_files, :control
77
+
78
+ def self.from(value)
79
+ case value
80
+ when Options then value
81
+ when true then new
82
+ when Hash
83
+ unknown = value.keys - KEYS
84
+ raise InvalidParameterError, "Bad parameter: transfer does not take #{unknown.map(&:inspect).join(', ')}" unless unknown.empty?
85
+
86
+ new(**value)
87
+ else
88
+ raise InvalidParameterError, "Bad parameter: transfer must be true, a Hash of transfer options, a Files::Transfer::Options or a Files::Transfer::Job"
89
+ end
90
+ end
91
+
92
+ def initialize(concurrency: :automatic, startup: :default, max_concurrency: nil, buffer_bytes: DEFAULT_BUFFER_BYTES, max_files: DEFAULT_MAX_FILES, control: nil)
93
+ @concurrency = concurrency
94
+ @startup = startup
95
+ @max_concurrency = max_concurrency
96
+ @buffer_bytes = buffer_bytes
97
+ @max_files = max_files
98
+ @control = control
99
+ validate
100
+ freeze
101
+ end
102
+
103
+ def automatic?
104
+ @concurrency == :automatic
105
+ end
106
+
107
+ def to_h
108
+ KEYS.to_h { |key| [ key, public_send(key) ] }
109
+ end
110
+
111
+ private def validate
112
+ raise InvalidParameterError, "Bad parameter: transfer concurrency must be :automatic or an Integer from 1 to #{MAX_CONCURRENCY}" unless automatic? || concurrency_count?(@concurrency)
113
+ raise InvalidParameterError, "Bad parameter: transfer startup must be :default or :aggressive" unless STARTUPS.include?(@startup)
114
+ raise InvalidParameterError, "Bad parameter: transfer startup applies only to automatic concurrency" if !automatic? && @startup != :default
115
+ raise InvalidParameterError, "Bad parameter: transfer max_concurrency must be an Integer from 1 to #{MAX_CONCURRENCY}, with automatic concurrency" unless @max_concurrency.nil? || (automatic? && concurrency_count?(@max_concurrency))
116
+ raise InvalidParameterError, "Bad parameter: transfer buffer_bytes must be an Integer of at least #{MIN_BUFFER_BYTES}" unless @buffer_bytes.is_a?(Integer) && @buffer_bytes >= MIN_BUFFER_BYTES
117
+ raise InvalidParameterError, "Bad parameter: transfer max_files must be a positive Integer" unless @max_files.is_a?(Integer) && @max_files.positive?
118
+ raise InvalidParameterError, "Bad parameter: transfer control must be a Files::Transfer::Control" unless @control.nil? || @control.is_a?(Control)
119
+ end
120
+
121
+ private def concurrency_count?(value)
122
+ value.is_a?(Integer) && value.between?(1, MAX_CONCURRENCY)
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Files
4
+ module Transfer
5
+ # The numbers a Controller works within for one kind of destination. Every count is capped at max, so a caller's
6
+ # max_concurrency, or a fixed concurrency, always dominates.
7
+ class Policy
8
+ MIB = 1024 * 1024
9
+ # A warm-start window at least this fast ends an upload's warm start at the start concurrency.
10
+ FAST_LINK_BYTES_PER_SECOND = 96 * MIB
11
+ # A window is judged only when its requests in flight averaged at least this share of the limit.
12
+ ELIGIBLE_OCCUPANCY = 0.75
13
+ # A failure, a throttle and a growing queue shrink the limit by these shares, once per epoch.
14
+ FAILURE_SHRINK = 0.35
15
+ THROTTLE_SHRINK = 0.10
16
+ LATENCY_SHRINK = 0.08
17
+ # A probe is kept when a window's throughput beats the one before it by this share, and given up after this many
18
+ # windows that do not; the limit then holds for HOLD_WINDOWS windows.
19
+ PROBE_GAIN = 0.01
20
+ PROBE_WINDOWS = 2
21
+ HOLD_WINDOWS = 1
22
+ # After a rejected warm-start probe, the windows to rest before probing again by one.
23
+ GROWTH_INTERVAL_WINDOWS = 1
24
+ WARM_REJECTIONS = 2
25
+ # Seconds after a throttle, or its Retry-After when longer, during which completions are not measured.
26
+ THROTTLE_COOLDOWN = 1.0
27
+ DOWNLOAD_RANGE_BYTES = 8 * MIB
28
+ # Parts asked for in one begin_upload.
29
+ UPLOAD_BATCH_PARTS = 5
30
+ # The most requests cloud storage and downloads have in flight, and a job across all its transfers, unless the
31
+ # caller's max_concurrency or fixed concurrency says otherwise.
32
+ DEFAULT_MAX = 64
33
+
34
+ attr_reader :name, :initial, :start, :min, :soft, :max, :window_attempts, :growth_step, :queue_suppress, :queue_backoff, :latency_floor, :rejected_floor
35
+
36
+ # Cloud storage, such as S3. :default starts at one request and warms up toward 32; :aggressive starts at 32.
37
+ def self.storage_upload(startup: :default, max_concurrency: nil)
38
+ new(name: :storage, initial: startup == :aggressive ? 32 : 1, start: 32, min: 4, soft: 32, max: max_concurrency || DEFAULT_MAX,
39
+ growth_step: 2, queue_suppress: 20, queue_backoff: 34, latency_floor: 12, rejected_floor: 12, warm: startup == :default
40
+ )
41
+ end
42
+
43
+ # A destination the API reports a direct connection for, such as an agent. Its queue thresholds are cloud
44
+ # storage's 20 and 34, scaled to its maximum of 16.
45
+ def self.direct_upload(max_concurrency: nil)
46
+ new(name: :direct, initial: 4, start: 4, min: 1, soft: 16, max: [ 16, max_concurrency || 16 ].min,
47
+ growth_step: 1, queue_suppress: 5, queue_backoff: 9, latency_floor: 1, rejected_floor: 1
48
+ )
49
+ end
50
+
51
+ def self.download(max_concurrency: nil)
52
+ new(name: :download, initial: 32, start: 32, min: 4, soft: 32, max: max_concurrency || DEFAULT_MAX,
53
+ growth_step: 2, queue_suppress: 20, queue_backoff: 34, latency_floor: 12, rejected_floor: 12
54
+ )
55
+ end
56
+
57
+ def self.fixed(concurrency)
58
+ new(name: :fixed, initial: concurrency, start: concurrency, min: concurrency, soft: concurrency, max: concurrency,
59
+ growth_step: 0, queue_suppress: concurrency, queue_backoff: concurrency, latency_floor: concurrency, rejected_floor: concurrency, fixed: true
60
+ )
61
+ end
62
+
63
+ def initialize(name:, initial:, start:, min:, soft:, max:, growth_step:, queue_suppress:, queue_backoff:, latency_floor:, rejected_floor:,
64
+ window_attempts: 16, warm: false, fixed: false)
65
+ @name = name
66
+ @max = max
67
+ @initial = [ initial, max ].min
68
+ @start = [ start, max ].min
69
+ @min = [ min, max ].min
70
+ @soft = [ soft, max ].min
71
+ @window_attempts = window_attempts
72
+ @growth_step = growth_step
73
+ @queue_suppress = queue_suppress
74
+ @queue_backoff = queue_backoff
75
+ @latency_floor = [ latency_floor, max ].min
76
+ @rejected_floor = [ rejected_floor, max ].min
77
+ @warm = warm && @initial < @start
78
+ @fixed = fixed
79
+ freeze
80
+ end
81
+
82
+ def warm?
83
+ @warm
84
+ end
85
+
86
+ def fixed?
87
+ @fixed
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+
5
+ module Files
6
+ module Transfer
7
+ # What a storage response means for the request that got it, and when a signed URL stops working.
8
+ #
9
+ # 429 and 503 are throttles, retried after their whole Retry-After (seconds or an HTTP date) or at once without
10
+ # one; a Retry-After too large to schedule refuses the request instead. 500, 502, 504, a network failure and S3's
11
+ # RequestTimeout error (a 400 whose whole body, of at most 64 KiB, is a complete, DTD-free XML document whose
12
+ # unnamespaced root <Error> has a direct <Code>RequestTimeout</Code>) are retried after 100 ms. Throttles hold the
13
+ # destination back for every request of the job (Holds). Anything else is refused: 501, the other 4xx,
14
+ # 3xx (redirects are never followed) and a success that does not acknowledge the part. Each retry is a new
15
+ # admission, within the caller's Files.max_network_retries, and never for a part whose instructions say
16
+ # retry_parts: false.
17
+ module StorageRetry
18
+ THROTTLE_STATUSES = [ 429, 503 ].freeze
19
+ RETRY_STATUSES = [ 500, 502, 504 ].freeze
20
+ RETRY_DELAY = 0.1
21
+ # Seconds before a signed URL's expiry at which it is renewed rather than used.
22
+ EXPIRY_MARGIN = 5
23
+ FRONTEND_HEADER = "x-files-frontend-app"
24
+ # The most of a 400's body judged, which bounds the XML checked.
25
+ XML_MAX_BYTES = 64 * 1024
26
+ # An XML declaration, accepted only at the very start of the body.
27
+ XML_DECLARATION = /\A<\?xml\s+version\s*=\s*(["'])1\.[0-9]+\1(?:\s+encoding\s*=\s*(["'])[A-Za-z][A-Za-z0-9._-]*\2)?(?:\s+standalone\s*=\s*(["'])(?:yes|no)\3)?\s*\?>/
28
+ # A predefined entity or a character reference: no other entity can be defined, since a DTD is never accepted.
29
+ XML_REFERENCE = '&(?:amp|lt|gt|quot|apos|#[0-9]+|#x\h+);'
30
+ XML_VALUE = %{(?:"(?:[^"<&]|#{XML_REFERENCE})*"|'(?:[^'<&]|#{XML_REFERENCE})*')}.freeze
31
+ # One attribute: its name, with any prefix, and its quoted value.
32
+ XML_ATTRIBUTE = /\s+([A-Za-z_][\w.:-]*)\s*=\s*(#{XML_VALUE})/
33
+ # A start, end or empty-element tag, with its attributes, or character data. A comment, processing instruction,
34
+ # CDATA section or DTD matches nothing, so a body holding one is refused, as is a prefixed element name.
35
+ XML_TOKEN = /\G(?:<(\/?)([A-Za-z_][\w.-]*)((?:\s+[A-Za-z_][\w.:-]*\s*=\s*#{XML_VALUE})*)\s*(\/?)>|((?:[^<&]|#{XML_REFERENCE})+))/
36
+ XML_CHARACTER_REFERENCE = /&#(x\h+|[0-9]+);/
37
+ # Characters XML never allows: the C0 controls other than tab, newline and carriage return.
38
+ XML_FORBIDDEN = /[\x00-\x08\x0B\x0C\x0E-\x1F]/n
39
+ SIGNATURE_PARAMS = [ %w[x-amz-date x-amz-expires], %w[x-files-date x-files-expires], %w[x-goog-date x-goog-expires] ].freeze
40
+
41
+ # [:throttle, seconds], [:retry, seconds] or [:refuse] for a response that is not a success. headers are
42
+ # lowercase names, each with its values. truncated says the body is only the first part of a longer one, which a
43
+ # transport keeps no more of: such a 400 is never taken for RequestTimeout, since its whole document was not seen.
44
+ def self.classify(status, headers, body, truncated: false)
45
+ if THROTTLE_STATUSES.include?(status)
46
+ seconds = retry_after(headers.fetch("retry-after", []).first)
47
+ return seconds.finite? ? [ :throttle, seconds ] : [ :refuse ]
48
+ end
49
+ return [ :retry, RETRY_DELAY ] if RETRY_STATUSES.include?(status)
50
+ return [ :retry, RETRY_DELAY ] if status == 400 && !truncated && request_timeout?(body)
51
+
52
+ [ :refuse ]
53
+ end
54
+
55
+ # Seconds to wait, never negative and never shortened. Infinity when the value cannot be scheduled.
56
+ def self.retry_after(value, now: Time.now)
57
+ return 0.0 if value.nil?
58
+
59
+ text = value.strip
60
+ seconds = text.match?(/\A\d+\z/) ? text.to_f : Time.httpdate(text) - now
61
+ seconds.nan? ? Float::INFINITY : [ seconds, 0.0 ].max
62
+ rescue ArgumentError
63
+ 0.0
64
+ end
65
+
66
+ # Whether body is exactly S3's RequestTimeout error: a well-formed XML document of at most 64 KiB in UTF-8, with
67
+ # no DTD, comment, processing instruction (but an XML declaration at its start), CDATA section or entity but the
68
+ # predefined ones, no namespace (neither an xmlns declaration nor a prefix on any element or attribute), no
69
+ # repeated attribute and no character XML disallows, whose root is <Error> with a single direct <Code> child
70
+ # holding only RequestTimeout. A nested or repeated Code, another code, or anything else is not.
71
+ def self.request_timeout?(body)
72
+ return false unless body.is_a?(String) && body.bytesize <= XML_MAX_BYTES
73
+
74
+ text = body.b
75
+ utf8 = text.dup.force_encoding(Encoding::UTF_8)
76
+ return false unless utf8.valid_encoding? && !text.match?(XML_FORBIDDEN) && !utf8.match?(/[￾￿]/)
77
+
78
+ text = text.sub(XML_DECLARATION, "").strip
79
+ stack = []
80
+ codes = []
81
+ rooted = false
82
+ position = 0
83
+ while position < text.bytesize
84
+ token = XML_TOKEN.match(text, position)
85
+ return false unless token&.begin(0) == position
86
+
87
+ position = token.end(0)
88
+ closing, name, attributes, empty, content = token.captures
89
+ if content
90
+ return false unless (stack.any? || content.strip.empty?) && valid_xml_text?(content)
91
+
92
+ codes[-1] << content if stack.size == 2 && stack.last == "Code" && codes[-1]
93
+ elsif closing == "/"
94
+ return false unless attributes.empty? && empty.empty? && stack.pop == name
95
+ else
96
+ return false unless valid_xml_attributes?(attributes)
97
+ return false if stack.empty? && (rooted || name != "Error")
98
+
99
+ rooted = true if stack.empty?
100
+ codes[-1] = nil if stack.size == 2 && stack.last == "Code"
101
+ codes << +"" if stack.size == 1 && name == "Code"
102
+ stack << name unless empty == "/"
103
+ end
104
+ end
105
+ rooted && stack.empty? && codes == [ "RequestTimeout" ]
106
+ end
107
+
108
+ # A tag's attributes: none in a namespace (no xmlns declaration, no prefix), none named twice, and each value well
109
+ # formed.
110
+ def self.valid_xml_attributes?(attributes)
111
+ names = []
112
+ attributes.scan(XML_ATTRIBUTE) do |name, value|
113
+ return false if name == "xmlns" || name.include?(":") || names.include?(name) || !valid_xml_text?(value)
114
+
115
+ names << name
116
+ end
117
+ true
118
+ end
119
+
120
+ # Character data or an attribute value: without "]]>", and with character references only to characters XML
121
+ # allows.
122
+ def self.valid_xml_text?(text)
123
+ !text.include?("]]>") && text.scan(XML_CHARACTER_REFERENCE).all? do |(digits)|
124
+ code = digits.start_with?("x") ? digits[1..].to_i(16) : digits.to_i
125
+ [ 0x9, 0xA, 0xD ].include?(code) || code.between?(0x20, 0xD7FF) || code.between?(0xE000, 0xFFFD) || code.between?(0x10000, 0x10FFFF)
126
+ end
127
+ end
128
+
129
+ def self.frontend?(headers)
130
+ headers.key?(FRONTEND_HEADER)
131
+ end
132
+
133
+ # The earliest of the API's expires and the expiry the signed URL states: S3's X-Amz-Date plus X-Amz-Expires,
134
+ # the same X-Files- and X-Goog- parameters, or an Azure SAS's se. nil when neither says.
135
+ def self.expires_at(url, api_expires = nil)
136
+ times = [ api_time(api_expires) ]
137
+ # Names compare without case, and the last of the parameters with a name wins, in the order the URL gives them.
138
+ query = {}
139
+ ::URI.decode_www_form(::URI.parse(url).query.to_s).each { |name, value| query[name.downcase] = value }
140
+ SIGNATURE_PARAMS.each do |date, lifetime|
141
+ signed = compact_time(query[date])
142
+ times << (signed + Integer(query[lifetime], 10)) if signed && query[lifetime].to_s.match?(/\A\d+\z/)
143
+ end
144
+ times << api_time(query["se"])
145
+ times.compact.min
146
+ rescue ArgumentError, ::URI::InvalidURIError
147
+ api_time(api_expires)
148
+ end
149
+
150
+ def self.expired?(expires_at, now: Time.now)
151
+ !expires_at.nil? && now >= expires_at - EXPIRY_MARGIN
152
+ end
153
+
154
+ def self.compact_time(text)
155
+ match = text.to_s.match(/\A(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z\z/)
156
+ match && Time.utc(*match.captures.map(&:to_i))
157
+ end
158
+
159
+ def self.api_time(value)
160
+ case value
161
+ when Time then value
162
+ when String then Time.iso8601(value)
163
+ end
164
+ rescue ArgumentError
165
+ nil
166
+ end
167
+ end
168
+ end
169
+ end