itsi 0.1.14 → 0.1.19

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 +126 -272
  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 +12 -11
  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 +126 -272
  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 +117 -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 +171 -99
  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 +327 -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,360 @@
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_level :debug
18
+ log_format :plain
21
19
 
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
20
+ get "/root" do |req|
21
+ req.ok "Got from root"
28
22
  end
29
23
 
30
- location "/etag-test" do
31
- etag type: 'strong', algorithm: 'sha256', min_body_size: 0
32
24
 
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}")
25
+ location "app" do
26
+ location "/users" do
27
+ get "/:id" do |req|
28
+ req.ok "Got user #{req.url_params[:id]}"
29
+ end
40
30
  end
31
+
32
+ run(->(env){
33
+ [200, {}, ["Fallback Rack app"]]
34
+ })
41
35
  end
42
36
 
43
37
 
44
- location "/cached-with-etag" do
45
- # Set up caching parameters
46
- cache_control max_age: 3600, public: true, vary: ['Accept-Encoding']
38
+ static_assets \
39
+ relative_path: true,
40
+ root_dir: "public",
41
+ not_found_behavior: "fallthrough",
42
+ try_html_extension: true,
43
+ auto_index: true,
44
+ max_file_size_in_memory: 1024 * 1024,
45
+ max_files_in_memory: 1000,
46
+ file_check_interval: 5,
47
+ serve_hidden_files: false
48
+
49
+ run(->(env){
50
+ [200, {}, ["Final Fallback Rack App. SN: #{env['SCRIPT_NAME']} PI: #{env['PATH_INFO']}"]]
51
+ })
52
+ # location %q{/foo/:id(\d+)/test/:name/bla} do
53
+ # get "/" do |req|
54
+ # req.respond json: req.url_params
55
+ # end
56
+ # end
47
57
 
48
- # Add ETags for cache validation
49
- etag type: 'weak', algorithm: 'md5', min_body_size: 0
58
+ # location "blocking" do
59
+ # run ->(env){
60
+ # sleep 0.25
61
+ # [200, {}, [:ok]]
62
+ # }
63
+ # end
50
64
 
51
- get '/' do |req|
52
- req.respond("This response will have both cache headers and an ETag.")
53
- end
54
- end
65
+ # location "nonblocking" do
66
+ # run(->(env){
67
+ # sleep 0.25
68
+ # [200, {}, [:ok]]
69
+ # }, nonblocking: true)
70
+ # end
55
71
 
72
+ # require_relative 'echo_service/echo_service_impl'
56
73
 
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' }
74
+ # grpc EchoServiceImpl.new, nonblocking: false do
75
+ # end
59
76
 
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
77
+ # location "foo" do
78
+ # static_response code: 200, headers: [["Headers","Foo"]], body: "This is my body"
79
+ # end
68
80
 
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
81
+ # location "/public" do
82
+ # static_assets relative_path: true, root_dir: "public", not_found_behavior: {error: 'not_found'},
83
+ # try_html_extension: true, auto_index: true, max_file_size_in_memory: 1024 * 1024,
84
+ # # Maximum number of files to keep in memory cache
85
+ # max_files_in_memory: 1000,
86
+ # # Check for file modifications every 5 seconds
87
+ # file_check_interval: 5,
88
+ # serve_hidden_files: false
89
+ # end
84
90
 
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
91
+ # require 'debug'
92
+ # require 'date'
92
93
 
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
94
+ # UserSchema = {
95
+ # id: Integer,
96
+ # name: String
97
+ # }
102
98
 
103
99
 
