Why is my upload error message only received by Edge? - servlets

I use MultipartConfig to handle file upload with a Servlet and send an error message if the file is to large:
private void doUpload(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
Part filePart;
try {
filePart = request.getPart("file");
} catch (IllegalStateException e) {
System.out.println("File To Large");
response.setStatus(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);
PrintWriter writer = response.getWriter();
writer.println("failure:filetobig");
writer.flush();
writer.close();
return;
}
... (file handling) ...
}
If I use the Servlet with Jquery-File-Upload (Ajax based) and the file-size exceeds maxFileSize, Edge returns the correct error-message with a status code of 413 and a ready-state of 4. If I do the same with Firefox or Chrome I receive a status-code of 0, a ready-state of 0 and the network-tab in dev-tools shows no answer is received.
image
In Firefox the bitrate switches from negative to positive and Chrome return NaN after a while. No matter which browser I use, the System.out.println("File To Large"); is executed. If I execute the Upload-Request with cURL the correct error-message is returned.
My MultipartConfig to test this:
#MultipartConfig(maxFileSize = 1024 * 1024 * 30, // 30 MB
maxRequestSize = 1024 * 1024 * 1000 // 1000 MB
)

I figured it out: The Server reset the connection because maxSwallowSize is set too low.
See: https://tomcat.apache.org/tomcat-8.0-doc/config/http.html
Weirdly enough Edge doesn't care for that and displays the correct message!?

Related

Creating error page in servlet filter causes error "Writer already obtained"

I'm creating a custom framework (something like portal) for numerous JSF 1.x and 2.x applications. For that purpose I created a servlet filter that "enrich" application HTML with framework menu, breadcrumb, logout, etc. In that filter I read app's HTML, modify it and write it to an output stream. So far everything worked great but now I'm having problem with creating a custom error page.
I tried to read a response status code and based on that code, I'm creating output HTML:
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) req;
HttpServletResponse res = (HttpServletResponse) resp;
StringServletResponseWrapper responseWrapper = new StringServletResponseWrapper(res);
// Invoke resource, accumulating output in the wrapper.
chain.doFilter(req, responseWrapper);
String contentType = res.getContentType();
byte[] data;
if (contentType.contains("text/html")) {
String html = null;
int statusCode = res.getStatus();
LOG.debug("status: {}, committed: {}", statusCode, res.isCommitted());
if (statusCode != 200) {
html = "<!DOCTYPE html>\r\n" +
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n" +
"<head>\r\n" +
"<script type=\"text/javascript\" src=\"/path/to/jquery/jquery-1.11.1.min.js\"></script>\r\n" +
"<title>Error</title>\r\n" +
"</head>\r\n" +
"<body>\r\n" +
"<h1>Error</h1>\r\n" +
"</body>\r\n" +
"</html>";
Collection<String> headerNames = res.getHeaderNames();
Map<String, String> headerMap = new HashMap<String, String>();
for (String header : headerNames) {
headerMap.put(header, res.getHeader(header));
}
res.reset();
for (Map.Entry<String,String> entry : headerMap.entrySet()) {
res.setHeader(entry.getKey(), entry.getValue());
}
res.setStatus(statusCode);
response.setContentType("text/html");
} else {
html = responseWrapper.getCaptureAsString();
}
if (ObjectUtils.isNotEmpty(html)) {
// do some modification
String modifiedResponse = doModification(html);
data = modifiedResponse.getBytes("UTF-8");
response.setContentLength(data.length);
response.getOutputStream().write(data); // this line causes error
}
} else {
data = responseWrapper.getCaptureAsBytes();
response.setContentLength(data.length);
response.getOutputStream().write(data);
}
}
This code works without any problem if status code equals 200 (else clause), but when it's not equal to 200 (I triggered 404 error), the following error occures:
com.ibm.ws.webcontainer.webapp.WebApp logServletError SRVE0293E: [Servlet Error]-[Faces Servlet]: java.lang.IllegalStateException: SRVE0209E: Writer already obtained
I don't really understand why does this error appear. The only difference between two cases is HTML content which is valid in both cases. Any help?
Using Websphere Application Server 8.5.5.18.
EDIT: I've tried to call reset() and then set headers and status code again, but that reset() call causes an IllegalStateException - as stated in javadoc, apparently response has already been committed. As far as I understand, flush() method of ServletOutputStream could cause response to be committed, but I'm not calling it anywhere. I've also added some log to see if response really is committed. In both cases (status 200 and 404) response.isCommitted() returns true. Does that mean that response is committed before doFilter is called?
Option 1 - downgrade JAX-RS to 1.1
Once JAX-RS version is changed back to 1.1 the errors in SystemOut.log will not be shown.
Do the following steps:
Change the JAX-RS version to 1.1 using WAS 9 Admin console. See the detailed instructions at
https://www.ibm.com/support/knowledgecenter/SSEQTP_9.0.0/com.ibm.websphere.base.doc/ae/twbs_jaxrs_coexist_adminconsole.html
Option 2 - move chain.doFilter to the end of your doFilter method
chain.doFilter(req, resp);
}
Option 3 - Remove other usages of PrintWriter or OuputStream
Review application to determine if both PrintWriter and OuputStream were obtained. Modify the failing servlet/JSP to only obtain one or the other.

