Out of ideas with CURL vs JS vs Xojo

Have a weird one
I have some code I wrote in Xojo thats basically a translation of some JS code - see below

But it gave me errors for reasons I couldn’t fathom

So I used RapidApi (formerly PAW) to test the same API
When I fill out what I need it generates a call using CURL
I get the following CURL invocation

curl -X “POST” “``https://graph.facebook.com/v24.0/958069794056499/media``”
-H ‘Authorization: Bearer EAAlZAJ8PZBulkBQr8Sr8eKMBM188ZBeAxPm6GXbKoPnjqKryAMFItZBkDNKUSVJMe7AOCkCmcx98ZCowIZAW15NJqQo4UOMKgTnpnLkYWujq93yUvCeGLag3dP7PhZC41TpKo0jbYCtxd0B6XxeeIihvRnqafLPqkHZChWEwX2141ohZBLv6S6s3b6g2LzXy2ISYtAY1FeZCy8xfSXZBhNZB8mhoa5f7S1RstIWLq77evO2B1MhvYnDEJDrpmFeE1IZCXeWzkn8ZAfmTJRhMknnMxFxGWccdlZC5cxaTggXBA4F’
-H ‘Content-Type: multipart/form-data’
-F “file=@/Users/npalardy/Desktop/dF8Jup.jpg”
-F “type=image/jpeg”
-F “messaging_product=whatsapp”

and I get a result like

{“id”:“2966336126899864”}

So far YAY !

Then I have Rapid API generate it as JS


