async-htty 0.4.0 → 0.5.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57888805cdcf6fd48de1750317bdaff0252e1b912109ac138ab16e781152efba
4
- data.tar.gz: b1f8814861de4ef764495a9a7ef5d43aa954ab01248c3e905b32bceec081f068
3
+ metadata.gz: c05feb5444f1fb836e415c02bc4298c1999358b35c5f9775dc7da7f6d52f3c7c
4
+ data.tar.gz: 93215fb6565f7033e45b70e4c3f7831998e0b10e926afac2e098c8fb38cdbfd5
5
5
  SHA512:
6
- metadata.gz: 0c564e40838c62f1918c1c95317638088e96b54aacf7aa3d0282fcb8046795a320a428e332fab27805fdccefbbd6f096090f7351bdf5143c1fd0a5618ebed753
7
- data.tar.gz: 1129bb1a6e58dc3dd0b4aff3572377871cc0ba98ae553330cc5211032d08c5b5d777f8392e45885ae95d4ebafe1fb1a9b13c4c74027f048eb72a21a15fa681c2
6
+ metadata.gz: e662f9bad50e239ef015d27b45299f959ef16376fe03d2eac4790ed1cf4ccd96e970792123c1346e1cb69d99436a47783e886e4ccafbbdbebf52d8d568d991d0
7
+ data.tar.gz: 167fa6017a168cb7a6cfcd13fb1feaa4e02d41afdfa8dfb6e612d58fc3cee145ee38424d8efad8f3db236f6c8f3a0899f3271e7baaec9f6e2f00b753af796e0a
checksums.yaml.gz.sig CHANGED
Binary file
@@ -49,15 +49,15 @@ TEXT
49
49
  STDIN.raw(min: 0, time: 1) do |input|
50
50
  loop do
51
51
  chunk = input.read_nonblock(32, exception: false)
52
-
52
+
53
53
  case chunk
54
54
  when :wait_readable, nil
55
55
  # Keep the process alive while waiting for external signals.
56
56
  next
57
57
  else
58
58
  bytes = chunk.bytes
59
- puts "read #{bytes.length} byte(s): #{bytes.map {|byte| format("0x%02X", byte)}.join(" ")}"
60
-
59
+ puts "read #{bytes.length} byte(s): #{bytes.map{|byte| format("0x%02X", byte)}.join(" ")}"
60
+
61
61
  break if chunk.include?("q")
62
62
  break if bytes.include?(3)
63
63
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ require_relative "../service/server"
7
+
8
+ module Async
9
+ module HTTY
10
+ module Environment
11
+ # An async-service environment module for HTTY transport.
12
+ # Include this (or let Lively::Environment::Application include it)
13
+ # to replace Falcon with an HTTY server in the service lifecycle.
14
+ module Server
15
+ # Do not restart after the HTTY session ends.
16
+ # Unlike a TCP server, there is nothing to reconnect to once
17
+ # stdin/stdout closes.
18
+ # @returns [Hash]
19
+ def container_options
20
+ {count: 1, restart: false}
21
+ end
22
+
23
+ # Use the HTTY service server instead of Falcon's.
24
+ # @returns [Class]
25
+ def service_class
26
+ Async::HTTY::Service::Server
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -28,7 +28,7 @@ module Async
28
28
  def self.open(app = nil, input: $stdin, output: $stdout, error: $stderr, env: ENV, **options, &block)
29
29
  app ||= block
30
30
  server = self.new(app, **options)
31
-
31
+
32
32
  case env["HTTY"]
33
33
  when "0"
34
34
  raise DisabledError, "HTTY is disabled!"
@@ -36,7 +36,7 @@ module Async
36
36
  $stderr.puts "HTTY is not supported by this environment, visit https://htty.dev for more information."
37
37
  raise UnsupportedError, "HTTY is not supported by this environment"
38
38
  end
39
-
39
+
40
40
  unless input.respond_to?(:tty?) && input.tty?
41
41
  raise UnsupportedError, "HTTY requires a TTY input stream"
