I am trying to use RequestBuilder in GWT to see if Accept-Ranges is supported.
Following is my client code:
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET,pathToServlet);
builder.setHeader("Range","bytes=0-10");
RequestCallback callback = new RequestCallback() {
#Override
public void onError(Request arg0, Throwable arg1) {
}
#Override
public void onResponseReceived(Request req, Response res) {
log.info("Text:"+res.getText());
log.info("Code:"+res.getStatusCode());
}
};
try {
builder.sendRequest(null, callback);
} catch (RequestException e) {}
And my servlet code is just a simple test code:
public class RangeTest extends HttpServlet{
static Logger log = Logger.getLogger(RangeTest.class);
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String output = new String("This is a test string to be sent to the client");
response.setContentType("text/xml");
PrintWriter out = response.getWriter();
out.println(output);
}
}
In the output I get on the client, following is printed:
Text:This is a test string to be sent to the client
Code:200
What I expected was, since I gave the range header as 0-10 in the request, only the first 10 bytes will be sent to the client. But here the entire string is getting sent. What am I doing wrong here? Is there anything I have missed?
I feel my comment is more readable for other as answer (and effectively it is one):
You are not evaluating the range-header in your servlet-method. And the super class HttpServlet does not evaluate it either (but DefaultServlet from Tomcat).
The servlet specification has left most of the implementation work to providers like Apache. This explains why API classes like HttpServlet does not do the work of interpreting special http headers, but provider classes like the mentioned Tomcat-DefaultServlet. The main purpose of a specification is mainly to enable different implementations not to force people to only one.
Related
Cannot get response body in my filter. Caching enabled.
I have tried many filter implementations - Filter, OncePerRequestFilter and GenericFilterBean. I have also tried writing custom caching mechanism but non of that work. I have already one logging filter working - this filter is executed before the new one and serves for reading request and response. The logging filter works but I want to add another one which reads response and validate it against XSD. Problem is, that logging filter gets the response filled OK but the XML validator filter gets empty string.
My #Controller method just returns Callable. Asynchronous processing may not be problem tho because logger filter works well.
#Component
public class ResposeBodyXmlValidator extends OncePerRequestFilter {
private final XmlUtils xmlUtils;
private final Resource xsdResource;
public ResposeBodyXmlValidator(
XmlUtils xmlUtils,
#Value("classpath:xsd/some.xsd") Resource xsdResource
) {
this.xmlUtils = xmlUtils;
this.xsdResource = xsdResource;
}
#Override
protected void doFilterInternal(
HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain
) throws ServletException, IOException {
ContentCachingResponseWrapper response = new ContentCachingResponseWrapper(httpServletResponse);
doFilter(httpServletRequest, response, filterChain);
if (MediaType.APPLICATION_XML.getType().equals(response.getContentType())) {
try {
xmlUtils.validate(new String(response.getContentAsByteArray(), response.getCharacterEncoding()), xsdResource.getInputStream());
} catch (IOException | SAXException e) {
String exceptionString = String.format("Chyba při volání %s\nNevalidní výstupní XML: %s",
httpServletRequest.getRemoteAddr(),
e.getMessage());
response.setContentType(MediaType.TEXT_PLAIN_VALUE + "; charset=UTF-8");
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.getWriter().print(exceptionString);
}
}
response.copyBodyToResponse(); // I found this needs to be added at the end of the filter
}
}
I expect all my filters to be able to read response body which is cached.
Update
I was mistaken myself. Even LoggerFilter cannot read seponse. I think this has something to do with asynchronous processing of a request.
Our application makes various usages of apache HttpAsyncClient:
CloseableHttpAsyncClient client= ...
HttpGet get = new HttpGet(...);
Future<HttpResponse> f = client.execute(get,null);
HttpResponse resp=f.get()
I'm looking for some hook to capture the response just before it's passed on to the business code that invoked 'f.get()' . Inside this hook, I'll perform auditing and security sanitation. BTW Responses are short texts, so there's no problem with buffering.
Would anyone please happen to know of such hooks?
I tried HttpRequestInterceptor, but it seems to work only for synchronous client:
// hook to audit & sanitize *synchronous* client response:
HttpClients.custom().addInterceptorLast(new HttpRequestInterceptor(){
public void process(HttpRequest req, HttpContext ctx) {
HttpEntityEnclosingRequest enclosing=(HttpEntityEnclosingRequest)req;
String body=EntityUtils.toString(enclosing.getEntity());
// ... audit 'body'
// ... sanitize 'body'
enclosing.setEntity(new StringEntity(sanitizedBody))
Unfortunately it doesn't work for async client - I suspect the interceptor runs before response is ready; I'm looking for a hook that runs when async response is ready.
Thanks
Consider using a custom HttpAsyncResponseConsumer. This should give you a complete control over the response message processing.
CloseableHttpAsyncClient client = HttpAsyncClients.createDefault();
HttpAsyncResponseConsumer responseConsumer = new BasicAsyncResponseConsumer() {
#Override
protected void onResponseReceived(final HttpResponse response) throws IOException {
super.onResponseReceived(response);
}
#Override
protected void onEntityEnclosed(final HttpEntity entity, final ContentType contentType) throws IOException {
super.onEntityEnclosed(entity, contentType);
}
#Override
protected void onContentReceived(final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
super.onContentReceived(decoder, ioctrl);
}
#Override
protected HttpResponse buildResult(HttpContext context) {
return super.buildResult(context);
}
#Override
protected void releaseResources() {
super.releaseResources();
}
};
client.execute(HttpAsyncMethods.createGet("http://target/"), consumer, null);
PS: one can have access to message content stream from inside a protocol interceptor with blocking HttpClient but not with HttpAsyncClient
I'm using Dropwizard 0.8.0 that comes with Jetty-Jersey-Jackson stack.
For security reasons, I want to add a filter that makes every request that passes through a particular route defined with Jersey returns always 200, even in case of error (4xx, 5xx, etc.).
Is this possibile with Jetty/Servlet filters? Can I intercept the request after it passed through Jersey resource (the controller), but before it is returned to client, in order to modify the http status code?
UPDATE:
I'm trying to do this with a ServletFilter, but it seems that the response is sent to client before my code is executed.
I've written the filter this way:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
if (response instanceof HttpServletResponse) {
/* No pre-processing */
chain.doFilter(request, response);
/* Post-processing: */
HttpServletResponse modifiedResponse = (HttpServletResponse) response;
if (modifiedResponse.getStatus() != 200) {
modifiedResponse.setStatus(200);
}
}
}
With this, registered in Dropwizard with:
environment.servlets().addFilter("MyCustomFilter", new MyCustomFilter())
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/mypath/*");
The filter is executed, and in the access.log I see all the requests with status code 200; but the client always get the "real" status code (ex, a 404, or a "Method not allowed").
It seems that the response is sent to client before the last part of filter is executed. Moreover, I can't modify the response body. I tried also with a HttpServletResponseWrapper, but with no luck.
You could create your custom javax.ws.rs.ext.ExceptionMapper<RuntimeException>. In that case, every exception that you get from your server, you can resolve it to 200.
Check this guy out. It should be easy to integrate with what you need.
Instead of what the tutorial shows you, like this example:
if (webAppException.getResponse().getStatus() == 401) {
return Response
.status(Response.Status.UNAUTHORIZED)
.entity(new PublicFreemarkerView("error/401.ftl"))
.build();
}
You code will be
if (webAppException.getResponse().getStatus() == 401) {
return Response
.status(Response.Status.OK)
.build();
}
I'm updating the question with my solution that finally works: I've used a Jersey filter instead of a Jetty http filtering, in order to manage directly the Response object from Jersey.
Obviously, this works only if you're using Jetty+Jersey, not with Jetty stand-alone.
Here's the filter I'm using:
/**
* When active, this filter transforms all responses for specified basePath to 200, even in case of error.
*/
#Provider
public class DiscardErrors implements ContainerResponseFilter
{
private String basePath;
public DiscardErrors(String basePath)
{
this.basePath = basePath;
}
#Override
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) throws IOException
{
if containerRequestContext.getUriInfo().getPath().startsWith(this.basePath) {
if (containerResponseContext.getStatus() != 200) { // Check if response code is different from 200
containerResponseContext.setStatus(200); // Force 200 status code
containerResponseContext.setEntity(""); // Empty body
}
}
}
}
And register it in Dropwizard:
environment.jersey().register(new DiscardErrors("/"));
I'm in the midst of testing my application which is using an HTTP-server. Instead of mocking I decided to go with a HTTP server fixture. Meaning that I do not have to mock any productional code. To accomplish this goal I currently chose for a free to use 3rd party library fixd.
I was able to successfully create several unit tests - which are working by means of a GET request. Most are quite simple, i.e.:
#Test
public void verifyConnectionTest()
{
try
{
final String body = FileUtils.readFileToString(RESOURCE);
final String path = "/";
this.server.handle(Method.GET, path).with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response)
{
response.setStatusCode(200);
response.setContentType("text/xml");
response.setBody(body);
}
});
// Setting up my HTTP client
// Execute some tasks
// asserting of everything was valid
}
catch (final IOException e)
{
fail(e.getMessage());
}
}
But I now have to send a POST request with multipart/form-data. Which does not make much of a difference other than changing the method and content-type:
#Test
public void executeStepTest()
{
try
{
final String body = FileUtils.readFileToString(SERVICE_RESPONSE);
final String path = "/";
this.server.handle(Method.POST, path, "multipart/form-data").with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response)
{
response.setStatusCode(200);
response.setContentType("text/xml");
response.setBody(body);
}
});
// Setting up my HTTP client
// Execute some tasks
// asserting of everything was valid
}
catch (final IOException e)
{
fail(e.getMessage());
}
}
However I get the following error: [ERROR] could not find a handler for POST - / - multipart/form-data; boundary=bqCBI7t-VW1xaJW7BADmTiGMg9w_YM2sHH8ukJYx and my guess is that fixd doesn't recognize the boundary-party. Since the documentation does not show an example I'm quite stuck on this part.
I tried using some wildcards such as '*', no succes. Thus; I need a way to either tell fixd to accept that boundary or use some wildcards I didn't yet discover. Any help would be great, thanks!
I've been making some debug and it seems to be that the problem is in the fixd core.
Basically, fixd indexes every RequestHandlerImpl by a HandlerKey (which includes ContentType as part of the key) in the map handlerMap. See method org.bigtesting.fixd.core.FixtureContainer#resolve.
...
HandlerKey key = new HandlerKey(method, route, contentType);
RequestHandlerImpl handler = handlerMap.get(key);
if (handler == null) {
// Error
}
...
Problem: When the request is multipart/form-data, boundary data (which it's generated dinamically every request) is part of the content type. So, any handler is found in handlerMap because the key changes with every running.
I've made a little test only to check that this is the cause of the problem, passing the contentType to fixd server.handle after the creation of the multipart request, and it works fine.
See the test below:
#Test
public void verifyConnectionTest_multipart() {
try {
// 1. Create multipart request (example with http-commons 3.1)
PostMethod filePost = new PostMethod(url);
Part[] parts = { new StringPart("param", "value") };
MultipartRequestEntity request = new MultipartRequestEntity(parts, filePost.getParams());
filePost.setRequestEntity(request);
// 2. fixd server handle (passing the request content type)
this.server.handle(Method.POST, "/", request.getContentType()).with(
new HttpRequestHandler() {
#Override
public void handle(final HttpRequest request,
final HttpResponse response) {
response.setStatusCode(200);
response.setContentType("text/xml");
}
});
// 3. Execute multipart request
HttpClient client = new HttpClient();
int status = client.executeMethod(filePost);
// 4. Assertions
Assert.assertEquals(200, status);
} catch (Exception e) {
Assert.fail(e.getMessage());
}
}
Hope it helps you to clarify the problem. Cheers
This was a bug in fixd, and has been fixed in version 1.0.3. Your original code should work using this new version of fixd.
I am writing a servlet filter to forward Jersy requests based on certain condition. But they does not seem to forwarding.
public class SampleFilter
extends GenericFilterBean
{
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException
{
String generateRedirectUrl=FormURL((HttpServletRequest)req);
RequestDispatcher dispatcher = req.getRequestDispatcher(generateRedirectUrl);
dispatcher.forward(req, resp);
}
private String FormURL(HttpServletRequest req)
{
// get the request check if it contains the customer
String reqUrl = req.getRequestURI();
log.info("Original Url is"+reqUrl);
if(reqUrl.contains("test"))
{
return "/api/abcd/" +"test";
}
return "Someurl";
}
}
I need to forward the url as below.
Original: http://localhost/api/test/1234/true
New URL:http://localhost/api/abcd/1234/true
Am I doing any thing wrong.
"Am I doing any thing wrong."
In general I think this will work - you can forward from a filter, but your logic is wrong.
Using your rule below:
Original: http://localhost/api/test/1234/true
New URL:http://localhost/api/abcd/1234/true
The code:
if(reqUrl.contains("test"))
{
return "/api/abcd/" +"test";
}
will produce /api/abcd/test which is not what you're after.
I would do something like the following:
private String formURL(HttpServletRequest req) {
// get the request check if it contains the customer
String reqUrl = req.getRequestURI();
if (log.isInfoEnabled() {
log.info("Original Url is"+reqUrl);
}
if(reqUrl.contains("test")) {
return reqUrl.replace("test", "abcd");
}
return "Someurl";
Also, the code is very brutal. This will also change the URL
http://test.site.com/abd/def
to
http://abcd.site.com/abd/def
which is probably not what you want. You'll probably need to either do more clever string manipulation or convert to something like the URI class which will allow you to target the path more accurately.
Hope this helps,
Will