Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
{@link ExecutionMode#V4_EMULATION_ENGINE}: request is handled by an instance of {@link ApiReactor}
*
{@link ExecutionMode#V3}: request is handled by an instance of {@link ReactorHandler}
*
*
* @author Jeoffrey HAEYAERT (jeoffrey.haeyaert at graviteesource.com)
* @author GraviteeSource Team
*/
public class DefaultHttpRequestDispatcher implements HttpRequestDispatcher {
private static final String ATTR_INTERNAL_VERTX_TIMER_ID = ContextAttributes.ATTR_PREFIX + "vertx-timer-id";
public static final String ATTR_ENTRYPOINT = ContextAttributes.ATTR_PREFIX + "entrypoint";
private final Logger log = LoggerFactory.getLogger(DefaultHttpRequestDispatcher.class);
private final GatewayConfiguration gatewayConfiguration;
private final HttpAcceptorResolver httpAcceptorResolver;
private final IdGenerator idGenerator;
private final RequestProcessorChainFactory requestProcessorChainFactory;
private final ResponseProcessorChainFactory responseProcessorChainFactory;
private final DefaultPlatformProcessorChainFactory platformProcessorChainFactory;
private final NotFoundProcessorChainFactory notFoundProcessorChainFactory;
private final RequestTimeoutConfiguration requestTimeoutConfiguration;
private final RequestClientAuthConfiguration requestClientAuthConfiguration;
private final Vertx vertx;
private final List processorChainHooks;
private final ComponentProvider globalComponentProvider;
public DefaultHttpRequestDispatcher(
GatewayConfiguration gatewayConfiguration,
HttpAcceptorResolver httpAcceptorResolver,
IdGenerator idGenerator,
ComponentProvider globalComponentProvider,
RequestProcessorChainFactory requestProcessorChainFactory,
ResponseProcessorChainFactory responseProcessorChainFactory,
DefaultPlatformProcessorChainFactory platformProcessorChainFactory,
NotFoundProcessorChainFactory notFoundProcessorChainFactory,
boolean tracingEnabled,
RequestTimeoutConfiguration requestTimeoutConfiguration,
RequestClientAuthConfiguration requestClientAuthConfiguration,
Vertx vertx
) {
this.gatewayConfiguration = gatewayConfiguration;
this.httpAcceptorResolver = httpAcceptorResolver;
this.idGenerator = idGenerator;
this.globalComponentProvider = globalComponentProvider;
this.requestProcessorChainFactory = requestProcessorChainFactory;
this.responseProcessorChainFactory = responseProcessorChainFactory;
this.platformProcessorChainFactory = platformProcessorChainFactory;
this.notFoundProcessorChainFactory = notFoundProcessorChainFactory;
this.requestTimeoutConfiguration = requestTimeoutConfiguration;
this.requestClientAuthConfiguration = requestClientAuthConfiguration;
this.vertx = vertx;
this.processorChainHooks = new ArrayList<>();
if (tracingEnabled) {
processorChainHooks.add(new TracingHook("processor-chain"));
}
}
/**
* {@inheritDoc}
* Each incoming request is dispatched respecting the following step order:
*
*
Api resolution: resolves the {@link ReactorHandler} that is able to handle the request based on the request host path.
*
Api request: invokes the V3 or V4 emulation engine {@link ReactorHandler} to handle the api request. Eventually, handle not found if no handler has been resolved.
*
Platform processors: in case of V3 {@link ReactorHandler} pre and post platform processor are executed
*
*/
@Override
public Completable dispatch(HttpServerRequest httpServerRequest, String serverId) {
log.debug("Dispatching request on host {} and path {}", httpServerRequest.host(), httpServerRequest.path());
final HttpAcceptor httpAcceptor = httpAcceptorResolver.resolve(httpServerRequest.host(), httpServerRequest.path(), serverId);
if (httpAcceptor == null || httpAcceptor.reactor() == null) {
MutableExecutionContext mutableCtx = prepareExecutionContext(httpServerRequest);
ProcessorChain preProcessorChain = platformProcessorChainFactory.preProcessorChain();
return HookHelper
.hook(
() -> preProcessorChain.execute(mutableCtx, ExecutionPhase.REQUEST),
preProcessorChain.getId(),
processorChainHooks,
mutableCtx,
ExecutionPhase.REQUEST
)
.andThen(handleNotFound(mutableCtx));
} else if (httpAcceptor.reactor() instanceof ApiReactor) {
MutableExecutionContext mutableCtx = prepareExecutionContext(httpServerRequest);
mutableCtx.request().contextPath(httpAcceptor.path());
final ApiReactor apiReactor = (ApiReactor) httpAcceptor.reactor();
mutableCtx.setInternalAttribute(InternalContextAttributes.ATTR_INTERNAL_REACTABLE_API, apiReactor.api());
ProcessorChain preProcessorChain = platformProcessorChainFactory.preProcessorChain();
return HookHelper
.hook(
() -> preProcessorChain.execute(mutableCtx, ExecutionPhase.REQUEST),
preProcessorChain.getId(),
processorChainHooks,
mutableCtx,
ExecutionPhase.REQUEST
)
.andThen(Completable.defer(() -> apiReactor.handle(mutableCtx)))
.doFinally(() -> {
// Post action are dissociated from the main execution once the request has been handled and cover all the cases (error, success, cancel).
ProcessorChain postProcessorChain = platformProcessorChainFactory.postProcessorChain();
HookHelper
.hook(
() -> postProcessorChain.execute(mutableCtx, ExecutionPhase.RESPONSE),
postProcessorChain.getId(),
processorChainHooks,
mutableCtx,
ExecutionPhase.RESPONSE
)
.subscribeOn(Schedulers.computation())
.onErrorComplete()
.subscribe();
});
}
// V3 execution mode.
return handleV3Request(httpServerRequest, httpAcceptor);
}
private MutableExecutionContext prepareExecutionContext(final HttpServerRequest httpServerRequest) {
VertxHttpServerRequest request = new VertxHttpServerRequest(
httpServerRequest,
idGenerator,
new VertxHttpServerRequest.VertxHttpServerRequestOptions(requestClientAuthConfiguration.getHeaderName())
);
MutableExecutionContext ctx = createExecutionContext(request);
ctx.componentProvider(globalComponentProvider);
ctx.setInternalAttribute(ATTR_INTERNAL_LISTENER_TYPE, ListenerType.HTTP);
return ctx;
}
protected DefaultExecutionContext createExecutionContext(VertxHttpServerRequest request) {
return new DefaultExecutionContext(request, request.response());
}
private Completable handleNotFound(final MutableExecutionContext ctx) {
ctx.request().contextPath("/");
ProcessorChain processorChain = notFoundProcessorChainFactory.processorChain();
return HookHelper.hook(
() -> processorChain.execute(ctx, ExecutionPhase.RESPONSE),
processorChain.getId(),
processorChainHooks,
ctx,
ExecutionPhase.RESPONSE
);
}
private Completable handleV3Request(final HttpServerRequest httpServerRequest, final HttpAcceptor handlerEntrypoint) {
final ReactorHandler reactorHandler = handlerEntrypoint.reactor();
io.gravitee.gateway.http.vertx.VertxHttpServerRequest request = createV3Request(httpServerRequest, idGenerator);
// Prepare invocation execution context.
SimpleExecutionContext simpleExecutionContext = createV3ExecutionContext(httpServerRequest, request);
// Required by the v3 execution mode.
simpleExecutionContext.setAttribute(ATTR_ENTRYPOINT, handlerEntrypoint);
// Set gateway tenants and zones in request metrics.
prepareV3Metrics(request.metrics());
// Prepare handler chain and catch the end of the v3 request handling to complete the reactive chain.
return Completable.create(emitter -> {
Handler endHandler = endRequestHandler(emitter, httpServerRequest);
requestProcessorChainFactory
.create()
.handler(ctx -> {
reactorHandler.handle(
ctx,
executionContext ->
executionContext.response().endHandler(aVoid -> processResponse(executionContext, endHandler)).end()
);
})
.errorHandler(result -> processResponse(simpleExecutionContext, endHandler))
.exitHandler(result -> processResponse(simpleExecutionContext, endHandler))
.handle(simpleExecutionContext);
});
}
private Handler endRequestHandler(
final CompletableEmitter emitter,
final HttpServerRequest httpServerRequest
) {
return context -> {
Long vertxTimerId = (Long) context.getAttribute(ATTR_INTERNAL_VERTX_TIMER_ID);
if (vertxTimerId != null) {
vertx.cancelTimer(vertxTimerId);
context.removeAttribute(ATTR_INTERNAL_VERTX_TIMER_ID);
}
if (context.response().ended()) {
emitter.onComplete();
} else {
httpServerRequest.response().rxEnd().subscribe(emitter::onComplete, emitter::tryOnError);
}
};
}
/**
* Prepare some global metrics for the current request (tenants, zones, ...).
*
* @param metrics the {@link Metrics} object to add information on.
*/
private void prepareV3Metrics(io.gravitee.reporter.api.http.Metrics metrics) {
// Set gateway tenant
gatewayConfiguration.tenant().ifPresent(metrics::setTenant);
// Set gateway zone
gatewayConfiguration.zone().ifPresent(metrics::setZone);
}
protected io.gravitee.gateway.http.vertx.VertxHttpServerRequest createV3Request(
HttpServerRequest httpServerRequest,
IdGenerator idGenerator
) {
io.gravitee.gateway.http.vertx.VertxHttpServerRequest request;
if (isV3WebSocket(httpServerRequest)) {
request = new VertxWebSocketServerRequest(httpServerRequest.getDelegate(), idGenerator);
} else {
if (httpServerRequest.version() == HttpVersion.HTTP_2) {
if (MediaType.APPLICATION_GRPC.equals(httpServerRequest.getHeader(HttpHeaders.CONTENT_TYPE))) {
request = new VertxGrpcServerRequest(httpServerRequest.getDelegate(), idGenerator);
} else {
request = new VertxHttp2ServerRequest(httpServerRequest.getDelegate(), idGenerator);
}
} else {
request = new io.gravitee.gateway.http.vertx.VertxHttpServerRequest(httpServerRequest.getDelegate(), idGenerator);
}
}
return request;
}
private SimpleExecutionContext createV3ExecutionContext(
HttpServerRequest httpServerRequest,
io.gravitee.gateway.http.vertx.VertxHttpServerRequest request
) {
SimpleExecutionContext simpleExecutionContext = new SimpleExecutionContext(request, request.createResponse());
if (requestTimeoutConfiguration.getRequestTimeout() > 0 && !isV3WebSocket(httpServerRequest)) {
final long vertxTimerId = vertx.setTimer(
requestTimeoutConfiguration.getRequestTimeout(),
event -> {
if (!httpServerRequest.response().ended()) {
final Handler handler = request.timeoutHandler();
handler.handle(event);
}
}
);
simpleExecutionContext.setAttribute(ATTR_INTERNAL_VERTX_TIMER_ID, vertxTimerId);
}
return simpleExecutionContext;
}
/**
* We are only considering HTTP_1.x requests for now.
* There is a dedicated RFC to support WebSockets over HTTP2: https://tools.ietf.org/html/rfc8441
*
* @param httpServerRequest
* @return true if given request is websocket, false otherwise
*/
private boolean isV3WebSocket(HttpServerRequest httpServerRequest) {
return RequestUtils.isWebSocket(httpServerRequest);
}
private void processResponse(
io.gravitee.gateway.api.ExecutionContext context,
Handler handler
) {
responseProcessorChainFactory.create().handler(handler).handle(context);
}
}