Skip to content

HTTP Headers

Mario Izquierdo edited this page Sep 17, 2018 · 1 revision

Sometimes, you need to send custom HTTP headers.

For Twirp, HTTP headers are a transport implementation detail, you should not have to worry about headers, but maybe your HTTP middleware requires them.

Client side

Send HTTP Headers with client requests

All client methods can have options as an optional second parameter. Use the :headers option to send custom headers. For example:

client.hello({name: 'World'}, headers: {'X-My-Header' => 'foobar', 'Auth-Token' => 'fxdafxda'})

Read HTTP Headers from responses

Twirp client responses are objects that depend only on the Protobuf response. HTTP headers in the response can not be used by the Twirp client in any way.

However, the Twirp client can be configured with Faraday, that can have custom middleware to handle response headers (for example to manage a local cache).

Server side

Respond with HTTP Headers on server responses

In your service handler implementation, set the special env[:http_response_headers] value to set response headers. For example:

class HelloWorldHandler
  def hello(req, env)
    env[:http_response_headers]['Cache-Control'] = 'public, max-age=60'
    {message: "Hello #{req.name}"}
  end
end

Read HTTP Headers from requests

Twirp service handler methods are abstracted away from HTTP, therefore they don't have direct access to HTTP Headers. However, they receive an env argument with values that can be modified by before hooks. Service before hooks have access to the raw rack_env, where they can read headers and add values to the twirp env.

For example, to read the 'X-My-Header' HTTP header, the service before hook can be like this:

service = Example::HelloWorld::HelloWorldService.new(handler)
service.before do |rack_env, env|
  env[:my_x_header] = rack_env['HTTP_MY_X_HEADER'] # << Rack HTTP header
end

Now the handler implementation can access the custom property env[:my_x_header], for example:

class HelloWorldHandler
  def hello(req, env)
    puts env[:my_x_header] # << value from header
    {message: "Hello #{req.name}"}
  end
end

While this approach requires more setup, it effectively separates HTTP details from the Twirp service implementation, which depends only on the protobuf request object and the Twirp environment. When testing this code, you only need to add the env[:my_x_header] value and forget about HTTP details (e.g. service.call_rpc(:Hello, {name: 'foo'}, env={my_x_header: 'foobar'}). See Unit Tests for more info on testing.