Recent Blogs
Ambimorphic, Mobile Configurations for Lamport Clocks After years of technical research into Smalltalk, with close cooperation with MIT Computer Science and Artificial Intelligence Laboratory (CSAIL) and its SCIgen, we have been able to show the construction of link-level acknowledgements. As a result, we propose an analysis of A* search, which we call Volt. 10/02/2008
Restlets and HTTP Sessions (Recipe 9) This recipe discusses some options in available to Web site developers using Restlet API to emulate traditional HttpSession's 07/31/2008
Restlet Cookbook - Delving into handleGet() and handlePost() (Recipe 8) This posting provides related examples that illustrate at least some simple things you may do with several handleX() methods where X may stand for various types of calls. 07/30/2008
Restlet Cookbook - How to enable SSL (Recipe 7) How to enable SSL on Restlet Web sites wihtout knowing too much keys, encryption, SSL and such. 07/29/2008
Restlet Cookbook - Using Filters in Restlet Applications (Recipe 6) This recipe contains code snippets illustrating the use of Filters in Restlet applications 03/09/2008
Restlet Cookbook - How to Create Virtual Hosts in Restlet Applications (Recipe 5) This recipe contains a code snippet for creating virtual hosts 02/24/2008
Popular HTML Escape Codes Here is a table of HTML escape codes for most frequently used symbols. If you use HTML, sooner or later you will find this table useful. 12/29/2007
Restlet Cookbook - Access Log (Recipe 4) This recipe describes access Logging using Restlet framework 11/09/2007
Restlet Cookbook - Form Submission and Processing (Recipe 3) This recipe describes a typical form submission using Restlet framework 10/15/2007
Restlet Cookbook - Site Application (Recipe 2) This recipe describes a possible way to code a Restlet Web site application 10/04/2007
more...
Restlet Cookbook - Using Filters in Restlet Applications (Recipe 6)
Posted: 03/09/2008 by ValThe idea of using filters (facilities that can transform a request or modify a response ) are not new in Web applications. For example, in Servlet API filters have been since Servlet API 2.3 (released in October 2001). The javax.servlet.Filter usually:
- Intercepts a servlet's invocation before the servlet is called
- Examines a request before a servlet is called
- Modifies the request's headers and data by wrapping the original request in its customized version
- Modify the response's headers and data by wrapping the real response in its customized version after the servlet is called
Restlet API provides a similar facility implemented as an abstract class org.restlet.Filter . You implement your own Filter by extending this abstract class. You should keep in mind the following while doing this:
- org.restlet.Filter extends org.restlet.Restlet; as such, all Restlet functionality is available in Filter
- Methods you usually override are:
- afterHandle(Request request, Response response) - Allows filtering after processing by the next Restlet.
- beforeHandle(Request request, Response response) - Allows filtering before processing by the next Restlet.
- doHandle(Request request, Response response) - Handles the call by distributing it to the next Restlet.
- Method handle(Request request, Response response) is final, so no matter how strong is your temptation, you cannot override it. For the record: the handle() simple invokes beforeHandle(), doHandle() and afterHandle().
- You can attach and detach targets while handling incoming calls as the filter is ensured to be thread-safe.
As usually, I illustrate this recipe with a specific example - in this case, it's a Filter that allows to monitor remote addresses and frequencies with which these IPs appear in incoming requests. If there are two many requests from the same IP in a predefined time interval, this IP is blocked by the Filter. We'll look at the Filter class, AccessMonitorFilter, later in Listing 2 as well as some other related stuff in Listing 3.
But let's start from the beginning - just creating your own Filter is not enough; it should be somehow incorporated into your application. Since I illustrate this recipe with a specific example, Listing 1 below shows how usually this can be done inside the createRoot() method of your Application. The recipe here, as everything in Restlet API, is very simple:
- Create an instance of your Filter (in our example it's an instance of AccessMonitorFilter).
- Set the instance of the Router as "next" to your Filter.
- In our example we return the AccessMonitorFilter instance as the root (since chaining is possible, keep tract of where you start!)
Listing 1: Setting Filter and Router
public class Naviquan extends Application {
...
public static void main(String ...args) throws Exception {
...
component.getDefaultHost().attach("", new Naviquan(component.getContext()));
...
component.start();
}
public Naviquan(Context context) {
super(context);
...
}
/** createRoot */
public Restlet createRoot() {
...
Router router = new Router(getContext());
...
//creates the filter and add it in front of the router
AccessMonitorFilter ipFilter = new AccessMonitorFilter();
ipFilter.setNext(router);
return ipFilter;
}
}
Thus, incorporating your our Filter can be as simple as writing two lines of code. The rest, of course, depends on the functionality you want in your filter. Our AccessMonitorFilter overrides beforeHandle() - no need to do anything else. As you can see from Listing 2 with AccessMonitorFilter code, I first try to analyze the request (its path) and decide whether I want to monitor it or not. This is needed since in addition to the 'main' page request, you will get a lot of other requests related to the same Web page from the same IP: images, scripts, CSS', etc. Obviously, it would be very difficult to take them into account while making blocking decision. Because of this, we want to consider only specific requests. In my code, I simply exploit the fact that in my application it's sufficient for practical purposes to consider only the first element of the path.
Once the resource to watch for is identified, the code delegates the blocking decision to the static method isAccessBanned(String ip) of the AccessInstanceMonitor utility class (see Listing 3).
Actual blocking is achieved by setting response status in the code below I set it to Status.CONNECTOR_ERROR_CONNECTION. I would like to point out that one of the options here, instead of setting the response status, is to set dynamically a Resource that can take care of processing blocked requests. This can be done using the following API in org.restlet.Filter: setNext(Class extends Resource> targetClass) - something like setNext(MyBlockedRequestProcessingResource.class). This opens a door for many interesting possibilities regarding the response.
Listing 2: AccessMonitorFilter Class
public class AccessMonitorFilter extends Filter {
private static String[] unprotectedResources = {"scripts", "styles",
"images", "html"};
protected void beforeHandle(Request request, Response response) {
String path = request.getResourceRef().getPath();
if (isProtectedResource(path)) {
String ip = request.getClientInfo().getAddress();
try {
if (AccessInstanceMonitor.isAccessBanned(ip)) {
response.setStatus(Status.CONNECTOR_ERROR_CONNECTION); }
}
catch (Exception ex) {}
}
}
private boolean isProtectedResource(String path) {
for (int i = 0; i < unprotectedResources.length; i++) {
if (path.indexOf(unprotectedResources[i]) == 1) {
return false; }
}
return true;
}
}
The core of the AccessInstanceMonitor are two static Map's - one is to keep 'banned' IP's, the second is to keep 'incoming' IPs. By the way, please keep in mind that, as usually, there are many ways of implementing your system requirements (and these ways depend on such requirements). Also, AccessInstanceMonitor code in Listing 3 has some parts omitted. For example, loadBannedAccessInstances() may depend on a specific back-end system (database) you are using. You may want to log somehow blocking event and persist the IP blocked for future use.
As far as the decision logic is concerned, it boils down to the addHit() method of the yet another utility class, AccessInstance (see Listing 4).
Listing 3: AccessInstanceMonitor Class
public abstract class AccessInstanceMonitor {
/** Map of current AccessInstance's keyed by IP address */
private static Map accessMap = new ConcurrentHashMap ();
/** Map of banned AccessInstance's keyed by IP address */
private static Map bannedMap = new ConcurrentHashMap();
/** load banned IPs from persistent storage at start-up */
public static void loadBannedAccessInstances() {
...
}
public static boolean isAccessBanned(String ip) throws PoolException {
if (bannedMap.containsKey(ip)) {
return true; }
else {
addAccessInstance(ip);
return false;
}
}
public static void addAccessInstance(String ip) throws PoolException {
AccessInstance monitoredAccessInstance = (AccessInstance) accessMap.get(ip);
if (monitoredAccessInstance == null) {
monitoredAccessInstance = new AccessInstance(ip);
accessMap.put(ip, monitoredAccessInstance);
}
else {
if (monitoredAccessInstance.addHit()) {
bannedMap.put(ip, monitoredAccessInstance);
/** Log blocking event */
/** Add IP to persistent storage */
}
}
}
}
The design of the AccessInstance class is also very simple. It holds values for:
- String ip - IP with which access instance is recorded.
- long firstTime - the time when this IP accessed the site first time during watch interval.
- long lastTime - the time when this IP accessed the site last time during watch interval.
- int count - number of times this IP has accessed the site during watch interval.
Note that two constants are defined elsewhere:
(1)
Constants.ALLOWED_ACCESS_TIME_DECISION_INTERVAL_MILLISECONDS and
(2)
Constants.ALLOWED_ACCESS_COUNT_PER_TIME_DECISION_INTERVAL.
Both constants
deal with the decision making logic (and if you do not like mine - well, invent
and code your own!). The former is basically the minimum time interval to
consider before making any decision. The latter is the maximum frequency with
which the IP can hit your protected resources.
So, with the approach illustrated, two things should happen for the IP to be banned:
- Hits should be taking place within some period of time.
- Hits should be frequent enough.
Finally, to answer question about specific details - the logic implemented does work; however, the actual implementation is bit more complex. And as for specific values used in decision making - the best way is to determine them empirically based on site traffic volumes and attach patterns.
Listing 4: AccessInstance Class
public class AccessInstance {
public String ip;
public long firstTime;
public long lastTime;
public int count;
public AccessInstance(String ip) {
this.ip = ip;
firstTime = System.currentTimeMillis();
lastTime = firstTime;
count = 1;
}
public boolean addHit() {
count = count + 1;
lastTime = System.currentTimeMillis();
return isToBeBanned();
}
public float getFrequency() {
float result = 0f;
if (lastTime - firstTime >= 0) {
result = (float)count / (lastTime - firstTime); }
return result;
}
public boolean isToBeBanned() {
return lastTime - firstTime >
Constants.ALLOWED_ACCESS_TIME_DECISION_INTERVAL_MILLISECONDS && getFrequency()
> Constants.ALLOWED_ACCESS_COUNT_PER_TIME_DECISION_INTERVAL;
}
}