Class: Above::Server
- Inherits:
-
Object
- Object
- Above::Server
- Defined in:
- lib/above/server.rb
Overview
Answer TCP requests
Instance Method Summary collapse
-
#initialize(config:, middleware: [MiddleWare::Log, MiddleWare::Block, MiddleWare::Cache, MiddleWare::Redirect, MiddleWare::Static]) ⇒ Server
constructor
A new instance of Server.
- #listener(tls:, limit:) ⇒ Object
- #middleware(env:) ⇒ Object
-
#middleware_init(middleware:) ⇒ Object
Instantiate each middleware class with an app vairable pointing to the next piece of middleware to call.
- #on_quit(barrier:) ⇒ Object
- #reply(env:, response:) ⇒ Object
-
#request(socket:) ⇒ Object
creates env hash, request:, config:, server:, valid:.
- #serve(tls:) ⇒ Object
- #start ⇒ Object
- #tls_server ⇒ Object
Constructor Details
#initialize(config:, middleware: [MiddleWare::Log, MiddleWare::Block, MiddleWare::Cache, MiddleWare::Redirect, MiddleWare::Static]) ⇒ Server
Returns a new instance of Server.
22 23 24 25 26 27 28 29 30 |
# File 'lib/above/server.rb', line 22 def initialize(config:, middleware: [MiddleWare::Log, MiddleWare::Block, MiddleWare::Cache, MiddleWare::Redirect, MiddleWare::Static]) @config = config @server_config = config["server"] middleware_init(middleware:) end |
Instance Method Details
#listener(tls:, limit:) ⇒ Object
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/above/server.rb', line 32 def listener(tls:, limit:) limit.async do socket = tls.accept # env is {socket:, request:, config:, valid:} env = request(socket:) response = middleware(env:) reply(env:, response:) rescue => error puts error. # may be unable to reply if error is with socket # TODO: logging at a level above the logging middleware ensure socket&.close end end |
#middleware(env:) ⇒ Object
61 62 63 64 65 66 67 68 |
# File 'lib/above/server.rb', line 61 def middleware(env:) # Each piece of middleware will call the next, the result here is # [status, header, [body]], just like in rack @middleware.first.call(env:) rescue # TODO - log this [42, Status::CODE[42], []] end |
#middleware_init(middleware:) ⇒ Object
Instantiate each middleware class with an app vairable pointing to the next piece of middleware to call
50 51 52 53 54 55 56 57 58 59 |
# File 'lib/above/server.rb', line 50 def middleware_init(middleware:) @middleware = [] previous = nil middleware.reverse_each do |mid| instance = mid.new(app: previous) previous = instance @middleware.push(instance) end @middleware.reverse! end |
#on_quit(barrier:) ⇒ Object
70 71 72 73 74 75 76 77 78 |
# File 'lib/above/server.rb', line 70 def on_quit(barrier:) %w[INT TERM].each do |signal| Signal.trap(signal) do .stop puts "\nShutting down" exit end end end |
#reply(env:, response:) ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/above/server.rb', line 80 def reply(env:, response:) # TODO: uncertain if there's meant to be a space before the \r\n # mentioned in https://geminiprotocol.net/news/2024_07_30.gmi # Maybe TODO: streaming, multipart replies etc status, header, body_arr = response if status === 0 # blocked env[:socket]&.close return elsif status.nil? status = 40 header = Status::CODE[40] end body = if body_arr.nil? || !body_arr.is_a?(Array) || body_arr.first.nil? "" else body_arr.first end env[:socket].puts "#{status} #{header}\r\n#{body}" rescue env[:socket].puts "40 #{Status::CODE[40]}\r\n" end |
#request(socket:) ⇒ Object
creates env hash, request:, config:, server:, valid:
104 105 106 107 108 109 110 111 |
# File 'lib/above/server.rb', line 104 def request(socket:) config = @config["middleware"] str = socket.gets.chomp request = URI(str) {socket:, request:, config:, server: @server_config, valid: true} rescue URI::InvalidURIError {socket:, request: str[0...1023], config:, server: @server_config, valid: false} end |
#serve(tls:) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/above/server.rb', line 113 def serve(tls:) = Async::Barrier.new Sync do on_quit(barrier:) limit = Async::Semaphore.new(@server_config["max_fibers"], parent: ) loop do listener(tls:, limit:) end ensure .stop end end |
#start ⇒ Object
126 127 128 |
# File 'lib/above/server.rb', line 126 def start serve(tls: tls_server) end |
#tls_server ⇒ Object
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/above/server.rb', line 130 def tls_server tcp_server = TCPServer.new(@server_config["ip"], @server_config["port"]) ssl_ctx = OpenSSL::SSL::SSLContext.new.tap do |ctx| ctx.key = OpenSSL::PKey::EC.new( File.read( File.( @server_config["tls_key"] ) ) ) ctx.cert = OpenSSL::X509::Certificate.new( File.read( File.( @server_config["tls_cert"] ) ) ) end OpenSSL::SSL::SSLServer.new(tcp_server, ssl_ctx) rescue => error puts "Certificate/Key loading errors - #{error.}" exit end |