gserver 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ac2ef003f2e6a480149eacf3fc3af0ad854d1d23
4
+ data.tar.gz: 89528c80a5bb9ec990abd98454de7bbe8fe11c7b
5
+ SHA512:
6
+ metadata.gz: 03cfeb39d1a7dea2643af0411c898c889f4d204ef7dee078fa6fa51422d0e33f9d60aa0ada165761411d8272b7e05e72144f727d2042f9034bd642dcb3abd087
7
+ data.tar.gz: c2fe1aae9435dcd7210306b0dc9587870c90e62dfabb390497637d2fd0ef260d3bf418e738d4d01fc644af24e2c5838c030b93a8db7c0dd574d7d4854b965abe
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in gserver.gemspec
4
+ gemspec
@@ -0,0 +1,56 @@
1
+ Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
2
+ You can redistribute it and/or modify it under either the terms of the
3
+ 2-clause BSDL (see the file BSDL), or the conditions below:
4
+
5
+ 1. You may make and give away verbatim copies of the source form of the
6
+ software without restriction, provided that you duplicate all of the
7
+ original copyright notices and associated disclaimers.
8
+
9
+ 2. You may modify your copy of the software in any way, provided that
10
+ you do at least ONE of the following:
11
+
12
+ a) place your modifications in the Public Domain or otherwise
13
+ make them Freely Available, such as by posting said
14
+ modifications to Usenet or an equivalent medium, or by allowing
15
+ the author to include your modifications in the software.
16
+
17
+ b) use the modified software only within your corporation or
18
+ organization.
19
+
20
+ c) give non-standard binaries non-standard names, with
21
+ instructions on where to get the original software distribution.
22
+
23
+ d) make other distribution arrangements with the author.
24
+
25
+ 3. You may distribute the software in object code or binary form,
26
+ provided that you do at least ONE of the following:
27
+
28
+ a) distribute the binaries and library files of the software,
29
+ together with instructions (in the manual page or equivalent)
30
+ on where to get the original distribution.
31
+
32
+ b) accompany the distribution with the machine-readable source of
33
+ the software.
34
+
35
+ c) give non-standard binaries non-standard names, with
36
+ instructions on where to get the original software distribution.
37
+
38
+ d) make other distribution arrangements with the author.
39
+
40
+ 4. You may modify and include the part of the software into any other
41
+ software (possibly commercial). But some files in the distribution
42
+ are not written by the author, so that they are not under these terms.
43
+
44
+ For the list of those files and their copying conditions, see the
45
+ file LEGAL.
46
+
47
+ 5. The scripts and library files supplied as input to or produced as
48
+ output from the software do not automatically fall under the
49
+ copyright of the software, but belong to whomever generated them,
50
+ and may be sold commercially, and may be aggregated with this
51
+ software.
52
+
53
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
54
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
55
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
56
+ PURPOSE.
@@ -0,0 +1,71 @@
1
+ # Gserver
2
+
3
+ GServer implements a generic server, featuring thread pool management,
4
+ simple logging, and multi-server management. See HttpServer in
5
+ <tt>sample/xmlrpc.rb</tt> in the Ruby standard library for an example of
6
+ GServer in action.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'gserver'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install gserver
23
+
24
+ ## Usage
25
+
26
+ Using GServer is simple. Below we implement a simple time server, run it,
27
+ query it, and shut it down. Try this code in +irb+:
28
+
29
+ require 'gserver'
30
+
31
+ #
32
+ # A server that returns the time in seconds since 1970.
33
+ #
34
+ class TimeServer < GServer
35
+ def initialize(port=10001, *args)
36
+ super(port, *args)
37
+ end
38
+ def serve(io)
39
+ io.puts(Time.now.to_i)
40
+ end
41
+ end
42
+
43
+ # Run the server with logging enabled (it's a separate thread).
44
+ server = TimeServer.new
45
+ server.audit = true # Turn logging on.
46
+ server.start
47
+
48
+ # *** Now point your browser to http://localhost:10001 to see it working ***
49
+
50
+ # See if it's still running.
51
+ GServer.in_service?(10001) # -> true
52
+ server.stopped? # -> false
53
+
54
+ # Shut the server down gracefully.
55
+ server.shutdown
56
+
57
+ # Alternatively, stop it immediately.
58
+ GServer.stop(10001)
59
+ # or, of course, "server.stop".
60
+
61
+ All the business of accepting connections and exception handling is taken
62
+ care of. All we have to do is implement the method that actually serves the
63
+ client.
64
+
65
+ ## Contributing
66
+
67
+ 1. Fork it ( https://github.com/[my-github-username]/gserver/fork )
68
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
69
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
70
+ 4. Push to the branch (`git push origin my-new-feature`)
71
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'gserver/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "gserver"
8
+ spec.version = Gserver::VERSION
9
+ spec.authors = ["John W. Small", "SHIBATA Hiroshi"]
10
+ spec.email = ["hsbt@ruby-lang.org"]
11
+ spec.summary = %q{GServer implements a generic server}
12
+ spec.description = %q{GServer implements a generic server}
13
+ spec.homepage = ""
14
+ spec.license = "Ruby"
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.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ end
@@ -0,0 +1,310 @@
1
+ #
2
+ # Copyright (C) 2001 John W. Small All Rights Reserved
3
+ #
4
+ # Author:: John W. Small
5
+ # Documentation:: Gavin Sinclair
6
+ # Licence:: Ruby License
7
+
8
+ require "socket"
9
+ require "thread"
10
+
11
+ #
12
+ # GServer implements a generic server, featuring thread pool management,
13
+ # simple logging, and multi-server management. See HttpServer in
14
+ # <tt>xmlrpc/httpserver.rb</tt> in the Ruby standard library for an example of
15
+ # GServer in action.
16
+ #
17
+ # Any kind of application-level server can be implemented using this class.
18
+ # It accepts multiple simultaneous connections from clients, up to an optional
19
+ # maximum number. Several _services_ (i.e. one service per TCP port) can be
20
+ # run simultaneously, and stopped at any time through the class method
21
+ # <tt>GServer.stop(port)</tt>. All the threading issues are handled, saving
22
+ # you the effort. All events are optionally logged, but you can provide your
23
+ # own event handlers if you wish.
24
+ #
25
+ # == Example
26
+ #
27
+ # Using GServer is simple. Below we implement a simple time server, run it,
28
+ # query it, and shut it down. Try this code in +irb+:
29
+ #
30
+ # require 'gserver'
31
+ #
32
+ # #
33
+ # # A server that returns the time in seconds since 1970.
34
+ # #
35
+ # class TimeServer < GServer
36
+ # def initialize(port=10001, *args)
37
+ # super(port, *args)
38
+ # end
39
+ # def serve(io)
40
+ # io.puts(Time.now.to_i)
41
+ # end
42
+ # end
43
+ #
44
+ # # Run the server with logging enabled (it's a separate thread).
45
+ # server = TimeServer.new
46
+ # server.audit = true # Turn logging on.
47
+ # server.start
48
+ #
49
+ # # *** Now point your browser to http://localhost:10001 to see it working ***
50
+ #
51
+ # # See if it's still running.
52
+ # GServer.in_service?(10001) # -> true
53
+ # server.stopped? # -> false
54
+ #
55
+ # # Shut the server down gracefully.
56
+ # server.shutdown
57
+ #
58
+ # # Alternatively, stop it immediately.
59
+ # GServer.stop(10001)
60
+ # # or, of course, "server.stop".
61
+ #
62
+ # All the business of accepting connections and exception handling is taken
63
+ # care of. All we have to do is implement the method that actually serves the
64
+ # client.
65
+ #
66
+ # === Advanced
67
+ #
68
+ # As the example above shows, the way to use GServer is to subclass it to
69
+ # create a specific server, overriding the +serve+ method. You can override
70
+ # other methods as well if you wish, perhaps to collect statistics, or emit
71
+ # more detailed logging.
72
+ #
73
+ # * #connecting
74
+ # * #disconnecting
75
+ # * #starting
76
+ # * #stopping
77
+ #
78
+ # The above methods are only called if auditing is enabled, via #audit=.
79
+ #
80
+ # You can also override #log and #error if, for example, you wish to use a
81
+ # more sophisticated logging system.
82
+ #
83
+ class GServer
84
+
85
+ DEFAULT_HOST = "127.0.0.1"
86
+
87
+ def serve(io)
88
+ end
89
+
90
+ @@services = {} # Hash of opened ports, i.e. services
91
+ @@servicesMutex = Mutex.new
92
+
93
+ # Stop the server running on the given port, bound to the given host
94
+ #
95
+ # +port+:: port, as a Fixnum, of the server to stop
96
+ # +host+:: host on which to find the server to stop
97
+ def GServer.stop(port, host = DEFAULT_HOST)
98
+ @@servicesMutex.synchronize {
99
+ @@services[host][port].stop
100
+ }
101
+ end
102
+
103
+ # Check if a server is running on the given port and host
104
+ #
105
+ # +port+:: port, as a Fixnum, of the server to check
106
+ # +host+:: host on which to find the server to check
107
+ #
108
+ # Returns true if a server is running on that port and host.
109
+ def GServer.in_service?(port, host = DEFAULT_HOST)
110
+ @@services.has_key?(host) and
111
+ @@services[host].has_key?(port)
112
+ end
113
+
114
+ # Stop the server
115
+ def stop
116
+ @connectionsMutex.synchronize {
117
+ if @tcpServerThread
118
+ @tcpServerThread.raise "stop"
119
+ end
120
+ }
121
+ end
122
+
123
+ # Returns true if the server has stopped.
124
+ def stopped?
125
+ @tcpServerThread == nil
126
+ end
127
+
128
+ # Schedule a shutdown for the server
129
+ def shutdown
130
+ @shutdown = true
131
+ end
132
+
133
+ # Return the current number of connected clients
134
+ def connections
135
+ @connections.size
136
+ end
137
+
138
+ # Join with the server thread
139
+ def join
140
+ @tcpServerThread.join if @tcpServerThread
141
+ end
142
+
143
+ # Port on which to listen, as a Fixnum
144
+ attr_reader :port
145
+ # Host on which to bind, as a String
146
+ attr_reader :host
147
+ # Maximum number of connections to accept at a time, as a Fixnum
148
+ attr_reader :maxConnections
149
+ # IO Device on which log messages should be written
150
+ attr_accessor :stdlog
151
+ # Set to true to cause the callbacks #connecting, #disconnecting, #starting,
152
+ # and #stopping to be called during the server's lifecycle
153
+ attr_accessor :audit
154
+ # Set to true to show more detailed logging
155
+ attr_accessor :debug
156
+
157
+ # Called when a client connects, if auditing is enabled.
158
+ #
159
+ # +client+:: a TCPSocket instance representing the client that connected
160
+ #
161
+ # Return true to allow this client to connect, false to prevent it.
162
+ def connecting(client)
163
+ addr = client.peeraddr
164
+ log("#{self.class} #{@host}:#{@port} client:#{addr[1]} " +
165
+ "#{addr[2]}<#{addr[3]}> connect")
166
+ true
167
+ end
168
+
169
+
170
+ # Called when a client disconnects, if audition is enabled.
171
+ #
172
+ # +clientPort+:: the port of the client that is connecting
173
+ def disconnecting(clientPort)
174
+ log("#{self.class} #{@host}:#{@port} " +
175
+ "client:#{clientPort} disconnect")
176
+ end
177
+
178
+ protected :connecting, :disconnecting
179
+
180
+ # Called when the server is starting up, if auditing is enabled.
181
+ def starting()
182
+ log("#{self.class} #{@host}:#{@port} start")
183
+ end
184
+
185
+ # Called when the server is shutting down, if auditing is enabled.
186
+ def stopping()
187
+ log("#{self.class} #{@host}:#{@port} stop")
188
+ end
189
+
190
+ protected :starting, :stopping
191
+
192
+ # Called if #debug is true whenever an unhandled exception is raised.
193
+ # This implementation simply logs the backtrace.
194
+ #
195
+ # +detail+:: the Exception that was caught
196
+ def error(detail)
197
+ log(detail.backtrace.join("\n"))
198
+ end
199
+
200
+ # Log a message to #stdlog, if it's defined. This implementation
201
+ # outputs the timestamp and message to the log.
202
+ #
203
+ # +msg+:: the message to log
204
+ def log(msg)
205
+ if @stdlog
206
+ @stdlog.puts("[#{Time.new.ctime}] %s" % msg)
207
+ @stdlog.flush
208
+ end
209
+ end
210
+
211
+ protected :error, :log
212
+
213
+ # Create a new server
214
+ #
215
+ # +port+:: the port, as a Fixnum, on which to listen
216
+ # +host+:: the host to bind to
217
+ # +maxConnections+:: the maximum number of simultaneous connections to
218
+ # accept
219
+ # +stdlog+:: IO device on which to log messages
220
+ # +audit+:: if true, lifecycle callbacks will be called. See #audit
221
+ # +debug+:: if true, error messages are logged. See #debug
222
+ def initialize(port, host = DEFAULT_HOST, maxConnections = 4,
223
+ stdlog = $stderr, audit = false, debug = false)
224
+ @tcpServerThread = nil
225
+ @port = port
226
+ @host = host
227
+ @maxConnections = maxConnections
228
+ @connections = []
229
+ @connectionsMutex = Mutex.new
230
+ @connectionsCV = ConditionVariable.new
231
+ @stdlog = stdlog
232
+ @audit = audit
233
+ @debug = debug
234
+ end
235
+
236
+ # Start the server if it isn't already running
237
+ #
238
+ # +maxConnections+::
239
+ # override +maxConnections+ given to the constructor. A negative
240
+ # value indicates that the value from the constructor should be used.
241
+ def start(maxConnections = -1)
242
+ raise "server is already running" if !stopped?
243
+ @shutdown = false
244
+ @maxConnections = maxConnections if maxConnections > 0
245
+ @@servicesMutex.synchronize {
246
+ if GServer.in_service?(@port,@host)
247
+ raise "Port already in use: #{host}:#{@port}!"
248
+ end
249
+ @tcpServer = TCPServer.new(@host,@port)
250
+ @port = @tcpServer.addr[1]
251
+ @@services[@host] = {} unless @@services.has_key?(@host)
252
+ @@services[@host][@port] = self;
253
+ }
254
+ @tcpServerThread = Thread.new {
255
+ begin
256
+ starting if @audit
257
+ while !@shutdown
258
+ @connectionsMutex.synchronize {
259
+ while @connections.size >= @maxConnections
260
+ @connectionsCV.wait(@connectionsMutex)
261
+ end
262
+ }
263
+ client = @tcpServer.accept
264
+ Thread.new(client) { |myClient|
265
+ @connections << Thread.current
266
+ begin
267
+ myPort = myClient.peeraddr[1]
268
+ serve(myClient) if !@audit or connecting(myClient)
269
+ rescue => detail
270
+ error(detail) if @debug
271
+ ensure
272
+ begin
273
+ myClient.close
274
+ rescue
275
+ end
276
+ @connectionsMutex.synchronize {
277
+ @connections.delete(Thread.current)
278
+ @connectionsCV.signal
279
+ }
280
+ disconnecting(myPort) if @audit
281
+ end
282
+ }
283
+ end
284
+ rescue => detail
285
+ error(detail) if @debug
286
+ ensure
287
+ begin
288
+ @tcpServer.close
289
+ rescue
290
+ end
291
+ if @shutdown
292
+ @connectionsMutex.synchronize {
293
+ while @connections.size > 0
294
+ @connectionsCV.wait(@connectionsMutex)
295
+ end
296
+ }
297
+ else
298
+ @connections.each { |c| c.raise "stop" }
299
+ end
300
+ @tcpServerThread = nil
301
+ @@servicesMutex.synchronize {
302
+ @@services[@host].delete(@port)
303
+ }
304
+ stopping if @audit
305
+ end
306
+ }
307
+ self
308
+ end
309
+
310
+ end
@@ -0,0 +1,3 @@
1
+ module Gserver
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,173 @@
1
+ # Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
2
+ #
3
+ # $Id$
4
+ #
5
+
6
+
7
+ require "gserver"
8
+
9
+ # Implements a simple HTTP-server by using John W. Small's (jsmall@laser.net)
10
+ # ruby-generic-server: GServer.
11
+ class HttpServer < GServer
12
+
13
+ ##
14
+ # +handle_obj+ specifies the object, that receives calls from +request_handler+
15
+ # and +ip_auth_handler+
16
+ def initialize(handle_obj, port = 8080, host = DEFAULT_HOST, maxConnections = 4,
17
+ stdlog = $stdout, audit = true, debug = true)
18
+ @handler = handle_obj
19
+ super(port, host, maxConnections, stdlog, audit, debug)
20
+ end
21
+
22
+ private
23
+
24
+ CRLF = "\r\n"
25
+ HTTP_PROTO = "HTTP/1.0"
26
+ SERVER_NAME = "HttpServer (Ruby #{RUBY_VERSION})"
27
+
28
+ # Default header for the server name
29
+ DEFAULT_HEADER = {
30
+ "Server" => SERVER_NAME
31
+ }
32
+
33
+ # Mapping of status codes and error messages
34
+ StatusCodeMapping = {
35
+ 200 => "OK",
36
+ 400 => "Bad Request",
37
+ 403 => "Forbidden",
38
+ 405 => "Method Not Allowed",
39
+ 411 => "Length Required",
40
+ 500 => "Internal Server Error"
41
+ }
42
+
43
+ class Request
44
+ attr_reader :data, :header, :method, :path, :proto
45
+
46
+ def initialize(data, method=nil, path=nil, proto=nil)
47
+ @header, @data = Table.new, data
48
+ @method, @path, @proto = method, path, proto
49
+ end
50
+
51
+ def content_length
52
+ len = @header['Content-Length']
53
+ return nil if len.nil?
54
+ return len.to_i
55
+ end
56
+
57
+ end
58
+
59
+ class Response
60
+ attr_reader :header
61
+ attr_accessor :body, :status, :status_message
62
+
63
+ def initialize(status=200)
64
+ @status = status
65
+ @status_message = nil
66
+ @header = Table.new
67
+ end
68
+ end
69
+
70
+ # A case-insensitive Hash class for HTTP header
71
+ class Table
72
+ include Enumerable
73
+
74
+ def initialize(hash={})
75
+ @hash = hash
76
+ update(hash)
77
+ end
78
+
79
+ def [](key)
80
+ @hash[key.to_s.capitalize]
81
+ end
82
+
83
+ def []=(key, value)
84
+ @hash[key.to_s.capitalize] = value
85
+ end
86
+
87
+ def update(hash)
88
+ hash.each {|k,v| self[k] = v}
89
+ self
90
+ end
91
+
92
+ def each
93
+ @hash.each {|k,v| yield k.capitalize, v }
94
+ end
95
+
96
+ # Output the Hash table for the HTTP header
97
+ def writeTo(port)
98
+ each { |k,v| port << "#{k}: #{v}" << CRLF }
99
+ end
100
+ end # class Table
101
+
102
+
103
+ # Generates a Hash with the HTTP headers
104
+ def http_header(header=nil) # :doc:
105
+ new_header = Table.new(DEFAULT_HEADER)
106
+ new_header.update(header) unless header.nil?
107
+
108
+ new_header["Connection"] = "close"
109
+ new_header["Date"] = http_date(Time.now)
110
+
111
+ new_header
112
+ end
113
+
114
+ # Returns a string which represents the time as rfc1123-date of HTTP-date
115
+ def http_date( aTime ) # :doc:
116
+ aTime.gmtime.strftime( "%a, %d %b %Y %H:%M:%S GMT" )
117
+ end
118
+
119
+ # Returns a string which includes the status code message as,
120
+ # http headers, and body for the response.
121
+ def http_resp(status_code, status_message=nil, header=nil, body=nil) # :doc:
122
+ status_message ||= StatusCodeMapping[status_code]
123
+
124
+ str = ""
125
+ str << "#{HTTP_PROTO} #{status_code} #{status_message}" << CRLF
126
+ http_header(header).writeTo(str)
127
+ str << CRLF
128
+ str << body unless body.nil?
129
+ str
130
+ end
131
+
132
+ # Handles the HTTP request and writes the response back to the client, +io+.
133
+ #
134
+ # If an Exception is raised while handling the request, the client will receive
135
+ # a 500 "Internal Server Error" message.
136
+ def serve(io) # :doc:
137
+ # perform IP authentication
138
+ unless @handler.ip_auth_handler(io)
139
+ io << http_resp(403, "Forbidden")
140
+ return
141
+ end
142
+
143
+ # parse first line
144
+ if io.gets =~ /^(\S+)\s+(\S+)\s+(\S+)/
145
+ request = Request.new(io, $1, $2, $3)
146
+ else
147
+ io << http_resp(400, "Bad Request")
148
+ return
149
+ end
150
+
151
+ # parse HTTP headers
152
+ while (line=io.gets) !~ /^(\n|\r)/
153
+ if line =~ /^([\w-]+):\s*(.*)$/
154
+ request.header[$1] = $2.strip
155
+ end
156
+ end
157
+
158
+ io.binmode
159
+ response = Response.new
160
+
161
+ # execute request handler
162
+ @handler.request_handler(request, response)
163
+
164
+ # write response back to the client
165
+ io << http_resp(response.status, response.status_message,
166
+ response.header, response.body)
167
+
168
+ rescue Exception
169
+ io << http_resp(500, "Internal Server Error")
170
+ end
171
+
172
+ end # class HttpServer
173
+
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gserver
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - John W. Small
8
+ - SHIBATA Hiroshi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-08-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.7'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.7'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ description: GServer implements a generic server
43
+ email:
44
+ - hsbt@ruby-lang.org
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - gserver.gemspec
55
+ - lib/gserver.rb
56
+ - lib/gserver/version.rb
57
+ - sample/xmlrpc.rb
58
+ homepage: ''
59
+ licenses:
60
+ - Ruby
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 2.4.1
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: GServer implements a generic server
82
+ test_files: []
83
+ has_rdoc: