org.cometd.server.http.jetty.CometDHandler Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cometd.server.http.jetty;
import java.util.Map;
import java.util.Objects;
import org.cometd.bayeux.Promise;
import org.cometd.bayeux.server.BayeuxServer;
import org.cometd.bayeux.server.ServerSession;
import org.cometd.server.BayeuxServerImpl;
import org.cometd.server.HttpException;
import org.cometd.server.ServerSessionImpl;
import org.cometd.server.http.AbstractHttpTransport;
import org.cometd.server.http.JSONHttpTransport;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Context;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.Callback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@code CometDHandler} maps HTTP requests to the HTTP server transports
* of a {@link BayeuxServer} instance.
* The {@link BayeuxServer} instance is created and configured using the
* given {@link #setOptions(Map) options}.
*/
// TODO: can this handler be the direct child of Server?
// In this way we will save the ContextHandler wrapping & machinery.
public class CometDHandler extends Handler.Abstract {
private static final Logger LOGGER = LoggerFactory.getLogger(CometDHandler.class);
private Map options = Map.of();
private BayeuxServer bayeuxServer;
private boolean exported;
public CometDHandler() {
this(InvocationType.BLOCKING);
}
public CometDHandler(InvocationType invocationType) {
super(invocationType);
}
public Map getOptions() {
return options;
}
public void setOptions(Map options) {
this.options = Objects.requireNonNull(options);
}
@Override
protected void doStart() throws Exception {
String bayeuxServerContextAttributeName = getBayeuxServerContextAttributeName();
Context context = ContextHandler.getCurrentContext(getServer());
bayeuxServer = (BayeuxServer)context.getAttribute(bayeuxServerContextAttributeName);
if (bayeuxServer == null) {
bayeuxServer = newBayeuxServer();
exported = true;
String transports = JSONHttpTransport.class.getName() + ",org.cometd.server.websocket.jetty.JettyWebSocketTransport";
bayeuxServer.setOption(BayeuxServerImpl.TRANSPORTS_OPTION, transports);
for (Map.Entry entry : getOptions().entrySet()) {
this.bayeuxServer.setOption(entry.getKey(), entry.getValue());
}
}
bayeuxServer.setOption(Context.class.getName(), context);
addBean(bayeuxServer);
if (exported) {
context.setAttribute(bayeuxServerContextAttributeName, bayeuxServer);
}
super.doStart();
}
@Override
protected void doStop() throws Exception {
for (ServerSession session : bayeuxServer.getSessions()) {
((ServerSessionImpl)session).destroyScheduler();
}
super.doStop();
removeBean(bayeuxServer);
bayeuxServer = null;
if (exported) {
Context context = ContextHandler.getCurrentContext(getServer());
context.removeAttribute(getBayeuxServerContextAttributeName());
}
}
public BayeuxServer getBayeuxServer() {
return bayeuxServer;
}
protected BayeuxServer newBayeuxServer() {
return new BayeuxServerImpl();
}
@Override
public boolean handle(Request request, Response response, Callback callback) {
if (HttpMethod.OPTIONS.is(request.getMethod())) {
serviceOptions(request, response, callback);
return true;
}
JettyCometDRequest cometDRequest = new JettyCometDRequest(request);
JettyCometDResponse cometDResponse = new JettyCometDResponse(response);
JettyBayeuxContext bayeuxContext = new JettyBayeuxContext(request);
Promise promise = new Promise<>() {
@Override
public void succeed(Void result) {
callback.succeeded();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Handling successful");
}
}
@Override
public void fail(Throwable failure) {
int code = HttpStatus.INTERNAL_SERVER_ERROR_500;
Throwable cause = failure;
if (failure instanceof HttpException http) {
code = http.getCode();
cause = http.getCause();
}
sendError(request, response, callback, code, cause);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Handling failed", failure);
}
}
};
AbstractHttpTransport transport = AbstractHttpTransport.find(bayeuxServer, cometDRequest);
if (transport == null) {
Response.writeError(request, response, callback, HttpStatus.BAD_REQUEST_400, "Unknown Bayeux Transport");
} else {
request.addIdleTimeoutListener(x -> false);
transport.handle(bayeuxContext, cometDRequest, cometDResponse, promise);
}
return true;
}
protected void serviceOptions(Request request, Response response, Callback callback) {
// OPTIONS requests are made by browsers that are CORS compliant
// (see http://www.w3.org/TR/cors/) during a "preflight request".
// Preflight requests happen for each different new URL, then
// results are cached by the browser.
// For the Bayeux protocol, preflight requests happen for URLs
// such as "/cometd/handshake", "/cometd/connect", etc., since
// the Bayeux clients append the Bayeux message type to the base
// Bayeux server URL.
// Just return 200 OK, there is nothing more to add to such requests.
callback.succeeded();
}
protected void sendError(Request request, Response response, Callback callback, int code, Throwable failure) {
Response.writeError(request, response, callback, code, null, failure);
}
private String getBayeuxServerContextAttributeName() {
String name = getOptions().get(BayeuxServerImpl.CONTEXT_ATTRIBUTE_NAME_OPTION);
return name != null ? name : BayeuxServer.ATTRIBUTE;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy