cos-operation.ts 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344
  1. /** Total deadlines and request cleanup for one qualification COS operation. */
  2. import * as http from 'node:http'
  3. import * as https from 'node:https'
  4. import type COS from 'cos-nodejs-sdk-v5'
  5. /**
  6. * Run one SDK operation with a shared deadline across its HTTP attempts.
  7. * @param cos Client dedicated to this operation, with no concurrent users.
  8. * @param timeoutMs Total network-operation budget in milliseconds.
  9. * @param operation SDK call whose result is returned after its requests close.
  10. * @returns SDK result; expiration rejects with TimeoutError after aborting the transport.
  11. */
  12. export async function cosOperation<T>(cos: COS, timeoutMs: number, operation: () => Promise<T>): Promise<T> {
  13. const controller = new AbortController()
  14. const timeout = new Error('installed update: COS operation exceeded its deadline')
  15. timeout.name = 'TimeoutError'
  16. const closed: Promise<void>[] = []
  17. const wrap = (transport: typeof http | typeof https) => ({
  18. ...transport,
  19. request(options: http.RequestOptions) {
  20. const request = transport.request({ ...options, signal: controller.signal })
  21. closed.push(new Promise<void>((resolve) => { request.once('close', resolve) }))
  22. return request
  23. },
  24. })
  25. const modules = { 'http:': wrap(http), 'https:': wrap(https) }
  26. // cos-request forwards httpModules to its native request layer, including every SDK retry.
  27. const configure = (options: { httpModules?: typeof modules }): void => { options.httpModules = modules }
  28. cos.on('before-send', configure)
  29. const timer = setTimeout(() => { controller.abort(timeout) }, timeoutMs)
  30. try {
  31. const result = await operation()
  32. if (controller.signal.aborted) throw timeout
  33. return result
  34. } catch (error) {
  35. if (controller.signal.aborted) throw timeout
  36. throw error
  37. } finally {
  38. clearTimeout(timer)
  39. controller.abort()
  40. await Promise.all(closed)
  41. cos.off('before-send', configure)
  42. }
  43. }