itsi 0.1.14 → 0.1.18

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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +124 -109
  3. data/Cargo.toml +6 -0
  4. data/crates/itsi_error/Cargo.toml +1 -0
  5. data/crates/itsi_error/src/lib.rs +100 -10
  6. data/crates/itsi_scheduler/src/itsi_scheduler.rs +1 -1
  7. data/crates/itsi_server/Cargo.toml +8 -10
  8. data/crates/itsi_server/src/default_responses/html/401.html +68 -0
  9. data/crates/itsi_server/src/default_responses/html/403.html +68 -0
  10. data/crates/itsi_server/src/default_responses/html/404.html +68 -0
  11. data/crates/itsi_server/src/default_responses/html/413.html +71 -0
  12. data/crates/itsi_server/src/default_responses/html/429.html +68 -0
  13. data/crates/itsi_server/src/default_responses/html/500.html +71 -0
  14. data/crates/itsi_server/src/default_responses/html/502.html +71 -0
  15. data/crates/itsi_server/src/default_responses/html/503.html +68 -0
  16. data/crates/itsi_server/src/default_responses/html/504.html +69 -0
  17. data/crates/itsi_server/src/default_responses/html/index.html +238 -0
  18. data/crates/itsi_server/src/default_responses/json/401.json +6 -0
  19. data/crates/itsi_server/src/default_responses/json/403.json +6 -0
  20. data/crates/itsi_server/src/default_responses/json/404.json +6 -0
  21. data/crates/itsi_server/src/default_responses/json/413.json +6 -0
  22. data/crates/itsi_server/src/default_responses/json/429.json +6 -0
  23. data/crates/itsi_server/src/default_responses/json/500.json +6 -0
  24. data/crates/itsi_server/src/default_responses/json/502.json +6 -0
  25. data/crates/itsi_server/src/default_responses/json/503.json +6 -0
  26. data/crates/itsi_server/src/default_responses/json/504.json +6 -0
  27. data/crates/itsi_server/src/default_responses/mod.rs +11 -0
  28. data/crates/itsi_server/src/lib.rs +58 -26
  29. data/crates/itsi_server/src/prelude.rs +2 -0
  30. data/crates/itsi_server/src/ruby_types/README.md +21 -0
  31. data/crates/itsi_server/src/ruby_types/itsi_body_proxy/mod.rs +8 -6
  32. data/crates/itsi_server/src/ruby_types/itsi_grpc_call.rs +344 -0
  33. data/crates/itsi_server/src/ruby_types/{itsi_grpc_stream → itsi_grpc_response_stream}/mod.rs +121 -73
  34. data/crates/itsi_server/src/ruby_types/itsi_http_request.rs +103 -40
  35. data/crates/itsi_server/src/ruby_types/itsi_http_response.rs +8 -5
  36. data/crates/itsi_server/src/ruby_types/itsi_server/file_watcher.rs +4 -4
  37. data/crates/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +37 -17
  38. data/crates/itsi_server/src/ruby_types/itsi_server.rs +4 -3
  39. data/crates/itsi_server/src/ruby_types/mod.rs +6 -13
  40. data/crates/itsi_server/src/server/{bind.rs → binds/bind.rs} +23 -4
  41. data/crates/itsi_server/src/server/{listener.rs → binds/listener.rs} +24 -10
  42. data/crates/itsi_server/src/server/binds/mod.rs +4 -0
  43. data/crates/itsi_server/src/server/{tls.rs → binds/tls.rs} +9 -4
  44. data/crates/itsi_server/src/server/http_message_types.rs +97 -0
  45. data/crates/itsi_server/src/server/io_stream.rs +2 -1
  46. data/crates/itsi_server/src/server/middleware_stack/middleware.rs +28 -16
  47. data/crates/itsi_server/src/server/middleware_stack/middlewares/allow_list.rs +17 -8
  48. data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_api_key.rs +47 -18
  49. data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_basic.rs +13 -9
  50. data/crates/itsi_server/src/server/middleware_stack/middlewares/auth_jwt.rs +50 -29
  51. data/crates/itsi_server/src/server/middleware_stack/middlewares/cache_control.rs +5 -2
  52. data/crates/itsi_server/src/server/middleware_stack/middlewares/compression.rs +37 -48
  53. data/crates/itsi_server/src/server/middleware_stack/middlewares/cors.rs +25 -20
  54. data/crates/itsi_server/src/server/middleware_stack/middlewares/deny_list.rs +14 -7
  55. data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response/default_responses.rs +190 -0
  56. data/crates/itsi_server/src/server/middleware_stack/middlewares/error_response.rs +125 -95
  57. data/crates/itsi_server/src/server/middleware_stack/middlewares/etag.rs +9 -5
  58. data/crates/itsi_server/src/server/middleware_stack/middlewares/header_interpretation.rs +1 -4
  59. data/crates/itsi_server/src/server/middleware_stack/middlewares/intrusion_protection.rs +25 -19
  60. data/crates/itsi_server/src/server/middleware_stack/middlewares/log_requests.rs +4 -4
  61. data/crates/itsi_server/src/server/middleware_stack/middlewares/max_body.rs +47 -0
  62. data/crates/itsi_server/src/server/middleware_stack/middlewares/mod.rs +9 -4
  63. data/crates/itsi_server/src/server/middleware_stack/middlewares/proxy.rs +260 -62
  64. data/crates/itsi_server/src/server/middleware_stack/middlewares/rate_limit.rs +29 -22
  65. data/crates/itsi_server/src/server/middleware_stack/middlewares/redirect.rs +6 -6
  66. data/crates/itsi_server/src/server/middleware_stack/middlewares/request_headers.rs +6 -5
  67. data/crates/itsi_server/src/server/middleware_stack/middlewares/response_headers.rs +4 -2
  68. data/crates/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +51 -18
  69. data/crates/itsi_server/src/server/middleware_stack/middlewares/static_assets.rs +31 -13
  70. data/crates/itsi_server/src/server/middleware_stack/middlewares/static_response.rs +55 -0
  71. data/crates/itsi_server/src/server/middleware_stack/middlewares/string_rewrite.rs +13 -8
  72. data/crates/itsi_server/src/server/middleware_stack/mod.rs +101 -69
  73. data/crates/itsi_server/src/server/mod.rs +3 -9
  74. data/crates/itsi_server/src/server/process_worker.rs +21 -3
  75. data/crates/itsi_server/src/server/request_job.rs +2 -2
  76. data/crates/itsi_server/src/server/serve_strategy/cluster_mode.rs +8 -3
  77. data/crates/itsi_server/src/server/serve_strategy/single_mode.rs +26 -26
  78. data/crates/itsi_server/src/server/signal.rs +24 -41
  79. data/crates/itsi_server/src/server/size_limited_incoming.rs +101 -0
  80. data/crates/itsi_server/src/server/thread_worker.rs +59 -28
  81. data/crates/itsi_server/src/services/itsi_http_service.rs +239 -0
  82. data/crates/itsi_server/src/services/mime_types.rs +1416 -0
  83. data/crates/itsi_server/src/services/mod.rs +6 -0
  84. data/crates/itsi_server/src/services/password_hasher.rs +83 -0
  85. data/crates/itsi_server/src/{server → services}/rate_limiter.rs +35 -31
  86. data/crates/itsi_server/src/{server → services}/static_file_server.rs +521 -181
  87. data/crates/itsi_tracing/src/lib.rs +145 -55
  88. data/{Itsi.rb → foo/Itsi.rb} +6 -9
  89. data/gems/scheduler/Cargo.lock +7 -0
  90. data/gems/scheduler/lib/itsi/scheduler/version.rb +1 -1
  91. data/gems/scheduler/test/helpers/test_helper.rb +0 -1
  92. data/gems/scheduler/test/test_address_resolve.rb +0 -1
  93. data/gems/scheduler/test/test_network_io.rb +1 -1
  94. data/gems/scheduler/test/test_process_wait.rb +0 -1
  95. data/gems/server/Cargo.lock +124 -109
  96. data/gems/server/exe/itsi +65 -19
  97. data/gems/server/itsi-server.gemspec +4 -3
  98. data/gems/server/lib/itsi/http_request/response_status_shortcodes.rb +74 -0
  99. data/gems/server/lib/itsi/http_request.rb +116 -17
  100. data/gems/server/lib/itsi/http_response.rb +2 -0
  101. data/gems/server/lib/itsi/passfile.rb +109 -0
  102. data/gems/server/lib/itsi/server/config/dsl.rb +160 -101
  103. data/gems/server/lib/itsi/server/config.rb +58 -23
  104. data/gems/server/lib/itsi/server/default_app/default_app.rb +25 -29
  105. data/gems/server/lib/itsi/server/default_app/index.html +113 -89
  106. data/gems/server/lib/itsi/server/{Itsi.rb → default_config/Itsi-rackup.rb} +1 -1
  107. data/gems/server/lib/itsi/server/default_config/Itsi.rb +107 -0
  108. data/gems/server/lib/itsi/server/grpc/grpc_call.rb +246 -0
  109. data/gems/server/lib/itsi/server/grpc/grpc_interface.rb +100 -0
  110. data/gems/server/lib/itsi/server/grpc/reflection/v1/reflection_pb.rb +26 -0
  111. data/gems/server/lib/itsi/server/grpc/reflection/v1/reflection_services_pb.rb +122 -0
  112. data/gems/server/lib/itsi/server/route_tester.rb +107 -0
  113. data/gems/server/lib/itsi/server/typed_handlers/param_parser.rb +200 -0
  114. data/gems/server/lib/itsi/server/typed_handlers/source_parser.rb +55 -0
  115. data/gems/server/lib/itsi/server/typed_handlers.rb +17 -0
  116. data/gems/server/lib/itsi/server/version.rb +1 -1
  117. data/gems/server/lib/itsi/server.rb +82 -12
  118. data/gems/server/lib/ruby_lsp/itsi/addon.rb +111 -0
  119. data/gems/server/lib/shell_completions/completions.rb +26 -0
  120. data/gems/server/test/helpers/test_helper.rb +2 -1
  121. data/lib/itsi/version.rb +1 -1
  122. data/sandbox/README.md +5 -0
  123. data/sandbox/itsi_file/Gemfile +4 -2
  124. data/sandbox/itsi_file/Gemfile.lock +48 -6
  125. data/sandbox/itsi_file/Itsi.rb +326 -129
  126. data/sandbox/itsi_file/call.json +1 -0
  127. data/sandbox/itsi_file/echo_client/Gemfile +10 -0
  128. data/sandbox/itsi_file/echo_client/Gemfile.lock +27 -0
  129. data/sandbox/itsi_file/echo_client/README.md +95 -0
  130. data/sandbox/itsi_file/echo_client/echo_client.rb +164 -0
  131. data/sandbox/itsi_file/echo_client/gen_proto.sh +17 -0
  132. data/sandbox/itsi_file/echo_client/lib/echo_pb.rb +16 -0
  133. data/sandbox/itsi_file/echo_client/lib/echo_services_pb.rb +29 -0
  134. data/sandbox/itsi_file/echo_client/run_client.rb +64 -0
  135. data/sandbox/itsi_file/echo_client/test_compressions.sh +20 -0
  136. data/sandbox/itsi_file/echo_service_nonitsi/Gemfile +10 -0
  137. data/sandbox/itsi_file/echo_service_nonitsi/Gemfile.lock +79 -0
  138. data/sandbox/itsi_file/echo_service_nonitsi/echo.proto +26 -0
  139. data/sandbox/itsi_file/echo_service_nonitsi/echo_pb.rb +16 -0
  140. data/sandbox/itsi_file/echo_service_nonitsi/echo_services_pb.rb +29 -0
  141. data/sandbox/itsi_file/echo_service_nonitsi/server.rb +52 -0
  142. data/sandbox/itsi_sandbox_async/config.ru +0 -1
  143. data/sandbox/itsi_sandbox_rack/Gemfile.lock +2 -2
  144. data/sandbox/itsi_sandbox_rails/Gemfile +2 -2
  145. data/sandbox/itsi_sandbox_rails/Gemfile.lock +76 -2
  146. data/sandbox/itsi_sandbox_rails/app/controllers/home_controller.rb +15 -0
  147. data/sandbox/itsi_sandbox_rails/config/environments/development.rb +1 -0
  148. data/sandbox/itsi_sandbox_rails/config/environments/production.rb +1 -0
  149. data/sandbox/itsi_sandbox_rails/config/routes.rb +2 -0
  150. data/sandbox/itsi_sinatra/app.rb +0 -1
  151. data/sandbox/static_files/.env +1 -0
  152. data/sandbox/static_files/404.html +25 -0
  153. data/sandbox/static_files/_DSC0102.NEF.jpg +0 -0
  154. data/sandbox/static_files/about.html +68 -0
  155. data/sandbox/static_files/tiny.html +1 -0
  156. data/sandbox/static_files/writebook.zip +0 -0
  157. data/tasks.txt +28 -33
  158. metadata +87 -26
  159. data/crates/itsi_error/src/from.rs +0 -68
  160. data/crates/itsi_server/src/ruby_types/itsi_grpc_request.rs +0 -147
  161. data/crates/itsi_server/src/ruby_types/itsi_grpc_response.rs +0 -19
  162. data/crates/itsi_server/src/server/itsi_service.rs +0 -172
  163. data/crates/itsi_server/src/server/middleware_stack/middlewares/grpc_service.rs +0 -72
  164. data/crates/itsi_server/src/server/types.rs +0 -43
  165. data/gems/server/lib/itsi/server/grpc_interface.rb +0 -213
  166. data/sandbox/itsi_file/public/assets/index.html +0 -1
  167. /data/crates/itsi_server/src/server/{bind_protocol.rs → binds/bind_protocol.rs} +0 -0
  168. /data/crates/itsi_server/src/server/{tls → binds/tls}/locked_dir_cache.rs +0 -0
  169. /data/crates/itsi_server/src/{server → services}/cache_store.rs +0 -0
