hoth 0.3.0 → 0.3.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,45 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ hoth (0.3.0)
5
+ activesupport
6
+ bertrpc
7
+ json
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ activesupport (3.0.3)
13
+ bert (1.1.2)
14
+ bertrpc (1.3.0)
15
+ bert (>= 1.1.0, < 2.0.0)
16
+ diff-lcs (1.1.2)
17
+ eventmachine (0.12.10)
18
+ json (1.4.6)
19
+ memcache-client (1.8.5)
20
+ rspec (2.4.0)
21
+ rspec-core (~> 2.4.0)
22
+ rspec-expectations (~> 2.4.0)
23
+ rspec-mocks (~> 2.4.0)
24
+ rspec-core (2.4.0)
25
+ rspec-expectations (2.4.0)
26
+ diff-lcs (~> 1.1.2)
27
+ rspec-mocks (2.4.0)
28
+ simple_publisher (0.1.1)
29
+ starling
30
+ system_timer
31
+ starling (0.10.1)
32
+ eventmachine (>= 0.12.0)
33
+ memcache-client (>= 1.7.0)
34
+ system_timer (1.0)
35
+
36
+ PLATFORMS
37
+ ruby
38
+
39
+ DEPENDENCIES
40
+ activesupport
41
+ bertrpc
42
+ hoth!
43
+ json
44
+ rspec
45
+ simple_publisher
@@ -18,6 +18,8 @@ require 'hoth/util/logger'
18
18
  require 'hoth/extension/core/exception'
19
19
  require 'hoth/exceptions'
20
20
 
21
+ require 'digest/sha1'
22
+
21
23
  module Hoth
22
24
 
23
25
  class <<self
@@ -25,6 +27,11 @@ module Hoth
25
27
  load_service_definition
26
28
  load_module_definition
27
29
  Logger.init_logging!
30
+ @client_uuid = Digest::SHA1.hexdigest("Time.now--#{rand()}")
31
+ end
32
+
33
+ def client_uuid
34
+ @client_uuid
28
35
  end
29
36
 
30
37
  def config_path
@@ -11,4 +11,11 @@ module Hoth
11
11
 
12
12
  class TransportError < HothException; end
13
13
  class TransportException < HothException; end
14
+
15
+ class EncodingError < HothException; end
16
+
17
+ class EmptyServiceNameError < HothException; end
18
+ class ServiceNotFoundException < HothException; end
19
+
20
+ class RecursiveServiceCallException < HothException; end
14
21
  end
@@ -4,8 +4,8 @@ module Hoth
4
4
  module Providers
5
5
  class RackProvider
6
6
 
7
- def initialize(app)
8
- @app = app
7
+ def initialize(app=nil)
8
+ @app = app || lambda {|env| [404, {'Content-Type' => "text/plain"}, ["Nothing here!"]]}
9
9
  end
10
10
 
11
11
  def call(env)
@@ -13,33 +13,42 @@ module Hoth
13
13
  if env["PATH_INFO"] =~ /^\/execute/
14
14
  begin
15
15
  req = Rack::Request.new(env)
16
+ Hoth::Logger.debug "req: #{req.inspect}"
16
17
 
17
18
  service_name = req.params["name"]
18
19
  service_params = req.params["params"]
19
-
20
+
21
+ raise EmptyServiceNameError.new("You must provide a service name!") if service_name.nil?
22
+
20
23
  responsible_service = ServiceRegistry.locate_service(service_name)
21
-
24
+
25
+ raise ServiceNotFoundException.new("The requested service '#{service_name}' was not found!") if responsible_service.nil?
26
+
27
+ # check if we have called ourself
28
+ raise RecursiveServiceCallException.new("Service caller and callee have the same Client-UUID!") if req.params["caller_uuid"] == Hoth.client_uuid
29
+
22
30
  decoded_params = responsible_service.transport.encoder.decode(service_params)
23
31
  result = Hoth::Services.send(service_name, *decoded_params)
24
-
32
+
25
33
  encoded_result = responsible_service.transport.encoder.encode({"result" => result})
26
-
34
+
27
35
  [200, {'Content-Type' => responsible_service.transport.encoder.content_type, 'Content-Length' => "#{encoded_result.length}"}, [encoded_result]]
