jimson 0.2.3 → 0.3.0

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/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,9 @@
1
+ == 0.3.0 / 2011-08-11
2
+
3
+ * Major enhancements
4
+
5
+ * Replace eventmachine-httpserver with rack for more cross-platform goodness
6
+
1
7
  == 0.2.3 / 2011-08-01
2
8
 
3
9
  * Bug fixes
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.3
1
+ 0.3.0
data/lib/jimson/client.rb CHANGED
@@ -80,8 +80,10 @@ module Jimson
80
80
 
81
81
  return data['result']
82
82
 
83
- rescue Exception, StandardError
84
- raise Jimson::ClientError::InternalError.new($!)
83
+ rescue Jimson::ClientError::Generic, Jimson::ServerError::Generic => e
84
+ raise e
85
+ rescue Exception, StandardError => e
86
+ raise Jimson::ClientError::InternalError.new(e)
85
87
  end
86
88
 
87
89
  def valid_response?(data)
@@ -1,24 +1,27 @@
1
1
  module Jimson
2
2
  module ClientError
3
- class InvalidResponse < Exception
3
+ class Generic < Exception
4
+ end
5
+
6
+ class InvalidResponse < Generic
4
7
  def initialize()
5
8
  super('Invalid or empty response from server.')
6
9
  end
7
10
  end
8
11
 
9
- class InvalidJSON < Exception
12
+ class InvalidJSON < Generic
10
13
  def initialize(json)
11
14
  super("Couldn't parse JSON string received from server:\n#{json}")
12
15
  end
13
16
  end
14
17
 
15
- class InternalError < Exception
18
+ class InternalError < Generic
16
19
  def initialize(e)
17
20
  super("An internal client error occurred when processing the request: #{e}\n#{e.backtrace.join("\n")}")
18
21
  end
19
22
  end
20
23
 
21
- class UnknownServerError < Exception
24
+ class UnknownServerError < Generic
22
25
  def initialize(code, message)
23
26
  super("The server specified an error the client doesn't know about: #{code} #{message}")
24
27
  end