@@ -1,150 +1,359 @@
1
1
  workers 1
2
2
  threads 1
3
+ fiber_scheduler true
4
+
5
+ scheduler_threads 1
6
+
7
+ request_timeout 2
3
8
 
4
9
  preload false
5
10
 
6
11
  bind 'http://localhost:3000'
7
- bind 'http://0.0.0.0:8000'
8
12
 
9
- shutdown_timeout 3
10
- multithreaded_reactor true
13
+ shutdown_timeout 10
11
14
 
12
- watch '*.rb', [%w[bundle exec itsi restart]]
15
+ auto_reload_config!
13
16
 
14
- def user_serve(request)
15
- response = request.response
16
- response.status = 200
17
- response.add_header('Content-Type', 'text/plain')
18
- response << 'Hello, user!'
19
- response.close
20
- end
17
+ log_format :plain
21
18
 
22
- def user_create(request)
23
- response = request.response
24
- response.status = 201
25
- response.add_header('Content-Type', 'text/plain')
26
- response << 'User created!'
27
- response.close
19
+ get "/root" do |req|
20
+ req.ok "Got from root"
28
21
  end
29
22
 
30
- location "/etag-test" do
31
- etag type: 'strong', algorithm: 'sha256', min_body_size: 0
32
23
 
33
- get '/' do |req|
34
- # Fixed content that will generate the same ETag each time
35
- content = "This is a fixed response that will always have the same ETag."
36
-
37
- # Add a timestamp as a comment to see the response is fresh
38
- resp = req.response
39
- req.respond("Content: #{content}.# Generated at: #{Time.now}")
24
+ location "app" do
25
+ location "/users" do
26
+ get "/:id" do |req|
27
+ req.ok "Got user #{req.url_params[:id]}"
28
+ end
40
29
  end
