Add an option event_headers_style to PostAgent

It is defaulted to `capitalized`, normalizing emitted headers by
capitalizing the names so downstream agents don't need to bother with
the letter cases.

Previously, the style of header names in an emitted event would vary
depending on the server and the backend HTTP library.  Most notably, the
`net_http` backend downcases header names whereas other backends do not.

For backward compatibility, preexisting PostAgents are automatically
configured to have `event_headers_style` set to `raw` via migration.
This commit is contained in:
Akinori MUSHA 2016-04-05 16:52:44 +09:00
parent feeb3d8903
commit b095317e4c
3 changed files with 72 additions and 3 deletions

View file

@ -25,6 +25,13 @@ module Agents
If `emit_events` is set to `true`, the server response will be emitted as an Event and can be fed to a WebsiteAgent for parsing (using its `data_from_event` and `type` options). No data processing
will be attempted by this Agent, so the Event's "body" value will always be raw text.
The Event will also have a "headers" hash and a "status" integer value.
Set `event_headers_style` to one of the following values to normalize the keys of "headers" for downstream agents' convenience:
* `capitalized` (default) - Header names are capitalized; e.g. "Content-Type"
* `downcased` - Header names are downcased; e.g. "content-type"
* `snakecased` - Header names are snakecased; e.g. "content_type"
* `raw` - Backward compatibility option to leave them unmodified from what the underlying HTTP library returns.
Other Options:
@ -92,6 +99,12 @@ module Agents
errors.add(:base, "if provided, emit_events must be true or false")
end
begin
normalize_response_headers({})
rescue ArgumentError => e
errors.add(:base, e.message)
end
unless %w[post get put delete patch].include?(method)
errors.add(:base, "method must be 'post', 'get', 'put', 'delete', or 'patch'")
end
@ -124,6 +137,29 @@ module Agents
private
def normalize_response_headers(headers)
case interpolated['event_headers_style']
when nil, '', 'capitalized'
normalize = ->name {
name.gsub(/(?:\A|(?<=-))([[:alpha:]])|([[:alpha:]]+)/) {
$1 ? $1.upcase : $2.downcase
}
}
when 'downcased'
normalize = :downcase.to_proc
when 'snakecased', nil
normalize = ->name { name.tr('A-Z-', 'a-z_') }
when 'raw'
normalize = ->name { name } # :itself.to_proc in Ruby >= 2.2
else
raise ArgumentError, "if provided, event_headers_style must be 'capitalized', 'downcased', 'snakecased' or 'raw'"
end
headers.each_with_object({}) { |(key, value), hash|
hash[normalize[key]] = value
}
end
def handle(data, payload = {})
url = interpolated(payload)[:post_url]
headers = headers()
@ -156,7 +192,11 @@ module Agents
}
if boolify(interpolated['emit_events'])
create_event payload: { body: response.body, headers: response.headers, status: response.status }
create_event payload: {
body: response.body,
headers: normalize_response_headers(response.headers),
status: response.status
}
end
end
end

View file

@ -0,0 +1,11 @@
class PostAgentSetEventHeaderStyle < ActiveRecord::Migration
def up
Agent.of_type("Agents::PostAgent").each do |post_agent|
if post_agent.send(:boolify, post_agent.options['emit_events']) &&
!post_agent.options.key?('event_headers_style')
post_agent.options['event_headers_style'] = 'raw'
post_agent.save!
end
end
end
end

View file

@ -52,7 +52,7 @@ describe Agents::PostAgent do
raise "unexpected Content-Type: #{content_type}"
end
end
{ status: 200, body: "<html>a webpage!</html>", headers: { 'Content-Type' => 'text/html' } }
{ status: 200, body: "<html>a webpage!</html>", headers: { 'Content-type' => 'text/html' } }
}
end
@ -226,10 +226,28 @@ describe Agents::PostAgent do
expect(@checker.events.last.payload['body']).to eq '<html>a webpage!</html>'
end
it "emits the response headers" do
it "emits the response headers capitalized by default" do
@checker.check
expect(@checker.events.last.payload['headers']).to eq({ 'Content-Type' => 'text/html' })
end
it "emits the response headers capitalized" do
@checker.options['event_headers_style'] = 'capitalized'
@checker.check
expect(@checker.events.last.payload['headers']).to eq({ 'Content-Type' => 'text/html' })
end
it "emits the response headers downcased" do
@checker.options['event_headers_style'] = 'downcased'
@checker.check
expect(@checker.events.last.payload['headers']).to eq({ 'content-type' => 'text/html' })
end
it "emits the response headers snakecased" do
@checker.options['event_headers_style'] = 'snakecased'
@checker.check
expect(@checker.events.last.payload['headers']).to eq({ 'content_type' => 'text/html' })
end
end
end
end