Class: Diode::Server
- Inherits:
-
Object
- Object
- Diode::Server
- Defined in:
- lib/diode/server.rb
Instance Attribute Summary collapse
-
#env ⇒ Object
Returns the value of attribute env.
-
#filters ⇒ Object
Returns the value of attribute filters.
Instance Method Summary collapse
-
#complete(conn, response) ⇒ Object
send the response, make sure its all sent.
-
#initialize(port, routing = [], env = {}) ⇒ Server
constructor
A new instance of Server.
- #read_request(client) ⇒ Object
-
#serve(request) ⇒ Object
handle a new request connection.
- #start ⇒ Object
- #url_encode(s) ⇒ Object
-
#validate_routing ⇒ Object
check routing table is sane.
Constructor Details
#initialize(port, routing = [], env = {}) ⇒ Server
Returns a new instance of Server.
16 17 18 19 20 21 |
# File 'lib/diode/server.rb', line 16 def initialize(port, routing=[], env={}) @port = port.to_i() @routing = routing @env = env @filters = [self] # list of filters that requests pass through before dispatch to a servlet end |
Instance Attribute Details
#env ⇒ Object
Returns the value of attribute env.
14 15 16 |
# File 'lib/diode/server.rb', line 14 def env @env end |
#filters ⇒ Object
Returns the value of attribute filters.
14 15 16 |
# File 'lib/diode/server.rb', line 14 def filters @filters end |
Instance Method Details
#complete(conn, response) ⇒ Object
send the response, make sure its all sent
95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/diode/server.rb', line 95 def complete(conn, response) # send the response, make sure its all sent begin http = response.to_s total = http.bytes.size() sent = 0 while sent < total msgsize = conn.send(http.byteslice(sent..-1), 0) sent = sent + msgsize end rescue Errno::EPIPE # ignore, we're finished anyway end end |
#read_request(client) ⇒ Object
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/diode/server.rb', line 64 def read_request(client) = client.recv(2048000) unless .empty? unless .index("Content-Length: ").nil? # handle large messages such as file uploads start = .index("Content-Length: ") stop = .index("\r\n", start) len = [start..stop].chomp.sub("Content-Length: ","").to_i bodyStart = .index("\r\n\r\n") + 3 # up to end of 4 bytes byteStart = [0..bodyStart].bytes.size remaining = len - (.bytes.size - byteStart) # we have already read some of the content while remaining > 0 chunk = client.recv(2048000) = + chunk remaining = remaining - chunk.bytes.size end end end end |
#serve(request) ⇒ Object
handle a new request connection
85 86 87 88 89 90 91 92 93 |
# File 'lib/diode/server.rb', line 85 def serve(request) # keep signature consistent with filters and servlets pattern, klass, args = @routing.find{ |pattern, klass, args| not pattern.match(request.path).nil? } raise(Diode::RequestError.new(404)) if klass.nil? servlet = klass.new(*args) request.pattern = pattern # provide the mount pattern to the request, useful for Diode::Static response = servlet.serve(request) end |
#start ⇒ Object
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/diode/server.rb', line 23 def start() validate_routing() Fiber.schedule { server = TCPServer.new("127.0.0.1", @port) Signal.trap("INT") { exit(0) } loop do client = server.accept() Fiber.schedule(client) { |task, client| rawRequest = read_request(client) begin request = Diode::Request.new(rawRequest) request.remote = client.remote_address.ip_address # decorate with request source address request.env = @env.dup() # copy environment into request request.filters = @filters.dup response = (request.filters.shift).serve(request) rescue Diode::SecurityError, Diode::RequestError => e response = Diode::Response.standard(e.code) end complete(client, response) client.close_write() } end } end |
#url_encode(s) ⇒ Object
108 109 110 111 112 |
# File 'lib/diode/server.rb', line 108 def url_encode(s) s.b.gsub(/([^ a-zA-Z0-9_.-]+)/) { |m| '%' + m.unpack('H2' * m.bytesize).join('%').upcase }.tr(' ', '+').force_encoding(Encoding::UTF_8) # url-encoded message end |
#validate_routing ⇒ Object
check routing table is sane
49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/diode/server.rb', line 49 def validate_routing() newRouting = [] raise("routing must be an Array") unless @routing.is_a?(Array) @routing.each { |pattern, klass, *args| raise("invalid pattern='#{pattern}' in routing table") unless pattern.is_a?(Regexp) begin servletKlass = klass.split("::").inject(Object) { |o,c| o.const_get(c) } newRouting << [pattern, servletKlass, args] rescue NameError raise("unrecognised class #{klass} found in routing table") end } @routing = newRouting # optimised so we can instantiate the servlet quickly end |