First mod_servlet filter - novalexei/mod_servlet GitHub Wiki

First mod_servlet filter

mod_servlet just as Java Servlets supports filters. Filter can intercept HTTP request and manipulate the request before it is received by the servlet and the response after the servlet generated it. Filters are normally used to:

  • Compress/decompress;
  • Encrypt/decript;
  • Authenticate;
  • Transform XML or JSON;
  • Transform images;
  • And whatever other twisted ideas you have.

The filter can be assigned to a URL pattern (just like servlet) or to a certain servlet by name (see filter-mapping for more information). Interesting feature of filter is that multiple filters can be chained.

For detailed information about filter API see http_filter reference.

Simple response interceptor

Let's try to write simple filter. The following filter will intercept the response stream, count characters in it and replace the response with its own. To intercept the response we will need to wrap it. For convenience there is a http_response_wrapper class. It provides API to add filter to the output stream. To use it we will need to override method out_filter *filter(). But before let's define simple out_filter:

struct my_out_filter : public out_filter
{
    std::streamsize write(char* s, std::streamsize n, basic_sink<char>& dst) override
    {
        _count += n;
        return n;
    }
    std::size_t get_count() const { return _count; }
private:
    std::size_t _count = 0;
};

Now when we have output filter, we can define response wrapper:

struct my_response_wrapper : public http_response_wrapper
{
    using http_response_wrapper::http_response_wrapper;
    ~my_response_wrapper() { delete _filter; }
    std::size_t get_count() const { return _filter ? _filter->get_count() : 0; }
protected:
    out_filter *filter() override
    {
        if (!_filter) _filter = new my_out_filter{};
        return _filter;
    }
private:
    my_out_filter *_filter = nullptr;
};

It is quite self-explanatory. Now let's code the actual filter:

class intercept_filter : public servlet::http_filter
{
public:
    void do_filter(http_request& req, http_response& resp, filter_chain& chain) override
    {
        // Wrap the response
        my_response_wrapper resp_wrap{resp};
        // Forward down the chain
        chain.do_filter(req, resp_wrap);
        // When the request is processed flush the output stream
        // Without flushing here we will not get correct count
        resp_wrap.get_output_stream().flush();
        // Generate our own response with intercepted response count
        std::ostream &out = resp.get_output_stream();
        out << "<!DOCTYPE html>\n"
                "<html>\n"
                "<head>\n"
                "<title>Intercept filter</title>\n"
                "</head>\n"
                "<body>\n"
                "<p>We intercepted response with the length "
            << resp_wrap.get_count() << "</p>\n"
                "</body>\n"
                "</html>\n";
    }
};

FILTER_EXPORT(inteceptFilter, intercept_filter)

And this is it. Note one important thing here - the filter invokes chain.do_filter(req, resp_wrap). This is absolutely necessary in order for the rest of the filter chain and the servlet itself get their chance to process the request. If we don't call this method, this is where the request processing stop. Also notice FILTER_EXPORT directive. It exports filters just as SERVLET_EXPORT exports servlets. More information you can find in http_filter documentation.

Compilation and deployment for filter is essentially the same as for servlet. But the configuration has some differences. Add to our web.xml the following section:

    <filter>
        <filter-name>InterceptFilter</filter-name>
        <filter-factory>libtut_filter.so:interceptFilter</filter-factory>
    </filter>
    <filter-mapping>
        <filter-name>InterceptFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

This is nearly identical to servlet configuration, but uses filter and filter-mapping tags. Note that in filter-mapping you can specify either url-pattern or servlet-name which will attach the filter to the servlet with that name.

Assuming, that you already have some servlet in your web.xml file (filters cannot be invoked by themselves, they still need a servlet, even if it is a default servlet) specify URL for that servlet and see the result of the filter.

We mastered filters. Let's talk about Authentication

To The Programming Guide

⚠️ **GitHub.com Fallback** ⚠️