Skip to content

Commit

Permalink
Added a number of enhancements, including JSON as primary argument pa…
Browse files Browse the repository at this point in the history
…ssing. Pulling this branch to experiment with environment rather than arguments.
  • Loading branch information
ryanuber committed Aug 30, 2013
1 parent f15cdee commit 2ab5be6
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 38 deletions.
17 changes: 8 additions & 9 deletions lib/oaf/http.rb
Expand Up @@ -54,32 +54,31 @@ def parse_response output
# == Parameters:
# path::
# The path in which to search for files
#
# port::
# The TCP port to listen on
#
def serve path, port
server = WEBrick::HTTPServer.new :Port => port
server.mount_proc '/' do |req, res|
req_body = ''
req_headers = Oaf::Util.format_hash req.header
req_headers = Oaf::Util.arg_format req.header.to_hash
req_query = Oaf::Util.arg_format req.query
if ['POST', 'PUT'].member? req.request_method
begin
req_body = req.body
rescue WEBrick::HTTPStatus::LengthRequired
true # without this, coverage is not collected.
end
else
req_body = req.query
end
req_body = Oaf::Util.arg_format req_body, false
file = Oaf::Util.get_request_file path, req.path, req.request_method
out = Oaf::Util.get_output file, req_headers, req_body
headers, status, body = Oaf::HTTP.parse_response out
headers.each do |name, value|
out = Oaf::Util.get_output file, req_headers, req_body, req_query
res_headers, res_status, res_body = Oaf::HTTP.parse_response out
res_headers.each do |name, value|
res.header[name] = value
end
res.status = status
res.body = body
res.status = res_status
res.body = res_body
end
trap 'INT' do server.shutdown end
server.start
Expand Down
60 changes: 48 additions & 12 deletions lib/oaf/util.rb
Expand Up @@ -21,6 +21,7 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

require 'open3'
require 'json'

module Oaf

Expand Down Expand Up @@ -85,23 +86,56 @@ def is_http_status? code
.concat((500..505).to_a).include? code.to_i
end

# Format a hash in preparation for passing it to an executable program as
# Format data in preparation for passing it to an executable program as
# an argument on the command line.
#
# == Parameters:
# headers::
# A hash in the form `name` => `value` containing pairs of headers.
# data::
# Some data to prepare
# flatten::
# A boolean flag determining whether or not to flatten the data
# structure before returning it. Default is true.
#
# == Returns:
# A JSON-encoded string
#
def arg_format data, flatten=true
data = Oaf::Util.arg_flatten data if flatten
if data.kind_of? Hash or data.kind_of? Array
result = data
else
result = [data]
end
JSON.dump result
end

# Flatten arguments in preparation for passing them around. This is mainly
# for ease of use so that we don't go indexing into hashes and arrays here
# and there for no reason.
#
# == Parameters:
# data::
# The data to flatten
#
# == Returns:
# A comma-delimited, colon-separated list of header names and values. The
# return value of this function should be parsed according to RFC2616.
# A flattened data structure similar to the input.
#
def format_hash hash
result = ''
hash.each do |name, value|
result += "#{name}:#{value},"
def arg_flatten data
result = data
if data.kind_of? Hash
result = Hash.new
data.each do |key, val|
val = val.join if val.kind_of? Array and val.length == 1
result[key] = val
end
elsif data.kind_of? Array
result = Array.new
data.each do |item|
item = item.join if item.kind_of? Array and item.length == 1
result << item
end
end
result.sub!(/,$/, '')
result
end

# Given an array of text lines, iterate over each of them and determine if
Expand Down Expand Up @@ -202,15 +236,17 @@ def run_buffered command
# The HTTP request headers to pass
# body::
# The HTTP request body to pass
# query::
# The HTTP query parameters to pass
#
# == Returns:
# The result from the file, or a default result if the file is not found.
#
def get_output file, headers, body
def get_output file, headers=nil, body=nil, query=nil
if file.nil?
out = Oaf::Util.get_default_response
elsif File.executable? file
out = Oaf::Util.run_buffered "#{file} '#{headers}' '#{body}'"
out = Oaf::Util.run_buffered "#{file} '#{headers}' '#{query}' '#{body}'"
else
out = File.open(file).read
end
Expand Down
2 changes: 1 addition & 1 deletion lib/oaf/version.rb
Expand Up @@ -21,5 +21,5 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

module Oaf
VERSION = '0.1.17'
VERSION = '0.1.18'
end
1 change: 1 addition & 0 deletions oaf.gemspec
Expand Up @@ -19,6 +19,7 @@ Gem::Specification.new do |s|
s.required_ruby_version = '>= 1.8'

s.add_runtime_dependency 'bundler'
s.add_runtime_dependency 'json'

