Skip to content

ofURLFileLoader bugfix for sending POST#8428

Merged
ofTheo merged 1 commit into
openframeworks:masterfrom
danomatika:bugfix/url-post-req
Apr 17, 2025
Merged

ofURLFileLoader bugfix for sending POST#8428
ofTheo merged 1 commit into
openframeworks:masterfrom
danomatika:bugfix/url-post-req

Conversation

@danomatika
Copy link
Copy Markdown
Contributor

@danomatika danomatika commented Apr 16, 2025

So I am a little lazy and decided to use ofURLFileLoader for basic REST stuff to a local ollama server instead of bothering with ofxHTTP. It's just a thin wrapper for HTTP and it's built into the core anyway...

I noticed that GETs work fine however trying to POST a body with stringified JSON resulted in a 405 METHOD NOT ALLOWED error. Enabling verbose in the request showed that, in my case, the actual request type being made was a GET even though I set the request method to POST.

I was able to fix this by making sure the libcurl method opts are set before dealing with the request body / POST data handling. This broke GET so only setting the POST handling stuff if not doing a GET was the fix. If the request is a GET, the request body should be ignored anyway, I believe. I think libcurl is making some assumptions in the background when setting some of these options, so order might be important, ie. setting POSTFIELDS (even disabling) might trigger it to change the request type.


My original tests are big made using ofxLua and the of_v20250330_osx_release build:

of.setLogLevel("ofURLFileLoader", of.LOG_VERBOSE)

-- http get
-- check with: curl -X GET "http://httpbin.org/get
local http = of.URLFileLoader()
local res = http:get("http://httpbin.org/get")
print("http get: "..res.status)
if res.data ~= "" then
  print(res.data:getText())
end

-- http post
-- check with: curl -X POST -d '{"foo":"bar","baz":123}' "http://httpbin.org/post"
local req = of.HttpRequest("http://httpbin.org/post", "test")
req.body = '{"foo":"bar","baz":123}'
req.contentType = "application/json"
req.method = of.HttpRequest_POST
req.verbose = true
res = http:handleRequest(req)
print("http post: "..res.status)
if res.data ~= "" then
  print(res.data:getText())
end

The equivalent C++ should be something like:

ofSetLogLevel("ofURLFileLoader", OF_LOG_VERBOSE);

// http get
// check with: curl -X GET "http://httpbin.org/get
auto http = ofURLFileLoader();
auto res = http.get("http://httpbin.org/get");
ofLog() << "http get: " << res.status << std::endl;
if(res.data != "") {
  ofLog() << res.data.getText() << std::endl;
}

// http post
// check with: curl -X POST -d '{"foo":"bar","baz":123}' "http://httpbin.org/post"
auto req = ofHttpRequest("http://httpbin.org/post", "test");
req.body = "{\"foo\":\"bar\",\"baz\":123}";
req.contentType = "application/json";
req.method = ofHttpRequest::POST;
req.verbose = true;
res = http.handleRequest(req);
ofLog() << "http post: " << res.status << std::endl;
if(res.data != "") {
  ofLog() << res.data.getText() << std::endl;
}

The output in my simple test is:

http get: 200.0
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "br,gzip", 
    "Host": "httpbin.org", 
    "X-Amzn-Trace-Id": "Root=1-68002ab3-08e2be1606af68f4570a815b"
  }, 
  "origin": "#.#.#.#", 
  "url": "http://httpbin.org/get"
}

* Host httpbin.org:80 was resolved.
* IPv6: (none)
* IPv4: 35.172.19.140, 34.238.6.191, 54.152.142.77, 3.224.7.64
*   Trying 35.172.19.140:80...
* Connected to httpbin.org (35.172.19.140) port 80
* using HTTP/1.x
> POST /post HTTP/1.1
Host: httpbin.org
User-Agent: curl/8.11.0
Accept: */*
Content-Type: application/json
Connection: close
Accept-Encoding: br
Accept-Encoding: gzip
Content-Length: 23

* upload completely sent off: 23 bytes
< HTTP/1.1 200 OK
< Date: Wed, 16 Apr 2025 22:09:56 GMT
< Content-Type: application/json
< Content-Length: 487
< Connection: close
< Server: gunicorn/19.9.0
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Credentials: true
< 
* shutting down connection #0
http post: 200.0
{
  "args": {}, 
  "data": "{\"foo\":\"bar\",\"baz\":123}", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "br,gzip", 
    "Content-Length": "23", 
    "Content-Type": "application/json", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/8.11.0", 
    "X-Amzn-Trace-Id": "Root=1-68002ab3-2f095ad9038b7121062c154c"
  }, 
  "json": {
    "baz": 123, 
    "foo": "bar"
  }, 
  "origin": "#.#.#.#", 
  "url": "http://httpbin.org/post"
}

I also tested imageLoaderWebExample which works fine.

@danomatika
Copy link
Copy Markdown
Contributor Author

danomatika commented Apr 16, 2025

Oh, and I also notice that the ofHttpResponse error variable is only set when there is a connection error. It is not actually set on a successful response. Maybe this should be a separate issue?

I tried setting the CURLOPT_FAILONERROR option but the err string is simply "HTTP status shows error" so the "error" variable will only reflect libcurl errors not the HTTP error.

@danomatika danomatika changed the title ofURLFileLoader bugfix for sending POST body text ofURLFileLoader bugfix for sending POST Apr 16, 2025
@danomatika danomatika mentioned this pull request Apr 16, 2025
54 tasks
@ofTheo ofTheo merged commit 13358c1 into openframeworks:master Apr 17, 2025
18 checks passed
@ofTheo
Copy link
Copy Markdown
Member

ofTheo commented Apr 17, 2025

Thanks Dan!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants