HttpServletRequestWrapper not working as expected - java

I need to log request body content. So I used filter and HttpServletRequestWrapper as below for this purpose. But when I invoke request.getParameter from my servlet I'm not getting anything. Appreciate any help
RequestWrapper Code
public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
private static final Log log = LogFactory.getLog(MultiReadHttpServletRequest.class);
private ByteArrayOutputStream cachedBytes;
public MultiReadHttpServletRequest(HttpServletRequest request) throws IOException {
super(request);
cachedBytes = new ByteArrayOutputStream();
byte[] buffer = new byte[1024 * 4];
int n;
while (-1 != (n = request.getInputStream().read(buffer))) {
cachedBytes.write(buffer, 0, n);
}
}
#Override
public ServletInputStream getInputStream() throws IOException {
return new CachedServletInputStream();
}
private class CachedServletInputStream extends ServletInputStream {
private ByteArrayInputStream input;
public CachedServletInputStream() {
input = new ByteArrayInputStream(cachedBytes.toByteArray());
}
#Override
public int read() throws IOException {
return input.read();
}
}
String getRequestBody() throws IOException {
StringBuilder inputBuffer = new StringBuilder();
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(getInputStream()));
try {
do {
line = reader.readLine();
if (null != line) {
inputBuffer.append(line.trim());
}
} while (line != null);
} catch (IOException ex) {
log.error("Unable to get request body from request: " + ex.getMessage(), ex);
} finally {
try {
reader.close();
} catch (IOException e) {
// Just log error
log.warn("Unable to close BufferReader: " + e.getMessage(), e);
}
}
return inputBuffer.toString().trim();
}
My filter code
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
if ((servletRequest instanceof HttpServletRequest) && (messageTracerApiClient != null)) {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
MultiReadHttpServletRequest bufferedRequest = new MultiReadHttpServletRequest(httpServletRequest);
Message message = new Message();
message.setHost(bufferedRequest.getLocalAddr());
message.setPayload(bufferedRequest.getRequestBody());
messageTracerApiClient.publishMessage(message);
System.out.println("bufferedRequest param= " + bufferedRequest.getParameterMap().size());
filterChain.doFilter(bufferedRequest, servletResponse);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
Please note bufferedRequest.getParameterMap().size() also print 0 even there are parameter.

Related

BufferedReader for Pdf File in Java Servlet

My BufferedReader corrupts my Pdf file and writes everything in the first line.
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
createPdf();
response.setHeader("Content-disposition","attachment; filename=\""+"myPdf.pdf"+"\"");
BufferedReader reader = null;
try {
File file = new File("myPath\\myNewPdf.pdf");
reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
response.getWriter().append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
I just want to read the Pdf and write it in a new one to make it a download.
this did the trick thx #user207421
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
createPdf();
response.setHeader("Content-disposition","attachment; filename=\""+"myNewPdf.pdf"+"\"");
InputStream inputStream = new FileInputStream("myPath\\myPdf.pdf");
int data;
while( (data = inputStream.read()) >= 0 ) {
response.getWriter().write(data);
}
inputStream.close();
}

How to modify the request body in HttpServletRequest method using HandlerInterceptorAdapter

I tried using HttpRequestWrapper but it keeps giving me stream closed exception. Below is my HttpRequestWrapper code. I was trying to modify the request body in preHandle method. after modifying the request body I want to send it to the controller. It seems like HandlerInterceptorAdapter been called twice. In the second time it complains that the stream is closed. I've seen post related to this issue but I could not find a solution.
public class RequestWrapper extends HttpServletRequestWrapper {
private final String body;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
stringBuilder.append("");
}
} catch (IOException ex) {
throw ex;
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
throw ex;
}
}
}
body = stringBuilder.toString();
}
#Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
#Override public boolean isFinished() {
return false;
}
#Override public boolean isReady() {
return false;
}
#Override public void setReadListener(ReadListener readListener) {
}
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
#Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody() {
return this.body;
}
}

