Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Undici does not like lowercase method names #2079

Closed
dglozic opened this issue Apr 18, 2023 · 13 comments
Closed

Undici does not like lowercase method names #2079

dglozic opened this issue Apr 18, 2023 · 13 comments
Labels
bug Something isn't working

Comments

@dglozic
Copy link

dglozic commented Apr 18, 2023

Bug Description

When using request with lowercase method (say, put) undici accepts the method name but nginx complains about it and throws error 400.

Reproducible By

Make a request call by explicitly specifying method property as lowercase (say, put).

Expected Behavior

Method names should be case-insensitive.

Logs & Screenshots

Error from nginx proxy when put is used:

2023/04/17 14:48:42 [info] 36173#0: *119 client sent invalid method while reading client request line, client: 127.0.0.1, server: 127.0.0.1:9442, request: "put /v2/user_profiles/johndoe@acme.com/reload HTTP/1.1"

Environment

Using Node.js v18.14.2, and the latest version of undici.

@dglozic dglozic added the bug Something isn't working label Apr 18, 2023
@ronag
Copy link
Member

ronag commented Apr 18, 2023

I don't think the spec disallows lower case methods. This looks more like a user error than a bug.

@dglozic
Copy link
Author

dglozic commented Apr 18, 2023

I am going to try to distill a use case that proves the problem that happens when method is put but goes away when I set it to PUT.

@ronag
Copy link
Member

ronag commented Apr 18, 2023

I believe you. But I am not convinced this is a undici bug.

@dglozic
Copy link
Author

dglozic commented Apr 18, 2023

Let me dig a bit deeper - if it is me I will happily close :-).

@dglozic
Copy link
Author

dglozic commented Apr 18, 2023

Here is a snippet where I can reproduce it without fail:

const {
	request,
	Agent
} = require("undici");

function testUndici() {
	const token = process.env.TOKEN;
	if (!token) {
		exit("Missing token - exiting");
		return;
	}

	// Making a put
	const options = {
		headers: {
			"Authorization": "Bearer " + token,
			"Accept": "application/json"
		},
		method: "GET",
		dispatcher: new Agent({ connect: { rejectUnauthorized: false } })
	};
	request("https://localhost:9442/v2/user_profiles/dejan@ca.ibm.com", options)
		.then((response) => {
			if (response.statusCode !== 200) {
				throw new Error("Unexpected status code: " + response.statusCode);
			}
			return response.statusCode < 400 ? response.body.json() : response.body.text();
		})
		.then((result) => {
			console.log("Response from reload profile: " + JSON.stringify(result));
			exit();
		})
		.catch((err) => {
			exit("Error detected: " + err.message);
		})
	;
}

function exit(message) {
	if (message) {
		console.log(message);
	}
	// eslint-disable-next-line no-process-exit
	process.exit(message ? 1 : 0);
}

testUndici();

If I use GET it works. If I rename it to get, it fails in nginx proxy with error 400.

@dglozic
Copy link
Author

dglozic commented Apr 18, 2023

The only quirk above is this:

		dispatcher: new Agent({ connect: { rejectUnauthorized: false } })

as I am running this in localhost using https and my server is using self-signed certificate. We use default global dispatcher when deployed in the cluster.

@ronag
Copy link
Member

ronag commented Apr 18, 2023

Don't use lowercase if your server doesn't support it...

@dglozic
Copy link
Author

dglozic commented Apr 18, 2023

Sure, so your position is that you are passing the method 'as is' and it is the caller's responsibility to use the method that the target server can support?

I think request from which we are porting was more forgiving (probably converting methods to upper case before sending). We can do the same, was just confused as to what the contract was.

Note that this happens in both localhost nginx (for testing) and k8s ingress based on nginx - same behaviour. Many of your users will use this against a service running in k8s. I am surprised it wasn't flagged before.

@mcollina
Copy link
Member

From a quick scavenge in the RFCs:

https://datatracker.ietf.org/doc/html/rfc7231#section-4.1

This specification defines a number of standardized methods that are
commonly used in HTTP, as outlined by the following table. By
convention, standardized methods are defined in all-uppercase
US-ASCII letters.

Apparently method names are all in uppercase.

@dglozic
Copy link
Author

dglozic commented Apr 18, 2023

I understand that, but if that is a spec, undici should reject 'options' object with a method that is not in uppercase (as 'invalid method value' error or something).

@dglozic
Copy link
Author

dglozic commented Apr 18, 2023

I understand this goes into the same grey area where RFC for bearer tokens states it should be "Authorization: Bearer {token}" and some services can handle both "Bearer" and "bearer" and some throw a hissy fit on "bearer" because RFC.

@ronag
Copy link
Member

ronag commented Apr 18, 2023

The standard methods are uppercase but servers are not forbidden from implementing whatever method name and casing they wish.

The upper casing is more of a convention than a rule.

@dglozic
Copy link
Author

dglozic commented Apr 18, 2023

Correct, and it looks like nginx (and nginx-based k8s ingress) does not like lowercase. OK, I can close this and we will make sure we don't use methods not supported by the APIs we are calling.

@dglozic dglozic closed this as completed Apr 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants