I found an inconsistency between Java's dev_appserver and the live App Engine server.
On my local development server I have a Servlet which returns:
return response.sendError(response.SC_BAD_REQUEST, "Please log in to comment");
When I access the page I get back a Status Code message in the header which is:
Status Code:400 Please log in to comment
The issue comes when I deploy this to App Engine. When accessing that same servlet I get this "Bad Request" instead of "Please log in to comment":
Status Code:400 Bad Request
The Please log in to comment Status Code message appears in the content HTML, but not in the header as it does in the development environment.
Why is this?
Edit
Here's the curl -vvvv traces for both dev_appserver and production:
dev_appserver curl trace:
> POST /add-comment HTTP/1.1
> User-Agent: Mozilla/4.0
> Host: localhost:8080
> Accept: */*
> Content-Length: 9
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 400 Please log in to comment
< Content-Type: text/html; charset=iso-8859-1
< Cache-Control: must-revalidate,no-cache,no-store
< Content-Length: 1406
< Server: Jetty(6.1.x)
Production curl trace:
> POST /add-comment HTTP/1.1
> User-Agent: Mozilla/4.0
> Host: www.xxx.org
> Accept: */*
> Content-Length: 9
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 400 Bad Request
< Content-Type: text/html; charset=utf-8
< Vary: Accept-Encoding
< Date: Thu, 18 Aug 2011 14:04:26 GMT
< Server: Google Frontend
< Cache-Control: private
< Transfer-Encoding: chunked
I would say the prod system is the correct implementation. The javadocs for sendError() say:
Sends an error response to the client using the specified status. The
server defaults to creating the response to look like an
HTML-formatted server error page containing the specified message,
setting the content type to "text/html", leaving cookies and other
headers unmodified. If an error-page declaration has been made for the
web application corresponding to the status code passed in, it will be
served back in preference to the suggested msg parameter.
If the response has already been committed, this method throws an
IllegalStateException. After using this method, the response should be
considered to be committed and should not be written to.
I highlighted a part. This says it just returns a html page with the message when possible. It doesn't say it uses it in the HTTP Status code (which I personally haven't seen anywhere as well :()
It isn't specifically a problem with sendError. The setStatus method will behave the same way. Under normal Java, both sendError and setStatus do set the status description. The issue is that the production App Engine server always sets the status description to the standard description for each code.
Related
So, I'm trying to create a simple (and I mean simple) POST request. Here is the class on the server-side.
#Stateless
#Path("cards")
public class CardsFacadeREST extends AbstractFacade<Cards> {
#POST
#Path("test")
#Consumes({"text/plain"})
public void createTestCard() {
Cards card = new Cards();
card.setName("Test Card");
super.create(card);
}
#GET
#Path("count")
#Produces("text/plain")
public String countREST() {
return String.valueOf(super.count());
}
}
The GET method works just fine, but the POST method isn't working for me. I'm using Chrome's Advanced Rest Client.
The URL is http://localhost:8080/dc-rest/webresources/cards/test
The type is POST
My header is like so: Content-Type: text/plain
And that's it.
I keep getting a "400: Bad Request. The request sent by the client was syntactically incorrect."
When I open the response in the JSON window, all it says is "Unexpected token <"
Here are the request headers if that makes any difference.
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.103 Safari/537.36
Origin: chrome-extension://hgmloofddffdnphfgcellkdfbfbjeloo
Content-Type: text/plain
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: JSESSIONID=f4c746a32b46244d422800192f04; treeForm_tree- hi=treeForm:tree:applications
Body is empty.
And the response:
X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 4.0 Java/Oracle Corporation/1.7)
Server: GlassFish Server Open Source Edition 4.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Allow: GET,DELETE,OPTIONS,PUT,POST
Access-Control-Allow-Headers: content-type
Content-Language:
Content-Type: text/html Date: Thu, 11 Feb 2016 20:48:12 GMT
Connection: close Content-Length: 1105
Body:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>GlassFish Server Open Source Edition 4.0 - Error report</title><style type="text/css"><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 400 - Bad Request</h1><hr/><p><b>type</b> Status report</p><p><b>message</b>Bad Request</p><p><b>description</b>The request sent by the client was syntactically incorrect.</p><hr/><h3>GlassFish Server Open Source Edition 4.0 </h3></body></html>
The 400-Bad request response is likely because the server has expressed that it expects a specific content type
#Consumes({"text/plain"}
However the client is not indicating that the post body is of this type.
To solve this, make sure that the POST request from your client contains the following HTTP header:
Content-Type: text/plain
Or, perhaps it is the case that you are not POSTing plain text and you intend to POST XML or JSON. Whatever the intended type, you just need to make sure the client and server are in agreement about it.
If an HTTP request has a body, it must have either Content-Length or Transfer-Encoding header.
With neither, the request has no body -- not even a body of length 0. If you want to send an empty body, the request should have header Content-Length: 0.
There is a semantic difference between having no body and having an empty body. The server apparently rejects the POST request with no body. (Though the request is actually syntactically valid according to RFC)
Actually, this part is not quite clear (discussion thread (don't read)). And some implementations set Content-Length: 0 for GET requests; some implementations omit Content-Length: 0 for empty POST bodies; both are wrong... Sometimes they work sometimes they don't. Welcome to the chaotic world of HTTP.
I hate it when this happens. It was something else entirely, my persistence bean is messed up. When I commented out "super.create(card)" in my POST method, everything worked (request body or no).
Still have no idea why that resulted in "400: Bad Request. The request sent by the client was syntactically incorrect."
Seems like I have another problem to figure out, but this one at least is solved.
Thank y'all for helping!
Is it possible to analyze the header of a HTTP-(POST)-Request before the body is transmitted?
I would like to send an error to a client if the file he is trying to upload via an HTTP-POST is to large to handle for the server. To improve the user experience (and safe traffic) I would prefer to send the error response before he uploads the whole file, by analyzing the content-length-header.
I thought about implementing a javax.servlet.filter like this:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request.getContentLength() > MAX_DOCUMENT_SIZE) {
ObjectMapper jsonMapper = new ObjectMapper();
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("messageCode", 1234);
jsonMap.put("messageDescription", "error message");
response.reset();
response.setStatus(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);
jsonMapper.writeValue(response.getWriter(), jsonMap);
return;
}
}
chain.doFilter(request, response);
}
but I am not sure if the tomcat is able to analyze the headers before the hole request was transmitted.
EDIT: curl
> CONNECT myserver.com:443 HTTP/1.1
> Host: myserver.com:443
> Proxy-Connection: Keep-Alive
> user-agent: my-test
>
< HTTP/1.0 200 Connection established
<
* Proxy replied OK to CONNECT request
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
* Server certificate: myserver.com
> POST /uploads HTTP/1.1
> Host: myserver
> Accept: */*
> user-agent: my-test
> Content-Length: 51951089
> Content-Type: application/x-www-form-urlencoded
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
< HTTP/1.1 404 Not Found
< Date: Tue, 01 Sep 2015 09:49:01 GMT
* Server WEB is not blacklisted
< Server: WEB
< X-XSS-Protection: 1; mode=block
< X-DNS-Prefetch-Control: off
< X-Content-Type-Options: nosniff
< X-Frame-Options: sameorigin
< Strict-Transport-Security: max-age=15768000 ; includeSubDomains
< Cache-Control: private
< Expires: Thu, 01 Jan 1970 01:00:00 GMT
< Content-Type: text/plain;charset=utf-8
< Content-Length: 0
< Vary: Accept-Encoding
< Connection: close
<
Yes, this is possible and I am actually doing this in Tomcat 6. I have not tried a Filter but a Tomcat-specific Valve. If this solution works as a Vavle, feel free to port it to a Filter.
Have Tomcat to invoke your valve. Your demo code looks just the way it should look, make sure that you send a 400 (or better 413) to indicate that the input is not appropriate. You may close the connection too. Now here comes the very important part: to make this work, the client has to POST or PUT the request, additionally the client must send a Expect: 100-continue header. The server will analyze all incoming headers and signal the client that the request is inappropriate. With that, the client will receive a 400 (or better 413) before it sends off its payload (request will be aborted). You'll exchange headers only and save resources. But beware, your clients must properly implement Expect: 100-continue. .NET clients don't. I would strongly recommend to verify the proper working of your valve with curl because it does the stuff right. If you'd like to see wire headers for this, no problem.
Caveat: your content length limit won't work if the client streams its payload in chunks.
You write a java script function and bind it onchange event on input type="file" files[0].size will give u size of the file.So u can check if it is uploadable.
Thx
I have developed a raw http post in java. I am trying to post a file to the post request dump website http://www.posttestserver.com/. But it shows and error
400 Bad Request. Pleas let me know what need to be done to avoid this error.
In this code , output => Stream to write on server.
filename -> path on server, here filename is initated to post.php
output.println("POST"+" "+filename+" HTTP/1.1\r");
//output.println("Content-Length: "+data.length());
output.println("Content-Type: multipart/form-data, boundary=AaB03x\r");
output.println("Content-length: 100\r");
//As http1.1 is by default keep-alive , close the connection explicitly
output.println("Connection: Close");
// blank line
output.println();
output.println("--AaB03x");
output.print(
"--AaB03x Content-Disposition: form-data; name=\"fileID\"; filename=\"temp.txt\" Content-Type: text/plain "
+"/nHello How are you?"
+ "/n--AaB03x--");
output.flush();
Error is
HTTP/1.1 400 Bad Request
Date: Wed, 18 Mar 2015 02:22:00 GMT
Server: Apache
Vary: Accept-Encoding
Content-Length: 226
Connection: close
Content-Type: text/html; charset=iso-8859-1
400 Bad Request
Bad Request
Your browser sent a request that this server could not understand.
This might be the issue of Content type. Server is expecting a request having header of content type text/HTML but your request content type is multipart/form data.
I'm having a weird issue where if my servlet sends a 200 ok http status, it works as intended, and the client gets a 200 status as expected. But, if my servlet returns a 404 status, tomcat seems to change it into a 403 status. This doesn't happen if I use the http get method. I haven't tested put or post.
I want to make it very clear that my servlets doDelete method gets executed just fine. It's just that the status code returned to the browser gets changed.
I'll provide a minimal testcase demonstrating the issue.
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
#WebServlet("/api/test403/*")
public class Test403 extends HttpServlet {
public void doDelete(HttpServletRequest request, HttpServletResponse response) {
try {
String p = request.getParameter("send404");
if (p != null && "1".equals(p)) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "not found.");
} else {
response.sendError(HttpServletResponse.SC_OK, "ok.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static final long serialVersionUID = 1L;
}
then i test via the following urls
myproject/api/test403?send404=1
myproject/api/test403?send404=0
What could cause this behavior? I'm not overly familiar the whole servlet/container architecture. I'm only experiencing this issue on 1 server which uses tomcat 7.0.41. I tried another server, which didn't exhibit this behavior.
edit - Per request, here's some output from the network panel in chrome. I used ajax to initiate this particular request:
Request Headers
DELETE /xxxxx HTTP/1.1
Host: xxxxx
Connection: keep-alive
Origin: xxx
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: */*
Referer: xxx
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: xxx
Response Headers
HTTP/1.1 403 Forbidden
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Content-Encoding: gzip
Vary: Accept-Encoding
Date: Wed, 16 Apr 2014 02:30:32 GMT
I didn't remove any headers, although I anonymized some values.
A combination of...
an http DELETE request
calling HttpServletResponse.sendError(status, message) to send the 404 from your servlets doDelete() method
configuring a custom 404 error handler page(eg, via the <error-page> directive in web.xml)
Keeping the default value of readonly = true for your context
will cause the client to receive a 403 status instead of the 404 you though you sent.
A request to a servlet can service an http delete request without needing readonly to be false, but a request to a file cannot. What happens is that when you call sendError(), tomcat will try to find a custom error page that matches up with whatever http status you specified. In this case, it found one (/my404.html), and so in order to process it, it basically restarts the entire request routing/dispatching process, including running all the filters on the request. But, this time, since it's a static file request, it comes across a built in filter that looks for http the DELETE method, and then checks if readonly = false. If it's not, the request is rejected, and it changes the response status to 403 forbidden because you're not allowed to delete the static file named /my404.html.
A sensible workaround is to use HttpServletResponse.setStatus(status) instead of HttpServletResponse.sendError(status, message), so that tomcat doesn't try to find an error page. As mentioned by #BogdanZurac , you may also need to send a brief response body (i.e., "oops Error 404") in addition to setting the status to prevent it from seeking the custom error page.
I have a Jersey app that has been run through our corporations website vulnerability tool. It came back with a vulnerability that is quite odd. If you send in this header:
"*/*'"!#$^*\/:;.,?{}[]`~-_<sCrIpT>alert(81363)</sCrIpT>"
you actually get it back in the response and it is not escaped. Anyone think this is a problem?
Here is the actual response:
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)
Accept: */*'"!#$^*\/:;.,?{}[]`~-_<sCrIpT>alert(81363)</sCrIpT>
Pragma: no-cache
...
And one more thing. I just upgraded to jersey 1.14 and it still does this...
You mean in produced error message? Something like:
< HTTP/1.1 400 Bad Request
< Content-Type: text/plain
< Date: Thu, 27 Sep 2012 14:30:30 GMT
< Connection: close
< Transfer-Encoding: chunked
<
* Closing connection #0
The HTTP header field "Accept" with value "..." could not be parsed
?
If so, we can definitely do something about it, please report this as a new RFE at http://java.net/jira/browse/JERSEY. (I wasn't able to reproduce anything else related to this issue).
Thanks!