Created
December 15, 2018 19:28
-
-
Save skateman/a4b8fa9192e96b432f7a4c476f617ffe to your computer and use it in GitHub Desktop.
Cockpit reverse-proxy written in Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'surro-gate' | |
require 'websocket/driver' | |
require 'rack/reverse_proxy' | |
require 'permessage_deflate' | |
class WSRack | |
attr_reader :env, :url | |
def initialize(env, sock) | |
@env = env | |
@sock = sock | |
@url = "ws://#{env['HTTP_HOST']}#{env['REQUEST_URI']}" | |
@driver = WebSocket::Driver.rack(self, :protocols => ['cockpit1']) | |
@driver.add_extension(PermessageDeflate) | |
@driver.start | |
end | |
def fetch(length) | |
@driver.on(:message) { |msg| yield(msg.data) } if @driver.listeners(:message).length.zero? | |
data = @sock.read_nonblock(length) | |
@driver.parse(data) | |
end | |
def issue(data) | |
@driver.text(data) | |
end | |
def write(data) | |
@sock.write_nonblock(data) | |
end | |
end | |
class WSClient | |
attr_reader :url | |
def initialize(env, sock) | |
@sock = sock | |
@url = "ws://localhost:9090#{env['REQUEST_URI']}" | |
@driver = WebSocket::Driver.client(self, :protocols => ['cockpit1']) | |
@driver.add_extension(PermessageDeflate) | |
@driver.set_header('Origin', 'http://localhost:9090') | |
@driver.set_header('Cookie', env['HTTP_COOKIE']) | |
@driver.start | |
end | |
def fetch(length) | |
@driver.on(:message) { |msg| yield(msg.data) } if @driver.listeners(:message).length.zero? | |
data = @sock.read_nonblock(length) | |
@driver.parse(data) | |
end | |
def issue(data) | |
@driver.text(data) | |
end | |
def write(data) | |
@sock.write_nonblock(data) | |
end | |
end | |
class Server | |
def initialize | |
@rp = Rack::ReverseProxy.new do | |
reverse_proxy '/', 'http://localhost:9090' | |
end | |
@sg = SurroGate.new | |
@adapters = {} | |
Thread.new do | |
loop do | |
@sg.select(0.1) | |
@sg.each_ready do |left, right| | |
@adapters[left].fetch(4096) { |data| @adapters[right].issue(data) } | |
end | |
end | |
end | |
end | |
def call(env) | |
if WebSocket::Driver.websocket?(env) | |
rsock = env['rack.hijack'].call | |
csock = TCPSocket.new('localhost', 9090) | |
@adapters[rsock] = WSRack.new(env, rsock) | |
@adapters[csock] = WSClient.new(env, csock) | |
@sg.push(rsock, csock) | |
[-1, {}, []] | |
else | |
@rp.call(env) | |
end | |
end | |
end | |
run Server.new |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment