Using PHP sessions from Java

This blog post is the first in a number of what I hope are useful coding related posts. I’ve been coding for many years in many languages on many platforms. I tend to solve issues using a combination of technologies so many of my posts will be based around combining technologies. Others will be basically describing gotchas that I have found during development and how I solved or worked around them. This post is about using PHP’s session handling from a Java client, i.e. not a browser.

PHP session handling

PHP session handling is a way of having persistent or stateful data across HTTP requests. The simplest snippet of PHP to achieve this is shown below.

// Start the session
session_start();

// Create a new session object if its a new session
if (!isset($_SESSION['session'])) {
	$_SESSION['session'] = new MySessionObject();
}

I tend to use a PHP class to encapsulate the session data. Using the code above a new instance of MySessionObject() is created if a session could not be restored. Otherwise the instance is restored from the previous time the code was called.

PHP actually manages this using a session ID stored in a cookie. (There is an alternative method of managing the session ID if cookies are disable in a browser but that’s beyond the scope of this post.) The session_start() call will parse the value of the Cookie: HTTP request header field looking for a value similar to PHPSESSID=b330c9b604d371e5c88cd0919990f41c. It will use the session ID value to retrieve the appropriate session data. It will deserialise any stored values and store them in the $_SESSION global array variable.

If the PHPSESSID does not exist session_start() will create a new empty session (and $_SESSION) and use Set-Cookie: in the HTTP response to pass the session ID cookie to the browser so it can be returned on the next request. The session cookie is valid for the lifetime of the browser.

The Java client

However this post is about using PHP sessions from Java. To make an HTTP request from Java use the HttpURLConnection class. This class does have a few gotchas which I will explain below but it is sufficient for our needs. Typically I encapsulate the class in my own class to manage extra data. The bones of such a class is as follows.

public class MyConnection {

	private final static String HTTP_HEADER_SET_COOKIE = "set-cookie";

	private URL url = null;

	private String cookies = null;

	public MyConnection(URL url) {
		this.url = url;
	}

	protected String readCookies(HttpURLConnection con) {
		// ...
	}
	
	public String sendRequest(String requestString) 
		throws IOException, SocketTimeoutException {
		// ...
	}
}

You create an instance of the class when you want to start communicating with the remote URL and it will store cookies between each communication session with the remote server. The main work is carried out by the sendRequest() method which is shown below.

HttpURLConnection is a little tricky to use due to the time at which the request is actually sent to the server. If you set header fields (using setRequestProperty()) too late then they wont be included. It is similar for other properties too.

By default the class expects to have an empty body to the request sent to the server and returns the body of the response to the user by way of the input stream obtained using getInputStream(). (Note that input and output here refers to data being input to the connection and output from the connection. I.e. input in to the server and output from the server which may be the reverse of what you’re expecting.) However you can tell the connection you intend providing a body to the request by calling setDoOutput() with a value of true. In this case the output data is buffered and sent with the headers when getInputStream() is called.

public String sendRequest(String requestString) 
	throws IOException, SocketTimeoutException {
		
	StringBuilder buffer = new StringBuilder();
	String line;
		
	// Get the connection.  Java will create a new one each time.
	HttpURLConnection con = (HttpURLConnection)url.openConnection();
		
	// Set the Cookie header
	if (cookies != null) {
		con.setRequestProperty("Cookie", cookies);
	}
		
	// Set the content-size header
	con.setRequestProperty("Content-size", 
		Integer.toString(requestString.length()));
		
	// Set the request method.
	con.setRequestMethod("POST");
		
	// Enable both reading and writing to and from the connection.
	con.setDoInput(true);
	con.setDoOutput(true);
		
	// Get the output stream
	OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream());
		
	// Send the request
	out.write(requestString.toString());
	out.flush();
		
	// Get the input stream.  This is the point at which data is sent to 
	// and read from the server.
	BufferedReader in = new BufferedReader(
		new InputStreamReader(con.getInputStream()));
		
	// If the response from the server is OK handle the content of the response
	String responseCode  = con.getResponseCode();
		
	if (responseCode == HttpURLConnection.HTTP_OK) {
		// Read the cookies
		String cookies = readCookies(con);
		if (cookies != null) {
			this.cookies = cookies;
		}
 			
		// Read the content
		while ((line = in.readLine()) != null) {
			buffer.append(line);
		}
		
		return buffer.toString();
	}
		
	// Error from server
	return null;
}

Basically the method obtains a connection and sets up important headers, the request type, sends the request string as the body of the request returning the response string to the caller. This is a fairly typical use of the HttpURLConnection class. The important bit with respect to this particular post is the cookie handling.

In the code above, if the cookies variable is set then it is passed to the server as the value of a Cookie: header otherwise that header is omitted. When an instance of the MyConnection class is created the cookies variable is initialised to null and is only set when a Set-Cookie: header is received from the server. This is handled by the readCookies() method.

protected String readCookies(HttpURLConnection con) {
	StringBuilder cookieBuffer = null;
	String        cookieField  = null;
	String        headerName   = null;
		
	for (int i = 1; (headerName = con.getHeaderFieldKey(i)) != null; i++) {
		if (headerName.toLowerCase().equals(HTTP_HEADER_SET_COOKIE)) {
			cookieField = con.getHeaderField(i);
			cookieField = cookieField.substring(0, 
				cookieField.indexOf(";"));
				
			if (cookieBuffer != null) {
				cookieBuffer.append("; ");
			} else {
				cookieBuffer = new StringBuilder();
			}
			cookieBuffer.append(cookieField);
		}
	}

	if (cookieBuffer != null) {
		return cookieBuffer.toString();
	} else {
		return null;
	}
}

Annoyingly there isn’t a getter equivalent to setRequestProperty(). You can only obtain headers provided in the response by index. This means that you have to iterate through all the headers looking for the ones your interested in. This is slightly simplified by the getHeaderFieldKey() which returns the header name, for example, Set-Cookie for the specified index. So you iterate through the header names obtaining the header data for each of the names you are interested. The value of the cookie is everything up to the first semi-colon. The rest is meta-data.

However nothing is simple in this world. It appears that the Android version of HttpURLConnection converts the header field name to lower case but the Sun version (in the JDK) does not. So above I convert the header name to lowercase before comparison to ensure I’m always comparing like with like.

Yet another gotcha is that you could have more than one Set-Cookie header but you can only call setRequestProperty() once for each header. If you call it more than once you overwrite the previous value. So I concatenate all the cookies received in to one cookie string separating them appropriately.

So combining everything the Java client creates an instance of MyConnection with the appropriate URL. When sendRequest() is first called no cookies are set. The PHP on the server calls session_start() that creates a new session. The session ID is returned to the Java client, obtained by readCookies() and stored in the instance of MyConnection.

Subsequent calls to sendRequest() will return the session ID back to the PHP running on the server for as long as the instance of MyConnection exists. This way creating a new instance of MyConnection creating a new session on the server automatically.

So that’s how you use PHP session from a Java client. I hope this post is useful and welcome any comments about content or style.

Tags:

Leave a Reply