itsi-server 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/exe/itsi +88 -28
  3. data/ext/itsi_error/Cargo.toml +2 -0
  4. data/ext/itsi_error/src/from.rs +70 -0
  5. data/ext/itsi_error/src/lib.rs +10 -37
  6. data/ext/itsi_instrument_entry/Cargo.toml +15 -0
  7. data/ext/itsi_instrument_entry/src/lib.rs +31 -0
  8. data/ext/itsi_rb_helpers/Cargo.toml +2 -0
  9. data/ext/itsi_rb_helpers/src/heap_value.rs +121 -0
  10. data/ext/itsi_rb_helpers/src/lib.rs +90 -10
  11. data/ext/itsi_scheduler/Cargo.toml +24 -0
  12. data/ext/itsi_scheduler/extconf.rb +6 -0
  13. data/ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs +56 -0
  14. data/ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs +44 -0
  15. data/ext/itsi_scheduler/src/itsi_scheduler/timer.rs +44 -0
  16. data/ext/itsi_scheduler/src/itsi_scheduler.rs +308 -0
  17. data/ext/itsi_scheduler/src/lib.rs +38 -0
  18. data/ext/itsi_server/Cargo.toml +14 -2
  19. data/ext/itsi_server/extconf.rb +1 -1
  20. data/ext/itsi_server/src/body_proxy/big_bytes.rs +104 -0
  21. data/ext/itsi_server/src/body_proxy/itsi_body_proxy.rs +122 -0
  22. data/ext/itsi_server/src/body_proxy/mod.rs +2 -0
  23. data/ext/itsi_server/src/lib.rs +58 -7
  24. data/ext/itsi_server/src/request/itsi_request.rs +238 -104
  25. data/ext/itsi_server/src/response/itsi_response.rs +347 -0
  26. data/ext/itsi_server/src/response/mod.rs +1 -0
  27. data/ext/itsi_server/src/server/bind.rs +50 -20
  28. data/ext/itsi_server/src/server/bind_protocol.rs +37 -0
  29. data/ext/itsi_server/src/server/io_stream.rs +104 -0
  30. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.crt +11 -30
  31. data/ext/itsi_server/src/server/itsi_ca/itsi_ca.key +3 -50
  32. data/ext/itsi_server/src/server/itsi_server.rs +196 -134
  33. data/ext/itsi_server/src/server/lifecycle_event.rs +9 -0
  34. data/ext/itsi_server/src/server/listener.rs +184 -127
  35. data/ext/itsi_server/src/server/mod.rs +7 -1
  36. data/ext/itsi_server/src/server/process_worker.rs +196 -0
  37. data/ext/itsi_server/src/server/serve_strategy/cluster_mode.rs +254 -0
  38. data/ext/itsi_server/src/server/serve_strategy/mod.rs +27 -0
  39. data/ext/itsi_server/src/server/serve_strategy/single_mode.rs +241 -0
  40. data/ext/itsi_server/src/server/signal.rs +70 -0
  41. data/ext/itsi_server/src/server/thread_worker.rs +368 -0
  42. data/ext/itsi_server/src/server/tls.rs +42 -28
  43. data/ext/itsi_tracing/Cargo.toml +4 -0
  44. data/ext/itsi_tracing/src/lib.rs +36 -6
  45. data/lib/itsi/request.rb +30 -14
  46. data/lib/itsi/server/rack/handler/itsi.rb +25 -0
  47. data/lib/itsi/server/scheduler_mode.rb +6 -0
  48. data/lib/itsi/server/version.rb +1 -1
  49. data/lib/itsi/server.rb +82 -2
  50. data/lib/itsi/signals.rb +23 -0
  51. data/lib/itsi/stream_io.rb +38 -0
  52. metadata +38 -25
  53. data/ext/itsi_server/src/server/transfer_protocol.rs +0 -23
  54. data/ext/itsi_server/src/stream_writer/mod.rs +0 -21
data/lib/itsi/server.rb CHANGED
@@ -2,12 +2,92 @@
2
2
 
3
3
  require_relative "server/version"
4
4
  require_relative "server/itsi_server"
5
+ require_relative "signals"
6
+ require_relative "request"
7
+ require_relative "stream_io"
8
+ require_relative "server/rack/handler/itsi"
5
9
 
6
10
  module Itsi
7
11
  class Server
8
- # Call our Rack app with our request ENV.
12
+
13
+ def self.running?
14
+ @running ||= false
15
+ end
16
+
17
+ def self.start(app:, **opts)
18
+ server = new(app: ->{app}, **opts)
19
+ @running = true
20
+ Signal.trap('INT', 'DEFAULT')
21
+ server.start
22
+ ensure
23
+ @running = false
24
+ end
25
+
9
26
  def self.call(app, request)
