use of javax.servlet.AsyncContext in project jetty.project by eclipse.
the class AsyncIOServletTest method testWriteListenerFromOtherThread.
@Test
public void testWriteListenerFromOtherThread() throws Exception {
start(new HttpServlet() {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(0);
request.getInputStream().setReadListener(new Listener(asyncContext));
}
});
int cores = 4;
int iterations = 10;
CountDownLatch latch = new CountDownLatch(cores * iterations);
Deque<Throwable> failures = new LinkedBlockingDeque<>();
for (int i = 0; i < cores; ++i) {
client.getExecutor().execute(() -> {
for (int j = 0; j < iterations; ++j) {
try {
ContentResponse response = client.newRequest(newURI()).method(HttpMethod.POST).path(servletPath).content(new InputStreamContentProvider(new ByteArrayInputStream(new byte[16 * 1024]) {
@Override
public int read(byte[] b, int off, int len) {
sleep(5);
return super.read(b, off, Math.min(len, 4242));
}
})).send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
latch.countDown();
} catch (Throwable x) {
failures.offer(x);
}
}
});
}
Assert.assertTrue(latch.await(30, TimeUnit.SECONDS));
Assert.assertTrue(failures.isEmpty());
}
use of javax.servlet.AsyncContext in project jetty.project by eclipse.
the class HttpClientStreamTest method testInputStreamResponseListenerBufferedRead.
@Test
public void testInputStreamResponseListenerBufferedRead() throws Exception {
AtomicReference<AsyncContext> asyncContextRef = new AtomicReference<>();
CountDownLatch latch = new CountDownLatch(1);
start(new AbstractHandler() {
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
baseRequest.setHandled(true);
asyncContextRef.set(request.startAsync());
latch.countDown();
}
});
InputStreamResponseListener listener = new InputStreamResponseListener();
client.newRequest("localhost", connector.getLocalPort()).scheme(getScheme()).timeout(5, TimeUnit.SECONDS).send(listener);
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
AsyncContext asyncContext = asyncContextRef.get();
Assert.assertNotNull(asyncContext);
Random random = new Random();
byte[] chunk = new byte[64];
random.nextBytes(chunk);
ServletOutputStream output = asyncContext.getResponse().getOutputStream();
output.write(chunk);
output.flush();
// Use a buffer larger than the data
// written to test that the read returns.
byte[] buffer = new byte[2 * chunk.length];
InputStream stream = listener.getInputStream();
int totalRead = 0;
while (totalRead < chunk.length) {
int read = stream.read(buffer);
Assert.assertTrue(read > 0);
totalRead += read;
}
asyncContext.complete();
Response response = listener.get(5, TimeUnit.SECONDS);
Assert.assertEquals(200, response.getStatus());
}
use of javax.servlet.AsyncContext in project jetty.project by eclipse.
the class HttpClientStreamTest method testInputStreamResponseListenerClosedBeforeContent.
@Test(expected = AsynchronousCloseException.class)
public void testInputStreamResponseListenerClosedBeforeContent() throws Exception {
AtomicReference<AsyncContext> contextRef = new AtomicReference<>();
start(new AbstractHandler() {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
baseRequest.setHandled(true);
contextRef.set(request.startAsync());
response.flushBuffer();
}
});
CountDownLatch latch = new CountDownLatch(1);
InputStreamResponseListener listener = new InputStreamResponseListener() {
@Override
public void onContent(Response response, ByteBuffer content, Callback callback) {
super.onContent(response, content, new Callback() {
@Override
public void failed(Throwable x) {
latch.countDown();
callback.failed(x);
}
});
}
};
client.newRequest("localhost", connector.getLocalPort()).scheme(getScheme()).send(listener);
Response response = listener.get(5, TimeUnit.SECONDS);
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
InputStream input = listener.getInputStream();
input.close();
AsyncContext asyncContext = contextRef.get();
asyncContext.getResponse().getOutputStream().write(new byte[1024]);
asyncContext.complete();
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
// Throws
input.read();
}
use of javax.servlet.AsyncContext in project jetty.project by eclipse.
the class AsyncIOServletTest method testIsReadyAtEOF.
@Test
public void testIsReadyAtEOF() throws Exception {
String text = "TEST\n";
byte[] data = text.getBytes(StandardCharsets.UTF_8);
start(new HttpServlet() {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
assertScope();
response.flushBuffer();
AsyncContext async = request.startAsync();
ServletInputStream input = request.getInputStream();
ServletOutputStream output = response.getOutputStream();
input.setReadListener(new ReadListener() {
transient int _i = 0;
transient boolean _minusOne = false;
transient boolean _finished = false;
@Override
public void onDataAvailable() throws IOException {
assertScope();
while (input.isReady() && !input.isFinished()) {
int b = input.read();
if (b == -1)
_minusOne = true;
else if (data[_i++] != b)
throw new IllegalStateException();
}
if (input.isFinished())
_finished = true;
}
@Override
public void onAllDataRead() throws IOException {
assertScope();
output.write(String.format("i=%d eof=%b finished=%b", _i, _minusOne, _finished).getBytes(StandardCharsets.UTF_8));
async.complete();
}
@Override
public void onError(Throwable t) {
assertScope();
t.printStackTrace();
async.complete();
}
});
}
});
ContentResponse response = client.newRequest(newURI()).method(HttpMethod.POST).path(servletPath).header(HttpHeader.CONNECTION, "close").content(new StringContentProvider(text)).timeout(5, TimeUnit.SECONDS).send();
String responseContent = response.getContentAsString();
assertThat(responseContent, containsString("i=" + data.length + " eof=true finished=true"));
}
use of javax.servlet.AsyncContext in project jetty.project by eclipse.
the class ResourceService method sendData.
/* ------------------------------------------------------------ */
protected boolean sendData(HttpServletRequest request, HttpServletResponse response, boolean include, final HttpContent content, Enumeration<String> reqRanges) throws IOException {
final long content_length = content.getContentLengthValue();
// Get the output stream (or writer)
OutputStream out = null;
boolean written;
try {
out = response.getOutputStream();
// has something already written to the response?
written = out instanceof HttpOutput ? ((HttpOutput) out).isWritten() : true;
} catch (IllegalStateException e) {
out = new WriterOutputStream(response.getWriter());
// there may be data in writer buffer, so assume written
written = true;
}
if (LOG.isDebugEnabled())
LOG.debug(String.format("sendData content=%s out=%s async=%b", content, out, request.isAsyncSupported()));
if (reqRanges == null || !reqRanges.hasMoreElements() || content_length < 0) {
// if there were no ranges, send entire entity
if (include) {
// write without headers
content.getResource().writeTo(out, 0, content_length);
} else // else if we can't do a bypass write because of wrapping
if (written || !(out instanceof HttpOutput)) {
// write normally
putHeaders(response, content, written ? -1 : 0);
ByteBuffer buffer = content.getIndirectBuffer();
if (buffer != null)
BufferUtil.writeTo(buffer, out);
else
content.getResource().writeTo(out, 0, content_length);
} else // else do a bypass write
{
// write the headers
putHeaders(response, content, 0);
// write the content asynchronously if supported
if (request.isAsyncSupported() && content.getContentLengthValue() > response.getBufferSize()) {
final AsyncContext context = request.startAsync();
context.setTimeout(0);
((HttpOutput) out).sendContent(content, new Callback() {
@Override
public void succeeded() {
context.complete();
content.release();
}
@Override
public void failed(Throwable x) {
if (x instanceof IOException)
LOG.debug(x);
else
LOG.warn(x);
context.complete();
content.release();
}
@Override
public String toString() {
return String.format("ResourceService@%x$CB", ResourceService.this.hashCode());
}
});
return false;
}
// otherwise write content blocking
((HttpOutput) out).sendContent(content);
}
} else {
// Parse the satisfiable ranges
List<InclusiveByteRange> ranges = InclusiveByteRange.satisfiableRanges(reqRanges, content_length);
// if there are no satisfiable ranges, send 416 response
if (ranges == null || ranges.size() == 0) {
putHeaders(response, content, 0);
response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
response.setHeader(HttpHeader.CONTENT_RANGE.asString(), InclusiveByteRange.to416HeaderRangeString(content_length));
content.getResource().writeTo(out, 0, content_length);
return true;
}
// since were here now), send that range with a 216 response
if (ranges.size() == 1) {
InclusiveByteRange singleSatisfiableRange = ranges.get(0);
long singleLength = singleSatisfiableRange.getSize(content_length);
putHeaders(response, content, singleLength);
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
if (!response.containsHeader(HttpHeader.DATE.asString()))
response.addDateHeader(HttpHeader.DATE.asString(), System.currentTimeMillis());
response.setHeader(HttpHeader.CONTENT_RANGE.asString(), singleSatisfiableRange.toHeaderRangeString(content_length));
content.getResource().writeTo(out, singleSatisfiableRange.getFirst(content_length), singleLength);
return true;
}
// multiple non-overlapping valid ranges cause a multipart
// 216 response which does not require an overall
// content-length header
//
putHeaders(response, content, -1);
String mimetype = (content == null ? null : content.getContentTypeValue());
if (mimetype == null)
LOG.warn("Unknown mimetype for " + request.getRequestURI());
MultiPartOutputStream multi = new MultiPartOutputStream(out);
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
if (!response.containsHeader(HttpHeader.DATE.asString()))
response.addDateHeader(HttpHeader.DATE.asString(), System.currentTimeMillis());
// If the request has a "Request-Range" header then we need to
// send an old style multipart/x-byteranges Content-Type. This
// keeps Netscape and acrobat happy. This is what Apache does.
String ctp;
if (request.getHeader(HttpHeader.REQUEST_RANGE.asString()) != null)
ctp = "multipart/x-byteranges; boundary=";
else
ctp = "multipart/byteranges; boundary=";
response.setContentType(ctp + multi.getBoundary());
InputStream in = content.getResource().getInputStream();
long pos = 0;
// calculate the content-length
int length = 0;
String[] header = new String[ranges.size()];
for (int i = 0; i < ranges.size(); i++) {
InclusiveByteRange ibr = ranges.get(i);
header[i] = ibr.toHeaderRangeString(content_length);
length += ((i > 0) ? 2 : 0) + 2 + multi.getBoundary().length() + 2 + (mimetype == null ? 0 : HttpHeader.CONTENT_TYPE.asString().length() + 2 + mimetype.length()) + 2 + HttpHeader.CONTENT_RANGE.asString().length() + 2 + header[i].length() + 2 + 2 + (ibr.getLast(content_length) - ibr.getFirst(content_length)) + 1;
}
length += 2 + 2 + multi.getBoundary().length() + 2 + 2;
response.setContentLength(length);
for (int i = 0; i < ranges.size(); i++) {
InclusiveByteRange ibr = ranges.get(i);
multi.startPart(mimetype, new String[] { HttpHeader.CONTENT_RANGE + ": " + header[i] });
long start = ibr.getFirst(content_length);
long size = ibr.getSize(content_length);
if (in != null) {
// Handle non cached resource
if (start < pos) {
in.close();
in = content.getResource().getInputStream();
pos = 0;
}
if (pos < start) {
in.skip(start - pos);
pos = start;
}
IO.copy(in, multi, size);
pos += size;
} else
// Handle cached resource
content.getResource().writeTo(multi, start, size);
}
if (in != null)
in.close();
multi.close();
}
return true;
}
Aggregations