[GH-ISSUE #796] Script doesn't allow ArrayBuffer as response.body #791

Closed
opened 2026-03-03 19:21:56 +03:00 by kerem · 18 comments
Owner

Originally created by @SupremeTechnopriest on GitHub (Feb 19, 2021).
Original GitHub issue: https://github.com/ProxymanApp/Proxyman/issues/796

Originally assigned to: @NghiaTranUIT on GitHub.

Proxyman version? (Ex. Proxyman 1.4.3)

2.18.0

macOS Version? (Ex. mac 10.14)

11.2.1

Steps to reproduce

Attempt to respond with an ArrayBuffer

function onResponse(context, url, request, response) {
  const buffer = new ArrayBuffer(256)
  const view = new Uint8Array(buffer)
  for (let i = 0; i < view.length; i++) {
    view[i] = i
  }
  response.body = buffer
  response.statusCode = 200
  response.headers['Content-Type'] = 'application/octet-stream'
  return response
}

Expected behavior

It should send the buffer to the client but instead it is throwing the following error:

❌ Error: Could not parse the body as data. (Content-Type=application/octet-stream)

Originally created by @SupremeTechnopriest on GitHub (Feb 19, 2021). Original GitHub issue: https://github.com/ProxymanApp/Proxyman/issues/796 Originally assigned to: @NghiaTranUIT on GitHub. ### Proxyman version? (Ex. Proxyman 1.4.3) `2.18.0` ### macOS Version? (Ex. mac 10.14) `11.2.1` ### Steps to reproduce Attempt to respond with an ArrayBuffer ```js function onResponse(context, url, request, response) { const buffer = new ArrayBuffer(256) const view = new Uint8Array(buffer) for (let i = 0; i < view.length; i++) { view[i] = i } response.body = buffer response.statusCode = 200 response.headers['Content-Type'] = 'application/octet-stream' return response } ``` ### Expected behavior It should send the buffer to the client but instead it is throwing the following error: `❌ Error: Could not parse the body as data. (Content-Type=application/octet-stream)`
kerem 2026-03-03 19:21:56 +03:00
Author
Owner

@SupremeTechnopriest commented on GitHub (Feb 20, 2021):

If you can solve this issue I will be dropping Charles and picking up a license to proxyman.

<!-- gh-comment-id:782465695 --> @SupremeTechnopriest commented on GitHub (Feb 20, 2021): If you can solve this issue I will be dropping Charles and picking up a license to proxyman.
Author
Owner

@NghiaTranUIT commented on GitHub (Feb 20, 2021):

@SupremeTechnopriest It's a limitation of JavascriptCore when mapping the JS Data Type to Swift. JavascriptCore doesn't allow passing directly ArrayBuffer to Swift (ref: https://nshipster.com/javascriptcore/)

To workaround it, we should convert ArrayBuffer to Base64Encoded String and set to Response as usual (https://docs.proxyman.io/scripting/script#onresponse-objects-format)

I will write down an example to you 👍

<!-- gh-comment-id:782519303 --> @NghiaTranUIT commented on GitHub (Feb 20, 2021): @SupremeTechnopriest It's a limitation of JavascriptCore when mapping the JS Data Type to Swift. JavascriptCore doesn't allow passing directly ArrayBuffer to Swift (ref: https://nshipster.com/javascriptcore/) To workaround it, we should convert ArrayBuffer to Base64Encoded String and set to Response as usual (https://docs.proxyman.io/scripting/script#onresponse-objects-format) I will write down an example to you 👍
Author
Owner

@SupremeTechnopriest commented on GitHub (Feb 20, 2021):

Ah I see. And the client will receive it as an ArrayBuffer when reading the stream with await res.arrayBuffer()?

<!-- gh-comment-id:782521034 --> @SupremeTechnopriest commented on GitHub (Feb 20, 2021): Ah I see. And the client will receive it as an ArrayBuffer when reading the stream with `await res.arrayBuffer()`?
Author
Owner

@NghiaTranUIT commented on GitHub (Feb 20, 2021):

Here is the script. Please try again if you see if it works 😄

// Addons List: https://docs.proxyman.io/scripting/addons
const { sayHello } = require("@addons/HelloWorld.js");
// Import
const { btoa } = require('@addons/Base64.js');

///
/// This func is called if the Response Checkbox is Enabled
/// You can manipulate the Response Data here before it goes to the client
/// Ex: Add/Update/Remove: headers, statusCode and body (json, plain-text, base64 encoded string)
/// Use console.log(response) to see all available fields
///
function onResponse(context, url, request, response) {
  // console.log(response);
  const buffer = new ArrayBuffer(256)
  const view = new Uint8Array(buffer)
  for (let i = 0; i < view.length; i++) {
    view[i] = i
  }
  
  var newBody = btoa(buffer);
  console.log(buffer);
  console.log(newBody);
  
  response.body = newBody;
  response.statusCode = 200
  response.headers['Content-Type'] = 'application/octet-stream'

  // Done
  return response;
}

Ref: https://docs.proxyman.io/scripting/script#5-notes

<!-- gh-comment-id:782521191 --> @NghiaTranUIT commented on GitHub (Feb 20, 2021): Here is the script. Please try again if you see if it works 😄 ```js // Addons List: https://docs.proxyman.io/scripting/addons const { sayHello } = require("@addons/HelloWorld.js"); // Import const { btoa } = require('@addons/Base64.js'); /// /// This func is called if the Response Checkbox is Enabled /// You can manipulate the Response Data here before it goes to the client /// Ex: Add/Update/Remove: headers, statusCode and body (json, plain-text, base64 encoded string) /// Use console.log(response) to see all available fields /// function onResponse(context, url, request, response) { // console.log(response); const buffer = new ArrayBuffer(256) const view = new Uint8Array(buffer) for (let i = 0; i < view.length; i++) { view[i] = i } var newBody = btoa(buffer); console.log(buffer); console.log(newBody); response.body = newBody; response.statusCode = 200 response.headers['Content-Type'] = 'application/octet-stream' // Done return response; } ``` Ref: https://docs.proxyman.io/scripting/script#5-notes
Author
Owner

@NghiaTranUIT commented on GitHub (Feb 20, 2021):

If you need to send a binary file, you can directly import the file and set it as a response.body (See Snippet Code https://docs.proxyman.io/scripting/snippet-code#binary-file)

<!-- gh-comment-id:782522393 --> @NghiaTranUIT commented on GitHub (Feb 20, 2021): If you need to send a binary file, you can directly import the file and set it as a response.body (See Snippet Code https://docs.proxyman.io/scripting/snippet-code#binary-file)
Author
Owner

@NghiaTranUIT commented on GitHub (Feb 20, 2021):

Ah I see. And the client will receive it as an ArrayBuffer when reading the stream with await res.arrayBuffer()?

Unfortunately, JavascriptCore doesn't support await or Promise : /

You might directly construct an ArrayBuffer or import it from a file (as I highly recommend) 👍

<!-- gh-comment-id:782523244 --> @NghiaTranUIT commented on GitHub (Feb 20, 2021): > Ah I see. And the client will receive it as an ArrayBuffer when reading the stream with `await res.arrayBuffer()`? Unfortunately, JavascriptCore doesn't support `await` or `Promise` : / You might directly construct an ArrayBuffer or import it from a file (as I highly recommend) 👍
Author
Owner

@SupremeTechnopriest commented on GitHub (Feb 20, 2021):

Ah I mean the client will receive a buffer and not a base64 encoded string? I will test it out shortly.

<!-- gh-comment-id:782523534 --> @SupremeTechnopriest commented on GitHub (Feb 20, 2021): Ah I mean the client will receive a buffer and not a base64 encoded string? I will test it out shortly.
Author
Owner

@NghiaTranUIT commented on GitHub (Feb 20, 2021):

yes, Proxyman will automatically convert the base64String to a buffer when making a request/response. Therefore, the client will receive correctly

<!-- gh-comment-id:782523855 --> @NghiaTranUIT commented on GitHub (Feb 20, 2021): yes, Proxyman will automatically convert the base64String to a buffer when making a request/response. Therefore, the client will receive correctly
Author
Owner

@SupremeTechnopriest commented on GitHub (Feb 20, 2021):

Beautiful! Take my money!

<!-- gh-comment-id:782523981 --> @SupremeTechnopriest commented on GitHub (Feb 20, 2021): Beautiful! Take my money!
Author
Owner

@NghiaTranUIT commented on GitHub (Feb 20, 2021):

If there is something that doesn't work as you expect. Feel free to give me an example code, I will check it out 😄

Meanwhile, I will update a Snippet Code Page for anyone who would use ArrayBuffer 👍

<!-- gh-comment-id:782524197 --> @NghiaTranUIT commented on GitHub (Feb 20, 2021): If there is something that doesn't work as you expect. Feel free to give me an example code, I will check it out 😄 Meanwhile, I will update a Snippet Code Page for anyone who would use ArrayBuffer 👍
Author
Owner

@SupremeTechnopriest commented on GitHub (Feb 20, 2021):

Sounds good! We can close this for now and if I run into any issues I will re open it. Thanks for your help!

<!-- gh-comment-id:782524429 --> @SupremeTechnopriest commented on GitHub (Feb 20, 2021): Sounds good! We can close this for now and if I run into any issues I will re open it. Thanks for your help!
Author
Owner

@NghiaTranUIT commented on GitHub (Feb 20, 2021):

You're welcome 🎉

<!-- gh-comment-id:782524543 --> @NghiaTranUIT commented on GitHub (Feb 20, 2021): You're welcome 🎉
Author
Owner

@NghiaTranUIT commented on GitHub (Feb 20, 2021):

Hey @SupremeTechnopriest

I've discovered that btoa doesn't accept if the param is an ArrayBuffer. btoa only accepts String

You have to convert ArrayBuffer to String then encoding to Base64 like var newBody = btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)));

Here is the example code:

// Addons List: https://docs.proxyman.io/scripting/addons
const { sayHello } = require("@addons/HelloWorld.js");
// Import
const { btoa } = require('@addons/Base64.js');

function onResponse(context, url, request, response) {
  
  const buffer = new ArrayBuffer(256)
  const view = new Uint8Array(buffer)
  for (let i = 0; i < view.length; i++) {
    view[i] = i
  }
  
  // Convert ArrayBuffer to Base64String
  var newBody = btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)));
  console.log(newBody);
  
  response.body = newBody;
  response.statusCode = 200
  response.headers['Content-Type'] = 'application/octet-stream'

  // Done
  return response;
}

