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/servicecomb-java-chassis.git


The following commit(s) were added to refs/heads/master by this push:
     new 65a114106 [SCB-2885]refactoring buffer reader to support SSE (#4382)
65a114106 is described below

commit 65a1141065ba45ef891fb46a4e41a786219eed9f
Author: liubao68 <[email protected]>
AuthorDate: Tue Jun 25 21:24:26 2024 +0800

    [SCB-2885]refactoring buffer reader to support SSE (#4382)
    
    * [SCB-2885]refactoring buffer reader to support SSE
    
    * fix test error
    
    * fix test error
---
 .../common/rest/RestProducerInvocationFlow.java    |  22 +---
 .../rest/filter/inner/RestServerCodecFilter.java   | 131 ++++++++++++++++-----
 .../filter/inner/RestServerCodecFilterTest.java    |   7 +-
 .../core/invocation/ProducerInvocationFlow.java    |   6 +-
 .../invocation/ProducerInvocationFlowTest.java     |   2 +-
 .../vertx/http/AbstractHttpServletResponse.java    |  11 ++
 .../vertx/http/HttpServletResponseEx.java          |   5 +
 .../vertx/http/StandardHttpServletResponseEx.java  |  18 ++-
 .../VertxServerResponseToHttpServletResponse.java  |  28 +++--
 .../http/TestStandardHttpServletResponseEx.java    |   2 +-
 ...stVertxServerResponseToHttpServletResponse.java |   6 +-
 .../highway/HighwayProducerInvocationFlow.java     |   2 +-
 12 files changed, 173 insertions(+), 67 deletions(-)

diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java
index dab6d6f6d..727346c82 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java
@@ -16,8 +16,6 @@
  */
 package org.apache.servicecomb.common.rest;
 
-import static 
org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter.isDownloadFileResponseType;
-
 import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor;
 import 
org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager;
 import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter;
@@ -25,11 +23,9 @@ import org.apache.servicecomb.core.Invocation;
 import org.apache.servicecomb.core.exception.Exceptions;
 import org.apache.servicecomb.core.invocation.InvocationCreator;
 import org.apache.servicecomb.core.invocation.ProducerInvocationFlow;
-import org.apache.servicecomb.foundation.common.utils.PartUtils;
 import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx;
 import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx;
 import org.apache.servicecomb.swagger.invocation.Response;
-import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -48,32 +44,26 @@ public class RestProducerInvocationFlow extends 
ProducerInvocationFlow {
   protected Invocation sendCreateInvocationException(Throwable throwable) {
     try {
       Response response = Exceptions.toProducerResponse(null, throwable);
-      RestServerCodecFilter.encodeResponse(null, response, false, 
DEFAULT_PRODUCE_PROCESSOR, responseEx);
+      RestServerCodecFilter.encodeResponse(null, response, 
DEFAULT_PRODUCE_PROCESSOR, responseEx);
     } catch (Throwable e) {
       LOGGER.error("Failed to send response when prepare invocation failed, 
request uri:{}",
           requestEx.getRequestURI(), e);
     }
 
-    flushResponse(null);
+    endResponse(null);
     return null;
   }
 
   @Override
-  protected void sendResponse(Invocation invocation, Response response) {
+  protected void endResponse(Invocation invocation, Response response) {
     invocation.getInvocationStageTrace().startProviderSendResponse();
-    boolean failed = response.getResult() instanceof InvocationException;
-    if (!failed && isDownloadFileResponseType(invocation, response)) {
-      responseEx.sendPart(PartUtils.getSinglePart(null, response.getResult()))
-          .whenComplete((r, e) -> flushResponse(invocation));
-      return;
-    }
 
-    flushResponse(invocation);
+    endResponse(invocation);
   }
 
-  private void flushResponse(Invocation invocation) {
+  private void endResponse(Invocation invocation) {
     try {
-      responseEx.flushBuffer();
+      responseEx.endResponse();
     } catch (Throwable flushException) {
       LOGGER.error("Failed to flush rest response, operation:{}, request 
uri:{}",
           invocation == null ? "NA" :
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java
index 348ee7f19..d47dd4bce 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java
@@ -39,14 +39,16 @@ import org.apache.servicecomb.core.filter.EdgeFilter;
 import org.apache.servicecomb.core.filter.Filter;
 import org.apache.servicecomb.core.filter.FilterNode;
 import org.apache.servicecomb.core.filter.ProviderFilter;
-import org.apache.servicecomb.foundation.common.utils.AsyncUtils;
+import org.apache.servicecomb.foundation.common.utils.PartUtils;
 import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx;
 import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx;
 import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream;
 import org.apache.servicecomb.swagger.invocation.Response;
 import org.apache.servicecomb.swagger.invocation.context.TransportContext;
-import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
 import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
+import org.reactivestreams.Publisher;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -106,54 +108,121 @@ public class RestServerCodecFilter extends 
AbstractFilter implements ProviderFil
 
   protected CompletableFuture<Response> encodeResponse(Invocation invocation, 
Response response) {
     invocation.onEncodeResponseStart(response);
-
     HttpTransportContext transportContext = invocation.getTransportContext();
-
+    HttpServletResponseEx responseEx = transportContext.getResponseEx();
     // TODO: response support JsonView
     ProduceProcessor produceProcessor = ProduceProcessorManager.INSTANCE
         .createProduceProcessor(invocation.getOperationMeta(), 
response.getStatusCode(),
             invocation.getRequestEx().getHeader(HttpHeaders.ACCEPT), null);
-    HttpServletResponseEx responseEx = transportContext.getResponseEx();
-    boolean download = isDownloadFileResponseType(invocation, response);
 
-    return encodeResponse(invocation, response, download, produceProcessor, 
responseEx)
+    return encodeResponse(invocation, response, produceProcessor, responseEx)
         .whenComplete((r, e) -> invocation.onEncodeResponseFinish());
   }
 
-  public static CompletableFuture<Response> encodeResponse(Invocation 
invocation, Response response, boolean download,
+  private static boolean isFailedResponse(Response response) {
+    return response.getResult() instanceof InvocationException;
+  }
+
+  private static CompletableFuture<Response> writePart(
+      HttpServletResponseEx responseEx, Object data, Response response) {
+    CompletableFuture<Response> result = new CompletableFuture<>();
+    responseEx.sendPart(PartUtils.getSinglePart(null, data))
+        .whenComplete((r, e) -> {
+          if (e != null) {
+            result.completeExceptionally(e);
+            return;
+          }
+          result.complete(response);
+        });
+    return result;
+  }
+
+  private static CompletableFuture<Response> writeResponse(
+      HttpServletResponseEx responseEx, ProduceProcessor produceProcessor, 
Object data, Response response,
+      boolean commit) {
+    try (BufferOutputStream output = new BufferOutputStream(Buffer.buffer())) {
+      produceProcessor.encodeResponse(output, data);
+
+      CompletableFuture<Response> result = new CompletableFuture<>();
+      responseEx.setBodyBuffer(output.getBuffer()); // For extensions usage
+      if (commit) {
+        responseEx.setContentLength(output.getBuffer().length());
+      }
+      responseEx.sendBuffer(output.getBuffer()).whenComplete((v, e) -> {
+        if (e != null) {
+          result.completeExceptionally(e);
+          return;
+        }
+        result.complete(response);
+      });
+      return result;
+    } catch (Throwable e) {
+      LOGGER.error("internal service error must be fixed.", e);
+      responseEx.setStatus(500);
+      return CompletableFuture.failedFuture(e);
+    }
+  }
+
+  public static CompletableFuture<Response> encodeResponse(Invocation 
invocation, Response response,
       ProduceProcessor produceProcessor, HttpServletResponseEx responseEx) {
     responseEx.setStatus(response.getStatusCode());
     copyHeadersToHttpResponse(invocation, response.getHeaders(), responseEx);
 
-    boolean failed = response.getResult() instanceof InvocationException;
+    if (isFailedResponse(response)) {
+      responseEx.setContentType(produceProcessor.getName());
+      return writeResponse(responseEx, produceProcessor, 
((InvocationException) response.getResult()).getErrorData(),
+          response, true);
+    }
+
+    if (isDownloadFileResponseType(invocation, response)) {
+      return writePart(responseEx, response.getResult(), response);
+    }
 
-    if (!failed && download) {
-      return CompletableFuture.completedFuture(response);
+    if (isServerSendEvent(response)) {
+      responseEx.setContentType(produceProcessor.getName());
+      writeServerSendEvent(response, produceProcessor, responseEx);
     }
 
     responseEx.setContentType(produceProcessor.getName());
-    try (BufferOutputStream output = new BufferOutputStream(Buffer.buffer())) {
-      if (failed) {
-        produceProcessor.encodeResponse(output, ((InvocationException) 
response.getResult()).getErrorData());
-      } else {
-        produceProcessor.encodeResponse(output, response.getResult());
+    return writeResponse(responseEx, produceProcessor, response.getResult(), 
response, true);
+  }
+
+  private static void writeServerSendEvent(Response response, ProduceProcessor 
produceProcessor,
+      HttpServletResponseEx responseEx) {
+    responseEx.setChunked(true);
+    CompletableFuture<Response> result = new CompletableFuture<>();
+    Publisher<?> publisher = response.getResult();
+    publisher.subscribe(new Subscriber<Object>() {
+      Subscription subscription;
+
+      @Override
+      public void onSubscribe(Subscription s) {
+        s.request(1);
+        subscription = s;
       }
 
-      responseEx.setBodyBuffer(output.getBuffer());
+      @Override
+      public void onNext(Object o) {
+        writeResponse(responseEx, produceProcessor, o, response, 
false).whenComplete((r, e) -> {
+          if (e != null) {
+            subscription.cancel();
+            result.completeExceptionally(e);
+            return;
+          }
+          subscription.request(1);
+        });
+      }
 
-      return CompletableFuture.completedFuture(response);
-    } catch (Throwable e) {
-      LOGGER.error("internal service error must be fixed.", e);
-      try (BufferOutputStream output = new 
BufferOutputStream(Buffer.buffer())) {
-        responseEx.setStatus(500);
-        produceProcessor.encodeResponse(output, new CommonExceptionData("500", 
"Internal Server Error"));
-        responseEx.setBodyBuffer(output.getBuffer());
-        return CompletableFuture.completedFuture(response);
-      } catch (Throwable e1) {
-        // we have no idea how to do, no response given to client.
-        return AsyncUtils.completeExceptionally(e);
+      @Override
+      public void onError(Throwable t) {
+        result.completeExceptionally(t);
       }
-    }
+
+      @Override
+      public void onComplete() {
+        result.complete(response);
+      }
+    });
   }
 
   /**
@@ -167,6 +236,10 @@ public class RestServerCodecFilter extends AbstractFilter 
implements ProviderFil
         invocation.findResponseType(response.getStatusCode()).getRawClass());
   }
 
+  public static boolean isServerSendEvent(Response response) {
+    return response.getResult() instanceof Publisher<?>;
+  }
+
   public static void copyHeadersToHttpResponse(Invocation invocation, MultiMap 
headers,
       HttpServletResponseEx responseEx) {
     if (headers != null) {
diff --git 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java
 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java
index 559a6af10..0decec28b 100644
--- 
a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java
+++ 
b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java
@@ -23,6 +23,7 @@ import static 
jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
 import static 
org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC;
 import static 
org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC;
 
+import java.io.ByteArrayInputStream;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
 
@@ -41,6 +42,7 @@ import org.apache.servicecomb.core.definition.SchemaMeta;
 import org.apache.servicecomb.core.filter.AbstractFilter;
 import org.apache.servicecomb.core.filter.FilterNode;
 import org.apache.servicecomb.core.invocation.InvocationFactory;
+import org.apache.servicecomb.foundation.common.part.InputStreamPart;
 import 
org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace;
 import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx;
 import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx;
@@ -88,6 +90,8 @@ public class RestServerCodecFilterTest {
 
   final MultiMap headers = MultiMap.caseInsensitiveMultiMap();
 
+  final Part part = new InputStreamPart("ok", new ByteArrayInputStream(new 
byte[] {1, 2}));
+
   final FilterNode nextNode = new FilterNode(new AbstractFilter() {
     @Override
     public String getName() {
@@ -96,7 +100,7 @@ public class RestServerCodecFilterTest {
 
     @Override
     public CompletableFuture<Response> onFilter(Invocation invocation, 
FilterNode nextNode) {
-      Response response = Response.ok("ok");
+      Response response = Response.ok(part);
       response.setHeaders(headers);
       return CompletableFuture.completedFuture(response);
     }
@@ -125,6 +129,7 @@ public class RestServerCodecFilterTest {
     
Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta);
     
Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType);
     invocation = Mockito.spy(InvocationFactory.forProvider(endpoint, 
operationMeta, null));
+    
Mockito.when(responseEx.sendPart(part)).thenReturn(CompletableFuture.completedFuture(null));
   }
 
   private void mockDecodeRequestFail() {
diff --git 
a/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java
 
b/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java
index 88371433c..7304fabae 100644
--- 
a/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java
+++ 
b/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java
@@ -76,7 +76,7 @@ public abstract class ProducerInvocationFlow {
               response = Response.createFail(new 
InvocationException(Status.INTERNAL_SERVER_ERROR,
                   new CommonExceptionData("Internal error, check logs for 
details.")));
             }
-            sendResponse(invocation, response);
+            endResponse(invocation, response);
             finishInvocation(invocation, response);
           });
       return;
@@ -91,7 +91,7 @@ public abstract class ProducerInvocationFlow {
             response = Response.createFail(new 
InvocationException(Status.INTERNAL_SERVER_ERROR,
                 new CommonExceptionData("Internal error, check logs for 
details.")));
           }
-          sendResponse(invocation, response);
+          endResponse(invocation, response);
           finishInvocation(invocation, response);
         });
   }
@@ -102,5 +102,5 @@ public abstract class ProducerInvocationFlow {
 
   protected abstract Invocation sendCreateInvocationException(Throwable 
throwable);
 
-  protected abstract void sendResponse(Invocation invocation, Response 
response);
+  protected abstract void endResponse(Invocation invocation, Response 
response);
 }
diff --git 
a/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java
 
b/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java
index 7bba26f4d..e2e3b2c42 100644
--- 
a/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java
+++ 
b/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java
@@ -50,7 +50,7 @@ public class ProducerInvocationFlowTest {
     }
 
     @Override
-    protected void sendResponse(Invocation invocation, Response response) {
+    protected void endResponse(Invocation invocation, Response response) {
       sendInvocation = invocation;
     }
   }
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 2a0559bab..43e438c14 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
@@ -25,6 +25,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 
+import io.vertx.core.buffer.Buffer;
 import jakarta.servlet.ServletOutputStream;
 import jakarta.servlet.http.Cookie;
 import jakarta.servlet.http.Part;
@@ -88,6 +89,11 @@ public abstract class AbstractHttpServletResponse extends 
BodyBufferSupportImpl
     throw new Error("not supported method");
   }
 
+  @Override
+  public void endResponse() throws IOException {
+    throw new Error("not supported method");
+  }
+
   @Override
   public void resetBuffer() {
     throw new Error("not supported method");
@@ -222,4 +228,9 @@ public abstract class AbstractHttpServletResponse extends 
BodyBufferSupportImpl
   public CompletableFuture<Void> sendPart(Part body) {
     throw new Error("not supported method");
   }
+
+  @Override
+  public CompletableFuture<Void> sendBuffer(Buffer buffer) {
+    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 e6ec15dc0..a565ed779 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
@@ -21,6 +21,7 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.concurrent.CompletableFuture;
 
+import io.vertx.core.buffer.Buffer;
 import io.vertx.core.http.HttpHeaders;
 import jakarta.servlet.ServletOutputStream;
 import jakarta.servlet.http.HttpServletResponse;
@@ -36,10 +37,14 @@ public interface HttpServletResponseEx extends 
HttpServletResponse, BodyBufferSu
 
   CompletableFuture<Void> sendPart(Part body);
 
+  CompletableFuture<Void> sendBuffer(Buffer buffer);
+
   default void setChunked(boolean chunked) {
     setHeader(HttpHeaders.TRANSFER_ENCODING.toString(), 
HttpHeaders.CHUNKED.toString());
   }
 
+  void endResponse() throws IOException;
+
   @Override
   default ServletOutputStream getOutputStream() throws IOException {
     throw new IOException("Not allowed");
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 430f2ace8..38db248ef 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
@@ -82,11 +82,7 @@ public class StandardHttpServletResponseEx extends 
HttpServletResponseWrapper im
   }
 
   @Override
-  public void flushBuffer() throws IOException {
-    byte[] bytes = getBodyBytes();
-    if (bytes != null) {
-      getOutputStream().write(bytes, 0, getBodyBytesLength());
-    }
+  public void endResponse() throws IOException {
     super.flushBuffer();
   }
 
@@ -121,4 +117,16 @@ public class StandardHttpServletResponseEx extends 
HttpServletResponseWrapper im
     Context context = Vertx.currentContext();
     return new PumpFromPart(context, part).toOutputStream(outputStream, false);
   }
+
+  @Override
+  public CompletableFuture<Void> sendBuffer(Buffer buffer) {
+    CompletableFuture<Void> future = new CompletableFuture<>();
+    try {
+      getOutputStream().write(buffer.getBytes(), 0, buffer.length());
+      future.complete(null);
+    } catch (IOException e) {
+      future.completeExceptionally(e);
+    }
+    return future;
+  }
 }
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 7e28e8bb7..bd4f45b5e 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
@@ -26,6 +26,7 @@ import 
org.apache.servicecomb.foundation.vertx.stream.PumpFromPart;
 
 import io.vertx.core.Context;
 import io.vertx.core.Vertx;
+import io.vertx.core.buffer.Buffer;
 import io.vertx.core.http.HttpServerResponse;
 import jakarta.servlet.http.Part;
 import jakarta.ws.rs.core.HttpHeaders;
@@ -50,6 +51,11 @@ public class VertxServerResponseToHttpServletResponse 
extends AbstractHttpServle
     serverResponse.headers().set(HttpHeaders.CONTENT_TYPE, type);
   }
 
+  @Override
+  public void setContentLength(int len) {
+    serverResponse.headers().set(HttpHeaders.CONTENT_LENGTH, 
String.valueOf(len));
+  }
+
 
   @Override
   public void setStatus(int sc) {
@@ -100,7 +106,7 @@ public class VertxServerResponseToHttpServletResponse 
extends AbstractHttpServle
   }
 
   @Override
-  public void flushBuffer() {
+  public void endResponse() {
     if (context == Vertx.currentContext()) {
       internalFlushBuffer();
       return;
@@ -110,12 +116,7 @@ public class VertxServerResponseToHttpServletResponse 
extends AbstractHttpServle
   }
 
   public void internalFlushBuffer() {
-    if (bodyBuffer == null) {
-      serverResponse.end();
-      return;
-    }
-
-    serverResponse.end(bodyBuffer);
+    serverResponse.end();
   }
 
   @Override
@@ -127,6 +128,19 @@ public class VertxServerResponseToHttpServletResponse 
extends AbstractHttpServle
     return new PumpFromPart(context, part).toWriteStream(serverResponse, null);
   }
 
+  @Override
+  public CompletableFuture<Void> sendBuffer(Buffer buffer) {
+    CompletableFuture<Void> future = new CompletableFuture<>();
+    serverResponse.write(buffer).onComplete(result -> {
+      if (result.failed()) {
+        future.completeExceptionally(result.cause());
+      } else {
+        future.complete(null);
+      }
+    });
+    return future;
+  }
+
   @Override
   public void setChunked(boolean chunked) {
     serverResponse.setChunked(chunked);
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 c672fb288..289d5b152 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
@@ -103,7 +103,7 @@ public class TestStandardHttpServletResponseEx {
     Buffer body = Buffer.buffer().appendString("body");
     responseEx.setBodyBuffer(body);
     responseEx.flushBuffer();
-    Assertions.assertEquals("body", buffer.toString());
+    Assertions.assertEquals(0, buffer.length());
   }
 
   @Test
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 66c675475..1ab6d81bc 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
@@ -248,7 +248,7 @@ public class TestVertxServerResponseToHttpServletResponse {
 
   @Test
   public void flushBuffer_sameContext() throws IOException {
-    response.flushBuffer();
+    response.endResponse();
 
     Assertions.assertFalse(runOnContextInvoked);
   }
@@ -261,7 +261,7 @@ public class TestVertxServerResponseToHttpServletResponse {
         result = null;
       }
     };
-    response.flushBuffer();
+    response.endResponse();
 
     Assertions.assertTrue(runOnContextInvoked);
   }
@@ -278,7 +278,7 @@ public class TestVertxServerResponseToHttpServletResponse {
     response.setBodyBuffer(Buffer.buffer());
     response.internalFlushBuffer();
 
-    Assertions.assertTrue(flushWithBody);
+    Assertions.assertFalse(flushWithBody);
   }
 
   @Test
diff --git 
a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayProducerInvocationFlow.java
 
b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayProducerInvocationFlow.java
index f973f9e1c..3f38fd760 100644
--- 
a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayProducerInvocationFlow.java
+++ 
b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayProducerInvocationFlow.java
@@ -56,7 +56,7 @@ public class HighwayProducerInvocationFlow extends 
ProducerInvocationFlow {
   }
 
   @Override
-  protected void sendResponse(Invocation invocation, Response response) {
+  protected void endResponse(Invocation invocation, Response response) {
     HighwayTransportContext transportContext = 
invocation.getTransportContext();
     connection.write(transportContext.getResponseBuffer());
   }

Reply via email to