How to rewrite POST request body on HttpServletRequest

I'm working on a Filter in which I have to get the request payload, decrypt it, check if it's a valid JSON and if it is go on with the chain and go to my service. The thing is that, so far I haven't been able to find a way to rewrite the body. Why I want to rewrite it? As the service expects a JSON and the request has an encrypted text in the body, once I decrypt it I want the body to be the decrypted JSON. Also, once I return from the service, I should rewrite the response to have the json encrypted. I've read a lot of forums and questions but couldn't get to a working solution.
Here's my code:
RequestLoginFilter.java
#WebFilter("/RequestLoginFilter")
public class RequestLoginFilter implements Filter{
protected final static Log logger = LogFactory.getLog(RequestLoginFilter.class);
private ServletContext context;
private CryptoUtil crypto;
public void init(FilterConfig fConfig) throws ServletException {
this.context = fConfig.getServletContext();
this.context.log("RequestLoggingFilter initialized");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// use wrapper to read multiple times the content
AuthenticationRequestWrapper req = new AuthenticationRequestWrapper((HttpServletRequest) request);
HttpServletResponse resp = (HttpServletResponse) response;
String payload = req.getPayload();
try {
String decryptedPayload = crypto.decrypt(payload);
JSONUtils.convertJSONStringToObject(decryptedPayload, LoginTokenTO.class);
} catch (GeneralSecurityException e) {
logger.error("Error when trying to decrypt payload '"+payload+"'");
throw new ServletException("Error when trying to decrypt payload '"+payload+"'", e);
}
chain.doFilter(req, resp);
System.out.println("a ver");
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
}
And also the wrapper, just in case:
AuthenticationRequestWrapper.java
public class AuthenticationRequestWrapper extends HttpServletRequestWrapper {
protected final static Log logger = LogFactory.getLog(AuthenticationRequestWrapper.class);
private final String payload;
public AuthenticationRequestWrapper (HttpServletRequest request) throws AuthenticationException {
super(request);
// read the original payload into the payload variable
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
// read the payload into the StringBuilder
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
} else {
// make an empty string since there is no payload
stringBuilder.append("");
}
} catch (IOException ex) {
logger.error("Error reading the request payload", ex);
throw new AuthenticationException("Error reading the request payload", ex);
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException iox) {
// ignore
}
}
}
payload = stringBuilder.toString();
}
#Override
public ServletInputStream getInputStream () throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(payload.getBytes());
ServletInputStream inputStream = new ServletInputStream() {
public int read ()
throws IOException {
return byteArrayInputStream.read();
}
};
return inputStream;
}
public String getPayload() {
return payload;
}
}
Hopefully somebody here knows how I can get to get this working.
Thanks in advance guys.
Whilst what you are asking is probably technically possible, it doesn't sound like the right approach to me.
What you need is a security layer that sits between the incoming request (endpoint) and your service. Re-writing the body of the request is a strange thing to be doing (which probably explains why you're having issues). Is there a reason you want this to be done in a Filter? After all, filters are designed to filter requests, not rewrite them ;)
A more logical/transparent solution would be to have your endpoint accept all incoming requests, decrypt and validate them before passing the request onto your service tier. Something like this:
public void handleRequest(Request request) {
try {
IncomingRequest x = securityManager.decrypt(request);
Response r = myService.handleRequest(x);
handleResponse(securityManager.encrypt(r));
}catch(InvlidateMessage x) {
handleInvalidMessage...
}catch(BusinessException x) {
handleBusinessException...
}
}

Java http request on binary file - can't read correct content-length

I'm sending a http request to get binary files (here i'm trying an image)
public class Main {
public static void main(String[] args) throws UnknownHostException,
IOException {
new Main(args);
}
public Main(String[] args) throws UnknownHostException, IOException {
lance(args);
}
private void lance(String[] args) throws UnknownHostException, IOException {
Lanceur lan = new Lanceur("www.cril.univ-artois.fr", "/IMG/arton451.jpg");
lan.requete();
}
}
public class Lanceur {
Socket s;
InputStream readStream;
OutputStream writeStream;
String host;
String ressource;
public Lanceur(String host, String ressource) throws UnknownHostException,
IOException {
s = new Socket(InetAddress.getByName(host), 80);
readStream = s.getInputStream();
writeStream = s.getOutputStream();
this.host = host;
this.ressource = ressource;
}
public void requete() throws IOException {
// String[] length = null;
writeStream.write(new String("GET " + ressource + " HTTP/1.1\r\n"
+ "Host: www.google.com\r\n" + "\r\n").getBytes());
writeStream.flush();
AnswerReader as = new AnswerReader(readStream);
as.read();
as.writeFile(this.ressource);
s.close();
}
}
public class AnswerReader {
BufferedReader br;
DataInputStream dis;
String status;
Map<String, String> attrs;
byte[] content;
public AnswerReader(InputStream is) {
br = new BufferedReader(new InputStreamReader(is));
dis = new DataInputStream(new BufferedInputStream(is));
}
public void read() throws NumberFormatException {
readStatus();
try {
readAttrs();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String contentL = attrs.get("Content-Length");
readContent(Integer.valueOf(contentL));
}
public void readStatus() {
try {
status = br.readLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void readAttrs() throws IOException {
attrs = new HashMap<String, String>();
String line;
for (line = br.readLine(); line.length() > 0; line = br.readLine()) {
int index = line.indexOf(':');
attrs.put(line.substring(0, index), line.substring(index + 2));
}
}
private void readContent(int size) {
this.content = new byte[size];
byte[] buff = new byte[1024];
int copied = 0;
int read = 0;
while (copied < size) {
try {
read = dis.read(buff);
if (read == -1)
break;
} catch (IOException e) {
e.printStackTrace();
}
// byte[] byteArray = new String(buff).getBytes();
System.arraycopy(buff, 0, content, copied, read);
copied += read;
}
System.out.println(copied + "///" + size);
}
public void writeFile(String name) throws IOException {
String tab[] = name.split("/");
String filename = tab[tab.length - 1];
FileOutputStream fos = new FileOutputStream("./" + filename);
fos.write(content);
fos.flush();
fos.close();
}
}
The problem comes from readContent(). The content-length is fine, but it doesn't read all the data. From this example it will reads that :
22325///38125
The BufferedReader is buffering some of the binary data. You'll have to find another way to read the header lines using the unbuffered input stream instead, e.g. DataInputStream.readLine(), deprecation or no.
But you should use HttpURLConnection. It's easier.

how to encrypt response data in filter

i am trying encrypt response data before it written into the HttpServletResponse, so i have implemented custom response wrapper and output streram and a filter classes,
Problem is i need to encrypt whole response data once, but there is no write(String content) method, but there are three methods available inside ServletOutputStream class which are write(int b), write(byte[] b) and write(byte[] b, int off, int len) when i run the application only one method is called write(int b).
So is there any workaround to get whole response data as string, where i can call encrypt(responseData) ?
my classes looks like:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
BufferedRequestWrapper bufferedReqest = new BufferedRequestWrapper(httpServletRequest);
BufferedServletResponseWrapper bufferedResponse = new BufferedServletResponseWrapper(httpServletResponse);
// pass the wrappers on to the next entry
chain.doFilter(bufferedReqest, bufferedResponse);
}
and
public class BufferedServletResponseWrapper extends HttpServletResponseWrapper {
private final Logger LOG = LoggerFactory.getLogger(getClass());
private ServletOutputStream outputStream;
private PrintWriter writer;
private MyServletOutputStream copier;
public BufferedServletResponseWrapper(HttpServletResponse response) throws IOException {
super(response);
}
#Override
public ServletOutputStream getOutputStream() throws IOException {
LOG.info("getOutputStream");
if (writer != null) {
throw new IllegalStateException("getWriter() has already been called on this response.");
}
if (outputStream == null) {
outputStream = getResponse().getOutputStream();
copier = new MyServletOutputStream(outputStream);
}
return copier;
}
#Override
public PrintWriter getWriter() throws IOException {
LOG.info("getWriter");
if (outputStream != null) {
throw new IllegalStateException("getOutputStream() has already been called on this response.");
}
if (writer == null) {
copier = new MyServletOutputStream(getResponse().getOutputStream());
writer = new PrintWriter(new OutputStreamWriter(copier, getResponse().getCharacterEncoding()), true);
}
return writer;
}
#Override
public void flushBuffer() throws IOException {
if (writer != null) {
writer.flush();
} else if (outputStream != null) {
copier.flush();
}
}
public byte[] getCopy() {
if (copier != null) {
return copier.getCopy();
} else {
return new byte[0];
}
}
}
and my custom output stream class looks like:
public class MyServletOutputStream extends ServletOutputStream{
private final Logger LOG = LoggerFactory.getLogger(getClass());
private OutputStream outputStream;
private ByteArrayOutputStream copy;
public MyServletOutputStream(OutputStream outputStream) {
this.outputStream = outputStream;
this.copy = new ByteArrayOutputStream(1024);
}
#Override
public void write(int b) throws IOException {
LOG.info("write int");
outputStream.write(b);
copy.write(b);
}
#Override
public void write(byte[] b) throws IOException {
LOG.info("write byte[]");
outputStream.write(b);
copy.write(b);
}
public byte[] getCopy() {
return copy.toByteArray();
}
}
String str= new String( bufferedResponse .getCopy());
This will give you the string of the data in outputStream. I can see in your code you are writing bytes to out as well as copier. This copier can also be modified later.
String encStr=Encrypt(str); // your encryption logic.
ServletOutputStream out= response.getOutputStream();
out.write(encStr.getByte()); //adding your encrypted data to response
out.flush();
out.close();
I have solved the problem by this way:
created a custom ByteArrayOutputStream and PrintWriter instance in response wrapper class.
modified getOutputStream() and getWriter() method to use custom instance created in step 1.
finally, in filter after doFilter() statement, retrieved response data from custom writer and output stream. and written encrypted data to original response.
here is how response wrapper looks like:
public class BufferedServletResponseWrapper extends HttpServletResponseWrapper {
private final Logger LOG = LoggerFactory.getLogger(getClass());
private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
private PrintWriter writer = new PrintWriter(outputStream);
public BufferedServletResponseWrapper(HttpServletResponse response)
throws IOException {
super(response);
}
#Override
public ServletOutputStream getOutputStream() throws IOException {
LOG.info("getOutputStream");
return new ServletOutputStream() {
#Override
public void write(int b) throws IOException {
LOG.info("write int");
outputStream.write(b);
}
#Override
public void write(byte[] b) throws IOException {
LOG.info("write byte[]");
outputStream.write(b);
}
};
}
#Override
public PrintWriter getWriter() throws IOException {
LOG.info("getWriter");
return writer;
}
#Override
public void flushBuffer() throws IOException {
if (writer != null) {
writer.flush();
} else if (outputStream != null) {
outputStream.flush();
}
}
public String getResponseData() {
return outputStream.toString();
}
}
and doFilter() looks like:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
BufferedServletResponseWrapper bufferedResponse = new BufferedServletResponseWrapper(
httpServletResponse);
// pass the wrappers on to the next entry
chain.doFilter(httpServletRequest, bufferedResponse);
String responseData = bufferedResponse.getResponseData();
String encryptedResponseData = encrypt(responseData);
OutputStream outputStream = httpServletResponse.getOutputStream();
outputStream.write(encryptedResponseData.getBytes());
outputStream.flush();
outputStream.close();
}

Categories

Resources