Showing posts with label request. Show all posts
Showing posts with label request. Show all posts

Saturday, January 16, 2010

Get real IP from request in Java

Simply getting the IP of the remote client in Java is an easy task. Assuming we have a request instance, we can simply invoke the “getRemoteAddr” method:

request.getRemoteAddr();

The problem, is that the IP we get is not always the correct IP. For example if our server is behind a load balancer, the method “request.getRemoteAddr” returns the IP of the load balancer and not the IP of the remote client. Another common example, is that the client is behind some proxy or even several proxies. The IP we will get will not be the correct IP.

Fortunately, In most of the cases when a request passes in a load balancer or a proxy, the IP of the remote client is passed in the header of the request. The header key most of the time is: x-forwarded-for. The header value can be one or more IP addresses. The first address, is the address of the remote client. The second or any other IP is the IP of the proxy on the way. The IP of the last proxy is the IP returned in: “request.getRemoteAddr”. For example, if we have 3 proxies, the request header will look like:

x-forwarded-for: client1, proxy1, proxy2

The IP of the client if the first IP. The IP of the third proxy will be returned when calling to: “request.getRemoteAddr”.

There is one none-common case that may happen. The first IP can be an IP of a private network (for example, an IP that starts with “192” or “10”). I such case we will want to take the second IP.

Writing a Java code that will get the IP from the request is quite easy. We will use assistance from code of these older posts: Convert IP String to numeric representation and numeric representation to IP String in Java, Test if IPv4 belongs to a private network address. The general logic of the code is:

  • Look for “x-forwarded-for” header.
  • If header exists, get the first IP.
  • Check that:
    • IP is valid.
    • IP is not a private IP.
  • If IP passes these 2 tests. Return this IP. If not move to the next IP and do the same test and so on.
  • If header doesn’t exist. Return the IP from calling “request.getRemoteAddr”.

Let’s see how it looks in Java:

package com.bashan.blog.ip;
import org.apache.commons.lang.text.StrTokenizer;
import javax.servlet.http.HttpServletRequest;
import java.util.regex.Pattern;
/**
 * @author Bashan
 */
public class IpUtils {
  public static final String _255 = "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
  public static final Pattern pattern = Pattern.compile("^(?:" + _255 + "\\.){3}" + _255 + "$");
  public static String longToIpV4(long longIp) {
    int octet3 = (int) ((longIp >> 24) % 256);
    int octet2 = (int) ((longIp >> 16) % 256);
    int octet1 = (int) ((longIp >> 8) % 256);
    int octet0 = (int) ((longIp) % 256);
    return octet3 + "." + octet2 + "." + octet1 + "." + octet0;
  }
  public static long ipV4ToLong(String ip) {
    String[] octets = ip.split("\\.");
    return (Long.parseLong(octets[0]) << 24) + (Integer.parseInt(octets[1]) << 16) +
        (Integer.parseInt(octets[2]) << 8) + Integer.parseInt(octets[3]);
  }
  public static boolean isIPv4Private(String ip) {
    long longIp = ipV4ToLong(ip);
    return (longIp >= ipV4ToLong("10.0.0.0") && longIp <= ipV4ToLong("10.255.255.255")) ||
        (longIp >= ipV4ToLong("172.16.0.0") && longIp <= ipV4ToLong("172.31.255.255")) ||
        longIp >= ipV4ToLong("192.168.0.0") && longIp <= ipV4ToLong("192.168.255.255");
  }
  public static boolean isIPv4Valid(String ip) {
    return pattern.matcher(ip).matches();
  }
  public static String getIpFromRequest(HttpServletRequest request) {
    String ip;
    boolean found = false;
    if ((ip = request.getHeader("x-forwarded-for")) != null) {
      StrTokenizer tokenizer = new StrTokenizer(ip, ",");
      while (tokenizer.hasNext()) {
        ip = tokenizer.nextToken().trim();
        if (isIPv4Valid(ip) && !isIPv4Private(ip)) {
          found = true;
          break;
        }
      }
    }
    if (!found) {
      ip = request.getRemoteAddr();
    }
    return ip;
  }
}

Note that the class “StrTokenizer” used to iterate the IPs of the header, is the Apache version from Apache commons lang.

You can download the class here.

Thursday, May 21, 2009

Directly download a file by pressing a link using Java Servlet

There are times in which we would like to directly download a resource by pressing on a link on a page. The meaning of directly is:
  • The browser dialog will be open asking where to save the resource.
  • After saving the file the browser will stay on the same page without doing any refresh or opening a child window.

Usually this is useful if we would like to allow the user to download some real time dynamically generated file that is being created on the server side.
For the example we will use the ZipDir class from this post. We will see how we can allow the user download a dynamically created zip file by pressing on a link.

In general, in order to achieve the above behavior we have to instruct the response with 2 instructions:

  • Set the response content type to: “application/zip”. This is in the case of sending back to the client a Zip file. For any other file type we should set the correct content type. For example, if we would like to send back to the client a PDF file the following content type should be used: “application/pdf”.
  • Set response header: “Content-disposition” with the following value: “attachment; filename=<filename>”, where filename is the name of the file we want the user to see in the “save” dialog.

Here is an example of a Servlet that generates a Zip file on the fly and sends it back to the browser:

package com.bashan.blog.zip;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ZipServlet extends HttpServlet {
  public void doGet(HttpServletRequest request,
                    HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("application/zip");
    response.setHeader("Content-disposition", "attachment; filename=demo.zip");
    ServletOutputStream outputStream = response.getOutputStream();
    new ZipDir("/somedir", outputStream).zip();
  }
}

Note that our ZipDir class is smart enough to set the generated Zip file directly to the output stream of the response. That is a neat solution saving us from needing to generate temporary Zip files on the server that later will have to be deleted.

Nothing special on the web.xml file, just define a Servlet, with “zip” url pattern:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
           version="2.5">
    <servlet>
      <servlet-name>Zip Servlet</servlet-name>
      <servlet-class>com.bashan.blog.zip.ZipServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
      <servlet-name>Zip Servlet</servlet-name>
      <url-pattern>/zip</url-pattern>
    </servlet-mapping>
      
</web-app>

And finally, a sample HTML page with a download link to the Zip resource:

<html>
 <head>
  <title> Zip Download Sample Page </title>
 </head>
 <body>
 <h3>Zip download sample page</h3>
 Press this link: <a href="zip">Download Zip resource</a>
 </body>
</html>
Note that we call “zip” Servlet directly as a relative path to the server context.