diff options
28 files changed, 382 insertions, 396 deletions
@@ -2,3 +2,5 @@ source 'https://rubygems.org' # Specify your gem's dependencies in keymachine.gemspec gemspec + +gem 'byebug' diff --git a/bin/nickserver b/bin/nickserver index f3795bf..93dfd53 100755 --- a/bin/nickserver +++ b/bin/nickserver @@ -20,7 +20,6 @@ load_local_gem require 'nickserver' Nickserver::Daemon.run('nickserver') do - EventMachine.run do - Nickserver::Server.start - end -end
\ No newline at end of file + Nickserver::Server.start + sleep +end diff --git a/lib/nickserver/adapters/celluloid_http.rb b/lib/nickserver/adapters/celluloid_http.rb new file mode 100644 index 0000000..d326b8a --- /dev/null +++ b/lib/nickserver/adapters/celluloid_http.rb @@ -0,0 +1,25 @@ +require 'nickserver/adapters' +silence_warnings do + require 'celluloid/io' +end +require 'http' + +module Nickserver::Adapters + class CelluloidHttp + include Celluloid::IO + + def get(url, options = {}) + response = HTTP.get url, + params: options[:query], + ssl_context: ctx, + ssl_socket_class: Celluloid::IO::SSLSocket + return response.code, response.to_s + end + + def ctx + OpenSSL::SSL::SSLContext.new.tap do |ctx| + ctx.ca_file = Nickserver::Config.hkp_ca_file + end + end + end +end diff --git a/lib/nickserver/adapters/em_http.rb b/lib/nickserver/adapters/em_http.rb deleted file mode 100644 index 16db5ae..0000000 --- a/lib/nickserver/adapters/em_http.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'nickserver/adapters' -require 'em-http' - -module Nickserver::Adapters - class EmHttp - - def initialize - @timeout = 5 - end - - def get(url, options = {}) - get_request(url, options).callback {|http| - yield http.response_header.status, http.response - }.errback {|http| - yield 0, http.error - } - end - - def get_request(url, options = {}) - @request = EventMachine::HttpRequest.new(url) - @request.get timeout: @timeout, query: options[:query] - end - end -end diff --git a/lib/nickserver/couch_db/source.rb b/lib/nickserver/couch_db/source.rb index b30fdfc..7c3ad95 100644 --- a/lib/nickserver/couch_db/source.rb +++ b/lib/nickserver/couch_db/source.rb @@ -11,9 +11,8 @@ module Nickserver::CouchDB VIEW = '/_design/Identity/_view/pgp_key_by_email' def query(nick) - adapter.get url, query: query_for(nick) do |status, body| - yield Response.new(nick, status: status, body: body) - end + status, body = adapter.get url, query: query_for(nick) + Response.new(nick, status: status, body: body) end protected diff --git a/lib/nickserver/em_server.rb b/lib/nickserver/em_server.rb deleted file mode 100644 index bcec4cd..0000000 --- a/lib/nickserver/em_server.rb +++ /dev/null @@ -1,51 +0,0 @@ -require 'eventmachine' -silence_warnings do - require 'evma_httpserver' -end -require 'nickserver/request_handler' - -module Nickserver - class EmServer < EM::Connection - include EM::HttpServer - - def self.start(options = {}) - EventMachine.run do - EM.start_server options[:host], options[:port], self - end - end - - def post_init - super - no_environment_strings - end - - def process_http_request - handler.respond_to params, @http_headers - end - - def send_response(options = {}) - response = EM::DelegatedHttpResponse.new(self) - response.status = options[:status] - response.content_type options[:content_type] - response.content = options[:content] - silence_warnings do - response.send_response - end - end - - private - - def handler - @handler ||= RequestHandler.new(self, Nickserver::Adapters::EmHttp.new) - end - - def params - if @http_query_string - CGI.parse(@http_query_string) - elsif @http_post_content - CGI.parse(@http_post_content) - end - end - - end -end diff --git a/lib/nickserver/email_address.rb b/lib/nickserver/email_address.rb index 26053a2..2b3f2c2 100644 --- a/lib/nickserver/email_address.rb +++ b/lib/nickserver/email_address.rb @@ -1,25 +1,45 @@ -# -# This rather crazy regexp is from here: http://code.iamcal.com/php/rfc822/ -# Licensed GPLv3 -# -# It is too liberal, allowing "!@x" as a valid address, for example, but it does -# follow the specification rather closely. -# - module Nickserver - EmailAddress = begin - qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]' - dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]' - atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+' - quoted_pair = '\\x5c[\\x00-\\x7f]' - domain_literal = "\\x5b(?:#{dtext}|#{quoted_pair})*\\x5d" - quoted_string = "\\x22(?:#{qtext}|#{quoted_pair})*\\x22" - domain_ref = atom - sub_domain = "(?:#{domain_ref}|#{domain_literal})" - word = "(?:#{atom}|#{quoted_string})" - domain = "#{sub_domain}(?:\\x2e#{sub_domain})*" - local_part = "#{word}(?:\\x2e#{word})*" - addr_spec = "#{local_part}\\x40#{domain}" - /\A#{addr_spec}\z/n + class EmailAddress + + REGEXP = begin + qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]' + dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]' + atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+' + quoted_pair = '\\x5c[\\x00-\\x7f]' + domain_literal = "\\x5b(?:#{dtext}|#{quoted_pair})*\\x5d" + quoted_string = "\\x22(?:#{qtext}|#{quoted_pair})*\\x22" + domain_ref = atom + sub_domain = "(?:#{domain_ref}|#{domain_literal})" + word = "(?:#{atom}|#{quoted_string})" + domain = "#{sub_domain}(?:\\x2e#{sub_domain})*" + local_part = "#{word}(?:\\x2e#{word})*" + addr_spec = "#{local_part}\\x40#{domain}" + /\A#{addr_spec}\z/n + end + + def initialize(address) + @address = address.to_s + end + + def valid? + address =~ REGEXP + end + + def invalid? + !valid? + end + + def domain?(domain) + address.end_with? "@#{domain}" + end + + def to_s + address + end + + protected + + attr_reader :address + end end diff --git a/lib/nickserver/hkp/client.rb b/lib/nickserver/hkp/client.rb index 6bd239d..1fbe7a2 100644 --- a/lib/nickserver/hkp/client.rb +++ b/lib/nickserver/hkp/client.rb @@ -21,7 +21,7 @@ module Nickserver; module Hkp # used to fetch an array of KeyInfo objects that match the given email # def get_key_infos_by_email(email, &block) - get op: 'vindex', search: email, fingerprint: 'on', &block + get op: 'vindex', search: email.to_s, fingerprint: 'on' end # @@ -35,10 +35,11 @@ module Nickserver; module Hkp attr_reader :adapter - def get(query, &block) + def get(query) # in practice, exact=on seems to have no effect query = {exact: 'on', options: 'mr'}.merge query - adapter.get Config.hkp_url, query: query, &block + response = adapter.get Config.hkp_url, query: query + return response end end end; end diff --git a/lib/nickserver/hkp/parse_key_info.rb b/lib/nickserver/hkp/parse_key_info.rb index 9d59d6b..2f928a0 100644 --- a/lib/nickserver/hkp/parse_key_info.rb +++ b/lib/nickserver/hkp/parse_key_info.rb @@ -19,7 +19,8 @@ module Nickserver; module Hkp @vindex_result = vindex_result end - def status_for(uid) + def status_for(email) + uid = email.to_s if hkp_ok? && keys(uid).empty? error_status(uid) else @@ -27,7 +28,8 @@ module Nickserver; module Hkp end end - def response_for(uid) + def response_for(email) + uid = email.to_s if keys(uid).any? keys(uid) else @@ -35,6 +37,8 @@ module Nickserver; module Hkp end end + protected + def keys(uid) key_infos(uid).reject { |key| error_for_key(key) } end @@ -47,8 +51,6 @@ module Nickserver; module Hkp end end - protected - attr_reader :status attr_reader :vindex_result diff --git a/lib/nickserver/hkp/source.rb b/lib/nickserver/hkp/source.rb index 8b2a62b..0d79856 100644 --- a/lib/nickserver/hkp/source.rb +++ b/lib/nickserver/hkp/source.rb @@ -1,3 +1,4 @@ +require 'nickserver/source' require 'nickserver/response' require 'nickserver/hkp/response' require 'nickserver/hkp/client' @@ -13,22 +14,20 @@ require "nickserver/hkp/key_info" module Nickserver; module Hkp class Source < Nickserver::Source - def query(nick, &block) - search(nick) do |status, response| - if status == 200 - best = pick_best_key(response) - get_key_by_fingerprint(nick, best.keyid, &block) - else - yield Nickserver::Response.new(status, response) - end + def query(nick) + status, response = search(nick) + if status == 200 + best = pick_best_key(response) + get_key_by_fingerprint(nick, best.keyid) + else + Nickserver::Response.new(status, response) end end - def search(nick, &block) - client.get_key_infos_by_email(nick) do |status, response| - parser = ParseKeyInfo.new status, response - yield parser.status_for(nick), parser.response_for(nick) - end + def search(nick) + status, response = client.get_key_infos_by_email(nick) + parser = ParseKeyInfo.new status, response + return parser.status_for(nick), parser.response_for(nick) end protected @@ -44,12 +43,11 @@ module Nickserver; module Hkp end def get_key_by_fingerprint(nick, fingerprint) - client.get_key_by_fingerprint fingerprint do |status, response| - if status == 200 - yield Response.new nick, response - else - yield Nickserver::Response.new status, "HKP Request failed" - end + status, response = client.get_key_by_fingerprint fingerprint + if status == 200 + Response.new nick, response + else + Nickserver::Response.new status, "HKP Request failed" end end diff --git a/lib/nickserver/nickname.rb b/lib/nickserver/nickname.rb deleted file mode 100644 index 938d4a4..0000000 --- a/lib/nickserver/nickname.rb +++ /dev/null @@ -1,51 +0,0 @@ -module Nickserver - class Nickname - - EmailAddress = begin - qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]' - dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]' - atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+' - quoted_pair = '\\x5c[\\x00-\\x7f]' - domain_literal = "\\x5b(?:#{dtext}|#{quoted_pair})*\\x5d" - quoted_string = "\\x22(?:#{qtext}|#{quoted_pair})*\\x22" - domain_ref = atom - sub_domain = "(?:#{domain_ref}|#{domain_literal})" - word = "(?:#{atom}|#{quoted_string})" - domain = "#{sub_domain}(?:\\x2e#{sub_domain})*" - local_part = "#{word}(?:\\x2e#{word})*" - addr_spec = "#{local_part}\\x40#{domain}" - /\A#{addr_spec}\z/n - end - - LOCAL_DOMAIN = 'test.me' - - def initialize(address) - @address = address.to_s - end - - def valid? - address =~ EmailAddress - end - - def invalid? - !valid? - end - - def local? - address.end_with? LOCAL_DOMAIN - end - - def remote? - !local? - end - - def to_s - address - end - - protected - - attr_reader :address - - end -end diff --git a/lib/nickserver/reel_server.rb b/lib/nickserver/reel_server.rb new file mode 100644 index 0000000..6f05e0b --- /dev/null +++ b/lib/nickserver/reel_server.rb @@ -0,0 +1,47 @@ +silence_warnings do + require 'reel' +end +require 'nickserver/adapters/celluloid_http' +require 'nickserver/request_handler' + +module Nickserver + class ReelServer < Reel::Server::HTTP + + def self.start(options = {}) + new(options[:host], options[:port]) + end + + def initialize(host = "127.0.0.1", port = 3000) + super(host, port, &method(:on_connection)) + end + + def handle_connection(*args) + silence_warnings do + super + end + end + + def on_connection(connection) + connection.each_request do |request| + handler = handler_for(request) + handler.respond_to params(request), request.headers + end + end + + + protected + + def handler_for(request) + RequestHandler.new(request, Nickserver::Adapters::CelluloidHttp.new) + end + + def params(request) + if request.query_string + CGI.parse request.query_string + else + CGI.parse request.body.to_s + end + end + + end +end diff --git a/lib/nickserver/request_handler.rb b/lib/nickserver/request_handler.rb index 26b6ec1..608db83 100644 --- a/lib/nickserver/request_handler.rb +++ b/lib/nickserver/request_handler.rb @@ -1,3 +1,6 @@ +require 'nickserver/hkp/source' +require 'nickserver/couch_db/source' + module Nickserver class RequestHandler @@ -7,13 +10,13 @@ module Nickserver end def respond_to(params, headers) - uid = get_uid_from_params(params) - if uid.nil? + email = get_email_from_params(params) + if email.nil? send_not_found - elsif uid !~ EmailAddress + elsif email.invalid? send_error("Not a valid address") else - send_key(uid, headers) + send_key(email, headers) end rescue RuntimeError => exc puts "Error: #{exc}" @@ -23,23 +26,22 @@ module Nickserver protected - def get_uid_from_params(params) + def get_email_from_params(params) if params && params["address"] && params["address"].any? - return params["address"].first - else - return nil + EmailAddress.new(params["address"].first) end end - def send_key(uid, headers) - if local_address?(uid, headers) + def send_key(email, headers) + if local_address?(email, headers) source = Nickserver::CouchDB::Source.new(adapter) else source = Nickserver::Hkp::Source.new(adapter) end - source.query(uid) do |response| - send_response(status: response.status, content: response.content) - end + response = source.query(email) + send_response response.status, response.content + rescue MissingHostHeader + send_error("HTTP request must include a Host header.") end # @@ -48,38 +50,32 @@ module Nickserver # # If 'domain' is not configured, we rely on the Host header of the HTTP request. # - def local_address?(uid, headers) - uid_domain = uid.sub(/^.*@(.*)$/, "\\1") - if Config.domain - return uid_domain == Config.domain - else - # no domain configured, use Host header - host_header = headers.split(/\0/).grep(/^Host: /).first - if host_header.nil? - send_error("HTTP request must include a Host header.") - else - host = host_header.split(':')[1].strip.sub(/^nicknym\./, '') - return uid_domain == host - end - end + def local_address?(email, headers) + email.domain?(Config.domain || domain_from_headers(headers)) end - def send_error(msg = "not supported") - send_response(status: 500, content: "500 #{msg}\n") + + # no domain configured, use Host header + def domain_from_headers(headers) + host_header = headers['Host'] + raise MissingHostHeader if host_header.nil? + host_header.split(':')[0].strip.sub(/^nicknym\./, '') end - def send_not_found(msg = "Not Found") - send_response(status: 404, content: "404 #{msg}\n") + def send_error(msg = "not supported") + send_response 500, "500 #{msg}\n" end - def send_response(opts = {}) - responder.send_response default_response.merge(opts) + def send_not_found(msg = "Not Found") + send_response 404, "404 #{msg}\n" end - def default_response - {status: 200, content_type: 'text/plain', content: ''} + def send_response(status = 200, content = '') + responder.respond status, content end attr_reader :responder, :adapter + class MissingHostHeader < StandardError + end end end diff --git a/lib/nickserver/server.rb b/lib/nickserver/server.rb index 174d6ac..8f4a49f 100644 --- a/lib/nickserver/server.rb +++ b/lib/nickserver/server.rb @@ -1,21 +1,17 @@ require 'kernel_ext' require 'json' -require 'nickserver/em_server' -require 'nickserver/couch_db/source' -require 'nickserver/hkp/source' -require 'nickserver/adapters/em_http' +require 'nickserver/reel_server' # # This is the main HTTP server that clients connect to in order to fetch keys # -# For info on EM::HttpServer, see https://github.com/eventmachine/evma_httpserver # module Nickserver class Server # - # Starts the Nickserver. Must be run inside an EM.run block. + # Starts the Nickserver. # # Available options: # @@ -33,7 +29,7 @@ module Nickserver puts "Starting nickserver #{options[:host]}:#{options[:port]}" end - Nickserver::EmServer.start(options) + Nickserver::ReelServer.start(options) end diff --git a/lib/server.rb b/lib/server.rb deleted file mode 100644 index 4e7cf51..0000000 --- a/lib/server.rb +++ /dev/null @@ -1,19 +0,0 @@ -class Server - - def process_http_request - lookup.respond_with(Responder) - end - - def lookup - LookupFactory.lookup_for(nick) - end - - def nick - Nickname.new(request.address) - end - - def request - Request.new(params) - end - -end diff --git a/nickserver.gemspec b/nickserver.gemspec index 2efdbb1..9338b3c 100644 --- a/nickserver.gemspec +++ b/nickserver.gemspec @@ -18,10 +18,9 @@ Gem::Specification.new do |gem| gem.require_paths = ["lib"] gem.add_development_dependency 'rake' - gem.add_development_dependency 'minitest', '~> 5.2' - gem.add_development_dependency 'webmock', '~> 1.17' + gem.add_development_dependency 'minitest' + gem.add_development_dependency 'webmock' - gem.add_dependency 'eventmachine', '~> 1.0.9' - gem.add_dependency 'em-http-request', '~> 1.1' - gem.add_dependency 'eventmachine_httpserver', '~> 0.2' + gem.add_dependency 'reel' + gem.add_dependency 'http' end diff --git a/test/helpers/test_adapter.rb b/test/helpers/test_adapter.rb deleted file mode 100644 index 46d4713..0000000 --- a/test/helpers/test_adapter.rb +++ /dev/null @@ -1,10 +0,0 @@ -class TestAdapter - def initialize(status, content) - @status = status - @content = content - end - - def get(url, opts) - yield @status, @content - end -end diff --git a/test/integration/couch_db/source_test.rb b/test/integration/couch_db/source_test.rb index 21e3642..cb1153e 100644 --- a/test/integration/couch_db/source_test.rb +++ b/test/integration/couch_db/source_test.rb @@ -1,6 +1,5 @@ require 'test_helper' require 'file_content' -require 'helpers/test_adapter' require 'nickserver/couch_db/source' module Nickserver::CouchDB @@ -8,12 +7,17 @@ module Nickserver::CouchDB include FileContent def test_couch_query_and_response - adapter = TestAdapter.new 200, file_content(:blue_couchdb_result) + adapter = adapter_returns 200, file_content(:blue_couchdb_result) source = Source.new adapter source.query 'blue@example.org' do |response| assert_equal 200, response.status assert_equal file_content(:blue_nickserver_result), response.content end end + + def adapter_returns(*return_values) + adapter = Minitest::Mock.new + adapter.expect :get, return_values, [String, Hash] + end end end diff --git a/test/integration/hkp_test.rb b/test/integration/hkp_test.rb index a824a3f..09673cf 100644 --- a/test/integration/hkp_test.rb +++ b/test/integration/hkp_test.rb @@ -1,9 +1,20 @@ require 'test_helper' require 'nickserver/hkp/source' -require 'nickserver/adapters/em_http' +require 'nickserver/adapters/celluloid_http' class HkpTest < Minitest::Test + def setup + super + Celluloid.boot + end + + def teardown + Celluloid.shutdown + super + end + + def test_key_info_expired fetch_key_info(:hkp_vindex_result, 'lemur@leap.se') do |keys| assert_equal 1, keys.length, 'should find a single key' @@ -89,9 +100,9 @@ class HkpTest < Minitest::Test ca_file = file_path('mayfirst-ca.pem') real_network do - stub_config(:hkp_url, hkp_url) do - stub_config(:hkp_ca_file, ca_file) do - #stub_config(:hkp_ca_file, file_path('autistici-ca.pem')) do + config.stub(:hkp_url, hkp_url) do + config.stub(:hkp_ca_file, ca_file) do + #config.stub(:hkp_ca_file, file_path('autistici-ca.pem')) do assert File.exist?(Nickserver::Config.hkp_ca_file) uid = 'elijah@riseup.net' assert_key_info_for_uid uid do |keys| @@ -112,26 +123,20 @@ class HkpTest < Minitest::Test end def assert_response_for_uid(uid, &block) - EM.run do - Nickserver::Hkp::Source.new(adapter).query uid do |response| - yield response - EM.stop - end + Nickserver::Hkp::Source.new(adapter).query uid do |response| + yield response end end def assert_key_info_for_uid(uid, &block) - EM.run do - Nickserver::Hkp::Source.new(adapter).search uid do |status, keys| - assert_equal 200, status - yield keys - EM.stop - end + Nickserver::Hkp::Source.new(adapter).search uid do |status, keys| + assert_equal 200, status + yield keys end end def adapter - Nickserver::Adapters::EmHttp.new + Nickserver::Adapters::CelluloidHttp.new end def fetch_key_info(body_source, uid, &block) diff --git a/test/integration/nickserver_test.rb b/test/integration/nickserver_test.rb index b4ff4da..710c3a1 100644 --- a/test/integration/nickserver_test.rb +++ b/test/integration/nickserver_test.rb @@ -4,18 +4,28 @@ require 'json' # # Some important notes to understanding these tests: # -# (1) Requests to localhost always bypass HTTP stub. +# (1) Requests to 127.0.0.1 always bypass HTTP stub. # -# (2) All requests to nickserver are to localhost. +# (2) All requests to nickserver are to 127.0.0.1. # # (3) the "Host" header for requests to nickserver must be set (or Config.domain set) # # (4) When stubbing requests to couchdb, the couchdb host is changed from the -# default (localhost) to a dummy value (notlocalhost). +# default (127.0.0.1) to a dummy value (notlocalhost). # class NickserverTest < Minitest::Test + def setup + super + Celluloid.boot + end + + def teardown + Celluloid.shutdown + super + end + def test_GET_served_via_SKS uid = 'cloudadmin@leap.se' key_id = 'E818C478D3141282F7590D29D041EB11B1647490' @@ -24,9 +34,8 @@ class NickserverTest < Minitest::Test start do params = {query: {"address" => uid}} - get(params) do |http| - assert_equal file_content(:leap_public_key), JSON.parse(http.response)["openpgp"] - stop + get(params) do |response| + assert_equal file_content(:leap_public_key), JSON.parse(response.to_s)["openpgp"] end end end @@ -39,9 +48,8 @@ class NickserverTest < Minitest::Test start do params = {body: {"address" => uid}} - post(params) do |http| - assert_equal file_content(:leap_public_key), JSON.parse(http.response)["openpgp"] - stop + post(params) do |response| + assert_equal file_content(:leap_public_key), JSON.parse(response.to_s)["openpgp"] end end end @@ -51,10 +59,9 @@ class NickserverTest < Minitest::Test uid = "bananas@" + domain stub_couch_response(uid, status: 404) do start do - params = {query: {"address" => uid}, head: {host: domain}} - get(params) do |http| - assert_equal 404, http.response_header.status - stop + params = {query: {"address" => uid}, head: {"Host" => domain}} + get(params) do |response| + assert_equal 404, response.code end end end @@ -66,9 +73,8 @@ class NickserverTest < Minitest::Test stub_couch_response(uid, body: file_content(:empty_couchdb_result)) do start do params = {query: {"address" => uid}, head: {host: domain}} - get(params) do |http| - assert_equal 404, http.response_header.status - stop + get(params) do |response| + assert_equal 404, response.code end end end @@ -79,10 +85,9 @@ class NickserverTest < Minitest::Test uid = "blue@" + domain stub_couch_response(uid, body: file_content(:blue_couchdb_result)) do start do - params = {query: {"address" => uid}, head: {host: domain}} - get(params) do |http| - assert_equal file_content(:blue_nickserver_result), http.response - stop + params = {query: {"address" => uid}, head: {"Host" => domain}} + get(params) do |response| + assert_equal file_content(:blue_nickserver_result), response.to_s end end end @@ -90,9 +95,8 @@ class NickserverTest < Minitest::Test def test_GET_empty start do - get({}) do |http| - assert_equal "404 Not Found\n", http.response - stop + get({}) do |response| + assert_equal "404 Not Found\n", response.to_s end end end @@ -103,53 +107,36 @@ class NickserverTest < Minitest::Test # start nickserver # def start(timeout = 1) - Timeout::timeout(timeout) do - EM.run do - Nickserver::Server.start - EM.epoll - yield - end - end - rescue Timeout::Error - flunk 'EventMachine was not stopped before the timeout expired' + server = Nickserver::ReelServer.new '127.0.0.1', config.port + yield server + ensure + server.terminate if server && server.alive? end # # http GET requests to nickserver # - def get(params, &block) - request(:get, params, &block) + def get(options = {}, &block) + request(:get, params: options[:query], head: options[:head], &block) end # # http POST requests to nickserver # - def post(params, &block) - request(:post, params, &block) + def post(options, &block) + request(:post, params: options[:body], head: options[:head], &block) end # # http request to nickserver # - # this works because http requests to localhost are not stubbed, but requests to other domains are. - # - def request(method, params) - EventMachine::HttpRequest.new("http://localhost:#{Nickserver::Config.port}/").send(method,params).callback {|http| - # p http.response_header.status - # p http.response_header - # p http.response - yield http - }.errback {|http| - flunk(http.error) if http.error - EM.stop - } - end - - # - # stop nickserver + # this works because http requests to 127.0.0.1 are not stubbed, but requests to other domains are. # - def stop - EM.stop + def request(method, options = {}) + response = HTTP. + headers(options.delete(:head)). + request method, "http://127.0.0.1:#{config.port}/", options + yield response end end diff --git a/test/test_helper.rb b/test/test_helper.rb index afdd3f9..1ed2a98 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -7,6 +7,7 @@ require 'minitest/autorun' silence_warnings do require 'webmock/minitest' end +require 'celluloid/test' require 'nickserver' require 'minitest/pride' require 'minitest/hell' @@ -48,26 +49,22 @@ class Minitest::Test def stub_sks_get_reponse(key_id, opts = {}) options = {status: 200, body: ""}.merge(opts) - stub_http_request(:get, Nickserver::Config.hkp_url).with( + stub_http_request(:get, config.hkp_url).with( query: {op: 'get', search: "0x"+key_id, exact: 'on', options: 'mr'} ).to_return(options) end def stub_couch_response(uid, opts = {}) # can't stub localhost, so set couch_host to anything else - Nickserver::Config.stub :couch_host, 'notlocalhost' do + config.stub :couch_host, 'notlocalhost' do options = {status: 200, body: ""}.merge(opts) query = "\?key=#{"%22#{uid}%22"}&reduce=false" - stub_http_request(:get, /#{Regexp.escape(Nickserver::Config.couch_url)}.*#{query}/).to_return(options) + stub_http_request(:get, /#{Regexp.escape(config.couch_url)}.*#{query}/).to_return(options) yield end end - # - # temporarily stubs the config property for the duration of the given block - # - def stub_config(property, value, &block) - Nickserver::Config.stub(property, value, &block) + def config + Nickserver::Config end - end diff --git a/test/unit/adapters/celluloid_http_test.rb b/test/unit/adapters/celluloid_http_test.rb new file mode 100644 index 0000000..4381b8f --- /dev/null +++ b/test/unit/adapters/celluloid_http_test.rb @@ -0,0 +1,39 @@ +require 'test_helper' +require 'nickserver/adapters/celluloid_http' + +class Nickserver::Adapters::CelluloidHttpTest < Minitest::Test + + def setup + super + Celluloid.boot + end + + def teardown + Celluloid.shutdown + super + end + + def test_successful_request + url = 'http://url.to' + stub_http_request(:get, url) + .with(query: {key: :value}) + .to_return status: 200, body: 'body' + status, body = adapter.get(url, query: {key: :value}) + assert_equal 200, status + assert_equal 'body', body + end + + def test_https_for_hkp + url = Nickserver::Config.hkp_url + real_network do + status, _body = adapter.get url + assert_equal 404, status + end + end + + protected + + def adapter + @adapter ||= Nickserver::Adapters::CelluloidHttp.new + end +end diff --git a/test/unit/adapters/em_http_test.rb b/test/unit/adapters/em_http_test.rb deleted file mode 100644 index 659ff1b..0000000 --- a/test/unit/adapters/em_http_test.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'test_helper' -require 'nickserver/adapters/em_http' - -class Nickserver::Adapters::EmHttpTest < Minitest::Test - - def test_successful_request - url = 'http://url.to' - stub_http_request(:get, url) - .with(query: {key: :value}) - .to_return status: 200, body: 'body' - EM.run do - adapter.get(url, query: {key: :value}) do |status, body| - assert_equal 200, status - assert_equal 'body', body - EM.stop - end - end - end - - protected - - def adapter - Nickserver::Adapters::EmHttp.new - end -end diff --git a/test/unit/couch_db/source_unit_test.rb b/test/unit/couch_db/source_unit_test.rb index 19ea9bc..fd07808 100644 --- a/test/unit/couch_db/source_unit_test.rb +++ b/test/unit/couch_db/source_unit_test.rb @@ -7,10 +7,9 @@ module Nickserver::CouchDB def test_query address = "nick@domain.tl" adapter = Minitest::Mock.new - adapter.expect :get, nil, + adapter.expect :get, [200, nil], [String, {query: { reduce: "false", key: "\"#{address}\"" }}] - query = Source.new(adapter) - query.query address + Source.new(adapter).query address adapter.verify end end diff --git a/test/unit/email_address_test.rb b/test/unit/email_address_test.rb new file mode 100644 index 0000000..6d57a8c --- /dev/null +++ b/test/unit/email_address_test.rb @@ -0,0 +1,22 @@ +require 'test_helper' +require 'nickserver/email_address' + +class EmailAddressTest < Minitest::Test + + def test_domain + nick = Nickserver::EmailAddress.new 'nick@test.me' + assert nick.domain?('test.me') + assert !nick.domain?('est.me') + end + + def test_valid + nick = Nickserver::EmailAddress.new 'nick@remote.domain' + assert nick.valid? + end + + def test_invalid + nick = Nickserver::EmailAddress.new 'asdf' + assert nick.invalid? + end + +end diff --git a/test/unit/hkp/client_test.rb b/test/unit/hkp/client_test.rb new file mode 100644 index 0000000..9784d0a --- /dev/null +++ b/test/unit/hkp/client_test.rb @@ -0,0 +1,53 @@ +require 'test_helper' +require 'nickserver/hkp/client' + +module Nickserver::Hkp + class ClientTest < Minitest::Test + + def test_get_key_infos_by_email + adapter_expects_query op: "vindex", + search: email, + options: "mr", + fingerprint: "on", + exact: "on" + client.get_key_infos_by_email(email) + @adapter.verify + end + + def test_key_by_fingerprint + adapter_expects_query op: "get", + search: "0x#{fingerprint}", + options: "mr", + exact: "on" + client.get_key_by_fingerprint(fingerprint) + @adapter.verify + end + + def client + @client ||= Client.new @adapter + end + + def adapter_expects_query(query = {}) + adapter_expects Nickserver::Config.hkp_url, query: query + end + + def adapter_expects(*args) + @adapter = Minitest::Mock.new + @adapter.expect :get, dummy_response, + args + end + + def email + 'dummy_email' + end + + def fingerprint + 'dummy_fingerprint' + end + + def dummy_response + [200, 'dummy_response'] + end + + end +end diff --git a/test/unit/nickname_test.rb b/test/unit/nickname_test.rb deleted file mode 100644 index 8681545..0000000 --- a/test/unit/nickname_test.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'test_helper' -require 'nickserver/nickname' - -class NicknameTest < Minitest::Test - - def test_local - nick = Nickserver::Nickname.new 'nick@test.me' - assert nick.local? - assert !nick.remote? - end - - def test_remote - nick = Nickserver::Nickname.new 'nick@remote.domain' - assert !nick.local? - assert nick.remote? - end - - def test_valid - nick = Nickserver::Nickname.new 'nick@remote.domain' - assert nick.valid? - end - - def test_invalid - nick = Nickserver::Nickname.new 'asdf' - assert nick.invalid? - end - -end diff --git a/test/unit/request_handler_test.rb b/test/unit/request_handler_test.rb index c9d316f..ef656f9 100644 --- a/test/unit/request_handler_test.rb +++ b/test/unit/request_handler_test.rb @@ -13,6 +13,11 @@ class Nickserver::RequestHandlerTest < Minitest::Test assert_response status: 500, content: "500 Not a valid address\n" end + def test_missing_domain + handle address: ['valid@email.tld'] + assert_response status: 500, content: "500 HTTP request must include a Host header.\n" + end + protected def handle(params = {}, headers = {}) @@ -21,8 +26,7 @@ class Nickserver::RequestHandlerTest < Minitest::Test end def assert_response(args) - args[:content_type] ||= 'text/plain' - responder.expect :send_response, nil, [args] + responder.expect :respond, nil, [args[:status], args[:content]] handler.respond_to @params, @headers responder.verify end |