HTTP post usage

Hi,

I’m using http.jl for oauth2. I have no issues getting an authorization code but I get an error trying to exchange the code for a token.

resp=HTTP.post("https://identity.xero.com/connect/token",headers=Dict("Authorization" => "Basic MUY3NDk4sdfdsafdsafdsfgsadfZHR2JFQjIxOUNtQ0ZafdsfsdfsdhpdmF4ZGhSazdNcWxhX2hB"),
body=Dict("grant_type" => "authorization_code","code" => "fb26sadfsdaf7b25a065dasdfdsafdasfdsaf37b9d89165f7a86a1c8","redirect_uri" => "https://xero.com/"))

gives an error

ERROR: LoadError: MethodError: no method matching write(::HTTP.Streams.Stream{HTTP.Messages.Response, HTTP.ConnectionPool.Transaction{MbedTLS.SSLContext}}, ::Pair{String, String})
Closest candidates are:
  write(::IO, ::Any) at C:\Users\steve\AppData\Local\Programs\Julia-1.7.0\share\julia\base\io.jl:637
  write(::IO, ::Any, ::Any...) at C:\Users\steve\AppData\Local\Programs\Julia-1.7.0\share\julia\base\io.jl:638
  write(::IO, ::Union{Float16, Float32, Float64, Int128, Int16, Int32, Int64, UInt128, UInt16, UInt32, UInt64})

Can anyone give me a hint what I should look at to resolve this.

Thanks
Steve

1 Like

Linking related thread.

Thanks Rafael.

I’m certainly not using a certificate, just a standard oauth2 flow.

I assume you are suggesting I create a client object using
client = HTTP.Client(tlsconfig=MbedTLS.SSLConfig(false))
and then pass this to the get like this
HTTP.get(client, url; options...)

However I get ERROR: LoadError: UndefVarError: Client not defined
and I can’t find it in the HTTP.jl documentation.

Thanks
Steve

1 Like

Are you sure that a Dict is a valid value for body? Looking at the docs it says:

body can take a number of forms:

  • a String , a Vector{UInt8} or any T accepted by write(::IO, ::T)
  • a collection of String or AbstractVector{UInt8} or IO streams or items of any type T accepted by write(::IO, ::T...)
  • a readable IO stream or any IO -like type T for which eof(T) and readavailable(T) are defined.
1 Like

Thanks Paul,

if I’m to use a string for a body with multiple parameters what should that look like?

resp=HTTP.post("https://identity.xero.com/connect/token",
headers=Dict("Authorization" => "Basic UY3NDk4ODY5MTAwNEU5QjkzQjkxQTE1MzUxNDAzQTY6OUpEb2RjNVZHR2JFQjIxOUNtQ0ZVc1NRX2hSdEQyZVhpdmF4ZGhSazdNcWxhX2hB"),
body="grant_type = 'authorization_code',
code = 'fb263ea987b2d82b9ae644d351b74361e429165f7a86a1c8',
redirect_uri = 'https://xero.com/'")

What defines the structure of this string? Is this specific to HTTP.jl or the HTTP standards?

I’m feeling so inadequate in this area of Julia. Should I perhaps consider just using python code in julia for this stuff because it’s usage documentation/tutorials etc are so much better or would this just introduce another raft of problems?

Thanks
Steve

Looking at the analogous API in Python’s requests module (which does take a dict), those values are encoded as content-type application/x-www-form-urlencoded:

import requests
r=requests.post('http://localhost:8080/', data={'a':1, 'b': "doh"})

melis@juggle 09:37:~$ nc -l -p 8080
POST / HTTP/1.1
Host: localhost:8080
User-Agent: python-requests/2.26.0
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
Content-Length: 9
Content-Type: application/x-www-form-urlencoded

a=1&b=doh

So if you can do the same with HTTP.jl that should work. Here’s actually a post on this forum from some years ago, with exactly the same issue, but no answer :-(. There’s actually also a closed HTTP.jl issue to document exactly the thing you want here, but the docs were only updated for the case of POSTing JSON, although the issue does give a solution (see this comment):

headers = ["Content-Type" => "application/x-www-form-urlencoded"]
params = Dict("Password" => "password")
body = HTTP.URIs.escapeuri(params)
response = HTTP.post("http://localhost:8080", headers, body, verbose=2)
HTTP.Messages.Request:
"""
POST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: localhost:8080
Accept: */*
User-Agent: HTTP.jl/1.7.1
Content-Length: 17

Password=password"""

Edit: deleted reference to example that does not apply here

I ran into a similar issue (same error message) today and largely solved the issue by wrapping the Dict-formatted body in a JSON.json() call.

url = "https://api.bit.io/api/v1beta/import/json/"
payload = Dict("create_table_if_not_exists" => true,
    "table_name" => "jl_test",
    "repo_name" => "DL Tests",
    "data" => arraytable(invoices))
headers = Dict(
    "Accept" => "application/json",
    "Content-Type"=> "application/json",
    "Authorization"=> "Bearer $pw"
)
HTTP.post(url, headers, json(payload))