@dignetwork/dig-sdk 0.0.1-alpha.100 → 0.0.1-alpha.101

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.
@@ -1,10 +1,11 @@
1
+ import http from "http";
2
+ import { Readable } from "stream";
1
3
  export declare class ContentServer {
2
4
  private ipAddress;
3
5
  private storeId;
4
6
  private static certPath;
5
7
  private static keyPath;
6
8
  private static readonly port;
7
- private static readonly inactivityTimeout;
8
9
  constructor(ipAddress: string, storeId: string);
9
10
  getKey(key: string, rootHash: string, challengeHex?: string): Promise<string>;
10
11
  getPaymentAddress(): Promise<string | null>;
@@ -14,18 +15,19 @@ export declare class ContentServer {
14
15
  getKeysIndex(rootHash?: string): Promise<any>;
15
16
  headKey(key: string, rootHash?: string): Promise<{
16
17
  success: boolean;
17
- headers?: any;
18
+ headers?: http.IncomingHttpHeaders;
18
19
  }>;
19
20
  headStore(options?: {
20
21
  hasRootHash: string;
21
22
  }): Promise<{
22
23
  success: boolean;
23
- headers?: any;
24
+ headers?: http.IncomingHttpHeaders;
24
25
  }>;
25
26
  hasRootHash(rootHash: string): Promise<boolean>;
27
+ streamKey(key: string, rootHash?: string): Promise<Readable>;
26
28
  private head;
27
29
  private fetchJson;
30
+ private fetchWithRetries;
28
31
  private fetch;
29
- private getAxiosConfig;
30
32
  }
31
33
  //# sourceMappingURL=ContentServer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ContentServer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/ContentServer.ts"],"names":[],"mappings":"AAMA,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAS;IAC/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAQ;IACpC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;gBAErC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAYjC,MAAM,CACjB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC;IAWL,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAc3C,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC;IAM5B,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAM9B,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAM9B,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAU7C,OAAO,CAClB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAUlC,SAAS,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QACjE,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,CAAC,EAAE,GAAG,CAAC;KACf,CAAC;IAQW,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAW9C,IAAI;YAcJ,SAAS;YAMT,KAAK;IA+DnB,OAAO,CAAC,cAAc;CAUvB"}
1
+ {"version":3,"file":"ContentServer.d.ts","sourceRoot":"","sources":["../../src/DigNetwork/ContentServer.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAGlC,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAS;IAC/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAQ;gBAExB,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAYjC,MAAM,CACjB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC;IAaL,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAe3C,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC;IAM5B,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAM9B,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC;IAM9B,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAY7C,OAAO,CAClB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAA;KAAE,CAAC;IAYvD,SAAS,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QACjE,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAC;KACpC,CAAC;IAUW,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUrD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;YAgDrD,IAAI;YA0EJ,SAAS;YAMT,gBAAgB;YAiChB,KAAK;CA6GpB"}
@@ -4,9 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ContentServer = void 0;
7
- const axios_1 = __importDefault(require("axios"));
8
7
  const fs_1 = __importDefault(require("fs"));
9
- const https_1 = __importDefault(require("https"));
8
+ const http_1 = __importDefault(require("http"));
9
+ const url_1 = require("url");
10
10
  const ssl_1 = require("../utils/ssl");
11
11
  class ContentServer {
12
12
  constructor(ipAddress, storeId) {
@@ -20,11 +20,13 @@ class ContentServer {
20
20
  }
21
21
  // Method to get the content of a specified key from the peer, with optional challenge query
22
22
  async getKey(key, rootHash, challengeHex) {
23
+ // Construct the base URL
23
24
  let url = `https://${this.ipAddress}:${ContentServer.port}/chia.${this.storeId}.${rootHash}/${key}`;
25
+ // If a challenge is provided, append it as a query parameter
24
26
  if (challengeHex) {
25
27
  url += `?challenge=${challengeHex}`;
26
28
  }
27
- return this.fetch(url);
29
+ return this.fetchWithRetries(url);
28
30
  }
29
31
  // Method to get the payment address from the peer
30
32
  async getPaymentAddress() {
@@ -88,89 +90,219 @@ class ContentServer {
88
90
  }
89
91
  return false;
90
92
  }
91
- // Helper method to perform HEAD requests using axios
92
- async head(url) {
93
- try {
94
- const config = this.getAxiosConfig();
95
- const response = await axios_1.default.head(url, config);
96
- return {
97
- success: response.status >= 200 && response.status < 300,
98
- headers: response.headers,
99
- };
100
- }
101
- catch (error) {
102
- return { success: false };
93
+ streamKey(key, rootHash) {
94
+ let udi = `chia.${this.storeId}`;
95
+ if (rootHash) {
96
+ udi += `.${rootHash}`;
103
97
  }
98
+ return new Promise((resolve, reject) => {
99
+ const url = `https://${this.ipAddress}:${ContentServer.port}/${udi}/${key}`;
100
+ const urlObj = new url_1.URL(url);
101
+ const requestOptions = {
102
+ hostname: urlObj.hostname,
103
+ port: urlObj.port || ContentServer.port,
104
+ path: urlObj.pathname + urlObj.search,
105
+ method: "GET",
106
+ };
107
+ const request = http_1.default.request(requestOptions, (response) => {
108
+ if (response.statusCode === 200) {
109
+ resolve(response); // Resolve with the readable stream
110
+ }
111
+ else if (response.statusCode === 301 || response.statusCode === 302) {
112
+ // Handle redirects
113
+ const redirectUrl = response.headers.location;
114
+ if (redirectUrl) {
115
+ this.streamKey(redirectUrl).then(resolve).catch(reject);
116
+ }
117
+ else {
118
+ reject(new Error("Redirected without a location header"));
119
+ }
120
+ }
121
+ else {
122
+ reject(new Error(`Failed to retrieve data from ${url}. Status code: ${response.statusCode}`));
123
+ }
124
+ });
125
+ request.on("error", (error) => {
126
+ console.error(`GET Request error for ${url}:`, error);
127
+ reject(error);
128
+ });
129
+ request.end();
130
+ });
131
+ }
132
+ // Helper method to perform HEAD requests
133
+ async head(url, maxRedirects = 5) {
134
+ return new Promise((resolve, reject) => {
135
+ try {
136
+ // Parse the input URL
137
+ const urlObj = new url_1.URL(url);
138
+ const requestOptions = {
139
+ hostname: urlObj.hostname,
140
+ port: urlObj.port ||
141
+ (urlObj.protocol === "http:" ? 80 : ContentServer.port),
142
+ path: urlObj.pathname + urlObj.search,
143
+ method: "HEAD",
144
+ key: fs_1.default.readFileSync(ContentServer.keyPath),
145
+ cert: fs_1.default.readFileSync(ContentServer.certPath),
146
+ rejectUnauthorized: false,
147
+ };
148
+ const request = http_1.default.request(requestOptions, (response) => {
149
+ const { statusCode, headers } = response;
150
+ // If status code is 2xx, return success
151
+ if (statusCode && statusCode >= 200 && statusCode < 300) {
152
+ resolve({ success: true, headers });
153
+ }
154
+ // Handle 3xx redirection
155
+ else if (statusCode &&
156
+ statusCode >= 300 &&
157
+ statusCode < 400 &&
158
+ headers.location) {
159
+ if (maxRedirects > 0) {
160
+ let redirectUrl = headers.location;
161
+ // Check if the redirect URL is relative
162
+ if (!/^https?:\/\//i.test(redirectUrl)) {
163
+ // Resolve the relative URL based on the original URL
164
+ redirectUrl = new url_1.URL(redirectUrl, url).toString();
165
+ console.log(`Resolved relative redirect to: ${redirectUrl}`);
166
+ }
167
+ else {
168
+ console.log(`Redirecting to: ${redirectUrl}`);
169
+ }
170
+ // Recursively follow the redirection
171
+ this.head(redirectUrl, maxRedirects - 1)
172
+ .then(resolve)
173
+ .catch(reject);
174
+ }
175
+ else {
176
+ reject({ success: false, message: "Too many redirects" });
177
+ }
178
+ }
179
+ else {
180
+ // For other status codes, consider it a failure
181
+ resolve({ success: false });
182
+ }
183
+ });
184
+ request.on("error", (error) => {
185
+ console.error(`HEAD ${url}:`, error.message);
186
+ reject({ success: false });
187
+ });
188
+ request.end();
189
+ }
190
+ catch (err) {
191
+ console.error(`Invalid URL: ${url}`, err);
192
+ reject({ success: false, message: "Invalid URL" });
193
+ }
194
+ });
104
195
  }
105
196
  // Helper method to fetch JSON data from a URL
106
197
  async fetchJson(url) {
107
- const response = await this.fetch(url);
198
+ const response = await this.fetchWithRetries(url);
108
199
  return JSON.parse(response);
109
200
  }
110
- // Core method to fetch content from a URL with a 5-second inactivity timeout, handling redirects
111
- async fetch(url) {
112
- const config = this.getAxiosConfig();
113
- // Create a cancel token for the inactivity timeout
114
- const source = axios_1.default.CancelToken.source();
115
- return new Promise((resolve, reject) => {
116
- let timeout = null;
117
- const resetTimeout = () => {
118
- if (timeout) {
119
- clearTimeout(timeout);
201
+ // Helper method to fetch content with retries and redirection handling
202
+ async fetchWithRetries(url) {
203
+ let attempt = 0;
204
+ const maxRetries = 1;
205
+ const initialDelay = 2000; // 2 seconds
206
+ const maxDelay = 10000; // 10 seconds
207
+ const delayMultiplier = 1.5;
208
+ let delay = initialDelay;
209
+ while (attempt < maxRetries) {
210
+ try {
211
+ return await this.fetch(url);
212
+ }
213
+ catch (error) {
214
+ if (attempt < maxRetries - 1) {
215
+ console.warn(`Attempt ${attempt + 1} failed: ${error.message}. Retrying in ${delay / 1000} seconds...`);
216
+ await new Promise((resolve) => setTimeout(resolve, delay));
217
+ delay = Math.min(maxDelay, delay * delayMultiplier);
120
218
  }
121
- timeout = setTimeout(() => {
122
- source.cancel(`Request timed out after ${ContentServer.inactivityTimeout / 1000} seconds of inactivity`);
123
- }, ContentServer.inactivityTimeout);
219
+ else {
220
+ console.error(`Failed to retrieve data from ${url}. Aborting.`);
221
+ throw new Error(`Failed to retrieve data: ${error.message}`);
222
+ }
223
+ }
224
+ attempt++;
225
+ }
226
+ throw new Error(`Failed to retrieve data from ${url} after ${maxRetries} attempts.`);
227
+ }
228
+ // Core method to fetch content from a URL with a 5-second inactivity timeout
229
+ async fetch(url, maxRedirects = 5) {
230
+ return new Promise((resolve, reject) => {
231
+ const urlObj = new url_1.URL(url);
232
+ const timeoutDuration = 5000; // 5 seconds
233
+ let timeout = null; // Initialize timeout
234
+ const requestOptions = {
235
+ hostname: urlObj.hostname,
236
+ port: urlObj.port || ContentServer.port,
237
+ path: urlObj.pathname + urlObj.search, // Include query params
238
+ method: "GET",
239
+ key: fs_1.default.readFileSync(ContentServer.keyPath),
240
+ cert: fs_1.default.readFileSync(ContentServer.certPath),
241
+ rejectUnauthorized: false,
124
242
  };
125
- axios_1.default
126
- .get(url, {
127
- ...config,
128
- cancelToken: source.token,
129
- responseType: "stream", // Use stream to track data transfer
130
- maxRedirects: 5, // Axios follows up to 5 redirects by default
131
- })
132
- .then((response) => {
243
+ const request = http_1.default.request(requestOptions, (response) => {
133
244
  let data = "";
134
- response.data.on("data", (chunk) => {
135
- data += chunk;
136
- resetTimeout(); // Reset the timeout when data is received
137
- });
138
- response.data.on("end", () => {
245
+ // Set timeout for inactivity
246
+ timeout = setTimeout(() => {
247
+ console.error(`Request timeout: No data received for ${timeoutDuration / 1000} seconds.`);
248
+ request.destroy(); // Use destroy instead of abort
249
+ reject(new Error(`Request timed out after ${timeoutDuration / 1000} seconds of inactivity`));
250
+ }, timeoutDuration);
251
+ const resetTimeout = () => {
139
252
  if (timeout) {
140
- clearTimeout(timeout); // Clear timeout on stream end
253
+ clearTimeout(timeout);
141
254
  }
142
- resolve(data);
143
- });
144
- response.data.on("error", (error) => {
255
+ timeout = setTimeout(() => {
256
+ console.error(`Request timeout: No data received for ${timeoutDuration / 1000} seconds.`);
257
+ request.destroy(); // Use destroy instead of abort
258
+ reject(new Error(`Request timed out after ${timeoutDuration / 1000} seconds of inactivity`));
259
+ }, timeoutDuration);
260
+ };
261
+ if (response.statusCode === 200) {
262
+ response.on("data", (chunk) => {
263
+ data += chunk;
264
+ resetTimeout(); // Reset the timeout every time data is received
265
+ });
266
+ response.on("end", () => {
267
+ if (timeout) {
268
+ clearTimeout(timeout);
269
+ }
270
+ resolve(data);
271
+ });
272
+ }
273
+ else if ((response.statusCode === 301 || response.statusCode === 302) &&
274
+ response.headers.location) {
275
+ // Handle redirects
276
+ if (maxRedirects > 0) {
277
+ const redirectUrl = new url_1.URL(response.headers.location, url); // Resolve relative URLs based on the original URL
278
+ if (timeout) {
279
+ clearTimeout(timeout);
280
+ }
281
+ this.fetch(redirectUrl.toString(), maxRedirects - 1)
282
+ .then(resolve)
283
+ .catch(reject);
284
+ }
285
+ else {
286
+ reject(new Error("Too many redirects"));
287
+ }
288
+ }
289
+ else {
145
290
  if (timeout) {
146
291
  clearTimeout(timeout);
147
292
  }
148
- reject(error);
149
- });
150
- })
151
- .catch((error) => {
152
- if (axios_1.default.isCancel(error)) {
153
- console.error("Request canceled:", error.message);
293
+ reject(new Error(`Failed to retrieve data from ${url}. Status code: ${response.statusCode}`));
154
294
  }
155
- else {
156
- console.error(`Failed to retrieve data from ${url}:`, error.message);
295
+ });
296
+ request.on("error", (error) => {
297
+ if (timeout) {
298
+ clearTimeout(timeout);
157
299
  }
300
+ console.error(`GET ${url}:`, error.message);
158
301
  reject(error);
159
302
  });
303
+ request.end();
160
304
  });
161
305
  }
162
- // Helper method to configure axios with HTTPS settings
163
- getAxiosConfig() {
164
- return {
165
- httpsAgent: new https_1.default.Agent({
166
- cert: fs_1.default.readFileSync(ContentServer.certPath),
167
- key: fs_1.default.readFileSync(ContentServer.keyPath),
168
- rejectUnauthorized: false,
169
- }),
170
- timeout: 0, // Disable axios timeout (we're using inactivity timeout)
171
- };
172
- }
173
306
  }
174
307
  exports.ContentServer = ContentServer;
175
308
  ContentServer.port = 4161;
176
- ContentServer.inactivityTimeout = 5000; // Inactivity timeout in milliseconds (5 seconds)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dignetwork/dig-sdk",
3
- "version": "0.0.1-alpha.100",
3
+ "version": "0.0.1-alpha.101",
4
4
  "description": "",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/index.js",