simple-httpd 0.0.4 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.rubocop.yml +9 -0
- data/.tm_properties +1 -0
- data/Gemfile +21 -1
- data/Makefile +9 -0
- data/README.md +87 -2
- data/Rakefile +5 -0
- data/VERSION +1 -1
- data/bin/simple-httpd +13 -0
- data/examples/README.md +41 -0
- data/examples/ex1/ex1_helpers.rb +5 -0
- data/examples/ex1/root.rb +11 -0
- data/examples/ex2/README.txt +1 -0
- data/examples/ex2/ex2_helpers.rb +5 -0
- data/examples/ex2/helpers.rb +15 -0
- data/examples/ex2/info.rb +4 -0
- data/examples/ex2/root.rb +3 -0
- data/examples/ex3/example_service.rb +13 -0
- data/examples/services/example_service.rb +25 -0
- data/examples/services/explicit_example_service.rb +18 -0
- data/examples/v2/api.js +1 -0
- data/examples/v2/jobs.rb +13 -0
- data/examples/v2/root.rb +3 -0
- data/examples/v2/v2_helpers.rb +5 -0
- data/lib/simple-service.rb +3 -0
- data/lib/simple/httpd.rb +99 -25
- data/lib/simple/httpd/base_controller.rb +2 -2
- data/lib/simple/httpd/base_controller/error_handling.rb +45 -17
- data/lib/simple/httpd/base_controller/json.rb +15 -8
- data/lib/simple/httpd/cli.rb +99 -0
- data/lib/simple/httpd/helpers.rb +54 -0
- data/lib/simple/httpd/mount_spec.rb +106 -0
- data/lib/simple/httpd/rack.rb +17 -0
- data/lib/simple/httpd/rack/dynamic_mount.rb +66 -0
- data/lib/simple/httpd/rack/merger.rb +28 -0
- data/lib/simple/httpd/rack/static_mount.rb +50 -0
- data/lib/simple/httpd/server.rb +69 -0
- data/lib/simple/httpd/service.rb +70 -0
- data/lib/simple/httpd/version.rb +1 -1
- data/lib/simple/service.rb +69 -0
- data/lib/simple/service/action.rb +78 -0
- data/lib/simple/service/context.rb +46 -0
- data/scripts/release +2 -0
- data/scripts/release.rb +91 -0
- data/simple-httpd.gemspec +9 -19
- data/spec/simple/httpd/base_controller/httpd_cors_spec.rb +15 -0
- data/spec/simple/httpd/base_controller/httpd_debug_spec.rb +11 -0
- data/spec/simple/httpd/base_controller/httpd_x_processing_copy.rb +15 -0
- data/spec/simple/httpd/base_spec.rb +16 -0
- data/spec/simple/httpd/dynamic_mounting_spec.rb +33 -0
- data/spec/simple/httpd/helpers_spec.rb +15 -0
- data/spec/simple/httpd/rspec_httpd_spec.rb +17 -0
- data/spec/simple/httpd/services/service_explicit_spec.rb +34 -0
- data/spec/simple/httpd/services/service_spec.rb +34 -0
- data/spec/simple/httpd/static_mounting_spec.rb +13 -0
- data/spec/spec_helper.rb +30 -6
- data/spec/support/004_simplecov.rb +3 -12
- metadata +61 -84
- data/lib/simple/httpd/app.rb +0 -84
- data/lib/simple/httpd/app/file_server.rb +0 -19
- data/spec/simple/httpd/version_spec.rb +0 -10
- data/tasks/release.rake +0 -104
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2cf16a40a84142155ccfdf2486e3f12d2a2be13429a942660136ba1c7ce5034
|
4
|
+
data.tar.gz: fe679b913fe4ea849945b797fd949ec4d4f9dc783d7ea252e358060037ec25e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36e023782c8602e0864b1787ee661b1b4c6df8b76ea28889a14acc77116b927c98dd7d720850337b011a7c32ad6694767a776ba92ed772953ebdf3960ff43932
|
7
|
+
data.tar.gz: 0011d3b37a962c6dd5b1781f5dfd934aef319b8e13033b370fc9f4b240fa921ef06475808d7780072cd6c82675db95103d6c856911dd8a61d4048f8a45430bd3
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/.tm_properties
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
excludeDirectories = "{_build,coverage,assets/node_modules,node_modules,deps,db,cover,priv/static,storage,github,vendor,arena,}"
|
data/Gemfile
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem 'byebug'
|
4
3
|
# Specify your gem's dependencies in {gemname}.gemspec
|
5
4
|
gemspec
|
5
|
+
|
6
|
+
# --- Local overrides for runtime dependencies ------------------------------
|
7
|
+
|
8
|
+
# gem "simple-cli", path: "../simple-cli"
|
9
|
+
|
10
|
+
# --- Development and test dependencies ------------------------------
|
11
|
+
|
12
|
+
group :development, :test do
|
13
|
+
gem "rspec-httpd", "~> 0.3.0"
|
14
|
+
# gem "rspec-httpd", path: "../rspec-httpd"
|
15
|
+
|
16
|
+
gem 'rake', '~> 11'
|
17
|
+
gem 'rspec', '~> 3.7'
|
18
|
+
# gem 'rubocop', '~> 0.61.1'
|
19
|
+
gem 'simplecov', '~> 0'
|
20
|
+
gem 'byebug'
|
21
|
+
|
22
|
+
if ENV["PRELOAD_SERVER_GEM"]
|
23
|
+
gem ENV["PRELOAD_SERVER_GEM"]
|
24
|
+
end
|
25
|
+
end
|
data/Makefile
ADDED
data/README.md
CHANGED
@@ -1,3 +1,88 @@
|
|
1
|
-
# simple-httpd
|
1
|
+
# simple-httpd – serving HTTP made simpler.
|
2
2
|
|
3
|
-
[
|
3
|
+
This ruby gem wraps around [sinatra](/) to provide an even simpler way of setting up http based backends. It is especially helpful to:
|
4
|
+
|
5
|
+
- bind loosely related pieces of code together: `simple-httpd` lets a developer lay out their code and assets in directories and trees of directories and can then serve these via HTTP.
|
6
|
+
- have an easy way to serve static assets via HTTP.
|
7
|
+
- allow existing applications, especially CLI tools, to easily start HTTP servers.
|
8
|
+
|
9
|
+
In some ways one might be reminded of the web's old days where one would throw a bunch of php scripts into a FTP location, and then an appache webserver (but, really, its php integration) would start serving requests via HTTPS. ***simple-httpd* is not like that.** This gem still supports the notion of an application; source files typically rely on other source files' existence and functionality.
|
10
|
+
|
11
|
+
Also, at least as of now, **simple-httpd** does not dynamically reload code parts on request. This might change in the future.
|
12
|
+
|
13
|
+
## Is it useful?
|
14
|
+
|
15
|
+
At this point I don't know yet. We'll see. In any case this gem is used to test [rspec-httpd](github.com/radiospiel/rspec-httpd) (a rspec extension helping with testing HTTP endpoints), and is used within [postjob-httpd](github.com/radiospiel/postjob-httpd) where it is configured to glue HTTP endpoints to the [postjob](github.com/radiospiel/postjob) job queue system.
|
16
|
+
|
17
|
+
It has proven useful so far - but as it is a really lean wrapper around sinatra one might probably also use sinatra in most cases.
|
18
|
+
|
19
|
+
## Mounting directories
|
20
|
+
|
21
|
+
`simple-httpd` lets a user of the gem "mount" directories onto "mount points". A "mount point" describes the location of the actions or static assets at the HTTP endpoint. Note that two or more directories can be mounted at the same mount point.
|
22
|
+
|
23
|
+
Files in a mounted directory fall into different categories:
|
24
|
+
|
25
|
+
### Static assets
|
26
|
+
|
27
|
+
Static assets are files with a predefined set of file extensions, including `.txt` and `.js`. (compare the `static_mount.rb` source file for a full list.)
|
28
|
+
|
29
|
+
They become available at the location specified by their filename and extension.
|
30
|
+
|
31
|
+
### Dynamic assets
|
32
|
+
|
33
|
+
Each mounted directory which contains ruby source files is converted into a sinatra application, which consists of a root configuration and controllers for each action file.
|
34
|
+
|
35
|
+
Ruby files ending in `_helpers.rb`, e.g. `examples/ex1/ex1_helpers.rb` are executed in the context of a directory tree's root controller and provide functionality available in all action files. Typically they do not implement HTTP handlers themselves.
|
36
|
+
|
37
|
+
All other ruby files implement HTTP handlers in typical sinatra fashion:
|
38
|
+
|
39
|
+
# in v2/jobs.rb
|
40
|
+
get "/queue/:id/events" do
|
41
|
+
events = [
|
42
|
+
{ job_id: params[:id], id: "event1" },
|
43
|
+
{ job_id: params[:id], id: "event2" }
|
44
|
+
]
|
45
|
+
|
46
|
+
json events
|
47
|
+
end
|
48
|
+
|
49
|
+
If this snippet is contained in a file `v2/jobs.rb` and the `v2` directory is mounted into `api/v2`, the snipped implements the handler for, for example, `GET /api/v2/jobs/queue/123/events`. In other words, the handler implement in the source file works on paths relative to a path combining the mount location and the file name.
|
50
|
+
|
51
|
+
To implement a action on the mountpoint itself one uses the `root.rb` file. The following
|
52
|
+
|
53
|
+
# in v2/root.rb
|
54
|
+
get "/" do
|
55
|
+
json version: "123"
|
56
|
+
end
|
57
|
+
|
58
|
+
would implement `GET /api/v2`.
|
59
|
+
|
60
|
+
## Command line usage
|
61
|
+
|
62
|
+
`simple-httpd` comes with a CLI tool, which lets one assemble multiple locations into a single HTTP backend: the following command serves the *./ex1* and *./ex2* directories at `http://0.0.0.0:12345` and the *./v2* directory at `http://0.0.0.0:12345/api/v2`.
|
63
|
+
|
64
|
+
simple-httpd --port=12345 ex1 ex2 v2:api/v2
|
65
|
+
|
66
|
+
The `v2:api/v2` argument asks the `v2` directory to be mounted into the web endpoint at `/api/v2`. All relevant content is therefore served below `http://0.0.0.0:12345/api/v2`.
|
67
|
+
|
68
|
+
The arguments `ex1` and `ex2` serve at the `/` location. This notation really is a shorthand for `ex1:/`
|
69
|
+
|
70
|
+
## Integration
|
71
|
+
|
72
|
+
`simple-httpd` can be integrated into other ruby scripts. Example:
|
73
|
+
|
74
|
+
require "simple-httpd"
|
75
|
+
|
76
|
+
httpd_root_dir = File.join(__dir__, "httpd")
|
77
|
+
port = 12345
|
78
|
+
|
79
|
+
app = ::Simple::Httpd.build("/" => httpd_root_dir)
|
80
|
+
::Simple::Httpd.listen! app, port: port,
|
81
|
+
logger: ::Logger.new(STDERR)
|
82
|
+
|
83
|
+
|
84
|
+
## The example application
|
85
|
+
|
86
|
+
An example application is contained in ./examples. (Well, this example is probably not as *useful* for any purpose, but I hope it demonstrates all simple-httpd use cases, also it is used during tests.)
|
87
|
+
|
88
|
+
See [its readme](examples/README.md) for more details.
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.3.0
|
data/bin/simple-httpd
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$: << "lib"
|
3
|
+
|
4
|
+
require "bundler"
|
5
|
+
Bundler.setup
|
6
|
+
|
7
|
+
# === setup and run Simple Httpd server =======================================
|
8
|
+
require "simple/cli"
|
9
|
+
require "simple/httpd/cli"
|
10
|
+
|
11
|
+
# run the :main command with these arguments. This switches simple-cli into
|
12
|
+
# non-subcommand mode.
|
13
|
+
Simple::Httpd::CLI.run!(:main)
|
data/examples/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# An example
|
2
|
+
|
3
|
+
This directory contains an example application. (Well, this example is probably not as *useful* for any purpose, but I hope it demonstrates all simple-httpd use cases, also it is used during tests.)
|
4
|
+
|
5
|
+
See [its readme](examples/README.md) for more details.
|
6
|
+
|
7
|
+
## How to start the example application
|
8
|
+
|
9
|
+
Assuming you have installed the simple-httpd gem via `gem install simple-httpd` you should be able to start the server via
|
10
|
+
|
11
|
+
simple-httpd --port=12345 ex1 ex2 v2:api/v2
|
12
|
+
|
13
|
+
This starts a HTTPD server on `http://0.0.0.0:12345`. The server serves content from the `./ex1` and `./ex2` directories at the root URL (`http://0.0.0.0:12345/`) and content from the ./v2 directory below `http://0.0.0.0:12345/api/v2`.
|
14
|
+
|
15
|
+
The following explanations assume you started a server with the configuration mentioned above.
|
16
|
+
|
17
|
+
## Files
|
18
|
+
|
19
|
+
This directory currently contains these files:
|
20
|
+
|
21
|
+
- ex1/root.rb
|
22
|
+
- ex1/ex1_helpers.rb
|
23
|
+
- ex2/helpers.rb
|
24
|
+
- ex2/root.rb
|
25
|
+
- ex2/info.rb
|
26
|
+
- ex2/ex2_helpers.rb
|
27
|
+
- ex2/README.txt
|
28
|
+
- v2/root.rb
|
29
|
+
- v2/jobs.rb
|
30
|
+
- v2/v2_helpers.rb
|
31
|
+
- v2/api.js
|
32
|
+
|
33
|
+
## Some routes
|
34
|
+
|
35
|
+
The following lists some routes and where they are implemented:
|
36
|
+
|
37
|
+
GET "/" .. in ex1/root.rb
|
38
|
+
GET "/debug" .. in ex2/root.rb
|
39
|
+
GET "/info/inspect" .. in ex2/info.rb
|
40
|
+
GET "/api/v2/" .. in v2/root.rb
|
41
|
+
GET "/api/v2/jobs/:id/events" .. in v2/jobs.rb
|
@@ -0,0 +1 @@
|
|
1
|
+
This is a README file
|
@@ -0,0 +1,13 @@
|
|
1
|
+
get "/check" do
|
2
|
+
"ok: explicit_service"
|
3
|
+
end
|
4
|
+
|
5
|
+
mount_service ExplicitService do |service|
|
6
|
+
# def echo(one, two, a:, b:)
|
7
|
+
post "/echo/:a" => :explicit_echo
|
8
|
+
|
9
|
+
put "/echo_context" do
|
10
|
+
# def echo_context
|
11
|
+
service.call(:echo_context, parsed_body, params, context: context)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName, Lint/UnusedMethodArgument
|
2
|
+
|
3
|
+
module Example; end
|
4
|
+
module Example::Service
|
5
|
+
include ::Simple::Service
|
6
|
+
|
7
|
+
def test(a:, b:)
|
8
|
+
# "this is a test; a is #{a.inspect}, b is #{b.inspect}"
|
9
|
+
"hello from ExampleService#test"
|
10
|
+
end
|
11
|
+
|
12
|
+
def echo(one, two, a:, b:)
|
13
|
+
"one: [#{one}]/two: [#{two}]/a: [#{a}]/b: [#{b}]"
|
14
|
+
end
|
15
|
+
|
16
|
+
def echo_context
|
17
|
+
::Simple::Service.context.inspect
|
18
|
+
end
|
19
|
+
|
20
|
+
def this_is_a_helper!; end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def this_is_a_private_helper; end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName, Lint/UnusedMethodArgument
|
2
|
+
|
3
|
+
module ExplicitService
|
4
|
+
include ::Simple::Service
|
5
|
+
|
6
|
+
def explicit_test(a:, b:)
|
7
|
+
# "this is a test; a is #{a.inspect}, b is #{b.inspect}"
|
8
|
+
"hello from ExplicitService#test"
|
9
|
+
end
|
10
|
+
|
11
|
+
def explicit_echo(one, two, a:, b:)
|
12
|
+
"one: [#{one}]/two: [#{two}]/a: [#{a}]/b: [#{b}]"
|
13
|
+
end
|
14
|
+
|
15
|
+
def echo_context
|
16
|
+
::Simple::Service.context.inspect
|
17
|
+
end
|
18
|
+
end
|
data/examples/v2/api.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
/* API example file */
|
data/examples/v2/jobs.rb
ADDED
data/examples/v2/root.rb
ADDED
data/lib/simple/httpd.rb
CHANGED
@@ -1,43 +1,117 @@
|
|
1
|
+
# rubocop:disable Style/TrivialAccessors
|
2
|
+
|
1
3
|
module Simple
|
2
4
|
end
|
3
5
|
|
4
|
-
|
6
|
+
class Simple::Httpd
|
5
7
|
end
|
6
8
|
|
7
|
-
require "simple/
|
8
|
-
|
9
|
+
require "simple/service"
|
10
|
+
|
11
|
+
require "simple/httpd/helpers"
|
9
12
|
require "simple/httpd/base_controller"
|
13
|
+
require "simple/httpd/version"
|
14
|
+
require "simple/httpd/mount_spec"
|
15
|
+
require "simple/httpd/server"
|
10
16
|
|
11
|
-
|
12
|
-
|
17
|
+
require "simple/httpd/service"
|
18
|
+
|
19
|
+
class Simple::Httpd
|
20
|
+
SELF = self
|
21
|
+
|
22
|
+
def self.logger=(logger)
|
23
|
+
@logger = logger
|
24
|
+
end
|
13
25
|
|
14
|
-
def
|
15
|
-
|
26
|
+
def self.logger
|
27
|
+
@logger ||= ::Logger.new(STDERR, level: ::Logger::INFO)
|
16
28
|
end
|
17
29
|
|
18
|
-
|
19
|
-
|
30
|
+
# Converts the passed in args into a Simple::Httpd application.
|
31
|
+
#
|
32
|
+
# The passed in arguments are used to create a Simple::Httpd object.
|
33
|
+
# If the function receives a rack app (determined by the ability to
|
34
|
+
# respond to call/3) it redirects to <tt>Server.listen!</tt> right
|
35
|
+
# away - this way this method can be used as a helper method
|
36
|
+
# to easily start a Rack server.
|
37
|
+
def self.listen!(*mount_specs, environment: "development", host: nil, port:, logger: nil, &block)
|
38
|
+
# If there is no argument but a block use the block as a rack server
|
39
|
+
if block
|
40
|
+
raise ArgumentError, "Can't deal w/block *and* mount_specs" unless mount_specs.empty?
|
20
41
|
|
21
|
-
|
22
|
-
|
42
|
+
app = block
|
43
|
+
elsif mount_specs.length == 1 && mount_specs.first.respond_to?(:call)
|
44
|
+
# there is one argument, and that looks like a Rack app: return that.
|
45
|
+
app = mount_specs.first
|
46
|
+
else
|
47
|
+
# Build a Httpd app, and listen
|
48
|
+
app = build(*mount_specs)
|
49
|
+
app.rack
|
50
|
+
end
|
23
51
|
|
24
|
-
|
52
|
+
Server.listen!(app, environment: environment, host: host, port: port, logger: logger)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Converts the passed in arguments into a Simple::Httpd application.
|
56
|
+
#
|
57
|
+
# For a description of mounts see <tt>#add</tt>
|
58
|
+
def self.build(*mount_specs)
|
59
|
+
new(*mount_specs)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Builds a Simple::Httpd application.
|
65
|
+
def initialize(*mount_specs)
|
66
|
+
@mount_specs = []
|
67
|
+
mount_specs.map do |mount_spec|
|
68
|
+
mount(mount_spec, at: nil)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
public
|
73
|
+
|
74
|
+
# Adds one or more mount_points
|
75
|
+
#
|
76
|
+
# Each entry in mounts can be either:
|
77
|
+
#
|
78
|
+
# - a mount_point <tt>[ mount_point, path ]</tt>, e.g. <tt>[ "path/to/root", "/"]</tt>
|
79
|
+
# - a string denoting a mount_point, e.g. "path/to/root:/")
|
80
|
+
# - a string denoting a "/" mount_point (e.g. "path", which is shorthand for "path:/")
|
81
|
+
#
|
82
|
+
def mount(mount_spec, at: nil)
|
83
|
+
raise ArgumentError, "Cannot mount onto an already built app" if built?
|
84
|
+
|
85
|
+
@mount_specs << MountSpec.build(mount_spec, at: at)
|
86
|
+
end
|
87
|
+
|
88
|
+
extend Forwardable
|
89
|
+
delegate :call => :rack # rubocop:disable Style/HashSyntax
|
90
|
+
|
91
|
+
def rack
|
92
|
+
@rack ||= build_rack
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def build_rack
|
98
|
+
uri_map = {}
|
99
|
+
|
100
|
+
@mount_specs.group_by(&:mount_point).map do |mount_point, mount_specs|
|
101
|
+
apps = mount_specs.map(&:build_rack_apps).flatten
|
102
|
+
uri_map[mount_point] = Rack.merge(apps)
|
103
|
+
end
|
104
|
+
|
105
|
+
::Rack::URLMap.new(uri_map)
|
106
|
+
end
|
25
107
|
|
26
|
-
|
27
|
-
|
28
|
-
#
|
29
|
-
# Instead we'll use a combination of Rack::CommonLogger (see Simple::Httpd.app),
|
30
|
-
# and sinatra's logger (see Simple::Httpd::BaseController).
|
31
|
-
Rack::Server.start app: app,
|
32
|
-
Port: port,
|
33
|
-
environment: environment,
|
34
|
-
Logger: logger,
|
35
|
-
AccessLog: [[NullLogger, ""]]
|
108
|
+
def built?
|
109
|
+
@rack != nil
|
36
110
|
end
|
37
111
|
|
38
|
-
|
39
|
-
extend self
|
112
|
+
public
|
40
113
|
|
41
|
-
|
114
|
+
def listen!(environment:, port:, logger:)
|
115
|
+
SELF.listen!(rack, environment: environment, port: port, logger: logger)
|
42
116
|
end
|
43
117
|
end
|