Monday, September 21, 2009

Tomcat 6.0.16 quoting cookies

Recently I've stumbled upon a feature in Tomcat 6.0.16:

  • browser cookies containing symbols like : or = get broken in Tomcat when reading using request.getCookies() - only part of the cookie value is returned
  • when writing such cookies using request.addCookie(), Tomcat automatically adds double quotes around the value

Arguable, such cookies are malformed and using them involves security risks.

In reality, such cookies are quite popular among web developers - they allow to put together some user preferences like that:

MyCookie=language=en:currency=EUR

Even Google uses cookies like that.

What options we have to solve the problem ?

  1. use the correct cookie format (with double quotes)
  2. stick to Tomcat 6.0.14 or older
  3. do some custom hack

Option 1 is the best if you can afford it (but you must still account for cookies already present in client browsers).

Option 2 is only a temporary solution - sooner or later you will need to upgrade (e.g. because of important security patches).

So the unfortunate souls like me are left with the Option 3. My cookie is used jointly by many web application and changing the format at once is not possible.

So here is the solution: write a servlet filter that does custom cookie reading and writing.

public class TomcatQuotedCookieFilter implements Filter {

    private static final String MY_COOKIE_NAME = "MyCookie";

    public static class CookieWrappedRequest extends HttpServletRequestWrapper {

        public CookieWrappedRequest(HttpServletRequest request) {
            super(request);
        }

        @Override
        public Cookie[] getCookies() {
            // getCookies() returns correct numbers of cookies, but myCookie value might be broken
            Cookie[] cookies = super.getCookies();

            int myCookieIndex = CookieUtils.findCookiePosition(MY_COOKIE_NAME, cookies);
            if (myCookieIndex == -1) {
                return cookies;
            }

            String cookieValue = CookieUtils.extractCookieValueFromHeader(MY_COOKIE_NAME, getHeader("Cookie"));
            if (cookieValue != null) {
                cookies[myCookieIndex] = new Cookie(MY_COOKIE_NAME, cookieValue);
            }

            return cookies;
        }

    }

    public static class CookieWrappedResponse extends HttpServletResponseWrapper {

        public CookieWrappedResponse(HttpServletResponse response) {
            super(response);
        }

        @Override
        public void addCookie(Cookie cookie) {
            // catch my cookie and apply custom formatting
            if (cookie.getName().equals(MY_COOKIE_NAME)) {
                addHeader("Set-Cookie", CookieUtils.formatSetCookieHeaderValue(cookie));
            } else {
                super.addCookie(cookie);
            }
        }

    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException,
            ServletException {

        filterChain.doFilter(new CookieWrappedRequest((HttpServletRequest) request), new CookieWrappedResponse(
                (HttpServletResponse) response));
    }

    // init, destroy...

}

CookieUtils methods are left as an exercise for the reader :) Those might be dependent on the specific cookie format that you are using.

Monday, February 23, 2009

BigDecimal gotchas

The BigDecimal class is full of gotchas. Today's gotcha is:

new BigDecimal("1").equals(new BigDecimal("1.00")) == false

To avoid this problem, use compareTo instead of equals.


Another nice gotcha is:
new BigDecimal(1.23) == 1.229999999999999982236431605997495353221893310546875
The correct way:
new BigDecimal("1.23")

Monday, September 29, 2008

Tomcat 5.5 and JSTL

How to use JSTL tag library with Tomcat 5.5 and Maven:
  1. Add Maven dependencies:
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.1.2</version>
    </dependency>
    <dependency>
        <groupId>taglibs</groupId>
        <artifactId>standard</artifactId>
        <version>1.1.2</version>
    </dependency>
    
  2. Make sure you use at least Servlet version 2.4 in web.xml:
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
                http://java.sun.com/xml/ns/j2ee
                http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
    version="2.4">
    
    </web-app>
    
  3. Add declaration in your JSP file:
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    
  4. Use JSTL tags:
    <c:set var="conditionValue" value="${true}"/>
    <c:if test="${conditionValue}">
    YES
    <c:if>
    

Sunday, March 16, 2008

Timed Screenshots on Mac

When working as a time-based contractor, recording spent time in "time manager" is sometimes problematic and always tedious. It happened to me that at the end of the day I have trouble recalling what tasks did I do and it what proportions. Now maybe this little shell script can help me:

#!/bin/bash 
# Makes screenshots every $PERIOD minutes 
# and puts them into /var/tmp/screens/ directory.  

PERIOD=5  
for ((i=0; i<=10*60/PERIOD; i+=1)); do
    FILENAME=/var/tmp/screens/screen`date +%y%m%d_%H%M`.png
    echo Capturing $FILENAME...
    screencapture $FILENAME
    echo Done, next capture in $(($PERIOD*60)) seconds.
    sleep $(($PERIOD*60))
done