URL decoding with unfiltered

8.4k views Asked by At

I'm working with Unfiltered 0.6.8 (using the Jetty connector) and have encountered an odd behaviour: path segments aren't URL decoded.

The following bit of code is my minimal test case:

import unfiltered.request._
import unfiltered.response._

object Test extends App with unfiltered.filter.Plan {
  def intent = {

    case reg @ Path(Seg(test :: Nil)) =>
      println(test)
      ResponseString(test)
  }

  unfiltered.jetty.Http.local(8080).filter(Test).run()
}

Querying http://localhost:8080/some_string yields the expected result: some_string, both on the client and server side.

On the other hand, http://localhost:8080/some%20string yields some%20string on both client and server, rather than the some string I was expecting.

Working around the issue is trivial (java.net.URLDecoder#decode(String, String)), but I'd like to know if:

  • I'm forgetting something trivial and making a fool of myself.
  • unfiltered has a kit to deal with the hassle automatically.
  • if none of the above, is there a particular reason for this behaviour, or should I file a bug report?

As a side note, the unfiltered tag doesn't exist and I do not have enough reputation to create it, which is why I defaulted to scala.

1

There are 1 answers

0
cmbaxter On BEST ANSWER

Strange, I'm seeing this behavior too. There is nothing in the Seg object that does any sort of url decoding prior to splitting up the path segments and I don't see anything else in the framework for this either. I ran into a post that detailed a solution using a custom extractor as follows:

object Decode {
  import java.net.URLDecoder
  import java.nio.charset.Charset

  trait Extract {
    def charset: Charset
    def unapply(raw: String) = 
      Try(URLDecoder.decode(raw, charset.name())).toOption
  }

  object utf8 extends Extract {
    val charset = Charset.forName("utf8")
  }
}

Then you could use it like this:

case reg @ Path(Seg(Decode.utf8(test) :: Nil)) =>
  println(test)
  ResponseString(test)

Or like this if you wanted the entire path decoded:

case reg @ Path(Decode.utf8(Seg(test :: Nil))) =>
  println(test)
  ResponseString(test)

Thankfully the framework is flexible and open to extension like this, so you certainly have options.