diff --git a/lib/clouddrop-upload.js b/lib/clouddrop-upload.js index 5b38b58..afa83ee 100644 --- a/lib/clouddrop-upload.js +++ b/lib/clouddrop-upload.js @@ -1,7 +1,7 @@ const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); -const { request } = require('undici'); +const { request, Agent } = require('undici'); const BASE_URL = 'https://clouddrop.cc'; const API_BASE = `${BASE_URL}/api/cloud`; @@ -15,6 +15,16 @@ const COMPLETE_TIMEOUT = 5 * 60_000; const SHARE_TIMEOUT = 30_000; const SIMPLE_UPLOAD_TIMEOUT = 30 * 60_000; +// Cap concurrent TCP connections to clouddrop.cc at 50 to stay well under +// the server's per-IP limit of 100 concurrent connections (cd_conn). +// Shared across all ClouddropUploader instances via module-level agent. +const clouddropAgent = new Agent({ + connections: 50, + pipelining: 1, + keepAliveTimeout: 30_000, + keepAliveMaxTimeout: 60_000 +}); + /** * Clouddrop.cc uploader — uses API Key (Bearer) authentication. * Files > 16 MB use the chunked protocol, smaller files use simple upload. @@ -115,6 +125,7 @@ class ClouddropUploader { const res = await request(`${API_BASE}/upload?mode=rename`, { method: 'POST', + dispatcher: clouddropAgent, body: generate(), signal, headers: this._headers({ @@ -138,6 +149,7 @@ class ClouddropUploader { // 1. Init session const initRes = await request(`${API_BASE}/upload/init`, { method: 'POST', + dispatcher: clouddropAgent, signal, headers: this._headers({ 'Content-Type': 'application/json' }), body: JSON.stringify({ filename: fileName, size: fileSize, parentId: null }), @@ -167,6 +179,7 @@ class ClouddropUploader { const chunkRes = await request(`${API_BASE}/upload/${sessionId}/chunk/${i}`, { method: 'PUT', + dispatcher: clouddropAgent, signal, body: buf, headers: this._headers({ @@ -188,6 +201,7 @@ class ClouddropUploader { // 3. Complete session const completeRes = await request(`${API_BASE}/upload/${sessionId}/complete`, { method: 'POST', + dispatcher: clouddropAgent, signal, headers: this._headers({ 'Content-Type': 'application/json' }), body: '{}', @@ -206,6 +220,7 @@ class ClouddropUploader { async _createShareLink(fileId, signal) { const res = await request(`${API_BASE}/share-link`, { method: 'POST', + dispatcher: clouddropAgent, signal, headers: this._headers({ 'Content-Type': 'application/json' }), body: JSON.stringify({ fileId, expiry: 'never' }), @@ -223,6 +238,7 @@ class ClouddropUploader { if (!this.apiKey) throw new Error('Clouddrop: API-Key fehlt'); const res = await request(`${API_BASE}/files?limit=1`, { method: 'GET', + dispatcher: clouddropAgent, signal, headers: this._headers(), headersTimeout: 15_000,