Valves and Filters

This chapter describes how to configure Valves and Filters in JEUS.

1. Overview

A Valve, a concept adopted from Apache Tomcat, is a component that can be inserted into the request processing pipeline to check or modify requests. Each Valve can be configured at different levels, such as the Web Engine, Virtual Host, and Context.

A Filter is another component used for processing requests, similar to Valves. It is represented by jakarta.servlet.Filter. Unlike Valves, Filters can be configured at the servlet level. For more information, refer to jakarta.servlet.Filter.

2. Concept of Valves

This chapter describes the basic concept and use of a Valve.

If Filters operate only at the servlet level, Valves serve in a broader scope, such as the server and virtual host levels. The Valve code execution starts at the web engine level, then proceeds to the virtual host level, and finally, the context level. If the code is called within the same level, the specified order will be applied. If Filters and Valves are both configured, Valves take precedence over Filters.

Configuring jeus-web-dd.xml

Create jeus-web-dd.xml under the WEB-INF/ directory. For further description for this, see Web Contexts.

The following is a sample jeus-web-dd.xml file. It shows only a specific part of the file. To see the full file with all configuration items, refer to "13. Configuration of jeus-web-dd.xml" in "JEUS XML Reference".

Web Context Configuration File: <jeus-web-dd.xml>
<jeus-web-dd xmlns="http://www.tmaxsoft.com/xml/ns/jeus" version="9">
<context-path>/examples</context-path>
    <pipeline>
        <valve>
            <class-name>jeus.servlet.valve.RemoteAddressValve</class-name>
            <property>
                <key>deny</key>
                <value>127\.0\.0\.1</value>
            </property>
            <property>
                <key>denyStatus</key>
                <value>403</value>
            </property>
        </valve>
    </pipeline>
</jeus-web-dd>

The following example configures a Valve at the web engine level in domain.xml.

Server configuration file: <domain.xml>
<domain xmlns="http://www.tmaxsoft.com/xml/ns/jeus" version="9">
<web-engine>
    <pipeline>
        <valve>
            <class-name>jeus.servlet.valve.RemoteAddressValve</class-name>
            <property>
                <key>deny</key>
                <value>127\.0\.0\.1</value>
            </property>
            <property>
                <key>denyStatus</key>
                <value>403</value>
            </property>
        </valve>
    </pipeline>
</web-engine>
</domain>

The following describes each configuration tag.

Tag Description

<valve>

Valve to use.

<class-name>

Class name of the valve. Same as the filter class name.

<property>

Property for the valve. This property consists of key-value pairs, which are stored in the parameter map within the valve. Multiple properties can be configured.

<key>

Key to use in the parameter map within the valve object. Must be unique within the map. If duplicate keys are found, the later configured key value is applied.

<value>

Value to store in the parameter map for the valve. The data type is string, and the type casting is performed in the init() method, which will be discussed later in this chapter.

The class to be specified in <class-name> must inherit the jeus.servlet.valve.ValveBase class.

3. Configuring JEUS-implemented Valves

JEUS provides valve implementation for user convenience.

3.1. Restricting the Use of HTTP Methods

This valve restricts or allows the use of certain HTTP methods.

Adding a valve to restrict methods: <jeus-web-dd.xml>
<jeus-web-dd xmlns="http://www.tmaxsoft.com/xml/ns/jeus" version="9">
<context-path>/examples</context-path>
    <pipeline>
      <valve>
       <class-name>jeus.servlet.valve.MethodConstraintValve</class-name>
            <property>
                <key>allow</key>
                <value>GET,POST</value>
            </property>
        </valve>
    </pipeline>
</jeus-web-dd>

The class provided by JEUS for restricting the use of HTTP methods is jeus.servlet.valve.MethodConstraintValve. You can set the key to either allow or deny, and the value to the methods to allow or restrict. Separate each method by comma (,).

3.2. Allowing or Blocking Remote Access

This valve determines whether to allow or block a remote host/address.

To block, set the key to deny. To allow, set the key to allow. The key is case-sensitive. In the value tag, specify the remote address or host to block or allow. Here, the value for the remote host must be the value returned by the getRemoteHost() method which can be called with the servletRequest object. For more information, see getRemoteHost() Method of ServletReques.

The string to be specified in the value tag must comply with the Java regular expressions. The java.util.regex provides the classes and interfaces for regular expressions. The denyStatus key specifies the HTTP status code to return when blocking the request. If not specified, the default value is 403.

The following example only allows local accesses.

Adding a valve to allow remote access by address: <jeus-web-dd.xml>
<jeus-web-dd xmlns="http://www.tmaxsoft.com/xml/ns/jeus" version="9">
<context-path>/examples</context-path>
    <pipeline>
        <valve>
            <class-name>jeus.servlet.valve.RemoteAddressValve</class-name>
            <property>
                <key>allow</key>
                <value>127\.0\.0\.1</value>
            </property>
            <property>
                <key>denyStatus</key>
                <value>403</value>
            </property>
        </valve>
    </pipeline>
