|
26 | 26 | import io.jooby.Value; |
27 | 27 | import io.jooby.ValueNode; |
28 | 28 | import io.jooby.WebSocket; |
| 29 | +import org.eclipse.jetty.http.HttpContent; |
29 | 30 | import org.eclipse.jetty.http.HttpFields; |
30 | 31 | import org.eclipse.jetty.http.HttpHeader; |
31 | 32 | import org.eclipse.jetty.http.HttpHeaderValue; |
|
34 | 35 | import org.eclipse.jetty.server.HttpOutput; |
35 | 36 | import org.eclipse.jetty.server.Request; |
36 | 37 | import org.eclipse.jetty.server.Response; |
| 38 | +import org.eclipse.jetty.util.BufferUtil; |
37 | 39 | import org.eclipse.jetty.util.Callback; |
38 | 40 | import org.eclipse.jetty.util.MultiMap; |
39 | 41 | import org.eclipse.jetty.websocket.api.WebSocketBehavior; |
|
43 | 45 |
|
44 | 46 | import javax.annotation.Nonnull; |
45 | 47 | import javax.annotation.Nullable; |
| 48 | +import javax.servlet.AsyncContext; |
46 | 49 | import javax.servlet.MultipartConfigElement; |
47 | 50 | import javax.servlet.ServletException; |
| 51 | +import javax.servlet.ServletOutputStream; |
| 52 | +import javax.servlet.WriteListener; |
48 | 53 | import javax.servlet.http.Part; |
49 | 54 | import java.io.FileInputStream; |
50 | 55 | import java.io.IOException; |
|
65 | 70 | import java.util.List; |
66 | 71 | import java.util.Map; |
67 | 72 | import java.util.concurrent.Executor; |
| 73 | +import java.util.concurrent.atomic.AtomicBoolean; |
68 | 74 |
|
69 | 75 | import static org.eclipse.jetty.http.HttpHeader.CONTENT_TYPE; |
70 | 76 | import static org.eclipse.jetty.http.HttpHeader.SET_COOKIE; |
@@ -380,6 +386,17 @@ public Context setResponseType(@Nonnull MediaType contentType, @Nullable Charset |
380 | 386 | return this; |
381 | 387 | } |
382 | 388 |
|
| 389 | + @Nonnull @Override public Context send(@Nonnull ByteBuffer[] data) { |
| 390 | + if (response.getContentLength() <= 0) { |
| 391 | + setResponseLength(BufferUtil.remaining(data)); |
| 392 | + } |
| 393 | + ifStartAsync(); |
| 394 | + HttpOutput out = response.getHttpOutput(); |
| 395 | + out.setWriteListener(writeListener(request.getAsyncContext(), out, data)); |
| 396 | + responseStarted = true; |
| 397 | + return this; |
| 398 | + } |
| 399 | + |
383 | 400 | @Nonnull @Override public Context send(@Nonnull byte[] data) { |
384 | 401 | return send(ByteBuffer.wrap(data)); |
385 | 402 | } |
@@ -554,4 +571,25 @@ private static void formParam(Request request, Formdata form) { |
554 | 571 | } |
555 | 572 | } |
556 | 573 |
|
| 574 | + private static WriteListener writeListener(AsyncContext async, HttpOutput out, |
| 575 | + ByteBuffer[] data) { |
| 576 | + return new WriteListener() { |
| 577 | + int i = 0; |
| 578 | + |
| 579 | + @Override public void onWritePossible() throws IOException { |
| 580 | + while (out.isReady()) { |
| 581 | + if (i < data.length) { |
| 582 | + out.write(data[i++]); |
| 583 | + } else { |
| 584 | + async.complete(); |
| 585 | + return; |
| 586 | + } |
| 587 | + } |
| 588 | + } |
| 589 | + |
| 590 | + @Override public void onError(Throwable x) { |
| 591 | + async.complete(); |
| 592 | + } |
| 593 | + }; |
| 594 | + } |
557 | 595 | } |
0 commit comments