30
+
31
+ run(->(env){
32
+ [200, {}, ["Fallback Rack app"]]
33
+ })
41
34
  end
42
35
 
43
36
 
44
- location "/cached-with-etag" do
45
- # Set up caching parameters
46
- cache_control max_age: 3600, public: true, vary: ['Accept-Encoding']
37
+ static_assets \
38
+ relative_path: true,
39
+ root_dir: "public",
40
+ not_found_behavior: "fallthrough",
41
+ try_html_extension: true,
42
+ auto_index: true,
43
+ max_file_size_in_memory: 1024 * 1024,
44
+ max_files_in_memory: 1000,
45
+ file_check_interval: 5,
46
+ serve_hidden_files: false
47
+
48
+ run(->(env){
49
+ [200, {}, ["Final Fallback Rack App"]]
50
+ })
51
+ # location %q{/foo/:id(\d+)/test/:name/bla} do
52
+ # get "/" do |req|
53
+ # req.respond json: req.url_params
54
+ # end
55
+ # end
47
56
 
48
- # Add ETags for cache validation
49
- etag type: 'weak', algorithm: 'md5', min_body_size: 0
57
+ # location "blocking" do
58
+ # run ->(env){
59
+ # sleep 0.25
60
+ # [200, {}, [:ok]]
61
+ # }
62
+ # end
50
63
 
