simple-httpd 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -0
  3. data/Gemfile +3 -1
  4. data/VERSION +1 -1
  5. data/bin/simple-httpd +1 -3
  6. data/examples/ex1.services/ex1_service_module.rb +8 -0
  7. data/examples/ex1/root.rb +4 -0
  8. data/examples/ex1/spec.rb +7 -0
  9. data/examples/ex2/info.rb +5 -3
  10. data/examples/ex3/example_service.rb +3 -2
  11. data/lib/simple/httpd.rb +37 -28
  12. data/lib/simple/httpd/base_controller.rb +6 -0
  13. data/lib/simple/httpd/base_controller/build_url.rb +1 -1
  14. data/lib/simple/httpd/base_controller/debug.rb +2 -0
  15. data/lib/simple/httpd/base_controller/error_handling.rb +21 -38
  16. data/lib/simple/httpd/base_controller/json.rb +2 -31
  17. data/lib/simple/httpd/base_controller/request.rb +23 -0
  18. data/lib/simple/httpd/base_controller/result.rb +19 -0
  19. data/lib/simple/httpd/cli.rb +71 -30
  20. data/lib/simple/httpd/helpers.rb +35 -3
  21. data/lib/simple/httpd/mount.rb +66 -0
  22. data/lib/simple/httpd/rack/dynamic_mount.rb +52 -32
  23. data/lib/simple/httpd/rack/static_mount.rb +20 -14
  24. data/lib/simple/httpd/route.rb +79 -0
  25. data/lib/simple/httpd/server.rb +28 -16
  26. data/lib/simple/httpd/service_adapter.rb +108 -0
  27. data/simple-httpd.gemspec +2 -1
  28. data/spec/simple/httpd/base_controller/httpd_cors_spec.rb +8 -3
  29. data/spec/simple/httpd/base_controller/httpd_url_building_spec.rb +17 -0
  30. data/spec/simple/httpd/helpers_spec.rb +25 -8
  31. data/spec/simple/httpd/loading_helpers_spec.rb +15 -0
  32. data/spec/simple/httpd/rspec_httpd_spec.rb +25 -9
  33. data/spec/simple/httpd/services/service_explicit_spec.rb +0 -5
  34. data/spec/simple/httpd/services/sideloading_spec.rb +9 -0
  35. data/spec/spec_helper.rb +2 -3
  36. metadata +31 -12
  37. data/examples/services/example_service.rb +0 -25
  38. data/lib/simple/httpd/mount_spec.rb +0 -106
  39. data/lib/simple/httpd/service.rb +0 -70
  40. data/lib/simple/service.rb +0 -69
  41. data/lib/simple/service/action.rb +0 -78
  42. data/lib/simple/service/context.rb +0 -46
  43. data/spec/simple/httpd/services/service_spec.rb +0 -34
@@ -1,3 +1,5 @@
1
+ # rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize
2
+
1
3
  class Simple::Httpd
2
4
  module Server
3
5
  extend self
@@ -8,16 +10,13 @@ class Simple::Httpd
8
10
  def <<(msg); end
9
11
  end
10
12
 
11
- def listen!(app, environment: "development", host: nil, port:, logger: nil)
13
+ def listen!(app, environment: "development", host: nil, port:)
12
14
  expect! app != nil
13
15
 
14
16
  host ||= "127.0.0.1"
15
17
  URI("http://#{host}:#{port}") # validate host and port
16
18
 
17
- logger ||= ::Simple::Httpd.logger
18
-
19
- prepare_logger!(logger)
20
- logger.info "Starting httpd server on http://#{host}:#{port}/"
19
+ ::Simple::Httpd.logger.info "Starting httpd server on http://#{host}:#{port}/"
21
20
 
22
21
  app = ::Rack::Lint.new(app) if environment != "production"
23
22
 
@@ -30,32 +29,45 @@ class Simple::Httpd
30
29
  Host: host,
31
30
  Port: port,
32
31
  environment: environment,
33
- Logger: logger,
32
+ Logger: build_logger,
34
33
  AccessLog: [[NullLogger, ""]]
35
34
  end
36
35
 
37
36
  private
38
37
 
