xmlrpc-scgi 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 01932824af6cb128b102b0a984c5c4a2bd9637c4
4
+ data.tar.gz: 12462a5f89c3a5beab9e5ddc9de1cf79a0d656a1
5
+ SHA512:
6
+ metadata.gz: 4b0cf638af945554445636afd32386fd82e622d703bdb1b0ca8eda345a64763771fb22c5c6335428be07b2ba6a18daa69313ac4dc043647abcae1a502db46d6f
7
+ data.tar.gz: 53bc728f0be1b82f5b39bf9745b54035069c8e9774b6a147f9522229d615a851051b1de44a30fbaaaebb2050fe0df3ea515444afde8f9921d317172a202a15b9
@@ -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
@@ -0,0 +1,7 @@
1
+ Metrics/MethodLength:
2
+ Max: 30
3
+ AllCops:
4
+ Exclude:
5
+ - '**/Rakefile'
6
+ - '**/*.gemspec'
7
+
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in scgi-xmlrpc.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 kannibalox
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.
@@ -0,0 +1,52 @@
1
+ # XMLRPC::SCGI
2
+
3
+ [![Build Status](https://travis-ci.org/smuth4/ruby-xmlrpc-scgi.svg)](https://travis-ci.org/smuth4/ruby-xmlrpc-scgi)
4
+
5
+ A small gem to extend the XMLRPC module by adding an SCGI client and server. It also goes the extra mile and adds support for the non-standard `i8` tag.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'xmlrpc-scgi'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install scgi-xmlrpc
22
+
23
+ ## Usage
24
+
25
+ Server
26
+ ```ruby
27
+ require 'xmlrpc/scgi'
28
+
29
+ server = XMLRPC::SCGI.new '127.0.0.1:6000'
30
+ server.add_handler('add') do |a, b|
31
+ a + b
32
+ end
33
+
34
+ # Runs until it is killed
35
+ server.serve
36
+ ```
37
+
38
+ Client
39
+ ```ruby
40
+ require 'xmlrpc/scgi'
41
+
42
+ client = XMLRPC::SCGIClient.new '127.0.0.1', '', 6000
43
+ puts client.call('add', 4, 5)
44
+ ```
45
+
46
+ ## Contributing
47
+
48
+ 1. Fork it ( https://github.com/[my-github-username]/scgi-xmlrpc/fork )
49
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
50
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
51
+ 4. Push to the branch (`git push origin my-new-feature`)
52
+ 5. Create a new Pull Request
@@ -0,0 +1,13 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ task default: %w(rubocop rspec)
4
+
5
+ desc 'Run rubocop'
6
+ task :rubocop do
7
+ sh 'rubocop'
8
+ end
9
+
10
+ desc 'Run RSpec'
11
+ task :rspec do
12
+ sh 'bundle exec rspec spec'
13
+ end
@@ -0,0 +1,122 @@
1
+ require 'pp'
2
+ require 'xmlrpc/server'
3
+ require 'xmlrpc/client'
4
+ require 'xmlrpc/config'
5
+
6
+ require 'xmlrpc/scgi/version'
7
+
8
+ module XMLRPC
9
+ # A simple and naive server for serving XMLRPC over the SCGI protocol
10
+ class SCGIServer < XMLRPC::BasicServer
11
+ def initialize(socket, *a)
12
+ @socket = socket
13
+ super(*a)
14
+ end
15
+
16
+ def serve
17
+ if @socket.include? ':'
18
+ server = TCPServer.new(*@socket.split(':'))
19
+ else
20
+ server = UNIXServer.new(@socket)
21
+ end
22
+ loop do
23
+ begin
24
+ client = server.accept
25
+ _, body = split_request(client)
26
+ client.write write_http("\n#{process(body)}")
27
+ ensure
28
+ # no matter what we have to put this thread on the bad list
29
+ client.close unless client.nil? || client.closed?
30
+ end
31
+ end
32
+ end
33
+
34
+ def write_http(data)
35
+ content = [
36
+ 'Status: 200 OK.',
37
+ 'Content-Type: text/xml',
38
+ "Content-Length: #{data.length}",
39
+ data
40
+ ]
41
+ content.join "\n\r"
42
+ end
43
+
44
+ def read_netstring(handle)
45
+ len = handle.gets(':', 10).sub(':', '')
46
+ payload = handle.read(len.to_i)
47
+ if handle.read(1) == ','
48
+ payload
49
+ else
50
+ fail "Malformed request, does not end with ','"
51
+ end
52
+ end
53
+
54
+ def write_netstring(str)
55
+ "#{str.length}:#{str},"
56
+ end
57
+
58
+ def split_request(socket)
59
+ return if socket.closed?
60
+ payload = read_netstring socket
61
+ request = Hash[*(payload.split("\0"))]
62
+ fail 'Missing CONTENT_LENGTH' unless request['CONTENT_LENGTH']
63
+ length = request['CONTENT_LENGTH'].to_i
64
+ body = length > 0 ? socket.read(length) : ''
65
+ [request, body]
66
+ end
67
+ end
68
+
69
+ # A small derivative class for calling XMLRPC over SCGI
70
+ class SCGIClient < XMLRPC::Client
71
+ def do_rpc(xml, _ = false)
72
+ headers = {
73
+ 'CONTENT_LENGTH' => xml.size,
74
+ 'SCGI' => 1
75
+ }
76
+
77
+ header = "#{headers.to_a.flatten.join("\x00")}"
78
+ request = "#{header.size}:#{header},#{xml}"
79
+
80
+ TCPSocket.open(@host, @port) do |s|
81
+ s.write(request)
82
+ s.read.split(/\n\s*?\n/, 2)[1]
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ module XMLRPC
89
+ module XMLParser
90
+ # Overriding the default XML parser
91
+ class AbstractStreamParser
92
+ # A patch for the response method as rtorrent xmlrpc
93
+ # call returns some integers as i8 instead of i4 expected
94
+ # this results in int8 fields showing up with no data
95
+ # as the parse method is not capable of handling such
96
+ # a tag.
97
+
98
+ alias_method 'original_parseMethodResponse', 'parseMethodResponse'
99
+ def parseMethodResponse(str) # rubocop:disable Style/MethodName
100
+ str.gsub!(%r{<((\/)*)i8>}, '<\1i4>')
101
+ original_parseMethodResponse(str)
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ module XMLRPC
108
+ # Monkey patch the Create class
109
+ class Create
110
+ alias_method 'original_conv2value', 'conv2value'
111
+
112
+ def conv2value(param)
113
+ if ([Fixnum, Bignum].include? param.class) &&
114
+ (param <= -(2**31) || param >= (2**31 - 1))
115
+ cval = @writer.tag('i8', param.to_s)
116
+ @writer.ele('value', cval)
117
+ else
118
+ original_conv2value param
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,6 @@
1
+ module XMLRPC
2
+ # SCGI XMLRPC Component
3
+ module SCGI
4
+ VERSION = '0.0.1'
5
+ end
6
+ end
@@ -0,0 +1,27 @@
1
+ require 'xmlrpc/scgi'
2
+
3
+ describe XMLRPC::SCGIClient do
4
+ before(:all) do
5
+ @server_thread = Thread.new do
6
+ server = XMLRPC::SCGIServer.new '127.0.0.1:6000'
7
+ server.add_handler('add') do |a, b|
8
+ a + b
9
+ end
10
+ server.serve
11
+ end
12
+ @client = XMLRPC::SCGIClient.new '127.0.0.1', '', 6000
13
+ end
14
+
15
+ after(:all) do
16
+ Thread.kill @server_thread
17
+ @server_thread.join
18
+ end
19
+
20
+ it 'should perform calls' do
21
+ expect(@client.call('add', 4, 5)).to be(4 + 5)
22
+ end
23
+
24
+ it 'should handle big ints calls' do
25
+ expect(@client.call('add', 2**(4 * 8), 5)).to be(2**(4 * 8) + 5)
26
+ end
27
+ end
@@ -0,0 +1,67 @@
1
+ require 'xmlrpc/scgi'
2
+
3
+ describe XMLRPC::SCGIServer do
4
+ before(:each) do
5
+ @server = XMLRPC::SCGIServer.new 'fake socket'
6
+ end
7
+
8
+ context '.read_netstring' do
9
+ it 'Valid netstring' do
10
+ expect(@server.read_netstring(StringIO.new('5:hello,'))).to eql('hello')
11
+ end
12
+
13
+ it 'Invalid netstring - missing end comma' do
14
+ expect do
15
+ @server.read_netstring(StringIO.new('5:hello'))
16
+ end.to raise_error(RuntimeError)
17
+ end
18
+
19
+ it 'Invalid netstring - invalid length' do
20
+ expect do
21
+ @server.read_netstring(StringIO.new('53457254754754:hello,'))
22
+ end.to raise_error(RuntimeError)
23
+ end
24
+ end
25
+
26
+ it '.write_netstring' do
27
+ expect(@server.write_netstring('hello')).to eql('5:hello,')
28
+ end
29
+
30
+ context '.split_request' do
31
+ before(:each) do
32
+ @request = [
33
+ 'CONTENT_LENGTH',
34
+ '56',
35
+ 'SCGI',
36
+ '1',
37
+ 'REQUEST_METHOD',
38
+ 'POST',
39
+ 'REQUEST_URI',
40
+ '/deepthought'
41
+ ]
42
+
43
+ @body = 'What is the answer to life, the Universe and everything?'
44
+ end
45
+
46
+ it 'Valid request' do
47
+ netstring = @server.write_netstring("#{@request.join("\0")}")
48
+ expect(@server.split_request(StringIO.new("#{netstring}#{@body}")))
49
+ .to eql([Hash[*@request], @body])
50
+ end
51
+
52
+ it 'Invalid request - short body' do
53
+ netstring = @server.write_netstring("#{@request[0..-3].join("\0")}")
54
+ expect(@server.split_request(StringIO.new("#{netstring}#{@body}")))
55
+ .to_not eql([Hash[*@request], @body])
56
+ end
57
+ end
58
+
59
+ it 'should spool up a server' do
60
+ server_thread = Thread.new do
61
+ server = XMLRPC::SCGIServer.new '127.0.0.1:6000'
62
+ server.serve
63
+ end
64
+ expect(server_thread.status).to be_a(String)
65
+ Thread.kill server_thread
66
+ end
67
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'xmlrpc/scgi/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "xmlrpc-scgi"
8
+ spec.version = XMLRPC::SCGI::VERSION
9
+ spec.authors = ["Stephen Muth"]
10
+ spec.email = ["smuth4@gmail.com"]
11
+ spec.summary = %q{Extend XMLRPC with SCGI client and server.}
12
+ spec.description = %q{Extend XMLRPC with SCGI client and 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.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+
24
+ spec.add_development_dependency "rspec", "~> 3.2"
25
+ spec.add_development_dependency "rubocop", "~> 0.35"
26
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xmlrpc-scgi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Stephen Muth
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-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.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.35'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.35'
69
+ description: Extend XMLRPC with SCGI client and server.
70
+ email:
71
+ - smuth4@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rubocop.yml"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - lib/xmlrpc/scgi.rb
84
+ - lib/xmlrpc/scgi/version.rb
85
+ - spec/client_spec.rb
86
+ - spec/server_spec.rb
87
+ - xmlrpc-scgi.gemspec
88
+ homepage: ''
89
+ licenses:
90
+ - MIT
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.2.2
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: Extend XMLRPC with SCGI client and server.
112
+ test_files:
113
+ - spec/client_spec.rb
114
+ - spec/server_spec.rb