51
- get '/' do |req|
52
- req.respond("This response will have both cache headers and an ETag.")
53
- end
54
- end
64
+ # location "nonblocking" do
65
+ # run(->(env){
66
+ # sleep 0.25
67
+ # [200, {}, [:ok]]
68
+ # }, nonblocking: true)
69
+ # end
55
70
 
71
+ # require_relative 'echo_service/echo_service_impl'
56
72
 
57
- location "/" do
58
- intrusion_protection banned_time_seconds: 5, banned_url_patterns: [/\/admin/, /\/secret/], store_config: { redis: { connection_url: 'redis://localhost:6379/0' } }, error_response: { plaintext: 'no way', code: 403, default: 'plaintext' }
73
+ # grpc EchoServiceImpl.new, nonblocking: false do
74
+ # end
59
75
 
60
- # Example for API endpoints with ETags
61
- location "/api" do
62
- etag type: 'weak', algorithm: 'md5', min_body_size: 0
63
- get '/users/id' do |req|
64
- user_id = 5
65
- req.respond("User data for #{user_id}")
66
- end
67
- end
76
+ # location "foo" do
77
+ # static_response code: 200, headers: [["Headers","Foo"]], body: "This is my body"
78
+ # end
68
79
 
69
- location "/hey" do
70
- location "/world*" do
71
- request_headers additions: {"X-Custom-Req" => ["Foo", "Bar", "Baz"]}, removals: [""]
72
- response_headers additions: {"X-Custom-Resp" => ["Foo", "Bar"]}, removals: []
73
- rate_limit requests: 5, seconds: 30, key: 'address', store_config: { redis: { connection_url: 'redis://localhost:6379/0' } }, error_response: { plaintext: 'no way', code: 429, default: 'plaintext' }
74
- post '/' do |req|
75
- puts "I'm a post!"
76
- req.respond("Post successful!")
77
- end
78
- get '/' do |req|
79
- puts "Headers are", "#{req["foo"]}"
80
- req.respond("Is this still fast?")
81
- end
82
- end
83
- end
80
+ # location "/public" do
81
+ # static_assets relative_path: true, root_dir: "public", not_found_behavior: {error: 'not_found'},
82
+ # try_html_extension: true, auto_index: true, max_file_size_in_memory: 1024 * 1024,
83
+ # # Maximum number of files to keep in memory cache
84
+ # max_files_in_memory: 1000,
85
+ # # Check for file modifications every 5 seconds
86
+ # file_check_interval: 5,
87
+ # serve_hidden_files: false
88
+ # end
84
89
 
85
- # Add a new location with cache_control middleware
86
- location "/cached" do
87
- cache_control max_age: 3600, public: true, vary: ['Accept-Encoding']
88
- get '/' do |req|
89
- req.respond("This content is cached for 1 hour.")
90
- end
91
- end
90
+ # require 'debug'
91
+ # require 'date'
92
92
 
