Can't send REST API request when Content-Length header is missing
Environment
- ejabberd version: 24.12
- Erlang version:
from the docker - OS: MAC arm64
- Installed from: docker
Configuration (only if needed): grep -Ev '^$|^\s*#' ejabberd.yml
loglevel: 4
...
Errors from error.log/crash.log
No errors
Bug description
I am using Spring Boot application that running REST API requests to the Ejabberd service. I have just updated the Spring vertion to the latest one an I see that when I use the RestTemplate to perform the API requests, I receive the following response:
<?xml version='1.0'?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
</head>
<body>
<h1>400 Bad Request</h1>
</body>
</html>
When I tried to reproduce the request in the Postman I saw the it looks like the header Content-Length is missing. A small reaserch told me the the reason this header is not being sent is that another header is sent: Transfer-Encoding: cunked.
The question is how can it be solved? It looks like that the Java RestTemplate doesn't sent the header.
I think you may be using wrong url, as i think this error is sent if we can't determine what command should be executed from url/payload.
@prefiks thanks for your response.
But, no, the request sent with the correct url (I tried to add manually a wrong Content-Length value and resent the request:
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setContentLength(23);
and the response was:
"I/O error on POST request for "http://localhost:5280/api/v0/create_room_with_opts/v3/":
too many bytes written"
For the same request without the content-length addition, the response was:
"400 Bad Request on POST request for "http://localhost:5280/api/v0/create_room_with_opts/v3/":
"<?xml version='1.0'?>
<EOL>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<EOL>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
<head><meta http-equiv='Content-Type' content='text/html; charset=utf-8'/></head><body>
<h1>400 Bad Request</h1></body></html>""
ejabberd includes a minimal HTTP server with those particularities:
That HTTP server doesn't read the content length header, so you can put wrong value or no header at all:
curl -X POST \
-H "Content-Length: 3" \
http://localhost:5280/api/register \
-d '{"user": "user-3-post-bad-length", "host": "localhost", "password": "asd2"}'
POST /api/register HTTP/1.1
Host: localhost:5280
User-Agent: curl/8.5.0
Accept: */*
Content-Length: 3
Content-Type: application/x-www-form-urlencoded
{"user": "user-3-post-bad-length", "host": "localhost", "password": "asd2"}
HTTP/1.1 200 OK
Content-Length: 63
Access-Control-Allow-Headers: Content-Type, Authorization, X-Admin
Access-Control-Allow-Origin: *
Content-Type: application/json
"User user-3-post-bad-length@localhost successfully registered"
That HTTP server does NOT support content with chunked encoding:
curl -X POST \
-H "Content-Length: 3" \
-H "Transfer-Encoding: chunked" \
http://localhost:5280/api/register \
-d '{"user": "user-4-post-transfer", "host": "localhost", "password": "asd2"}'
POST /api/register HTTP/1.1
Host: localhost:5280
User-Agent: curl/8.5.0
Accept: */*
Content-Length: 3
Transfer-Encoding: chunked
Content-Type: application/x-www-form-urlencoded
49
{"user": "user-4-post-transfer", "host": "localhost", "password": "asd2"}
0
HTTP/1.1 400 Bad Request
Content-Length: 17
Access-Control-Allow-Headers: Content-Type, Authorization, X-Admin
Access-Control-Allow-Origin: *
Content-Type: application/json
"400 Bad Request"
You can use a GET query. Notice that the transfer-encoding header is not read. The only important topic is that the content is not chunked encoded:
curl -X GET \
-H "Content-Length: 3" \
-H "Transfer-Encoding: chunked" \
http://localhost:5280/api/register?user=user-5-get-chunked\&host=localhost\&password=asd1
GET /api/register?user=user-5-get-chunked&host=localhost&password=asd1 HTTP/1.1
Host: localhost:5280
User-Agent: curl/8.5.0
Accept: */*
Content-Length: 3
Transfer-Encoding: chunked
HTTP/1.1 200 OK
Content-Length: 59
Access-Control-Allow-Headers: Content-Type, Authorization, X-Admin
Access-Control-Allow-Origin: *
Content-Type: application/json
"User user-5-get-chunked@localhost successfully registered"