@@ -0,0 +1,138 @@
1
+ require 'jimson/server_error'
2
+
3
+ module Jimson
4
+ class HttpServer
5
+
6
+ JSON_RPC_VERSION = '2.0'
7
+
8
+ def initialize(handler)
9
+ @handler = handler
10
+ end
11
+
12
+ def call(env)
13
+ req = Rack::Request.new(env)
14
+ resp = Rack::Response.new
15
+ return resp.finish if !req.post?
16
+ resp.write process(req.body.read)
17
+ resp.finish
18
+ end
19
+
20
+ def process(content)
21
+ begin
22
+ request = parse_request(content)
23
+ if request.is_a?(Array)
24
+ raise Jimson::ServerError::InvalidRequest.new if request.empty?
25
+ response = request.map { |req| handle_request(req) }
26
+ else
27
+ response = handle_request(request)
28
+ end
29
+ rescue Jimson::ServerError::ParseError, Jimson::ServerError::InvalidRequest => e
30
+ response = error_response(e)
31
+ rescue Jimson::ServerError::Generic => e
32
+ response = error_response(e, request)
33
+ rescue StandardError, Exception
34
+ response = error_response(Jimson::ServerError::InternalError.new)
35
+ end
36
+
37
+ response.compact! if response.is_a?(Array)
38
+
39
+ return nil if response.nil? || (response.respond_to?(:empty?) && response.empty?)
40
+
41
+ response.to_json
42
+ end
43
+
44
+ def handle_request(request)
45
+ response = nil
46
+ begin
47
+ if !validate_request(request)
48
+ response = error_response(Jimson::ServerError::InvalidRequest.new)
49
+ else
50
+ response = create_response(request)
51
+ end
52
+ rescue Jimson::ServerError::Generic => e
53
+ response = error_response(e, request)
54
+ end
55
+
56
+ response
57
+ end
58
+
59
+ def validate_request(request)
60
+ required_keys = %w(jsonrpc method)
61
+ required_types = {
62
+ 'jsonrpc' => [String],
63
+ 'method' => [String],
64
+ 'params' => [Hash, Array],
65
+ 'id' => [String, Fixnum, NilClass]
66
+ }
67
+
68
+ return false if !request.is_a?(Hash)
69
+
70
+ required_keys.each do |key|
71
+ return false if !request.has_key?(key)
72
+ end
73
+
74
+ required_types.each do |key, types|
75
+ return false if request.has_key?(key) && !types.any? { |type| request[key].is_a?(type) }
76
+ end
77
+
78
+ return false if request['jsonrpc'] != JSON_RPC_VERSION
79
+
80
+ true
81
+ end
82
+
83
+ def create_response(request)
84
+ params = request['params']
85
+ begin
86
+ if params.is_a?(Hash)
87
+ result = @handler.send(request['method'], params)
88
+ else
89
+ result = @handler.send(request['method'], *params)
90
+ end
91
+ rescue NoMethodError
92
+ raise Jimson::ServerError::MethodNotFound.new
93
+ rescue ArgumentError
94
+ raise Jimson::ServerError::InvalidParams.new
95
+ rescue
96
+ raise Jimson::ServerError::ApplicationError.new($!)
97
+ end
98
+
99
+ response = success_response(request, result)
100
+
101
+ # A Notification is a Request object without an "id" member.
102
+ # The Server MUST NOT reply to a Notification, including those
103
+ # that are within a batch request.
104
+ response = nil if !request.has_key?('id')
105
+
106
+ response
107
+ end
108
+
109
+ def error_response(error, request = nil)
110
+ resp = {
111
+ 'jsonrpc' => JSON_RPC_VERSION,
112
+ 'error' => error.to_h,
113
+ }
114
+ if !!request && request.has_key?('id')
115
+ resp['id'] = request['id']
116
+ else
117
+ resp['id'] = nil
118
+ end
119
+
120
+ resp
121
+ end
122
+
123
+ def success_response(request, result)
124
+ {
125
+ 'jsonrpc' => JSON_RPC_VERSION,
126
+ 'result' => result,
127
+ 'id' => request['id']
128
+ }
129
+ end
130
+
131
+ def parse_request(post)
132
+ data = JSON.parse(post)
133
+ rescue
134
+ raise Jimson::ServerError::ParseError.new
135
+ end
136
+
137
+ end
138
+ end
data/lib/jimson/server.rb CHANGED
@@ -1,162 +1,41 @@
1
- require 'eventmachine'
2
- require 'evma_httpserver'
3
- require 'logger'
1
+ require 'rack'
2
+ require 'rack/request'
3
+ require 'rack/response'
4
+ require 'rack/showexceptions'
4
5
  require 'json'
5
- require 'jimson/server_error'
6
+ require 'jimson/http_server'
6
7
 
7
8
  module Jimson