39
- # When Webrick is being shut down via SIGTERM - which we do at least during
40
- # rspec-httpd triggered runs - it sends a fatal message to the logger. We catch
41
- # it - to "downgrade" it to INFO - but we still abort.
42
- def prepare_logger!(logger)
38
+ def build_logger
39
+ # We create a fresh STDERR logger. The log level is taken from Simple::Httpd -
40
+ # but is at maximum :info, since we don't want to log internals of Webrick & co.
41
+ #
42
+ # (Note that our own services would probably use ::Simple::Httpd.logger, which
43
+ # sticks at the current level.)
44
+ if ::Simple::Httpd.custom_logger?
45
+ logger = ::Simple::Httpd.logger
46
+ else
47
+ log_level = ::Simple::Httpd.logger.debug? ? :info : ::Simple::Httpd.logger.level
48
+ logger = ::Logger.new STDERR, level: log_level
49
+ end
50
+
51
+ # When Webrick is being shut down via SIGTERM - which we do at least during
52
+ # rspec-httpd triggered runs - it sends a fatal message to the logger. We catch
53
+ # it - to "downgrade" it to INFO - but we still abort.
43
54
  def logger.fatal(msg, &block)
44
55
  if msg.is_a?(SignalException) && msg.signo == ::Signal.list["TERM"]
45
- if %w(test development).include?(::Simple::Httpd.env)
46
- info "Received SIGTERM: hard killing server (due to running in #{::Simple::Httpd.env.inspect} environment)"
47
- Simple::Httpd::Server.exit!
56
+ env = ::Simple::Httpd.env
57
+ if %w(test development).include?(env)
58
+ ::Simple::Httpd.logger.info "Received SIGTERM: hard killing server (due to running in #{env.inspect} environment)"
59
+ ::Simple::Httpd::Server.send :exit!
48
60
  else
49
- info "Received SIGTERM: shutting down server..."
61
+ ::Simple::Httpd.logger.info "Received SIGTERM: shutting down server..."
50
62
  exit 1
51
63
  end
52
64
  end
53
65
 
54
66
  super
55
67
  end
56
- end
57
68
 
58
- public
69
+ logger
70
+ end
59
71
 
60
72
  def exit!(exit_status = 1)
61
73
  # Run SimpleCov if exists, and if this is the PID that started SimpleCov in the first place.
@@ -0,0 +1,108 @@
1
+ require "simple-service"
2
+
3
+ module Simple::Httpd::ServiceAdapter
4
+ def mount_service(service)
5
+ @service = service
6
+
7
+ instance_eval do
8
+ def dispatch!
9
+ ::Simple::Service.with_context(context)
10
+ super
11
+ ensure
12
+ ::Simple::Service.context = nil
13
+ end
14
+
15
+ yield(service)
16
+ end
17
+ ensure
18
+ @service = nil
19
+ end
20
+
21
+ def get(path, opts = {}, &block)
22
+ install_route("GET", path, opts, &block)
23
+ end
24
+
25
+ def post(path, opts = {}, &block)
26
+ install_route("POST", path, opts, &block)
27
+ end
28
+
29
+ def put(path, opts = {}, &block)
30
+ install_route("PUT", path, opts, &block)
31
+ end
32
+
33
+ def delete(path, opts = {}, &block)
34
+ install_route("DELETE", path, opts, &block)
35
+ end
36
+
37
+ def head(path, opts = {}, &block)
38
+ install_route("HEAD", path, opts, &block)
39
+ end
40
+
41
+ private
42
+
43
+ def service_route?(_verb, path, opts, &block)
44
+ return false unless @service
45
+ return false if block
46
+ return false unless opts.empty?
47
+ return false unless path.is_a?(Hash) && path.size == 1
48
+
49
+ true
50
+ end
51
+
52
+ def install_route(verb, path, opts, &block)
53
+ if service_route?(verb, path, opts, &block)
54
+ path, action_name = *path.first
55
+ handle_service_route(verb, path, action_name)
56
+ else
57
+ handle_non_service_route(verb, path, opts, &block)
58
+ end
59
+ end
60
+
61
+ def handle_service_route(verb, path, action_name)
62
+ # Fetch action's source_location. This also verifies that the action
63
+ # is defined in the first place.
64
+ action = ::Simple::Service.action(@service, action_name)
65
+
66
+ describe_route!(verb: verb, path: path, source_location: action.source_location)
67
+
68
+ # get service reference into binding, to make it available for the route
69
+ # definition's callback block.
70
+ service = @service
71
+
72
+ # define sinatra route.
73
+ route(verb, path) do
74
+ ::Simple::Service.with_context(context) do
75
+ # [TODO] - symbolizing keys opens up this for DDOS effects.
76
+ # THIS MUST BE FIXED IN simple-service.
77
+ flags = self.params.inject({}) { |hsh, (k,v)| hsh.update k.to_sym => v }
78
+ args = self.parsed_body.inject({}) { |hsh, (k,v)| hsh.update k.to_sym => v }
79
+
80
+ result = ::Simple::Service.invoke2(service, action_name, args: args, flags: flags)
81
+ encode_result(result)
82
+ end
83
+ end
84
+ end
85
+
86
+ def handle_non_service_route(verb, path, opts, &block)
87
+ describe_route!(verb: verb, path: path, source_location: block.source_location) if block
88
+
89
+ route(verb, path, opts) do
90
+ result = instance_eval(&block)
91
+ unless headers["Content-Type"]
92
+ result = encode_result(result)
93
+ end
94
+ result
95
+ end
96
+ end
97
+
98
+ module Helpers
99
+ def context
100
+ # We return nil here. nil *is* a valid value for Simple::Service.with_context.
101
+ # Important is that with_context is being called.
102
+ nil
103
+ end
104
+ end
105
+ end
106
+
107
+ ::Simple::Httpd::BaseController.extend(::Simple::Httpd::ServiceAdapter)
108
+ ::Simple::Httpd::BaseController.helpers(::Simple::Httpd::ServiceAdapter::Helpers)
data/simple-httpd.gemspec CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |gem|
26
26
  gem.add_dependency "sinatra", "~> 2"
