I've been trying to figure out why the Servlet won't return the bytes to the client with this code (although the bytes is read based on the logs):
Redirector redirector = new SelfInjectingRedirector(getContext(), targetPattern,
Redirector.MODE_SERVER_OUTBOUND){
#Inject
FileStore fileStore;
String fileName = "something_for_sample";
boolean isBufferFirst = true;
#Override
public void handle(Request request, Response response) {
try {
HttpServletRequest servletRequest = ServletUtils.getRequest(request);
HttpServletResponse servletResponse = ServletUtils.getResponse(response);
//
// Either statement here wont return the bytes to the client
//
if(isBufferFirst) {
byte[] bytes = fileStore.get(fileName);
System.out.println("Bytes read: " + bytes.length); // Bytes read: 5731
servletResponse.getOutputStream().write(bytes, 0, bytes.length)
} else {
fileStore.get(fileName, servletResponse.getOutputStream());
}
} catch (Exception e) {
response.setStatus(Status.SERVER_ERROR_INTERNAL);
e.printStackTrace();
}
System.out.println("Handle Done");
}
};
The solution is to add the "Content-Length" header.
Related
I'm adding message level encryption (MLE) to an existing code base for outgoing requests. To do this, I simply wrote an interceptor that will catch outgoing requests, encrypt their bodies, and then send the request out. The response we get is also encrypted, and must be decrypted. This all is working fine for me. The only problem I'm having is that I must replace the ClientHttpResponse encrypted body with the now decrypted JSON. How can I do this? I don't see any methods that will let me alter the response body. Thanks in advance.
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
ClientHttpResponse response;
String bodyStr = new String(body);
// Encrypt the body and send
bodyStr = encrypt(bodyStr);
try {
response = execution.execute(request, bodyStr.getBytes());
} catch (Exception e) {
throw e;
}
// Decrypt the response body
String decryptedResponseBody = decrypt(response.getBody());
// Set the response body to the decrypted data (JSON)
// response.setBody(decryptedResponseBody)?????????
return response;
}
You will need to create an implementation of ClientHttpResponse which is not too hard since there are only a few methods to override, I added an example of how you would fix this. I hope this helps. I would suggest adding a named bean for this type of request, you don't want to have all your resttemplates being encrypted/decrypted.
restTemplate.getInterceptors().add( (ClientHttpRequestInterceptor)
(request, body, execution) -> {
ClientHttpResponse response;
String bodyStr = new String(body);
// Encrypt the body and send
bodyStr = encrypt(bodyStr);
try {
response = execution.execute(request, bodyStr.getBytes());
String text = IOUtils.toString(response.getBody(), StandardCharsets.UTF_8.name());
// Decrypt the response body
String decryptedResponseBody = decrypt(text);
} catch (Exception e) {
throw e;
}
InputStream inputStream = inputStream = new ByteArrayInputStream(decryptedResponseBody.getBytes());
return new ClientHttpResponse() {
#Override
public HttpHeaders getHeaders() {
return response.getHeaders();
}
#Override
public InputStream getBody() throws IOException {
return inputStream;
}
#Override
public HttpStatus getStatusCode() throws IOException {
return response.getStatusCode();
}
#Override
public int getRawStatusCode() throws IOException {
return response.getRawStatusCode();
}
#Override
public String getStatusText() throws IOException {
return response.getStatusText();
}
#Override
public void close() {
response.close();
}
};
}))
I have a rest method for downloading files which works. But, it seems that the download doesn't start on the web client until the file is completely copied to the output stream, which can take a while for large files.
#GetMapping(value = "download-single-report")
public void downloadSingleReport(HttpServletResponse response) {
File dlFile = new File("some_path");
try {
response.setContentType("application/pdf");
response.setHeader("Content-disposition", "attachment; filename="+ dlFile.getName());
InputStream inputStream = new FileInputStream(dlFile);
IOUtils.copy(inputStream, response.getOutputStream());
response.flushBuffer();
} catch (FileNotFoundException e) {
// error
} catch (IOException e) {
// error
}
}
Is there a way to "stream" the file such that the download starts as soon as I begin writing to the output stream?
I also have a similar method that takes multiple files and puts them in a zip, adding each zip entry to the zip stream, and the download also only begins after the zip has been created:
ZipEntry zipEntry = new ZipEntry(entryName);
zipOutStream.putNextEntry(zipEntry);
IOUtils.copy(fileStream, zipOutStream);
You can use InputStreamResource to return stream result. I tested and it is started copying to output immediately.
#GetMapping(value = "download-single-report")
public ResponseEntity<Resource> downloadSingleReport() {
File dlFile = new File("some_path");
if (!dlFile.exists()) {
return ResponseEntity.notFound().build();
}
try {
try (InputStream stream = new FileInputStream(dlFile)) {
InputStreamResource streamResource = new InputStreamResource(stream);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + dlFile.getName() + "\"")
.body(streamResource);
}
/*
// FileSystemResource alternative
FileSystemResource fileSystemResource = new FileSystemResource(dlFile);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + dlFile.getName() + "\"")
.body(fileSystemResource);
*/
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
The second alternative is a partial download method.
#GetMapping(value = "download-single-report-partial")
public void downloadSingleReportPartial(HttpServletRequest request, HttpServletResponse response) {
File dlFile = new File("some_path");
if (!dlFile.exists()) {
response.setStatus(HttpStatus.NOT_FOUND.value());
return;
}
try {
writeRangeResource(request, response, dlFile);
} catch (Exception ex) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
}
}
public static void writeRangeResource(HttpServletRequest request, HttpServletResponse response, File file) throws IOException {
String range = request.getHeader("Range");
if (StringUtils.hasLength(range)) {
//http
ResourceRegion region = getResourceRegion(file, range);
long start = region.getPosition();
long end = start + region.getCount() - 1;
long resourceLength = region.getResource().contentLength();
end = Math.min(end, resourceLength - 1);
long rangeLength = end - start + 1;
response.setStatus(206);
response.addHeader("Accept-Ranges", "bytes");
response.addHeader("Content-Range", String.format("bytes %s-%s/%s", start, end, resourceLength));
response.setContentLengthLong(rangeLength);
try (OutputStream outputStream = response.getOutputStream()) {
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
StreamUtils.copyRange(inputStream, outputStream, start, end);
}
}
} else {
response.setStatus(200);
response.addHeader("Accept-Ranges", "bytes");
response.setContentLengthLong(file.length());
try (OutputStream outputStream = response.getOutputStream()) {
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
StreamUtils.copy(inputStream, outputStream);
}
}
}
}
private static ResourceRegion getResourceRegion(File file, String range) {
List<HttpRange> httpRanges = HttpRange.parseRanges(range);
if (httpRanges.isEmpty()) {
return new ResourceRegion(new FileSystemResource(file), 0, file.length());
}
return httpRanges.get(0).toResourceRegion(new FileSystemResource(file));
}
Spring Framework Resource Response Process
Resource response managed by ResourceHttpMessageConverter class. In writeContent method, StreamUtils.copy is called.
package org.springframework.http.converter;
public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<Resource> {
..
protected void writeContent(Resource resource, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
try {
InputStream in = resource.getInputStream();
try {
StreamUtils.copy(in, outputMessage.getBody());
}
catch (NullPointerException ex) {
// ignore, see SPR-13620
}
finally {
try {
in.close();
}
catch (Throwable ex) {
// ignore, see SPR-12999
}
}
}
catch (FileNotFoundException ex) {
// ignore, see SPR-12999
}
}
}
out.write(buffer, 0, bytesRead); sends data immediately to output (I have tested on my local machine). When whole data is transferred, out.flush(); is called.
package org.springframework.util;
public abstract class StreamUtils {
..
public static int copy(InputStream in, OutputStream out) throws IOException {
Assert.notNull(in, "No InputStream specified");
Assert.notNull(out, "No OutputStream specified");
int byteCount = 0;
int bytesRead;
for(byte[] buffer = new byte[4096]; (bytesRead = in.read(buffer)) != -1; byteCount += bytesRead) {
out.write(buffer, 0, bytesRead);
}
out.flush();
return byteCount;
}
}
Use
IOUtils.copyLarge(InputStream input, OutputStream output)
Copy bytes from a large (over 2GB) InputStream to an OutputStream.
This method buffers the input internally, so there is no need to use a BufferedInputStream.
The buffer size is given by DEFAULT_BUFFER_SIZE.
or
IOUtils.copyLarge(InputStream input, OutputStream output, byte[] buffer)
Copy bytes from a large (over 2GB) InputStream to an OutputStream.
This method uses the provided buffer, so there is no need to use a BufferedInputStream.
http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html
You can use "StreamingResponseBody" File download would start immediately while the chunks are written to the output stream. Below is the code snippet
#GetMapping (value = "/download-single-report")
public ResponseEntity<StreamingResponseBody> downloadSingleReport(final HttpServletResponse response) {
final File dlFile = new File("Sample.pdf");
response.setContentType("application/pdf");
response.setHeader(
"Content-Disposition",
"attachment;filename="+ dlFile.getName());
StreamingResponseBody stream = out -> FileCopyUtils.copy(new FileInputStream(dlFile), out);
return new ResponseEntity(stream, HttpStatus.OK);
}
I know a similar problem has been posted several times on StackOverflow, still all the answers did not help in my case.
Target: I need to log the SOAP-Request XML into a database before processing it further.
For this discussion, I'm happy if I can get it as a String and log it to console.
Problem: Request XML is always empty.
Environment: IBM WebSphere Application Server v8.5, EJB 3.1 web service (session bean)
I actually produced a working solution using javax.xml.soap.SOAPBody and SOAPMessage, but on production there seems to be another component which causes the following JAX-WS conflict:
ERROR org.apache.axis2.engine.AxisEngine receive - com.sun.xml.messaging.saaj.soap.ver1_1.Body1_1Impl incompatible with com.ibm.ws.webservices.engine.xmlsoap.SOAPBody
Yes, there are multiple workarounds on StackOverflow and IBM about changing the ClassLoader policy, like "local class loader first (parent last)", but we currently can't go this way.
So, my current approach (which was working on a different servlet) is to get the HTTPServletRequest, get it's InputStream and convert it to a String using IOUtils.toString().
I am aware that a request can only be consumed once and I found several approaches to avoid it (like using a HTTPServletRequestWrapper) but even using these workarounds the request XML is always empty.
Eventually I want to do the logging in an adapter, but for testing reasons I also put my attempts into the service itself (it did not have any effect).
The strange thing is: I can read all Headers and Attributes from the request (using request.getHeader() and request.getAttribute()! Only the body itself is blank!
I'm testing the application using SoapUI.
Request-XML:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ping="http://services.axonactive.com/wsdl/cet/pingservice-v1.0">
<soapenv:Header/>
<soapenv:Body>
<ping:PingPingIn/>
</soapenv:Body>
</soapenv:Envelope>
Response is actually irrelevant, but correctly working.
Console output:
[ch.zek.ecls.EclsPingServiceImpl]: Initialization successful.
EclsPingServiceImpl ping - start ping()...
EclsPingServiceImpl ping - Body:
EclsPingServiceImpl ping - body2:
EclsUtil printRequestData - [H] Accept-Encoding: gzip,deflate
EclsUtil printRequestData - [H] Content-Type: text/xml;charset=UTF-8
EclsUtil printRequestData - [H] SOAPAction: ""
EclsUtil printRequestData - [H] Content-Length: 254
EclsUtil printRequestData - [H] Host: localhost:9443
EclsUtil printRequestData - [H] Connection: Keep-Alive
EclsUtil printRequestData - [H] User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
EclsUtil printRequestData - [A] javax.servlet.request.key_size: 128
EclsUtil printRequestData - [A] javax.servlet.request.cipher_suite: SSL_RSA_WITH_AES_128_CBC_SHA
EclsUtil printRequestData - [A] com.ibm.websphere.servlet.uri_non_decoded: /NewZekEclsHTTPRouter/PingService
EclsPingServiceImpl ping - end ping()
Edit: Note the headers [H] for Content-Type and Content-Length:
They "know" the content - if I put some more characters into the request XML then Content-Length is updated accordingly.
So I conclude that the content is not lost, but somehow not accessible... .
Webservice:
public class EclsPingServiceImpl{
#javax.annotation.Resource
WebServiceContext wsContext;
#javax.annotation.Resource
SessionContext sessionContext;
private Log log = LogFactory.getLog(EclsPingServiceImpl.class);
public PingOut ping(PingIn parameters) throws PingEntityNotFoundException, PingPermissionException, PingSystemException {
MessageContext messageContext = wsContext.getMessageContext();
HttpServletRequest request = (HttpServletRequest) messageContext.get(MessageContext.SERVLET_REQUEST);
// start Try 1
MultiReadHttpServletRequest multiReadHttpServletRequest = new MultiReadHttpServletRequest(request);
try {
InputStream bodyInputStream = multiReadHttpServletRequest.getInputStream();
String body = IOUtils.toString(bodyInputStream);
if (log.isDebugEnabled()) {
log.debug("Body: " + body);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// end Try 1
// start Try 2
try {
InputStream body2 = request.getInputStream();
String xml = IOUtils.toString(body2, "UTF-8");
if (log.isDebugEnabled()) {
log.debug("body2: " + xml);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// end Try 2
// Get Header data:
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
if (log.isDebugEnabled()) {
log.debug("[H] " + headerName + ": " + request.getHeader(headerName));
}
}
// Get Attribute data:
Enumeration<String> attributeNames = request.getAttributeNames();
while(attributeNames.hasMoreElements()) {
String attributeName = attributeNames.nextElement();
if (log.isDebugEnabled()) {
log.debug("[A] " + attributeName + ": " + request.getAttribute(attributeName));
}
}
PingOut pingOut = new PingOut();
// some irrelevant stuff...
return pingOut;
}
}
MultiReadHttpServletRequestWrapper:
Copied from StackOverflow: Http Servlet request lose params from POST body after read it once
public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
private ByteArrayOutputStream cachedBytes;
public MultiReadHttpServletRequest(HttpServletRequest request) {
super(request);
}
#Override
public ServletInputStream getInputStream() throws IOException {
if (cachedBytes == null)
cacheInputStream();
return new CachedServletInputStream();
}
#Override
public BufferedReader getReader() throws IOException{
return new BufferedReader(new InputStreamReader(getInputStream()));
}
private void cacheInputStream() throws IOException {
/* Cache the inputstream in order to read it multiple times. For
* convenience, I use apache.commons IOUtils
*/
cachedBytes = new ByteArrayOutputStream();
IOUtils.copy(super.getInputStream(), cachedBytes);
}
/* An inputstream which reads the cached request body */
public class CachedServletInputStream extends ServletInputStream {
private ByteArrayInputStream input;
public CachedServletInputStream() {
/* create a new input stream from the cached request body */
input = new ByteArrayInputStream(cachedBytes.toByteArray());
}
#Override
public int read() throws IOException {
return input.read();
}
}
}
LogHandler - just to show what I tried, too...:
public class EclsSimpleLogHandler implements javax.xml.ws.handler.soap.SOAPHandler
{
private Log log = LogFactory.getLog(EclsSimpleLogHandler.class);
#Override
public boolean handleMessage(MessageContext context) {
boolean success = false;
if (log.isDebugEnabled()) {
log.debug("handleMessage()...");
}
Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
// check if handler is called for inbound (request) or outbound (response) message
if (outboundProperty.booleanValue()) {
success = handleResponse(context);
} else {
//success = handleRequest(context);
success = true;
}
return success;
}
private boolean handleRequest(MessageContext messageContext) {
if(log.isDebugEnabled()) {
log.debug("handling request (inbound)");
}
// Initially logging planned here, moved out for testing reasons
boolean success = false;
return success;
}
private boolean handleResponse(MessageContext messageContext) {
if(log.isDebugEnabled()) {
log.debug("handling response (outbound)");
}
boolean success = false;
ByteArrayOutputStream outputStream = null;
SOAPMessageContext context = (SOAPMessageContext) messageContext;
SOAPMessage soapMessage = (SOAPMessage) context.getMessage();
try {
/*
Initial solution, but sometimes causing:
ERROR org.apache.axis2.engine.AxisEngine receive - com.sun.xml.messaging.saaj.soap.ver1_1.Body1_1Impl
incompatible with com.ibm.ws.webservices.engine.xmlsoap.SOAPBody
// SOAPBody soapBody = soapMessage.getSOAPBody();
// String soapBodyXml = soapBody.toString();
*/
// This is working - but I want to avoid using SOAPMessage:
outputStream = new ByteArrayOutputStream();
soapMessage.writeTo(outputStream);
String soapBodyXml = new String(outputStream.toByteArray(), "UTF-8");
if (log.isDebugEnabled()) {
log.debug("responseXml:\n" + soapBodyXml);
}
success = true;
} catch (SOAPException e) {
if (log.isErrorEnabled()) {
log.error("Error while accessing SOAPMessage: " + e.getMessage());
}
} catch (IOException e) {
if (log.isErrorEnabled()) {
log.error("IOException for soapMessage.writeTo(): " + e.getMessage());
}
e.printStackTrace();
}
return success;
}
// Another method, but also resulting in an empty body:
static String extractPostRequestBody(HttpServletRequest request) {
if ("POST".equalsIgnoreCase(request.getMethod())) {
Scanner s = null;
try {
s = new Scanner(request.getInputStream(), "UTF-8").useDelimiter("\\A");
} catch (IOException e) {
e.printStackTrace();
}
return s.hasNext() ? s.next() : "";
}
return "";
}
// Another method, but also resulting in an empty body:
private String getRequestBody(HttpServletRequest request) {
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = requestWrapper.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) != -1) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
}
} catch (IOException ex) {
log.error("Error reading the request payload", ex);
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException iox) {
// ignore
}
}
}
return stringBuilder.toString();
}
}
I am trying to log all the outgoing Http requests in my spring based web application. Is there is interceptor for this purpose? I want to log all outgoing the contents and headers before it leaves the application. I am using spring-ws to send SOAP requests. So basically, I want to log not only the SOAP request xml (as mentioned here How can I make Spring WebServices log all SOAP requests?) but the http request as a whole.
Intercept the request/response using a ClientInterceptor on the WebServiceGatewaySupport:
// soapClient extends WebServiceGatewaySupport
soapClient.setInterceptors(new ClientInterceptor[]{new ClientInterceptor() {
#Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
messageContext.getRequest().writeTo(os);
} catch (IOException e) {
throw new WebServiceIOException(e.getMessage(), e);
}
String request = new String(os.toByteArray());
logger.trace("Request Envelope: " + request);
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
messageContext.getResponse().writeTo(os);
} catch (IOException e) {
throw new WebServiceIOException(e.getMessage(), e);
}
String response = new String(os.toByteArray());
logger.trace("Response Envelope: " + response);
return true;
}
...
To get the headers as well you need an instance of TransportOutputStream.
Unfortunately the class is abstract, so you need to subclass is. Here's how it might look:
class ByteArrayTransportOutputStream extends TransportOutputStream {
private ByteArrayOutputStream outputStream;
#Override
public void addHeader(String name, String value) throws IOException {
createOutputStream();
String header = name + ": " + value + "\n";
outputStream.write(header.getBytes());
}
public byte[] toByteArray() {
return outputStream.toByteArray();
}
#Override
protected OutputStream createOutputStream() throws IOException {
if (outputStream == null) {
outputStream = new ByteArrayOutputStream();
}
return outputStream;
}
}
I'm curious if there is an annotation/filter/interceptor capability in JAX-RS to detect if PUT or POST method contains an empty body.
Currently I have method that, if request has empty body, possibly throws NPE.
#PUT
#Produces("application/json")
#Consumes("application/json")
#Path("/update/{id}")
public Response updateCustomer(#PathParam("id") final String customerIdStr, final CustomerJson customer) {
// if request body is empty -> customer == null
return Response.ok().build();
}
I can check customer for null . But since I have plenty of such methods, it's better to have filter to do such validation.
Please!
Did you try to use Bean Validation, using an #NotNull annotation on your CustomerJson method parameter ?
Interceptors read the HTTP body and I dont find a way to send the body for further processing. But you can do this by Servlet Filter and HTTP servlet request wrapper,
public class EmptyCheckFilter implements javax.servlet.Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if (req.getMethod().equals("POST") || req.getMethod().equals("PUT")) {
boolean dirty = false;
HttpRequestWrapper wrapper = new MyHTTPRequestWrapper(req);
try {
// check body is empty by wrapper.getBody() and set dirty = true;
} catch (Exception e) {
}
if (dirty) {
res.sendError(400, "Invalid input");
} else
chain.doFilter(wrapper, response);
} else
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
public class MyHTTPRequestWrapper extends HttpServletRequestWrapper {
private final String body;
public MyHTTPRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
if (request.getCharacterEncoding() != null)
bufferedReader = new BufferedReader(new InputStreamReader(inputStream, request.getCharacterEncoding()));
else
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() {
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;
}
}