Class: RightDevelop::Testing::Server::MightApi::App::Base
- Inherits:
-
Object
- Object
- RightDevelop::Testing::Server::MightApi::App::Base
- Defined in:
- lib/right_develop/testing/servers/might_api/app/base.rb
Defined Under Namespace
Classes: MightError, MissingRoute
Constant Summary collapse
- MUTEX =
semaphore for critical sections
::Mutex.new
- MAX_REDIRECTS =
500 after so many redirects
10
- DEFAULT_PROXY_SETTINGS =
Rack (and Skeletor) apps and some known AWS apps only accept dash and not underscore so ensure the default settings reflect the 80-20 rule.
RightSupport::Data::Mash.new( header: RightSupport::Data::Mash.new( case: :capitalize, separator: :dash ).freeze ).freeze
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
-
#logger ⇒ Object
readonly
Returns the value of attribute logger.
-
#state_file_path ⇒ Object
readonly
Returns the value of attribute state_file_path.
Class Method Summary collapse
Instance Method Summary collapse
- #call(env) ⇒ Object
-
#cleanup ⇒ Object
Removes state and/or fixtures for current mode (overridable).
-
#handle_request(env, verb, uri, headers, body) ⇒ TrueClass
Handler.
-
#initialize(options = {}) ⇒ Base
constructor
A new instance of Base.
Constructor Details
#initialize(options = {}) ⇒ Base
Returns a new instance of Base.
60 61 62 63 64 65 66 67 |
# File 'lib/right_develop/testing/servers/might_api/app/base.rb', line 60 def initialize( = {}) @config = [:config] || ::RightDevelop::Testing::Server::MightApi::Config @logger = [:logger] || ::RightDevelop::Testing::Server::MightApi.logger @state_file_path = [:state_file_name] ? ::File.join(@config.fixtures_dir, [:state_file_name]) : nil end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
51 52 53 |
# File 'lib/right_develop/testing/servers/might_api/app/base.rb', line 51 def config @config end |
#logger ⇒ Object (readonly)
Returns the value of attribute logger.
51 52 53 |
# File 'lib/right_develop/testing/servers/might_api/app/base.rb', line 51 def logger @logger end |
#state_file_path ⇒ Object (readonly)
Returns the value of attribute state_file_path.
51 52 53 |
# File 'lib/right_develop/testing/servers/might_api/app/base.rb', line 51 def state_file_path @state_file_path end |
Class Method Details
.app_threads ⇒ Object
77 78 79 |
# File 'lib/right_develop/testing/servers/might_api/app/base.rb', line 77 def self.app_threads @app_threads ||= ::Set.new end |
.interrupted=(value) ⇒ Object
73 74 75 |
# File 'lib/right_develop/testing/servers/might_api/app/base.rb', line 73 def self.interrupted=(value) @interrupted = value end |
.interrupted? ⇒ Boolean
69 70 71 |
# File 'lib/right_develop/testing/servers/might_api/app/base.rb', line 69 def self.interrupted? !!@interrupted end |
Instance Method Details
#call(env) ⇒ Object
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/right_develop/testing/servers/might_api/app/base.rb', line 81 def call(env) # HACK: chain trap interrupt on first call to app because the trap chain # does not exist, in rack terms, until just before app is run. # unforunately due to poor design of rack, this object gets no # calls from rack other than calls to handle requests (i.e. a call to # do run/shutdown would be nice). # # the downside here is that if the server never receives any request # then the trap chain is never setup so temporary files cannot be # cleaned-up on shutdown, etc. admin mode has a workaround whereby it is # able to clean-up any temporary files immediately after reading its # config and whenever the administered configuration changes. raise ::Interrupt if self.class.interrupted? MUTEX.synchronize do entrapment unless Base.trapped? self.class.app_threads << ::Thread.current end env['rack.logger'] ||= logger # read body from stream. request = ::Rack::Request.new(env) body = request.body.read # proxy any headers from env starting with HTTP_ headers = env.inject({}) do |r, (k,v)| # note that HTTP_HOST refers to this proxy server instead of the # proxied target server. in the case of AWS authentication, it is # necessary to pass the value through unmodified or else AWS auth # fails. if k.start_with?('HTTP_') r[k[5..-1]] = v end r end # special cases. ['ACCEPT', 'CONTENT_TYPE', 'CONTENT_LENGTH', 'USER_AGENT'].each do |key| headers[key] = env[key] unless env[key].to_s.empty? end # prepare and call handler # # note that verb supposed to already be .to_s.upcase but we want to # ensure that we agree on that. verb = request.request_method.to_s.upcase uri = ::URI.parse(request.url) logger.info("#{verb} #{uri}") result = handle_request(env, verb, uri, headers, body) logger.info(result.first.to_s) result rescue MissingRoute => e = "#{e.class} #{e.}" logger.error() if config.routes.empty? logger.error("No routes configured.") else logger.error("The following routes are configured:") config.routes.keys.each do |prefix| logger.error(" #{prefix}...") end end # not a 404 because this is a proxy/stub service and 40x might appear to # have come from a proxied request/response whereas 500 is never an # expected response. internal_server_error() rescue ::RightDevelop::Testing::Client::Rest::Request::Playback::PeerResetConnectionError => e # FIX: have only implemented socket close for webrick; not sure if this # is needed for other rack implementations. if socket = ::Thread.current[:WEBrickSocket] # closing the socket causes 'peer reset connection' on client side and # also prevents any response coming back on connection. socket.close end = e. trace = [e.class.name] + (e.backtrace || []) logger.info() internal_server_error() rescue ::RightDevelop::Testing::Recording::Metadata::PlaybackError => e # response has not been recorded, etc. = e. trace = [e.class.name] + (e.backtrace || []) logger.error() logger.debug(trace.join("\n")) internal_server_error() rescue ::Interrupt # setting interrupted=true may or may not be redundant, depending on # visibility of interrupted flag to all outstanding app threads. # the problem is that we are not allowed to synchronize a mutex inside # of a trap context. self.class.interrupted = true internal_server_error('interrupt') rescue ::Exception => e = "Unhandled exception: #{e.class} #{e.}" trace = e.backtrace || [] if logger logger.error() logger.debug(trace.join("\n")) else env['rack.errors'].puts() env['rack.errors'].puts(trace.join("\n")) end internal_server_error() ensure unless self.class.interrupted? MUTEX.synchronize do self.class.app_threads.delete(::Thread.current) end end end |
#cleanup ⇒ Object
Removes state and/or fixtures for current mode (overridable).
207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/right_develop/testing/servers/might_api/app/base.rb', line 207 def cleanup # the state file, if any, is always temporary. if @state_file_path && ::File.file?(@state_file_path) ::File.unlink(@state_file_path) end # remove any directories listed as temporary by config. (config.cleanup_dirs || []).each do |dir| ::FileUtils.rm_rf(dir) if ::File.directory?(dir) end true end |
#handle_request(env, verb, uri, headers, body) ⇒ TrueClass
Handler.
202 203 204 |
# File 'lib/right_develop/testing/servers/might_api/app/base.rb', line 202 def handle_request(env, verb, uri, headers, body) raise ::NotImplementedError, 'Must be overridden' end |