27
27
  # gem.add_dependency "sinatra-reloader", "~> 1"
28
28
  gem.add_dependency "expectation", "~> 1"
29
- gem.add_dependency "simple-cli", "~> 0.3.0"
29
+ gem.add_dependency "simple-cli", "~> 0.3.3"
30
+ gem.add_dependency "simple-service", "~> 0.1.2"
30
31
  end
31
32
  #require "expectation"
@@ -7,9 +7,14 @@ describe Simple::Httpd do
7
7
  expect(http.response.headers.keys).to include("access-control-allow-origin")
8
8
  end
9
9
 
10
- it "returns CORS headers" do
11
- http.get "/README.txt"
12
- expect(http.response.headers.keys).not_to include("access-control-allow-origin")
10
+ it "sends proper headers in all request methods" do
11
+ verbs = [ :get, :post, :put, :delete, :options, :head ]
12
+ verbs.each do |verb|
13
+ http.send verb, "/info/inspect?qux"
14
+
15
+ expect_response 200
16
+ expect(http.response.headers.keys).to include("access-control-allow-origin")
17
+ end
13
18
  end
14
19
  end
15
20
  end
@@ -0,0 +1,17 @@
1
+ require "spec_helper"
2
+
3
+ describe Simple::Httpd do
4
+ describe "full_url" do
5
+ it "builds a full_url" do
6
+ http.get "/spec/full_url"
7
+ expect_response("http://127.0.0.1:12345/foo?search=s&page=1")
8
+ end
9
+ end
10
+
11
+ describe "url" do
12
+ it "returns X-Processing headers on dynamic responses" do
13
+ http.get "/spec/url"
14
+ expect_response("foo?search=s&page=1")
15
+ end
16
+ end
17
+ end
@@ -1,15 +1,32 @@
1
1
  require "spec_helper"
2
2
 
3
- describe Simple::Httpd do
4
- describe "helpers" do
5
- it "loads helpers from the same directory tree" do
6
- http.get "/helpers/ex2"
7
- expect_response "ex2_helper"
3
+ describe "Simple::Httpd::Helpers" do
4
+ H = Simple::Httpd::Helpers
5
+
6
+ describe ".filtered_stacktrace" do
7
+ def some_stacktrace(depth = 1)
8
+ if depth == 0
9
+ caller
10
+ else
11
+ some_stacktrace(depth-1)
12
+ end
8
13
  end
9
14
 
10
- it "does not load helpers from other directory tree even on the same URL tree" do
11
- http.get "/helpers/ex1"
12
- expect_response status: 404
15
+ def filtered_stacktrace
16
+ H.filtered_stacktrace(some_stacktrace)
17
+ end
18
+
19
+ it "removes .rvm lines" do
20
+ actual = filtered_stacktrace
21
+
22
+ expect(actual.grep(/lines removed/).count).to eq(1)
23
+ expect(actual.grep(/helpers_spec/).count).to be > 1
24
+ expect(actual.grep(/some_stacktrace/).count).to eq(1)
25
+ end
26
+
27
+ it "shortens paths" do
28
+ source_path = filtered_stacktrace.grep(/helpers_spec/).first
29
+ expect(source_path).to start_with("./")
13
30
  end
14
31
  end
