github.com/trevoraustin/hub@v2.2.0-preview1.0.20141105230840-96d8bfc654cc+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(
    89                :message => "expected %p; got %p" % [
    90                  expected, auth.credentials
    91                ]
    92              )
    93            end
    94          end
    95  
    96          def generate_patch(subject)
    97          <<PATCH
    98  From 7eb75a26ee8e402aad79fcf36a4c1461e3ec2592 Mon Sep 17 00:00:00 2001
    99  From: Mislav <mislav.marohnic@gmail.com>
   100  Date: Tue, 24 Jun 2014 11:07:05 -0700
   101  Subject: [PATCH] #{subject}
   102  ---
   103  diff --git a/README.md b/README.md
   104  new file mode 100644
   105  index 0000000..ce01362
   106  --- /dev/null
   107  +++ b/README.md
   108  +hello
   109  -- 
   110  1.9.3
   111  PATCH
   112          end
   113        end
   114  
   115        new(klass.new).start
   116      end
   117  
   118      attr_reader :app, :host, :port
   119      attr_accessor :server
   120  
   121      def initialize(app, host = '127.0.0.1')
   122        @app = app
   123        @host = host
   124        @server = nil
   125        @server_thread = nil
   126      end
   127  
   128      def responsive?
   129        return false if @server_thread && @server_thread.join(0)
   130  
   131        res = Net::HTTP.start(host, port) { |http| http.get('/__identify__') }
   132  
   133        res.is_a?(Net::HTTPSuccess) and res.body == app.object_id.to_s
   134      rescue Errno::ECONNREFUSED, Errno::EBADF
   135        return false
   136      end
   137  
   138      def start
   139        @port = self.class.ports[app.object_id]
   140  
   141        if not @port or not responsive?
   142          @server_thread = start_handler(Identify.new(app)) do |server, host, port|
   143            self.server = server
   144            @port = self.class.ports[app.object_id] = port
   145          end
   146  
   147          Timeout.timeout(60) { @server_thread.join(0.01) until responsive? }
   148        end
   149      rescue TimeoutError
   150        raise "Rack application timed out during boot"
   151      else
   152        self
   153      end
   154  
   155      def start_handler(app)
   156        server = nil
   157        thread = Rack::Handler::WEBrick.run(app, server_options) { |s| server = s }
   158        addr = server.listeners[0].addr
   159        yield server, addr[3], addr[1]
   160        return thread
   161      end
   162  
   163      def server_options
   164        { :Port => 0,
   165          :BindAddress => '127.0.0.1',
   166          :ShutdownSocketWithoutClose => true,
   167          :ServerType => Thread,
   168          :AccessLog => [],
   169          :Logger => WEBrick::Log::new(nil, 0)
   170        }
   171      end
   172  
   173      def stop
   174        server.shutdown
   175        @server_thread.join
   176      end
   177    end
   178  end
   179  
   180  WEBrick::HTTPStatus::StatusMessage[422] = "Unprocessable Entity"