itsi-server 0.1.1 → 0.1.2

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 +181 -133
  33. data/ext/itsi_server/src/server/lifecycle_event.rs +8 -0
  34. data/ext/itsi_server/src/server/listener.rs +169 -128
  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 +253 -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 +238 -0
  40. data/ext/itsi_server/src/server/signal.rs +57 -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 +68 -2
  50. data/lib/itsi/signals.rb +18 -0
  51. data/lib/itsi/stream_io.rb +38 -0
  52. metadata +41 -14
  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,78 @@
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.
9
12
  def self.call(app, request)
10
- app.call(request.to_env)
13
+ respond request, app.call(request.to_env)
14
+ end
15
+
16
+ def self.streaming_body?(body)
17
+ body.respond_to?(:call) && !body.respond_to?(:each)
18
+ end
19
+
20
+ def self.respond(request, (status, headers, body))
21
+ response = request.response
22
+
23
+ # Don't try and respond if we've been hijacked.
24
+ # The hijacker is now responsible for this.
25
+ return if request.hijacked
26
+
27
+ # 1. Set Status
28
+ response.status = status
29
+
30
+ # 2. Set Headers
31
+ headers.each do |key, value|
32
+ next response.add_header(key, value) unless value.is_a?(Array)
33
+
34
+ value.each do |v|
35
+ response.add_header(key, v)
36
+ end
37
+ end
38
+
39
+ # 3. Set Body
40
+ # As soon as we start setting the response
41
+ # the server will begin to stream it to the client.
42
+
43
+ # If we're partially hijacked or returned a streaming body,
44
+ # stream this response.
45
+
46
+ if (body_streamer = streaming_body?(body) ? body : headers.delete("rack.hijack"))
47
+ body_streamer.call(StreamIO.new(response))
48
+
49
+ # If we're enumerable with more than one chunk
50
+ # also stream, otherwise write in a single chunk
51
+ elsif body.respond_to?(:each) || body.respond_to?(:to_ary)
52
+ unless body.respond_to?(:each)
53
+ body = body.to_ary
54
+ raise "Body #to_ary didn't return an array" unless body.is_a?(Array)
55
+ end
56
+ # We offset this iteration intentionally,
57
+ # to optimize for the case where there's only one chunk.
58
+ buffer = nil
59
+ body.each do |part|
60
+ response.send_frame(buffer.to_s) if buffer
61
+ buffer = part
62
+ end
63
+
64
+ response.send_and_close(buffer.to_s)
65
+ else
66
+ response.send_and_close(body.to_s)
67
+ end
68
+ ensure
69
+ response.close_write
70
+ body.close if body.respond_to?(:close)
71
+ end
72
+
73
+ def self.start_scheduler_loop(scheduler_class, scheduler_task)
74
+ scheduler = scheduler_class.new
75
+ Fiber.set_scheduler(scheduler)
76
+ [scheduler, Fiber.schedule(&scheduler_task)]
11
77
  end
12
78
 
13
79
  # If scheduler is enabled
@@ -0,0 +1,18 @@
1
+ module Itsi
2
+ module Signals
3
+ DEFAULT_SIGNALS = ["DEFAULT", ""].freeze
4
+ module TrapInterceptor
5
+ def trap(signal, command = nil, &block)
6
+ return super unless DEFAULT_SIGNALS.include?(command.to_s) && block.nil?
7
+ Itsi::Server.reset_signal_handlers
8
+ end
9
+ end
10
+ [Kernel, Signal].each do |receiver|
11
+ receiver.singleton_class.prepend(TrapInterceptor)
12
+ end
13
+
14
+ [Object].each do |receiver|
15
+ receiver.include(TrapInterceptor)
16
+ end
17
+ end
18
+ 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,13 +1,13 @@
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.2
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-13 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: libclang
@@ -24,33 +24,33 @@ dependencies:
24
24
  - !ruby/object:Gem::Version
25
25
  version: '14.0'
26
26
  - !ruby/object:Gem::Dependency
27
- name: rb_sys
27
+ name: rack
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
- - - "~>"
30
+ - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 0.9.91
32
+ version: '1.6'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - "~>"
37
+ - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: 0.9.91
39
+ version: '1.6'
40
40
  - !ruby/object:Gem::Dependency
41
- name: rack
41
+ name: rb_sys
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '2'
46
+ version: 0.9.91
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '2'
53
+ version: 0.9.91
54
54
  description: Itsi Server - A light-weight Rack Server implementation for Ruby