15
32
  end
@@ -0,0 +1,15 @@
1
+ require "spec_helper"
2
+
3
+ describe Simple::Httpd do
4
+ describe "helpers" do
5
+ it "loads helpers from the same directory tree" do
6
+ http.get "/helpers/ex2"
7
+ expect_response "ex2_helper"
8
+ end
9
+
10
+ it "does not load helpers from other directory tree even on the same URL tree" do
11
+ http.get "/helpers/ex1"
12
+ expect_response status: 404
13
+ end
14
+ end
15
+ end
@@ -1,17 +1,33 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe "RSpec::Httpd features" do
4
- it "sends proper headers" do
5
- http.get "/info/inspect?qux"
4
+ VERBS_WO_BODY = [ :options, :head ]
6
5
 
7
- result_lines = http.content.split("\n")
6
+ VERBS = [ :get, :post, :put, :delete ]
8
7
 
9
- expect(result_lines).to include("QUERY_STRING=qux")
10
- expect(result_lines).to include("REQUEST_METHOD=GET")
11
- expect(result_lines).to include("REQUEST_PATH=/info/inspect")
12
- expect(result_lines).to include("SERVER_NAME=127.0.0.1")
13
- expect(result_lines).to include("SERVER_PORT=12345")
8
+ VERBS.each do |verb|
9
+ it "sends proper headers in #{verb} request" do
10
+ http.send verb, "/info/inspect?qux"
14
11
 
15
- expect(http.response.headers["content-type"]).to match(/text\/plain/)
12
+ result_lines = http.content.split("\n")
13
+
14
+ expect(result_lines).to include("QUERY_STRING=qux")
15
+ expect(result_lines).to include("REQUEST_METHOD=#{verb.upcase}")
16
+ expect(result_lines).to include("REQUEST_PATH=/info/inspect")
17
+ expect(result_lines).to include("SERVER_NAME=127.0.0.1")
18
+ expect(result_lines).to include("SERVER_PORT=12345")
19
+
20
+ expect(http.response.headers["content-type"]).to match(/text\/plain/)
21
+ end
22
+ end
23
+
24
+ VERBS_WO_BODY = [ :options, :head ]
25
+
26
+ VERBS_WO_BODY.each do |verb|
27
+ it "supports #{verb} methods" do
28
+ http.send verb, "/info/inspect?qux"
29
+
30
+ expect_response 200
31
+ end
16
32
  end
17
33
  end
@@ -22,11 +22,6 @@ describe "explicit mounting of service" do
22
22
  expect_response 422
23
23
  end
24
24
 
25
- it "ignores missing parameters arguments" do
26
- http.post "/example_service/echo/1", { one: "foo", two: "bar" }
27
- expect_response "one: [foo]/two: [bar]/a: [1]/b: []"
28
- end
29
-
30
25
  it "properly extracts arguments and parameters" do
31
26
  http.put "/example_service/echo_context"
32
27
  expect_response /Simple::Service::Context/
@@ -0,0 +1,9 @@
1
+ require "spec_helper"
2
+
3
+ describe "sideloading" do
4
+ # mounting not at root level
5
+ it "sideloads code" do
6
+ http.get "/sideloaded_service"
7
+ expect_response("hello_world")
8
+ end
9
+ end
data/spec/spec_helper.rb CHANGED
@@ -12,15 +12,14 @@ end
12
12
  # You can comment parts of the command below by prepending the line with a '#'
13
13
  HTTPD_COMMAND = <<~CMD
14
14
  PORT=12345
15
- bin/simple-httpd
15
+ bin/simple-httpd start
16
16
  --environment=test
17
17
  examples/ex1
18
18
  examples/ex2
19
19
  examples/ex3
20
20
  examples/v2:api/v2
21
21
  --services=examples/services
22
- Example::Service:service/example
23
- 2> log/test.log
22
+ # 2> log/test.log
24
23
  CMD
25
24
 
26
25
  RSpec::Httpd.configure do |config|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple-httpd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - radiospiel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-16 00:00:00.000000000 Z
11
+ date: 2019-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: neatjson
@@ -58,14 +58,28 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.3.0
61
+ version: 0.3.3
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.3.0
68
+ version: 0.3.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: simple-service
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.1.2
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.1.2
69
83
  description: Super-simple HTTPD server - sinatra w/gimmiks
70
84
  email: eno@radiospiel.org
71
85
  executables: []
@@ -87,15 +101,16 @@ files:
87
101
  - bin/rubocop
88
102
  - bin/simple-httpd