93
- location "/etag" do
94
- etag enabled: true, etag_type: 'strong', hash_algorithm: 'sha256', min_body_size: 0
95
- get '/' do |req|
96
- # Create a response large enough to be worth ETagging
97
- content = "This content will have ETags. " * 20
98
- req.respond(content)
99
- end
100
- end
101
- end
93
+ # UserSchema = {
94
+ # id: Integer,
95
+ # name: String
96
+ # }
102
97
 
103
98
 
99
+ # ResultSchema = {
100
+ # status_code: Integer,
101
+ # user: UserSchema
102
+ # }
104
103
 
105
- # For an assets example
106
- location "/assets" do
107
- cache_control max_age: 604800, s_max_age: 2592000,
108
- stale_while_revalidate: 86400,
109
- public: true, immutable: true
104
+ # module UserController
110
105
 
111
- get "/:file" do |req|
112
- file_name = req.params["file"]
113
- req.respond("Pretending to serve #{file_name} with long-lived caching")
114
- end
115
- end
106
+ # def self.create(req)
107
+ # req.ok \
108
+ # json: {
109
+ # status: :created,
110
+ # user: user
111
+ # }
112
+ # end
113
+
114
+ # def self.create(req, params: UserSchema, return_type: ResultSchema)
115
+ # req.ok \
116
+ # json: {
117
+ # status_code: 5,
118
+ # user: params
119
+ # },
120
+ # as: return_type
121
+ # end
122
+
123
+ # def self.create_no_type(req, params)
124
+ # req.ok json: "foo"
125
+ # end
126
+
127
+ # end
128
+
129
+ # controller UserController
130
+ # post "/", :create
131
+ # get "/" do |req, return_type: ResultSchema|
132
+ # req.ok as: return_type, json: {
133
+ # status_code: 5,
134
+ # user: {
135
+ # id: 3,
136
+ # name: "Foo Bar"
137
+ # }
138
+ # }
139
+ # end
140
+
141
+ # post "/nt", :create_no_type
142
+
143
+ # location "/rack" do
144
+ # run(->(env){
145
+ # sleep 5
146
+ # [200, {}, [:ok]]
147
+ # })
148
+ # end
149
+ # location '*' do
150
+ # proxy to: 'http://{host}{path_and_query}', backends: ['127.0.0.1:8082','127.0.0.1:8080', '127.0.0.1:8081'], headers: {}, verify_ssl: false, timeout: 1, tls_sni: false, backend_priority: 'round_robin'
151
+ # end
116
152
 
