syntropy 0.36.0 → 0.37.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: f92ed1375642f12fb92654d872bfde1d07c7ad74be97507af3c1616390b6ce14
4
- data.tar.gz: 5072d514bc07149f1d26bc900bcc9379970ebbbaf1b88eaa23933189fb684b37
3
+ metadata.gz: eb6c66cfca4b2d7be9060c5571028b5356950fb34f8fba634b0d43b97169cd48
4
+ data.tar.gz: 447b2442d22332e0642869e01eee03889d8c85ba31e58909d603382be51f4c7d
5
5
  SHA512:
6
- metadata.gz: f77aed02801a7b1c10f67a5ed7fbac9196580bd482c19ca660c81beaf5f2adcba86047635e35f9750d003538b12401962888193c058fbc3023ae88eae9038506
7
- data.tar.gz: f923ab3aa4e535d5a85a563acf7f97534639ca1ed3d02e2c2b94d9cecef57e06cefc3a72a1ca782d0118b012189298bc3c743b017c0ee2d06be8bbf55d03dc89
6
+ metadata.gz: 19d092f17b75b7cc3290cb2802f46bd19efbb30855f3acf78e1cfc2f88084e0c0dca68bbfcd661545bd50def484cc9526d8a2f4057c9a6da8d6639c95b2cd8c4
7
+ data.tar.gz: 34d839e355da94f8993ffefa1a48c9bebfecb6da9c6b43cfe7f934d4fe8a804889d2a7a75874f0107b3226bd6a880037466610f4cffd87d48bdab2f19c5e0545
data/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ # 0.37.0 2026-06-07
2
+
3
+ - Call `IO#clear` before closing server connection
4
+ - syntropy new:
5
+ - Remove socket mapping for backend in docker-compose.yml
6
+ - Add caddy reverse proxy to template
7
+ - Add overwrite confirmation, better file copying
8
+ - Verify storage module `migrate!` method exists
9
+ - Fix `set_schema_version` for usage in PG DB
10
+ - Do not raise exception on missing config module
11
+ - Do not convert class export value to class instance, allow exporting a
12
+ class
13
+ - Fix HTTP protocol error when pipelining post requests with empty body
14
+ - Setup fiber scheduler when running server
15
+
1
16
  # 0.36.0 2026-06-04
2
17
 
3
18
  - Rename `DB` to `Storage`
@@ -1,3 +1,4 @@
1
1
  Gemfile.lock