GZip HttpServletResponse in Servlet Filter based on "Content-Length"

I have a specific requirement to Zip the HttpServletResponse based on the content length. The application I am working on is Spring based and exposes several services with JSON payload. However some of the payloads are quite huge in size ~2MB but are not consistently so i.e. are dependent on responses from external systems.
I have tried searching but could only succeed in Zipping based on URL mappings and not based on actual size of the payload. The server used is weblogic but does not support the GZIP configuration and hence looking to achieve this through Servlet Filter. Following is the code from my filter that I have so far but doesn't seem to be working. Any pointers in how to achieve dynamic GZipping based on the Content-Length and what would be the best way to do so?
public void doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse, FilterChain paramFilterChain) throws IOException, ServletException {
if ((paramServletRequest instanceof HttpServletRequest)) {
HttpServletRequest localHttpServletRequest = (HttpServletRequest) paramServletRequest;
HttpServletResponse localHttpServletResponse = (HttpServletResponse) paramServletResponse;
if (supportsGzip(localHttpServletRequest)) {
// This block is where response is being validated for
// eligibility of compression based on "Content-Length" header
paramFilterChain.doFilter(paramServletRequest, localHttpServletResponse);
String contentLengthString = localHttpServletResponse.getHeader("Content-Length");
int contentLength = contentLengthString != null && !contentLengthString.isEmpty() ? Integer.parseInt(contentLengthString) : 0;
//GZip only if the content length is greater than 750 KB
if (contentLength > (750*1024)) {
System.out.println("This should be zipped content");
localHttpServletResponse = new GZipResponseWrapper(localHttpServletResponse);
localHttpServletResponse.addHeader("Content-Encoding", "gzip");
((GZipResponseWrapper)localHttpServletResponse).finishResponse();
return;
}
}
paramFilterChain.doFilter(paramServletRequest, paramServletResponse);
}
}
Quite new to the forum and apologize in advance for any incorrect formats / structure.

try-with-resource vs java.lang.IllegalStateException: Cannot call sendError() after the response has been committed

My RSS servlet uses try-with-resource for the OutputStream out of the HttpServletResponse and the writer for it. In some cases SomeException is thrown whilst generating the RSS document, in which case I need to return an HTTP status 500 to the client:
try (ServletOutputStream out = response.getOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(out, "utf-8")) {
response.setContentType("text/xml");
// Generate RSS here
} catch (SomeException e) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
return;
}
However, by the time response.sendError() is called, the $out$ has already been closed and I get said IllegalStateException saying that the response has already been committed (closing the stream seems to commit the response automatically ).
If I move the initialization of out and writer outside of the try-block and close them in a finally-block (the pre-Java7 way), the error code gets sent correctly.
I was wondering whether there's a way to keep using try-with-resource and still be able to return error codes in case of an exception.
Thanks!
You don't need to close resources which you didn't create yourself. The container created the underlying OutputStream all by itself and is therefore also all by itself responsible for properly closing it. You should visualise it that the container has already put a try-with-resources around the servlet's doXxx() method. See also Should I close the servlet outputstream?
Differently put, the whole try-with-resources on OutputStream inside doXxx() is unnecessary.
Just do:
try {
// Generate RSS here
OutputStreamWriter writer = new OutputStreamWriter(response.getOutputStream(), "UTF-8")) {
response.setContentType("text/xml");
// Write RSS here.
} catch (SomeException e) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
}
Unrelated to the concrete problem, if you rethrow any checked exception as ServletException, then the container will also all by itself take care of the proper response code and message.
try {
// ...
} catch (SomeException e) {
throw new ServletException(e);
}