117
- # location '' do
118
-
119
- # location '/spa' do
120
- # # Serve static files from the "public/assets" directory
121
- # log_requests before: { format: "REQUEST_ID={request_id} METHOD={method} PATH={path} QUERY={query} HOST={host} PORT={port} START_TIME={start_time}" , level: 'INFO'}, after: { format: "REQUEST_ID={request_id} RESPONSE_TIME={response_time}", level: 'INFO' }
122
- # allow_list allowed_patterns: [/127.*/], error_response: { plaintext: 'no way', code: 401, default: 'plaintext' }
123
- # static_assets\
124
- # relative_path: true,
125
- # root_dir: 'spa/dist',
126
- # # # Only allow certain file extensions
127
- # # allowed_extensions: %w[css js jpg jpeg png gif svg ico woff woff2 ttf
128
- # # otf html],
129
- # # Return a 404 error if file is not found
130
- # not_found_behavior: { index: 'index.html' },
131
- # # Enable auto-indexing of directories
132
- # auto_index: true,
133
- # # Try adding .html extension to extensionless URLs
134
- # try_html_extension: true,
135
- # # Files under this size are cached in memory
136
- # max_file_size_in_memory: 1024 * 1024, # 1MB
137
- # # Maximum number of files to keep in memory cache
138
- # max_files_in_memory: 1000,
139
- # # Check for file modifications every 5 seconds
140
- # file_check_interval: 5,
141
- # # Add custom headers to all responses
142
- # headers: {
143
- # 'Cache-Control' => 'public, max-age=86400',
144
- # 'X-Content-Type-Options' => 'nosniff'
145
- # }
146
- # compress algorithms: ['zstd'], min_size: 0, compress_streams: true, mime_types: ['all'], level: 'fastest'
147
- # end
153
+ # # require_relative 'echo_service/echo_service_impl'
154
+
155
+ # # grpc EchoServiceImpl.new do
156
+
157
+ # # end
158
+
159
+ # location '/' do
160
+ # # compress algorithms: ['zstd'], min_size: 0, compress_streams: true, mime_types: ['all'], level: 'fastest'
161
+ # max_body max_size: 5
162
+ # get '/' do |req|
163
+ # req.respond('Foo')
164
+ # end
165
+ # etag type: 'weak', algorithm: 'md5', min_body_size: 0
166
+ # post '/' do |req|
167
+ # puts "Post to me!"
168
+ # req.respond('Foo')
169
+ # end
170
+ # end
171
+
172
+ # location '/' do
173
+ # end
174
+ # rate_limit requests: 5, seconds: 5, key: 'address', store_config: { redis: { connection_url: 'redis://localhost:6379/0' } }, error_response: { plaintext: 'no way', code: 429, default: 'plaintext' }
175
+
176
+ #
177
+
178
+ # static_assets\
179
+ # relative_path: true,
180
+ # root_dir: 'spa/dist',
181
+ # # # Only allow certain file extensions
182
+ # # allowed_extensions: %w[css js jpg jpeg png gif svg ico woff woff2 ttf
183
+ # # otf html],
184
+ # # Return a 404 error if file is not found
185
+ # not_found_behavior: { index: 'index.html' },
186
+ # # Enable auto-indexing of directories
187
+ # auto_index: true,
188
+ # # Try adding .html extension to extensionless URLs
189
+ # try_html_extension: true,
190
+ # # Files under this size are cached in memory
191
+ # max_file_size_in_memory: 1024 * 1024, # 1MB
192
+ # # Maximum number of files to keep in memory cache
193
+ # max_files_in_memory: 1000,
194
+ # # Check for file modifications every 5 seconds
195
+ # file_check_interval: 5,
196
+ # # Add custom headers to all responses
197
+ # headers: {
198
+ # 'Cache-Control' => 'public, max-age=86400',
199
+ # 'X-Content-Type-Options' => 'nosniff'
200
+ # }
201
+
202
+ # run(lambda { |env|
203
+ # [200, {}, ["I'm a fallback"]]
204
+ # })
205
+
206
+ # def user_serve(request)
207
+ # response = request.response
208
+ # response.status = 200
209
+ # response.add_header('Content-Type', 'text/plain')
210
+ # response << 'Hello, user!'
211
+ # response.close
212
+ # end
213
+
214
+ # def user_create(request)
215
+ # response = request.response
216
+ # response.status = 201
217
+ # response.add_header('Content-Type', 'text/plain')
218
+ # response << 'User created!'
219
+ # response.close
220
+ # end
221
+
222
+ # location "/etag-test" do
223
+ # etag type: 'strong', algorithm: 'sha256', min_body_size: 0
224
+
225
+ # get '/' do |req|
226
+ # # Fixed content that will generate the same ETag each time
227
+ # content = "This is a fixed response that will always have the same ETag."
228
+
229
+ # # Add a timestamp as a comment to see the response is fresh
230
+ # resp = req.response
231
+ # req.respond("Content: #{content}.# Generated at: #{Time.now}")
232
+ # end
233
+ # end
234
+
235
+ # location "/cached-with-etag" do
236
+ # # Set up caching parameters
237
+ # cache_control max_age: 3600, public: true, vary: ['Accept-Encoding']
238
+
239
+ # # Add ETags for cache validation
240
+ # etag type: 'weak', algorithm: 'md5', min_body_size: 0
241
+
242
+ # get '/' do |req|
243
+ # req.respond("This response will have both cache headers and an ETag.")
244
+ # end
245
+ # end
246
+
247
+ # location "/" do
248
+ # intrusion_protection banned_time_seconds: 5, banned_url_patterns: [/\/admin/, /\/secret/], store_config: { redis: { connection_url: 'redis://localhost:6379/0' } }, error_response: { plaintext: 'no way', code: 403, default: 'plaintext' }
249
+
250
+ # # Example for API endpoints with ETags
251
+ # location "/api" do
252
+ # etag type: 'weak', algorithm: 'md5', min_body_size: 0
253
+ # get '/users/id' do |req|
254
+ # user_id = 5
255
+ # req.respond("User data for #{user_id}")
256
+ # end
257
+ # end
258
+
259
+ # location "/hey" do
260
+ # location "/world*" do
261
+ # request_headers additions: {"X-Custom-Req" => ["Foo", "Bar", "Baz"]}, removals: [""]
262
+ # response_headers additions: {"X-Custom-Resp" => ["Foo", "Bar"]}, removals: []
263
+ # rate_limit requests: 5, seconds: 30, key: 'address', store_config: { redis: { connection_url: 'redis://localhost:6379/0' } }, error_response: { plaintext: 'no way', code: 429, default: 'plaintext' }
264
+ # post '/' do |req|
265
+ # puts "I'm a post!"
266
+ # req.respond("Post successful!")
267
+ # end
268
+ # get '/' do |req|
269
+ # req.respond("Is this still fast?")
270
+ # end
271
+ # end
272
+ # end
273
+
274
+ # # Add a new location with cache_control middleware
275
+ # location "/cached" do
276
+ # cache_control max_age: 3600, public: true, vary: ['Accept-Encoding']
277
+ # get '/' do |req|
278
+ # req.respond("This content is cached for 1 hour.")
279
+ # end
280
+ # end
281
+
282
+ # location "/etag" do
283
+ # etag enabled: true, etag_type: 'strong', hash_algorithm: 'sha256', min_body_size: 0
284
+ # get '/' do |req|
285
+ # # Create a response large enough to be worth ETagging
286
+ # content = "This content will have ETags. " * 20
287
+ # req.respond(content)
288
+ # end
289
+ # end
290
+
291
+ # location "/jwt" do
292
+ # auth_jwt token_source: { header: { name: 'Authorization', prefix: 'Bearer ' } },
293
+ # verifiers: {
294
+ # 'hs256' => ["1V6nZzztO/F6Y3XAYXJ1I37AAXCJ/V7EVHJoVnD8lr4="],
295
+ # 'rs256' => [%Q{
296
+ # -----BEGIN PUBLIC KEY-----
297
+ # MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs8XK+wP7nnKkOGXP6+il
298
+ # +JDQMFRV8zoVg1BYYapGla6AzR0YCV6xq28lNB1FE8C6xhCEKYiOlnkXZwj+a4UW
299
+ # q4Bex/U9qvfza0zGxHlTnQT9T+vjwXNsWYuefYC4EAZjEd6bGeP23De128QfhlLc
300
+ # oQpcfW32/e8KjSYtn8rtWuKV6Anqf92o9zEbVn8y2OZwZbiW0LbfDnimazqP5ATy
301
+ # SI6a5kQorBeG9cU4WW93ctuT5nkyAbBaEpiejWIpjbMkofD5fj2pUgg7H/Xvh+eR
302
+ # S9A+//VGfy4A4SFjBooVewhJ04VhDkMnVC29fbVwVuNqm6Z+FZaxI5pqvisxvH8S
303
+ # XwIDAQAB
304
+ # -----END PUBLIC KEY-----
305
+ # }]
306
+ # }, error_response: { code: 401, plaintext: 'Unauthorized', default: 'plaintext' }
307
+ # get "/" do |req|
308
+ # req.respond('Hello, JWT user!')
309
+ # end
310
+ # end
311
+
312
+ # end
313
+
314
+ # # For an assets example
315
+ # location "/assets" do
316
+ # cache_control max_age: 604800, s_max_age: 2592000,
317
+ # stale_while_revalidate: 86400,
318
+ # public: true, immutable: true
319
+
320
+ # get "/:file" do |req|
321
+ # file_name = req.params["file"]
322
+ # req.respond("Pretending to serve #{file_name} with long-lived caching")
323
+ # end
324
+ # end
325
+
326
+ # # location '' do
327
+
328
+ # location '/spa' do
329
+ # # Serve static files from the "public/assets" directory
330
+ # log_requests before: { format: "REQUEST_ID={request_id} METHOD={method} PATH={path} QUERY={query} HOST={host} PORT={port} START_TIME={start_time}" , level: 'INFO'}, after: { format: "REQUEST_ID={request_id} RESPONSE_TIME={response_time}", level: 'INFO' }
331
+ # allow_list allowed_patterns: [/127.*/], error_response: { plaintext: 'no way', code: 401, default: 'plaintext' }
332
+ # static_assets\
333
+ # relative_path: true,
334
+ # root_dir: 'spa/dist',
335
+ # # # Only allow certain file extensions
336
+ # # allowed_extensions: %w[css js jpg jpeg png gif svg ico woff woff2 ttf
337
+ # # otf html],
338
+ # # Return a 404 error if file is not found
339
+ # not_found_behavior: { index: 'index.html' },
340
+ # # Enable auto-indexing of directories
341
+ # auto_index: true,
342
+ # # Try adding .html extension to extensionless URLs
343
+ # try_html_extension: true,
344
+ # # Files under this size are cached in memory
345
+ # max_file_size_in_memory: 1024 * 1024, # 1MB
346
+ # # Maximum number of files to keep in memory cache
347
+ # max_files_in_memory: 1000,
348
+ # # Check for file modifications every 5 seconds
349
+ # file_check_interval: 5,
350
+ # # Add custom headers to all responses
351
+ # headers: {
352
+ # 'Cache-Control' => 'public, max-age=86400',
353
+ # 'X-Content-Type-Options' => 'nosniff'
354
+ # }
355
+ # compress algorithms: ['zstd'], min_size: 0, compress_streams: true, mime_types: ['all'], level: 'fastest'
356
+ # end
148
357
  # end