Results

The response body has correct ArrayBuffer data

Screen Shot 2021-02-20 at 09 43 13

Improvements

In the next build, I will:

  • Add the ArrayBuffer link in this error ❌ Error: Could not parse the body as data. (Content-Type=application/octet-stream), so users can know how to fix it
  • Improve built-in btoa, it will automatically check ArrayBuffer type and do it behind the scene
<!-- gh-comment-id:782541910 --> @NghiaTranUIT commented on GitHub (Feb 20, 2021): Hey @SupremeTechnopriest I've discovered that `btoa` doesn't accept if the param is an ArrayBuffer. `btoa` only accepts String You have to convert ArrayBuffer to String then encoding to Base64 like `var newBody = btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)));` Here is the example code: ```js // Addons List: https://docs.proxyman.io/scripting/addons const { sayHello } = require("@addons/HelloWorld.js"); // Import const { btoa } = require('@addons/Base64.js'); function onResponse(context, url, request, response) { const buffer = new ArrayBuffer(256) const view = new Uint8Array(buffer) for (let i = 0; i < view.length; i++) { view[i] = i } // Convert ArrayBuffer to Base64String var newBody = btoa(String.fromCharCode.apply(null, new Uint8Array(buffer))); console.log(newBody); response.body = newBody; response.statusCode = 200 response.headers['Content-Type'] = 'application/octet-stream' // Done return response; } ``` ### Results The response body has correct ArrayBuffer data <img width="1290" alt="Screen Shot 2021-02-20 at 09 43 13" src="https://user-images.githubusercontent.com/5878421/108580906-406ef400-7360-11eb-9ea8-ade9353336bc.png"> ### Improvements In the next build, I will: - Add the ArrayBuffer link in this error `❌ Error: Could not parse the body as data. (Content-Type=application/octet-stream)`, so users can know how to fix it - Improve built-in `btoa`, it will automatically check ArrayBuffer type and do it behind the scene
Author
Owner

