Re-use get, post, put etc. in overloaded methods

This commit is contained in:
Dave Syer
2017-03-31 08:58:27 +01:00
committed by markfisher
parent 2b88eaeb08
commit 4346a7adc8

View File

@@ -77,8 +77,8 @@ import org.springframework.web.util.AbstractUriTemplateHandler;
* <pre> * <pre>
* &#64;GetMapping("/proxy/{id}") * &#64;GetMapping("/proxy/{id}")
* public ResponseEntity&lt;?&gt; proxy(@PathVariable Integer id, ProxyExchange&lt;?&gt; proxy) * public ResponseEntity&lt;?&gt; proxy(@PathVariable Integer id, ProxyExchange&lt;?&gt; proxy)
* throws Exception { * throws Exception {
* return proxy.uri("http://localhost:9000/foos/" + id).get(); * return proxy.uri("http://localhost:9000/foos/" + id).get();
* } * }
* </pre> * </pre>
* *
@@ -100,19 +100,16 @@ import org.springframework.web.util.AbstractUriTemplateHandler;
* to the type you declare. * to the type you declare.
* </p> * </p>
* <p> * <p>
* To manipulate the response use the overloaded HTTP methods * To manipulate the response use the overloaded HTTP methods with a <code>Function</code>
* with a <code>Function</code> argument and pass in code to transform the response. E.g. * argument and pass in code to transform the response. E.g.
* *
* <pre> * <pre>
* &#64;PostMapping("/proxy") * &#64;PostMapping("/proxy")
* public ResponseEntity&lt;Foo&gt; proxy(ProxyExchange&lt;Foo&gt; proxy) * public ResponseEntity&lt;Foo&gt; proxy(ProxyExchange&lt;Foo&gt; proxy) throws Exception {
* throws Exception { * return proxy.uri("http://localhost:9000/foos/")
* return proxy.uri("http://localhost:9000/foos/").post(response -> * .post(response -> ResponseEntity.status(response.getStatusCode())
* ResponseEntity.status(response.getStatusCode()) * .headers(response.getHeaders())
* .headers(response.getHeaders()) * .header("X-Custom", "MyCustomHeader").body(response.getBody()));
* .header("X-Custom", "MyCustomHeader")
* .body(response.getBody())
* );
* } * }
* *
* </pre> * </pre>
@@ -136,410 +133,396 @@ import org.springframework.web.util.AbstractUriTemplateHandler;
*/ */
public class ProxyExchange<T> { public class ProxyExchange<T> {
public static Set<String> DEFAULT_SENSITIVE = new HashSet<>( public static Set<String> DEFAULT_SENSITIVE = new HashSet<>(
Arrays.asList("cookie", "authorization")); Arrays.asList("cookie", "authorization"));
private URI uri; private URI uri;
private NestedTemplate rest; private NestedTemplate rest;
private Object body; private Object body;
private RequestResponseBodyMethodProcessor delegate; private RequestResponseBodyMethodProcessor delegate;
private NativeWebRequest webRequest; private NativeWebRequest webRequest;
private ModelAndViewContainer mavContainer; private ModelAndViewContainer mavContainer;
private WebDataBinderFactory binderFactory; private WebDataBinderFactory binderFactory;
private Set<String> sensitive; private Set<String> sensitive;
private HttpHeaders headers = new HttpHeaders(); private HttpHeaders headers = new HttpHeaders();
private Type responseType; private Type responseType;
public ProxyExchange(RestTemplate rest, NativeWebRequest webRequest, public ProxyExchange(RestTemplate rest, NativeWebRequest webRequest,
ModelAndViewContainer mavContainer, WebDataBinderFactory binderFactory, ModelAndViewContainer mavContainer, WebDataBinderFactory binderFactory,
Type type) { Type type) {
this.responseType = type; this.responseType = type;
this.rest = createTemplate(rest); this.rest = createTemplate(rest);
this.webRequest = webRequest; this.webRequest = webRequest;
this.mavContainer = mavContainer; this.mavContainer = mavContainer;
this.binderFactory = binderFactory; this.binderFactory = binderFactory;
this.delegate = new RequestResponseBodyMethodProcessor( this.delegate = new RequestResponseBodyMethodProcessor(
rest.getMessageConverters()); rest.getMessageConverters());
} }
/** /**
* Sets the body for the downstream request (if using {@link #post()}, {@link #put()} * Sets the body for the downstream request (if using {@link #post()}, {@link #put()}
* or {@link #patch()}). The body can be omitted if you just want to pass the incoming * or {@link #patch()}). The body can be omitted if you just want to pass the incoming
* request downstream without changing it. If you want to transform the incoming * request downstream without changing it. If you want to transform the incoming
* request you can declare it as a <code>@RequestBody</code> in your * request you can declare it as a <code>@RequestBody</code> in your
* <code>@RequestMapping</code> in the usual Spring MVC way. * <code>@RequestMapping</code> in the usual Spring MVC way.
* *
* @param body the request body to send downstream * @param body the request body to send downstream
* @return this for convenience * @return this for convenience
*/ */
public ProxyExchange<T> body(Object body) { public ProxyExchange<T> body(Object body) {
this.body = body; this.body = body;
return this; return this;
} }
/** /**
* Sets a header for the downstream call. * Sets a header for the downstream call.
* *
* @param name * @param name
* @param value * @param value
* @return this for convenience * @return this for convenience
*/ */
public ProxyExchange<T> header(String name, String... value) { public ProxyExchange<T> header(String name, String... value) {
this.headers.put(name, Arrays.asList(value)); this.headers.put(name, Arrays.asList(value));
return this; return this;
} }
/** /**
* Additional headers, or overrides of the incoming ones, to be used in the downstream * Additional headers, or overrides of the incoming ones, to be used in the downstream
* call. * call.
* *
* @param headers the http headers to use in the downstream call * @param headers the http headers to use in the downstream call
* @return this for convenience * @return this for convenience
*/ */
public ProxyExchange<T> headers(HttpHeaders headers) { public ProxyExchange<T> headers(HttpHeaders headers) {
this.headers.putAll(headers); this.headers.putAll(headers);
return this; return this;
} }
/** /**
* Sets the names of sensitive headers that are not passed downstream to the backend * Sets the names of sensitive headers that are not passed downstream to the backend
* service. * service.
* *
* @param names the names of sensitive headers * @param names the names of sensitive headers
* @return this for convenience * @return this for convenience
*/ */
public ProxyExchange<T> sensitive(String... names) { public ProxyExchange<T> sensitive(String... names) {
if (this.sensitive == null) { if (this.sensitive == null) {
this.sensitive = new HashSet<>(); this.sensitive = new HashSet<>();
} }
for (String name : names) { for (String name : names) {
this.sensitive.add(name.toLowerCase()); this.sensitive.add(name.toLowerCase());
} }
return this; return this;
} }
/** /**
* Sets the uri for the backend call when triggered by the HTTP methods. * Sets the uri for the backend call when triggered by the HTTP methods.
* *
* @param uri the backend uri to send the request to * @param uri the backend uri to send the request to
* @return this for convenience * @return this for convenience
*/ */
public ProxyExchange<T> uri(String uri) { public ProxyExchange<T> uri(String uri) {
try { try {
this.uri = new URI(uri); this.uri = new URI(uri);
} }
catch (URISyntaxException e) { catch (URISyntaxException e) {
throw new IllegalStateException("Cannot create URI", e); throw new IllegalStateException("Cannot create URI", e);
} }
return this; return this;
} }
public String path() { public String path() {
return (String) this.webRequest.getAttribute( return (String) this.webRequest.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE,
WebRequest.SCOPE_REQUEST); WebRequest.SCOPE_REQUEST);
} }
public String path(String prefix) { public String path(String prefix) {
String path = path(); String path = path();
if (!path.startsWith(prefix)) { if (!path.startsWith(prefix)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Path does not start with prefix (" + prefix + "): " + path); "Path does not start with prefix (" + prefix + "): " + path);
} }
return path.substring(prefix.length()); return path.substring(prefix.length());
} }
public void forward(String path) { public void forward(String path) {
HttpServletRequest request = this.webRequest HttpServletRequest request = this.webRequest
.getNativeRequest(HttpServletRequest.class); .getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = this.webRequest HttpServletResponse response = this.webRequest
.getNativeResponse(HttpServletResponse.class); .getNativeResponse(HttpServletResponse.class);
try { try {
request.getRequestDispatcher(path).forward( request.getRequestDispatcher(path).forward(
new BodyForwardingHttpServletRequest(request, response), response); new BodyForwardingHttpServletRequest(request, response), response);
} }
catch (Exception e) { catch (Exception e) {
throw new IllegalStateException("Cannot forward request", e); throw new IllegalStateException("Cannot forward request", e);
} }
} }
public ResponseEntity<T> get() { public ResponseEntity<T> get() {
RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.get(uri)) RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.get(uri))
.build(); .build();
return exchange(requestEntity); return exchange(requestEntity);
} }
public <S> ResponseEntity<S> get( public <S> ResponseEntity<S> get(
Function<ResponseEntity<T>, ResponseEntity<S>> converter) { Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.get(uri)) return converter.apply(get());
.build(); }
return converter.apply(exchange(requestEntity));
}
public ResponseEntity<T> head() { public ResponseEntity<T> head() {
RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.head(uri)) RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.head(uri))
.build(); .build();
return exchange(requestEntity); return exchange(requestEntity);
} }
public <S> ResponseEntity<S> head( public <S> ResponseEntity<S> head(
Function<ResponseEntity<T>, ResponseEntity<S>> converter) { Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.head(uri)) return converter.apply(head());
.build(); }
return converter.apply(exchange(requestEntity));
}
public ResponseEntity<T> options() { public ResponseEntity<T> options() {
RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.options(uri)) RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.options(uri))
.build(); .build();
return exchange(requestEntity); return exchange(requestEntity);
} }
public <S> ResponseEntity<S> options( public <S> ResponseEntity<S> options(
Function<ResponseEntity<T>, ResponseEntity<S>> converter) { Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
RequestEntity<?> requestEntity = headers((BodyBuilder) RequestEntity.options(uri)) return converter.apply(options());
.build(); }
return converter.apply(exchange(requestEntity));
}
public ResponseEntity<T> post() { public ResponseEntity<T> post() {
RequestEntity<Object> requestEntity = headers(RequestEntity.post(uri)) RequestEntity<Object> requestEntity = headers(RequestEntity.post(uri))
.body(body()); .body(body());
return exchange(requestEntity); return exchange(requestEntity);
} }
public <S> ResponseEntity<S> post( public <S> ResponseEntity<S> post(
Function<ResponseEntity<T>, ResponseEntity<S>> converter) { Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
RequestEntity<Object> requestEntity = headers(RequestEntity.post(uri)) return converter.apply(post());
.body(body()); }
return converter.apply(exchange(requestEntity));
}
public ResponseEntity<T> delete() { public ResponseEntity<T> delete() {
RequestEntity<Void> requestEntity = headers( RequestEntity<Void> requestEntity = headers(
(BodyBuilder) RequestEntity.delete(uri)).build(); (BodyBuilder) RequestEntity.delete(uri)).build();
return exchange(requestEntity); return exchange(requestEntity);
} }
public <S> ResponseEntity<S> delete( public <S> ResponseEntity<S> delete(
Function<ResponseEntity<T>, ResponseEntity<S>> converter) { Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
RequestEntity<Void> requestEntity = headers( return converter.apply(delete());
(BodyBuilder) RequestEntity.delete(uri)).build(); }
return converter.apply(exchange(requestEntity));
}
public ResponseEntity<T> put() { public ResponseEntity<T> put() {
RequestEntity<Object> requestEntity = headers(RequestEntity.put(uri)) RequestEntity<Object> requestEntity = headers(RequestEntity.put(uri))
.body(body()); .body(body());
return exchange(requestEntity); return exchange(requestEntity);
} }
public <S> ResponseEntity<S> put( public <S> ResponseEntity<S> put(
Function<ResponseEntity<T>, ResponseEntity<S>> converter) { Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
RequestEntity<Object> requestEntity = headers(RequestEntity.put(uri)) return converter.apply(put());
.body(body()); }
return converter.apply(exchange(requestEntity));
}
public ResponseEntity<T> patch() { public ResponseEntity<T> patch() {
RequestEntity<Object> requestEntity = headers(RequestEntity.patch(uri)) RequestEntity<Object> requestEntity = headers(RequestEntity.patch(uri))
.body(body()); .body(body());
return exchange(requestEntity); return exchange(requestEntity);
} }
public <S> ResponseEntity<S> patch( public <S> ResponseEntity<S> patch(
Function<ResponseEntity<T>, ResponseEntity<S>> converter) { Function<ResponseEntity<T>, ResponseEntity<S>> converter) {
RequestEntity<Object> requestEntity = headers(RequestEntity.patch(uri)) return converter.apply(patch());
.body(body()); }
return converter.apply(exchange(requestEntity));
}
private ResponseEntity<T> exchange(RequestEntity<?> requestEntity) { private ResponseEntity<T> exchange(RequestEntity<?> requestEntity) {
Type type = this.responseType; Type type = this.responseType;
if (type instanceof TypeVariable || type instanceof WildcardType) { if (type instanceof TypeVariable || type instanceof WildcardType) {
type = Object.class; type = Object.class;
} }
RequestCallback requestCallback = rest.httpEntityCallback((Object) requestEntity, RequestCallback requestCallback = rest.httpEntityCallback((Object) requestEntity,
type); type);
ResponseExtractor<ResponseEntity<T>> responseExtractor = rest ResponseExtractor<ResponseEntity<T>> responseExtractor = rest
.responseEntityExtractor(type); .responseEntityExtractor(type);
return rest.execute(requestEntity.getUrl(), requestEntity.getMethod(), return rest.execute(requestEntity.getUrl(), requestEntity.getMethod(),
requestCallback, responseExtractor); requestCallback, responseExtractor);
} }
private BodyBuilder headers(BodyBuilder builder) { private BodyBuilder headers(BodyBuilder builder) {
Set<String> sensitive = this.sensitive; Set<String> sensitive = this.sensitive;
if (sensitive == null) { if (sensitive == null) {
sensitive = DEFAULT_SENSITIVE; sensitive = DEFAULT_SENSITIVE;
} }
for (String name : headers.keySet()) { for (String name : headers.keySet()) {
if (sensitive.contains(name.toLowerCase())) { if (sensitive.contains(name.toLowerCase())) {
continue; continue;
} }
builder.header(name, headers.get(name).toArray(new String[0])); builder.header(name, headers.get(name).toArray(new String[0]));
} }
return builder; return builder;
} }
private Object body() { private Object body() {
if (body != null) { if (body != null) {
return body; return body;
} }
body = getRequestBody(); body = getRequestBody();
return body; return body;
} }
/** /**
* Search for the request body if it was already deserialized using * Search for the request body if it was already deserialized using
* <code>@RequestBody</code>. If it is not found then deserialize it in the same way * <code>@RequestBody</code>. If it is not found then deserialize it in the same way
* that it would have been for a <code>@RequestBody</code>. * that it would have been for a <code>@RequestBody</code>.
* *
* @return the request body * @return the request body
*/ */
private Object getRequestBody() { private Object getRequestBody() {
for (String key : mavContainer.getModel().keySet()) { for (String key : mavContainer.getModel().keySet()) {
if (key.startsWith(BindingResult.MODEL_KEY_PREFIX)) { if (key.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
BindingResult result = (BindingResult) mavContainer.getModel().get(key); BindingResult result = (BindingResult) mavContainer.getModel().get(key);
return result.getTarget(); return result.getTarget();
} }
} }
MethodParameter input = new MethodParameter( MethodParameter input = new MethodParameter(
ClassUtils.getMethod(BodyGrabber.class, "body", Object.class), 0); ClassUtils.getMethod(BodyGrabber.class, "body", Object.class), 0);
try { try {
delegate.resolveArgument(input, mavContainer, webRequest, binderFactory); delegate.resolveArgument(input, mavContainer, webRequest, binderFactory);
} }
catch (Exception e) { catch (Exception e) {
throw new IllegalStateException("Cannot resolve body", e); throw new IllegalStateException("Cannot resolve body", e);
} }
String name = Conventions.getVariableNameForParameter(input); String name = Conventions.getVariableNameForParameter(input);
BindingResult result = (BindingResult) mavContainer.getModel() BindingResult result = (BindingResult) mavContainer.getModel()
.get(BindingResult.MODEL_KEY_PREFIX + name); .get(BindingResult.MODEL_KEY_PREFIX + name);
return result.getTarget(); return result.getTarget();
} }
private NestedTemplate createTemplate(RestTemplate input) { private NestedTemplate createTemplate(RestTemplate input) {
NestedTemplate rest = new NestedTemplate(); NestedTemplate rest = new NestedTemplate();
rest.setMessageConverters(input.getMessageConverters()); rest.setMessageConverters(input.getMessageConverters());
rest.setErrorHandler(input.getErrorHandler()); rest.setErrorHandler(input.getErrorHandler());
rest.setDefaultUriVariables( rest.setDefaultUriVariables(
((AbstractUriTemplateHandler) input.getUriTemplateHandler()) ((AbstractUriTemplateHandler) input.getUriTemplateHandler())
.getDefaultUriVariables()); .getDefaultUriVariables());
rest.setRequestFactory(input.getRequestFactory()); rest.setRequestFactory(input.getRequestFactory());
rest.setInterceptors(input.getInterceptors()); rest.setInterceptors(input.getInterceptors());
return rest; return rest;
} }
/** /**
* A special {@link RestTemplate} that knows about the {@link Type} of its response * A special {@link RestTemplate} that knows about the {@link Type} of its response
* body explicitly (rather than through a {@link ParameterizedTypeReference}, which is * body explicitly (rather than through a {@link ParameterizedTypeReference}, which is
* the only way to access this feature in a regular template). * the only way to access this feature in a regular template).
* *
*/ */
class NestedTemplate extends RestTemplate { class NestedTemplate extends RestTemplate {
@Override @Override
protected <S> RequestCallback httpEntityCallback(Object requestBody, protected <S> RequestCallback httpEntityCallback(Object requestBody,
Type responseType) { Type responseType) {
return super.httpEntityCallback(requestBody, responseType); return super.httpEntityCallback(requestBody, responseType);
} }
@Override @Override
protected <S> ResponseExtractor<ResponseEntity<S>> responseEntityExtractor( protected <S> ResponseExtractor<ResponseEntity<S>> responseEntityExtractor(
Type responseType) { Type responseType) {
return super.responseEntityExtractor(responseType); return super.responseEntityExtractor(responseType);
} }
} }
/** /**
* A servlet request wrapper that can be safely passed downstream to an internal * A servlet request wrapper that can be safely passed downstream to an internal
* forward dispatch, caching its body, and making it available in converted form using * forward dispatch, caching its body, and making it available in converted form using
* Spring message converters. * Spring message converters.
* *
*/ */
class BodyForwardingHttpServletRequest extends HttpServletRequestWrapper { class BodyForwardingHttpServletRequest extends HttpServletRequestWrapper {
private HttpServletRequest request; private HttpServletRequest request;
private HttpServletResponse response; private HttpServletResponse response;
BodyForwardingHttpServletRequest(HttpServletRequest request, BodyForwardingHttpServletRequest(HttpServletRequest request,
HttpServletResponse response) { HttpServletResponse response) {
super(request); super(request);
this.request = request; this.request = request;
this.response = response; this.response = response;
} }
private List<String> header(String name) { private List<String> header(String name) {
List<String> list = headers.get(name); List<String> list = headers.get(name);
return list; return list;
} }
@Override @Override
public ServletInputStream getInputStream() throws IOException { public ServletInputStream getInputStream() throws IOException {
Object body = body(); Object body = body();
MethodParameter output = new MethodParameter( MethodParameter output = new MethodParameter(
ClassUtils.getMethod(BodySender.class, "body"), -1); ClassUtils.getMethod(BodySender.class, "body"), -1);
ServletOutputToInputConverter response = new ServletOutputToInputConverter( ServletOutputToInputConverter response = new ServletOutputToInputConverter(
this.response); this.response);
ServletWebRequest webRequest = new ServletWebRequest(this.request, response); ServletWebRequest webRequest = new ServletWebRequest(this.request, response);
try { try {
delegate.handleReturnValue(body, output, mavContainer, webRequest); delegate.handleReturnValue(body, output, mavContainer, webRequest);
} }
catch (HttpMessageNotWritableException catch (HttpMessageNotWritableException
| HttpMediaTypeNotAcceptableException e) { | HttpMediaTypeNotAcceptableException e) {
throw new IllegalStateException("Cannot convert body"); throw new IllegalStateException("Cannot convert body");
} }
return response.getInputStream(); return response.getInputStream();
} }
@Override @Override
public Enumeration<String> getHeaderNames() { public Enumeration<String> getHeaderNames() {
Set<String> names = headers.keySet(); Set<String> names = headers.keySet();
if (names.isEmpty()) { if (names.isEmpty()) {
return super.getHeaderNames(); return super.getHeaderNames();
} }
Set<String> result = new LinkedHashSet<>(names); Set<String> result = new LinkedHashSet<>(names);
result.addAll(Collections.list(super.getHeaderNames())); result.addAll(Collections.list(super.getHeaderNames()));
return new Vector<String>(result).elements(); return new Vector<String>(result).elements();
} }
@Override @Override
public Enumeration<String> getHeaders(String name) { public Enumeration<String> getHeaders(String name) {
List<String> list = header(name); List<String> list = header(name);
if (list != null) { if (list != null) {
return new Vector<String>(list).elements(); return new Vector<String>(list).elements();
} }
return super.getHeaders(name); return super.getHeaders(name);
} }
@Override @Override
public String getHeader(String name) { public String getHeader(String name) {
List<String> list = header(name); List<String> list = header(name);
if (list != null && !list.isEmpty()) { if (list != null && !list.isEmpty()) {
return list.iterator().next(); return list.iterator().next();
} }
return super.getHeader(name); return super.getHeader(name);
} }
} }
protected static class BodyGrabber { protected static class BodyGrabber {
public Object body(@RequestBody Object body) { public Object body(@RequestBody Object body) {
return body; return body;
} }
} }
protected static class BodySender { protected static class BodySender {
@ResponseBody @ResponseBody
public Object body() { public Object body() {
return null; return null;
} }
} }
} }
@@ -556,56 +539,56 @@ public class ProxyExchange<T> {
*/ */
class ServletOutputToInputConverter extends HttpServletResponseWrapper { class ServletOutputToInputConverter extends HttpServletResponseWrapper {
private StringBuilder builder = new StringBuilder(); private StringBuilder builder = new StringBuilder();
public ServletOutputToInputConverter(HttpServletResponse response) { public ServletOutputToInputConverter(HttpServletResponse response) {
super(response); super(response);
} }
@Override @Override
public ServletOutputStream getOutputStream() throws IOException { public ServletOutputStream getOutputStream() throws IOException {
return new ServletOutputStream() { return new ServletOutputStream() {
@Override @Override
public void write(int b) throws IOException { public void write(int b) throws IOException {
builder.append(new Character((char) b)); builder.append(new Character((char) b));
} }
@Override @Override
public void setWriteListener(WriteListener listener) { public void setWriteListener(WriteListener listener) {
} }
@Override @Override
public boolean isReady() { public boolean isReady() {
return true; return true;
} }
}; };
} }
public ServletInputStream getInputStream() { public ServletInputStream getInputStream() {
ByteArrayInputStream body = new ByteArrayInputStream( ByteArrayInputStream body = new ByteArrayInputStream(
builder.toString().getBytes()); builder.toString().getBytes());
return new ServletInputStream() { return new ServletInputStream() {
@Override @Override
public int read() throws IOException { public int read() throws IOException {
return body.read(); return body.read();
} }
@Override @Override
public void setReadListener(ReadListener listener) { public void setReadListener(ReadListener listener) {
} }
@Override @Override
public boolean isReady() { public boolean isReady() {
return true; return true;
} }
@Override @Override
public boolean isFinished() { public boolean isFinished() {
return body.available() <= 0; return body.available() <= 0;
} }
}; };
} }
} }