10
- app.call(request.to_env)
27
+ respond request, app.call(request.to_env)
28
+ end
29
+
30
+ def self.streaming_body?(body)
31
+ body.respond_to?(:call) && !body.respond_to?(:each)
32
+ end
33
+
34
+ def self.respond(request, (status, headers, body))
35
+ response = request.response
36
+
37
+ # Don't try and respond if we've been hijacked.
38
+ # The hijacker is now responsible for this.
39
+ return if request.hijacked
40
+
41
+ # 1. Set Status
42
+ response.status = status
43
+
44
+ # 2. Set Headers
45
+ headers.each do |key, value|
46
+ next response.add_header(key, value) unless value.is_a?(Array)
47
+
48
+ value.each do |v|
49
+ response.add_header(key, v)
50
+ end
51
+ end
52
+
53
+ # 3. Set Body
54
+ # As soon as we start setting the response
55
+ # the server will begin to stream it to the client.
56
+
57
+ # If we're partially hijacked or returned a streaming body,
58
+ # stream this response.
59
+
60
+ if (body_streamer = streaming_body?(body) ? body : headers.delete("rack.hijack"))
61
+ body_streamer.call(StreamIO.new(response))
62
+
63
+ # If we're enumerable with more than one chunk
64
+ # also stream, otherwise write in a single chunk
65
+ elsif body.respond_to?(:each) || body.respond_to?(:to_ary)
66
+ unless body.respond_to?(:each)
67
+ body = body.to_ary
68
+ raise "Body #to_ary didn't return an array" unless body.is_a?(Array)
69
+ end
70
+ # We offset this iteration intentionally,
71
+ # to optimize for the case where there's only one chunk.
72
+ buffer = nil
73
+ body.each do |part|
74
+ response.send_frame(buffer.to_s) if buffer
75
+ buffer = part
76
+ end
77
+
78
+ response.send_and_close(buffer.to_s)
79
+ else
80
+ response.send_and_close(body.to_s)
81
+ end
82
+ ensure
83
+ response.close_write
84
+ body.close if body.respond_to?(:close)
85
+ end
86
+
87
+ def self.start_scheduler_loop(scheduler_class, scheduler_task)
88
+ scheduler = scheduler_class.new
89
+ Fiber.set_scheduler(scheduler)
90
+ [scheduler, Fiber.schedule(&scheduler_task)]
11
91
  end
12
92
 
13
93
  # If scheduler is enabled
@@ -0,0 +1,23 @@
1
+ module Itsi
2
+ module Signals
3
+ DEFAULT_SIGNALS = ["DEFAULT", ""].freeze
4
+ module SignalTrap
5
+ def self.trap(signal, *args, &block)
6
+ if DEFAULT_SIGNALS.include?(command.to_s) && block.nil?
7
+ Itsi::Server.reset_signal_handlers
8
+ nil
9
+ else
10
+ super(signal, *args, &block)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ [Kernel, Signal].each do |receiver|
18
+ receiver.singleton_class.prepend(Itsi::Signals::SignalTrap)
19
+ end
20
+
21
+ [Object].each do |receiver|
22
+ receiver.include(Itsi::Signals::SignalTrap)
23
+ end
@@ -0,0 +1,38 @@
1
+ module Itsi
2
+ class StreamIO
3
+ attr :response
4
+ def initialize(response)
5
+ @response = response
6
+ end
7
+
8
+ def read
9
+ response.recv_frame
10
+ end
11
+
12
+ def write(string)
13
+ response.send_frame(string)
14
+ end
15
+
16
+ def <<(string)
17
+ response.send_frame(string)
18
+ end
19
+
20
+ def flush
21
+ # No-op
22
+ end
23
+
24
+ def close
25
+ close_read
26
+ close_write
27
+ end
28
+
29
+ def close_read
30
+ response.close_read
31
+ end
32
+
33
+ def close_write
34
+ response.close_write
35
+ end
36
+
37
+ end
38
+ end
metadata CHANGED
@@ -1,28 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: itsi-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wouter Coppieters
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-03-01 00:00:00.000000000 Z
10
+ date: 2025-03-14 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: libclang
13
+ name: rack
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - "~>"
16
+ - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '14.0'
18
+ version: '1.6'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - "~>"
23
+ - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '14.0'
25
+ version: '1.6'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: rb_sys
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -37,20 +37,6 @@ dependencies:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: 0.9.91
40
- - !ruby/object:Gem::Dependency
41
- name: rack
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '2'
47
- type: :runtime
48
- prerelease: false
49
- version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '2'
54
40
  description: Itsi Server - A light-weight Rack Server implementation for Ruby
55
41
  email:
56
42
  - wc@pico.net.nz
@@ -69,30 +55,57 @@ files:
69
55
  - exe/itsi
70
56
  - ext/itsi_error/Cargo.lock
71
57
  - ext/itsi_error/Cargo.toml
58
+ - ext/itsi_error/src/from.rs
72
59
  - ext/itsi_error/src/lib.rs
60
+ - ext/itsi_instrument_entry/Cargo.toml
61
+ - ext/itsi_instrument_entry/src/lib.rs
73
62
  - ext/itsi_rb_helpers/Cargo.lock
74
63
  - ext/itsi_rb_helpers/Cargo.toml
64
+ - ext/itsi_rb_helpers/src/heap_value.rs
75
65
  - ext/itsi_rb_helpers/src/lib.rs
