CoyoteAdapterpublic class CoyoteAdapter extends Object implements org.apache.coyote.AdapterImplementation of a request processor which delegates the processing to a
Coyote processor. |
Fields Summary |
---|
private static com.sun.org.apache.commons.logging.Log | log | public static final int | ADAPTER_NOTES | protected static final boolean | ALLOW_BACKSLASH | private static final boolean | COLLAPSE_ADJACENT_SLASHES | private boolean | compatWithTomcatWhen 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 String | serverName | private CoyoteConnector | connectorThe CoyoteConnector with which this processor is associated. | private int | debugThe debugging detail level for this component. | private org.apache.tomcat.util.buf.UEncoder | urlEncoder | protected org.apache.catalina.util.StringManager | smThe string manager for this package. |
Constructors Summary |
---|
public CoyoteAdapter(CoyoteConnector connector)Construct a new CoyoteProcessor associated with the specified connector.
// ----------------------------------------------------------- Constructors
super();
this.connector = connector;
this.debug = connector.getDebug();
// START GlassFish 936
urlEncoder.addSafeCharacter('/");
// END GlassFish 936
|
Methods Summary |
---|
public void | afterService(org.apache.coyote.Request req, org.apache.coyote.Response res)Finish the response and close the connection based on the connection
header.
CoyoteRequest request = (CoyoteRequest) req.getNote(ADAPTER_NOTES);
CoyoteResponse response = (CoyoteResponse) res.getNote(ADAPTER_NOTES);
if ( request == null || response == null) return;
try{
response.finishResponse();
req.action( ActionCode.ACTION_POST_REQUEST , null);
}catch (Throwable t) {
log.error(sm.getString("coyoteAdapter.service"), t);
} finally {
// Recycle the wrapper request and response
request.recycle();
response.recycle();
}
| protected static void | copyBytes(byte[] b, int dest, int src, int len)Copy an array of bytes to a different position. Used during
normalization.
for (int pos = 0; pos < len; pos++) {
b[pos + dest] = b[pos + src];
}
| private static void | copyChars(char[] c, int dest, int src, int len)Copy an array of chars to a different position. Used during
normalization.
for (int pos = 0; pos < len; pos++) {
c[pos + dest] = c[pos + src];
}
| public void | fireAdapterEvent(java.lang.String type, java.lang.Object data)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.
if ( connector != null && connector.getContainer() != null) {
try{
((ContainerBase)connector.getContainer())
.fireContainerEvent(type,data);
} catch (Throwable t){
log.error(sm.getString("coyoteAdapter.service"), t);
}
}
| public boolean | isCompatWithTomcat()Return true when an instance is executed the same way it does in Tomcat.
return compatWithTomcat;
| protected void | log(java.lang.String message)Log a message on the Logger associated with our Container (if any)
log.info( message );
| protected void | log(java.lang.String message, java.lang.Throwable throwable)Log a message on the Logger associated with our Container (if any)
log.error( message, throwable);
| public static boolean | normalize(org.apache.tomcat.util.buf.MessageBytes uriMB)Normalize URI.
This method normalizes "\", "//", "/./" and "/../". This method will
return false when trying to go above the root, or if the URI contains
a null byte.
int type = uriMB.getType();
if (type == MessageBytes.T_CHARS) {
return normalizeChars(uriMB);
} else {
return normalizeBytes(uriMB);
}
| private static boolean | normalizeBytes(org.apache.tomcat.util.buf.MessageBytes uriMB)
ByteChunk uriBC = uriMB.getByteChunk();
byte[] b = uriBC.getBytes();
int start = uriBC.getStart();
int end = uriBC.getEnd();
// 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(org.apache.tomcat.util.buf.MessageBytes uriMB)
CharChunk uriCC = uriMB.getCharChunk();
char[] c = uriCC.getChars();
int start = uriCC.getStart();
int end = uriCC.getEnd();
// URL * is acceptable
if ((end - start == 1) && c[start] == (char) '*")
return true;
int pos = 0;
int index = 0;
// Replace '\' with '/'
// Check for null char
for (pos = start; pos < end; pos++) {
if (c[pos] == (char) '\\") {
if (ALLOW_BACKSLASH) {
c[pos] = (char) '/";
} else {
return false;
}
}
if (c[pos] == (char) 0) {
return false;
}
}
// The URL must start with '/'
if (c[start] != (char) '/") {
return false;
}
// Replace "//" with "/"
if (COLLAPSE_ADJACENT_SLASHES) {
for (pos = start; pos < (end - 1); pos++) {
if (c[pos] == (char) '/") {
while ((pos + 1 < end) && (c[pos + 1] == (char) '/")) {
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] == (char) '.")) {
if ((c[end - 2] == (char) '/")
|| ((c[end - 2] == (char) '.")
&& (c[end - 3] == (char) '/"))) {
c[end] = (char) '/";
end++;
}
}
uriCC.setEnd(end);
index = 0;
// Resolve occurrences of "/./" in the normalized path
while (true) {
index = uriCC.indexOf("/./", 0, 3, index);
if (index < 0)
break;
copyChars(c, start + index, start + index + 2,
end - start - index - 2);
end = end - 2;
uriCC.setEnd(end);
}
index = 0;
// Resolve occurrences of "/../" in the normalized path
while (true) {
index = uriCC.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] == (char) '/") {
index2 = pos;
}
}
copyChars(c, start + index2, start + index + 3,
end - start - index - 3);
end = end + index2 - index - 3;
uriCC.setEnd(end);
index = index2;
}
uriCC.setChars(c, start, end);
return true;
| protected boolean | postParseRequest(org.apache.coyote.Request req, CoyoteRequest request, org.apache.coyote.Response res, CoyoteResponse response)Parse additional request parameters.
// XXX the processor needs to set a correct scheme and port prior to this point,
// in ajp13 protocols dont make sense to get the port from the connector..
// 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
if (! req.scheme().isNull()) {
// use processor specified scheme to determine secure state
request.setSecure(req.scheme().equals("https"));
} else {
// use connector scheme and secure configuration, (defaults to
// "http" and false respectively)
req.scheme().setString(connector.getScheme());
request.setSecure(connector.getSecure());
}
// FIXME: the code below doesnt 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) {
req.setServerPort(proxyPort);
}
if (proxyName != null) {
req.serverName().setString(proxyName);
}
// URI decoding
MessageBytes decodedURI = req.decodedURI();
decodedURI.duplicate(req.requestURI());
try {
req.getURLDecoder().convert(decodedURI, false);
} catch (IOException ioe) {
res.setStatus(400);
res.setMessage("Invalid URI: " + ioe.getMessage());
return false;
}
/* GlassFish Issue 2339
// Normalize decoded URI
if (!normalize(req.decodedURI())) {
res.setStatus(400);
res.setMessage("Invalid URI");
return false;
}
*/
// Set the remote principal
String principal = req.getRemoteUser().toString();
if (principal != null) {
request.setUserPrincipal(new CoyotePrincipal(principal));
}
// Set the authorization type
String authtype = req.getAuthType().toString();
if (authtype != null) {
request.setAuthType(authtype);
}
/* CR 6309511
// URI character decoding
convertURI(decodedURI, request);
// Parse session Id
parseSessionId(req, request);
*/
// START CR 6309511
// URI character decoding
request.convertURI(decodedURI);
// START GlassFish Issue 2339
// Normalize decoded URI
if (!normalize(decodedURI)) {
res.setStatus(400);
res.setMessage("Invalid URI");
return false;
}
// END GlassFish Issue 2339
// Parse session Id
request.parseSessionId();
// END CR 6309511
// Remove any remaining parameters (other than session id, which has
// already been removed in parseSessionId()) from the URI, so they
// won't be considered by the mapping algorithm.
CharChunk uriCC = decodedURI.getCharChunk();
int semicolon = uriCC.indexOf(';");
String sessionVersionString = null;
if (semicolon > 0) {
sessionVersionString = request.parseSessionVersion();
decodedURI.setChars
(uriCC.getBuffer(), uriCC.getStart(), semicolon);
}
// Request mapping.
connector.getMapper().map(req.serverName(), decodedURI,
request.getMappingData());
// START GlassFish 1024
request.setDefaultContext(request.getMappingData().isDefaultContext);
// END GlassFish 1024
// START SJSAS 6253524
// request.setContext((Context) request.getMappingData().context);
// END SJSAS 6253524
// START SJSAS 6253524
Context ctx = (Context) request.getMappingData().context;
request.setContext(ctx);
// END SJSAS 6253524
request.setWrapper((Wrapper) request.getMappingData().wrapper);
// Filter trace method
if (!connector.getAllowTrace()
&& req.method().equalsIgnoreCase("TRACE")) {
Wrapper wrapper = request.getWrapper();
String header = null;
if (wrapper != null) {
String[] methods = wrapper.getServletMethods();
if (methods != null) {
for (int i=0; i<methods.length; i++) {
// Exclude TRACE from methods returned in Allow header
if ("TRACE".equals(methods[i])) {
continue;
}
if (header == null) {
header = methods[i];
} else {
header += ", " + methods[i];
}
}
}
}
res.setStatus(405);
res.addHeader("Allow", header);
res.setMessage("TRACE method is not allowed");
return false;
}
// Possible redirect
MessageBytes redirectPathMB = request.getMappingData().redirectPath;
// START SJSAS 6253524
// if (!redirectPathMB.isNull()) {
// END SJSAS 6253524
// START SJSAS 6253524
if (!redirectPathMB.isNull()
&& (!ctx.hasAdHocPaths()
|| (ctx.getAdHocServletName(((HttpServletRequest)
request.getRequest()).getServletPath()) == null))) {
// END SJSAS 6253524
String redirectPath = redirectPathMB.toString();
String query = request.getQueryString();
if (request.isRequestedSessionIdFromURL()) {
// This is not optimal, but as this is not very common, it
// shouldn't matter
redirectPath = redirectPath + ";jsessionid="
+ request.getRequestedSessionId();
}
// START GlassFish 936
redirectPath = urlEncoder.encodeURL(redirectPath);
// END GlassFish 936
if (query != null) {
// This is not optimal, but as this is not very common, it
// shouldn't matter
redirectPath = redirectPath + "?" + query;
}
response.sendRedirect(redirectPath);
return false;
}
// Parse session Id
/* CR 6309511
parseSessionCookiesId(req, request);
*/
// START CR 6309511
request.parseSessionCookiesId();
// END CR 6309511
// START SJSAS 6346226
request.parseJrouteCookie();
// END SJSAS 6346226
if (sessionVersionString != null) {
request.parseSessionVersionString(sessionVersionString);
}
return true;
| public void | service(org.apache.coyote.Request req, org.apache.coyote.Response res)Service method.
// -------------------------------------------------------- Adapter Methods
CoyoteRequest request = (CoyoteRequest) req.getNote(ADAPTER_NOTES);
CoyoteResponse response = (CoyoteResponse) res.getNote(ADAPTER_NOTES);
if (request == null) {
// Create objects
request = (CoyoteRequest) connector.createRequest();
request.setCoyoteRequest(req);
response = (CoyoteResponse) connector.createResponse();
response.setCoyoteResponse(res);
// Link objects
request.setResponse(response);
response.setRequest(request);
// Set as notes
req.setNote(ADAPTER_NOTES, request);
res.setNote(ADAPTER_NOTES, response);
// Set query string encoding
req.getParameters().setQueryStringEncoding
(connector.getURIEncoding());
}
// START SJSAS 6331392
// Check connector for disabled state
if (!connector.isEnabled()) {
String msg = sm.getString("coyoteAdapter.listenerOff",
String.valueOf(connector.getPort()));
if (log.isDebugEnabled()) {
log.debug(msg);
}
response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);
return;
}
// END SJSAS 6331392
if (connector.isXpoweredBy()) {
response.addHeader("X-Powered-By", "Servlet/2.5");
}
try {
// Parse and set Catalina and configuration specific
// request parameters
if ( postParseRequest(req, request, res, response) ) {
// START S1AS 6188932
boolean authPassthroughEnabled =
connector.getAuthPassthroughEnabled();
ProxyHandler proxyHandler = connector.getProxyHandler();
if (authPassthroughEnabled && proxyHandler != null) {
// START SJSAS 6397218
if (proxyHandler.getSSLKeysize(
(HttpServletRequest)request.getRequest()) > 0) {
request.setSecure(true);
}
// END SJSAS 6397218
X509Certificate[] certs = null;
try {
certs = proxyHandler.getSSLClientCertificateChain(
request.getRequest());
} catch (CertificateException ce) {
log.error(sm.getString(
"coyoteAdapter.proxyAuthCertError"),
ce);
}
if (certs != null) {
request.setAttribute(Globals.CERTIFICATES_ATTR,
certs);
}
}
// END S1AS 6188932
response.addHeader("Server",serverName);
// Calling the container
connector.getContainer().invoke(request, response);
}
/* GlassFish Issue 79
response.finishResponse();
req.action( ActionCode.ACTION_POST_REQUEST , null);
} catch (IOException e) {
;
} catch (Throwable t) {
log.error(sm.getString("coyoteAdapter.service"), t);
} finally {
// Recycle the wrapper request and response
request.recycle();
response.recycle();
}*/
// START GlassFish Issue 798
} catch (IOException e) {
// Recycle the wrapper request and response
request.recycle();
response.recycle();
} catch (Throwable t) {
log.error(sm.getString("coyoteAdapter.service"), t);
// Recycle the wrapper request and response
request.recycle();
response.recycle();
}
// END GlassFish Issue 798
if ( compatWithTomcat ) {
afterService(req,res);
}
| public void | setCompatWithTomcat(boolean compatWithTomcat)true 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.
this.compatWithTomcat = compatWithTomcat;
// Add server header
if (compatWithTomcat){
serverName = "Apache/" + serverName;
} else {
// Recalculate.
serverName = System.getProperty("product.name");
}
|
|