Cannot call sendRedirect() after downloading PDF

I saw many questions like the one I am asking, but they are not exactly about what I am looking for.
I am using Command pattern, and want to create PDF-file and download it. Creating is perfect, but when I want to download it, it's starts downloading and throws an exception.
java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed
org.apache.jasper.JasperException: java.lang.IllegalStateException: getOutputStream() has already been called for this response
java.lang.IllegalStateException: getOutputStream() has already been called for this response
Here is my code from Command Pattern
#Override
public String execute(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException, AppException {
String fontPath = request.getServletContext().getRealPath(AppConstants.FONT_PATH);
DBManager db = DBManager.getInstance();
String ticketCode = request.getParameter("ticketCode");
String place = request.getParameter("place");
int amountTickets = Integer.valueOf(place);
String flightName = Encoding.encoding(request.getParameter("flightName"));
User user = (User) request.getSession().getAttribute("client");
String locale = (String) request.getServletContext().getAttribute("currentLocale");
db.updateFlightTickets(flightName, --amountTickets);
///////create pdf document and represent it to the byte array
ByteArrayOutputStream baos =ReportCreator.createReport(locale, fontPath, ticketCode, place, user,
db.getFlightByName(flightName));
response.setContentType("application/pdf");
response.setContentLength(baos.size());
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-Disposition","attachment; filename=\"Ticket\"");
OutputStream os = response.getOutputStream();
baos.writeTo(os);
os.flush();
os.close();
return Path.SUCCESS;
}
Here is my "success page", sorry but can not add more, not enough reputation
<fmt:message key="success_jsp.label.success" />
And here is my servlet code
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
process(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
process(request, response);
}
private void process(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
String commandName = request.getParameter("command");
Command command = CommandContainer.get(commandName);
String forward = "";
try {
forward = command.execute(request, response);
} catch (AppException ex) {
request.setAttribute("errorMessage", ex.getMessage());
}
if (forward.equals(Path.SUCCESS)) {
response.sendRedirect(forward);
} else {
request.getRequestDispatcher(forward).forward(request, response);
}
}
Part of code in JSP, where click is calling the servlet
<td><button><fmt:message key="welcome_jsp.submit.buy_ticket" /></button></td>
How can i avoid it?
The exception says you are trying to working with the request/response once you redirect it or viceversa, and it's not valid.
Once you redirect a request, you cannot do anything else with the request/response, so getting the output stream and writing something to it is completely insane.
It's true about vice-versa situation, writing something and then redirect it will cause the browser will ignore the response data, or exception on server as I'm guessing you got.(but it depends on container)
So you either do not redirect the browser, or provide the pdf file with the target servlet/cgi where you are trying to redirect.
=================
And your current situation/problem:
Server sets the content-length, content-type,... and starts to write down some stream to the browser, since you haven't set any status, container will set default 200 OK which indicates there is some right response for the request.
Then browser will get some data(the pdf file) as 200 OK data(and consider it done), now how would you redirect the user once the response is almost done?!!?!!?!
I still do not understand why do you like to redirect a request when it's almost closed? you like to redirect the user after download complete? you cannot.

Servlet output stream not flushing with jrun

I'm attempting to use HTTP Streaming with a servlet. Locally I use orion as a servlet container and it works fine, but on the test server, which runs JRUN 4.0, the output is buffered even when I call flush() on the output stream. Any thoughts on why the output is being buffered and what I can do to stop it?
OutputStream os = servletResponse.getOutputStream();
while (true)
{
//attempt to write to output before doing anything else. If browser has disconnected, an IOException will be thrown so nothing else will be done
os.write(".".getBytes());
os.flush();
String response = getData();
os.write(response.getBytes());
os.flush();
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
}
}

Resources