blob: f02578a59ef38b3d58637c9e09605bb951e4634c [file] [log] [blame]
/*
* Copyright (c) 2022, 2022 Contributors to the Eclipse Foundation.
* Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
* Copyright 2004 The Apache Software Foundation
*
* 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.apache.catalina.connector;
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.SEVERE;
import static org.apache.catalina.Globals.SESSION_PARAMETER_NAME;
import static org.apache.catalina.LogFacade.FAILED_TO_INITIALIZE_THE_INTERCEPTOR;
import static org.apache.catalina.LogFacade.HTTP_LISTENER_DISABLED;
import static org.apache.catalina.LogFacade.INTERNAL_ERROR;
import static org.apache.catalina.LogFacade.NO_HOST_MATCHES_SERVER_NAME_INFO;
import static org.apache.catalina.LogFacade.PARSING_CLIENT_CERT_EXCEPTION;
import static org.apache.catalina.LogFacade.REQUEST_PROCESSING_EXCEPTION;
import static org.apache.catalina.connector.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER;
import static org.glassfish.internal.api.Globals.getDefaultHabitat;
import java.io.CharConversionException;
import java.nio.charset.Charset;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.ResourceBundle;
import java.util.logging.Logger;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.Host;
import org.apache.catalina.LogFacade;
import org.apache.catalina.Wrapper;
import org.apache.catalina.core.ContainerBase;
import org.apache.catalina.util.ServerInfo;
import org.glassfish.common.util.InputValidationUtil;
import org.glassfish.grizzly.http.Method;
import org.glassfish.grizzly.http.Note;
import org.glassfish.grizzly.http.server.AfterServiceListener;
import org.glassfish.grizzly.http.server.HttpHandler;
import org.glassfish.grizzly.http.server.util.MappingData;
import org.glassfish.grizzly.http.util.ByteChunk;
import org.glassfish.grizzly.http.util.CharChunk;
import org.glassfish.grizzly.http.util.DataChunk;
import org.glassfish.grizzly.http.util.MessageBytes;
import org.glassfish.web.valve.GlassFishValve;
import org.glassfish.web.valve.ServletContainerInterceptor;
import com.sun.appserv.ProxyHandler;
/**
* Implementation of a request processor which delegates the processing to a Coyote processor.
*
* @author Craig R. McClanahan
* @author Remy Maucherat
* @version $Revision: 1.34 $ $Date: 2007/08/24 18:38:28 $
*/
public class CoyoteAdapter extends HttpHandler {
private static final Logger log = LogFacade.getLogger();
private static final ResourceBundle rb = log.getResourceBundle();
// -------------------------------------------------------------- Constants
static final String JVM_ROUTE = System.getProperty("jvmRoute");
protected static final boolean ALLOW_BACKSLASH =
Boolean.valueOf(System.getProperty("org.glassfish.grizzly.tcp.tomcat5.CoyoteAdapter.ALLOW_BACKSLASH", "false"));
private static final boolean COLLAPSE_ADJACENT_SLASHES =
Boolean.valueOf(System.getProperty("com.sun.enterprise.web.collapseAdjacentSlashes", "true"));
// Make sure this value is always aligned with {@link ContainerMapper}
// (@see com.sun.enterprise.v3.service.impl.ContainerMapper)
protected static final Note<MappingData> MAPPING_DATA =
org.glassfish.grizzly.http.server.Request.<MappingData>createNote("MappingData");
static final Note<Request> CATALINA_REQUEST_NOTE =
org.glassfish.grizzly.http.server.Request.createNote(Request.class.getName());
static final Note<Response> CATALINA_RESPONSE_NOTE =
org.glassfish.grizzly.http.server.Request.createNote(Response.class.getName());
static final CatalinaAfterServiceListener catalinaAfterServiceListener = new CatalinaAfterServiceListener();
// Make sure this value is always aligned with {@link ContainerMapper}
// (@see com.sun.enterprise.v3.service.impl.ContainerMapper)
private final static Note<DataChunk> DATA_CHUNK =
org.glassfish.grizzly.http.server.Request.<DataChunk>createNote("DataChunk");
// ----------------------------------------------------- Instance Variables
private Collection<ServletContainerInterceptor> interceptors;
/**
* When mod_jk is used, the adapter must be invoked the same way Tomcat does by invoking service(...) and the
* afterService(...). This is a hack to make it compatible with Tomcat 5|6.
*/
private boolean compatWithTomcat;
private String serverName = ServerInfo.getPublicServerInfo();
/**
* The CoyoteConnector with which this processor is associated.
*/
private Connector connector;
// ----------------------------------------------------------- Constructors
/**
* Construct a new CoyoteProcessor associated with the specified connector.
*
* @param connector CoyoteConnector that owns this processor
*/
public CoyoteAdapter(Connector connector) {
super();
this.connector = connector;
initServletInterceptors();
}
// -------------------------------------------------------- Adapter Methods
/**
* Service method.
*/
@Override
public void service(org.glassfish.grizzly.http.server.Request grizzlyRequest, org.glassfish.grizzly.http.server.Response grizzlyResponse) throws Exception {
grizzlyResponse.getResponse().setAllowCustomReasonPhrase(USE_CUSTOM_STATUS_MSG_IN_HEADER);
Request catalinaRequest = grizzlyRequest.getNote(CATALINA_REQUEST_NOTE);
Response catalinaResponse = grizzlyRequest.getNote(CATALINA_RESPONSE_NOTE);
// Grizzly already parsed, decoded, and mapped the request.
// Let's re-use this info here, before firing the
// requestStartEvent probe, so that the mapping data will be
// available to any probe event listener via standard
// ServletRequest APIs (such as getContextPath())
MappingData mappingData = grizzlyRequest.getNote(MAPPING_DATA);
final boolean v3Enabled = mappingData != null;
if (catalinaRequest == null) {
// Create objects
catalinaRequest = (Request) connector.createRequest();
catalinaResponse = (Response) connector.createResponse();
// Link objects
catalinaRequest.setResponse(catalinaResponse);
catalinaResponse.setRequest(catalinaRequest);
// Set as notes
grizzlyRequest.setNote(CATALINA_REQUEST_NOTE, catalinaRequest);
grizzlyRequest.setNote(CATALINA_RESPONSE_NOTE, catalinaResponse);
// Set query string encoding
grizzlyRequest.getRequest().getRequestURIRef().setDefaultURIEncoding(Charset.forName(connector.getURIEncoding()));
}
catalinaRequest.setGrizzlyRequest(grizzlyRequest);
catalinaResponse.setCoyoteResponse(grizzlyResponse);
if (v3Enabled && !compatWithTomcat) {
catalinaRequest.setMappingData(mappingData);
catalinaRequest.updatePaths(mappingData);
}
grizzlyRequest.addAfterServiceListener(catalinaAfterServiceListener);
try {
doService(grizzlyRequest, catalinaRequest, grizzlyResponse, catalinaResponse, v3Enabled);
// Request may want to initialize async processing
catalinaRequest.onExitService();
} catch (Throwable t) {
log.log(SEVERE, REQUEST_PROCESSING_EXCEPTION, t);
}
}
private void enteringServletContainer(Request req, Response res) {
if (interceptors == null) {
return;
}
for (ServletContainerInterceptor interceptor : interceptors) {
try {
interceptor.preInvoke(req, res);
} catch (Throwable th) {
log.log(SEVERE, INTERNAL_ERROR, th);
}
}
}
private void leavingServletContainer(Request req, Response res) {
if (interceptors == null) {
return;
}
for (ServletContainerInterceptor interceptor : interceptors) {
try {
interceptor.postInvoke(req, res);
} catch (Throwable th) {
log.log(SEVERE, INTERNAL_ERROR, th);
}
}
}
private void initServletInterceptors() {
try {
interceptors = getDefaultHabitat().getAllServices(ServletContainerInterceptor.class);
} catch (Throwable th) {
log.log(SEVERE, FAILED_TO_INITIALIZE_THE_INTERCEPTOR, th);
}
}
private void doService(
final org.glassfish.grizzly.http.server.Request grizzlyRequest, final Request catalinaRequest,
final org.glassfish.grizzly.http.server.Response grizzlyResponse, final Response catalinaResponse, final boolean v3Enabled) throws Exception {
// Check connector for disabled state
if (!connector.isEnabled()) {
String msg = MessageFormat.format(rb.getString(HTTP_LISTENER_DISABLED), String.valueOf(connector.getPort()));
if (log.isLoggable(FINE)) {
log.log(FINE, msg);
}
catalinaResponse.sendError(SC_NOT_FOUND, msg);
return;
}
// Parse and set Catalina and configuration specific request parameters
if (postParseRequest(grizzlyRequest, catalinaRequest, grizzlyResponse, catalinaResponse, v3Enabled)) {
boolean authPassthroughEnabled = connector.getAuthPassthroughEnabled();
ProxyHandler proxyHandler = connector.getProxyHandler();
if (authPassthroughEnabled && proxyHandler != null) {
// Otherwise Servlet request.isSecure() value is not propagated when authPassthroughEnabled is set to true
if (proxyHandler.getSSLKeysize(catalinaRequest.getRequest()) > 0) {
catalinaRequest.setSecure(true);
}
X509Certificate[] certs = null;
try {
certs = proxyHandler.getSSLClientCertificateChain(catalinaRequest.getRequest());
} catch (CertificateException ce) {
log.log(SEVERE, PARSING_CLIENT_CERT_EXCEPTION, ce);
}
if (certs != null) {
catalinaRequest.setAttribute(Globals.CERTIFICATES_ATTR, certs);
}
}
// Invoke the web container
connector.requestStartEvent(catalinaRequest.getRequest(), catalinaRequest.getHost(), catalinaRequest.getContext());
Container container = connector.getContainer();
enteringServletContainer(catalinaRequest, catalinaResponse);
try {
catalinaRequest.lockSession();
if (container.getPipeline().hasNonBasicValves() || container.hasCustomPipeline()) {
container.getPipeline().invoke(catalinaRequest, catalinaResponse);
} else {
// Invoke host directly
Host host = catalinaRequest.getHost();
if (host == null) {
catalinaResponse.sendError(SC_BAD_REQUEST);
catalinaResponse.setDetailMessage(
MessageFormat.format(
rb.getString(NO_HOST_MATCHES_SERVER_NAME_INFO),
catalinaRequest.getRequest().getServerName()));
return;
}
if (host.getPipeline().hasNonBasicValves() || host.hasCustomPipeline()) {
host.getPipeline().invoke(catalinaRequest, catalinaResponse);
} else {
GlassFishValve hostValve = host.getPipeline().getBasic();
hostValve.invoke(catalinaRequest, catalinaResponse);
// Error handling
hostValve.postInvoke(catalinaRequest, catalinaResponse);
}
}
} finally {
try {
connector.requestEndEvent(catalinaRequest.getRequest(), catalinaRequest.getHost(), catalinaRequest.getContext(), catalinaResponse.getStatus());
} finally {
leavingServletContainer(catalinaRequest, catalinaResponse);
}
}
}
}
// ------------------------------------------------------ Protected Methods
/**
* Parse additional request parameters.
*/
protected boolean postParseRequest(
final org.glassfish.grizzly.http.server.Request grizzlyRequest, final Request catalinaRequest,
final org.glassfish.grizzly.http.server.Response grizzlyResponse, final Response catalinaResponse, final boolean v3Enabled) throws Exception {
// XXX the processor may have set a correct scheme and port prior to this point,
// in ajp13 protocols dont make sense to get the port from the connector...
// otherwise, use connector configuration
catalinaRequest.setSecure(grizzlyRequest.isSecure());
// URI decoding
DataChunk decodedURI;
try {
decodedURI = grizzlyRequest.getRequest().getRequestURIRef().getDecodedRequestURIBC();
} catch (CharConversionException cce) {
catalinaResponse.sendError(SC_BAD_REQUEST, "Invalid URI");
return false;
}
if (compatWithTomcat || !v3Enabled) {
// Set the remote principal
String principal = grizzlyRequest.getRemoteUser();
if (principal != null) {
catalinaRequest.setUserPrincipal(new CoyotePrincipal(principal));
}
// Set the authorization type
String authtype = grizzlyRequest.getAuthType();
if (authtype != null) {
catalinaRequest.setAuthType(authtype);
}
}
/*
* Remove any parameters from the URI, so they won't be considered by the mapping algorithm, and save them in a
* temporary CharChunk, so that any session id param may be parsed once the target context, which may use a custom
* session parameter name, has been identified
*/
final CharChunk uriParamsCC = catalinaRequest.getURIParams();
final CharChunk uriCC = decodedURI.getCharChunk();
final int semicolon = uriCC.indexOf(';');
if (semicolon > 0) {
final int absSemicolon = uriCC.getStart() + semicolon;
uriParamsCC.setChars(uriCC.getBuffer(), absSemicolon, uriCC.getEnd() - absSemicolon);
decodedURI.setChars(uriCC.getBuffer(), uriCC.getStart(), absSemicolon - uriCC.getStart());
}
if (compatWithTomcat || !v3Enabled) {
/* mod_jk */
DataChunk localDecodedURI = decodedURI;
if (semicolon > 0) {
localDecodedURI = grizzlyRequest.getNote(DATA_CHUNK);
if (localDecodedURI == null) {
localDecodedURI = DataChunk.newInstance();
grizzlyRequest.setNote(DATA_CHUNK, localDecodedURI);
}
localDecodedURI.duplicate(decodedURI);
}
connector.getMapper()
.map(
grizzlyRequest.getRequest().serverName(),
localDecodedURI,
catalinaRequest.getMappingData());
MappingData md = catalinaRequest.getMappingData();
grizzlyRequest.setNote(MAPPING_DATA, md);
catalinaRequest.updatePaths(md);
}
// FIXME: the code below doesn't belongs to here, this is only have sense in Http11, not in ajp13..
// At this point the Host header has been processed. Override if the proxyPort/proxyHost are set
String proxyName = connector.getProxyName();
int proxyPort = connector.getProxyPort();
if (proxyPort != 0) {
grizzlyRequest.setServerPort(proxyPort);
}
if (proxyName != null) {
grizzlyRequest.setServerName(proxyName);
}
Context catalinaContext = (Context) catalinaRequest.getMappingData().context;
// Parse session id
if (catalinaContext != null) {
if (grizzlyRequest.isRequestedSessionIdFromURL() && SESSION_PARAMETER_NAME.equals(catalinaContext.getSessionParameterName())) {
catalinaRequest.obtainSessionId();
} else if (!uriParamsCC.isNull()) {
catalinaRequest.parseSessionId(catalinaContext.getSessionParameterName(), uriParamsCC);
}
}
catalinaRequest.setDefaultContext(catalinaRequest.getMappingData().isDefaultContext);
catalinaRequest.setContext(catalinaContext);
if (catalinaContext != null && !uriParamsCC.isNull()) {
catalinaRequest.parseSessionVersion(uriParamsCC);
}
if (!uriParamsCC.isNull()) {
catalinaRequest.parseJReplica(uriParamsCC);
}
catalinaRequest.setWrapper((Wrapper) catalinaRequest.getMappingData().wrapper);
// Filter trace method
if (!connector.getAllowTrace() && Method.TRACE.equals(grizzlyRequest.getMethod())) {
Wrapper wrapper = catalinaRequest.getWrapper();
String header = null;
if (wrapper != null) {
String[] methods = wrapper.getServletMethods();
if (methods != null) {
for (String method : methods) {
// Exclude TRACE from methods returned in Allow header
if ("TRACE".equals(method)) {
continue;
}
if (header == null) {
header = method;
} else {
header += ", " + method;
}
}
}
}
grizzlyResponse.setStatus(405, "TRACE method is not allowed");
grizzlyResponse.addHeader("Allow", header);
return false;
}
// Possible redirect
DataChunk redirectPathMB = catalinaRequest.getMappingData().redirectPath;
if (!redirectPathMB.isNull() && (!catalinaContext.hasAdHocPaths() || (catalinaContext.getAdHocServletName(catalinaRequest.getRequest().getServletPath()) == null))) {
String redirectPath = redirectPathMB.toString();
String query = catalinaRequest.getQueryString();
if (catalinaRequest.isRequestedSessionIdFromURL()) {
// This is not optimal, but as this is not very common, it shouldn't matter
redirectPath = redirectPath + ";" + catalinaContext.getSessionParameterName() + "=" + catalinaRequest.getRequestedSessionId();
}
redirectPath = catalinaResponse.encode(redirectPath);
if (query != null) {
// This is not optimal, but as this is not very common, it shouldn't matter
redirectPath = redirectPath + "?" + query;
}
boolean authPassthroughEnabled = connector.getAuthPassthroughEnabled();
ProxyHandler proxyHandler = connector.getProxyHandler();
if (authPassthroughEnabled && proxyHandler != null) {
if (proxyHandler.getSSLKeysize(catalinaRequest.getRequest()) > 0) {
catalinaRequest.setSecure(true);
}
}
// Issue a permanent redirect
// Validating the redirectPath for header injection
if (InputValidationUtil.validateStringforCRLF(redirectPath)) {
catalinaResponse.sendError(403, "Forbidden");
} else {
catalinaResponse.sendRedirect(InputValidationUtil.removeLinearWhiteSpaces(redirectPath), false);
}
return false;
}
// Parse session Id
catalinaRequest.parseSessionCookiesId();
catalinaRequest.parseJrouteCookie();
return true;
}
/**
* Normalize URI.
* <p>
* This method normalizes "\", "//", "/./" and "/../". This method will return false when trying to go above the root,
* or if the URI contains a null byte.
*
* @param uriMB URI to be normalized
*/
public static boolean normalize(MessageBytes uriMB) {
int type = uriMB.getType();
if (type == MessageBytes.T_CHARS) {
return normalizeChars(uriMB);
}
return normalizeBytes(uriMB);
}
private static boolean normalizeBytes(MessageBytes uriMB) {
ByteChunk uriBC = uriMB.getByteChunk();
byte[] b = uriBC.getBytes();
int start = uriBC.getStart();
int end = uriBC.getEnd();
// An empty URL is not acceptable
if (start == end) {
return false;
}
// URL * is acceptable
if ((end - start == 1) && b[start] == (byte) '*') {
return true;
}
int pos = 0;
int index = 0;
// Replace '\' with '/'
// Check for null byte
for (pos = start; pos < end; pos++) {
if (b[pos] == (byte) '\\') {
if (ALLOW_BACKSLASH) {
b[pos] = (byte) '/';
} else {
return false;
}
}
if (b[pos] == (byte) 0) {
return false;
}
}
// The URL must start with '/'
if (b[start] != (byte) '/') {
return false;
}
// Replace "//" with "/"
if (COLLAPSE_ADJACENT_SLASHES) {
for (pos = start; pos < (end - 1); pos++) {
if (b[pos] == (byte) '/') {
while ((pos + 1 < end) && (b[pos + 1] == (byte) '/')) {
copyBytes(b, pos, pos + 1, end - pos - 1);
end--;
}
}
}
}
// If the URI ends with "/." or "/..", then we append an extra "/"
// Note: It is possible to extend the URI by 1 without any side effect
// as the next character is a non-significant WS.
if (((end - start) > 2) && (b[end - 1] == (byte) '.')) {
if ((b[end - 2] == (byte) '/') || ((b[end - 2] == (byte) '.') && (b[end - 3] == (byte) '/'))) {
b[end] = (byte) '/';
end++;
}
}
uriBC.setEnd(end);
index = 0;
// Resolve occurrences of "/./" in the normalized path
while (true) {
index = uriBC.indexOf("/./", 0, 3, index);
if (index < 0) {
break;
}
copyBytes(b, start + index, start + index + 2, end - start - index - 2);
end = end - 2;
uriBC.setEnd(end);
}
index = 0;
// Resolve occurrences of "/../" in the normalized path
while (true) {
index = uriBC.indexOf("/../", 0, 4, index);
if (index < 0) {
break;
}
// Prevent from going outside our context
if (index == 0) {
return false;
}
int index2 = -1;
for (pos = start + index - 1; (pos >= 0) && (index2 < 0); pos--) {
if (b[pos] == (byte) '/') {
index2 = pos;
}
}
copyBytes(b, start + index2, start + index + 3, end - start - index - 3);
end = end + index2 - index - 3;
uriBC.setEnd(end);
index = index2;
}
uriBC.setBytes(b, start, end);
return true;
}
private static boolean normalizeChars(MessageBytes uriMessageBytes) {
CharChunk uriCharChunk = uriMessageBytes.getCharChunk();
char[] c = uriCharChunk.getChars();
int start = uriCharChunk.getStart();
int end = uriCharChunk.getEnd();
// URL * is acceptable
if ((end - start == 1) && c[start] == '*') {
return true;
}
int pos = 0;
int index = 0;
// Replace '\' with '/'
// Check for null char
for (pos = start; pos < end; pos++) {
if (c[pos] == '\\') {
if (ALLOW_BACKSLASH) {
c[pos] = '/';
} else {
return false;
}
}
if (c[pos] == (char) 0) {
return false;
}
}
// The URL must start with '/'
if (c[start] != '/') {
return false;
}
// Replace "//" with "/"
if (COLLAPSE_ADJACENT_SLASHES) {
for (pos = start; pos < (end - 1); pos++) {
if (c[pos] == '/') {
while ((pos + 1 < end) && (c[pos + 1] == '/')) {
copyChars(c, pos, pos + 1, end - pos - 1);
end--;
}
}
}
}
// If the URI ends with "/." or "/..", then we append an extra "/"
// Note: It is possible to extend the URI by 1 without any side effect
// as the next character is a non-significant WS.
if (((end - start) > 2) && (c[end - 1] == '.')) {
if ((c[end - 2] == '/') || ((c[end - 2] == '.') && (c[end - 3] == '/'))) {
c[end] = '/';
end++;
}
}
uriCharChunk.setEnd(end);
index = 0;
// Resolve occurrences of "/./" in the normalized path
while (true) {
index = uriCharChunk.indexOf("/./", 0, 3, index);
if (index < 0) {
break;
}
copyChars(c, start + index, start + index + 2, end - start - index - 2);
end = end - 2;
uriCharChunk.setEnd(end);
}
index = 0;
// Resolve occurrences of "/../" in the normalized path
while (true) {
index = uriCharChunk.indexOf("/../", 0, 4, index);
if (index < 0) {
break;
}
// Prevent from going outside our context
if (index == 0) {
return false;
}
int index2 = -1;
for (pos = start + index - 1; (pos >= 0) && (index2 < 0); pos--) {
if (c[pos] == '/') {
index2 = pos;
}
}
copyChars(c, start + index2, start + index + 3, end - start - index - 3);
end = end + index2 - index - 3;
uriCharChunk.setEnd(end);
index = index2;
}
uriCharChunk.setChars(c, start, end);
return true;
}
// ------------------------------------------------------ Protected Methods
/**
* Copy an array of bytes to a different position. Used during normalization.
*/
protected static void copyBytes(byte[] b, int dest, int src, int len) {
for (int pos = 0; pos < len; pos++) {
b[pos + dest] = b[pos + src];
}
}
/**
* Copy an array of chars to a different position. Used during normalization.
*/
private static void copyChars(char[] c, int dest, int src, int len) {
for (int pos = 0; pos < len; pos++) {
c[pos + dest] = c[pos + src];
}
}
/**
* Log a message on the Logger associated with our Container (if any)
*
* @param message Message to be logged
*/
protected void log(String message) {
log.log(INFO, message);
}
/**
* Log a message on the Logger associated with our Container (if any)
*
* @param message Message to be logged
* @param throwable Associated exception
*/
protected void log(String message, Throwable throwable) {
log.log(SEVERE, message, throwable);
}
/**
* Notify all container event listeners that a particular event has occurred for this Adapter. The default
* implementation performs this notification synchronously using the calling thread.
*
* @param type Event type
* @param data Event data
*/
public void fireAdapterEvent(String type, Object data) {
if (connector != null && connector.getContainer() != null) {
try {
((ContainerBase) connector.getContainer()).fireContainerEvent(type, data);
} catch (Throwable t) {
log.log(SEVERE, REQUEST_PROCESSING_EXCEPTION, t);
}
}
}
/**
* Return true when an instance is executed the same way it does in Tomcat.
*/
public boolean isCompatWithTomcat() {
return compatWithTomcat;
}
/**
* <tt>true</tt> if this class needs to be compatible with Tomcat Adapter class. Since Tomcat Adapter implementation
* doesn't support the afterService method, the afterService method must be invoked inside the service method.
*/
public void setCompatWithTomcat(boolean compatWithTomcat) {
this.compatWithTomcat = compatWithTomcat;
// Add server header
if (compatWithTomcat) {
serverName = "Apache/" + serverName;
} else {
// Recalculate.
serverName = ServerInfo.getPublicServerInfo();
}
}
/**
* Gets the port of this CoyoteAdapter.
*
* @return the port of this CoyoteAdapter
*/
public int getPort() {
return connector.getPort();
}
/**
* AfterServiceListener, which is responsible for recycle catalina request and response objects.
*/
static final class CatalinaAfterServiceListener implements AfterServiceListener {
@Override
public void onAfterService(final org.glassfish.grizzly.http.server.Request grizzlyRequest) {
final Request catalinaRequest = grizzlyRequest.getNote(CATALINA_REQUEST_NOTE);
final Response catalinaResponse = grizzlyRequest.getNote(CATALINA_RESPONSE_NOTE);
if (catalinaRequest != null) {
try {
if (!catalinaRequest.isUpgrade()) {
catalinaResponse.finishResponse();
} else {
catalinaResponse.setUpgrade(catalinaRequest.isUpgrade());
}
} catch (Exception e) {
log.log(SEVERE, REQUEST_PROCESSING_EXCEPTION, e);
} finally {
try {
catalinaRequest.unlockSession();
} finally {
catalinaRequest.recycle();
catalinaResponse.recycle();
}
}
}
}
}
}