28
36
  rescue Exception => e
29
- Hoth::Logger.debug "e: #{e.message}"
37
+ Hoth::Logger.error "e: #{e.message}"
38
+
30
39
  if responsible_service
31
40
  encoded_error = responsible_service.transport.encoder.encode({"error" => e})
32
- [500, {'Content-Type' => service.transport.encoder.content_type, 'Content-Length' => "#{encoded_error.length}"}, [encoded_error]]
41
+ [500, {'Content-Type' => responsible_service.transport.encoder.content_type, 'Content-Length' => "#{encoded_error.length}"}, [encoded_error]]
33
42
  else
34
- plain_error = "An error occuered! (#{e.message})"
35
- [500, {'Content-Type' => "text/plain", 'Content-Length' => "#{plain_error.length}"}, [plain_error]]
43
+ plain_error = "An error occurred! (#{e.message})"
44
+ [e.class == ServiceNotFoundException ? 404 : 500, {'Content-Type' => "text/plain", 'Content-Length' => "#{plain_error.length}"}, [plain_error]]
36
45
  end
37
46
  end
38
47
  else
39
48
  @app.call(env)
40
49
  end
41
50
  end
42
-
51
+
43
52
  end
44
53
  end
45
54
  end
@@ -1,5 +1,6 @@
1
1
  require 'hoth/transport/base'
2
2
  require 'hoth/transport/http'
3
+ require 'hoth/transport/https'
3
4
  require 'hoth/transport/bert'
4
5
  require 'hoth/transport/workling'
5
6
 
@@ -15,6 +16,13 @@ module Hoth
15
16
  :encoder => Encoding::Json
16
17
  },
17
18
  :http => :json_via_http,
19
+
20
+ :json_via_https => {
21
+ :transport_class => Transport::Https,
22
+ :encoder => Encoding::Json
23
+ },
24
+ :https => :json_via_https,
25
+
18
26
  :workling => {
19
27
  :transport_class => Transport::Workling
20
28
  }
@@ -4,14 +4,10 @@ module Hoth
4
4
  module Transport
5
5
  class Http < Base
6
6
  def call_remote_with(*params)
7
- unless return_nothing?
8
- begin
9
- handle_response post_payload(params)
10
- rescue Exception => e
11
- raise TransportError.wrap(e)
12
- end
13
- else
14
- return nil
7
+ begin
8
+ handle_response post_payload(params)
9
+ rescue Exception => e
10
+ raise TransportError.wrap(e)
15
11
  end
16
12
  end
17
13
 
@@ -35,8 +31,9 @@ module Hoth
35
31
  def post_payload(payload)
36
32
  uri = URI.parse(self.endpoint.to_url)
37
33
  return Net::HTTP.post_form(uri,
38
- 'name' => self.name.to_s,
39
- 'params' => encoder.encode(payload)
34
+ 'name' => self.name.to_s,
35
+ 'caller_uuid' => Hoth.client_uuid,
36
+ 'params' => encoder.encode(payload)
40
37
  )
41
38
  end
42
39
 
@@ -0,0 +1,31 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+
4
+ module Hoth
5
+ module Transport
6
+ class Https < Http
7
+ def post_payload(payload)
8
+ uri = URI.parse(self.endpoint.to_url)
9
+
10
+ post = Net::HTTP::Post.new(uri.path)
11
+
12
+ post.set_form_data({
13
+ 'name' => self.name.to_s,
14
+ 'caller_uuid' => Hoth.client_uuid,
15
+ 'params' => encoder.encode(payload)
16
+ }, ';')
17
+
18
+ request = Net::HTTP.new(uri.host, uri.port)
19
+ request.use_ssl = true
20
+ response = request.start {|http| http.request(post) }
21
+
22
+ case response
23
+ when Net::HTTPSuccess, Net::HTTPRedirection
24
+ response
25
+ else
26
+ response.error!
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,46 +1,43 @@
1
1
  module Hoth
2
2
  class Logger
3
3
  class <<self
4
-
4
+
5
5
  def log_provider=(log_provider)
6
6
  @log_provider = log_provider
7
7
  end
