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;
}
}
Related
I have this code:
private static void flow(InputStream is, OutputStream os, byte[] buf)
throws IOException {
int numRead;
while ((numRead = is.read(buf)) >= 0) {
os.write(buf, 0, numRead);
}
}
Which basically streams from is to the OutputStream provided.
My goal is to cache the is when the flow has completed.
As such I have:
cacheService.cache(key, bytes);
The solution to this is to implement a Caching output stream:
public class CachingOutputStream extends OutputStream {
private final OutputStream os;
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
public CachingOutputStream(OutputStream os) {
this.os = os;
}
public void write(int b) throws IOException {
try {
os.write(b);
baos.write(b);
} catch (Exception e) {
if(e instanceof IOException) {
throw e;
} else {
e.printStackTrace();
}
}
}
public byte[] getCache() {
return baos.toByteArray();
}
public void close() throws IOException {
os.close();
}
public void flush() throws IOException {
os.flush();
}
}
And do this:
final CachingOutputStream cachingOutputStream = new CachingOutputStream(outputStream);
flow(inputStream, cachingOutputStream, buff);
cached = cachingOutputStream.getCache();
if(cached != null) {
cacheService.put(cacheKey, cached);
}
Using org.apache.poi.util.IOUtils,
IOUtils.toByteArray(inputStream);
I have a problem with Interceptor in SpringBoot I am trying to read the body in a request at preHandle() method.
public class LogInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
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) {
System.out.println("Error reading the request body...");
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
System.out.println("Error closing bufferedReader...");
}
}
}
String body = stringBuilder.toString();
System.out.println("--Body--"+body);
}
}
This code print body correctly but when I try to made a POST petition with Postman I receive the following error.
I/O error while reading input message; nested exception is java.io.IOException: Stream closed
If I do the same petition witouth this code the petition works correctly.
Could anyone help to me ? Or said a better solution to intercept body ?
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.
I am creating some unit testing and trying to Mock out some calls. Here is what I have in my working code:
String soapRequest = (SimUtil.readInputStream(request.getInputStream())).toString();
if (soapRequest.equals("My String")) { ... }
and SimUtil.readInputSteam looks like this:
StringBuffer sb = new StringBuffer();
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(inputStream));
final int buffSize = 1024;
char[] buf = new char[buffSize];
int numRead = 0;
while ((numRead = reader.read(buf)) != -1) {
String readData = String.valueOf(buf, 0, numRead);
sb.append(readData);
buf = new char[buffSize];
}
} catch (IOException e) {
LOG.error(e.getMessage(), e);
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
}
What I am trying to do is the request.getInputStream(), the stream returns certain String.
HttpServletRequest request = mock(HttpServletRequest.class);
ServletInputStream inputStream = mock(ServletInputStream.class);
when(request.getInputStream()).thenReturn(inputStream);
So This is the code I want to condition
when(inputStream.read()).thenReturn("My String".toInt());
Any Help would be greatly appreciated.
Don't mock the InputStream. Instead, transform the String to an array of bytes using the
getBytes() method. Then create a ByteArrayInputStream with the array as input, so that it returns the String when consumed, each byte at a time. Next, create a ServletInputStream that wraps a regular InputStream like the one from Spring:
public class DelegatingServletInputStream extends ServletInputStream {
private final InputStream sourceStream;
/**
* Create a DelegatingServletInputStream for the given source stream.
* #param sourceStream the source stream (never <code>null</code>)
*/
public DelegatingServletInputStream(InputStream sourceStream) {
Assert.notNull(sourceStream, "Source InputStream must not be null");
this.sourceStream = sourceStream;
}
/**
* Return the underlying source stream (never <code>null</code>).
*/
public final InputStream getSourceStream() {
return this.sourceStream;
}
public int read() throws IOException {
return this.sourceStream.read();
}
public void close() throws IOException {
super.close();
this.sourceStream.close();
}
}
and finally, the HttpServletRequest mock would return this DelegatingServletInputStream object.
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.