Module: Capsium::Reactor::Serving
- Includes:
- Responses
- Included in:
- Capsium::Reactor
- Defined in:
- lib/capsium/reactor/serving.rb,
sig/capsium/reactor/serving.rbs
Overview
Serving of resolved routes (static files through the merged view, datasets, handler-route 501s), mixed into Reactor. Includes route inheritance processing per 05x-routing.
Instance Method Summary collapse
-
#authorized?(identity, route, response) ⇒ Boolean
Route-level access control; false when the response was written.
-
#content_type_for(mount, route, content_path) ⇒ String
The route's declared MIME type, detected from the resolved file for resources the manifest does not list (dependency content reached through inheritance, layer-only files).
- #headers_for(route) ⇒ Hash[String, String]
-
#inherited_processing(route, body, headers) ⇒ [String, Hash[String, String]]
Route inheritance processing (05x-routing section "Route Inheritance"): responseRewrite replaces the body and/or overrides headers; responseHeaders are merged over the served headers.
- #mounts_by_length ⇒ Array[Mount]
-
#record_request(request, response) ⇒ Object
One request metric and one log line per served request.
-
#resolve_mount(path) ⇒ Mount?
The mount answering a request path: longest matching prefix wins.
-
#root_mount ⇒ Mount
The root ("/") mount, or the first mount when nothing is mounted at the root.
- #serve_content_api(mount, identity, inner, request, response) ⇒ Object
- #serve_data_api(mount, identity, inner, request, response) ⇒ Object
- #serve_dataset(mount, dataset_name, response) ⇒ Object
- #serve_file(mount, route, response) ⇒ Object
-
#serve_mounted_request(mount, identity, request, response) ⇒ Object
Serves a request that matched a mount.
- #serve_route(mount, route, response) ⇒ Object
Methods included from Responses
#respond_error, #respond_forbidden, #respond_json, #respond_method_not_allowed, #respond_not_found, #respond_not_implemented, #respond_text
Instance Method Details
#authorized?(identity, route, response) ⇒ Boolean
Route-level access control; false when the response was written.
81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/capsium/reactor/serving.rb', line 81 def (identity, route, response) case @authenticator.(identity, route.access_control) when :unauthenticated @authenticator.challenge(response) false when :forbidden respond_forbidden(response) false else true end end |
#content_type_for(mount, route, content_path) ⇒ String
The route's declared MIME type, detected from the resolved file for resources the manifest does not list (dependency content reached through inheritance, layer-only files).
132 133 134 135 136 |
# File 'lib/capsium/reactor/serving.rb', line 132 def content_type_for(mount, route, content_path) route.mime(mount.package.manifest) || Marcel::MimeType.for(Pathname.new(content_path), name: File.basename(content_path)) end |
#headers_for(route) ⇒ Hash[String, String]
138 139 140 |
# File 'lib/capsium/reactor/serving.rb', line 138 def headers_for(route) route.headers || (@cache_control ? { "Cache-Control" => @cache_control } : {}) end |
#inherited_processing(route, body, headers) ⇒ [String, Hash[String, String]]
Route inheritance processing (05x-routing section "Route Inheritance"): responseRewrite replaces the body and/or overrides headers; responseHeaders are merged over the served headers. requestHeaders are parsed and exposed on the route for forwarding reactors; this reactor serves statically without an upstream, so they do not alter its responses.
119 120 121 122 123 124 125 126 127 |
# File 'lib/capsium/reactor/serving.rb', line 119 def inherited_processing(route, body, headers) rewrite = route.response_rewrite if rewrite body = rewrite.body unless rewrite.body.nil? headers = headers.merge(rewrite.headers) if rewrite.headers end headers = headers.merge(route.response_headers) if route.response_headers [body, headers] end |
#mounts_by_length ⇒ Array[Mount]
30 31 32 |
# File 'lib/capsium/reactor/serving.rb', line 30 def mounts_by_length @mounts_by_length ||= @mounts.sort_by { |mount| -mount.path.length } end |
#record_request(request, response) ⇒ Object
One request metric and one log line per served request.
17 18 19 20 21 22 23 |
# File 'lib/capsium/reactor/serving.rb', line 17 def record_request(request, response) status = response.status return if status.nil? @metrics.record(status) @log_buffer.add("#{request.request_method} #{request.path} -> #{status}") end |
#resolve_mount(path) ⇒ Mount?
The mount answering a request path: longest matching prefix wins.
26 27 28 |
# File 'lib/capsium/reactor/serving.rb', line 26 def resolve_mount(path) mounts_by_length.find { |mount| mount.matches?(path) } end |
#root_mount ⇒ Mount
The root ("/") mount, or the first mount when nothing is mounted at the root.
38 39 40 |
# File 'lib/capsium/reactor/serving.rb', line 38 def root_mount @mounts.find { |mount| mount.path == Mount::ROOT_PATH } || @mounts.first end |
#serve_content_api(mount, identity, inner, request, response) ⇒ Object
71 72 73 74 75 76 |
# File 'lib/capsium/reactor/serving.rb', line 71 def serve_content_api(mount, identity, inner, request, response) route = mount.routes.resolve(inner) return unless !route || (identity, route, response) mount.content_api.handle(inner, request, response) end |
#serve_data_api(mount, identity, inner, request, response) ⇒ Object
63 64 65 66 67 68 69 |
# File 'lib/capsium/reactor/serving.rb', line 63 def serve_data_api(mount, identity, inner, request, response) route = mount.data_api.route_for(inner) return respond_not_found(response) unless route return unless (identity, route, response) mount.data_api.handle(inner, request, response) end |
#serve_dataset(mount, dataset_name, response) ⇒ Object
142 143 144 145 146 147 148 149 150 151 |
# File 'lib/capsium/reactor/serving.rb', line 142 def serve_dataset(mount, dataset_name, response) dataset = mount.package.storage.dataset(dataset_name) if dataset response.status = 200 response["Content-Type"] = "application/json" response.body = JSON.generate(mount.dataset_data(dataset)) else respond_not_found(response) end end |
#serve_file(mount, route, response) ⇒ Object
101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/capsium/reactor/serving.rb', line 101 def serve_file(mount, route, response) content_path = mount.merged_view.resolve(route.resource) return respond_not_found(response) unless content_path body, headers = inherited_processing(route, File.read(content_path), headers_for(route)) response.status = 200 response["Content-Type"] = content_type_for(mount, route, content_path) headers.each { |name, value| response[name] = value } response.body = body end |
#serve_mounted_request(mount, identity, request, response) ⇒ Object
Serves a request that matched a mount.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/capsium/reactor/serving.rb', line 46 def serve_mounted_request(mount, identity, request, response) inner = mount.inner_path(request.path) return serve_data_api(mount, identity, inner, request, response) if DataApi.path?(inner) if GraphqlApi.path?(inner) && mount.graphql? return mount.graphql_api.handle(request, response) end if ContentApi.write_method?(request.request_method) return serve_content_api(mount, identity, inner, request, response) end route = mount.routes.resolve(inner) return respond_not_found(response) unless route return unless (identity, route, response) serve_route(mount, route, response) end |
#serve_route(mount, route, response) ⇒ Object
93 94 95 96 97 98 99 |
# File 'lib/capsium/reactor/serving.rb', line 93 def serve_route(mount, route, response) case route.kind when :dataset then serve_dataset(mount, route.dataset, response) when :resource then serve_file(mount, route, response) else respond_not_implemented(response) end end |