</jeus-web-dd>

The following example configures a valve to block remote accesses by a remote host 'www.tamx.co.kr'.

Adding a valve to block remote access by host: <jeus-web-dd.xml>
<jeus-web-dd xmlns="http://www.tmaxsoft.com/xml/ns/jeus" version="9">
<context-path>/examples</context-path>
    <pipeline>
    <valve>
        <class-name>jeus.servlet.valve.RemoteHostValve</class-name>
        <property>
            <key>deny</key>
            <value>www\.tmax\.co\.kr</value>
        </property>
    </valve>
</pipeline>
</jeus-web-dd>

The key and value strings are case-sensitive. Also, do not specify both deny and allow at the same time.

3.3. URL Rewriting

This valve enables URL rewriting and implements the same rules as the RewriteValve in Tomcat. URL rewriting serves two primary purposes: preventing domain exposure and redirecting requests to other locations. Define the rules for rewriting in a dedicated configuration file named 'rewrite.config'. For more information about rewriting rules, refer to Apache Tomcat 8.5 Rewriting Document.

You can configure the URL rewriting valve either at the web engine or context level. To configure the valve at the web engine level, place the configuration file under the DOMAIN/config directory. To configure the valve at the context (application) level, place the file under APP_HOME/WEB-INF. The class provided by JEUS for URL rewriting is jeus.servlet.valve.rewrite.RewriteValve.

Adding a valve to enable URL rewriting: <jeus-web-dd.xml>
<jeus-web-dd xmlns="http://www.tmaxsoft.com/xml/ns/jeus" version="9">
<context-path>/examples</context-path>
    <pipeline>
        <valve>
            <class-name>jeus.servlet.valve.rewrite.RewriteValve</class-name>
            <property>
                <key>encoding</key>
                <value>utf-8</value>
            </property>
        </valve>
    </pipeline>
</jeus-web-dd>

Enter 'encoding' in the key tag with the desired encoding for URL rewriting in the value tag. If the encoding is not explicitly specified, the request query encoding of JEUS is used by default. Ensure that the encoding remains consistent across JEUS, the rewrite.config file, and the RewriteValve class, especially when using Korean characters.

In JEUS, the query encoding is determined in the following order of priority:

  1. Forced URL encoding in domain.xml

  2. Forced request encoding

  3. Client’s override encoding

  4. URL mapping encoding

  5. Default encoding

  6. ISO-8859-1, if none of the above is set.

You must avoid using jeus.servlet.request.6CompatibleSetCharacterEncoding along with any of the above settings. It is intended for backward compatibility, and may lead to out-of-specification behaviors and disrupt the priority order of encoding.

4. User Custom Valves

Developers using JEUS can also implement inherited valves.

To add a valve, inherit jeus.servlet.valve.ValveBase. Inheriting this class requires two libraries: jakarta.servlet-api.jar and jeus-servlet-engine.jar located in JEUS_HOME/lib/system. After building the class, package it into a jar file along with these libraries and add the file to JEUS_HOME/lib/thirdparty. When adding the jar file, ensure that it does not contain duplicate jakarta.servlet-api.jar and jeus-servlet-engine.jar.

The following shows a sample valve that adds test to the HTTP response header.

Adding test to the response header: test
package com.test.valve;

import jeus.servlet.valve.ValveBase;
import jeus.servlet.valve.ValveException;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

public class TestValve extends ValveBase {
    private String test;

    @Override public void init() throws ValveException {
        Map<String,String> paramMap = getValveParameterMap();
        test = paramMap.get("test");
    }
    @Override public void invoke(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
         //Before running application
         getNext().invoke(req, resp);
         //After running application
         resp.setHeader(test,test);
    }
}

If the valve is configured at the context level, the init() method is invoked when the application is deployed. If configured in domain.xml, it is invoked when the virtual host or web engine starts up. The getValveParameterMap() method imports the properties map which is configured in jeus-web-dd.xml or domain.xml. The invoke() method defines the behaviors of the valve for incoming requests. It basically has the same structure as in a servlet filter. The getNext().invoke() method functions the same as the chain.doFilter() method in filter implementation. Before getNext().invoke() is called, filter is operated with the request, and then response filtering is performed with the response.

Refer to JEUS_HOME/docs/api/jeus-servlet for information about jeus.servlet.valve.ValveBase.

Even if the init() method performs no specific operation, it must be implemented anyway. The invoke() method must be implemented as well, because it defines the behaviors of the valve. Also, ensure that the getNext().invoke() method is called when implementing the invoke() method. Request processing is always performed at the last valve defined in JEUS. Therefore, if a request fails to proceed to the subsequent valve, it cannot be processed.