import (“https://code.jquery.com/jquery-3.6.3.min.js”) ;

var formData = new FormData();
formData.append(“file”, “@/Users/npalardy/Desktop/dF8Jup.jpg”);
formData.append(“type”, “image/jpeg”);
formData.append(“messaging_product”, “whatsapp”);

jQuery.ajax({
url: “https://graph.facebook.com/v24.0/958069794056499/media”,
type: “POST”,
headers: {
“Authorization”: “Bearer EAAlZAJ8PZBulkBQr8Sr8eKMBM188ZBeAxPm6GXbKoPnjqKryAMFItZBkDNKUSVJMe7AOCkCmcx98ZCowIZAW15NJqQo4UOMKgTnpnLkYWujq93yUvCeGLag3dP7PhZC41TpKo0jbYCtxd0B6XxeeIihvRnqafLPqkHZChWEwX2141ohZBLv6S6s3b6g2LzXy2ISYtAY1FeZCy8xfSXZBhNZB8mhoa5f7S1RstIWLq77evO2B1MhvYnDEJDrpmFeE1IZCXeWzkn8ZAfmTJRhMknnMxFxGWccdlZC5cxaTggXBA4F”,
“Content-Type”: “multipart/form-data”,
},
processData: false,
contentType: false,
data: formData,
})
.done(function(data, textStatus, jqXHR) {
console.log("HTTP Request Succeeded: " + jqXHR.status);
console.log(data);
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.log(“HTTP Request Failed”);
console.log(jqXHR) ;
})
.always(function() {
/* … */
});

I test this in RunJS online
But this time I get
Promise { }
{
readyState: 1,
getResponseHeader: [Function: getResponseHeader],
getAllResponseHeaders: [Function: getAllResponseHeaders],
setRequestHeader: [Function: setRequestHeader],
overrideMimeType: [Function: overrideMimeType],
statusCode: [Function: statusCode],
abort: [Function: abort],
state: [Function: state],
always: [Function: always],
catch: [Function: catch],
pipe: [Function: pipe],
then: [Function: then],
promise: [Function: promise],
progress: [Function: add],
done: [Function: add],
fail: [Function: add]
}
‘HTTP Request Failed’
{
readyState: 4,
getResponseHeader: [Function: getResponseHeader],
getAllResponseHeaders: [Function: getAllResponseHeaders],
setRequestHeader: [Function: setRequestHeader],
overrideMimeType: [Function: overrideMimeType],
statusCode: [Function: statusCode],
abort: [Function: abort],
state: [Function: state],
always: [Function: always],
catch: [Function: catch],
pipe: [Function: pipe],
then: [Function: then],
promise: [Function: promise],
progress: [Function: add],
done: [Function: add],
fail: [Function: add],
responseText: '{
’ +
’ “error”: {
’ +
’ “message”: “(#100) The parameter messaging_product is required.”,
’ +
’ “type”: “OAuthException”,
’ +
’ “code”: 100,
’ +
’ “fbtrace_id”: “A6ziDy4fz48k2lZtqcHXE4i”
’ +
’ }
’ +
‘}’,
responseJSON: {
error: {
message: ‘(#100) The parameter messaging_product is required.’,
type: ‘OAuthException’,
code: 100,
fbtrace_id: ‘A6ziDy4fz48k2lZtqcHXE4i’
}
},
status: 400,
statusText: ‘error’
}

One works
the other two dont
I cant figure out why

Anyone got ANY ideas ?

Firstly: Remove/Revoke the Access Token, it’s now compromised.

Secondly: I see a content-type mismatch in your JS. In curl you send properly a binary file (the content of.jpg file) in JS you just send a string. Likely there’s the mistake.

Lastly: CORS?

The tokens are temporary me so they expired before I posted :slight_smile:
And I now see what my issue is

CURL treats the @ specially

so I need to manually do what that does

OK I have fixed all that stuff and comparing what CURL sends vs what I now send its darned near byte for byte the same (boundary markers aside)

And damned FB still rejects it saying a parameter is missing

scratching my head

Converted my Xojo to C# and .. it works

tried changing headers in Xojo … no dice
added Aton of headers to the CURL request - it just works

Now I’m really scratching the bottom of the barrel here

make your own rest API and check what Xojo is doing…

I just had a tiny little xojo app with HandleURL in it and as far as I can tell CURL & Xojo do exactly the same

The body content is the same

the URL is the same

headers the same (I even added the ones Xojo sends by default to the CURL call & it made no difference)

the CURL command still works
the Xojo code doesnt

Converted the Xojo code to C#
it works

Xojo doesnt

so I’m really out of IDEAS

Could it be a transport protocol issue?

Apparently not

I thought had exactly duplicated what CURL was doing and now I have

When I compare the binary content that my code generates & what curl sends they are now identical

Turns out the BOUNDARY markers seem to be what causes the issue

I had skipped comparing those because each attempt will generate new ones
Mine were the same SIZE as the ones CURL generated but not formed the exact same way with a certain # of - characters leading & trailing and actual ascii characters as a tag
Now they are EXACTLY the same and VOILA !

things work

1 Like

RFC 2046 (MIME) and RFC 7578 (Multipart/form-data). Boundary strings:

1. The Composition Rules

The boundary string must follow these syntax constraints:

  • Length: It can be up to 70 characters long (not including the leading dashes).

  • Characters: It can consist of alphanumeric characters, and a very limited set of symbols:

    • 0-9, a-z, A-Z

    • Special characters: ' ( ) + _ - . , / : = ?

  • No Whitespace: It cannot end with a space.

  • Uniqueness: The most important “unwritten” rule is that the boundary string must not appear anywhere within the data being sent. If your boundary is --MyBoundary and your text message contains the string --MyBoundary, the parser will crash or truncate your data.

2. Declaration vs. Usage

There is often confusion about how many dashes to use. The rules differ depending on whether you are declaring the boundary or using it.

Location Formatting Rule Example
Content-Type Header Declared exactly as defined. boundary=gc0p4Jq0M2Yt
Body (Start/Middle) Prepend with two dashes (--). --gc0p4Jq0M2Yt
Body (Final Marker) Prepend and append two dashes. --gc0p4Jq0M2Yt--

3. Structural Rules in the Body

When the boundary is placed inside the HTTP request body, it must follow a strict structural sequence:

  1. CRLF Precedence: Every boundary (except potentially the first one) must be preceded by a Carriage Return and Line Feed (\r\n). Yes, even in a unix-like system.

  2. The Encapsulation Boundary: This starts each part. It consists of -- plus the boundary string.

  3. The Final Boundary: To signal the end of the entire entity, the boundary is wrapped in dashes: -- + boundary + --.

4. Random strings?

You will often see boundaries that look like ----WebKitFormBoundary7MA4YWxkTrZu0gW. Browsers generate these using:

  • A fixed prefix (usually a tip to identify the browser engine).

  • A high-entropy random hash (proper chars, to ensure the string is statistically unlikely to exist inside the file being uploaded).

Tip: When you are creating your own, you can use a some_short_prefix_string+UUID+few_random_proper_chars as a marker to minimize the risk of a collision, make it less than 60 chars for a good result. Rick

1 Like

I can’t believe that you’re still using jquery. That junk was obsolete years ago. Use fetch.

Jquery rocks.

The jQuery code was literally from RapidAPI when I asked it to write the CURL code as javascript

I wrote nothing beyond the single import line

It was just curious that as CURL things worked
As just about any other code (js, C# etc it didnt)