github.com/echohead/hub@v2.2.1+incompatible/features/support/local_server.rb (about)

     1  # based on <github.com/jnicklas/capybara/blob/ab62b27/lib/capybara/server.rb>
     2  require 'net/http'
     3  require 'rack/handler/webrick'
     4  require 'json'
     5  require 'sinatra/base'
     6  
     7  module Hub
     8    class LocalServer
     9      class Identify < Struct.new(:app)
    10        def call(env)
    11          if env["PATH_INFO"] == "/__identify__"
    12            [200, {}, [app.object_id.to_s]]
    13          else
    14            app.call(env)
    15          end
    16        end
    17      end
    18  
    19      def self.ports
    20        @ports ||= {}
    21      end
    22  
    23      class JsonParamsParser < Struct.new(:app)
    24        def call(env)
    25          if env['rack.input'] and not input_parsed?(env) and type_match?(env)
    26            env['rack.request.form_input'] = env['rack.input']
    27            data = env['rack.input'].read
    28            env['rack.request.form_hash'] = data.empty?? {} : JSON.parse(data)
    29          end
    30          app.call(env)
    31        end
    32  
    33        def input_parsed? env
    34          env['rack.request.form_input'].eql? env['rack.input']
    35        end
    36  
    37        def type_match? env
    38          type = env['CONTENT_TYPE'] and
    39            type.split(/\s*[;,]\s*/, 2).first.downcase =~ /[\/+]json$/
    40        end
    41      end
    42  
    43      class App < Sinatra::Base
    44        def invoke
    45          res = super
    46          content_type :json unless response.content_type
    47          response.body = '{}' if blank_response?(response.body) ||
    48            (response.body.respond_to?(:[]) && blank_response?(response.body[0]))
    49          res
    50        end
    51  
    52        def blank_response?(obj)
    53          obj.nil? || (obj.respond_to?(:empty?) && obj.empty?)
    54        end
    55      end
    56  
    57      def self.start_sinatra(&block)
    58        klass = Class.new(App)
    59        klass.use JsonParamsParser
    60        klass.set :environment, :test
    61        klass.disable :protection
    62        klass.error(404, 401) { content_type :json; nil }
    63        klass.class_eval(&block)
    64        klass.helpers do
    65          def json(value)
    66            content_type :json
    67            JSON.generate value
    68          end
    69  
    70          def assert(expected)
    71            expected.each do |key, value|
    72              if params[key] != value
    73                halt 422, json(
    74                  :message => "expected %s to be %s; got %s" % [
    75                    key.inspect,
    76                    value.inspect,
    77                    params[key].inspect
    78                  ]
    79                )
    80              end
    81            end
    82          end
    83  
    84          def assert_basic_auth(*expected)
    85            require 'rack/auth/basic'
    86            auth = Rack::Auth::Basic::Request.new(env)
    87            if auth.credentials != expected
    88              halt 401, json(:message => "Bad credentials")
    89            end
    90          end
    91  
    92          def generate_patch(subject)
    93          <<PATCH
    94  From 7eb75a26ee8e402aad79fcf36a4c1461e3ec2592 Mon Sep 17 00:00:00 2001
    95  From: Mislav <mislav.marohnic@gmail.com>
    96  Date: Tue, 24 Jun 2014 11:07:05 -0700
    97  Subject: [PATCH] #{subject}
    98  ---
    99  diff --git a/README.md b/README.md
   100  new file mode 100644
   101  index 0000000..ce01362
   102  --- /dev/null
   103  +++ b/README.md
   104  +hello
   105  -- 
   106  1.9.3
   107  PATCH
   108          end
   109        end
   110  
   111        new(klass.new).start
   112      end
   113  
   114      attr_reader :app, :host, :port
   115      attr_accessor :server
   116  
   117      def initialize(app, host = '127.0.0.1')
   118        @app = app
   119        @host = host
   120        @server = nil
   121        @server_thread = nil
   122      end
   123  
   124      def responsive?
   125        return false if @server_thread && @server_thread.join(0)
   126  
   127        res = Net::HTTP.start(host, port) { |http| http.get('/__identify__') }
   128  
   129        res.is_a?(Net::HTTPSuccess) and res.body == app.object_id.to_s
   130      rescue Errno::ECONNREFUSED, Errno::EBADF
   131        return false
   132      end
   133  
   134      def start
   135        @port = self.class.ports[app.object_id]
   136  
   137        if not @port or not responsive?
   138          @server_thread = start_handler(Identify.new(app)) do |server, host, port|
   139            self.server = server
   140            @port = self.class.ports[app.object_id] = port
   141          end
   142  
   143          Timeout.timeout(60) { @server_thread.join(0.01) until responsive? }
   144        end
   145      rescue TimeoutError
   146        raise "Rack application timed out during boot"
   147      else
   148        self
   149      end
   150  
   151      def start_handler(app)
   152        server = nil
   153        thread = Rack::Handler::WEBrick.run(app, server_options) { |s| server = s }
   154        addr = server.listeners[0].addr
   155        yield server, addr[3], addr[1]
   156        return thread
   157      end
   158  
   159      def server_options
   160        { :Port => 0,
   161          :BindAddress => '127.0.0.1',
   162          :ShutdownSocketWithoutClose => true,
   163          :ServerType => Thread,
   164          :AccessLog => [],
   165          :Logger => WEBrick::Log::new(nil, 0)
   166        }
   167      end
   168  
   169      def stop
   170        server.shutdown
   171        @server_thread.join
   172      end
   173    end
   174  end
   175  
   176  WEBrick::HTTPStatus::StatusMessage[422] = "Unprocessable Entity"