Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spray: Bringing RequestContext in scope results in timeout

Hi scala and spray people!

I have a small annoying issue with extracting the HTTP 'Accept' header from the RequestContext and matching on it. On a normal route like so:

get {
  respondWithMediaType(`text/plain`) {
    complete ( "Hello World!" )
  }
}

It works like a charm. But whenever I bring the context into scope like so (as suggested in the documentation for directives):

get { context => {
  respondWithMediaType(`text/plain`) {
    complete ( "Hello World!" )
  }
} }

The result becomes the following error message:

The server was not able to produce a timely response to your request.

I am fairly new to Spray, but it looks really odd to me that bringing an (otherwise implicit) object into scope can have such a weird sideeffect. Does any of you have a clue on what is going on?

like image 575
Jens Egholm Avatar asked Dec 27 '13 08:12

Jens Egholm


Video Answer


1 Answers

Direct access to the RequestContext is rarely needed. In fact, you only need it if you want to write custom directives. Common tasks and extracting the usual bits of data can normally be handled using one of the predefined directives.

It seems what you want to do is manual content type negotiation. In fact, you don't have to do it manually as spray does content type automatically for common data structures. Your example can be shortened to

get {
  complete("Hello World!")
}

When complete is called with a string the response will always be of type text/plain. If the client would send a request with an Accept header that doesn't accept text/plain the request would already be rejected by the server.

If you want to customize the kinds of content types that can be provided from a Scala data-type you need to provide a custom Marshaller. See the documentation on how to achieve that.

Answering your original question why adding context => makes the request timeout: This is because the predefined directives already are of type RequestContext => Unit. So, writing

respondWithMediaType(`text/plain`) {
  complete("Hello World!")
}

is exactly equivalent to (i.e. automatically expanded to)

ctx => respondWithMediaType(`text/plain`) {
  complete("Hello World!")
}.apply(ctx)

So, if you add only ctx => manually, but don't add the apply call, an incoming request is never fed into the inner route and therefore never completed. The compiler doesn't catch this kind of error because the type of a route is RequestContext => Unit and so the variant with and the variant without the apply invocation are both valid. We are going to improve this in the future.

See the documentation for more info about how routes are built.

Finally, if you need to extract a header or its value you can use one of the predefined HeaderDirectives that simplify working with request headers a lot.

like image 113
jrudolph Avatar answered Oct 05 '22 10:10

jrudolph