8
- class HttpServer < EM::Connection
9
- include EM::HttpServer
10
-
11
- JSON_RPC_VERSION = '2.0'
12
-
13
- def self.handler=(handler)
14
- @@handler = handler
15
- end
16
-
17
- def process_http_request
18
- resp = EM::DelegatedHttpResponse.new( self )
19
- resp.status = 200
20
- resp.content = process_post(@http_post_content)
21
- resp.send_response
22
- end
23
-
24
- def process_post(content)
25
- begin
26
- request = parse_request(@http_post_content)
27
- if request.is_a?(Array)
28
- raise Jimson::ServerError::InvalidRequest.new if request.empty?
29
- response = request.map { |req| handle_request(req) }
30
- else
31
- response = handle_request(request)
32
- end
33
- rescue Jimson::ServerError::ParseError, Jimson::ServerError::InvalidRequest => e
34
- response = error_response(e)
35
- rescue Jimson::ServerError::Generic => e
36
- response = error_response(e, request)
37
- rescue StandardError, Exception
38
- response = error_response(Jimson::ServerError::InternalError.new)
39
- end
40
-
41
- response.compact! if response.is_a?(Array)
42
-
43
- return nil if response.nil? || (response.respond_to?(:empty?) && response.empty?)
44
-
45
- response.to_json
46
- end
47
-
48
- def handle_request(request)
49
- response = nil
50
- begin
51
- if !validate_request(request)
52
- response = error_response(Jimson::ServerError::InvalidRequest.new)
53
- else
54
- response = create_response(request)
55
- end
56
- rescue Jimson::ServerError::Generic => e
57
- response = error_response(e, request)
58
- end
59
-
60
- response
61
- end
62
-
63
- def validate_request(request)
64
- required_keys = %w(jsonrpc method)
65
- required_types = {
66
- 'jsonrpc' => [String],
67
- 'method' => [String],
68
- 'params' => [Hash, Array],
69
- 'id' => [String, Fixnum, NilClass]
70
- }
71
-
72
- return false if !request.is_a?(Hash)
73
-
74
- required_keys.each do |key|
75
- return false if !request.has_key?(key)
76
- end
77
-
78
- required_types.each do |key, types|
79
- return false if request.has_key?(key) && !types.any? { |type| request[key].is_a?(type) }
80
- end
81
-
82
- return false if request['jsonrpc'] != JSON_RPC_VERSION
83
-
84
- true
85
- end
86
-
87
- def create_response(request)
88
- params = request['params']
89
- begin
90
- if params.is_a?(Hash)
91
- result = @@handler.send(request['method'], params)
92
- else
93
- result = @@handler.send(request['method'], *params)
94
- end
95
- rescue NoMethodError
96
- raise Jimson::ServerError::MethodNotFound.new
97
- rescue ArgumentError
98
- raise Jimson::ServerError::InvalidParams.new
99
- rescue
100
- raise Jimson::ServerError::ApplicationError.new($!)
101
- end
102
-
103
- response = success_response(request, result)
104
-
105
- # A Notification is a Request object without an "id" member.
106
- # The Server MUST NOT reply to a Notification, including those
107
- # that are within a batch request.
108
- response = nil if !request.has_key?('id')
109
-
110
- response
111
- end
112
-
113
- def error_response(error, request = nil)
114
- resp = {
115
- 'jsonrpc' => JSON_RPC_VERSION,
116
- 'error' => error.to_h,
117
- }
118
- if !!request && request.has_key?('id')
119
- resp['id'] = request['id']
120
- else
121
- resp['id'] = nil
122
- end
123
-
124
- resp
125
- end
126
-
127
- def success_response(request, result)
128
- {
129
- 'jsonrpc' => JSON_RPC_VERSION,
130
- 'result' => result,
131
- 'id' => request['id']
132
- }
133
- end
134
-
135
- def parse_request(post)
136
- data = JSON.parse(post)
137
- rescue
138
- raise Jimson::ServerError::ParseError.new
139
- end
140
-
141
- end
142
-
143
9
  class Server
144
10
 
145
- attr_accessor :handler, :host, :port, :logger
146
-
147
- def initialize(handler, host = '0.0.0.0', port = 8999, logger = Logger.new(STDOUT))
11
+ attr_accessor :handler, :host, :port
12
+
13
+ #
14
+ # +handler+ is an instance of the class to expose as a JSON-RPC server
15
+ #
16
+ # +opts+ may include:
17
+ # * :host - the hostname or ip to bind to
18
+ # * :port - the port to listen on
19
+ # * :server - the rack handler to use, e.g. 'webrick' or 'thin'
20
+ #
21
+ def initialize(handler, opts = {})
148
22
  @handler = handler
149
- @host = host
150
- @port = port
151
- @logger = logger
23
+ @app = Jimson::HttpServer.new(@handler)
24
+ @host = opts[:host] || '0.0.0.0'
25
+ @port = opts[:port] || 8999
26
+ @server = opts[:server] || 'webrick'
152
27
  end
153
28
 
29
+ #
30
+ # Starts the server so it can process requests
31
+ #
154
32
  def start
155
- Jimson::HttpServer.handler = @handler
156
- EM.run do
157
- EM::start_server(@host, @port, Jimson::HttpServer)
158
- @logger.info("Server listening on #{@host}:#{@port} with handler '#{@handler}'")
159
- end
33
+ Rack::Server.start(
34
+ :server => @server,
35
+ :app => @app,
36
+ :Host => @host,
37
+ :Port => @port
38
+ )
160
39
  end
161
40
 
162
41
  end
File without changes
data/spec/spec_helper.rb CHANGED
@@ -7,7 +7,7 @@ require 'json'
7
7
 
8
8
  SPEC_URL = 'http://localhost:8999'
9
9
 