66
+ - ext/itsi_scheduler/Cargo.toml
67
+ - ext/itsi_scheduler/extconf.rb
68
+ - ext/itsi_scheduler/src/itsi_scheduler.rs
69
+ - ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs
70
+ - ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs
71
+ - ext/itsi_scheduler/src/itsi_scheduler/timer.rs
72
+ - ext/itsi_scheduler/src/lib.rs
76
73
  - ext/itsi_server/Cargo.toml
77
74
  - ext/itsi_server/extconf.rb
75
+ - ext/itsi_server/src/body_proxy/big_bytes.rs
76
+ - ext/itsi_server/src/body_proxy/itsi_body_proxy.rs
77
+ - ext/itsi_server/src/body_proxy/mod.rs
78
78
  - ext/itsi_server/src/lib.rs
79
79
  - ext/itsi_server/src/request/itsi_request.rs
80
80
  - ext/itsi_server/src/request/mod.rs
81
+ - ext/itsi_server/src/response/itsi_response.rs
82
+ - ext/itsi_server/src/response/mod.rs
81
83
  - ext/itsi_server/src/server/bind.rs
84
+ - ext/itsi_server/src/server/bind_protocol.rs
85
+ - ext/itsi_server/src/server/io_stream.rs
82
86
  - ext/itsi_server/src/server/itsi_ca/itsi_ca.crt
83
87
  - ext/itsi_server/src/server/itsi_ca/itsi_ca.key
84
88
  - ext/itsi_server/src/server/itsi_server.rs
89
+ - ext/itsi_server/src/server/lifecycle_event.rs
85
90
  - ext/itsi_server/src/server/listener.rs
86
91
  - ext/itsi_server/src/server/mod.rs
92
+ - ext/itsi_server/src/server/process_worker.rs
93
+ - ext/itsi_server/src/server/serve_strategy/cluster_mode.rs
94
+ - ext/itsi_server/src/server/serve_strategy/mod.rs
95
+ - ext/itsi_server/src/server/serve_strategy/single_mode.rs
96
+ - ext/itsi_server/src/server/signal.rs
97
+ - ext/itsi_server/src/server/thread_worker.rs
87
98
  - ext/itsi_server/src/server/tls.rs
88
- - ext/itsi_server/src/server/transfer_protocol.rs
89
- - ext/itsi_server/src/stream_writer/mod.rs
90
99
  - ext/itsi_tracing/Cargo.lock
91
100
  - ext/itsi_tracing/Cargo.toml
92
101
  - ext/itsi_tracing/src/lib.rs
93
102
  - lib/itsi/request.rb
94
103
  - lib/itsi/server.rb
104
+ - lib/itsi/server/rack/handler/itsi.rb
105
+ - lib/itsi/server/scheduler_mode.rb
95
106
  - lib/itsi/server/version.rb
107
+ - lib/itsi/signals.rb
108
+ - lib/itsi/stream_io.rb
96
109
  - sig/itsi_server.rbs
97
110
  homepage: https://itsi.fyi
98
111
  licenses:
@@ -108,12 +121,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
108
121
  requirements:
109
122
  - - ">="
110
123
  - !ruby/object:Gem::Version
111
- version: 3.1.0
124
+ version: 2.7.0
112
125
  required_rubygems_version: !ruby/object:Gem::Requirement
113
126
  requirements:
114
127
  - - ">="
115
128
  - !ruby/object:Gem::Version
116
- version: 3.3.11
129
+ version: '3.1'
117
130
  requirements: []
118
131
  rubygems_version: 3.6.2
119
132
  specification_version: 4
@@ -1,23 +0,0 @@
1
- use itsi_error::ItsiError;
2
- use std::str::FromStr;
3
-
4
- #[derive(Debug, Default, Clone)]
5
- pub enum TransferProtocol {
6
- #[default]
7
- Https,
8
- Http,
9
- Unix,
10
- }
11
-
12
- impl FromStr for TransferProtocol {
13
- type Err = ItsiError;
14
-
15
- fn from_str(s: &str) -> Result<Self, Self::Err> {
16
- match s {
17
- "http" => Ok(TransferProtocol::Http),
18
- "https" => Ok(TransferProtocol::Https),
19
- "unix" => Ok(TransferProtocol::Unix),
20
- _ => Err(ItsiError::UnsupportedProtocol(s.to_string())),
21
- }
22
- }
23
- }
@@ -1,21 +0,0 @@
1
- use bytes::Bytes;
2
- use magnus::{error::Result as MagnusResult, exception::io_error, Error};
3
- use tokio::sync::mpsc::Sender;
4
-
5
- #[magnus::wrap(class = "Itsi::StreamWriter", free_immediately, size)]
6
- pub struct StreamWriter {
7
- sender: Sender<Bytes>,
8
- }
9
-
10
- impl StreamWriter {
11
- pub fn new(sender: Sender<Bytes>) -> Self {
12
- StreamWriter { sender }
13
- }
14
-
15
- pub fn write(&self, bytes: Bytes) -> MagnusResult<()> {
16
- self.sender
17
- .blocking_send(bytes)
18
- .map_err(|e| Error::new(io_error(), format!("{:?}", e)))?;
19
- Ok(())
20
- }
21
- }