42
42
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ require "async/service/generic"
7
+ require_relative "../server"
8
+
9
+ module Async
10
+ module HTTY
11
+ module Service
12
+ # An async-service managed service that runs an HTTY server.
13
+ # Mirrors Falcon::Service::Server but binds to stdin/stdout instead
14
+ # of a TCP endpoint. The container thread lifetime equals the HTTY session.
15
+ #
16
+ # We use Generic rather than Managed::Service because Managed::Service
17
+ # calls instance.ready! *after* run() returns. For HTTY, run() blocks for
18
+ # the entire session, so ready! would never be signalled while the session
19
+ # is live — causing the container controller to hang in wait_until_ready.
20
+ class Server < Async::Service::Generic
21
+ # Set up the container to run the HTTY server.
22
+ # Signals instance.ready! before the blocking accept loop so the
23
+ # controller does not stall waiting for startup confirmation.
24
+ #
25
+ # @parameter container [Async::Container::Generic]
26
+ def setup(container)
27
+ super
28
+
29
+ container.run(**@evaluator.container_options) do |instance|
30
+ Sync do
31
+ server = Async::HTTY::Server.new(@evaluator.middleware)
32
+
33
+ original_input = $stdin.dup
34
+ original_output = $stdout.dup
35
+ original_error = $stderr.dup
36
+
37
+ stream = ::Protocol::HTTY::Stream.new(original_input, original_output)
38
+
39
+ $stdin.reopen(File::NULL)
40
+ $stdout.reopen(File::NULL)
41
+ if path = ENV.fetch("HTTY_ERROR_LOG", nil)
42
+ $stderr.reopen(File.open(path, "a"))
43
+ else
44
+ $stderr.reopen(File::NULL)
45
+ end
46
+
47
+ instance.ready!
48
+
49
+ Async::HTTY::Server.with_raw_terminal(original_input) do
50
+ server.accept(stream)
51
+ end
52
+ ensure
53
+ $stdin.reopen(original_input) rescue nil
54
+ $stdout.reopen(original_output) rescue nil
55
+ $stderr.reopen(original_error) rescue nil
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -5,6 +5,6 @@
5
5
 
6
6
  module Async
7
7
  module HTTY
8
- VERSION = "0.4.0"
8
+ VERSION = "0.5.0"
9
9
  end
10
10
  end
data/readme.md CHANGED
@@ -14,6 +14,10 @@ Please see the [project documentation](https://socketry.github.io/async-htty/) f
14
14
 
15
15
  Please see the [project releases](https://socketry.github.io/async-htty/releases/index) for all releases.
16
16
 
17
+ ### v0.5.0
18
+
19
+ - Add `Async::HTTY::Service::Server` and `Async::HTTY::Environment::Server`.
20
+
17
21
  ### v0.4.0
18
22
 
19
23
  - Suppress errors from `send_goaway` during connection cleanup to prevent shutdown exceptions from propagating.
data/releases.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Releases
2
2
 
3
+ ## v0.5.0
4
+
5
+ - Add `Async::HTTY::Service::Server` and `Async::HTTY::Environment::Server`.
6
+
3
7
  ## v0.4.0
4
8
 
5
9
  - Suppress errors from `send_goaway` during connection cleanup to prevent shutdown exceptions from propagating.
@@ -209,15 +209,15 @@ describe Async::HTTY::Server do
209
209
 
210
210
  expect(Async::HTTY::DisabledError).to be < Async::HTTY::UnsupportedError
211
211
  end
212
-
212
+
213
213
  it "raises a typed error when stdin is not a tty" do
214
214
  input = Async::HTTY::FakeFile.new
215
215
  output = Async::HTTY::FakeFile.new
216
-
216
+
217
217
  expect do
218
218
  subject.open(Protocol::HTTP::Middleware::Okay, input: input, output: output, error: error, env: env)
219
219
  end.to raise_exception(Async::HTTY::UnsupportedError, message: be =~ /TTY input/)
220
-
220
+
221
221
  expect(input.reopen_events).to be == []
222
222
  expect(output.reopen_events).to be == []
223
223
  end
@@ -238,12 +238,12 @@ describe Async::HTTY::Server do
238
238
 
239
239
  expect(error_output.string).to be(:include?, "https://htty.dev")
240
240
  end
241
-
241
+
242
242
  it "opens a server within its own async context when no task is provided" do
243
243
  input = Async::HTTY::FakeFile.new(tty: true)
244
244
  output = Async::HTTY::FakeFile.new
245
245
  accepted = false
246
-
246
+
247
247
  server = Object.new
248
248
  server.define_singleton_method(:each) do
249
249
  end
@@ -254,15 +254,15 @@ describe Async::HTTY::Server do
254
254
  end
255
255
  server.define_singleton_method(:close) do
256
256
  end
257
-
257
+
258
258
  protocol = Object.new
259
259
  protocol.define_singleton_method(:server) do |stream|
260
260
  accepted = true
261
261
  server
262
262
  end
263
-
263
+
264
264
  subject.open(Protocol::HTTP::Middleware::Okay, input: input, output: output, error: error, env: env, protocol: protocol)
265
-
265
+
266
266
  expect(accepted).to be == true
267
267
  end
268
268
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-htty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.3'
89
+ version: '0.4'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.3'
96
+ version: '0.4'
97
97
  executables: []
98
98
  extensions: []
99
99
  extra_rdoc_files: []
@@ -102,11 +102,13 @@ files:
102
102
  - examples/hello_world.rb
103
103
  - examples/raw_mode_probe.rb
104
104
  - lib/async/htty.rb
105
+ - lib/async/htty/environment/server.rb
105
106
  - lib/async/htty/error.rb
106
107
  - lib/async/htty/protocol.rb
107
108
  - lib/async/htty/protocol/htty.rb
108
109
  - lib/async/htty/protocol/htty/server.rb
109
110
  - lib/async/htty/server.rb
111
+ - lib/async/htty/service/server.rb
110
112
  - lib/async/htty/version.rb
111
113
  - license.md
112
114
  - readme.md
metadata.gz.sig CHANGED
Binary file