request.headers does not return a hash, but an instance of ActionDispatch::Http::Headers, which is a wrapper around rack env.
ActionDispatch::Http::Headers implements many methods like [] and []= which make it behave like a hash, but it doesn't override the default inspect, hence you can't see the key-value pairs by just p or pp it.
You can, however, see the request headers in the rack env:
pp request.headers.env.select{|k, _| k =~ /^HTTP_/}
Remember that the request headers in rack env are the upcased, underscored and HTTP_ prefixed version of the original http request headers.
UPDATE
Actually there are a finite set of request headers that are not prefixed HTTP_. These (capitalized and underscored) header names are stored in ActionDispatch::Http::Headers::CGI_VARIABLES. I list them below:
    AUTH_TYPE
    CONTENT_LENGTH
    CONTENT_TYPE
    GATEWAY_INTERFACE
    HTTPS
    PATH_INFO
    PATH_TRANSLATED
    QUERY_STRING
    REMOTE_ADDR
    REMOTE_HOST
    REMOTE_IDENT
    REMOTE_USER
    REQUEST_METHOD
    SCRIPT_NAME
    SERVER_NAME
    SERVER_PORT
    SERVER_PROTOCOL
    SERVER_SOFTWARE
So the full version of listing request headers would be
pp request.headers.env.select{|k, _| k.in?(ActionDispatch::Http::Headers::CGI_VARIABLES) || k =~ /^HTTP_/}