I am working on a Java program that implements an HTTP Client.
I test it sending requests to a server. GET, POST and DELETE requests work ok.
For example after a POST request I get an output
Data extracted:
{"status":{"message":"ok"}}
and the database reflects the changes made.
After a PUT request, however I get the following html markup of a webpage indicating an error.
Data extracted:
<html>
<head><title>411 Length Required</title></head>
<body bgcolor="white">
<center><h1>411 Length Required</h1></center>
<hr><center>nginx/1.2.6</center>
</body>
</html>
and accordingly no changes in the database.
I found that this can have something to do with the Content-Length header, but I'm not sure.
After trying to add this header my program waits for a minute and then throws an exception informing that it couldn't handle the server response.
I can also provide any code or stack trace if needed.
Yes, the issue related to Content-Length. HTTP Error 411 means
The server refuses to accept the request without a defined Content- Length. The client MAY repeat the request if it adds a valid Content-Length header field containing the length of the message-body in the request message.
So when you send an empty RequestBody in POST/PUT Method then you also need to send Content-Length:0. So add this header in your request. I don't think this header will cause a problem of you added into Request Object.
Related
I noticed our code is adding the access token / authorization token to the response in the form of a cookie, using
((HttpServletResponse) response).addCookie(accessTokenCookie);
I've done some research and discovered this info:
Request cookies are the cookies sent from browser to server
Response cookies are the cookies sent out (from server to browser).
If this is the case, wouldn't my request interceptor / doFilterInternal need to apply the access token cookie to the request before it goes through, and not the response?
What makes it more confusing is the access token cookie is being created by fetching the access token value using request.getAttribute. If the request already has the access token as an attribute, why does a cookie needed to be added to the response?
You misunderstand how cookies work.
The browser sends a request to some resource on some server.
The server returns the bytes that comprise the resource.
That's the basic flow. Now, let's talk about cookies:
When the server returns those bytes, it can send headers along with it. It is in fact required; for example, to talk about what the data is representing. For example, if you ask for /img/background.png, the server returns the image data and also sends Content-Type: image/png. Because browsers don't actually do the whole 'extensions indicate what the data is' thing, that's what that Content-Type header is for.
One of those headers is a Set-Cookie header.
Set-Cookie headers tell the browser: Take this data and save it somewhere. Next time you load this resource, send it again. There's some control about when to send it (you can make a Set-Cookie header sent when the server reponds for request /foo/bar make the browser send that cookie back even when asking for /foo/baz, or e.g. when foo.myserver.com sends a cookie, it can tell the browser: Send that back also when requesting resources from bar.myserver.com. There are limits though; when setting a cookie when responding to a request on myserver.com, you can't tell the browser to send that back when sending to google.com. Browsers are complex beasts; they ship with a huge list of top-level-ish domains and won't let you do this.
You don't 'add' cookies to a 'request' - the request stuff (the data you can obtain from an HttpServletRequest object) is just representing what the browser sent the server. The model for HttpServletRequest/Response is strictly two phases: The browser sends a request, and the server responds. That's it. It's not a long conversation. Just one message each way, that is all you get.
So, you add a cookie to the 'response' which means that future requests made by that same browser will then send it in the 'request'.
I am calling an API, that blacklists certain HttpHeaders including Content-Length which seems to be preset by the HttpClient underneath spring-openfeign.
To properly receive an API response, I'd need to remove the Content-Length header.
The following workarounds had been tried:
I tried to set the header to null or an empty String using the available Feign annotations #Headers, #RequestHeaders
I implemented a RequestInterceptor that creates a copy of the available (immutable) header map, deletes the blacklisted header and sets the Map as requestTemplate.headers(newHeaders). But only new headers can be added and the available ones not modified (seems to be really immutable ;))
I researched on overriding the used HttpClient but wasn't successful until now.
Experienced errors/ issues:
The API I am calling returns a 400 based on their header schema validation.
Code:
In case any code-snippets are needed, I am happy to provide them but to me the issue does not seem to be related to any code issue as I am not running into any exceptions.
Thanks in advance!!
The Apache Http Client included in feign-httpclient will always set the content length header if there is a request body present. One way to address this to configure the Apache Client directly and provide it to Feign via the builder:
This custom client can have an Apache Http Client interceptor applied that allows you to modify the request after it leaves Feign and before Apache sends it. Review their javadoc for more information.
public class Example {
public static void main(String[] args) {
HttpClient httpClient = HttpClients.custom.build();
GitHub github = Feign.builder()
.client(new ApacheHttpClient(httpClient))
.target(GitHub.class, "https://api.github.com");
}
}
FeignClient will preset Content_Length in the request header. In a keep-alive connection mode, either Content-Length or Transfer-Encoding header field must be set to signal the presence of a message body, so you can set Transfer-Encoding=chunked and Content-Length will be ignored by the serverside.
You can refer to rfc7230#section-3.3.1
"The presence of a message body in a request is signaled by a
Content-Length or Transfer-Encoding header field. Request message
framing is independent of method semantics, even if the method does
not define any use for a message body."
"In order to remain persistent, all messages on a connection need
to have a self-defined message length (i.e., one not defined by
closure of the connection), as described in Section 3.3. A server
MUST read the entire request message body or close the connection
after sending its response, since otherwise the remaining data on a
persistent connection would be misinterpreted as the next request.
Likewise, a client MUST read the entire response message body if it
intends to reuse the same connection for a subsequent request."
and from here , you can read:
"All HTTP/1.1 applications that receive entities MUST accept the
"chunked" transfer-coding (section 3.6), thus allowing this mechanism
to be used for messages when the message length cannot be determined
in advance.
Messages MUST NOT include both a Content-Length header field and a
non-identity transfer-coding. If the message does include a non-
identity transfer-coding, the Content-Length MUST be ignored.
When a Content-Length is given in a message where a message-body is
allowed, its field value MUST exactly match the number of OCTETs in
the message-body. HTTP/1.1 user agents MUST notify the user when an
invalid length is received and detected."
I have a simple REST client with some basic functionality, and so far I'm stuck as I don't know how to process those request and send them correctly into the server. So far I've tried this in the filter, without any luck.
if (!request.getHeader("/rest/").equals(null)){
String loginForm = config.getInitParameter("LoginParam");
res.sendRedirect(req.getContextPath() + loginForm);
return;
}
And I get the following error because of that deny.
Exception in thread "main" org.jboss.resteasy.client.ClientResponseFailure
How should I check that the request is coming from the REST client so I can let it pass through?
I'd normally add header Accept: application/json (or xml or whatever) to indicate the client wants to get data as oppose to HTML.
In a java web-app I write to my HttpServletResponse:
httpResponse.getWriter().write(someJsonString);
httpResponse.getWriter().flush();
The client (apache jmeter in this case) gets the response with the json in the body and status 200 as expected.
If I decide to change the response status:
httpResponse.getWriter().write(someJsonString);
httpResponse.setStatus(Response.Status.NO_CONTENT.getStatusCode());
httpResponse.getWriter().flush();
My client gets the response with the right status (204 in this case) but an empty body for some reason.
What can cause this?
When you send response as 204, it means there is no body.
See w3c rfc
The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields.
It means while sending response either container is not considering body or your client is discarding after reading status in response.
One way could be to check this response in web-browser if possible. With tools like fire-bug or similar in Chrome you could actual check response.
I have a client who is posting some data to our server with http POST method. Our server is resin 3.0 with java. When I send response whether data is saved or not the the content length of the response is not set. client is using curl library(php wrapper over it) and they are receiving content length as 0. When I try to submit a form through a browser to our server on the same url it works and response is shown.
I tried using Apache HttpClient to submit data through postmethod and I received content length as -1 but i did get the full response. I'm not able to understand where is the problem. Also I did some google and found that resin do some chunked encoding while sending the response. But i guess it does this also for GET method. But for GET method my client is getting the content length and is able to get the response as well. Need help with this.
"Content-Length" is a header in the response, which warns the client on how big the response will be. It is not the actual length of the stream.
You can set it's value with response.setContentLength(...); in your Servlet.