8
-
9
- def init_logging!
8
+
9
+ def init_logging!(logfile="log/hoth.log")
10
10
  Hoth::Logger.log_provider = if Object.const_defined?("Rails")
11
11
  Rails.logger
12
12
  else
13
13
  require 'logger'
14
- ::Logger.new("/tmp/hoth.log")
14
+ ::Logger.new(logfile)
15
15
  end
16
16
  end
17
-
17
+
18
18
  def debug(msg)
19
19
  log_provider.debug msg
20
20
  end
21
-
21
+
22
22
  def info(msg)
23
23
  log_provider.info msg
24
24
  end
25
-
25
+
26
26
  def warn(msg)
27
27
  log_provider.warn msg
28
28
  end
29
-
29
+
30
30
  def error(msg)
31
31
  log_provider.error msg
32
32
  end
33
-
33
+
34
34
  def fatal(msg)
35
35
  log_provider.fatal msg
36
36
  end
37
-
38
- private
39
-
40
- def log_provider
41
- @log_provider || init_logging!
42
- end
37
+
38
+ def log_provider
39
+ @log_provider || init_logging!
40
+ end
43
41
  end
44
-
45
42
  end
46
43
  end
@@ -10,6 +10,7 @@ module Hoth
10
10
  describe RackProvider do
11
11
 
12
12
  it "should get transport and encoder based on called service" do
13
+ Hoth.should_receive(:client_uuid).and_return("CALLER_UUID")
13
14
  app = stub("ApplicationStub").as_null_object
14
15
  middleware = Hoth::Providers::RackProvider.new(app)
15
16
 
@@ -40,10 +41,10 @@ module Hoth
40
41
  rack_response = middleware.call env
41
42
  rack_response.first.should == 500 #status code
42
43
  rack_response.last.should be_a_kind_of(Array)
43
- rack_response.last.first.should == "An error occuered! (RuntimeError)"
44
+ rack_response.last.first.should == "An error occurred! (RuntimeError)"
44
45
  end
45
46
 
46
47
  end
47
48
 
48
49
  end
49
- end
50
+ end
@@ -42,7 +42,16 @@ module Hoth
42
42
  service.should_receive(:is_local?).and_return(true)
43
43
  service.impl_class.should_receive(:execute).with(:arg1, :arg2)
44
44
 
45
- service.execute(:arg1, :arg2)
45
+ service.execute(:arg1, :arg2).should be(nil)
46
+ end
47
+
48
+ it "should execute the service stub locally if its impl-class was found and return a value" do
49
+ service = Service.new("test_service") { |p1, p2| returns :value }
50
+
51
+ service.should_receive(:is_local?).and_return(true)
52
+ service.impl_class.should_receive(:execute).with(:arg1, :arg2).and_return(result = mock("ResultMock"))
53
+
54
+ service.execute(:arg1, :arg2).should be(result)
46
55
  end
47
56
 
48
57
  it "should call the remote service if impl-class does not exist" do
