Class: Purr::Server
- Inherits:
-
Object
- Object
- Purr::Server
- Defined in:
- lib/purr/server.rb
Overview
This class implements a Rack-based server with socket hijacking and proxying to a remote TCP endpoint.
The remote TCP endpoint selection is implemented by passing a block to the class instantiation. If any kind of error happens during the hijacking process a 404 error is returned to the requester.
Instance Method Summary collapse
-
#call(env) ⇒ Object
Method required by the Rack API.
-
#initialize {|env| ... } ⇒ Server
constructor
A new instance of Server.
Constructor Details
#initialize {|env| ... } ⇒ Server
Returns a new instance of Server.
13 14 15 16 17 18 |
# File 'lib/purr/server.rb', line 13 def initialize(&block) raise ArgumentError, 'The method requires a block with a single argument' unless block && block.arity == 1 @remote = block @proxy = SurroGate.new end |
Instance Method Details
#call(env) ⇒ Object
Method required by the Rack API
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/purr/server.rb', line 23 def call(env) upgrade = parse_headers(env) # Return with a 404 error if the upgrade header is not present return not_found unless i(websocket purr).include?(upgrade) host, port = @remote.call(env) # Return with a 404 error if no host:port pair was determined if host.nil? || port.nil? logger(env, :error, "No matching endpoint found for request incoming from #{env['REMOTE_ADDR']}") return not_found end # Hijack the HTTP socket from the Rack middleware http = env['rack.hijack'].call # Write a proper HTTP response http.write(http_response(upgrade)) # Open the remote TCP socket sock = TCPSocket.new(host, port) # Start proxying @proxy.push(http, sock) logger(env, :info, "Redirecting incoming request from #{env['REMOTE_ADDR']} to [#{host}]:#{port}") # Rack requires this line below return [200, {}, []] rescue => ex logger(env, :error, "#{ex.class} happened for #{env['REMOTE_ADDR']} trying to access #{host}:#{port}") # Clean up the opened sockets if available http.close unless http.nil? || http.closed? sock.close unless sock.nil? || sock.closed? # Return with a 404 error return not_found end |