You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2018/04/20 01:50:27 UTC
[incubator-servicecomb-java-chassis] 03/09: SCB-483
HttpServletResponseEx from vertx support sendPart
This is an automated email from the ASF dual-hosted git repository.
liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git
commit 211afe6cc8815c08e6d8d452c09e8c75b4d08bf4
Author: wujimin <wu...@huawei.com>
AuthorDate: Tue Apr 17 23:50:39 2018 +0800
SCB-483 HttpServletResponseEx from vertx support sendPart
---
.../vertx/http/AbstractHttpServletResponse.java | 7 ++
.../vertx/http/HttpServletResponseEx.java | 5 +
.../vertx/http/StandardHttpServletResponseEx.java | 7 ++
.../VertxServerResponseToHttpServletResponse.java | 52 ++++++++
.../http/TestAbstractHttpServletResponse.java | 15 +++
.../http/TestStandardHttpServletResponseEx.java | 24 ++++
...stVertxServerResponseToHttpServletResponse.java | 132 +++++++++++++++++++++
7 files changed, 242 insertions(+)
diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletResponse.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletResponse.java
index a09e773..2140429 100644
--- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletResponse.java
+++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletResponse.java
@@ -23,9 +23,11 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
+import javax.servlet.http.Part;
import javax.ws.rs.core.Response.StatusType;
public abstract class AbstractHttpServletResponse extends BodyBufferSupportImpl implements HttpServletResponseEx {
@@ -230,4 +232,9 @@ public abstract class AbstractHttpServletResponse extends BodyBufferSupportImpl
public Object getAttribute(String key) {
return this.attributes.get(key);
}
+
+ @Override
+ public CompletableFuture<Void> sendPart(Part body) {
+ throw new Error("not supported method");
+ }
}
diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletResponseEx.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletResponseEx.java
index 9749f4b..a1ee34e 100644
--- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletResponseEx.java
+++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletResponseEx.java
@@ -17,7 +17,10 @@
package org.apache.servicecomb.foundation.vertx.http;
+import java.util.concurrent.CompletableFuture;
+
import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.Part;
import javax.ws.rs.core.Response.StatusType;
public interface HttpServletResponseEx extends HttpServletResponse, BodyBufferSupport {
@@ -26,4 +29,6 @@ public interface HttpServletResponseEx extends HttpServletResponse, BodyBufferSu
void setAttribute(String key, Object value);
Object getAttribute(String key);
+
+ CompletableFuture<Void> sendPart(Part body);
}
diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletResponseEx.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletResponseEx.java
index c2c1408..13c0ba7 100644
--- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletResponseEx.java
+++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletResponseEx.java
@@ -20,9 +20,11 @@ package org.apache.servicecomb.foundation.vertx.http;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
+import javax.servlet.http.Part;
import javax.ws.rs.core.Response.StatusType;
import org.apache.servicecomb.foundation.common.http.HttpStatus;
@@ -95,4 +97,9 @@ public class StandardHttpServletResponseEx extends HttpServletResponseWrapper im
public Object getAttribute(String key) {
return this.attributes.get(key);
}
+
+ @Override
+ public CompletableFuture<Void> sendPart(Part body) {
+ throw new Error("not supported method");
+ }
}
diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java
index ca5510c..9f5db55 100644
--- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java
+++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java
@@ -18,17 +18,23 @@
package org.apache.servicecomb.foundation.vertx.http;
import java.io.IOException;
+import java.io.InputStream;
import java.util.Collection;
import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import javax.servlet.http.Part;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response.StatusType;
+import org.apache.commons.io.IOUtils;
import org.apache.servicecomb.foundation.common.http.HttpStatus;
+import org.apache.servicecomb.foundation.vertx.stream.InputStreamToReadStream;
import io.vertx.core.Context;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServerResponse;
+import io.vertx.core.streams.Pump;
public class VertxServerResponseToHttpServletResponse extends AbstractHttpServletResponse {
private Context context;
@@ -118,4 +124,50 @@ public class VertxServerResponseToHttpServletResponse extends AbstractHttpServle
serverResponse.end(bodyBuffer);
}
+
+ @Override
+ public CompletableFuture<Void> sendPart(Part part) {
+ CompletableFuture<Void> future = new CompletableFuture<Void>();
+
+ prepareSendPartHeader(part);
+
+ try {
+ InputStream is = part.getInputStream();
+ context.runOnContext(v -> {
+ InputStreamToReadStream aa = new InputStreamToReadStream(context.owner(), is);
+ aa.exceptionHandler(t -> {
+ clearPartResource(part, is);
+ future.completeExceptionally(t);
+ });
+ aa.endHandler(V -> {
+ clearPartResource(part, is);
+ future.complete(null);
+ });
+ Pump.pump(aa, serverResponse).start();
+ });
+ } catch (IOException e) {
+ future.completeExceptionally(e);
+ }
+
+ return future;
+ }
+
+ protected void prepareSendPartHeader(Part part) {
+ if (!serverResponse.headers().contains(HttpHeaders.CONTENT_LENGTH)) {
+ serverResponse.setChunked(true);
+ }
+
+ if (!serverResponse.headers().contains(HttpHeaders.CONTENT_TYPE)) {
+ serverResponse.putHeader(HttpHeaders.CONTENT_TYPE, part.getContentType());
+ }
+
+ if (!serverResponse.headers().contains(HttpHeaders.CONTENT_DISPOSITION)) {
+ serverResponse.putHeader(HttpHeaders.CONTENT_DISPOSITION,
+ "attachment;filename=" + part.getSubmittedFileName());
+ }
+ }
+
+ protected void clearPartResource(Part part, InputStream is) {
+ IOUtils.closeQuietly(is);
+ }
}
diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletResponse.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletResponse.java
index 2462772..ec884ee 100644
--- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletResponse.java
+++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletResponse.java
@@ -20,10 +20,12 @@ package org.apache.servicecomb.foundation.vertx.http;
import java.io.IOException;
import org.hamcrest.Matchers;
+import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+
public class TestAbstractHttpServletResponse {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@@ -304,4 +306,17 @@ public class TestAbstractHttpServletResponse {
response.getStatusType();
}
+
+ @Test
+ public void attribute() {
+ response.setAttribute("k", "v");
+ Assert.assertEquals("v", response.getAttribute("k"));
+ }
+
+ @Test
+ public void sendPart() {
+ setExceptionExpected();
+
+ response.sendPart(null);
+ }
}
diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletResponseEx.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletResponseEx.java
index 82853b2..00cb71e 100644
--- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletResponseEx.java
+++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletResponseEx.java
@@ -23,9 +23,12 @@ import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
+import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
import io.vertx.core.buffer.Buffer;
import mockit.Mock;
@@ -38,6 +41,14 @@ public class TestStandardHttpServletResponseEx {
StandardHttpServletResponseEx responseEx;
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private void setExceptionExpected() {
+ expectedException.expect(Error.class);
+ expectedException.expectMessage(Matchers.is("not supported method"));
+ }
+
@Before
public void setup() {
responseEx = new StandardHttpServletResponseEx(response);
@@ -114,4 +125,17 @@ public class TestStandardHttpServletResponseEx {
responseEx.flushBuffer();
Assert.assertEquals("body", buffer.toString());
}
+
+ @Test
+ public void attribute() {
+ responseEx.setAttribute("k", "v");
+ Assert.assertEquals("v", responseEx.getAttribute("k"));
+ }
+
+ @Test
+ public void sendPart() {
+ setExceptionExpected();
+
+ responseEx.sendPart(null);
+ }
}
diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerResponseToHttpServletResponse.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerResponseToHttpServletResponse.java
index d821d14..c69d41c 100644
--- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerResponseToHttpServletResponse.java
+++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerResponseToHttpServletResponse.java
@@ -18,7 +18,11 @@
package org.apache.servicecomb.foundation.vertx.http;
import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import javax.servlet.http.Part;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response.StatusType;
@@ -30,9 +34,12 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import io.vertx.core.AsyncResult;
import io.vertx.core.Context;
+import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
+import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.impl.VertxImpl;
@@ -56,11 +63,16 @@ public class TestVertxServerResponseToHttpServletResponse {
boolean runOnContextInvoked;
@Mocked
+ Vertx vertx;
+
+ @Mocked
Context context;
@Rule
public ExpectedException expectedException = ExpectedException.none();
+ boolean chunked;
+
@Before
public void setup() {
serverResponse = new MockUp<HttpServerResponse>() {
@@ -92,6 +104,12 @@ public class TestVertxServerResponseToHttpServletResponse {
}
@Mock
+ HttpServerResponse putHeader(String name, String value) {
+ headers.set(name, value);
+ return serverResponse;
+ }
+
+ @Mock
void end() {
flushWithBody = false;
}
@@ -100,6 +118,17 @@ public class TestVertxServerResponseToHttpServletResponse {
void end(Buffer chunk) {
flushWithBody = true;
}
+
+ @Mock
+ HttpServerResponse setChunked(boolean chunked) {
+ TestVertxServerResponseToHttpServletResponse.this.chunked = chunked;
+ return serverResponse;
+ }
+
+ @Mock
+ boolean isChunked() {
+ return chunked;
+ }
}.getMockInstance();
new Expectations(VertxImpl.class) {
@@ -115,6 +144,22 @@ public class TestVertxServerResponseToHttpServletResponse {
runOnContextInvoked = true;
action.handle(null);
}
+
+ @Mock
+ Vertx owner() {
+ return vertx;
+ }
+ };
+
+ new MockUp<Vertx>(vertx) {
+ @Mock
+ <T> void executeBlocking(Handler<Future<T>> blockingCodeHandler, boolean ordered,
+ Handler<AsyncResult<T>> resultHandler) {
+ Future<T> future = Future.future();
+ future.setHandler(resultHandler);
+
+ blockingCodeHandler.handle(future);
+ }
};
response = new VertxServerResponseToHttpServletResponse(serverResponse);
@@ -246,4 +291,91 @@ public class TestVertxServerResponseToHttpServletResponse {
Assert.assertTrue(flushWithBody);
}
+
+ @Test
+ public void prepareSendPartHeader_update(@Mocked Part part) {
+ new Expectations() {
+ {
+ part.getContentType();
+ result = "type";
+ part.getSubmittedFileName();
+ result = "name";
+ }
+ };
+ response.prepareSendPartHeader(part);
+
+ Assert.assertTrue(serverResponse.isChunked());
+ Assert.assertEquals("type", response.getHeader(HttpHeaders.CONTENT_TYPE));
+ Assert.assertEquals("attachment;filename=name", response.getHeader(HttpHeaders.CONTENT_DISPOSITION));
+ }
+
+ @Test
+ public void prepareSendPartHeader_notUpdate(@Mocked Part part) {
+ headers.add(HttpHeaders.CONTENT_LENGTH, "10");
+ headers.add(HttpHeaders.CONTENT_TYPE, "type");
+ headers.add(HttpHeaders.CONTENT_DISPOSITION, "disposition");
+
+ response.prepareSendPartHeader(part);
+
+ Assert.assertFalse(serverResponse.isChunked());
+ Assert.assertEquals("type", response.getHeader(HttpHeaders.CONTENT_TYPE));
+ Assert.assertEquals("disposition", response.getHeader(HttpHeaders.CONTENT_DISPOSITION));
+ }
+
+ @Test
+ public void sendPart_openInputStreamFailed(@Mocked Part part)
+ throws IOException, InterruptedException, ExecutionException {
+ IOException ioException = new IOException("forbid open stream");
+ new Expectations() {
+ {
+ part.getInputStream();
+ result = ioException;
+ }
+ };
+
+ CompletableFuture<Void> future = response.sendPart(part);
+
+ expectedException.expect(ExecutionException.class);
+ expectedException.expectCause(Matchers.sameInstance(ioException));
+
+ future.get();
+ }
+
+ @Test
+ public void sendPart_inputStreamBreak(@Mocked Part part, @Mocked InputStream inputStream)
+ throws IOException, InterruptedException, ExecutionException {
+ IOException ioException = new IOException("forbid read");
+ new Expectations() {
+ {
+ part.getInputStream();
+ result = inputStream;
+ inputStream.read((byte[]) any);
+ result = ioException;
+ }
+ };
+
+ CompletableFuture<Void> future = response.sendPart(part);
+
+ expectedException.expect(ExecutionException.class);
+ expectedException.expectCause(Matchers.sameInstance(ioException));
+
+ future.get();
+ }
+
+ @Test
+ public void sendPart_succ(@Mocked Part part, @Mocked InputStream inputStream)
+ throws IOException, InterruptedException, ExecutionException {
+ new Expectations() {
+ {
+ part.getInputStream();
+ result = inputStream;
+ inputStream.read((byte[]) any);
+ result = -1;
+ }
+ };
+
+ CompletableFuture<Void> future = response.sendPart(part);
+
+ Assert.assertNull(future.get());
+ }
}
--
To stop receiving notification emails like this one, please contact
liubao@apache.org.