JBossWeb SVN: r1375 - trunk/java/org/apache/catalina/startup.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2010-01-27 09:57:29 -0500 (Wed, 27 Jan 2010)
New Revision: 1375
Modified:
trunk/java/org/apache/catalina/startup/CatalinaProperties.java
Log:
- Also look for catalina.properties without the nesting.
Modified: trunk/java/org/apache/catalina/startup/CatalinaProperties.java
===================================================================
--- trunk/java/org/apache/catalina/startup/CatalinaProperties.java 2010-01-27 00:09:20 UTC (rev 1374)
+++ trunk/java/org/apache/catalina/startup/CatalinaProperties.java 2010-01-27 14:57:29 UTC (rev 1375)
@@ -107,6 +107,14 @@
if (is == null) {
try {
+ is = CatalinaProperties.class.getResourceAsStream("catalina.properties");
+ } catch (Throwable t) {
+ // Ignore
+ }
+ }
+
+ if (is == null) {
+ try {
is = CatalinaProperties.class.getResourceAsStream
("/org/apache/catalina/startup/catalina.properties");
} catch (Throwable t) {
14 years, 11 months
JBossWeb SVN: r1374 - tags.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2010-01-26 19:09:20 -0500 (Tue, 26 Jan 2010)
New Revision: 1374
Added:
tags/JBOSSWEB_3_0_0_BETA2/
Log:
- Beta 2.
Copied: tags/JBOSSWEB_3_0_0_BETA2 (from rev 1373, trunk)
14 years, 11 months
JBossWeb SVN: r1373 - in sandbox/webapps: src and 1 other directory.
by jbossweb-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2010-01-26 17:30:01 -0500 (Tue, 26 Jan 2010)
New Revision: 1373
Added:
sandbox/webapps/src/TestAsyncServlet.java
Modified:
sandbox/webapps/build.xml
Log:
Add AsyncServlet example.
Modified: sandbox/webapps/build.xml
===================================================================
--- sandbox/webapps/build.xml 2010-01-26 14:38:17 UTC (rev 1372)
+++ sandbox/webapps/build.xml 2010-01-26 22:30:01 UTC (rev 1373)
@@ -5,11 +5,11 @@
<!--
<javac srcdir="src" classpath="/home/jfclere/tc6.0.x/output/build/lib/servlet-api.jar" destdir="classes" />
-->
- <javac srcdir="src" debug="on" debuglevel="lines,vars,source" classpath="/home/jfclere/apache-tomcat-5.5.17/common/lib/servlet-api.jar" destdir="classes" excludes="Comet*,MyServletContextListener*" />
+ <javac srcdir="src" debug="on" debuglevel="lines,vars,source" classpath="/home/jfclere/apache-tomcat-5.5.17/common/lib/servlet-api.jar" destdir="classes" excludes="Comet*,MyServletContextListener*,TestAsync*" />
- <javac srcdir="src" debug="on" debuglevel="lines,vars,source" classpath="/home/jfclere/jbossweb_trunk/output/jars/jbossweb.jar:/home/jfclere/jbossweb_trunk/output/jars/servlet-api.jar" destdir="classes" includes="Comet*,MyServletContextListener*" />
+ <javac srcdir="src" debug="on" debuglevel="lines,vars,source" classpath="/home/jfclere/jbossweb_trunk/output/jars/jbossweb.jar:/home/jfclere/jbossweb_trunk/output/jars/servlet-api.jar" destdir="classes" includes="Comet*,MyServletContextListener*,TestAsync*" />
- <jar destfile="./lib/myservlets.jar" basedir="./classes" excludes="Comet*,MyServletContextListener*" />
+ <jar destfile="./lib/myservlets.jar" basedir="./classes" excludes="Comet*,MyServletContextListener*,TestAsync*" />
<war destfile="myapp.war" webxml="myapp.xml">
<fileset dir="html" />
Added: sandbox/webapps/src/TestAsyncServlet.java
===================================================================
--- sandbox/webapps/src/TestAsyncServlet.java (rev 0)
+++ sandbox/webapps/src/TestAsyncServlet.java 2010-01-26 22:30:01 UTC (rev 1373)
@@ -0,0 +1,79 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+
+// package org.jboss.web.comet;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.servlet.annotation.WebServlet;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import javax.servlet.AsyncListener;
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+
+/* Asynchronous example */
+// @WebServlet("/TestAsyncServlet")
+@WebServlet(urlPatterns = {"/TestAsyncServlet"}, asyncSupported = true)
+public class TestAsyncServlet extends HttpServlet {
+ public void doPost(HttpServletRequest req, HttpServletResponse res) {
+ // Servlet Code
+ // ...........
+ // Call startAsync
+ AsyncContext context = req.startAsync();
+ // Give AsyncContext to the Listener MonListener
+ context.addListener(new MonListener());
+ // ...........
+ }
+ public class MonListener implements AsyncListener {
+
+ public void onComplete(AsyncEvent event) {
+ System.out.println("onComplete");
+ }
+ public void onError(AsyncEvent event) {
+ ServletResponse res = event.getSuppliedResponse();
+ System.out.println("onError: " + res);
+ }
+ public void onTimeout(AsyncEvent event) {
+ ServletResponse res = event.getSuppliedResponse();
+ try {
+ ServletOutputStream os = res.getOutputStream();
+ System.out.println("onTimeout: " + res);
+ } catch (Exception e) {
+ }
+ }
+ public void onStartAsync(AsyncEvent event) {
+ System.out.println("onStartAsync");
+ }
+ }
+
+}
14 years, 11 months
JBossWeb SVN: r1372 - in sandbox/webapps: src and 1 other directory.
by jbossweb-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2010-01-26 09:38:17 -0500 (Tue, 26 Jan 2010)
New Revision: 1372
Added:
sandbox/webapps/src/MyServletContextListener.java
Modified:
sandbox/webapps/build.xml
Log:
Add an example of dynamic add of a servlet.
Modified: sandbox/webapps/build.xml
===================================================================
--- sandbox/webapps/build.xml 2010-01-26 14:37:25 UTC (rev 1371)
+++ sandbox/webapps/build.xml 2010-01-26 14:38:17 UTC (rev 1372)
@@ -5,11 +5,11 @@
<!--
<javac srcdir="src" classpath="/home/jfclere/tc6.0.x/output/build/lib/servlet-api.jar" destdir="classes" />
-->
- <javac srcdir="src" debug="on" debuglevel="lines,vars,source" classpath="/home/jfclere/apache-tomcat-5.5.17/common/lib/servlet-api.jar" destdir="classes" excludes="Comet*" />
+ <javac srcdir="src" debug="on" debuglevel="lines,vars,source" classpath="/home/jfclere/apache-tomcat-5.5.17/common/lib/servlet-api.jar" destdir="classes" excludes="Comet*,MyServletContextListener*" />
- <javac srcdir="src" debug="on" debuglevel="lines,vars,source" classpath="/home/jfclere/jbossweb_trunk/output/jars/jbossweb.jar:/home/jfclere/jbossweb_trunk/output/jars/servlet-api.jar" destdir="classes" includes="Comet*" />
+ <javac srcdir="src" debug="on" debuglevel="lines,vars,source" classpath="/home/jfclere/jbossweb_trunk/output/jars/jbossweb.jar:/home/jfclere/jbossweb_trunk/output/jars/servlet-api.jar" destdir="classes" includes="Comet*,MyServletContextListener*" />
- <jar destfile="./lib/myservlets.jar" basedir="./classes" excludes="Comet*" />
+ <jar destfile="./lib/myservlets.jar" basedir="./classes" excludes="Comet*,MyServletContextListener*" />
<war destfile="myapp.war" webxml="myapp.xml">
<fileset dir="html" />
@@ -61,5 +61,8 @@
<war destfile="comet.war" needxmlfile="false" >
<classes dir="./classes" includes="Comet*" />
</war>
+ <war destfile="all.war" needxmlfile="false" >
+ <classes dir="./classes"/>
+ </war>
</target>
</project>
Added: sandbox/webapps/src/MyServletContextListener.java
===================================================================
--- sandbox/webapps/src/MyServletContextListener.java (rev 0)
+++ sandbox/webapps/src/MyServletContextListener.java 2010-01-26 14:38:17 UTC (rev 1372)
@@ -0,0 +1,56 @@
+/*
+ * Copyright(c) 2010 Red Hat Middleware, LLC,
+ * and individual contributors as indicated by the @authors tag.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library in the file COPYING.LIB;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * @author Jean-Frederic Clere
+ * @version $Revision: 420067 $, $Date: 2006-07-08 09:16:58 +0200 (sub, 08 srp 2006) $
+ */
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import javax.servlet.*;
+import javax.servlet.http.*;
+
+import javax.servlet.annotation.WebListener;
+
+/**
+ * ServletContextListener that adds a Servlet.
+ * 3.0 example.
+ */
+
+@WebListener
+public class MyServletContextListener implements ServletContextListener {
+ public void contextInitialized(ServletContextEvent event) {
+ System.out.println("contextInitialized");
+ try {
+ ServletContext sc = event.getServletContext();
+ Class<MyCount> servletCl = (Class<MyCount>) Class.forName("MyCount");
+ MyCount servlet = sc.createServlet(servletCl);
+ ServletRegistration.Dynamic sr = (ServletRegistration.Dynamic) sc.addServlet("test.MyCount", servlet);
+ sr.addMapping("/newMyCount");
+ } catch (Exception e) {
+ System.out.println("contextInitialized failed: " + e);
+ }
+ }
+ public void contextDestroyed(ServletContextEvent event) {
+ System.out.println("contextDestroyed");
+ }
+}
14 years, 11 months
JBossWeb SVN: r1371 - sandbox/webapps/src.
by jbossweb-commits@lists.jboss.org
Author: jfrederic.clere(a)jboss.com
Date: 2010-01-26 09:37:25 -0500 (Tue, 26 Jan 2010)
New Revision: 1371
Modified:
sandbox/webapps/src/MyCookies.java
Log:
Print headers...
Modified: sandbox/webapps/src/MyCookies.java
===================================================================
--- sandbox/webapps/src/MyCookies.java 2010-01-26 14:34:32 UTC (rev 1370)
+++ sandbox/webapps/src/MyCookies.java 2010-01-26 14:37:25 UTC (rev 1371)
@@ -65,6 +65,13 @@
response.setContentType("text/html");
+ Enumeration names = request.getHeaderNames();
+ System.out.println("Header: " + names);
+ while (names.hasMoreElements()) {
+ String name = (String) names.nextElement();
+ System.out.println("Header: " + name + " : " + request.getHeader(name));
+ }
+
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<body bgcolor=\"white\">");
14 years, 11 months
JBossWeb SVN: r1370 - in trunk: webapps/docs and 1 other directories.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2010-01-26 09:34:32 -0500 (Tue, 26 Jan 2010)
New Revision: 1370
Added:
trunk/java/org/apache/catalina/valves/RemoteIpValve.java
Modified:
trunk/java/org/apache/catalina/valves/LocalStrings.properties
trunk/java/org/apache/catalina/valves/ValveBase.java
trunk/java/org/apache/catalina/valves/mbeans-descriptors.xml
trunk/webapps/docs/changelog.xml
trunk/webapps/docs/config/valve.xml
Log:
- Port mod_remoteip clone.
Modified: trunk/java/org/apache/catalina/valves/LocalStrings.properties
===================================================================
--- trunk/java/org/apache/catalina/valves/LocalStrings.properties 2010-01-26 13:04:28 UTC (rev 1369)
+++ trunk/java/org/apache/catalina/valves/LocalStrings.properties 2010-01-26 14:34:32 UTC (rev 1370)
@@ -26,6 +26,9 @@
errorReportValve.note=note
errorReportValve.rootCauseInLogs=The full stack trace of the root cause is available in the {0} logs.
+# Remote IP valve
+remoteIpValve.syntax=Invalid regular expressions [{0}] provided.
+
# HTTP status reports
http.100=The client may continue ({0}).
http.101=The server is switching protocols according to the "Upgrade" header ({0}).
Added: trunk/java/org/apache/catalina/valves/RemoteIpValve.java
===================================================================
--- trunk/java/org/apache/catalina/valves/RemoteIpValve.java (rev 0)
+++ trunk/java/org/apache/catalina/valves/RemoteIpValve.java 2010-01-26 14:34:32 UTC (rev 1370)
@@ -0,0 +1,708 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.valves;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import javax.servlet.ServletException;
+
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * <p>
+ * Tomcat port of <a href="http://httpd.apache.org/docs/trunk/mod/mod_remoteip.html">mod_remoteip</a>, this valve replaces the apparent
+ * client remote IP address and hostname for the request with the IP address list presented by a proxy or a load balancer via a request
+ * headers (e.g. "X-Forwarded-For").
+ * </p>
+ * <p>
+ * Another feature of this valve is to replace the apparent scheme (http/https) and server port with the scheme presented by a proxy or a
+ * load balancer via a request header (e.g. "X-Forwarded-Proto").
+ * </p>
+ * <p>
+ * This valve proceeds as follows:
+ * </p>
+ * <p>
+ * If the incoming <code>request.getRemoteAddr()</code> matches the valve's list of internal proxies :
+ * <ul>
+ * <li>Loop on the comma delimited list of IPs and hostnames passed by the preceding load balancer or proxy in the given request's Http
+ * header named <code>$remoteIPHeader</code> (default value <code>x-forwarded-for</code>). Values are processed in right-to-left order.</li>
+ * <li>For each ip/host of the list:
+ * <ul>
+ * <li>if it matches the internal proxies list, the ip/host is swallowed</li>
+ * <li>if it matches the trusted proxies list, the ip/host is added to the created proxies header</li>
+ * <li>otherwise, the ip/host is declared to be the remote ip and looping is stopped.</li>
+ * </ul>
+ * </li>
+ * <li>If the request http header named <code>$protocolHeader</code> (e.g. <code>x-forwarded-for</code>) equals to the value of
+ * <code>protocolHeaderHttpsValue</code> configuration parameter (default <code>https</code>) then <code>request.isSecure = true</code>,
+ * <code>request.scheme = https</code> and <code>request.serverPort = 443</code>. Note that 443 can be overwritten with the
+ * <code>$httpsServerPort</code> configuration parameter.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * <strong>Configuration parameters:</strong>
+ * <table border="1">
+ * <tr>
+ * <th>RemoteIpValve property</th>
+ * <th>Description</th>
+ * <th>Equivalent mod_remoteip directive</th>
+ * <th>Format</th>
+ * <th>Default Value</th>
+ * </tr>
+ * <tr>
+ * <td>remoteIPHeader</td>
+ * <td>Name of the Http Header read by this valve that holds the list of traversed IP addresses starting from the requesting client</td>
+ * <td>RemoteIPHeader</td>
+ * <td>Compliant http header name</td>
+ * <td>x-forwarded-for</td>
+ * </tr>
+ * <tr>
+ * <td>internalProxies</td>
+ * <td>List of internal proxies ip adress. If they appear in the <code>remoteIpHeader</code> value, they will be trusted and will not appear
+ * in the <code>proxiesHeader</code> value</td>
+ * <td>RemoteIPInternalProxy</td>
+ * <td>Comma delimited list of regular expressions (in the syntax supported by the {@link java.util.regex.Pattern} library)</td>
+ * <td>10\.\d{1,3}\.\d{1,3}\.\d{1,3}, 192\.168\.\d{1,3}\.\d{1,3}, 169\.254\.\d{1,3}\.\d{1,3}, 127\.\d{1,3}\.\d{1,3}\.\d{1,3} <br/>
+ * By default, 10/8, 192.168/16, 169.254/16 and 127/8 are allowed ; 172.16/12 has not been enabled by default because it is complex to
+ * describe with regular expressions</td>
+ * </tr>
+ * </tr>
+ * <tr>
+ * <td>proxiesHeader</td>
+ * <td>Name of the http header created by this valve to hold the list of proxies that have been processed in the incoming
+ * <code>remoteIPHeader</code></td>
+ * <td>RemoteIPProxiesHeader</td>
+ * <td>Compliant http header name</td>
+ * <td>x-forwarded-by</td>
+ * </tr>
+ * <tr>
+ * <td>trustedProxies</td>
+ * <td>List of trusted proxies ip adress. If they appear in the <code>remoteIpHeader</code> value, they will be trusted and will appear
+ * in the <code>proxiesHeader</code> value</td>
+ * <td>RemoteIPTrustedProxy</td>
+ * <td>Comma delimited list of regular expressions (in the syntax supported by the {@link java.util.regex.Pattern} library)</td>
+ * <td> </td>
+ * </tr>
+ * <tr>
+ * <td>protocolHeader</td>
+ * <td>Name of the http header read by this valve that holds the flag that this request </td>
+ * <td>N/A</td>
+ * <td>Compliant http header name like <code>X-Forwarded-Proto</code>, <code>X-Forwarded-Ssl</code> or <code>Front-End-Https</code></td>
+ * <td><code>null</code></td>
+ * </tr>
+ * <tr>
+ * <td>protocolHeaderHttpsValue</td>
+ * <td>Value of the <code>protocolHeader</code> to indicate that it is an Https request</td>
+ * <td>N/A</td>
+ * <td>String like <code>https</code> or <code>ON</code></td>
+ * <td><code>https</code></td>
+ * </tr>
+ * <tr>
+ * </table>
+ * </p>
+ * <p>
+ * <p>
+ * This Valve may be attached to any Container, depending on the granularity of the filtering you wish to perform.
+ * </p>
+ * <p>
+ * <strong>Regular expression vs. IP address blocks:</strong> <code>mod_remoteip</code> allows to use address blocks (e.g.
+ * <code>192.168/16</code>) to configure <code>RemoteIPInternalProxy</code> and <code>RemoteIPTrustedProxy</code> ; as Tomcat doesn't have a
+ * library similar to <a
+ * href="http://apr.apache.org/docs/apr/1.3/group__apr__network__io.html#gb74d21b8...">apr_ipsubnet_test</a>,
+ * <code>RemoteIpValve</code> uses regular expression to configure <code>internalProxies</code> and <code>trustedProxies</code> in the same
+ * fashion as {@link RequestFilterValve} does.
+ * </p>
+ * <hr/>
+ * <p>
+ * <strong>Sample with internal proxies</strong>
+ * </p>
+ * <p>
+ * RemoteIpValve configuration:
+ * </p>
+ * <code><pre>
+ * <Valve
+ * className="org.apache.catalina.connector.RemoteIpValve"
+ * internalProxies="192\.168\.0\.10, 192\.168\.0\.11"
+ * remoteIPHeader="x-forwarded-for"
+ * remoteIPProxiesHeader="x-forwarded-by"
+ * protocolHeader="x-forwarded-proto"
+ * /></pre></code>
+ * <p>
+ * Request values:
+ * <table border="1">
+ * <tr>
+ * <th>property</th>
+ * <th>Value Before RemoteIpValve</th>
+ * <th>Value After RemoteIpValve</th>
+ * </tr>
+ * <tr>
+ * <td>request.remoteAddr</td>
+ * <td>192.168.0.10</td>
+ * <td>140.211.11.130</td>
+ * </tr>
+ * <tr>
+ * <td>request.header['x-forwarded-for']</td>
+ * <td>140.211.11.130, 192.168.0.10</td>
+ * <td>null</td>
+ * </tr>
+ * <tr>
+ * <td>request.header['x-forwarded-by']</td>
+ * <td>null</td>
+ * <td>null</td>
+ * </tr>
+ * <tr>
+ * <td>request.header['x-forwarded-proto']</td>
+ * <td>https</td>
+ * <td>https</td>
+ * </tr>
+ * <tr>
+ * <td>request.scheme</td>
+ * <td>http</td>
+ * <td>https</td>
+ * </tr>
+ * <tr>
+ * <td>request.secure</td>
+ * <td>false</td>
+ * <td>true</td>
+ * </tr>
+ * <tr>
+ * <td>request.serverPort</td>
+ * <td>80</td>
+ * <td>443</td>
+ * </tr>
+ * </table>
+ * Note : <code>x-forwarded-by</code> header is null because only internal proxies as been traversed by the request.
+ * <code>x-forwarded-by</code> is null because all the proxies are trusted or internal.
+ * </p>
+ * <hr/>
+ * <p>
+ * <strong>Sample with trusted proxies</strong>
+ * </p>
+ * <p>
+ * RemoteIpValve configuration:
+ * </p>
+ * <code><pre>
+ * <Valve
+ * className="org.apache.catalina.connector.RemoteIpValve"
+ * internalProxies="192\.168\.0\.10, 192\.168\.0\.11"
+ * remoteIPHeader="x-forwarded-for"
+ * remoteIPProxiesHeader="x-forwarded-by"
+ * trustedProxies="proxy1, proxy2"
+ * /></pre></code>
+ * <p>
+ * Request values:
+ * <table border="1">
+ * <tr>
+ * <th>property</th>
+ * <th>Value Before RemoteIpValve</th>
+ * <th>Value After RemoteIpValve</th>
+ * </tr>
+ * <tr>
+ * <td>request.remoteAddr</td>
+ * <td>192.168.0.10</td>
+ * <td>140.211.11.130</td>
+ * </tr>
+ * <tr>
+ * <td>request.header['x-forwarded-for']</td>
+ * <td>140.211.11.130, proxy1, proxy2</td>
+ * <td>null</td>
+ * </tr>
+ * <tr>
+ * <td>request.header['x-forwarded-by']</td>
+ * <td>null</td>
+ * <td>proxy1, proxy2</td>
+ * </tr>
+ * </table>
+ * Note : <code>proxy1</code> and <code>proxy2</code> are both trusted proxies that come in <code>x-forwarded-for</code> header, they both
+ * are migrated in <code>x-forwarded-by</code> header. <code>x-forwarded-by</code> is null because all the proxies are trusted or internal.
+ * </p>
+ * <hr/>
+ * <p>
+ * <strong>Sample with internal and trusted proxies</strong>
+ * </p>
+ * <p>
+ * RemoteIpValve configuration:
+ * </p>
+ * <code><pre>
+ * <Valve
+ * className="org.apache.catalina.connector.RemoteIpValve"
+ * internalProxies="192\.168\.0\.10, 192\.168\.0\.11"
+ * remoteIPHeader="x-forwarded-for"
+ * remoteIPProxiesHeader="x-forwarded-by"
+ * trustedProxies="proxy1, proxy2"
+ * /></pre></code>
+ * <p>
+ * Request values:
+ * <table border="1">
+ * <tr>
+ * <th>property</th>
+ * <th>Value Before RemoteIpValve</th>
+ * <th>Value After RemoteIpValve</th>
+ * </tr>
+ * <tr>
+ * <td>request.remoteAddr</td>
+ * <td>192.168.0.10</td>
+ * <td>140.211.11.130</td>
+ * </tr>
+ * <tr>
+ * <td>request.header['x-forwarded-for']</td>
+ * <td>140.211.11.130, proxy1, proxy2, 192.168.0.10</td>
+ * <td>null</td>
+ * </tr>
+ * <tr>
+ * <td>request.header['x-forwarded-by']</td>
+ * <td>null</td>
+ * <td>proxy1, proxy2</td>
+ * </tr>
+ * </table>
+ * Note : <code>proxy1</code> and <code>proxy2</code> are both trusted proxies that come in <code>x-forwarded-for</code> header, they both
+ * are migrated in <code>x-forwarded-by</code> header. As <code>192.168.0.10</code> is an internal proxy, it does not appear in
+ * <code>x-forwarded-by</code>. <code>x-forwarded-by</code> is null because all the proxies are trusted or internal.
+ * </p>
+ * <hr/>
+ * <p>
+ * <strong>Sample with an untrusted proxy</strong>
+ * </p>
+ * <p>
+ * RemoteIpValve configuration:
+ * </p>
+ * <code><pre>
+ * <Valve
+ * className="org.apache.catalina.connector.RemoteIpValve"
+ * internalProxies="192\.168\.0\.10, 192\.168\.0\.11"
+ * remoteIPHeader="x-forwarded-for"
+ * remoteIPProxiesHeader="x-forwarded-by"
+ * trustedProxies="proxy1, proxy2"
+ * /></pre></code>
+ * <p>
+ * Request values:
+ * <table border="1">
+ * <tr>
+ * <th>property</th>
+ * <th>Value Before RemoteIpValve</th>
+ * <th>Value After RemoteIpValve</th>
+ * </tr>
+ * <tr>
+ * <td>request.remoteAddr</td>
+ * <td>192.168.0.10</td>
+ * <td>untrusted-proxy</td>
+ * </tr>
+ * <tr>
+ * <td>request.header['x-forwarded-for']</td>
+ * <td>140.211.11.130, untrusted-proxy, proxy1</td>
+ * <td>140.211.11.130</td>
+ * </tr>
+ * <tr>
+ * <td>request.header['x-forwarded-by']</td>
+ * <td>null</td>
+ * <td>proxy1</td>
+ * </tr>
+ * </table>
+ * Note : <code>x-forwarded-by</code> holds the trusted proxy <code>proxy1</code>. <code>x-forwarded-by</code> holds
+ * <code>140.211.11.130</code> because <code>untrusted-proxy</code> is not trusted and thus, we can not trust that
+ * <code>untrusted-proxy</code> is the actual remote ip. <code>request.remoteAddr</code> is <code>untrusted-proxy</code> that is an IP
+ * verified by <code>proxy1</code>.
+ * </p>
+ */
+public class RemoteIpValve extends ValveBase {
+
+ /**
+ * {@link Pattern} for a comma delimited string that support whitespace characters
+ */
+ private static final Pattern commaSeparatedValuesPattern = Pattern.compile("\\s*,\\s*");
+
+ /**
+ * The descriptive information related to this implementation.
+ */
+ private static final String info = "org.apache.catalina.connector.RemoteIpValve/1.0";
+
+ /**
+ * Logger
+ */
+ private static org.jboss.logging.Logger log =
+ org.jboss.logging.Logger.getLogger(RemoteIpValve.class);
+
+ /**
+ * The StringManager for this package.
+ */
+ protected static StringManager sm = StringManager.getManager(Constants.Package);
+
+ /**
+ * Convert a given comma delimited list of regular expressions into an array of compiled {@link Pattern}
+ *
+ * @return array of patterns (not <code>null</code>)
+ */
+ protected static Pattern[] commaDelimitedListToPatternArray(String commaDelimitedPatterns) {
+ String[] patterns = commaDelimitedListToStringArray(commaDelimitedPatterns);
+ List<Pattern> patternsList = new ArrayList<Pattern>();
+ for (String pattern : patterns) {
+ try {
+ patternsList.add(Pattern.compile(pattern));
+ } catch (PatternSyntaxException e) {
+ throw new IllegalArgumentException(sm.getString("remoteIpValve.syntax", pattern), e);
+ }
+ }
+ return patternsList.toArray(new Pattern[0]);
+ }
+
+ /**
+ * Convert a given comma delimited list of regular expressions into an array of String
+ *
+ * @return array of patterns (non <code>null</code>)
+ */
+ protected static String[] commaDelimitedListToStringArray(String commaDelimitedStrings) {
+ return (commaDelimitedStrings == null || commaDelimitedStrings.length() == 0) ? new String[0] : commaSeparatedValuesPattern
+ .split(commaDelimitedStrings);
+ }
+
+ /**
+ * Convert an array of strings in a comma delimited string
+ */
+ protected static String listToCommaDelimitedString(List<String> stringList) {
+ if (stringList == null) {
+ return "";
+ }
+ StringBuilder result = new StringBuilder();
+ for (Iterator<String> it = stringList.iterator(); it.hasNext();) {
+ Object element = it.next();
+ if (element != null) {
+ result.append(element);
+ if (it.hasNext()) {
+ result.append(", ");
+ }
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Return <code>true</code> if the given <code>str</code> matches at least one of the given <code>patterns</code>.
+ */
+ protected static boolean matchesOne(String str, Pattern... patterns) {
+ for (Pattern pattern : patterns) {
+ if (pattern.matcher(str).matches()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @see #setHttpsServerPort(int)
+ */
+ private int httpsServerPort = 443;
+
+ /**
+ * @see #setInternalProxies(String)
+ */
+ private Pattern[] internalProxies = new Pattern[] {
+ Pattern.compile("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"), Pattern.compile("192\\.168\\.\\d{1,3}\\.\\d{1,3}"),
+ Pattern.compile("169\\.254\\.\\d{1,3}\\.\\d{1,3}"), Pattern.compile("127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")
+ };
+
+ /**
+ * @see #setProtocolHeader(String)
+ */
+ private String protocolHeader = null;
+
+ /**
+ * @see #setProtocolHeaderHttpsValue(String)
+ */
+ private String protocolHeaderHttpsValue = "https";
+
+ /**
+ * @see #setProxiesHeader(String)
+ */
+ private String proxiesHeader = "X-Forwarded-By";
+
+ /**
+ * @see #setRemoteIpHeader(String)
+ */
+ private String remoteIpHeader = "X-Forwarded-For";
+
+ /**
+ * @see RemoteIpValve#setTrustedProxies(String)
+ */
+ private Pattern[] trustedProxies = new Pattern[0];
+
+ public int getHttpsServerPort() {
+ return httpsServerPort;
+ }
+
+ /**
+ * Return descriptive information about this Valve implementation.
+ */
+ @Override
+ public String getInfo() {
+ return info;
+ }
+
+ /**
+ * @see #setInternalProxies(String)
+ * @return comma delimited list of internal proxies
+ */
+ public String getInternalProxies() {
+ List<String> internalProxiesAsStringList = new ArrayList<String>();
+ for (Pattern internalProxyPattern : internalProxies) {
+ internalProxiesAsStringList.add(String.valueOf(internalProxyPattern));
+ }
+ return listToCommaDelimitedString(internalProxiesAsStringList);
+ }
+
+ /**
+ * @see #setProtocolHeader(String)
+ * @return the protocol header (e.g. "X-Forwarded-Proto")
+ */
+ public String getProtocolHeader() {
+ return protocolHeader;
+ }
+
+ /**
+ * @see RemoteIpValve#setProtocolHeaderHttpsValue(String)
+ * @return the value of the protocol header for incoming https request (e.g. "https")
+ */
+ public String getProtocolHeaderHttpsValue() {
+ return protocolHeaderHttpsValue;
+ }
+
+ /**
+ * @see #setProxiesHeader(String)
+ * @return the proxies header name (e.g. "X-Forwarded-By")
+ */
+ public String getProxiesHeader() {
+ return proxiesHeader;
+ }
+
+ /**
+ * @see #setRemoteIpHeader(String)
+ * @return the remote IP header name (e.g. "X-Forwarded-For")
+ */
+ public String getRemoteIpHeader() {
+ return remoteIpHeader;
+ }
+
+ /**
+ * @see #setTrustedProxies(String)
+ * @return comma delimited list of trusted proxies
+ */
+ public String getTrustedProxies() {
+ List<String> trustedProxiesAsStringList = new ArrayList<String>();
+ for (Pattern trustedProxy : trustedProxies) {
+ trustedProxiesAsStringList.add(String.valueOf(trustedProxy));
+ }
+ return listToCommaDelimitedString(trustedProxiesAsStringList);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void invoke(Request request, Response response) throws IOException, ServletException {
+ final String originalRemoteAddr = request.getRemoteAddr();
+ final String originalRemoteHost = request.getRemoteHost();
+ final String originalScheme = request.getScheme();
+ final boolean originalSecure = request.isSecure();
+ final int originalServerPort = request.getServerPort();
+
+ if (matchesOne(originalRemoteAddr, internalProxies)) {
+ String remoteIp = null;
+ // In java 6, proxiesHeaderValue should be declared as a java.util.Deque
+ LinkedList<String> proxiesHeaderValue = new LinkedList<String>();
+
+ String[] remoteIPHeaderValue = commaDelimitedListToStringArray(request.getHeader(remoteIpHeader));
+ int idx;
+ // loop on remoteIPHeaderValue to find the first trusted remote ip and to build the proxies chain
+ for (idx = remoteIPHeaderValue.length - 1; idx >= 0; idx--) {
+ String currentRemoteIp = remoteIPHeaderValue[idx];
+ remoteIp = currentRemoteIp;
+ if (matchesOne(currentRemoteIp, internalProxies)) {
+ // do nothing, internalProxies IPs are not appended to the
+ } else if (matchesOne(currentRemoteIp, trustedProxies)) {
+ proxiesHeaderValue.addFirst(currentRemoteIp);
+ } else {
+ idx--; // decrement idx because break statement doesn't do it
+ break;
+ }
+ }
+ // continue to loop on remoteIPHeaderValue to build the new value of the remoteIPHeader
+ LinkedList<String> newRemoteIpHeaderValue = new LinkedList<String>();
+ for (; idx >= 0; idx--) {
+ String currentRemoteIp = remoteIPHeaderValue[idx];
+ newRemoteIpHeaderValue.addFirst(currentRemoteIp);
+ }
+ if (remoteIp != null) {
+
+ request.setRemoteAddr(remoteIp);
+ request.setRemoteHost(remoteIp);
+
+ // use request.coyoteRequest.mimeHeaders.setValue(str).setString(str) because request.addHeader(str, str) is no-op in Tomcat
+ // 6.0
+ if (proxiesHeaderValue.size() == 0) {
+ request.getCoyoteRequest().getMimeHeaders().removeHeader(proxiesHeader);
+ } else {
+ String commaDelimitedListOfProxies = listToCommaDelimitedString(proxiesHeaderValue);
+ request.getCoyoteRequest().getMimeHeaders().setValue(proxiesHeader).setString(commaDelimitedListOfProxies);
+ }
+ if (newRemoteIpHeaderValue.size() == 0) {
+ request.getCoyoteRequest().getMimeHeaders().removeHeader(remoteIpHeader);
+ } else {
+ String commaDelimitedRemoteIpHeaderValue = listToCommaDelimitedString(newRemoteIpHeaderValue);
+ request.getCoyoteRequest().getMimeHeaders().setValue(remoteIpHeader).setString(commaDelimitedRemoteIpHeaderValue);
+ }
+ }
+
+ if (protocolHeader != null) {
+ String protocolHeaderValue = request.getHeader(protocolHeader);
+ if (protocolHeaderValue != null && protocolHeaderHttpsValue.equalsIgnoreCase(protocolHeaderValue)) {
+ request.setSecure(true);
+ // use request.coyoteRequest.scheme instead of request.setScheme() because request.setScheme() is no-op in Tomcat 6.0
+ request.getCoyoteRequest().scheme().setString("https");
+
+ request.setServerPort(httpsServerPort);
+ }
+ }
+
+ if (log.isDebugEnabled()) {
+ log.debug("Incoming request " + request.getRequestURI() + " with originalRemoteAddr '" + originalRemoteAddr
+ + "', originalRemoteHost='" + originalRemoteHost + "', originalSecure='" + originalSecure + "', originalScheme='"
+ + originalScheme + "' will be seen as newRemoteAddr='" + request.getRemoteAddr() + "', newRemoteHost='"
+ + request.getRemoteHost() + "', newScheme='" + request.getScheme() + "', newSecure='" + request.isSecure() + "'");
+ }
+ }
+ try {
+ getNext().invoke(request, response);
+ } finally {
+ request.setRemoteAddr(originalRemoteAddr);
+ request.setRemoteHost(originalRemoteHost);
+
+ request.setSecure(originalSecure);
+
+ // use request.coyoteRequest.scheme instead of request.setScheme() because request.setScheme() is no-op in Tomcat 6.0
+ request.getCoyoteRequest().scheme().setString(originalScheme);
+
+ request.setServerPort(originalServerPort);
+ }
+ }
+
+ /**
+ * <p>
+ * Server Port value if the {@link #protocolHeader} indicates HTTPS
+ * </p>
+ * <p>
+ * Default value : 443
+ * </p>
+ */
+ public void setHttpsServerPort(int httpsServerPort) {
+ this.httpsServerPort = httpsServerPort;
+ }
+
+ /**
+ * <p>
+ * Comma delimited list of internal proxies. Can be expressed with regular expressions.
+ * </p>
+ * <p>
+ * Default value : 10\.\d{1,3}\.\d{1,3}\.\d{1,3}, 192\.168\.\d{1,3}\.\d{1,3}, 127\.\d{1,3}\.\d{1,3}\.\d{1,3}
+ * </p>
+ */
+ public void setInternalProxies(String commaDelimitedInternalProxies) {
+ this.internalProxies = commaDelimitedListToPatternArray(commaDelimitedInternalProxies);
+ }
+
+ /**
+ * <p>
+ * Header that holds the incoming protocol, usally named <code>X-Forwarded-Proto</code>. If <code>null</code>, request.scheme and
+ * request.secure will not be modified.
+ * </p>
+ * <p>
+ * Default value : <code>null</code>
+ * </p>
+ */
+ public void setProtocolHeader(String protocolHeader) {
+ this.protocolHeader = protocolHeader;
+ }
+
+ /**
+ * <p>
+ * Case insensitive value of the protocol header to indicate that the incoming http request uses SSL.
+ * </p>
+ * <p>
+ * Default value : <code>https</code>
+ * </p>
+ */
+ public void setProtocolHeaderHttpsValue(String protocolHeaderHttpsValue) {
+ this.protocolHeaderHttpsValue = protocolHeaderHttpsValue;
+ }
+
+ /**
+ * <p>
+ * The proxiesHeader directive specifies a header into which mod_remoteip will collect a list of all of the intermediate client IP
+ * addresses trusted to resolve the actual remote IP. Note that intermediate RemoteIPTrustedProxy addresses are recorded in this header,
+ * while any intermediate RemoteIPInternalProxy addresses are discarded.
+ * </p>
+ * <p>
+ * Name of the http header that holds the list of trusted proxies that has been traversed by the http request.
+ * </p>
+ * <p>
+ * The value of this header can be comma delimited.
+ * </p>
+ * <p>
+ * Default value : <code>X-Forwarded-By</code>
+ * </p>
+ */
+ public void setProxiesHeader(String proxiesHeader) {
+ this.proxiesHeader = proxiesHeader;
+ }
+
+ /**
+ * <p>
+ * Name of the http header from which the remote ip is extracted.
+ * </p>
+ * <p>
+ * The value of this header can be comma delimited.
+ * </p>
+ * <p>
+ * Default value : <code>X-Forwarded-For</code>
+ * </p>
+ *
+ * @param remoteIPHeader
+ */
+ public void setRemoteIpHeader(String remoteIpHeader) {
+ this.remoteIpHeader = remoteIpHeader;
+ }
+
+ /**
+ * <p>
+ * Comma delimited list of proxies that are trusted when they appear in the {@link #remoteIPHeader} header. Can be expressed as a
+ * regular expression.
+ * </p>
+ * <p>
+ * Default value : empty list, no external proxy is trusted.
+ * </p>
+ */
+ public void setTrustedProxies(String commaDelimitedTrustedProxies) {
+ this.trustedProxies = commaDelimitedListToPatternArray(commaDelimitedTrustedProxies);
+ }
+}
Modified: trunk/java/org/apache/catalina/valves/ValveBase.java
===================================================================
--- trunk/java/org/apache/catalina/valves/ValveBase.java 2010-01-26 13:04:28 UTC (rev 1369)
+++ trunk/java/org/apache/catalina/valves/ValveBase.java 2010-01-26 14:34:32 UTC (rev 1370)
@@ -56,8 +56,8 @@
public abstract class ValveBase
implements Contained, Valve, MBeanRegistration {
- private static Logger log = Logger.getLogger(ValveBase.class);
+
//------------------------------------------------------ Instance Variables
@@ -303,7 +303,6 @@
parentName=",servlet=" + container.getName() +
",path=" + path + ",host=" + host.getName();
}
- log.debug("valve parent=" + parentName + " " + parent);
String className=this.getClass().getName();
int period = className.lastIndexOf('.');
@@ -318,7 +317,6 @@
}
if( valves[i]!=null &&
valves[i].getClass() == this.getClass() ) {
- log.debug("Duplicate " + valves[i] + " " + this + " " + container);
seq++;
}
}
@@ -327,10 +325,7 @@
ext=",seq=" + seq;
}
- ObjectName objectName =
- new ObjectName( domain + ":type=Valve,name=" + className + ext + parentName);
- log.debug("valve objectname = "+objectName);
- return objectName;
+ return new ObjectName( domain + ":type=Valve,name=" + className + ext + parentName);
}
// -------------------- JMX data --------------------
Modified: trunk/java/org/apache/catalina/valves/mbeans-descriptors.xml
===================================================================
--- trunk/java/org/apache/catalina/valves/mbeans-descriptors.xml 2010-01-26 13:04:28 UTC (rev 1369)
+++ trunk/java/org/apache/catalina/valves/mbeans-descriptors.xml 2010-01-26 14:34:32 UTC (rev 1370)
@@ -339,4 +339,42 @@
</mbean>
+ <mbean name="RemoteIpValve"
+ description="Valve that sets client information (eg IP address) based on data from a trusted proxy"
+ domain="Catalina"
+ group="Valve"
+ type="org.apache.catalina.valves.RemoteIpValve">
+
+ <attribute name="internalProxies"
+ description="Comma delimited list of internal proxies"
+ type="java.lang.String"
+ writeable="false" />
+
+ <attribute name="protocolHeader"
+ description="The protocol header (e.g. "X-Forwarded-Proto")"
+ type="java.lang.String"
+ writeable="false" />
+
+ <attribute name="protocolHeaderHttpsValue"
+ description="The value of the protocol header for incoming https request (e.g. "https")"
+ type="java.lang.String"
+ writeable="false" />
+
+ <attribute name="proxiesHeader"
+ description="The proxies header name (e.g. "X-Forwarded-By")"
+ type="java.lang.String"
+ writeable="false" />
+
+ <attribute name="remoteIpHedaer"
+ description="The remote IP header name (e.g. "X-Forwarded-For")"
+ type="java.lang.String"
+ writeable="false" />
+
+ <attribute name="trustedProxies"
+ description="Comma delimited list of trusted proxies"
+ type="java.lang.String"
+ writeable="false" />
+
+ </mbean>
+
</mbeans-descriptors>
Modified: trunk/webapps/docs/changelog.xml
===================================================================
--- trunk/webapps/docs/changelog.xml 2010-01-26 13:04:28 UTC (rev 1369)
+++ trunk/webapps/docs/changelog.xml 2010-01-26 14:34:32 UTC (rev 1370)
@@ -25,6 +25,9 @@
<fix>
<jira>157</jira>: NPE on host with HTTP/1.0, submitted by Markus Grieder. (remm)
</fix>
+ <add>
+ Remote IP valve. (markt)
+ </add>
</changelog>
</subsection>
<subsection name="Coyote">
Modified: trunk/webapps/docs/config/valve.xml
===================================================================
--- trunk/webapps/docs/config/valve.xml 2010-01-26 13:04:28 UTC (rev 1369)
+++ trunk/webapps/docs/config/valve.xml 2010-01-26 14:34:32 UTC (rev 1370)
@@ -434,6 +434,92 @@
</section>
+<section name="Remote IP Valve">
+
+ <subsection name="Introduction">
+
+ <p>Tomcat port of
+ <a href="http://httpd.apache.org/docs/trunk/mod/mod_remoteip.html">mod_remoteip</a>,
+ this valve replaces the apparent client remote IP address and hostname for
+ the request with the IP address list presented by a proxy or a load balancer
+ via a request headers (e.g. "X-Forwarded-For").</p>
+
+ <p>Another feature of this valve is to replace the apparent scheme
+ (http/https) and server port with the scheme presented by a proxy or a load
+ balancer via a request header (e.g. "X-Forwarded-Proto").</p>
+
+ <p>This Valve may be used at the <code>Engine</code>, <code>Host</code> or
+ <code>Context</code> level as required. Normally, this Valve would be used
+ at the <code>Engine</code> level.</p>
+
+ <p>If used in conjunction with Remote Address/Host valves then this valve
+ should be defined first to ensure that the correct client IP address is
+ presented to the Remote Address/Host valves.</p>
+
+ </subsection>
+
+ <subsection name="Attributes">
+
+ <p>The <strong>Remote IP Valve</strong> supports the
+ following configuration attributes:</p>
+
+ <attributes>
+
+ <attribute name="className" required="true">
+ <p>Java class name of the implementation to use. This MUST be set to
+ <strong>org.apache.catalina.valves.RemoteIpValve</strong>.</p>
+ </attribute>
+
+ <attribute name="remoteIPHeader" required="false">
+ <p>Name of the HTTP Header read by this valve that holds the list of
+ traversed IP addresses starting from the requesting client. If not
+ specified, the default of <code>x-forwarded-for</code> is used.</p>
+ </attribute>
+
+ <attribute name="internalProxies" required="false">
+ <p>List of internal proxies' IP addresses as comma separated regular
+ expressions. If they appear in the <strong>remoteIpHeader</strong>
+ value, they will be trusted and will not appear in the
+ <strong>proxiesHeader</strong> value. If not specified the default value
+ of <code>10\.\d{1,3}\.\d{1,3}\.\d{1,3}, 192\.168\.\d{1,3}\.\d{1,3},
+ 169\.254\.\d{1,3}\.\d{1,3}, 127\.\d{1,3}\.\d{1,3}\.\d{1,3}</code> will
+ be used.</p>
+ </attribute>
+
+ <attribute name="proxiesHeader" required="false">
+ <p>Name of the HTTP header created by this valve to hold the list of
+ proxies that have been processed in the incoming
+ <strong>remoteIpHeader</strong>. If not specified, the default of
+ <code>x-forwarded-by</code> is used.</p>
+ </attribute>
+
+ <attribute name="trustedProxies" required="false">
+ <p>List of trusted proxies' IP addresses as comma separated regular
+ expressions. If they appear in the <strong>remoteIpHeader</strong>
+ value, they will be trusted and will appear in the
+ <strong>proxiesHeader</strong> value. If not specified, no proxies will
+ be trusted.</p>
+ </attribute>
+
+ <attribute name="protocolHeader" required="false">
+ <p>Name of the HTTP Header read by this valve that holds the protocol
+ used by the client to connect to the proxy. If not specified, the
+ default of <code>null</code> is used.</p>
+ </attribute>
+
+ <attribute name="protocolHeaderHttpsValue" required="false">
+ <p>Value of the <strong>protocolHeader</strong> to indicate that it is
+ an HTTPS request. If not specified, the default of <code>https</code> is
+ used.</p>
+ </attribute>
+
+ </attributes>
+
+ </subsection>
+
+</section>
+
+
</body>
14 years, 11 months
JBossWeb SVN: r1369 - trunk/java/org/apache/tomcat/util/modeler.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2010-01-26 08:04:28 -0500 (Tue, 26 Jan 2010)
New Revision: 1369
Modified:
trunk/java/org/apache/tomcat/util/modeler/Registry.java
Log:
- Add guard.
Modified: trunk/java/org/apache/tomcat/util/modeler/Registry.java
===================================================================
--- trunk/java/org/apache/tomcat/util/modeler/Registry.java 2010-01-25 14:15:20 UTC (rev 1368)
+++ trunk/java/org/apache/tomcat/util/modeler/Registry.java 2010-01-26 13:04:28 UTC (rev 1369)
@@ -836,7 +836,9 @@
return;
}
- log.debug( "Found " + dURL);
+ if (log.isDebugEnabled()) {
+ log.debug( "Found " + dURL);
+ }
searchedPaths.put( packageName, dURL );
try {
if( descriptors.endsWith(".xml" ))
14 years, 11 months
JBossWeb SVN: r1368 - trunk/java/org/apache/tomcat/util/http/mapper.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2010-01-25 09:15:20 -0500 (Mon, 25 Jan 2010)
New Revision: 1368
Modified:
trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java
Log:
- The loader flag should be equivalent to Context.object == null.
Modified: trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java
===================================================================
--- trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java 2010-01-25 12:54:58 UTC (rev 1367)
+++ trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java 2010-01-25 14:15:20 UTC (rev 1368)
@@ -238,7 +238,6 @@
Context[] newContexts = new Context[contexts.length + 1];
Context newContext = new Context();
newContext.name = path;
- newContext.loaded = false;
if (insertMap(contexts, newContexts, newContext)) {
host.contextList.contexts = newContexts;
}
@@ -700,7 +699,7 @@
if (mappingData.context == null) {
context = findContext(uri, contexts, nesting);
if (context != null) {
- if (!context.loaded) {
+ if (context.object == null) {
notifyLazyLoadContextMappingListeners(mappedHost, context);
// See if the notification resulted in deploying the context
// First reestablish refs to the host fields as adding
@@ -708,7 +707,7 @@
contexts = mappedHost.contextList.contexts;
nesting = mappedHost.contextList.nesting;
context = findContext(uri, contexts, nesting);
- if (context != null && !context.loaded) {
+ if (context != null && context.object == null) {
// notification did not result in deployment
// don't map to the unloaded context
context = null;
@@ -1386,10 +1385,9 @@
private static final boolean insertLazyLoadedContext(Context[] oldMap, Context[] newMap, Context newElement) {
int pos = find(oldMap, newElement.name);
if ((pos != -1) && (newElement.name.equals(oldMap[pos].name))) {
- if (oldMap[pos].loaded || !newElement.loaded) {
+ if (oldMap[pos].object != null || newElement.object == null) {
return false;
- }
- else {
+ } else {
// We're going to replace the old context in the map, but mark
// it as replaced so anyone with a ref to it knows to look again
oldMap[pos].replaced = true;
@@ -1451,7 +1449,6 @@
public Wrapper[] wildcardWrappers = new Wrapper[0];
public Wrapper[] extensionWrappers = new Wrapper[0];
public int nesting = 0;
- private boolean loaded = true;
private volatile boolean replaced = false;
}
14 years, 11 months
JBossWeb SVN: r1367 - in trunk: webapps/docs and 1 other directory.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2010-01-25 07:54:58 -0500 (Mon, 25 Jan 2010)
New Revision: 1367
Added:
trunk/java/org/apache/tomcat/util/http/mapper/OnDemandContextMappingListener.java
Modified:
trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java
trunk/webapps/docs/changelog.xml
Log:
- Add the plumbing for on demand.
- Submitted by Brian Stansberry.
Modified: trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java
===================================================================
--- trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java 2010-01-25 12:52:45 UTC (rev 1366)
+++ trunk/java/org/apache/tomcat/util/http/mapper/Mapper.java 2010-01-25 12:54:58 UTC (rev 1367)
@@ -51,12 +51,20 @@
*/
protected String defaultHostName = null;
+
/**
* Context associated with this wrapper, used for wrapper mapping.
*/
protected Context context = new Context();
+ /**
+ * Listeners for lazy loading of web applications.
+ */
+ protected final List<OnDemandContextMappingListener> lazyLoadListeners =
+ new ArrayList<OnDemandContextMappingListener>();
+
+
// --------------------------------------------------------- Public Methods
@@ -201,6 +209,46 @@
/**
+ * Add a new undeployed Context to an existing Host.
+ *
+ * @param hostName Virtual host name this context belongs to
+ * @param path Context path
+ */
+ public void addOnDemandContext(String hostName, String path) {
+
+ Host[] hosts = this.hosts;
+ int pos = find(hosts, hostName);
+ if( pos <0 ) {
+ addHost(hostName, new String[0], "");
+ hosts = this.hosts;
+ pos = find(hosts, hostName);
+ }
+ if (pos < 0) {
+ logger.error("No host found: " + hostName);
+ }
+ Host host = hosts[pos];
+ if (host.name.equals(hostName)) {
+ int slashCount = slashCount(path);
+ synchronized (host) {
+ Context[] contexts = host.contextList.contexts;
+ // Update nesting
+ if (slashCount > host.contextList.nesting) {
+ host.contextList.nesting = slashCount;
+ }
+ Context[] newContexts = new Context[contexts.length + 1];
+ Context newContext = new Context();
+ newContext.name = path;
+ newContext.loaded = false;
+ if (insertMap(contexts, newContexts, newContext)) {
+ host.contextList.contexts = newContexts;
+ }
+ }
+ }
+
+ }
+
+
+ /**
* Add a new Context to an existing Host.
*
* @param hostName Virtual host name this context belongs to
@@ -240,6 +288,11 @@
newContext.resources = resources;
if (insertMap(contexts, newContexts, newContext)) {
host.contextList.contexts = newContexts;
+ } else {
+ newContexts = new Context[contexts.length];
+ if (insertLazyLoadedContext(contexts, newContexts, newContext)) {
+ host.contextList.contexts = newContexts;
+ }
}
}
}
@@ -584,6 +637,23 @@
}
+ public void registerOnDemandContextMappingListener(OnDemandContextMappingListener listener) {
+ if (listener != null) {
+ synchronized (lazyLoadListeners) {
+ lazyLoadListeners.add(listener);
+ }
+ }
+ }
+
+ public void removeOnDemandContextMappingListener(OnDemandContextMappingListener listener) {
+ if (listener != null) {
+ synchronized (lazyLoadListeners) {
+ lazyLoadListeners.remove(listener);
+ }
+ }
+ }
+
+
// -------------------------------------------------------- Private Methods
@@ -597,6 +667,7 @@
uri.setLimit(-1);
Context[] contexts = null;
+ Host mappedHost = null;
Context context = null;
int nesting = 0;
@@ -605,18 +676,20 @@
Host[] hosts = this.hosts;
int pos = findIgnoreCase(hosts, host);
if ((pos != -1) && (host.equalsIgnoreCase(hosts[pos].name))) {
- mappingData.host = hosts[pos].object;
- contexts = hosts[pos].contextList.contexts;
- nesting = hosts[pos].contextList.nesting;
+ mappedHost = hosts[pos];
+ mappingData.host = mappedHost.object;
+ contexts = mappedHost.contextList.contexts;
+ nesting = mappedHost.contextList.nesting;
} else {
if (defaultHostName == null) {
return;
}
pos = find(hosts, defaultHostName);
if ((pos != -1) && (defaultHostName.equals(hosts[pos].name))) {
- mappingData.host = hosts[pos].object;
- contexts = hosts[pos].contextList.contexts;
- nesting = hosts[pos].contextList.nesting;
+ mappedHost = hosts[pos];
+ mappingData.host = mappedHost.object;
+ contexts = mappedHost.contextList.contexts;
+ nesting = mappedHost.contextList.nesting;
} else {
return;
}
@@ -625,47 +698,27 @@
// Context mapping
if (mappingData.context == null) {
- int pos = find(contexts, uri);
- if (pos == -1) {
- return;
- }
-
- int lastSlash = -1;
- int uriEnd = uri.getEnd();
- int length = -1;
- boolean found = false;
- while (pos >= 0) {
- if (uri.startsWith(contexts[pos].name)) {
- length = contexts[pos].name.length();
- if (uri.getLength() == length) {
- found = true;
- break;
- } else if (uri.startsWithIgnoreCase("/", length)) {
- found = true;
- break;
+ context = findContext(uri, contexts, nesting);
+ if (context != null) {
+ if (!context.loaded) {
+ notifyLazyLoadContextMappingListeners(mappedHost, context);
+ // See if the notification resulted in deploying the context
+ // First reestablish refs to the host fields as adding
+ // the real context will have changed them
+ contexts = mappedHost.contextList.contexts;
+ nesting = mappedHost.contextList.nesting;
+ context = findContext(uri, contexts, nesting);
+ if (context != null && !context.loaded) {
+ // notification did not result in deployment
+ // don't map to the unloaded context
+ context = null;
}
}
- if (lastSlash == -1) {
- lastSlash = nthSlash(uri, nesting + 1);
- } else {
- lastSlash = lastSlash(uri);
+ if (context != null) {
+ mappingData.context = context.object;
+ mappingData.contextPath.setString(context.name);
}
- uri.setEnd(lastSlash);
- pos = find(contexts, uri);
}
- uri.setEnd(uriEnd);
-
- if (!found) {
- if (contexts[0].name.equals("")) {
- context = contexts[0];
- }
- } else {
- context = contexts[pos];
- }
- if (context != null) {
- mappingData.context = context.object;
- mappingData.contextPath.setString(context.name);
- }
}
// Wrapper mapping
@@ -676,6 +729,63 @@
}
+ private void notifyLazyLoadContextMappingListeners(Host mappedHost, Context context) {
+ synchronized (context) {
+ if (context.replaced)
+ return;
+ synchronized (lazyLoadListeners) {
+ for (OnDemandContextMappingListener listener : lazyLoadListeners)
+ listener.onDemandContextMapped(mappedHost.name, context.name);
+ }
+ }
+
+ }
+
+
+ private Context findContext(CharChunk uri, Context[] contexts, int nesting) {
+ int pos = find(contexts, uri);
+ if (pos == -1) {
+ return null;
+ }
+
+ Context foundContext = null;
+
+ int lastSlash = -1;
+ int uriEnd = uri.getEnd();
+ int length = -1;
+ boolean found = false;
+ while (pos >= 0) {
+ if (uri.startsWith(contexts[pos].name)) {
+ length = contexts[pos].name.length();
+ if (uri.getLength() == length) {
+ found = true;
+ break;
+ } else if (uri.startsWithIgnoreCase("/", length)) {
+ found = true;
+ break;
+ }
+ }
+ if (lastSlash == -1) {
+ lastSlash = nthSlash(uri, nesting + 1);
+ } else {
+ lastSlash = lastSlash(uri);
+ }
+ uri.setEnd(lastSlash);
+ pos = find(contexts, uri);
+ }
+ uri.setEnd(uriEnd);
+
+ if (!found) {
+ if (contexts[0].name.equals("")) {
+ foundContext = contexts[0];
+ }
+ } else {
+ foundContext = contexts[pos];
+ }
+ return foundContext;
+ }
+
+
/**
* Wrapper mapping.
*/
@@ -879,7 +989,6 @@
MappingData mappingData) {
int pathEnd = path.getEnd();
- int pathOffset = path.getOffset();
int lastSlash = -1;
int length = -1;
@@ -1274,6 +1383,27 @@
}
+ private static final boolean insertLazyLoadedContext(Context[] oldMap, Context[] newMap, Context newElement) {
+ int pos = find(oldMap, newElement.name);
+ if ((pos != -1) && (newElement.name.equals(oldMap[pos].name))) {
+ if (oldMap[pos].loaded || !newElement.loaded) {
+ return false;
+ }
+ else {
+ // We're going to replace the old context in the map, but mark
+ // it as replaced so anyone with a ref to it knows to look again
+ oldMap[pos].replaced = true;
+ System.arraycopy(oldMap, 0, newMap, 0, oldMap.length);
+ newMap[pos] = newElement;
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+
+
// ------------------------------------------------- MapElement Inner Class
@@ -1321,6 +1451,8 @@
public Wrapper[] wildcardWrappers = new Wrapper[0];
public Wrapper[] extensionWrappers = new Wrapper[0];
public int nesting = 0;
+ private boolean loaded = true;
+ private volatile boolean replaced = false;
}
Added: trunk/java/org/apache/tomcat/util/http/mapper/OnDemandContextMappingListener.java
===================================================================
--- trunk/java/org/apache/tomcat/util/http/mapper/OnDemandContextMappingListener.java (rev 0)
+++ trunk/java/org/apache/tomcat/util/http/mapper/OnDemandContextMappingListener.java 2010-01-25 12:54:58 UTC (rev 1367)
@@ -0,0 +1,35 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2010, Red Hat, Inc, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.apache.tomcat.util.http.mapper;
+
+/**
+ * A listener for notifications when a request is being mapped to a context
+ * path that was
+ * {@link Mapper#addOnDemandContext(String, String) registered as an on-demand context}.
+ *
+ * @author Brian Stansberry
+ * @version $Revision$
+ */
+public interface OnDemandContextMappingListener
+{
+ void onDemandContextMapped(String host, String context);
+}
Modified: trunk/webapps/docs/changelog.xml
===================================================================
--- trunk/webapps/docs/changelog.xml 2010-01-25 12:52:45 UTC (rev 1366)
+++ trunk/webapps/docs/changelog.xml 2010-01-25 12:54:58 UTC (rev 1367)
@@ -27,6 +27,13 @@
</fix>
</changelog>
</subsection>
+ <subsection name="Coyote">
+ <changelog>
+ <add>
+ On demand webapp startup plumbing, submitted by Brian Stansberry. (remm)
+ </add>
+ </changelog>
+ </subsection>
<subsection name="Jasper">
<changelog>
<fix>
14 years, 11 months
JBossWeb SVN: r1366 - trunk/res/jboss/org/apache/catalina/startup.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2010-01-25 07:52:45 -0500 (Mon, 25 Jan 2010)
New Revision: 1366
Modified:
trunk/res/jboss/org/apache/catalina/startup/catalina.properties
Log:
- Default to not using instance manager and injection for JSP tags.
Modified: trunk/res/jboss/org/apache/catalina/startup/catalina.properties
===================================================================
--- trunk/res/jboss/org/apache/catalina/startup/catalina.properties 2010-01-25 03:28:00 UTC (rev 1365)
+++ trunk/res/jboss/org/apache/catalina/startup/catalina.properties 2010-01-25 12:52:45 UTC (rev 1366)
@@ -2,6 +2,8 @@
org.apache.catalina.STRICT_SERVLET_COMPLIANCE=true
org.apache.catalina.core.StandardService.DELAY_CONNECTOR_STARTUP=true
org.apache.jasper.runtime.BodyContentImpl.LIMIT_BUFFER=true
+org.apache.jasper.Constants.INJECT_TAGS=false
+org.apache.jasper.Constants.USE_INSTANCE_MANAGER_FOR_TAGS=false
org.apache.catalina.connector.Connector.X_POWERED_BY=true
org.apache.catalina.connector.Request.SESSION_ID_CHECK=true
org.apache.catalina.connector.CoyoteAdapter.X_POWERED_BY=Servlet/3.0; JBossAS-6
14 years, 11 months