2
- storage/*
2
+ storage/*.db*
3
+ storage/caddy/*
3
4
  vendor/bundle
@@ -0,0 +1,5 @@
1
+ localhost {
2
+ reverse_proxy app_server:1234
3
+ tls internal
4
+ encode
5
+ }
@@ -7,17 +7,18 @@ services:
7
7
  - seccomp:unconfined
8
8
  volumes:
9
9
  - .:/syntropy
10
- ports:
11
- - "1234:1234"
12
10
  stop_signal: SIGINT
13
11
  stop_grace_period: 10s
14
- restart: always
12
+ restart: unless-stopped
15
13
  healthcheck:
16
14
  test: "curl 'http://localhost:1234/'"
17
15
  interval: "30s"
18
16
  timeout: "3s"
19
17
  start_period: "5s"
20
18
  retries: 3
19
+ networks:
20
+ - proxy_network
21
+
21
22
  console:
22
23
  build: .
23
24
  command: bundle exec syntropy console
@@ -31,6 +32,7 @@ services:
31
32
  - .:/syntropy
32
33
  stop_signal: SIGINT
33
34
  restart: never
35
+
34
36
  test:
35
37
  build: .
36
38
  command: bundle exec syntropy test -w
@@ -44,3 +46,26 @@ services:
44
46
  - .:/syntropy
45
47
  stop_signal: SIGINT
46
48
  restart: never
49
+
50
+ proxy:
51
+ depends_on:
52
+ - app_server
53
+ image: caddy:2-alpine
54
+ build:
55
+ context: ./proxy
56
+ dockerfile: Dockerfile
57
+ restart: unless-stopped
58
+ ports:
59
+ - "80:80"
60
+ - "443:443"
61
+ - "443:443/udp"
62
+ volumes:
63
+ - ./config/Caddyfile:/etc/caddy/Caddyfile
64
+ - ./storage/caddy/data:/data
65
+ - ./storage/caddy/config:/config
66
+ networks:
67
+ - proxy_network
68
+
69
+ networks:
70
+ proxy_network:
71
+ name: proxy_network
data/cmd/new.rb CHANGED
@@ -3,9 +3,15 @@
3
3
  require 'optparse'
4
4
  require 'fileutils'
5
5
 
6
+ opts = {}
7
+
6
8
  parser = OptionParser.new do |o|
7
9
  o.banner = 'Usage: syntropy new NAME [options]'
8
10
 
11
+ o.on('-y', '--yes', 'Confirm all overwrites') do
12
+ opts[:yes] = true
13
+ end
14
+
9
15
  o.on('-h', '--help', 'Show this help message') do
10
16
  puts o
11
17
  exit
@@ -37,7 +43,7 @@ template_path = File.join(__dir__, 'new/template')
37
43
 
38
44
  begin
39
45
  `mkdir -p "#{path}"`
40
- `cp -r #{template_path}/* "#{path}/"`
46
+ system("cp -rv#{opts[:yes] ? '' : 'i'} #{template_path}/* \"#{path}/\"")
41
47
  puts "Your app is ready in #{path}"
42
48
  rescue => e
43
49
  p e
data/cmd/serve.rb CHANGED
@@ -94,7 +94,9 @@ env[:banner] = false
94
94
  env[:machine] = Syntropy.machine = UM.new
95
95
  env[:logger] = env[:logger] && Syntropy::Logger.new(env[:machine], **env)
96
96
 
97
- require 'syntropy/version'
97
+ require 'uringmachine/fiber_scheduler'
98
+ Fiber.set_scheduler(UM::FiberScheduler.new(env[:machine]))
99
+
98
100
  require 'syntropy/dev_mode' if Syntropy.dev_mode
99
101
 
100
102
  app = Syntropy::App.load(env)
@@ -19,4 +19,4 @@ class CounterAPI < Syntropy::JSONAPI
19
19
  end
20
20
  end
21
21
 
22
- export CounterAPI
22
+ export CounterAPI.new(@env)
@@ -76,6 +76,9 @@ module Syntropy
76
76
  def http_read_body(headers)
77
77
  content_length = headers['content-length']
78
78
  if content_length
79
+ content_length = content_length.to_i
80
+ return nil if content_length == 0
81
+
79
82
  chunk = read(content_length.to_i)
80
83
  return chunk
81
84
  end
@@ -95,6 +98,9 @@ module Syntropy
95
98
  def http_skip_body(headers)
96
99
  content_length = headers['content-length']
97
100
  if content_length
101
+ content_length = content_length.to_i
102
+ return if content_length == 0
103
+
98
104
  return skip(content_length.to_i)
99
105
  end
100
106
 
@@ -110,6 +116,9 @@ module Syntropy
110
116
  def http_read_body_chunk(headers)
111
117
  content_length = headers['content-length']
112
118
  if content_length
119
+ content_length = content_length.to_i
120
+ return nil if content_length == 0
121
+
113
122
  chunk = read(content_length.to_i)
114
123
  return chunk
115
124
  end
@@ -39,6 +39,7 @@ module Syntropy
39
39
  error: e
40
40
  )
41
41
  ensure
42
+ @io.clear
42
43
  @machine.close_async(@fd)
43
44
  end
44
45
 
@@ -25,6 +25,11 @@ module Syntropy
25
25
  ':status' => status,
26
26
  'Content-Type' => 'application/json'
27
27
  )
28
+ rescue => e
29
+ puts '*' * 40
30
+ p e
31
+ p e.backtrace.join
32
+ puts
28
33
  end
29
34
 
30
35
  private
@@ -167,8 +167,6 @@ module Syntropy
167
167
  raise Syntropy::Error, "No export found in #{fn}" if raise_on_missing
168
168
  when String
169
169
  ->(req) { req.respond(export_value) }
170
- when Class
171
- export_value.new(@env)
172
170
  else
173
171
  export_value
174
172
  end
@@ -81,10 +81,10 @@ module Syntropy
81
81
  end
82
82
 
83
83
  def set_schema_version(db, version)
84
- db.execute <<~SQL, v: version
84
+ db.execute <<~SQL, version
85
85
  insert into __syntropy_schema__ (k, v)
86
- values ('version', :v)
87
- on conflict(k) do update set v = :v
86
+ values ('version', $1)
87
+ on conflict(k) do update set v = $1
88
88
  SQL
89
89
  end
90
90
  end
data/lib/syntropy/test.rb CHANGED
@@ -119,7 +119,7 @@ module Syntropy
119
119
  @test_harness = Syntropy::TestHarness.new(@app)
120
120
 
121
121
  @db = load_module('/_lib/storage', raise_on_missing: false)
122
- @db&.migrate!
122
+ @db&.migrate! if @db.respond_to?(:migrate!)
123
123
  end
124
124
 
125
125
  # Cleans up a test instance.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Syntropy
4
- VERSION = '0.36.0'
4
+ VERSION = '0.37.0'
5
5
  end
data/lib/syntropy.rb CHANGED
@@ -83,9 +83,9 @@ module Syntropy
83
83
  logger: nil
84
84
  )
85
85
  loader = ModuleLoader.new(loader_env)
86
- config = loader.load(env[:mode])
87
-
88
- env[:config] = config
86
+ if (config = loader.load(env[:mode], raise_on_missing: false))
87
+ env[:config] = config
88
+ end
89
89
  end
90
90
 
91
91
  private
@@ -12,4 +12,4 @@ class Klass
12
12
  end
13
13
  end
14
14
 
15
- export Klass
15
+ export Klass.new(@env)
@@ -21,4 +21,4 @@ class API < Syntropy::JSONAPI
21
21
  end
22
22
  end
23
23
 
24
- export API
24
+ export API.new(@env)
@@ -248,3 +248,57 @@ class HTTPProtocolResponseTest < HTTPProtocolTest
248
248
  assert_raises(Syntropy::ProtocolError) { @io.http_read_response_headers }
249
249
  end
250
250
  end
251
+
252
+ class PipelineTest < HTTPProtocolTest
253
+ def test_pipeline_post_zero_content_length
254
+ msg = "POST /counter_api?q=incr HTTP/1.1\r\n" +
255
+ "Host: localhost:1234\r\n" +
256
+ "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0\r\n" +
257
+ "Accept: */*\r\n" +
258
+ "Accept-Language: en-US,en;q=0.9\r\n" +
259
+ "Accept-Encoding: gzip, deflate, br, zstd\r\n" +
260
+ "Referer: http://localhost:1234/counter\r\n" +
261
+ "Origin: http://localhost:1234\r\n" +
262
+ "Connection: keep-alive\r\n" +
263
+ "Sec-Fetch-Dest: empty\r\n" +
264
+ "Sec-Fetch-Mode: cors\r\n" +
265
+ "Sec-Fetch-Site: same-origin\r\n" +
266
+ "Priority: u=0\r\nPragma: no-cache\r\n" +
267
+ "Cache-Control: no-cache\r\n" +
268
+ "Content-Length: 0\r\n\r\n"
269
+
270
+ write(msg * 3)
271
+ 3.times {
272
+ h = @io.http_read_request_headers
273
+ assert_equal '*/*', h['accept']
274
+ assert_nil @io.http_read_body_chunk(h)
275
+ }
276
+ end
277
+
278
+ def test_pipeline_post_with_body
279
+ msg = "POST /counter_api?q=incr HTTP/1.1\r\n" +
280
+ "Host: localhost:1234\r\n" +
281
+ "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0\r\n" +
282
+ "Accept: */*\r\n" +
283
+ "Accept-Language: en-US,en;q=0.9\r\n" +
284
+ "Accept-Encoding: gzip, deflate, br, zstd\r\n" +
285
+ "Referer: http://localhost:1234/counter\r\n" +
286
+ "Origin: http://localhost:1234\r\n" +
287
+ "Connection: keep-alive\r\n" +
288
+ "Sec-Fetch-Dest: empty\r\n" +
289
+ "Sec-Fetch-Mode: cors\r\n" +
290
+ "Sec-Fetch-Site: same-origin\r\n" +
291
+ "Priority: u=0\r\nPragma: no-cache\r\n" +
292
+ "Cache-Control: no-cache\r\n" +
293
+ "Content-Length: 3\r\n\r\n" +
294
+ "abc"
295
+
296
+ write(msg * 3)
297
+ 3.times {
298
+ h = @io.http_read_request_headers
299
+ assert_equal '*/*', h['accept']
300
+ assert_equal 'abc', @io.http_read_body_chunk(h)
301
+ }
302
+
303
+ end
304
+ end
@@ -25,8 +25,7 @@ class ModuleTest < Minitest::Test
25
25
 
26
26
  mod = @loader.load('_lib/klass')
27
27
  assert_equal :bar, mod.foo
28
- @env[:baz] += 1
29
- assert_equal 43, mod.bar
28
+ assert_equal 42, mod.bar
30
29
  end
31
30
 
32
31
  def test_import_paths
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syntropy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.36.0
4
+ version: 0.37.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
@@ -181,6 +181,7 @@ files:
181
181
  - cmd/new/template/app/assets/style.css
182
182
  - cmd/new/template/app/index.rb
183
183
  - cmd/new/template/app/test.rb
184
+ - cmd/new/template/config/Caddyfile
184
185
  - cmd/new/template/config/development.rb
185
186
  - cmd/new/template/config/production.rb
186
187
  - cmd/new/template/config/test.rb