mlserver 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6b326cb18fd091f0272824b58d166980a1e3add7e87d508dd4895388eae223c8
4
+ data.tar.gz: 26e225d538c7791fe256aa31f1cd14e40b505ffcdb3217296ec602ad1318fe8d
5
+ SHA512:
6
+ metadata.gz: 2fc0d8d718f919271737cddc7fc361e47b5cdba17091c8b53684612dabbca8b5ed42aaf80c0f6f06fa5e4163ee41f8957a4f4ef3a27dbc3905a8f2c34512c286
7
+ data.tar.gz: 58c17b39a1fc34cc1aaf72242683d3fb22d3a177a362b235964d94ca961da435400c2f684239dfe7af67db6f410a245a882d8ef305675a982a7e2c81c6b5b97f
data/bin/mlserver ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "MLserver"
@@ -0,0 +1,11 @@
1
+ module MLserver
2
+ class ClientHandler
3
+ def initialize(&block)
4
+ @block = block
5
+ end
6
+
7
+ def run(request, client)
8
+ @block.call(request, client)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,60 @@
1
+ module MLserver
2
+ class ErrorResponse
3
+ @@error_messages_by_code = {
4
+ 400 => "bad request",
5
+ 401 => "unauthorized",
6
+ 403 => "forbidden",
7
+ 404 => "page not found",
8
+ 405 => "method not allowed",
9
+ 406 => "not acceptable",
10
+ 407 => "proxy authentication required",
11
+ 408 => "request timeout",
12
+ 409 => "conflict",
13
+ 410 => "resource gone",
14
+ 411 => "length required",
15
+ 412 => "precondition failed",
16
+ 413 => "payload too large",
17
+ 414 => "URI too long",
18
+ 415 => "unsupported media type",
19
+ 416 => "range not satisfiable",
20
+ 417 => "expectation failed",
21
+ 418 => "I'm a teapot",
22
+ 421 => "misdirected request",
23
+ 422 => "unprocessable entity",
24
+ 423 => "resource locked",
25
+ 424 => "failed dependency",
26
+ 425 => "too early",
27
+ 426 => "upgrade required",
28
+ 428 => "precondition required",
29
+ 429 => "too many requests",
30
+ 431 => "request header fields too large",
31
+ 451 => "unavailable for legal reasons",
32
+ 500 => "internal server error",
33
+ 501 => "not implemented",
34
+ 502 => "bad gateway",
35
+ 503 => "service unavailable",
36
+ 504 => "gateway timeout",
37
+ 505 => "HTTP version not supported",
38
+ 506 => "variant also negotiates",
39
+ 507 => "insufficient storage",
40
+ 508 => "loop detected",
41
+ 510 => "not extended",
42
+ 511 => "network authentication required"
43
+ }
44
+
45
+ def initialize(code, message: nil, httpver: "HTTP/1.0")
46
+ code = code.to_i
47
+ @code = code
48
+ @message = message ? message : "Error: #{code} (#{@@error_messages_by_code[code]})"
49
+ @httpver = httpver
50
+ end
51
+
52
+ def response
53
+ return Response.new(status: @code, data: html_page, content_type: "text/html", httpver: @httpver, headers: {Connection: "close"})
54
+ end
55
+
56
+ def html_page
57
+ return File.read(File.dirname(__FILE__) + "/html/error_page.template.html").gsub("[ecode]", @code.to_s).gsub("[emsg]", @message)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,10 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Error [ecode]</title>
5
+ </head>
6
+
7
+ <body>
8
+ <h1>[emsg]</h1>
9
+ </body>
10
+ </html>
@@ -0,0 +1,51 @@
1
+ module MLserver
2
+ class Logger
3
+ @@log_levels = [:info, :warn, :error]
4
+
5
+ def initialize(out:, err:, log_colors: {}, outputs: {error: :err})
6
+ @out = out
7
+ @err = err
8
+ @log_colors = log_colors
9
+ @outputs = outputs
10
+ end
11
+
12
+ def log(message, level = :info)
13
+ out = @out
14
+ out = @err if @outputs[level] == :err
15
+
16
+ if @log_colors[level]
17
+ message = message.color @log_colors[level]
18
+ end
19
+ out.puts message
20
+ end
21
+
22
+ def format_ip_address(address)
23
+ ipv4_re = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/
24
+ ipv4_anywhere_re = /((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}/
25
+ ipv4_in_ipv6_re = /^(\:\:ffff\:)((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/
26
+ ipv6_re = /(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))/
27
+
28
+ return address if address.match?(ipv4_re)
29
+ if address.match?(ipv4_in_ipv6_re)
30
+ ipv4_address = address.match(ipv4_anywhere_re).to_s
31
+ return ipv4_address
32
+ end
33
+ if address.match?(ipv6_re)
34
+ return "[#{address}]"
35
+ end
36
+ end
37
+
38
+ def log_traffic(ip, direction, data)
39
+ symbol = (direction == :incoming ? "=>" : "<=")
40
+
41
+ log("#{format_ip_address ip} #{symbol} #{data}")
42
+ end
43
+ end
44
+
45
+ dlcolors = {
46
+ warn: :yellow,
47
+ error: :red
48
+ }
49
+
50
+ DefaultLogger = Logger.new(out: STDOUT, err: STDERR, log_colors: dlcolors)
51
+ end
@@ -0,0 +1,13 @@
1
+ module MLserver
2
+ class RedirectResponse
3
+ def initialize(url, type: 302, httpver: "HTTP/1.1")
4
+ @type = type.to_i
5
+ @url = url.to_s
6
+ @httpver = httpver
7
+ end
8
+
9
+ def response
10
+ return Response.new(status: @type, headers: {Location: @url, Connection: "close"}, data: "Redirecting... If you don't get redirected, go to the following URL: #{@url}", httpver: @httpver)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,31 @@
1
+ module MLserver
2
+ class Request
3
+ def initialize(headers: {}, request:, data: "", client:)
4
+ @headers = headers
5
+ @request = request
6
+ @data = data
7
+ @client = client
8
+
9
+ @request_split = @request.split(" ")
10
+ end
11
+
12
+ def method
13
+ @request_split[0].upcase
14
+ end
15
+
16
+ def path
17
+ @request_split[1]
18
+ end
19
+
20
+ def httpver
21
+ @request_split[2]
22
+ end
23
+
24
+ def respond(response)
25
+ MLserver.settings.logger.log_traffic @client.peeraddr[2], :outgoing, "#{response.httpver} #{response.status}"
26
+ @client.puts response.to_s
27
+ end
28
+
29
+ attr_reader :headers, :request, :data
30
+ end
31
+ end
@@ -0,0 +1,42 @@
1
+ module MLserver
2
+ module RequestParser
3
+ def self.parse_request(client)
4
+ keepReading = true
5
+ headers = {}
6
+ data = ""
7
+
8
+ req = client.gets.to_s
9
+
10
+ while req.gsub("\r\n", "") == "" do
11
+ req = client.gets.to_s
12
+ end
13
+
14
+ #Close if bad request
15
+ if req.to_s.length < 14
16
+ client.puts ErrorResponse.new(400).response.to_s
17
+ client.close
18
+ Thread.exit
19
+ end
20
+
21
+ #Get all headers
22
+ while keepReading do
23
+ x = client.gets.to_s
24
+ if x.chomp.length == 0
25
+ keepReading = false
26
+ else
27
+ if x.split(": ").length < 2
28
+ client.puts ErrorResponse.new(400).response.to_s
29
+ client.close
30
+ Thread.exit
31
+ end
32
+ headers[x.split(": ")[0].to_sym] = x.split(": ")[1].chomp
33
+ end
34
+ end
35
+
36
+ #Get payload data
37
+ data = client.read(headers[:"Content-Length"].to_i)
38
+
39
+ return Request.new(headers: headers, request: req, data: data, client: client)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,41 @@
1
+ module MLserver
2
+ class Response
3
+ @@default_headers = {
4
+ Server: "MLserver #{MLserver.version}",
5
+ "Content-Type": "text/plain"
6
+ }
7
+
8
+ def initialize(status:200, headers:{}, data:"", content_type: nil, httpver: "HTTP/1.1")
9
+ @status = status
10
+ @data = data
11
+ @httpver = httpver
12
+ @headers = @@default_headers.merge(response_specific_headers).merge(headers)
13
+ @headers[:"Content-Type"] = content_type if content_type
14
+ end
15
+
16
+ def to_s(array:false)
17
+ status_line = "#{httpver} #{status}"
18
+ headers = @headers.map { |header|
19
+ k=header[0].to_s
20
+ v=header[1].to_s
21
+ header = "#{k}: #{v}"
22
+ }
23
+ headers = headers.join("\r\n") if !array
24
+ data = @data
25
+
26
+ return "#{status_line}\r\n#{headers}\r\n\r\n#{data}\r\n" if !array
27
+ return [status_line, headers, "", data, ""] if array
28
+ end
29
+
30
+ attr_accessor :status, :headers, :data, :httpver
31
+
32
+ private
33
+
34
+ def response_specific_headers
35
+ {
36
+ Date: Time.now.strftime("%a, %d %b %Y %H:%M:%S %Z"),
37
+ "Content-Length": @data.length,
38
+ }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,61 @@
1
+ module MLserver
2
+ module Server
3
+ @@valid_http_versions = ["HTTP/1.0", "HTTP/1.1"]
4
+
5
+ def self.start
6
+ settings = MLserver.settings
7
+
8
+ host = settings.host
9
+ port = settings.port
10
+ handler = settings.handler
11
+ logger = settings.logger
12
+
13
+ logger.log "MLserver #{MLserver.version}"
14
+
15
+ server = TCPServer.new(host, port)
16
+
17
+ logger.log "Listening on #{logger.format_ip_address host}:#{port}"
18
+
19
+ loop do
20
+ Thread.start(server.accept) do |client|
21
+ loop do
22
+ r=RequestParser.parse_request(client)
23
+
24
+ logger.log_traffic client.peeraddr[2], :incoming, "#{r.method} #{r.path} #{r.httpver}"
25
+
26
+ if !@@valid_http_versions.include?(r.httpver)
27
+ client_ip = client.peeraddr[2]
28
+
29
+ resp = MLserver::ErrorResponse.new(505)
30
+ r.respond resp.response
31
+ client.close
32
+
33
+ Thread.exit
34
+ end
35
+
36
+ if r.httpver == "HTTP/1.1"
37
+ if !r.headers[:Host]
38
+ r.respond ErrorResponse.new(400).response
39
+ client.close
40
+ Thread.exit
41
+ elsif settings.force_host
42
+ if !settings.force_host.include?(r.headers[:Host])
43
+ r.respond ErrorResponse.new(400).response
44
+ client.close
45
+ Thread.exit
46
+ end
47
+ end
48
+ end
49
+
50
+ handler.run(r, client)
51
+
52
+ if r.httpver == "HTTP/1.0" || r.headers[:Connection] == "close"
53
+ client.close
54
+ Thread.exit
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,23 @@
1
+ module MLserver
2
+ @@settings = nil
3
+
4
+ class Settings
5
+ def initialize(host: "0.0.0.0", port: "5555", handler:, logger:, force_host: false)
6
+ @host = host
7
+ @port = port.to_i
8
+ @handler = handler
9
+ @logger = logger
10
+ @force_host = force_host
11
+ end
12
+
13
+ attr_accessor :host, :port, :handler, :logger, :force_host
14
+ end
15
+
16
+ def self.settings
17
+ @@settings
18
+ end
19
+
20
+ def self.settings=(x)
21
+ @@settings=x
22
+ end
23
+ end
data/lib/MLserver.rb ADDED
@@ -0,0 +1,18 @@
1
+ module MLserver
2
+ def self.version
3
+ "1.0.0"
4
+ end
5
+ end
6
+
7
+ require "socket"
8
+ require "rbtext"
9
+ require "rbtext/string_methods"
10
+ require_relative "MLserver/request.rb"
11
+ require_relative "MLserver/request_parser.rb"
12
+ require_relative "MLserver/response.rb"
13
+ require_relative "MLserver/server.rb"
14
+ require_relative "MLserver/client_handler.rb"
15
+ require_relative "MLserver/logger.rb"
16
+ require_relative "MLserver/error_response.rb"
17
+ require_relative "MLserver/redirect_response.rb"
18
+ require_relative "MLserver/settings.rb"
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mlserver
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Matthias Lee
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-04-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: argparse
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.0.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.0.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: rbtext
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.3.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.3.3
41
+ description: A simple web server
42
+ email: matthias@matthiasclee.com
43
+ executables:
44
+ - mlserver
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - bin/mlserver
49
+ - lib/MLserver.rb
50
+ - lib/MLserver/client_handler.rb
51
+ - lib/MLserver/error_response.rb
52
+ - lib/MLserver/html/error_page.template.html
53
+ - lib/MLserver/logger.rb
54
+ - lib/MLserver/redirect_response.rb
55
+ - lib/MLserver/request.rb
56
+ - lib/MLserver/request_parser.rb
57
+ - lib/MLserver/response.rb
58
+ - lib/MLserver/server.rb
59
+ - lib/MLserver/settings.rb
60
+ homepage: https://github.com/Matthiasclee/MLServer
61
+ licenses: []
62
+ metadata: {}
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubygems_version: 3.1.6
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: A simple web server
82
+ test_files: []