crap_server 0.0.2
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.
- checksums.yaml +7 -0
- data/.gitignore +24 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +60 -0
- data/Rakefile +1 -0
- data/crap_server.gemspec +24 -0
- data/lib/crap_server.rb +8 -0
- data/lib/crap_server/application.rb +196 -0
- data/lib/crap_server/configure.rb +41 -0
- data/lib/crap_server/connection_instance.rb +53 -0
- data/lib/crap_server/helpers/socket_reader.rb +35 -0
- data/lib/crap_server/version.rb +3 -0
- metadata +71 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: eedef645e51d16d796efdd9893f69809adce20d0
|
4
|
+
data.tar.gz: e220bbd8a3878f29037216942fb3cc00ba5d4e60
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f2e78629c99e7564e0352f3a754623b7108e84ed9f695ce7f5f12e9f1c0ab1829830223f2f16aa43b343ed0423e8eb37f1f3e046a5939ba5bf48ffef88630163
|
7
|
+
data.tar.gz: 167fcab8bc1047971cee75ad00de1e0297b245e455de3210ce7a3dce0e8b032df6a0e94143a9ee49f1d5bd8a9e2b57418ab765164838228970e51edc7aa1336b
|
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
.idea/
|
24
|
+
.idea/*
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Andres B.
|
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,60 @@
|
|
1
|
+
# CrapServer
|
2
|
+
|
3
|
+
Really thin a non intuitive ruby server and framework. Made to be fast and ready for really heavy servers (not only http server).
|
4
|
+
|
5
|
+
# Another one?
|
6
|
+
|
7
|
+
Yes. Why? because 2 main reasons. First, this is not a HTTP Web server, this is a generic server that can be used for any kind of TCP Socket server.
|
8
|
+
Second and most important, because if funny :)
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
gem 'crap_server'
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install crap_server
|
23
|
+
|
24
|
+
## Basic Usage
|
25
|
+
|
26
|
+
CrapServer::Application.run! do |data|
|
27
|
+
if data =~ /^GET/
|
28
|
+
write "Hello world"
|
29
|
+
elsif data =~ /^SET/
|
30
|
+
write "Setting value"
|
31
|
+
else
|
32
|
+
write "Something is wrong"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
## Configuring the app
|
37
|
+
|
38
|
+
CrapServer::Application.configure do |config|
|
39
|
+
config.port = 80
|
40
|
+
config.read_method = :partial
|
41
|
+
config.read_buffer_size = 1024 # 1K
|
42
|
+
end
|
43
|
+
|
44
|
+
See all available options in lib/crap_server/configure.rb
|
45
|
+
|
46
|
+
# Running our application
|
47
|
+
|
48
|
+
ruby my_app.rb
|
49
|
+
|
50
|
+
# Production ready?
|
51
|
+
|
52
|
+
No. At the moment is only a thin server that abstract you from TCP sockets works.
|
53
|
+
|
54
|
+
## Contributing
|
55
|
+
|
56
|
+
1. Fork it ( https://github.com/[my-github-username]/crap_server/fork )
|
57
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
58
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
59
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
60
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/crap_server.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'crap_server/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "crap_server"
|
8
|
+
spec.version = CrapServer::VERSION
|
9
|
+
spec.authors = ["Andres Joser Borek"]
|
10
|
+
spec.email = ["andres.b.dev@gmail.com"]
|
11
|
+
spec.summary = %q{Really thin a non intuitive ruby server and framework.}
|
12
|
+
spec.description = %q{Really thin a non intuitive ruby server and framework. Made to be fast and ready for really heavy servers (not only http server).}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
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
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
22
|
+
# celluloid is for future use. Right now is not used
|
23
|
+
# spec.add_dependency "celluloid", '~> 0.15.2'
|
24
|
+
end
|
data/lib/crap_server.rb
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module CrapServer
|
5
|
+
class ConnectionError < StandardError; end
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# Crap::Application.run! do |data|
|
9
|
+
# if data =~ /^GET/i
|
10
|
+
# write SomeDatabase.get('column')
|
11
|
+
# elsif data =~/SET/
|
12
|
+
# SomeDatabase.set('column', data.split(' ')[1])
|
13
|
+
# else
|
14
|
+
# write 'Error. Invalid action.'
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
class Application
|
18
|
+
class << self
|
19
|
+
|
20
|
+
def configure(&block)
|
21
|
+
@config ||= CrapServer::Configure.new
|
22
|
+
block.yield @config
|
23
|
+
end
|
24
|
+
|
25
|
+
# Main method. This setup all the connections and make the logic of the app
|
26
|
+
def run!(&block)
|
27
|
+
|
28
|
+
# Bup the maximum opened file to the maximum allowed by the system
|
29
|
+
Process.setrlimit(:NOFILE, Process.getrlimit(:NOFILE)[1])
|
30
|
+
|
31
|
+
# Start IPv4 and IPv6 connection for the current port
|
32
|
+
open_connections
|
33
|
+
|
34
|
+
|
35
|
+
# Some log info to the user :)
|
36
|
+
logger.info 'Initializing Crap Server'
|
37
|
+
logger.info "Listening 0.0.0.0:#{config.port}"
|
38
|
+
logger.debug "Maximum allowed waiting connections: #{Socket::SOMAXCONN}"
|
39
|
+
logger.debug "Maximum number of allowed connections: #{Process.getrlimit(:NOFILE)[1]}" # Same as maximum of opened files
|
40
|
+
logger.info ''
|
41
|
+
|
42
|
+
# The main loop. Listening IPv4 and IPv6 connections
|
43
|
+
Socket.accept_loop([socket_ipv4, socket_ipv6]) do |remote_socket, address_info|
|
44
|
+
connection_loop(remote_socket, address_info, &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
close_connections
|
48
|
+
# If any kind of error happens, we MUST close the sockets
|
49
|
+
rescue => e
|
50
|
+
close_connections
|
51
|
+
|
52
|
+
raise e
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
def connection_loop(remote_socket, addres_info, &block)
|
57
|
+
# Work with the connection...
|
58
|
+
if we_should_read?
|
59
|
+
reader = CrapServer::Helpers::SocketReader.new(remote_socket, config.method)
|
60
|
+
reader.address = addres_info
|
61
|
+
reader.config = config
|
62
|
+
reader.on_message(&block)
|
63
|
+
else
|
64
|
+
begin
|
65
|
+
if block.parameters == 1
|
66
|
+
block.call(remote_socket)
|
67
|
+
else
|
68
|
+
block.call(remote_socket, addres_info)
|
69
|
+
end
|
70
|
+
# If we get out of data to read (but still having an opened connection), we wait for new data.
|
71
|
+
rescue IO::WaitReadable
|
72
|
+
# This, prevent to execute so many retry and block the code until a new bunch of data gets available
|
73
|
+
IO.select([remote_socket])
|
74
|
+
# Yay!, we have more data. Now we can continue!
|
75
|
+
retry
|
76
|
+
end
|
77
|
+
end
|
78
|
+
# ...
|
79
|
+
|
80
|
+
# Close the connection
|
81
|
+
remote_socket.close if config.auto_close_connection
|
82
|
+
end
|
83
|
+
|
84
|
+
# Return true or false if the read process is done by the server.
|
85
|
+
def we_should_read?
|
86
|
+
not config.manual_read
|
87
|
+
end
|
88
|
+
|
89
|
+
# Open TCP connection (IPv4 and IPv6)
|
90
|
+
def open_connections
|
91
|
+
start_ipv4_socket
|
92
|
+
start_ipv6_socket
|
93
|
+
end
|
94
|
+
|
95
|
+
# Close all the sockets.
|
96
|
+
def close_connections
|
97
|
+
# If any kind of error happens, we MUST close the sockets
|
98
|
+
if socket_ipv4
|
99
|
+
# Shuts down communication on all copies of the connection.
|
100
|
+
socket_ipv4.shutdown
|
101
|
+
socket_ipv4.close
|
102
|
+
end
|
103
|
+
|
104
|
+
if socket_ipv6
|
105
|
+
# Shuts down communication on all copies of the connection.
|
106
|
+
socket_ipv6.shutdown
|
107
|
+
socket_ipv6.close
|
108
|
+
end
|
109
|
+
# TODO: Close all opened sockets connections from other threads and processes
|
110
|
+
end
|
111
|
+
|
112
|
+
def start_ipv6_socket
|
113
|
+
# :INET6 is to open an IPv6 connection
|
114
|
+
# :STREAM is to open a TCP socket
|
115
|
+
# After this line, the app is not yet ready to work, we only opened a socket
|
116
|
+
socket_ipv6 = Socket.new(:INET6, :STREAM)
|
117
|
+
|
118
|
+
begin
|
119
|
+
# Now, bind the port.
|
120
|
+
# ::1 is loopback for IPv6
|
121
|
+
socket_ipv6.bind(Socket.pack_sockaddr_in(config.port, '::1'))
|
122
|
+
rescue Errno::EADDRINUSE
|
123
|
+
socket_ipv6.close
|
124
|
+
raise ConnectionError.new "Unable to bind #{config.port} port."
|
125
|
+
end
|
126
|
+
socket_ipv6.listen(config.max_pending_connections)
|
127
|
+
# Tell to the Kernel that is ok to rebind the port if is in TIME_WAIT state (after close the connection
|
128
|
+
# and the Kernel wait for client acknowledgement)
|
129
|
+
socket_ipv6.setsockopt(:SOCKET, :REUSEADDR, true)
|
130
|
+
@socket6 = socket_ipv6
|
131
|
+
end
|
132
|
+
|
133
|
+
def start_ipv4_socket
|
134
|
+
# :INET6 is to open an IPv6 connection
|
135
|
+
# :STREAM is to open a TCP socket
|
136
|
+
# After this line, the app is not yet ready to work, we only opened a socket
|
137
|
+
socket_ipv4 = Socket.new(:INET, :STREAM)
|
138
|
+
|
139
|
+
begin
|
140
|
+
# Now, bind the port.
|
141
|
+
socket_ipv4.bind(Socket.pack_sockaddr_in(config.port, '0.0.0.0'))
|
142
|
+
rescue Errno::EADDRINUSE
|
143
|
+
socket_ipv4.close
|
144
|
+
raise ConnectionError.new "Unable to bind #{config.port} port."
|
145
|
+
end
|
146
|
+
|
147
|
+
puts "config: #{config.manual_read}"
|
148
|
+
|
149
|
+
socket_ipv4.listen(config.max_pending_connections)
|
150
|
+
# Tell to the Kernel that is ok to rebind the port if is in TIME_WAIT state (after close the connection
|
151
|
+
# and the Kernel wait for client acknowledgement)
|
152
|
+
socket_ipv4.setsockopt(:SOCKET, :REUSEADDR, true)
|
153
|
+
@socket4 = socket_ipv4
|
154
|
+
end
|
155
|
+
|
156
|
+
def socket_ipv6
|
157
|
+
@socket6
|
158
|
+
end
|
159
|
+
|
160
|
+
def socket_ipv6=(value)
|
161
|
+
@socket6 = value
|
162
|
+
end
|
163
|
+
|
164
|
+
def socket_ipv4
|
165
|
+
@socket4
|
166
|
+
end
|
167
|
+
|
168
|
+
def socket_ipv4=(value)
|
169
|
+
@socket4 = value
|
170
|
+
end
|
171
|
+
|
172
|
+
# TCP Socket reader
|
173
|
+
def reader(socket)
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
# Main configuration.
|
178
|
+
# See Crap::Configure
|
179
|
+
def config
|
180
|
+
@config
|
181
|
+
end
|
182
|
+
|
183
|
+
def logger=(value)
|
184
|
+
@logger = value
|
185
|
+
end
|
186
|
+
|
187
|
+
def logger
|
188
|
+
if not @logger
|
189
|
+
@logger = Logger.new(config.log_file)
|
190
|
+
@logger.level = @config.log_level
|
191
|
+
end
|
192
|
+
@logger
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module CrapServer
|
2
|
+
class Configure
|
3
|
+
# The port used.
|
4
|
+
# Default: 7331
|
5
|
+
attr_accessor :port
|
6
|
+
# Set to true if you want to manage the read.
|
7
|
+
# Default false
|
8
|
+
attr_accessor :manual_read
|
9
|
+
# Max read buffer size
|
10
|
+
# Default: 16K
|
11
|
+
attr_accessor :read_buffer_size
|
12
|
+
# The number of maximum penning connections.
|
13
|
+
# Default: Max allowed by the OS
|
14
|
+
attr_accessor :max_pending_connections
|
15
|
+
# Working method (read, write, accept, connect).
|
16
|
+
# Available values are:
|
17
|
+
# :normal
|
18
|
+
# :partial
|
19
|
+
# :non_blocking
|
20
|
+
# If :non_blocking is used TODO
|
21
|
+
# Default :readpartial
|
22
|
+
attr_accessor :method
|
23
|
+
# Set to false if you want to manage the close of the connection.
|
24
|
+
# Note that this require manual_read set to true.
|
25
|
+
attr_accessor :auto_close_connection
|
26
|
+
# The file to use as log
|
27
|
+
attr_accessor :log_file
|
28
|
+
# The log level used
|
29
|
+
attr_accessor :log_level
|
30
|
+
def initialize
|
31
|
+
@port = 7331
|
32
|
+
@manual_read = false
|
33
|
+
@read_buffer_size = 1024*16 # 16K for read buffer
|
34
|
+
@max_pending_connections = Socket::SOMAXCONN
|
35
|
+
@method = :partial
|
36
|
+
@auto_close_connection = true
|
37
|
+
@log_file = STDOUT
|
38
|
+
@log_level = Logger::DEBUG
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module CrapServer
|
2
|
+
# This is the class used to bind the block that is passed to run!. Every method defined here is available inside
|
3
|
+
# the run! block
|
4
|
+
class ConnectionInstance
|
5
|
+
attr_accessor :socket
|
6
|
+
attr_accessor :address
|
7
|
+
attr_accessor :config
|
8
|
+
attr_accessor :method
|
9
|
+
def initialize; end
|
10
|
+
|
11
|
+
# This method execute the block sent to run! method
|
12
|
+
def run(&block)
|
13
|
+
# Undefine the last definition if was defined
|
14
|
+
undef :call if self.respond_to? :call
|
15
|
+
# Define the new method to bind the block with this class.
|
16
|
+
self.class.send :define_method, :call, &block
|
17
|
+
# Running the code depending of the number of args
|
18
|
+
if block.parameters.size == 1
|
19
|
+
self.call(read_data)
|
20
|
+
elsif block.parameters.size == 2
|
21
|
+
self.call(read_data, socket)
|
22
|
+
else
|
23
|
+
self.call(read_data, socket, address)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Write to the client the given string
|
28
|
+
def write(string)
|
29
|
+
if @method == :normal or @method == :partial
|
30
|
+
@socket.write(string)
|
31
|
+
elsif @method == :non_blocking
|
32
|
+
@socket.write_nonblock(string)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Give access to logger class to the user
|
37
|
+
def logger
|
38
|
+
@config.logger
|
39
|
+
end
|
40
|
+
protected
|
41
|
+
# Read the data from the socket
|
42
|
+
def read_data
|
43
|
+
# Read the data from the socket
|
44
|
+
if @method == :normal
|
45
|
+
@socket.read(config.read_buffer_size)
|
46
|
+
elsif @method == :partial
|
47
|
+
@socket.readpartial(config.read_buffer_size)
|
48
|
+
elsif @method == :non_blocking
|
49
|
+
@socket.read_nonblock(config.read_buffer_size)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module CrapServer
|
2
|
+
module Helpers
|
3
|
+
# Makes easier work with sockets and read.
|
4
|
+
class SocketReader
|
5
|
+
attr_accessor :method
|
6
|
+
attr_accessor :socket
|
7
|
+
attr_accessor :address
|
8
|
+
attr_accessor :config
|
9
|
+
def initialize(socket_, method_=:partial)
|
10
|
+
@socket = socket_
|
11
|
+
@method = method_
|
12
|
+
end
|
13
|
+
|
14
|
+
def on_message(&block)
|
15
|
+
begin
|
16
|
+
instance = CrapServer::ConnectionInstance.new
|
17
|
+
instance.socket = socket
|
18
|
+
instance.config = config
|
19
|
+
instance.address = address
|
20
|
+
instance.send(:method=, method)
|
21
|
+
instance.run &block
|
22
|
+
# If we get out of data to read (but still having an opened connection), we wait for new data.
|
23
|
+
rescue IO::WaitReadable
|
24
|
+
# This, prevent to execute so many retry and block the code until a new bunch of data gets available
|
25
|
+
IO.select([@socket])
|
26
|
+
# Yay!, we have more data. Now we can continue!
|
27
|
+
retry
|
28
|
+
# When we use non_blocking method, and the client close the connection we will get EOF after that moment
|
29
|
+
# We do nothing special in that moment
|
30
|
+
rescue EOFError
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: crap_server
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andres Joser Borek
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
description: Really thin a non intuitive ruby server and framework. Made to be fast
|
28
|
+
and ready for really heavy servers (not only http server).
|
29
|
+
email:
|
30
|
+
- andres.b.dev@gmail.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- ".gitignore"
|
36
|
+
- Gemfile
|
37
|
+
- LICENSE.txt
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- crap_server.gemspec
|
41
|
+
- lib/crap_server.rb
|
42
|
+
- lib/crap_server/application.rb
|
43
|
+
- lib/crap_server/configure.rb
|
44
|
+
- lib/crap_server/connection_instance.rb
|
45
|
+
- lib/crap_server/helpers/socket_reader.rb
|
46
|
+
- lib/crap_server/version.rb
|
47
|
+
homepage: ''
|
48
|
+
licenses:
|
49
|
+
- MIT
|
50
|
+
metadata: {}
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 2.2.2
|
68
|
+
signing_key:
|
69
|
+
specification_version: 4
|
70
|
+
summary: Really thin a non intuitive ruby server and framework.
|
71
|
+
test_files: []
|