149
358
 
150
359
  # location "/cors_test" do
@@ -154,17 +363,7 @@ end
154
363
  # end
155
364
  # end
156
365
 
157
- # location "/basic" do
158
- # auth_basic realm: 'My Realm', credential_pairs: { 'user' => 'password' }
159
- # end
160
366
 
161
- # location "/jwt" do
162
- # auth_jwt token_source: { header: { name: 'Authorization', prefix: 'Bearer ' } }, verifiers: {'hs256' => ["1V6nZzztO/F6Y3XAYXJ1I37AAXCJ/V7EVHJoVnD8lr4="]
163
- # }, error_response: { code: 401, plaintext: 'Unauthorized', default: 'plaintext' }
164
- # get "/" do |req|
165
- # req.respond('Hello, JWT user!')
166
- # end
167
- # end
168
367
 
169
368
  # location '/proxy_as_foo_com' do
170
369
  # compress algorithms: ['zstd'], min_size: 0, compress_streams: true, mime_types: ['all'], level: 'fastest'
@@ -229,7 +428,6 @@ end
229
428
  # req.respond("This is a test")
230
429
  # end
231
430
 
232
-
233
431
  # location '*', protocols: :http do
234
432
  # location 'foo' do
235
433
  # redirect to: 'https://{host}:3001{path}'