@NghiaTranUIT commented on GitHub (Feb 20, 2021):

Here is the beta build, which has some improvement I mention above 👍 (https://proxyman.s3.us-east-2.amazonaws.com/beta/Proxyman_2.18.0_Improve_Scripting_Array_Buffer.dmg)

You can use btoa to convert ArrayBuffer to Base64 without using the following helper code:

Old

var newBody = btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)));

New

const { btoa } = require('@addons/Base64.js');

var newBody = btoa(buffer);
response.body = newBody;

Notes

  • Please delete the old addon at ~/Library/Application Support/com.proxyman.NSProxy/addons/Base64.js and relaunch the beta build.
  • It will automatically update the file if I bump the build version, but for now, we have to do it manually 😄
<!-- gh-comment-id:782554644 --> @NghiaTranUIT commented on GitHub (Feb 20, 2021): Here is the beta build, which has some improvement I mention above 👍 (https://proxyman.s3.us-east-2.amazonaws.com/beta/Proxyman_2.18.0_Improve_Scripting_Array_Buffer.dmg) You can use `btoa` to convert ArrayBuffer to Base64 without using the following helper code: ### Old ```js var newBody = btoa(String.fromCharCode.apply(null, new Uint8Array(buffer))); ``` ### New ```js const { btoa } = require('@addons/Base64.js'); var newBody = btoa(buffer); response.body = newBody; ``` ### Notes - Please delete the old addon at `~/Library/Application Support/com.proxyman.NSProxy/addons/Base64.js` and relaunch the beta build. - It will automatically update the file if I bump the build version, but for now, we have to do it manually 😄
Author
Owner

@SupremeTechnopriest commented on GitHub (Feb 22, 2021):

Thanks for the update. I will modify my custom addon to stringify the buffer until you bump the build version.

<!-- gh-comment-id:783704820 --> @SupremeTechnopriest commented on GitHub (Feb 22, 2021): Thanks for the update. I will modify my custom addon to stringify the buffer until you bump the build version.
Author
Owner

@SupremeTechnopriest commented on GitHub (Feb 23, 2021):

@NghiaTranUIT Quick question. How would I go the other way? Parsing an ArrayBuffer on the request. It comes in as Base64, but calling atob on it turns it into a string.

<!-- gh-comment-id:783771699 --> @SupremeTechnopriest commented on GitHub (Feb 23, 2021): @NghiaTranUIT Quick question. How would I go the other way? Parsing an ArrayBuffer on the request. It comes in as Base64, but calling `atob` on it turns it into a string.
Author
Owner

@SupremeTechnopriest commented on GitHub (Feb 23, 2021):

Nevermind, I figured it out.

function convert (str) {
  const buffer = new ArrayBuffer(str.length * 2)
  const view = new Uint8Array(buffer)
  for (let i=0; i < str.length; i++) {
    view[i] = str.charCodeAt(i)
  }
  return buffer
}

Maybe you can add this to the Base64 addon as well 🙂

<!-- gh-comment-id:783775117 --> @SupremeTechnopriest commented on GitHub (Feb 23, 2021): Nevermind, I figured it out. ```js function convert (str) { const buffer = new ArrayBuffer(str.length * 2) const view = new Uint8Array(buffer) for (let i=0; i < str.length; i++) { view[i] = str.charCodeAt(i) } return buffer } ``` Maybe you can add this to the Base64 addon as well 🙂
Author
Owner

@NghiaTranUIT commented on GitHub (Feb 23, 2021):

Glad to know that you found the answer 😄 I will improve the atob too 👍

<!-- gh-comment-id:783807771 --> @NghiaTranUIT commented on GitHub (Feb 23, 2021): Glad to know that you found the answer 😄 I will improve the atob too 👍
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/Proxyman#791
No description provided.