55
55
  email:
56
56
  - wc@pico.net.nz
@@ -69,30 +69,57 @@ files:
69
69
  - exe/itsi
70
70
  - ext/itsi_error/Cargo.lock
71
71
  - ext/itsi_error/Cargo.toml
72
+ - ext/itsi_error/src/from.rs
72
73
  - ext/itsi_error/src/lib.rs
74
+ - ext/itsi_instrument_entry/Cargo.toml
75
+ - ext/itsi_instrument_entry/src/lib.rs
73
76
  - ext/itsi_rb_helpers/Cargo.lock
74
77
  - ext/itsi_rb_helpers/Cargo.toml
78
+ - ext/itsi_rb_helpers/src/heap_value.rs
75
79
  - ext/itsi_rb_helpers/src/lib.rs
80
+ - ext/itsi_scheduler/Cargo.toml
81
+ - ext/itsi_scheduler/extconf.rb
82
+ - ext/itsi_scheduler/src/itsi_scheduler.rs
83
+ - ext/itsi_scheduler/src/itsi_scheduler/io_helpers.rs
84
+ - ext/itsi_scheduler/src/itsi_scheduler/io_waiter.rs
85
+ - ext/itsi_scheduler/src/itsi_scheduler/timer.rs
86
+ - ext/itsi_scheduler/src/lib.rs
76
87
  - ext/itsi_server/Cargo.toml
77
88
  - ext/itsi_server/extconf.rb
89
+ - ext/itsi_server/src/body_proxy/big_bytes.rs
90
+ - ext/itsi_server/src/body_proxy/itsi_body_proxy.rs
91
+ - ext/itsi_server/src/body_proxy/mod.rs
78
92
  - ext/itsi_server/src/lib.rs
79
93
  - ext/itsi_server/src/request/itsi_request.rs
80
94
  - ext/itsi_server/src/request/mod.rs
95
+ - ext/itsi_server/src/response/itsi_response.rs
96
+ - ext/itsi_server/src/response/mod.rs
81
97
  - ext/itsi_server/src/server/bind.rs
98
+ - ext/itsi_server/src/server/bind_protocol.rs
99
+ - ext/itsi_server/src/server/io_stream.rs
82
100
  - ext/itsi_server/src/server/itsi_ca/itsi_ca.crt
83
101
  - ext/itsi_server/src/server/itsi_ca/itsi_ca.key
84
102
  - ext/itsi_server/src/server/itsi_server.rs
103
+ - ext/itsi_server/src/server/lifecycle_event.rs
85
104
  - ext/itsi_server/src/server/listener.rs
86
105
  - ext/itsi_server/src/server/mod.rs
106
+ - ext/itsi_server/src/server/process_worker.rs
107
+ - ext/itsi_server/src/server/serve_strategy/cluster_mode.rs
108
+ - ext/itsi_server/src/server/serve_strategy/mod.rs
109
+ - ext/itsi_server/src/server/serve_strategy/single_mode.rs
110
+ - ext/itsi_server/src/server/signal.rs
111
+ - ext/itsi_server/src/server/thread_worker.rs
87
112
  - 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
113
  - ext/itsi_tracing/Cargo.lock
91
114
  - ext/itsi_tracing/Cargo.toml
92
115
  - ext/itsi_tracing/src/lib.rs
93
116
  - lib/itsi/request.rb
94
117
  - lib/itsi/server.rb
118
+ - lib/itsi/server/rack/handler/itsi.rb
119
+ - lib/itsi/server/scheduler_mode.rb
95
120
  - lib/itsi/server/version.rb
121
+ - lib/itsi/signals.rb
122
+ - lib/itsi/stream_io.rb
96
123
  - sig/itsi_server.rbs
97
124
  homepage: https://itsi.fyi
98
125
  licenses:
@@ -108,12 +135,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
108
135
  requirements:
109
136
  - - ">="
110
137
  - !ruby/object:Gem::Version
111
- version: 3.1.0
138
+ version: 2.7.0
112
139
  required_rubygems_version: !ruby/object:Gem::Requirement
113
140
  requirements:
114
141
  - - ">="
115
142
  - !ruby/object:Gem::Version
116
- version: 3.3.11
143
+ version: '3.1'
117
144
  requirements: []
118
145
  rubygems_version: 3.6.2
119
146
  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
- }