piedesaint 0.0.1
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.
- data/.gitignore +18 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +53 -0
- data/bin/sug +8 -0
- data/lib/piedesaint/cli.rb +89 -0
- data/lib/piedesaint/version.rb +3 -0
- data/lib/piedesaint.rb +163 -0
- data/piedesaint.gemspec +28 -0
- data/tasks/gem.thor +33 -0
- metadata +129 -0
data/.gitignore
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
piedesaint
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3-p392
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 tnarik
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Piedesaint
|
2
|
+
|
3
|
+
## In short
|
4
|
+
|
5
|
+
[Piedesaint](https://github.com/tnarik/piedesaint) is a minimal web server designed to expose directly files and directories (in [TAR](http://en.wikipedia.org/wiki/Tar_(computing) format) via HTTP or HTTPS.
|
6
|
+
|
7
|
+
## Motivation
|
8
|
+
|
9
|
+
It was born from the need of having the simplest web server possible (while still being reasonably fast and secure) to provide files and directories to be used by [remote_file](http://docs.opscode.com/resource_remote_file.html) Chef resources, solving the issue of distributing packages that for different reasons are not public or require some interaction to get downloaded, without requiring the installation of a full fledged web server.
|
10
|
+
|
11
|
+
It also serves directories (packaging them on the fly) as a single resource.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
You can add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
gem 'piedesaint'
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install piedesaint
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
After installation you will need to initialize the configuration by executing:
|
30
|
+
|
31
|
+
$ sug init [list of folders to serve, in cascade order]
|
32
|
+
|
33
|
+
This creates the ```.piedesaint``` folder that you can inspect and configure (it contains a default shortlived SSL key/certificate pair and some additional configuration in [YAML](http://en.wikipedia.org/wiki/YAML) format).
|
34
|
+
|
35
|
+
By default the configuration will serve the current directory, unless a list of folders is specified. If you want to serve a different folder or set of folders, just edit the configuration.
|
36
|
+
|
37
|
+
After this, whenever you want to serve the files/directories, just execute:
|
38
|
+
|
39
|
+
$ sug
|
40
|
+
|
41
|
+
## License
|
42
|
+
|
43
|
+
MIT
|
44
|
+
|
45
|
+
## Contributing
|
46
|
+
|
47
|
+
If you want to contribute:
|
48
|
+
|
49
|
+
1. Just fork this project.
|
50
|
+
2. Create your feature branch (`git checkout -b my-new-feature`).
|
51
|
+
3. Commit your changes (`git commit -am 'Add some feature'`).
|
52
|
+
4. Push to the branch (`git push origin my-new-feature`).
|
53
|
+
5. Create new Pull Request.
|
data/bin/sug
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'piedesaint'
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Piedesaint
|
7
|
+
|
8
|
+
# The CLI class encapsulates the behavior of Piedesaint when it is invoked
|
9
|
+
# as a command-line utility. This allows other programs to embed Piedesaint
|
10
|
+
# and preserve its command-line semantics.
|
11
|
+
class CLI
|
12
|
+
def initialize
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute
|
16
|
+
load_config
|
17
|
+
piedesanto = Piedesaint.new @config
|
18
|
+
piedesanto.start
|
19
|
+
end
|
20
|
+
|
21
|
+
def init ( parameters )
|
22
|
+
if File.exist?(".piedesaint")
|
23
|
+
abort "Configuration already exists at #{Dir.pwd}/.piedesaint"
|
24
|
+
end
|
25
|
+
key, cert = create_ssl_artifacts
|
26
|
+
|
27
|
+
FileUtils.mkdir_p ".piedesaint"
|
28
|
+
FileUtils.cd ".piedesaint" do
|
29
|
+
FileUtils.mkdir_p "ssl"
|
30
|
+
FileUtils.cd "ssl" do
|
31
|
+
open 'server.key', 'w' do |io| io.write key.to_pem end
|
32
|
+
open 'server.crt', 'w' do |io| io.write cert.to_pem end
|
33
|
+
end
|
34
|
+
|
35
|
+
config = { host: "0.0.0.0",
|
36
|
+
http_port: 8080,
|
37
|
+
https_port: 9292,
|
38
|
+
key: File.join(".", ".piedesaint", "ssl", "server.key" ),
|
39
|
+
cert: File.join(".", ".piedesaint", "ssl", "server.crt" ),
|
40
|
+
username: "user",
|
41
|
+
password: "password",
|
42
|
+
folders: parameters }
|
43
|
+
|
44
|
+
open 'config', 'w' do |io| io.write config.to_yaml end
|
45
|
+
end
|
46
|
+
|
47
|
+
puts "Configuration created at #{Dir.pwd}/.piedesaint"
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def load_config
|
52
|
+
config_path = find_default_config_path
|
53
|
+
if config_path.nil?
|
54
|
+
abort "Configuration not provided.\nExecute '#{$PROGRAM_NAME} init' to generate one"
|
55
|
+
end
|
56
|
+
|
57
|
+
@config = YAML.load_file File.join(config_path, "config")
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_ssl_artifacts
|
61
|
+
key = OpenSSL::PKey::RSA.new 2048
|
62
|
+
|
63
|
+
cert = OpenSSL::X509::Certificate.new
|
64
|
+
cert.version = 2
|
65
|
+
cert.serial = 0
|
66
|
+
cert.not_before = Time.now
|
67
|
+
cert.not_after = Time.now + 3 * 3600
|
68
|
+
cert.public_key = key.public_key
|
69
|
+
name = OpenSSL::X509::Name.parse 'CN=Piedesaint'
|
70
|
+
cert.subject = name
|
71
|
+
cert.issuer = name
|
72
|
+
cert.sign key, OpenSSL::Digest::SHA1.new
|
73
|
+
|
74
|
+
return key, cert
|
75
|
+
end
|
76
|
+
|
77
|
+
def find_default_config_path
|
78
|
+
previous = nil
|
79
|
+
current = File.expand_path(Dir.pwd)
|
80
|
+
until !File.directory?(current) || current == previous
|
81
|
+
filename = File.join(current, '.piedesaint')
|
82
|
+
return filename if File.directory?(filename)
|
83
|
+
current, previous = File.expand_path("..", current), current
|
84
|
+
end
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
data/lib/piedesaint.rb
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
require "piedesaint/version"
|
2
|
+
|
3
|
+
require 'puma/server'
|
4
|
+
require 'puma/events'
|
5
|
+
#require 'puma/minissl' #For Puma 2.0.1
|
6
|
+
require 'openssl'
|
7
|
+
|
8
|
+
require 'rack/ssl-enforcer'
|
9
|
+
require 'rubygems/package'
|
10
|
+
require "stringio"
|
11
|
+
|
12
|
+
|
13
|
+
module Piedesaint
|
14
|
+
|
15
|
+
module Rack
|
16
|
+
class DirectoryCompress < ::Rack::Directory
|
17
|
+
def list_directory
|
18
|
+
return [200, {}, ::Piedesaint.tar(@path)]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class DirectoriesCompress
|
23
|
+
def initialize(roots, app=nil)
|
24
|
+
@apps = []
|
25
|
+
roots.each do |root|
|
26
|
+
root = File.expand_path root
|
27
|
+
puts "Service root: #{root}"
|
28
|
+
@apps << Rack::DirectoryCompress.new(root, app)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def call(env)
|
33
|
+
response = nil
|
34
|
+
@apps.each do |app|
|
35
|
+
response = app.call(env)
|
36
|
+
break unless response[0] == 404
|
37
|
+
end
|
38
|
+
response
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
class Piedesaint
|
45
|
+
def initialize ( options = {} )
|
46
|
+
@config = options
|
47
|
+
@config[:folders] = [ "." ] if @config[:folders].empty?
|
48
|
+
end
|
49
|
+
|
50
|
+
def start
|
51
|
+
::Piedesaint.execute @config
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.app ( options = {} )
|
56
|
+
::Rack::Builder.app do
|
57
|
+
use ::Rack::CommonLogger
|
58
|
+
use ::Rack::ShowExceptions
|
59
|
+
use ::Rack::SslEnforcer, http_port: options[:http_port], https_port: options[:https_port]
|
60
|
+
use ::Rack::Deflater
|
61
|
+
use ::Rack::Auth::Basic, "Icecreamland" do |username, password|
|
62
|
+
( options[:username] == username ) && ( options[:password] == password )
|
63
|
+
end
|
64
|
+
|
65
|
+
map "/" do
|
66
|
+
run Rack::DirectoriesCompress.new(options[:folders])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.execute ( options = {} )
|
72
|
+
event = ::Puma::Events.new STDOUT, STDERR
|
73
|
+
puma = ::Puma::Server.new self.app( options ), event
|
74
|
+
|
75
|
+
## For Puma 2.0.1 (there is a bug regarding SSL and at least Ruby 1.9.3)
|
76
|
+
## Puma server doesn't receive 'event' (that's left to Puma::Binder)
|
77
|
+
#binder = ::Puma::Binder.new event
|
78
|
+
#puma.binder = binder
|
79
|
+
#ctx = ::Puma::MiniSSL::SSLContext.new
|
80
|
+
#ctx.key = "./server.key"
|
81
|
+
#ctx.cert = "./server.crt"
|
82
|
+
#ctx.verify_mode = ::Puma::MiniSSL::VERIFY_NONE
|
83
|
+
|
84
|
+
ctx = ::OpenSSL::SSL::SSLContext.new
|
85
|
+
ctx.key = OpenSSL::PKey::RSA.new File.read(options[:key])
|
86
|
+
ctx.cert = OpenSSL::X509::Certificate.new File.read(options[:cert])
|
87
|
+
ctx.verify_mode = ::OpenSSL::SSL::VERIFY_NONE
|
88
|
+
|
89
|
+
puma.add_tcp_listener options[:host], options[:http_port]
|
90
|
+
puma.add_ssl_listener options[:host], options[:https_port], ctx
|
91
|
+
|
92
|
+
puma.min_threads = 1
|
93
|
+
puma.max_threads = 1
|
94
|
+
|
95
|
+
begin
|
96
|
+
Signal.trap "SIGUSR2" do
|
97
|
+
@restart = true
|
98
|
+
puma.begin_restart
|
99
|
+
end
|
100
|
+
rescue Exception
|
101
|
+
p "*** Sorry signal SIGUSR2 not implemented, restart feature disabled!"
|
102
|
+
end
|
103
|
+
|
104
|
+
begin
|
105
|
+
Signal.trap "SIGTERM" do
|
106
|
+
p " - Gracefully stopping, waiting for requests to finish"
|
107
|
+
puma.stop false
|
108
|
+
end
|
109
|
+
rescue Exception
|
110
|
+
p "*** Sorry signal SIGTERM not implemented, gracefully stopping feature disabled!"
|
111
|
+
end
|
112
|
+
|
113
|
+
begin
|
114
|
+
puma.run.join
|
115
|
+
rescue Interrupt
|
116
|
+
graceful_stop puma
|
117
|
+
end
|
118
|
+
|
119
|
+
if @restart
|
120
|
+
p "* Restarting..."
|
121
|
+
@status.stop true if @status
|
122
|
+
restart!
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
def self.graceful_stop(puma)
|
128
|
+
p " - Gracefully stopping, waiting for requests to finish"
|
129
|
+
@status.stop(true) if @status
|
130
|
+
puma.stop(true)
|
131
|
+
delete_pidfile
|
132
|
+
p " - Goodbye!"
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.delete_pidfile
|
136
|
+
#�if path = @options[:pidfile]
|
137
|
+
# File.unlink path
|
138
|
+
#end
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
def self.tar(path)
|
143
|
+
tar = StringIO.new
|
144
|
+
Gem::Package::TarWriter.new(tar) do |tarwriter|
|
145
|
+
Dir[File.join(path, "**/{*,.*}")].each do |file|
|
146
|
+
mode = File.stat(file).mode
|
147
|
+
relative_file = file.sub /^#{Regexp::escape path}\/?/, ''
|
148
|
+
|
149
|
+
if File.directory? file
|
150
|
+
next if [ ".", ".."].include? File.basename(file)
|
151
|
+
tarwriter.mkdir relative_file, mode
|
152
|
+
else
|
153
|
+
tarwriter.add_file(relative_file, mode) do |filepart|
|
154
|
+
File.open(file, "rb") { |f| filepart.write f.read }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
tar.rewind
|
160
|
+
tar
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
data/piedesaint.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'piedesaint/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "piedesaint"
|
8
|
+
spec.version = Piedesaint::VERSION
|
9
|
+
spec.authors = ["Tnarik Innael"]
|
10
|
+
spec.email = ["tnarik@gmail.com"]
|
11
|
+
spec.description = %q{Drop-in web server}
|
12
|
+
spec.summary = %q{Drop-in web server to serve files and tar'ed directories}
|
13
|
+
spec.homepage = "https://github.com/tnarik/piedesaint"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# development dependencies
|
22
|
+
spec.add_development_dependency "thor"
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
|
25
|
+
# runtime dependencies
|
26
|
+
spec.add_dependency "puma", "~>1.6.3"
|
27
|
+
spec.add_dependency "rack-ssl-enforcer"
|
28
|
+
end
|
data/tasks/gem.thor
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
class Gem < Thor
|
2
|
+
include Thor::Actions
|
3
|
+
|
4
|
+
# This allows for dynamic descriptions
|
5
|
+
begin
|
6
|
+
@gemhelper = Bundler::GemHelper.new
|
7
|
+
rescue
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(*args)
|
11
|
+
super
|
12
|
+
@gemhelper = Bundler::GemHelper.new
|
13
|
+
end
|
14
|
+
|
15
|
+
# tasks
|
16
|
+
desc "build", @gemhelper.nil? ? "Building gem into the pkg directory" :
|
17
|
+
"Building #{@gemhelper.gemspec.name}-#{@gemhelper.gemspec.version}.gem into the pkg directory"
|
18
|
+
def build
|
19
|
+
@gemhelper.build_gem
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "install", @gemhelper.nil? ? "Build and install gem into system gems" :
|
23
|
+
"Build and install #{@gemhelper.gemspec.name}-#{@gemhelper.gemspec.version}.gem into system gems"
|
24
|
+
def install
|
25
|
+
@gemhelper.install_gem
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "release", @gemhelper.nil? ? "Create tag and build and push gem to Rubygems" :
|
29
|
+
"Create tag v#{@gemhelper.gemspec.version} and build and push #{@gemhelper.gemspec.name}-#{@gemhelper.gemspec.version}.gem to Rubygems"
|
30
|
+
def release
|
31
|
+
@gemhelper.release_gem
|
32
|
+
end
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: piedesaint
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tnarik Innael
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-06-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: thor
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: bundler
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.3'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.3'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: puma
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.6.3
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.6.3
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rack-ssl-enforcer
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: Drop-in web server
|
79
|
+
email:
|
80
|
+
- tnarik@gmail.com
|
81
|
+
executables:
|
82
|
+
- sug
|
83
|
+
extensions: []
|
84
|
+
extra_rdoc_files: []
|
85
|
+
files:
|
86
|
+
- .gitignore
|
87
|
+
- .ruby-gemset
|
88
|
+
- .ruby-version
|
89
|
+
- Gemfile
|
90
|
+
- LICENSE.txt
|
91
|
+
- README.md
|
92
|
+
- bin/sug
|
93
|
+
- lib/piedesaint.rb
|
94
|
+
- lib/piedesaint/cli.rb
|
95
|
+
- lib/piedesaint/version.rb
|
96
|
+
- piedesaint.gemspec
|
97
|
+
- tasks/gem.thor
|
98
|
+
homepage: https://github.com/tnarik/piedesaint
|
99
|
+
licenses:
|
100
|
+
- MIT
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
segments:
|
112
|
+
- 0
|
113
|
+
hash: -466208012132623450
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
hash: -466208012132623450
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 1.8.25
|
126
|
+
signing_key:
|
127
|
+
specification_version: 3
|
128
|
+
summary: Drop-in web server to serve files and tar'ed directories
|
129
|
+
test_files: []
|