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.