ericgjackson / slumbot2019

Implementations of CFR for solving a variety of Holdem-like poker games
MIT License
133 stars 31 forks source link

Can't access API from Typescript/Javascript #40

Closed nathanielyoon closed 2 months ago

nathanielyoon commented 2 months ago

I ran the following code with Deno, with Node, and in the browser console:

fetch("https://slumbot.com/api/new_hand", {
  method: "POST",
  headers: { "content-type": "application/json" },
  body: "{}",
}).then(console.log);

Each time, I got a 400 response. I tried with other endpoints too, including with a token I got from a call in another program - cURL, Python, and Go worked fine.

Is the code wrong, is there something that Javascript's fetch doesn't implement, or something else?

nathanielyoon commented 2 months ago

I also tried:

new Deno.Command("curl", {
  args: [
    "https://slumbot.com/api/new_hand",
    "--header",
    "'Content-Type: application/json'",
    "-d",
    "'{}'",
  ],
}).output().then(($) => console.log(new TextDecoder().decode($.stdout)));
ericgjackson commented 2 months ago

The header should be "Content-Type", not "content-type".

In your second example (with "curl"), I'm not sure why you have the single quotes (e.g., before Content-Type). That could throw things off if they are not stripped somehow.

nathanielyoon commented 2 months ago

I assumed headers are case-insensitive, as per rfc7230, but when changing their case, the Typescript fetch doesn't work.

fetch("https://slumbot.com/api/new_hand", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: "{}",
}).then(console.log);

For the second (Deno.Command), removing the single quotes from the header and body worked, i.e. the program's call to curl returned a successful response. This is why I suspect something in the way Javascript's fetch API makes requests is to blame. I noticed in the response object, the headers are converted to lowercase:

Response {
  body: ReadableStream { locked: false },
  bodyUsed: false,
  headers: Headers {
    connection: "keep-alive",
    "content-length": "0",
    "content-type": "application/json",
    date: "Tue, 18 Jun 2024 12:32:41 GMT",
    server: "nginx/1.18.0 (Ubuntu)"
  },
  ok: false,
  redirected: false,
  status: 400,
  statusText: "Bad Request",
  url: "https://slumbot.com/api/new_hand"
}

There's no option to change this behavior, so if the server treats (required) headers case-sensitively, it may be impossible to make requests from Javascript.

ericgjackson commented 2 months ago

I think you're right. I'll change the server code to be case-insensitive in handling HTTP headers and we'll see if that fixes things. Should be able to do this today or tomorrow.

ericgjackson commented 2 months ago

I've pushed the fix to be case-insensitive in parsing HTTP headers. Can you try again to see if you can access the API using fetch() from Javascript?

nathanielyoon commented 2 months ago

Running the code from the original post yielded the same (400) response.

Is requiring the content-type header necessary? Presumably, non-JSON bodies would fail regardless.

nathanielyoon commented 2 months ago

I think the header parsing is still case-sensitive (as opposed to another unique characteristic of fetch), cause this failed:

curl -s https://slumbot.com/api/new_hand --header "content-type: application/json" -d "{}"

and this succeeded:

curl -s https://slumbot.com/api/new_hand --header "Content-Type: application/json" -d "{}"
ericgjackson commented 2 months ago

Oh, shoot, I hadn't pushed the libraries containing the fix.

Now I think I've pushed the fix and the curl command you provided (with lower-case content-type) is now working. Please try again.

nathanielyoon commented 2 months ago

Yep, works now. Thanks for the fix.