@@ -272,5 +470,4 @@ end
272
470
 
273
471
  # Example with both caching and ETags
274
472
 
275
-
276
473
  # Simple ETag test endpoint with predictable content
@@ -0,0 +1 @@
1
+ {"message": "Hello, World!"}
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'grpc', '~> 1.50'
4
+ gem 'grpc-tools', '~> 1.50'
5
+
6
+ # Ensure we use the same version of protocol buffers
7
+ gem 'google-protobuf', '~> 3.21'
8
+
9
+ # For pretty printing
10
+ gem 'colorize', '~> 0.8.1'
@@ -0,0 +1,27 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ colorize (0.8.1)
5
+ google-protobuf (3.25.6)
6
+ googleapis-common-protos-types (1.19.0)
7
+ google-protobuf (>= 3.18, < 5.a)
8
+ grpc (1.71.0)
9
+ google-protobuf (>= 3.25, < 5.0)
10
+ googleapis-common-protos-types (~> 1.0)
11
+ grpc (1.71.0-arm64-darwin)
12
+ google-protobuf (>= 3.25, < 5.0)
13
+ googleapis-common-protos-types (~> 1.0)
14
+ grpc-tools (1.71.0)
15
+
16
+ PLATFORMS
17
+ arm64-darwin-23
18
+ ruby
19
+
20
+ DEPENDENCIES
21
+ colorize (~> 0.8.1)
22
+ google-protobuf (~> 3.21)
23
+ grpc (~> 1.50)
24
+ grpc-tools (~> 1.50)
25
+
26
+ BUNDLED WITH
27
+ 2.6.3
@@ -0,0 +1,95 @@
1
+ # Echo gRPC Client
2
+
3
+ A simple Ruby gRPC client for the Echo service.
4
+
5
+ ## Setup
6
+
7
+ 1. Install dependencies:
8
+
9
+ ```bash
10
+ bundle install
11
+ ```
12
+
13
+ 2. Generate Ruby code from the proto file:
14
+
15
+ ```bash
16
+ ./gen_proto.sh
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ Run the client with optional compression:
22
+
23
+ ```bash
24
+ # Run without compression (default)
25
+ ruby echo_client.rb
26
+
27
+ # Run with gzip compression
28
+ ruby echo_client.rb gzip
29
+
30
+ # Run with deflate compression
31
+ ruby echo_client.rb deflate
32
+
33
+ # Show help
34
+ ruby echo_client.rb help
35
+ ```
36
+
37
+ ## Client Details
38
+
39
+ This client implements all four types of gRPC methods:
40
+
41
+ 1. Unary RPC (`echo`)
42
+ 2. Server streaming RPC (`echo_stream`)
43
+ 3. Client streaming RPC (`echo_collect`)
44
+ 4. Bidirectional streaming RPC (`echo_bidirectional`)
45
+
46
+ ## Compression Options
47
+
48
+ You can control compression by modifying the client initialization in `echo_client.rb`:
49
+
50
+ ```ruby
51
+ # No compression
52
+ client = EchoClient.new('localhost', 50051, compression: nil)
53
+
54
+ # Gzip compression
55
+ client = EchoClient.new('localhost', 50051, compression: :gzip)
56
+
57
+ # Deflate compression
58
+ client = EchoClient.new('localhost', 50051, compression: :deflate)
59
+ ```
60
+
61
+ The compression settings are implemented using the gRPC channel arguments, which allows you to test different compression algorithms.
62
+
63
+ ## Troubleshooting
64
+
65
+ If you encounter any issues:
66
+
67
+ 1. Make sure you have installed all dependencies with `bundle install`
68
+ 2. Ensure the protocol buffer code has been generated with `./gen_proto.sh`
69
+ 3. Check that the gRPC server is running on the specified host and port
70
+ 4. If you see connectivity issues, try running with debugging enabled:
71
+
72
+ ```bash
73
+ GRPC_VERBOSITY=DEBUG GRPC_TRACE=all ruby echo_client.rb
74
+ ```
75
+
76
+ ## Example Server
77
+
78
+ The client is designed to work with the EchoService server in the `../echo_service_nonitsi` directory. Make sure the server is running before starting the client.
79
+
80
+ ## Compression Testing
81
+
82
+ To test different compression algorithms with the same message payload:
83
+
84
+ ```bash
85
+ # Test without compression
86
+ ./run_client.rb -m "Large message repeated many times" -c none
87
+
88
+ # Test with gzip compression
89
+ ./run_client.rb -m "Large message repeated many times" -c gzip
90
+
91
+ # Test with deflate compression
92
+ ./run_client.rb -m "Large message repeated many times" -c deflate
93
+ ```
94
+
95
+ This allows you to compare the performance and behavior with different compression settings.