-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce caching of ResteasyUriInfo.InitData #4563
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,8 @@ | |
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
import javax.enterprise.inject.Instance; | ||
import javax.enterprise.inject.spi.CDI; | ||
|
@@ -35,20 +37,25 @@ public class VertxRequestHandler implements Handler<RoutingContext> { | |
protected final Vertx vertx; | ||
protected final RequestDispatcher dispatcher; | ||
protected final String rootPath; | ||
protected final int resteasyUriInfoCacheMaxSize; | ||
protected final BufferAllocator allocator; | ||
protected final BeanContainer beanContainer; | ||
protected final CurrentIdentityAssociation association; | ||
|
||
private final Map<String, ResteasyUriInfo.InitData> resteasyUriInfoInitDataMap = new ConcurrentHashMap<>(); | ||
|
||
public VertxRequestHandler(Vertx vertx, | ||
BeanContainer beanContainer, | ||
ResteasyDeployment deployment, | ||
String rootPath, | ||
int resteasyUriInfoCacheMaxSize, | ||
BufferAllocator allocator) { | ||
this.vertx = vertx; | ||
this.beanContainer = beanContainer; | ||
this.dispatcher = new RequestDispatcher((SynchronousDispatcher) deployment.getDispatcher(), | ||
deployment.getProviderFactory(), null); | ||
this.rootPath = rootPath; | ||
this.resteasyUriInfoCacheMaxSize = resteasyUriInfoCacheMaxSize; | ||
this.allocator = allocator; | ||
Instance<CurrentIdentityAssociation> association = CDI.current().select(CurrentIdentityAssociation.class); | ||
this.association = association.isResolvable() ? association.get() : null; | ||
|
@@ -90,7 +97,23 @@ private void dispatch(RoutingContext routingContext, InputStream is, VertxOutput | |
try { | ||
Context ctx = vertx.getOrCreateContext(); | ||
HttpServerRequest request = routingContext.request(); | ||
ResteasyUriInfo uriInfo = VertxUtil.extractUriInfo(request, rootPath); | ||
|
||
String uriString = VertxUtil.getUriString(request); | ||
ResteasyUriInfo.InitData resteasyUriInfoCachedInitData = null; | ||
boolean setInitDataUponSuccess = false; | ||
if (ResteasyUriInfo.InitData.canBeCached(uriString) && resteasyUriInfoCacheMaxSize > 0) { | ||
String cacheKey = ResteasyUriInfo.InitData.getCacheKey(uriString, rootPath); | ||
resteasyUriInfoCachedInitData = resteasyUriInfoInitDataMap.get(cacheKey); | ||
if (resteasyUriInfoCachedInitData == null) { | ||
setInitDataUponSuccess = true; | ||
} | ||
} | ||
if (resteasyUriInfoCachedInitData == null) { | ||
resteasyUriInfoCachedInitData = new ResteasyUriInfo.InitData(uriString, rootPath); | ||
} | ||
|
||
ResteasyUriInfo uriInfo = new ResteasyUriInfo(uriString, rootPath, resteasyUriInfoCachedInitData); | ||
|
||
ResteasyHttpHeaders headers = VertxUtil.extractHttpHeaders(request); | ||
HttpServerResponse response = request.response(); | ||
VertxHttpResponse vertxResponse = new VertxHttpResponse(request, dispatcher.getProviderFactory(), | ||
|
@@ -114,6 +137,19 @@ private void dispatch(RoutingContext routingContext, InputStream is, VertxOutput | |
if (!vertxRequest.getAsyncContext().isSuspended()) { | ||
try { | ||
vertxResponse.finish(); | ||
// the reason we only cache successful responses is to ensure that a torrent of erroneous URLs | ||
// doesn't fill up the cache and cause a DoS | ||
if (setInitDataUponSuccess && vertxResponse.getStatus() >= 200 && vertxResponse.getStatus() <= 300) { | ||
// we don't want the cache to grow unbounded since values path params could potentially be infinite | ||
// and if left unchecked would take up all memory if the application is up for long enough | ||
if (resteasyUriInfoInitDataMap.size() > resteasyUriInfoCacheMaxSize) { | ||
resteasyUriInfoInitDataMap.clear(); // this is super lame and should probably be revisited | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes it's dangerous. See my other comments on the design discussion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we have the RESTEasy part of the PR in which puts in the necessary hooks, we can absolutely improve this based on what you mention about the existence or not of Caffeine. |
||
} | ||
// this could potentially be written multiple times for initial requests but it doesn't matter | ||
// since the data is always the same | ||
resteasyUriInfoInitDataMap.put(ResteasyUriInfo.InitData.getCacheKey(uriString, rootPath), | ||
resteasyUriInfoCachedInitData); | ||
} | ||
} catch (IOException e) { | ||
log.error("Unexpected failure", e); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should cache unsuccesfull responses as well, especially if we go for having a proper cache.
DDoS are caused by the system being overwhelmed; hitting the "bad response cache" would be the best way to avoid it as the cache would help fighting load.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree 💯%. If we have a proper cache than all this nastiness can go away.