89
103
  - examples/README.md
104
+ - examples/ex1.services/ex1_service_module.rb
90
105
  - examples/ex1/ex1_helpers.rb
91
106
  - examples/ex1/root.rb
107
+ - examples/ex1/spec.rb
92
108
  - examples/ex2/README.txt
93
109
  - examples/ex2/ex2_helpers.rb
94
110
  - examples/ex2/helpers.rb
95
111
  - examples/ex2/info.rb
96
112
  - examples/ex2/root.rb
97
113
  - examples/ex3/example_service.rb
98
- - examples/services/example_service.rb
99
114
  - examples/services/explicit_example_service.rb
100
115
  - examples/v2/api.js
101
116
  - examples/v2/jobs.rb
@@ -111,20 +126,20 @@ files:
111
126
  - lib/simple/httpd/base_controller/debug.rb
112
127
  - lib/simple/httpd/base_controller/error_handling.rb
113
128
  - lib/simple/httpd/base_controller/json.rb
129
+ - lib/simple/httpd/base_controller/request.rb
130
+ - lib/simple/httpd/base_controller/result.rb
114
131
  - lib/simple/httpd/base_controller/x_processing.rb
115
132
  - lib/simple/httpd/cli.rb
116
133
  - lib/simple/httpd/helpers.rb
117
- - lib/simple/httpd/mount_spec.rb
134
+ - lib/simple/httpd/mount.rb
118
135
  - lib/simple/httpd/rack.rb
119
136
  - lib/simple/httpd/rack/dynamic_mount.rb
120
137
  - lib/simple/httpd/rack/merger.rb
121
138
  - lib/simple/httpd/rack/static_mount.rb
139
+ - lib/simple/httpd/route.rb
122
140
  - lib/simple/httpd/server.rb
123
- - lib/simple/httpd/service.rb
141
+ - lib/simple/httpd/service_adapter.rb
124
142
  - lib/simple/httpd/version.rb
125
- - lib/simple/service.rb
126
- - lib/simple/service/action.rb
127
- - lib/simple/service/context.rb
128
143
  - log/.gitkeep
129
144
  - scripts/release
130
145
  - scripts/release.rb
@@ -133,13 +148,15 @@ files:
133
148
  - simple-httpd.gemspec
134
149
  - spec/simple/httpd/base_controller/httpd_cors_spec.rb
135
150
  - spec/simple/httpd/base_controller/httpd_debug_spec.rb
151
+ - spec/simple/httpd/base_controller/httpd_url_building_spec.rb
136
152
  - spec/simple/httpd/base_controller/httpd_x_processing_copy.rb
137
153
  - spec/simple/httpd/base_spec.rb
138
154
  - spec/simple/httpd/dynamic_mounting_spec.rb
139
155
  - spec/simple/httpd/helpers_spec.rb
156
+ - spec/simple/httpd/loading_helpers_spec.rb
140
157
  - spec/simple/httpd/rspec_httpd_spec.rb
141
158
  - spec/simple/httpd/services/service_explicit_spec.rb
142
- - spec/simple/httpd/services/service_spec.rb
159
+ - spec/simple/httpd/services/sideloading_spec.rb
143
160
  - spec/simple/httpd/static_mounting_spec.rb
144
161
  - spec/spec_helper.rb
145
162
  - spec/support/004_simplecov.rb
@@ -168,13 +185,15 @@ summary: Super-simple HTTPD server
168
185
  test_files:
169
186
  - spec/simple/httpd/base_controller/httpd_cors_spec.rb
170
187
  - spec/simple/httpd/base_controller/httpd_debug_spec.rb
188
+ - spec/simple/httpd/base_controller/httpd_url_building_spec.rb
171
189
  - spec/simple/httpd/base_controller/httpd_x_processing_copy.rb
172
190
  - spec/simple/httpd/base_spec.rb
173
191
  - spec/simple/httpd/dynamic_mounting_spec.rb
174
192
  - spec/simple/httpd/helpers_spec.rb
193
+ - spec/simple/httpd/loading_helpers_spec.rb
175
194
  - spec/simple/httpd/rspec_httpd_spec.rb
176
195
  - spec/simple/httpd/services/service_explicit_spec.rb
177
- - spec/simple/httpd/services/service_spec.rb
196
+ - spec/simple/httpd/services/sideloading_spec.rb
178
197
  - spec/simple/httpd/static_mounting_spec.rb
179
198
  - spec/spec_helper.rb
180
199
  - spec/support/004_simplecov.rb