Skip to content

Commit

Permalink
Some restructuring, passthrough mode and direct mode implemented.
Browse files Browse the repository at this point in the history
  • Loading branch information
ioj-x30d committed Feb 9, 2012
1 parent 1affe91 commit f842a45
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 8 deletions.
43 changes: 43 additions & 0 deletions README
Original file line number Diff line number Diff line change
@@ -1,3 +1,46 @@
// vim:set ts=8 sts=8 sw=8 noet ai ff=unix:

A Sinatra + JRuby app to mock simple http services.

Just a prototype.


Match Behaviour:

Rules may contain various JSON object keys which control how the
rules behave.

Rules must contain a key named "strings", which must be a JSON
array of strings. All the strings specified here must be present
in the request entity (POST body).
Special case: If the list has only item, the string "default", this
is taken to mean a positive match.

Response data for a given request may be provided directly in the
JSON, if specified as the value of a key named "response".

A response can be kept in a file and its name may be referenced as
the value of a key named "file".
Note that such files should be in UTF-8.

A rule may have a key named "service", with its value being a URI
to a http service. In this case, the request will be POSTed to that
service, and its response will be sent back to the client.

The existence of these keys are checked in the above order.

For "response" and "file", the JSON should contain a key named
"content_type" that specifies a valid MIME type.

Any rule may contain a key named "path_info". If specified, the
path_info used when POSTing to Alecto must end with the value of
"path_info" specified in the rule.
If path_info is specified in a rule, but does not match the
path_info in the http request, the match is considered to have
failed. Note that this occurs before the body content string
comparison.

Combined with "default", "path_info" can be used to set up a real
service to serve most requests, leaving us free to intercept the
(hopefully few) requests we are interested in.

11 changes: 10 additions & 1 deletion rules.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@
"number": 1,
"description": "GetGameInformation Langoddsen test",
"strings": [ "GameInformationRequest", "GameNo>12</", "NoOfBetObjects>117</" ],
"response": "responses/sample-langoddsen.xml"
"content_type": "application/xml;charset=UTF-8",
"file": "responses/sample-langoddsen.xml"
},
{
"number": 5,
"description": "Passthrough test",
"path_info": "/GetCustomerProfile_TesteLykken_5.3/servlet/GetCustomerProfileServlet",
"strings": [ "default" ],
"service": "http://s1jis001.norsk-tipping.no:7080/GetCustomerProfile_TesteLykken_5.3/servlet/GetCustomerProfileServlet",
"content_type": "application/xml"
}
]
}
89 changes: 82 additions & 7 deletions shim_classes.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#! jruby
# vim:set ts=8 sts=4 sw=4 ai et:

require 'net/http'
require 'uri'
require 'hashie/mash'
require 'json'
require 'pp'

module ShimUtil
# A utility method to check of a set of strings are present in
# another string (where), in the order they are given to the
Expand All @@ -21,6 +27,27 @@ def contains_in_order(where, *what)
return true
end

# TODO: use Apache HttpClient
def POST(uri, headers, data)
parsed_uri = URI.parse(uri)
http = Net::HTTP.new(parsed_uri.host, parsed_uri.port)
resp = http.post(parsed_uri.path, data, headers)

if resp.code == '200' then
[ resp.code.to_i, resp.body, resp ]
else
[ resp.code.to_i, nil, resp ]
end
end

def passthrough(headers, body, target_url)
if not headers.key?('User-Agent') then
headers['User-Agent'] = 'Alecto'
end

POST(target_url, headers, body)
end


def valid_json_rulespec(rule)
if rule.nil? then
Expand All @@ -38,14 +65,56 @@ def valid_json_rulespec(rule)
return false
end

if not File.exists?(rule.response) then
$stderr.puts "WARNING: Response file #{rule.response} used by rule #{rule.number}/#{rule.description} not on disk (yet)"
# return true
if rule.key?('response') then
return false if rule.response.class != String
return false if rule.response.length < 1
elsif rule.key?('file') then
if not File.exists?(rule.file) then
$stderr.puts "WARNING: Response file #{rule.response} used by rule #{rule.number}/#{rule.description} not on disk (yet)"
# return true
end
elsif rule.key?('service') then
true
else
return false # no action specified
end

return true
end

def process_shim_request(rule, shimreq)
ret_data = nil
ret_headers = {}
ret_code = 500
if rule['response'] then
ret_data = rule.response
ret_headers['Content-Type'] = rule.content_type;
ret_code = 200
elsif rule['file'] then
ret_data = open(rule.file, 'r:UTF-8').read
ret_headers['Content-Type'] = rule.content_type;
ret_code = 200
elsif rule['service'] then
ret_code, ret_data, http_resp = *passthrough({}, shimreq.body, rule.service)
tmp = http_resp.to_hash
# now tmp is like:
# {"server"=>["Sun GlassFish Enterprise Server v2.1 Patch02"],
# "content-type"=>["application/xml;charset=UTF-8"],
# "content-length"=>["898"],
# "date"=>["Thu, 09 Feb 2012 14:30:43 GMT"],
# "connection"=>["close"]}
http_resp.to_hash.map do |k, v|
ret_headers['X-Backend-' + k] = v[0]
end

# ret_headers.delete('server')
# ret_headers.delete('content-length')
# ret_headers.delete('date')
end

succeeded = if ret_code == 200 then true else false end
MatchResult.new(succeeded, ShimResponse.new(ret_code, ret_headers, ret_data))
end

# Load simple rules from a text file, and generate lambdas
def make_matchers
Expand All @@ -64,10 +133,15 @@ def make_matchers

matcher = lambda do |shimreq|
# TODO: rule.number, rule.description should be embedded.
if contains_in_order(shimreq.body, *rule.strings) then
return MatchResult.new(true, ShimResponse.new(200,
{ 'Content-Type' => 'application/xml;charset=UTF-8' },
open(rule.response).read))

# if path_info is specified in the rule, it must match what the client sent.
if rule.key?('path_info') and not shimreq.path_info.end_with?(rule.path_info) then
return nil
end

if contains_in_order(shimreq.body, *rule.strings) or
rule.strings.length > 0 and rule.strings[0].downcase == 'default' then
process_shim_request(rule, shimreq)
end
end

Expand All @@ -80,6 +154,7 @@ def make_matchers


class ShimRequest
# TODO: headers
attr_reader :request_method, :query_string, :path_info, :body

def initialize(sinatra_request)
Expand Down

0 comments on commit f842a45

Please sign in to comment.