10
- pid = Process.spawn(File.dirname(__FILE__) + '/em_helper.rb')
10
+ pid = Process.spawn(File.dirname(__FILE__) + '/server_helper.rb')
11
11
 
12
12
  RSpec.configure do |config|
13
13
  config.after(:all) do
metadata CHANGED
@@ -1,71 +1,56 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: jimson
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
4
5
  prerelease:
5
- version: 0.2.3
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Chris Kite
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-08-01 00:00:00 -05:00
14
- default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
12
+ date: 2011-08-11 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
17
15
  name: rest-client
18
- prerelease: false
19
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &17785440 !ruby/object:Gem::Requirement
20
17
  none: false
21
- requirements:
22
- - - ">="
23
- - !ruby/object:Gem::Version
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
24
21
  version: 1.6.3
25
22
  type: :runtime
26
- version_requirements: *id001
27
- - !ruby/object:Gem::Dependency
28
- name: eventmachine
29
23
  prerelease: false
30
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *17785440
25
+ - !ruby/object:Gem::Dependency
26
+ name: json
27
+ requirement: &17780040 !ruby/object:Gem::Requirement
31
28
  none: false
32
- requirements:
33
- - - ">="
34
- - !ruby/object:Gem::Version
35
- version: 0.12.10
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 1.5.1
36
33
  type: :runtime
37
- version_requirements: *id002
38
- - !ruby/object:Gem::Dependency
39
- name: eventmachine_httpserver
40
34
  prerelease: false
41
- requirement: &id003 !ruby/object:Gem::Requirement
35
+ version_requirements: *17780040
36
+ - !ruby/object:Gem::Dependency
37
+ name: rack
38
+ requirement: &17779580 !ruby/object:Gem::Requirement
42
39
  none: false
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: 0.2.1
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '1.3'
47
44
  type: :runtime
48
- version_requirements: *id003
49
- - !ruby/object:Gem::Dependency
50
- name: json
51
45
  prerelease: false
52
- requirement: &id004 !ruby/object:Gem::Requirement
53
- none: false
54
- requirements:
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- version: 1.5.1
58
- type: :runtime
59
- version_requirements: *id004
46
+ version_requirements: *17779580
60
47
  description:
61
48
  email:
62
49
  executables: []
63
-
64
50
  extensions: []
65
-
66
- extra_rdoc_files:
51
+ extra_rdoc_files:
67
52
  - README.rdoc
68
- files:
53
+ files:
69
54
  - VERSION
70
55
  - LICENSE.txt
71
56
  - CHANGELOG.rdoc
@@ -77,41 +62,38 @@ files:
77
62
  - lib/jimson/server_error.rb
78
63
  - lib/jimson/response.rb
79
64
  - lib/jimson/request.rb
65
+ - lib/jimson/http_server.rb
80
66
  - lib/jimson/client.rb
81
67
  - spec/server_spec.rb
82
68
  - spec/client_spec.rb
83
- - spec/em_helper.rb
69
+ - spec/server_helper.rb
84
70
  - spec/spec_helper.rb
85
- has_rdoc: true
86
71
  homepage: http://www.github.com/chriskite/jimson
87
72
  licenses: []
88
-
89
73
  post_install_message:
90
74
  rdoc_options: []
91
-
92
- require_paths:
75
+ require_paths:
93
76
  - lib
94
- required_ruby_version: !ruby/object:Gem::Requirement
77
+ required_ruby_version: !ruby/object:Gem::Requirement
95
78
  none: false
96
- requirements:
97
- - - ">="
98
- - !ruby/object:Gem::Version
99
- version: "0"
100
- required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
84
  none: false
102
- requirements:
103
- - - ">="
104
- - !ruby/object:Gem::Version
105
- version: "0"
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
106
89
  requirements: []
107
-
108
90
  rubyforge_project:
109
- rubygems_version: 1.6.2
91
+ rubygems_version: 1.8.6
110
92
  signing_key:
111
93
  specification_version: 3
112
94
  summary: JSON-RPC 2.0 client and server
113
- test_files:
95
+ test_files:
114
96
  - spec/server_spec.rb
115
97
  - spec/client_spec.rb
116
- - spec/em_helper.rb
98
+ - spec/server_helper.rb
117
99
  - spec/spec_helper.rb