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
Related
I am having trouble with Apache camel REST API CORS , It is working for GET request but not for other methods.
restConfiguration()
.component("servlet")
.bindingMode(RestBindingMode.auto)
.enableCORS(true)
.corsAllowCredentials(true);
Actual Rest end point implementation
from("rest:post:endpoint1")
//.setHeader("Access-Control-Allow-Credentials",constant( true))
.unmarshal().json(JsonLibrary.Jackson, request.class)
.process(processor);
When adding header to rest request it working for GET request.
You probably need to specify more things, eg
the list of allowed methods
the list of allowed origins
This can be done using adhoc HTTP headers:
.setHeader("Access-Control-Allow-Origin", constant("*") )
.setHeader("Access-Control-Allow-Methods", constant("GET, POST, OPTIONS") )
It is likely not supported by the old Rest component as it is only meant to be used for basic use cases.
For advanced features like CORS, you need to define your rest endpoints using the Rest DSL as next:
rest("/")
.post("/endpoint1").to("direct:endpoint1");
from("direct:endpoint1")
.unmarshal().json(JsonLibrary.Jackson, request.class)
.process(processor);
With the exact same Rest configuration (enableCORS(true)) but with your endpoint defined with the Rest DSL, the CORS HTTP headers will automatically be added to your HTTP responses whatever the HTTP method used.
Here are the HTTP response headers of a POST endpoint defined using the Rest DSL:
curl -v localhost:8080/say -X POST
< HTTP/1.1 200 OK
< Date: Mon, 11 Apr 2022 11:55:57 GMT
< Content-Type: application/json
< Accept: */*
< Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers
< Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH
< Access-Control-Allow-Origin: *
< Access-Control-Max-Age: 3600
< User-Agent: curl/7.79.1
< Transfer-Encoding: chunked
< Server: Jetty(9.4.45.v20220203)
Here are the HTTP response headers of a POST endpoint defined using the old Rest component:
curl -v localhost:8080/hello -X POST
< HTTP/1.1 200 OK
< Date: Mon, 11 Apr 2022 11:55:53 GMT
< Accept: */*
< User-Agent: curl/7.79.1
< Transfer-Encoding: chunked
< Server: Jetty(9.4.45.v20220203)
Simple question.Got registered Payload URL on Github: using ngrok.com (ngrok) link like explained in Github documentation for Webhooks: Creating Webhooks
ngrok definition: “I want to securely expose a local web server to the internet and capture all traffic for detailed inspection and replay.”
When i send POST request with payload from github on correct Payload URL the response code is 200, how can I handle that request/response and get payload (JSON) in java? With servlet or?
I have no idea where to start. Tried to search but nothing for Java :(
If i put ngrok.com/something, Eclipse console throw:[WARN] 404 - POST /pas (127.0.0.1) 1368 bytes
Request headers
Host: ....ngrok.com
X-Real-IP: 192....
X-Forwarded-Proto: http
Connection: close
Content-Length: 5759
Accept: */*
User-Agent: GitHub-Hookshot/e9dfd89
X-GitHub-Event: ping
X-GitHub-Delivery: c5493000-b67e-11e4-8199-8b09d3d66948
Content-Type: application/json
X-Hub-Signature: sha1=b2947ce6a6de23f4274831523bae375d64e20021
Response headers
Connection: close
Content-Type: text/html;charset=ISO-8859-1
Cache-Control: must-revalidate,no-cache,no-store
Content-Length: 1368
If i put good URL, status is 200. Response on Github Webhooks / Manage webhook:Accept-Ranges: bytes
Connection: keep-alive
Content-Length: 1521
Content-Type: text/html
Date: Tue, 17 Feb 2015 10:17:46 GMT
Last-Modified: Thu, 12 Feb 2015 09:06:18 GMT
Server: nginx/1.6.2
So the question is actually "How to handle that payload?"
In documentation they use Sinatra and that's a big ? for me.
Sinatra code looks like this:
require "sinatra"
require "json"
post "/payload" do
push = JSON.parse(request.body.read)
puts "I got some JSON: #{push.inspect}"
end
New to this, sorry if its stupid question.
Resolved, i used HttpServlet doPost method to fetch request, then from request i getReader() and read line so i can make JSONObject. My servlet is on page/Payload and Webhook is on http://server.com/page/Payload
public class Payload extends HttpServlet {
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
StringBuilder builder = new StringBuilder();
String aux = "";
while ((aux = req.getReader().readLine()) != null) {
builder.append(aux);
}
String text = builder.toString();
try {
JSONObject json = new JSONObject(text);
String teams_url = json.getJSONObject("repository").getString("teams_url");
System.out.println("Teams URL:: "+teams_url);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}}
You can make use of gitlab4j-api library. For example usage, have a look at simple review project, exactly here: https://github.com/gitlab4j/simple-cr/blob/master/src/main/java/org/gitlab4j/simplecr/service/GitLabWebHookService.java
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!
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.