use of javax.servlet.AsyncContext in project jetty.project by eclipse.
the class DoSFilter method doFilter.
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
if (!isEnabled()) {
filterChain.doFilter(request, response);
return;
}
// Look for the rate tracker for this request.
RateTracker tracker = (RateTracker) request.getAttribute(__TRACKER);
if (tracker == null) {
// This is the first time we have seen this request.
if (LOG.isDebugEnabled())
LOG.debug("Filtering {}", request);
// Get a rate tracker associated with this request, and record one hit.
tracker = getRateTracker(request);
// Calculate the rate and check it is over the allowed limit
final boolean overRateLimit = tracker.isRateExceeded(System.currentTimeMillis());
// Pass it through if we are not currently over the rate limit.
if (!overRateLimit) {
if (LOG.isDebugEnabled())
LOG.debug("Allowing {}", request);
doFilterChain(filterChain, request, response);
return;
}
// We are over the limit.
// So either reject it, delay it or throttle it.
long delayMs = getDelayMs();
boolean insertHeaders = isInsertHeaders();
switch((int) delayMs) {
case -1:
{
// Reject this request.
LOG.warn("DOS ALERT: Request rejected ip={}, session={}, user={}", request.getRemoteAddr(), request.getRequestedSessionId(), request.getUserPrincipal());
if (insertHeaders)
response.addHeader("DoSFilter", "unavailable");
response.sendError(getTooManyCode());
return;
}
case 0:
{
// Fall through to throttle the request.
LOG.warn("DOS ALERT: Request throttled ip={}, session={}, user={}", request.getRemoteAddr(), request.getRequestedSessionId(), request.getUserPrincipal());
request.setAttribute(__TRACKER, tracker);
break;
}
default:
{
// Insert a delay before throttling the request,
// using the suspend+timeout mechanism of AsyncContext.
LOG.warn("DOS ALERT: Request delayed={}ms, ip={}, session={}, user={}", delayMs, request.getRemoteAddr(), request.getRequestedSessionId(), request.getUserPrincipal());
if (insertHeaders)
response.addHeader("DoSFilter", "delayed");
request.setAttribute(__TRACKER, tracker);
AsyncContext asyncContext = request.startAsync();
if (delayMs > 0)
asyncContext.setTimeout(delayMs);
asyncContext.addListener(new DoSTimeoutAsyncListener());
return;
}
}
}
if (LOG.isDebugEnabled())
LOG.debug("Throttling {}", request);
// Throttle the request.
boolean accepted = false;
try {
// Check if we can afford to accept another request at this time.
accepted = _passes.tryAcquire(getMaxWaitMs(), TimeUnit.MILLISECONDS);
if (!accepted) {
// We were not accepted, so either we suspend to wait,
// or if we were woken up we insist or we fail.
Boolean throttled = (Boolean) request.getAttribute(__THROTTLED);
long throttleMs = getThrottleMs();
if (throttled != Boolean.TRUE && throttleMs > 0) {
int priority = getPriority(request, tracker);
request.setAttribute(__THROTTLED, Boolean.TRUE);
if (isInsertHeaders())
response.addHeader("DoSFilter", "throttled");
AsyncContext asyncContext = request.startAsync();
request.setAttribute(_suspended, Boolean.TRUE);
if (throttleMs > 0)
asyncContext.setTimeout(throttleMs);
asyncContext.addListener(_listeners[priority]);
_queues[priority].add(asyncContext);
if (LOG.isDebugEnabled())
LOG.debug("Throttled {}, {}ms", request, throttleMs);
return;
}
Boolean resumed = (Boolean) request.getAttribute(_resumed);
if (resumed == Boolean.TRUE) {
// We were resumed, we wait for the next pass.
_passes.acquire();
accepted = true;
}
}
// If we were accepted (either immediately or after throttle)...
if (accepted) {
// ...call the chain.
if (LOG.isDebugEnabled())
LOG.debug("Allowing {}", request);
doFilterChain(filterChain, request, response);
} else {
// ...otherwise fail the request.
if (LOG.isDebugEnabled())
LOG.debug("Rejecting {}", request);
if (isInsertHeaders())
response.addHeader("DoSFilter", "unavailable");
response.sendError(getTooManyCode());
}
} catch (InterruptedException e) {
LOG.ignore(e);
response.sendError(getTooManyCode());
} finally {
if (accepted) {
try {
// Wake up the next highest priority request.
for (int p = _queues.length - 1; p >= 0; --p) {
AsyncContext asyncContext = _queues[p].poll();
if (asyncContext != null) {
ServletRequest candidate = asyncContext.getRequest();
Boolean suspended = (Boolean) candidate.getAttribute(_suspended);
if (suspended == Boolean.TRUE) {
if (LOG.isDebugEnabled())
LOG.debug("Resuming {}", request);
candidate.setAttribute(_resumed, Boolean.TRUE);
asyncContext.dispatch();
break;
}
}
}
} finally {
_passes.release();
}
}
}
}
use of javax.servlet.AsyncContext in project jetty.project by eclipse.
the class EventSourceServlet method doGet.
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
@SuppressWarnings("unchecked") Enumeration<String> acceptValues = request.getHeaders("Accept");
while (acceptValues.hasMoreElements()) {
String accept = acceptValues.nextElement();
if (accept.equals("text/event-stream")) {
EventSource eventSource = newEventSource(request);
if (eventSource == null) {
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
} else {
respond(request, response);
AsyncContext async = request.startAsync();
// Infinite timeout because the continuation is never resumed,
// but only completed on close
async.setTimeout(0);
EventSourceEmitter emitter = new EventSourceEmitter(eventSource, async);
emitter.scheduleHeartBeat();
open(eventSource, emitter);
}
return;
}
}
super.doGet(request, response);
}
use of javax.servlet.AsyncContext in project jetty.project by eclipse.
the class DataRateLimitedServlet method doGet.
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Get the path of the static resource to serve.
String info = request.getPathInfo();
// We don't handle directories
if (info.endsWith("/")) {
response.sendError(503, "directories not supported");
return;
}
// Set the mime type of the response
String content_type = getServletContext().getMimeType(info);
response.setContentType(content_type == null ? "application/x-data" : content_type);
// Look for a matching file path
String path = request.getPathTranslated();
// If we have a file path and this is a jetty response, we can use the JettyStream impl
ServletOutputStream out = response.getOutputStream();
if (path != null && out instanceof HttpOutput) {
// If the file exists
File file = new File(path);
if (file.exists() && file.canRead()) {
// Set the content length
response.setContentLengthLong(file.length());
// Look for a file mapped buffer in the cache
ByteBuffer mapped = cache.get(path);
// Handle cache miss
if (mapped == null) {
// TODO implement LRU cache flush
try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
ByteBuffer buf = raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length());
mapped = cache.putIfAbsent(path, buf);
if (mapped == null)
mapped = buf;
}
}
// start async request handling
AsyncContext async = request.startAsync();
// Set a JettyStream as the write listener to write the content asynchronously.
out.setWriteListener(new JettyDataStream(mapped, async, out));
return;
}
}
// Jetty API was not used, so lets try the standards approach
// Can we find the content as an input stream
InputStream content = getServletContext().getResourceAsStream(info);
if (content == null) {
response.sendError(404);
return;
}
// Set a StandardStream as he write listener to write the content asynchronously
out.setWriteListener(new StandardDataStream(content, request.startAsync(), out));
}
use of javax.servlet.AsyncContext in project jetty.project by eclipse.
the class ServerTimeoutsTest method testAsyncWriteIdleTimeoutFires.
@Test
public void testAsyncWriteIdleTimeoutFires() throws Exception {
CountDownLatch handlerLatch = new CountDownLatch(1);
start(new AbstractHandler() {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
baseRequest.setHandled(true);
AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(0);
ServletOutputStream output = response.getOutputStream();
output.setWriteListener(new WriteListener() {
@Override
public void onWritePossible() throws IOException {
output.write(new byte[64 * 1024 * 1024]);
}
@Override
public void onError(Throwable failure) {
if (failure instanceof TimeoutException) {
asyncContext.complete();
handlerLatch.countDown();
}
}
});
}
});
long idleTimeout = 2500;
setServerIdleTimeout(idleTimeout);
BlockingQueue<Callback> callbacks = new LinkedBlockingQueue<>();
CountDownLatch resultLatch = new CountDownLatch(1);
client.newRequest(newURI()).onResponseContentAsync((response, content, callback) -> {
// Do not succeed the callback so the server will block writing.
callbacks.offer(callback);
}).send(result -> {
if (result.isFailed())
resultLatch.countDown();
});
// Async write should timeout.
Assert.assertTrue(handlerLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
// After the server stopped sending, consume on the client to read the early EOF.
while (true) {
Callback callback = callbacks.poll(1, TimeUnit.SECONDS);
if (callback == null)
break;
callback.succeeded();
}
Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
}
use of javax.servlet.AsyncContext in project jetty.project by eclipse.
the class ServerTimeoutsTest method testAsyncReadHttpIdleTimeoutOverridesIdleTimeout.
@Test
public void testAsyncReadHttpIdleTimeoutOverridesIdleTimeout() throws Exception {
long httpIdleTimeout = 2500;
long idleTimeout = 3 * httpIdleTimeout;
httpConfig.setIdleTimeout(httpIdleTimeout);
CountDownLatch handlerLatch = new CountDownLatch(1);
start(new AbstractHandler() {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
baseRequest.setHandled(true);
AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(0);
ServletInputStream input = request.getInputStream();
input.setReadListener(new ReadListener() {
@Override
public void onDataAvailable() throws IOException {
Assert.assertEquals(0, input.read());
Assert.assertFalse(input.isReady());
}
@Override
public void onAllDataRead() throws IOException {
}
@Override
public void onError(Throwable failure) {
if (failure instanceof TimeoutException) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
asyncContext.complete();
handlerLatch.countDown();
}
}
});
}
});
setServerIdleTimeout(idleTimeout);
DeferredContentProvider contentProvider = new DeferredContentProvider(ByteBuffer.allocate(1));
CountDownLatch resultLatch = new CountDownLatch(1);
client.POST(newURI()).content(contentProvider).send(result -> {
if (result.getResponse().getStatus() == HttpStatus.INTERNAL_SERVER_ERROR_500)
resultLatch.countDown();
});
// Async read should timeout.
Assert.assertTrue(handlerLatch.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
// Complete the request.
contentProvider.close();
Assert.assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
}
Aggregations