hbsig 0.2.4 → 0.2.5

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.
package/cjs/httpsig.js CHANGED
@@ -348,48 +348,36 @@ function encodeBodyPart(partName, bodyPart, inlineKey) {
348
348
  return "";
349
349
  }
350
350
 
351
- // Improved helper to detect if data is likely binary
352
- function isBinaryData(data) {
353
- var buf;
354
- if (typeof data === "string") {
355
- // Convert string to buffer using binary encoding to check byte values
356
- buf = Buffer.from(data, "binary");
357
- } else if (Buffer.isBuffer(data)) {
358
- buf = data;
359
- } else {
360
- return false;
351
+ // Helper to detect if a Buffer contains binary data
352
+ function isBinaryData(buf) {
353
+ // Check first 512 bytes for binary indicators
354
+ var checkLength = Math.min(buf.length, 512);
355
+
356
+ // Any null byte means binary
357
+ for (var i = 0; i < checkLength; i++) {
358
+ if (buf[i] === 0) return true;
361
359
  }
362
360
 
363
- // Check a larger sample - double to 1024 bytes for better detection
364
- var sampleSize = Math.min(buf.length, 1024);
365
- var nullCount = 0;
361
+ // Count non-text bytes
366
362
  var controlCount = 0;
367
363
  var highByteCount = 0;
368
- for (var i = 0; i < sampleSize; i++) {
369
- var _byte = buf[i];
370
-
371
- // Null bytes are a strong indicator of binary
372
- if (_byte === 0) {
373
- nullCount++;
374
- if (nullCount > 0) return true; // Even one null byte indicates binary
375
- }
376
-
377
- // Control characters (except common text ones: TAB, LF, CR)
364
+ for (var _i8 = 0; _i8 < checkLength; _i8++) {
365
+ var _byte = buf[_i8];
366
+ // Non-printable chars (except CR/LF/TAB)
378
367
  if (_byte < 32 && _byte !== 9 && _byte !== 10 && _byte !== 13) {
379
368
  controlCount++;
380
369
  }
381
-
382
- // High byte values that aren't valid UTF-8 continuation bytes
370
+ // High byte range
383
371
  if (_byte > 127) {
384
372
  highByteCount++;
385
373
  }
386
374
  }
387
375
 
388
- // If more than 10% of bytes are control chars, likely binary
389
- if (controlCount / sampleSize > 0.1) return true;
376
+ // If more than 5% are control chars, likely binary
377
+ if (controlCount / checkLength > 0.05) return true;
390
378
 
391
- // If more than 30% are high bytes without valid UTF-8 sequences, likely binary
392
- if (highByteCount / sampleSize > 0.3) return true;
379
+ // If more than 20% are high bytes, likely binary
380
+ if (highByteCount / checkLength > 0.2) return true;
393
381
  return false;
394
382
  }
395
383
 
@@ -488,32 +476,30 @@ function parseMultipart(contentType, body) {
488
476
  searchPos = nextNewline + 1;
489
477
  }
490
478
 
491
- // Extract the value
479
+ // Extract the value - valueEnd points to \n which is NOT part of the value
492
480
  var value = headerBlock.substring(valueStart, valueEnd);
493
481
 
494
- // Only trim trailing newlines if we actually found a next header
495
- // (valueEnd < headerBlock.length means we found a boundary)
496
- if (valueEnd < headerBlock.length) {
497
- // Trim trailing CRLF or LF only at the boundary
498
- if (value.endsWith("\r\n")) {
499
- value = value.substring(0, value.length - 2);
500
- } else if (value.endsWith("\n")) {
501
- value = value.substring(0, value.length - 1);
502
- } else if (value.endsWith("\r")) {
482
+ // Determine if this is binary data FIRST (before trimming)
483
+ var isBinary = value.length > 0 && isBinaryData(Buffer.from(value, "binary"));
484
+ if (isBinary) {
485
+ // Binary data: only remove trailing \r if it's part of CRLF separator
486
+ // (when valueEnd points to \n and value ends with \r)
487
+ if (valueEnd < headerBlock.length && headerBlock[valueEnd] === "\n" && value.endsWith("\r")) {
503
488
  value = value.substring(0, value.length - 1);
504
489
  }
505
- }
506
-
507
- // Determine if this is binary data
508
- if (value.length > 0 && isBinaryData(value)) {
509
490
  headers[name] = Buffer.from(value, "binary");
510
491
  } else {
492
+ // Text data: remove all trailing CRLF/LF (these are line separators, not data)
493
+ value = value.replace(/[\r\n]+$/, "");
511
494
  headers[name] = value;
512
495
  }
513
496
 
514
497
  // Move to the end of this header's value
515
- currentPos = valueEnd + 1;
516
- if (headerBlock[valueEnd] === "\r") {
498
+ currentPos = valueEnd;
499
+ if (currentPos < headerBlock.length && headerBlock[currentPos] === "\n") {
500
+ currentPos++;
501
+ }
502
+ if (currentPos < headerBlock.length && headerBlock[currentPos] === "\r") {
517
503
  currentPos++;
518
504
  }
519
505
  }
@@ -530,8 +516,8 @@ function parseMultipart(contentType, body) {
530
516
  delete restHeaders["content-disposition"];
531
517
 
532
518
  // Add each header from the inline part to the top level of result
533
- for (var _i8 = 0, _Object$entries8 = Object.entries(restHeaders); _i8 < _Object$entries8.length; _i8++) {
534
- var _Object$entries8$_i = _slicedToArray(_Object$entries8[_i8], 2),
519
+ for (var _i9 = 0, _Object$entries8 = Object.entries(restHeaders); _i9 < _Object$entries8.length; _i9++) {
520
+ var _Object$entries8$_i = _slicedToArray(_Object$entries8[_i9], 2),
535
521
  key = _Object$entries8$_i[0],
536
522
  _value2 = _Object$entries8$_i[1];
537
523
  result[key] = _value2;
@@ -539,8 +525,8 @@ function parseMultipart(contentType, body) {
539
525
 
540
526
  // If there's body content in the inline part, add it as 'body'
541
527
  if (partBody) {
542
- // Keep as Buffer if it's binary data
543
- result[partName] = isBinaryData(partBody) ? Buffer.from(partBody, "binary") : partBody;
528
+ // Keep as string unless it's binary data
529
+ result[partName] = isBinaryData(Buffer.from(partBody, "binary")) ? Buffer.from(partBody, "binary") : partBody;
544
530
  }
545
531
  } else {
546
532
  // Handle named form-data parts
@@ -554,15 +540,15 @@ function parseMultipart(contentType, body) {
554
540
  var _restHeaders = _objectSpread({}, headers);
555
541
  delete _restHeaders["content-disposition"];
556
542
  if (Object.keys(_restHeaders).length === 0) {
557
- // Keep as Buffer if it's binary data
558
- result[partName] = isBinaryData(partBody) ? Buffer.from(partBody, "binary") : partBody;
543
+ // Keep as string unless it's binary data
544
+ result[partName] = isBinaryData(Buffer.from(partBody, "binary")) ? Buffer.from(partBody, "binary") : partBody;
559
545
  } else if (!partBody) {
560
546
  // ao-types should stay with this part, not be extracted
561
547
  result[partName] = _restHeaders;
562
548
  } else {
563
- // Keep as Buffer if it's binary data
549
+ // Keep as string unless it's binary data
564
550
  result[partName] = _objectSpread(_objectSpread({}, _restHeaders), {}, {
565
- body: isBinaryData(partBody) ? Buffer.from(partBody, "binary") : partBody
551
+ body: isBinaryData(Buffer.from(partBody, "binary")) ? Buffer.from(partBody, "binary") : partBody
566
552
  });
567
553
  }
568
554
  }
@@ -639,8 +625,8 @@ function httpsig_from(http) {
639
625
  // Convert flat structure to nested using flat.js
640
626
  var flat = {};
641
627
  var nonFlat = {};
642
- for (var _i9 = 0, _Object$entries9 = Object.entries(withBodyKeys); _i9 < _Object$entries9.length; _i9++) {
643
- var _Object$entries9$_i = _slicedToArray(_Object$entries9[_i9], 2),
628
+ for (var _i0 = 0, _Object$entries9 = Object.entries(withBodyKeys); _i0 < _Object$entries9.length; _i0++) {
629
+ var _Object$entries9$_i = _slicedToArray(_Object$entries9[_i0], 2),
644
630
  key = _Object$entries9$_i[0],
645
631
  value = _Object$entries9$_i[1];
646
632
  if (key.includes("/")) {
@@ -675,8 +661,8 @@ function httpsig_from(http) {
675
661
  delete result["content-digest"];
676
662
 
677
663
  // Extract hashpaths if any
678
- for (var _i0 = 0, _Object$keys = Object.keys(result); _i0 < _Object$keys.length; _i0++) {
679
- var _key2 = _Object$keys[_i0];
664
+ for (var _i1 = 0, _Object$keys = Object.keys(result); _i1 < _Object$keys.length; _i1++) {
665
+ var _key2 = _Object$keys[_i1];
680
666
  if (_key2.startsWith("hashpath")) {
681
667
  delete result[_key2];
682
668
  }
@@ -716,8 +702,8 @@ function httpsig_to(tabm) {
716
702
  // For flat structures, just return with normalized keys
717
703
  // This matches Erlang which returns the map unchanged
718
704
  var result = _objectSpread({}, inlineFieldHdrs);
719
- for (var _i1 = 0, _Object$entries0 = Object.entries(stripped); _i1 < _Object$entries0.length; _i1++) {
720
- var _Object$entries0$_i = _slicedToArray(_Object$entries0[_i1], 2),
705
+ for (var _i10 = 0, _Object$entries0 = Object.entries(stripped); _i10 < _Object$entries0.length; _i10++) {
706
+ var _Object$entries0$_i = _slicedToArray(_Object$entries0[_i10], 2),
721
707
  key = _Object$entries0$_i[0],
722
708
  value = _Object$entries0$_i[1];
723
709
  // Keep Buffers as Buffers - don't convert to strings
@@ -742,8 +728,8 @@ function httpsig_to(tabm) {
742
728
  var headers = _objectSpread({}, inlineFieldHdrs);
743
729
 
744
730
  // Process each field - ao-types at top level should go to headers
745
- for (var _i10 = 0, _Object$entries1 = Object.entries(stripped); _i10 < _Object$entries1.length; _i10++) {
746
- var _Object$entries1$_i = _slicedToArray(_Object$entries1[_i10], 2),
731
+ for (var _i11 = 0, _Object$entries1 = Object.entries(stripped); _i11 < _Object$entries1.length; _i11++) {
732
+ var _Object$entries1$_i = _slicedToArray(_Object$entries1[_i11], 2),
747
733
  _key3 = _Object$entries1$_i[0],
748
734
  _value3 = _Object$entries1$_i[1];
749
735
  if (_key3 === "ao-types") {
package/esm/httpsig.js CHANGED
@@ -291,50 +291,37 @@ function encodeBodyPart(partName, bodyPart, inlineKey) {
291
291
  return ""
292
292
  }
293
293
 
294
- // Improved helper to detect if data is likely binary
295
- function isBinaryData(data) {
296
- let buf
297
-
298
- if (typeof data === "string") {
299
- // Convert string to buffer using binary encoding to check byte values
300
- buf = Buffer.from(data, "binary")
301
- } else if (Buffer.isBuffer(data)) {
302
- buf = data
303
- } else {
304
- return false
294
+ // Helper to detect if a Buffer contains binary data
295
+ function isBinaryData(buf) {
296
+ // Check first 512 bytes for binary indicators
297
+ const checkLength = Math.min(buf.length, 512)
298
+
299
+ // Any null byte means binary
300
+ for (let i = 0; i < checkLength; i++) {
301
+ if (buf[i] === 0) return true
305
302
  }
306
303
 
307
- // Check a larger sample - double to 1024 bytes for better detection
308
- const sampleSize = Math.min(buf.length, 1024)
309
- let nullCount = 0
304
+ // Count non-text bytes
310
305
  let controlCount = 0
311
306
  let highByteCount = 0
312
307
 
313
- for (let i = 0; i < sampleSize; i++) {
308
+ for (let i = 0; i < checkLength; i++) {
314
309
  const byte = buf[i]
315
-
316
- // Null bytes are a strong indicator of binary
317
- if (byte === 0) {
318
- nullCount++
319
- if (nullCount > 0) return true // Even one null byte indicates binary
320
- }
321
-
322
- // Control characters (except common text ones: TAB, LF, CR)
310
+ // Non-printable chars (except CR/LF/TAB)
323
311
  if (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) {
324
312
  controlCount++
325
313
  }
326
-
327
- // High byte values that aren't valid UTF-8 continuation bytes
314
+ // High byte range
328
315
  if (byte > 127) {
329
316
  highByteCount++
330
317
  }
331
318
  }
332
319
 
333
- // If more than 10% of bytes are control chars, likely binary
334
- if (controlCount / sampleSize > 0.1) return true
320
+ // If more than 5% are control chars, likely binary
321
+ if (controlCount / checkLength > 0.05) return true
335
322
 
336
- // If more than 30% are high bytes without valid UTF-8 sequences, likely binary
337
- if (highByteCount / sampleSize > 0.3) return true
323
+ // If more than 20% are high bytes, likely binary
324
+ if (highByteCount / checkLength > 0.2) return true
338
325
 
339
326
  return false
340
327
  }
@@ -445,32 +432,36 @@ function parseMultipart(contentType, body) {
445
432
  searchPos = nextNewline + 1
446
433
  }
447
434
 
448
- // Extract the value
435
+ // Extract the value - valueEnd points to \n which is NOT part of the value
449
436
  let value = headerBlock.substring(valueStart, valueEnd)
450
437
 
451
- // Only trim trailing newlines if we actually found a next header
452
- // (valueEnd < headerBlock.length means we found a boundary)
453
- if (valueEnd < headerBlock.length) {
454
- // Trim trailing CRLF or LF only at the boundary
455
- if (value.endsWith("\r\n")) {
456
- value = value.substring(0, value.length - 2)
457
- } else if (value.endsWith("\n")) {
458
- value = value.substring(0, value.length - 1)
459
- } else if (value.endsWith("\r")) {
438
+ // Determine if this is binary data FIRST (before trimming)
439
+ const isBinary =
440
+ value.length > 0 && isBinaryData(Buffer.from(value, "binary"))
441
+
442
+ if (isBinary) {
443
+ // Binary data: only remove trailing \r if it's part of CRLF separator
444
+ // (when valueEnd points to \n and value ends with \r)
445
+ if (
446
+ valueEnd < headerBlock.length &&
447
+ headerBlock[valueEnd] === "\n" &&
448
+ value.endsWith("\r")
449
+ ) {
460
450
  value = value.substring(0, value.length - 1)
461
451
  }
462
- }
463
-
464
- // Determine if this is binary data
465
- if (value.length > 0 && isBinaryData(value)) {
466
452
  headers[name] = Buffer.from(value, "binary")
467
453
  } else {
454
+ // Text data: remove all trailing CRLF/LF (these are line separators, not data)
455
+ value = value.replace(/[\r\n]+$/, "")
468
456
  headers[name] = value
469
457
  }
470
458
 
471
459
  // Move to the end of this header's value
472
- currentPos = valueEnd + 1
473
- if (headerBlock[valueEnd] === "\r") {
460
+ currentPos = valueEnd
461
+ if (currentPos < headerBlock.length && headerBlock[currentPos] === "\n") {
462
+ currentPos++
463
+ }
464
+ if (currentPos < headerBlock.length && headerBlock[currentPos] === "\r") {
474
465
  currentPos++
475
466
  }
476
467
  }
@@ -495,8 +486,8 @@ function parseMultipart(contentType, body) {
495
486
 
496
487
  // If there's body content in the inline part, add it as 'body'
497
488
  if (partBody) {
498
- // Keep as Buffer if it's binary data
499
- result[partName] = isBinaryData(partBody)
489
+ // Keep as string unless it's binary data
490
+ result[partName] = isBinaryData(Buffer.from(partBody, "binary"))
500
491
  ? Buffer.from(partBody, "binary")
501
492
  : partBody
502
493
  }
@@ -515,18 +506,18 @@ function parseMultipart(contentType, body) {
515
506
  delete restHeaders["content-disposition"]
516
507
 
517
508
  if (Object.keys(restHeaders).length === 0) {
518
- // Keep as Buffer if it's binary data
519
- result[partName] = isBinaryData(partBody)
509
+ // Keep as string unless it's binary data
510
+ result[partName] = isBinaryData(Buffer.from(partBody, "binary"))
520
511
  ? Buffer.from(partBody, "binary")
521
512
  : partBody
522
513
  } else if (!partBody) {
523
514
  // ao-types should stay with this part, not be extracted
524
515
  result[partName] = restHeaders
525
516
  } else {
526
- // Keep as Buffer if it's binary data
517
+ // Keep as string unless it's binary data
527
518
  result[partName] = {
528
519
  ...restHeaders,
529
- body: isBinaryData(partBody)
520
+ body: isBinaryData(Buffer.from(partBody, "binary"))
530
521
  ? Buffer.from(partBody, "binary")
531
522
  : partBody,
532
523
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hbsig",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "main": "cjs/index.js",
5
5
  "license": "MIT",
6
6
  "devDependencies": {