5. Configuring JEUS-implemented Filters

JEUS provides filter implementation for user convenience.

5.1. SameSite Restriction

SameSite restriction can be configured individually at the context level, but cannot operate differently by identifying individual user-agents. Since there may be some clients that cannot process SameSite, JEUS provides a filter designed to remove SameSite based on the identification of different user-agents.

Adding a filter for SameSite restriction: <web.xml>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         metadata-complete="false"
         version="5.0">

<filter>
    <filter-name>NoSameSiteFilter</filter-name>
    <filter-class>jeus.servlet.filters.NoSameSiteFilter</filter-class>
    <init-param>
        <param-name>agent</param-name>
        <param-value>^Chrome/\w$</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>NoSameSiteFilter</filter-name>
    <servlet-name>NoSameSiteServletTest</servlet-name>
</filter-mapping>

</web-app>

The class provided by JEUS for SameSite restriction is jeus.servlet.filters.NoSameSiteFilter. Specify 'agent' as the parameter name in the init-param tag, with the regular expression for the user-agent for which the SameSite attribute will be removed in the value tag.

5.2. Forwarded Header

This filter retrieves information from the client instead of the proxy through the jakarta.servlet.HttpServletRequest API, by identifying the RFC 7239-defined Forwarded header and the de facto headers (e.g. X-Forwarded).

The filter class is jeus.servlet.filters.ForwardedFilter, which automatically finds and applies the Forwarded header. However, only four Forwarded headers (for, host, proto, by) are supported based on the specification. For more information, refer to RFC 7239.

To apply de facto headers as well, specify the headers to use in the InitParameter (init-param) for the filter. InitParameter supports 6 keys: Forwarded-For, Forwarded-Host, Forwarded-Proto, Forwarded-By, Forwarded-Port, and Forwarded-Server; In the InitParameter value, specify the headers to apply by separating each with a comma(,). The following shows an example.

<filter>
    <filter-name>ForwardedFilter</filter-name>
    <filter-class>jeus.servlet.filters.ForwardedFilter</filter-class>
    <init-param>
         <param-name>Forwarded-For</param-name>
         <param-value>X-Forwarded-For,Proxy-Client-IP,WL-Proxy-Client-IP,HTTP_CLIENT_IP,HTTP_X_FORWARDED_FOR</param-value>
    </init-param>
</filter>

Among the headers, Forwarded has the highest priority. After this, those specified in the param-value tag are applied in the written order. It should be noted that if you specify headers that are not related with the key (host headers), invalid values may be returned by the API.

To check the candidate headers filtered through the filter, call the HttpServletRequest.getAttribute() API with the following. The value will be returned in the format of Map<String, List<String>>. Here, the String is the header name and List<String> contains the header values.

jeus.servlet.filters.ForwardedFilter.For
jeus.servlet.filters.ForwardedFilter.Host
jeus.servlet.filters.ForwardedFilter.Proto
jeus.servlet.filters.ForwardedFilter.By
jeus.servlet.filters.ForwardedFilter.Port
jeus.servlet.filters.ForwardedFilter.Server

The associated HttpServletRequest APIs are getRemoteAddr(), getServerName(), getServerPort(), getScheme(), and isSecure().

Adding a filter for Forwarded headers: <web.xml>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
         metadata-complete="false"
         version="5.0">

<filter>
    <filter-name>ForwardedFilter</filter-name>
    <filter-class>jeus.servlet.filters.ForwardedFilter</filter-class>
    <init-param>
        <param-name>Forwarded-For</param-name>
        <param-value>X-Forwarded-For,Proxy-Client-IP,WL-Proxy-Client-IP,HTTP_CLIENT_IP,HTTP_X_FORWARDED_FOR</param-value>
    </init-param>
    <init-param>
         <param-name>Forwarded-Host</param-name>
         <param-value>X-Forwarded-Host</param-value>
    </init-param>
    <init-param>
        <param-name>Forwarded-Proto</param-name>
        <param-value>X-Forwarded-Proto, WL-Proxy-SSL</param-value>
    </init-param>
    <init-param>
        <param-name>Forwarded-By</param-name>
        <param-value>X-Forwarded-By</param-value>
    </init-param>
    <init-param>
        <param-name>Forwarded-Port</param-name>
        <param-value>X-Forwarded-Port</param-value>
    </init-param>
    <init-param>
        <param-name>Forwarded-Server</param-name>
        <param-value>X-Forwarded-Server</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>ForwardedFilter</filter-name>
    <servlet-name>ForwardedFilterServletTest</servlet-name>
</filter-mapping>
</web-app>

The class provided by JEUS for Forwarded headers filter is jeus.servlet.filters.ForwardedFilter.