@@ -3,47 +3,44 @@ require File.expand_path(File.join(File.dirname(__FILE__), '../../', 'spec_help
3
3
  module Hoth
4
4
  module Transport
5
5
  describe Http do
6
-
6
+
7
7
  before(:each) do
8
8
  @service_mock = mock("ServiceMock")
9
9
  end
10
-
10
+
11
11
  it "should call a remote via http" do
12
- @service_mock.should_receive(:return_nothing?).and_return(false)
13
-
14
12
  params = {:first_name => "Seras", :last_name => "Victoria"}
15
13
 
16
14
  transport = Http.new(@service_mock)
17
15
  transport.should_receive(:post_payload).with([params])
18
16
  transport.call_remote_with(params)
19
17
  end
20
-
18
+
21
19
  it "should post payload encoded with JSON" do
20
+ Hoth.should_receive(:client_uuid).and_return("CLIENT_UUID")
22
21
  @service_mock.should_receive(:endpoint).and_return(endpoint = mock("EndpointMock"))
23
22
  @service_mock.should_receive(:name).and_return("service_name")
24
23
  endpoint.should_receive(:to_url).and_return("http://localhost:3000/execute")
25
24
 
26
25
  encoder = mock("JsonEncoderMock")
27
26
  encoder.should_receive(:encode).with("params").and_return("encoded_params")
28
-
27
+
29
28
  URI.should_receive(:parse).with("http://localhost:3000/execute").and_return(uri = mock("URIMock"))
30
- Net::HTTP.should_receive(:post_form).with(uri, {"name" => "service_name", "params" => "encoded_params"})
31
-
29
+ Net::HTTP.should_receive(:post_form).with(uri, {"name" => "service_name", "params" => "encoded_params", "caller_uuid" => "CLIENT_UUID"})
30
+
32
31
  transport = Http.new(@service_mock, {:encoder => encoder})
33
32
  transport.post_payload("params")
34
33
  end
35
-
34
+
36
35
  describe "Error Handling" do
37
-
36
+
38
37
  before(:each) do
39
38
  service_mock = mock("ServiceMock")
40
- service_mock.should_receive(:return_nothing?).any_number_of_times.and_return(false)
41
-
42
39
  @params = {:first_name => "Seras", :last_name => "Victoria"}
43
40
  encoder = mock("JsonEncoderMock")
44
41
  @transport = Http.new(service_mock, {:encoder => encoder})
45
42
  end
46
-
43
+
47
44
  it "should handle http connection error" do
48
45
  @transport.should_receive(:post_payload).and_raise(Exception)
49
46
  lambda { @transport.call_remote_with(@params) }.should raise_error(TransportError)
@@ -70,8 +67,53 @@ module Hoth
70
67
  lambda { @transport.call_remote_with(@params) }.should raise_error(TransportError)
71
68
  end
72
69
  end
73
- end
74
70
 
71
+ it "should handle Timeout::Error and wrap them in a TransportError" do
72
+ @transport.should_receive(:post_payload).and_raise(Timeout::Error)
73
+ lambda { @transport.call_remote_with(@params) }.should raise_error(TransportError)
74
+ end
75
+
76
+ it "should handle Errno::EINVAL and wrap them in a TransportError" do
77
+ @transport.should_receive(:post_payload).and_raise(Errno::EINVAL)
78
+ lambda { @transport.call_remote_with(@params) }.should raise_error(TransportError)
79
+ end
80
+
81
+ it "should handle Errno::ECONNRESET and wrap them in a TransportError" do
82
+ @transport.should_receive(:post_payload).and_raise(Errno::ECONNRESET)
83
+ lambda { @transport.call_remote_with(@params) }.should raise_error(TransportError)
84
+ end
85
+
86
+ it "should handle EOFError and wrap them in a TransportError" do
87
+ @transport.should_receive(:post_payload).and_raise(EOFError)
88
+ lambda { @transport.call_remote_with(@params) }.should raise_error(TransportError)
89
+ end
90
+
91
+ it "should handle Net::HTTPBadResponse and wrap them in a TransportError" do
92
+ @transport.should_receive(:post_payload).and_raise(Net::HTTPBadResponse)
93
+ lambda { @transport.call_remote_with(@params) }.should raise_error(TransportError)
94
+ end
95
+
96
+ it "should handle Net::HTTPHeaderSyntaxError and wrap them in a TransportError" do
97
+ @transport.should_receive(:post_payload).and_raise(Net::HTTPHeaderSyntaxError)
98
+ lambda { @transport.call_remote_with(@params) }.should raise_error(TransportError)
99
+ end
100
+
101
+ it "should handle Net::ProtocolError and wrap them in a TransportError" do
102
+ @transport.should_receive(:post_payload).and_raise(Net::ProtocolError)
103
+ lambda { @transport.call_remote_with(@params) }.should raise_error(TransportError)
104
+ end
105
+
106
+ it "should handle Errno::ECONNREFUSED and wrap them in a TransportError" do
107
+ @transport.should_receive(:post_payload).and_raise(Errno::ECONNREFUSED)
108
+ lambda { @transport.call_remote_with(@params) }.should raise_error(TransportError)
109
+ end
110
+
111
+ it "should handle SocketError and wrap them in a TransportError" do
112
+ @transport.should_receive(:post_payload).and_raise(SocketError)
113
+ lambda { @transport.call_remote_with(@params) }.should raise_error(TransportError)
114
+ end
115
+ end
75
116
  end
117
+
76
118
  end
77
119
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hoth
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 17
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
8
  - 3
8
- - 0
9
- version: 0.3.0
9
+ - 1
10
+ version: 0.3.1
10
11
  platform: ruby
11
12
  authors:
12
13
  - Dirk Breuer
@@ -14,16 +15,18 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-03-31 00:00:00 +02:00
18
+ date: 2011-01-03 00:00:00 +01:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: activesupport
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ">="
26
28
  - !ruby/object:Gem::Version
29
+ hash: 3
27
30
  segments:
28
31
  - 0
29
32
  version: "0"
@@ -33,9 +36,11 @@ dependencies:
33
36
  name: bertrpc
34
37
  prerelease: false
35
38
  requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
36
40
  requirements:
37
41
  - - ">="
38
42
  - !ruby/object:Gem::Version
43
+ hash: 3
39
44
  segments:
40
45
  - 0
41
46
  version: "0"
@@ -45,9 +50,11 @@ dependencies:
45
50
  name: json
46
51
  prerelease: false
47
52
  requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
48
54
  requirements:
49
55
  - - ">="
50
56
  - !ruby/object:Gem::Version
57
+ hash: 3
51
58
  segments:
52
59
  - 0
53
60
  version: "0"
@@ -57,9 +64,11 @@ dependencies:
57
64
  name: rspec
58
65
  prerelease: false
59
66
  requirement: &id004 !ruby/object:Gem::Requirement
67
+ none: false
60
68
  requirements:
61
69
  - - ">="
62
70
  - !ruby/object:Gem::Version
71
+ hash: 3
63
72
  segments:
64
73
  - 0
65
74
  version: "0"
@@ -69,9 +78,11 @@ dependencies:
69
78
  name: simple_publisher
70
79
  prerelease: false
71
80
  requirement: &id005 !ruby/object:Gem::Requirement
81
+ none: false
72
82
  requirements:
73
83
  - - ">="
74
84
  - !ruby/object:Gem::Version
85
+ hash: 3
75
86
  segments:
76
87
  - 0
77
88
  version: "0"
@@ -91,6 +102,7 @@ extra_rdoc_files:
91
102
  - README.rdoc
92
103
  - TODO
93
104
  files:
105
+ - Gemfile.lock
94
106
  - README.rdoc
95
107
  - THANKS.md
96
108
  - lib/hoth.rb
@@ -111,6 +123,7 @@ files:
111
123
  - lib/hoth/transport/base.rb
112
124
  - lib/hoth/transport/bert.rb
113
125
  - lib/hoth/transport/http.rb
126
+ - lib/hoth/transport/https.rb
114
127
  - lib/hoth/transport/workling.rb
115
128
  - lib/hoth/util/logger.rb
116
129
  - spec/spec_helper.rb
@@ -129,7 +142,7 @@ files:
129
142
  - LICENSE
130
143
  - TODO
131
144
  has_rdoc: true
132
- homepage: http://github.com/railsbros/hoth
145
+ homepage: http://github.com/galaxycats/hoth
133
146
  licenses: []
134
147
 
135
148
  post_install_message:
@@ -138,23 +151,27 @@ rdoc_options:
138
151
  require_paths:
139
152
  - lib
140
153
  required_ruby_version: !ruby/object:Gem::Requirement
154
+ none: false
141
155
  requirements:
142
156
  - - ">="
143
157
  - !ruby/object:Gem::Version
158
+ hash: 3
144
159
  segments:
145
160
  - 0
146
161
  version: "0"
147
162
  required_rubygems_version: !ruby/object:Gem::Requirement
163
+ none: false
148
164
  requirements:
149
165
  - - ">="
150
166
  - !ruby/object:Gem::Version
167
+ hash: 3
151
168
  segments:
152
169
  - 0
153
170
  version: "0"
154
171
  requirements: []
155
172
 
156
173
  rubyforge_project:
157
- rubygems_version: 1.3.6
174
+ rubygems_version: 1.3.7
158
175
  signing_key:
159
176
  specification_version: 3
160
177
  summary: Registry and deployment description abstraction for SOA-Services