100
+ # ResultSchema = {
101
+ # status_code: Integer,
102
+ # user: UserSchema
103
+ # }
104
104
 
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
105
+ # module UserController
110
106
 
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
107
+ # def self.create(req)
108
+ # req.ok \
109
+ # json: {
110
+ # status: :created,
111
+ # user: user
112
+ # }
113
+ # end
114
+
115
+ # def self.create(req, params: UserSchema, return_type: ResultSchema)
116
+ # req.ok \
117
+ # json: {
118
+ # status_code: 5,
119
+ # user: params
120
+ # },
121
+ # as: return_type
122
+ # end
123
+
124
+ # def self.create_no_type(req, params)
125
+ # req.ok json: "foo"
126
+ # end
127
+
128
+ # end
129
+
130
+ # controller UserController
131
+ # post "/", :create
132
+ # get "/" do |req, return_type: ResultSchema|
133
+ # req.ok as: return_type, json: {
134
+ # status_code: 5,
135
+ # user: {
136
+ # id: 3,
137
+ # name: "Foo Bar"
138
+ # }
139
+ # }
140
+ # end
141
+
142
+ # post "/nt", :create_no_type
143
+
144
+ # location "/rack" do
145
+ # run(->(env){
146
+ # sleep 5
147
+ # [200, {}, [:ok]]
148
+ # })
149
+ # end
150
+ # location '*' do
151
+ # 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'
152
+ # end
116
153
 
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
154
+ # # require_relative 'echo_service/echo_service_impl'
155
+
156
+ # # grpc EchoServiceImpl.new do
157
+
158
+ # # end
159
+
160
+ # location '/' do
161
+ # # compress algorithms: ['zstd'], min_size: 0, compress_streams: true, mime_types: ['all'], level: 'fastest'
162
+ # max_body max_size: 5
163
+ # get '/' do |req|
164
+ # req.respond('Foo')
165
+ # end
166
+ # etag type: 'weak', algorithm: 'md5', min_body_size: 0
167
+ # post '/' do |req|
168
+ # puts "Post to me!"
169
+ # req.respond('Foo')
170
+ # end
171
+ # end
172
+
173
+ # location '/' do
174
+ # end
175
+ # 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' }
176
+
177
+ #
178
+
179
+ # static_assets\
180
+ # relative_path: true,
181
+ # root_dir: 'spa/dist',
182
+ # # # Only allow certain file extensions
183
+ # # allowed_extensions: %w[css js jpg jpeg png gif svg ico woff woff2 ttf
184
+ # # otf html],
185
+ # # Return a 404 error if file is not found
186
+ # not_found_behavior: { index: 'index.html' },
187
+ # # Enable auto-indexing of directories
188
+ # auto_index: true,
189
+ # # Try adding .html extension to extensionless URLs
190
+ # try_html_extension: true,
191
+ # # Files under this size are cached in memory
192
+ # max_file_size_in_memory: 1024 * 1024, # 1MB
193
+ # # Maximum number of files to keep in memory cache
194
+ # max_files_in_memory: 1000,
195
+ # # Check for file modifications every 5 seconds
196
+ # file_check_interval: 5,
197
+ # # Add custom headers to all responses
198
+ # headers: {
199
+ # 'Cache-Control' => 'public, max-age=86400',
200
+ # 'X-Content-Type-Options' => 'nosniff'
201
+ # }
202
+
203
+ # run(lambda { |env|
204
+ # [200, {}, ["I'm a fallback"]]
205
+ # })
206
+
207
+ # def user_serve(request)
208
+ # response = request.response
209
+ # response.status = 200
210
+ # response.add_header('Content-Type', 'text/plain')
211
+ # response << 'Hello, user!'
212
+ # response.close
213
+ # end
214
+
215
+ # def user_create(request)
216
+ # response = request.response
217
+ # response.status = 201
218
+ # response.add_header('Content-Type', 'text/plain')
219
+ # response << 'User created!'
220
+ # response.close
221
+ # end
222
+
223
+ # location "/etag-test" do
224
+ # etag type: 'strong', algorithm: 'sha256', min_body_size: 0
225
+
226
+ # get '/' do |req|
227
+ # # Fixed content that will generate the same ETag each time
228
+ # content = "This is a fixed response that will always have the same ETag."
229
+
230
+ # # Add a timestamp as a comment to see the response is fresh
231
+ # resp = req.response
232
+ # req.respond("Content: #{content}.# Generated at: #{Time.now}")
233
+ # end
234
+ # end
235
+
236
+ # location "/cached-with-etag" do
237
+ # # Set up caching parameters
238
+ # cache_control max_age: 3600, public: true, vary: ['Accept-Encoding']
239
+
240
+ # # Add ETags for cache validation
241
+ # etag type: 'weak', algorithm: 'md5', min_body_size: 0
242
+
243
+ # get '/' do |req|
244
+ # req.respond("This response will have both cache headers and an ETag.")
245
+ # end
246
+ # end
247
+
248
+ # location "/" do
249
+ # 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' }
250
+
251
+ # # Example for API endpoints with ETags
252
+ # location "/api" do
253
+ # etag type: 'weak', algorithm: 'md5', min_body_size: 0
254
+ # get '/users/id' do |req|
255
+ # user_id = 5
256
+ # req.respond("User data for #{user_id}")
257
+ # end
258
+ # end
259
+
260
+ # location "/hey" do
261
+ # location "/world*" do
262
+ # request_headers additions: {"X-Custom-Req" => ["Foo", "Bar", "Baz"]}, removals: [""]
263
+ # response_headers additions: {"X-Custom-Resp" => ["Foo", "Bar"]}, removals: []
264
+ # 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' }
265
+ # post '/' do |req|
266
+ # puts "I'm a post!"
267
+ # req.respond("Post successful!")
268
+ # end
269
+ # get '/' do |req|
270
+ # req.respond("Is this still fast?")
271
+ # end
272
+ # end
273
+ # end
274
+
275
+ # # Add a new location with cache_control middleware
276
+ # location "/cached" do
277
+ # cache_control max_age: 3600, public: true, vary: ['Accept-Encoding']
278
+ # get '/' do |req|
279
+ # req.respond("This content is cached for 1 hour.")
280
+ # end
281
+ # end
282
+
283
+ # location "/etag" do
284
+ # etag enabled: true, etag_type: 'strong', hash_algorithm: 'sha256', min_body_size: 0
285
+ # get '/' do |req|
286
+ # # Create a response large enough to be worth ETagging
287
+ # content = "This content will have ETags. " * 20
288
+ # req.respond(content)
289
+ # end
290
+ # end
291
+
292
+ # location "/jwt" do
293
+ # auth_jwt token_source: { header: { name: 'Authorization', prefix: 'Bearer ' } },
294
+ # verifiers: {
295
+ # 'hs256' => ["1V6nZzztO/F6Y3XAYXJ1I37AAXCJ/V7EVHJoVnD8lr4="],
296
+ # 'rs256' => [%Q{
297
+ # -----BEGIN PUBLIC KEY-----
298
+ # MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs8XK+wP7nnKkOGXP6+il
299
+ # +JDQMFRV8zoVg1BYYapGla6AzR0YCV6xq28lNB1FE8C6xhCEKYiOlnkXZwj+a4UW
300
+ # q4Bex/U9qvfza0zGxHlTnQT9T+vjwXNsWYuefYC4EAZjEd6bGeP23De128QfhlLc
301
+ # oQpcfW32/e8KjSYtn8rtWuKV6Anqf92o9zEbVn8y2OZwZbiW0LbfDnimazqP5ATy
302
+ # SI6a5kQorBeG9cU4WW93ctuT5nkyAbBaEpiejWIpjbMkofD5fj2pUgg7H/Xvh+eR
303
+ # S9A+//VGfy4A4SFjBooVewhJ04VhDkMnVC29fbVwVuNqm6Z+FZaxI5pqvisxvH8S
304
+ # XwIDAQAB
305
+ # -----END PUBLIC KEY-----
306
+ # }]
307
+ # }, error_response: { code: 401, plaintext: 'Unauthorized', default: 'plaintext' }
308
+ # get "/" do |req|
309
+ # req.respond('Hello, JWT user!')
310
+ # end
311
+ # end
312
+
313
+ # end
314
+
315
+ # # For an assets example
316
+ # location "/assets" do
317
+ # cache_control max_age: 604800, s_max_age: 2592000,
318
+ # stale_while_revalidate: 86400,
319
+ # public: true, immutable: true
320
+
321
+ # get "/:file" do |req|
322
+ # file_name = req.params["file"]
323
+ # req.respond("Pretending to serve #{file_name} with long-lived caching")
324
+ # end
325
+ # end
326
+
327
+ # # location '' do
328
+
329
+ # location '/spa' do
330
+ # # Serve static files from the "public/assets" directory
331
+ # 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' }
332
+ # allow_list allowed_patterns: [/127.*/], error_response: { plaintext: 'no way', code: 401, default: 'plaintext' }
333
+ # static_assets\
334
+ # relative_path: true,
335
+ # root_dir: 'spa/dist',
336
+ # # # Only allow certain file extensions
337
+ # # allowed_extensions: %w[css js jpg jpeg png gif svg ico woff woff2 ttf
338
+ # # otf html],
339
+ # # Return a 404 error if file is not found
340
+ # not_found_behavior: { index: 'index.html' },
341
+ # # Enable auto-indexing of directories
342
+ # auto_index: true,
343
+ # # Try adding .html extension to extensionless URLs
344
+ # try_html_extension: true,
345
+ # # Files under this size are cached in memory
346
+ # max_file_size_in_memory: 1024 * 1024, # 1MB
347
+ # # Maximum number of files to keep in memory cache
348
+ # max_files_in_memory: 1000,
349
+ # # Check for file modifications every 5 seconds
350
+ # file_check_interval: 5,
351
+ # # Add custom headers to all responses
352
+ # headers: {
353
+ # 'Cache-Control' => 'public, max-age=86400',
354
+ # 'X-Content-Type-Options' => 'nosniff'
355
+ # }
356
+ # compress algorithms: ['zstd'], min_size: 0, compress_streams: true, mime_types: ['all'], level: 'fastest'
357
+ # end
148
358
  # end
149
359
 
150
360
  # location "/cors_test" do
@@ -154,17 +364,7 @@ end
154
364
  # end
155
365
  # end
156
366
 
157
- # location "/basic" do
158
- # auth_basic realm: 'My Realm', credential_pairs: { 'user' => 'password' }
159
- # end
160
367
 
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
368
 
169
369
  # location '/proxy_as_foo_com' do
170
370
  # compress algorithms: ['zstd'], min_size: 0, compress_streams: true, mime_types: ['all'], level: 'fastest'
@@ -229,7 +429,6 @@ end
229
429
  # req.respond("This is a test")
230
430
  # end
231
431
 
232
-
233
432
  # location '*', protocols: :http do
234
433
  # location 'foo' do
235
434
  # redirect to: 'https://{host}:3001{path}'
@@ -272,5 +471,4 @@ end
272
471
 
273
472
  # Example with both caching and ETags
274
473
 
275
-
276
474
  # 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.