s.add_development_dependency 'rake'
s.add_development_dependency 'rspec'
Expand Down
87 changes: 71 additions & 16 deletions spec/oaf/util_spec.rb
Expand Up @@ -80,23 +80,53 @@ module Oaf
end
end

describe "Format Request Headers" do
describe "Argument Format" do
it "should return a single key/value for just one header" do
headers = [['x-powered-by', 'oaf']]
result = Oaf::Util.format_hash headers
result.should eq('x-powered-by:oaf')
headers = {'x-powered-by' => 'oaf'}
result = Oaf::Util.arg_format headers
result.should eq('{"x-powered-by":"oaf"}')
end

it "should return a comma-delimited list for multiple headers" do
headers = [['x-powered-by', 'oaf'], ['content-type', 'text/plain']]
result = Oaf::Util.format_hash headers
result.should eq('x-powered-by:oaf,content-type:text/plain')
it "should return multiple values for more than one header" do
headers = {'x-powered-by' => 'oaf', 'content-type' => 'text/plain'}
result = Oaf::Util.arg_format headers
result.should eq('{"x-powered-by":"oaf","content-type":"text/plain"}')
end

it "should return nil if no headers present" do
headers = []
result = Oaf::Util.format_hash headers
result.should be_nil
it "should flatten hash values if they are single-value arrays" do
headers = {'x-powered-by' => ['oaf']}
result = Oaf::Util.arg_format headers
result.should eq('{"x-powered-by":"oaf"}')
end

it "should flatten array values if they are single-value arrays" do
headers = ['x-powered-by', ['oaf']]
result = Oaf::Util.arg_format headers
result.should eq('["x-powered-by","oaf"]')
end

it "should preserve multiple values in multi-value arrays" do
headers = {'x-powered-by' => ['oaf', 'oaf']}
result = Oaf::Util.arg_format headers
result.should eq('{"x-powered-by":["oaf","oaf"]}')
end

it "should not flatten values if requested" do
headers = {'x-powered-by' => ['oaf']}
result = Oaf::Util.arg_format headers, false
result.should eq('{"x-powered-by":["oaf"]}')
end

it "should return valid JSON if no headers present" do
headers = Hash.new
result = Oaf::Util.arg_format headers
result.should eq('{}')
end

it "should return valid JSON if a non-hash is passed in" do
headers = 0
result = Oaf::Util.arg_format headers
result.should eq("[0]")
end
end

Expand Down Expand Up @@ -194,32 +224,57 @@ module Oaf
@f3.chmod 0755
@f3.write "#!/bin/bash\necho 'test1'\necho 'test2' 1>&2\n"
@f3.close

@f4 = Tempfile.new 'oaf'
@f4.chmod 0755
@f4.write "#!/bin/bash\necho \"$1\"\necho \"$2\"\necho \"$3\"\n"
@f4.close

@f5 = Tempfile.new 'oaf'
@f5.chmod 0755
@f5.write "#!/bin/bash\necho ${#\@}\n"
@f5.close
end

after(:all) do
@f1.delete
@f2.delete
@f3.delete
@f4.delete
@f5.delete
end

it "should execute a file if it is executable" do
result = Oaf::Util.get_output @f1.path, nil, nil
result = Oaf::Util.get_output @f1.path
result.should eq("This is a test\n")
end

it "should read file contents if it is not executable" do
result = Oaf::Util.get_output @f2.path, nil, nil
result = Oaf::Util.get_output @f2.path
result.should eq("This is a test\n")
end

it "should assume safe defaults if the file doesnt exist" do
result = Oaf::Util.get_output nil, nil, nil
result = Oaf::Util.get_output nil
result.should eq(Oaf::Util.get_default_response)
end

it "should catch stderr output instead of dumping it" do
result = Oaf::Util.get_output @f3.path, nil, nil
result = Oaf::Util.get_output @f3.path
result.should eq("test1\ntest2\n")
end

it "should pass arguments for headers and request body/params" do
headers = Oaf::Util.arg_format({'x-powered-by' => 'oaf'})
body = Oaf::Util.arg_format({'mydata' => 'myvalue'})
query = Oaf::Util.arg_format({'myparam' => 'myvalue'})
result = Oaf::Util.get_output @f4.path, headers, body, query
result.should eq("#{headers}\n#{query}\n#{body}\n")
end

it "should still pass arguments without body, query or headers" do
result = Oaf::Util.get_output @f5.path
result.should eq("3\n")
end
end
end
4 changes: 4 additions & 0 deletions spec/spec_helper.rb
@@ -1,6 +1,10 @@
require 'simplecov'
require 'coveralls'
Coveralls.wear!
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
SimpleCov::Formatter::HTMLFormatter,
Coveralls::SimpleCov::Formatter
]
SimpleCov.start do
add_filter '/spec/'
end
Expand Down

0 comments on commit 2ab5be6

Please sign in to comment.