yup 0.0.8 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +21 -0
- data/Gemfile +7 -6
- data/Gemfile.lock +36 -19
- data/README.rdoc +9 -20
- data/Rakefile +1 -12
- data/VERSION +1 -1
- data/bin/yupd +5 -4
- data/lib/yup.rb +11 -10
- data/lib/yup/request_forwarder.rb +98 -41
- data/lib/yup/request_handler.rb +21 -11
- data/lib/yup/state.rb +9 -3
- data/lib/yup/version.rb +3 -0
- data/test/helper.rb +15 -0
- data/test/test_persistence_yup.rb +101 -0
- data/test/test_yup.rb +74 -4
- data/yup.gemspec +21 -19
- metadata +45 -28
data/.travis.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
before_install:
|
2
|
+
- sudo apt-get install libdb-dev
|
3
|
+
language: ruby
|
4
|
+
rvm:
|
5
|
+
- ree-1.8.7
|
6
|
+
- 1.8.7
|
7
|
+
- 1.9.2
|
8
|
+
- 1.9.3
|
9
|
+
- jruby-18mode
|
10
|
+
- jruby-19mode
|
11
|
+
- rbx-18mode
|
12
|
+
- rbx-19mode
|
13
|
+
notifications:
|
14
|
+
recipients:
|
15
|
+
- d.sukhonin@gmail.com
|
16
|
+
matrix:
|
17
|
+
allow_failures:
|
18
|
+
- rvm: jruby-18mode
|
19
|
+
- rvm: jruby-19mode
|
20
|
+
- rvm: rbx-18mode
|
21
|
+
- rvm: rbx-19mode
|
data/Gemfile
CHANGED
@@ -2,16 +2,17 @@ source "http://rubygems.org"
|
|
2
2
|
|
3
3
|
gem "eventmachine"
|
4
4
|
gem "em-http-request"
|
5
|
-
gem "http_request.rb"
|
6
5
|
gem "http_parser.rb"
|
7
6
|
gem "tuple"
|
8
7
|
gem "yajl-ruby"
|
9
8
|
|
10
9
|
group :development do
|
11
10
|
gem "bdb"
|
12
|
-
gem "yard",
|
13
|
-
gem "minitest",
|
14
|
-
gem "bundler",
|
15
|
-
gem "jeweler",
|
16
|
-
gem "
|
11
|
+
gem "yard", "~> 0.6.0"
|
12
|
+
gem "minitest", ">= 0"
|
13
|
+
gem "bundler", "~> 1.0"
|
14
|
+
gem "jeweler", "~> 1.8.4"
|
15
|
+
gem "simplecov", ">= 0"
|
16
|
+
gem "simplecov-rcov", ">= 0"
|
17
|
+
gem "travis-lint"
|
17
18
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,27 +1,43 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
-
addressable (2.2
|
4
|
+
addressable (2.3.2)
|
5
5
|
bdb (0.2.6.5)
|
6
6
|
tuple (>= 0.1.2)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
cookiejar (0.3.0)
|
8
|
+
em-http-request (1.0.3)
|
9
|
+
addressable (>= 2.2.3)
|
10
|
+
cookiejar
|
11
|
+
em-socksify
|
12
|
+
eventmachine (>= 1.0.0.beta.4)
|
13
|
+
http_parser.rb (>= 0.5.3)
|
14
|
+
em-socksify (0.2.1)
|
15
|
+
eventmachine (>= 1.0.0.beta.4)
|
16
|
+
eventmachine (1.0.0)
|
13
17
|
git (1.2.5)
|
14
|
-
|
15
|
-
|
16
|
-
jeweler (1.
|
17
|
-
bundler (~> 1.0
|
18
|
+
hashr (0.0.22)
|
19
|
+
http_parser.rb (0.5.3)
|
20
|
+
jeweler (1.8.4)
|
21
|
+
bundler (~> 1.0)
|
18
22
|
git (>= 1.2.5)
|
19
23
|
rake
|
20
|
-
|
21
|
-
|
22
|
-
|
24
|
+
rdoc
|
25
|
+
json (1.7.5)
|
26
|
+
minitest (4.2.0)
|
27
|
+
multi_json (1.3.7)
|
28
|
+
rake (0.9.2.2)
|
29
|
+
rdoc (3.12)
|
30
|
+
json (~> 1.4)
|
31
|
+
simplecov (0.7.1)
|
32
|
+
multi_json (~> 1.0)
|
33
|
+
simplecov-html (~> 0.7.1)
|
34
|
+
simplecov-html (0.7.1)
|
35
|
+
simplecov-rcov (0.2.3)
|
36
|
+
simplecov (>= 0.4.1)
|
37
|
+
travis-lint (1.4.0)
|
38
|
+
hashr (>= 0.0.19)
|
23
39
|
tuple (0.1.2)
|
24
|
-
yajl-ruby (
|
40
|
+
yajl-ruby (1.1.0)
|
25
41
|
yard (0.6.8)
|
26
42
|
|
27
43
|
PLATFORMS
|
@@ -29,14 +45,15 @@ PLATFORMS
|
|
29
45
|
|
30
46
|
DEPENDENCIES
|
31
47
|
bdb
|
32
|
-
bundler (~> 1.0
|
48
|
+
bundler (~> 1.0)
|
33
49
|
em-http-request
|
34
50
|
eventmachine
|
35
51
|
http_parser.rb
|
36
|
-
|
37
|
-
jeweler (~> 1.5.2)
|
52
|
+
jeweler (~> 1.8.4)
|
38
53
|
minitest
|
39
|
-
|
54
|
+
simplecov
|
55
|
+
simplecov-rcov
|
56
|
+
travis-lint
|
40
57
|
tuple
|
41
58
|
yajl-ruby
|
42
59
|
yard (~> 0.6.0)
|
data/README.rdoc
CHANGED
@@ -1,18 +1,21 @@
|
|
1
1
|
= yup daemon
|
2
2
|
|
3
|
-
|
3
|
+
{<img src="https://secure.travis-ci.org/neglectedvalue/yup.png" alt="Build Status" />}[http://travis-ci.org/neglectedvalue/yup]
|
4
|
+
{<img src="https://codeclimate.com/badge.png" />}[https://codeclimate.com/github/neglectedvalue/yup]
|
4
5
|
|
5
|
-
|
6
|
+
This is the small daemon to forward HTTP requests when response is known or unimportant.
|
7
|
+
|
8
|
+
When a http request is arrived the yup daemon (yupd), it answers 200 OK (customizable). Then the yupd forwards the http request to the specified host and retries if a timeout error was happend.
|
6
9
|
|
7
10
|
== Non-persistent queue
|
8
|
-
By default, nonpersistent queue is used. A limit (the option --watermark) at which new requests will be dropped.
|
11
|
+
By default, nonpersistent queue is used. A limit (the option --watermark) at which new concurrent requests will be dropped.
|
9
12
|
|
10
13
|
== Persistent queue
|
11
14
|
If you want use persistent queue you need to specify the option --persistent with a path to a database.
|
12
15
|
|
13
16
|
== One of use cases
|
14
17
|
|
15
|
-
For example we can have a rails app which send exceptions to an
|
18
|
+
For example we can have a rails app which send exceptions to an Errbit by the gem airbrake. We know the errbit can be not available by network issues or some else reasons, but we do not want to lose exceptions. To resolve this problem we can start yupd on the same host with the rails app:
|
16
19
|
yupd --listen localhost:8081 --status-code 201 --persistent /var/db/yupd-errbit errbit.host.somewhere
|
17
20
|
|
18
21
|
Reconfiguration of hoptoad_notifier is very ease:
|
@@ -22,25 +25,11 @@ Reconfiguration of hoptoad_notifier is very ease:
|
|
22
25
|
config.api_key = "api_key_for_your_app"
|
23
26
|
end
|
24
27
|
|
25
|
-
Now problem of availability
|
26
|
-
|
27
|
-
== Roadmap to 0.1
|
28
|
-
|
29
|
-
* More better documentation
|
30
|
-
* Daemonize
|
31
|
-
* Preforking
|
32
|
-
* A configurable map of different delegating rules
|
33
|
-
* Tests...
|
28
|
+
Now problem of availability errbit is assigned to the yupd.
|
34
29
|
|
35
30
|
== Contributing to yup
|
36
31
|
|
37
|
-
|
38
|
-
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
39
|
-
* Fork the project
|
40
|
-
* Start a feature/bugfix branch
|
41
|
-
* Commit and push until you are happy with your contribution
|
42
|
-
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
43
|
-
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
32
|
+
Feel free to contribute.
|
44
33
|
|
45
34
|
== Copyright
|
46
35
|
|
data/Rakefile
CHANGED
@@ -15,14 +15,10 @@ Jeweler::Tasks.new do |gem|
|
|
15
15
|
gem.name = "yup"
|
16
16
|
gem.homepage = "http://github.com/neglectedvalue/yup"
|
17
17
|
gem.license = "MIT"
|
18
|
-
gem.summary = "
|
18
|
+
gem.summary = "HTTP forwarder"
|
19
19
|
gem.description = "Just answers 200 (or specified) to a client and asynchronously forwards HTTP request to a configured host"
|
20
20
|
gem.email = "d.sukhonin@gmail.com"
|
21
21
|
gem.authors = ["Denis Sukhonin"]
|
22
|
-
# Include your dependencies below. Runtime dependencies are required when using your gem,
|
23
|
-
# and development dependencies are only needed for development (ie running rake tasks, tests, etc)
|
24
|
-
# gem.add_runtime_dependency 'jabber4r', '> 0.1'
|
25
|
-
# gem.add_development_dependency 'rspec', '> 1.2.3'
|
26
22
|
end
|
27
23
|
Jeweler::RubygemsDotOrgTasks.new
|
28
24
|
|
@@ -33,13 +29,6 @@ Rake::TestTask.new(:test) do |test|
|
|
33
29
|
test.verbose = true
|
34
30
|
end
|
35
31
|
|
36
|
-
require 'rcov/rcovtask'
|
37
|
-
Rcov::RcovTask.new do |test|
|
38
|
-
test.libs << 'test'
|
39
|
-
test.pattern = 'test/**/test_*.rb'
|
40
|
-
test.verbose = true
|
41
|
-
end
|
42
|
-
|
43
32
|
task :default => :test
|
44
33
|
|
45
34
|
require 'yard'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
data/bin/yupd
CHANGED
@@ -8,18 +8,20 @@ require 'yup'
|
|
8
8
|
|
9
9
|
def usage
|
10
10
|
puts <<-EOF
|
11
|
+
yup #{Yup::VERSION}
|
12
|
+
|
11
13
|
Usage: #{$0} [OPTION] ... FORWARD_TO_HOST
|
12
14
|
|
13
15
|
Options:
|
14
16
|
-h, --help Show help
|
15
17
|
--listen <host:port>, -l Listen on an address (default localhost:8080)
|
16
18
|
--status-code <code> Send status code to a client on request (default 200)
|
17
|
-
--resend-delay <seconds> Resend failed requests in seconds (default
|
19
|
+
--resend-delay <seconds> Resend failed requests in seconds (default 60.0)
|
18
20
|
--watermark <number> Maximum of concurrent requests (default 100)
|
19
21
|
--loglevel <level> Logging severity (default fatal).
|
20
22
|
Available log levels: fatal, error, warn, info, debug.
|
21
23
|
--persistent <path> Use persistent queue.
|
22
|
-
--timeout <seconds> Timeout for answer from FORWARD_TO_HOST
|
24
|
+
--timeout <seconds> Timeout for answer from FORWARD_TO_HOST (default 60.0)
|
23
25
|
|
24
26
|
Examples:
|
25
27
|
yupd --listen 0.0.0.0:8081 --status-code 201 errbit.host.somewhere
|
@@ -70,7 +72,7 @@ opts.each do |opt, arg|
|
|
70
72
|
when '--persistent'
|
71
73
|
config[:persistent] = arg || "/tmp/queue"
|
72
74
|
when '--timeout'
|
73
|
-
config[:timeout] = arg.
|
75
|
+
config[:timeout] = arg.to_f
|
74
76
|
end
|
75
77
|
end
|
76
78
|
|
@@ -87,7 +89,6 @@ Yup.watermark = config[:watermark] if config.has_key?(:watermark)
|
|
87
89
|
Yup.resend_delay = config[:resend_delay] if config.has_key?(:resend_delay)
|
88
90
|
|
89
91
|
if config.has_key?(:persistent)
|
90
|
-
# Yup::State.repair_if_need(config[:persistent])
|
91
92
|
Yup.run_with_state(config)
|
92
93
|
else
|
93
94
|
Yup.run(config)
|
data/lib/yup.rb
CHANGED
@@ -4,11 +4,12 @@ require 'logger'
|
|
4
4
|
require 'yajl'
|
5
5
|
require 'tmpdir'
|
6
6
|
|
7
|
+
require 'yup/version'
|
7
8
|
require 'yup/request_forwarder'
|
8
9
|
require 'yup/request_handler'
|
9
10
|
|
10
11
|
module Yup
|
11
|
-
@@resend_delay =
|
12
|
+
@@resend_delay = 60.0
|
12
13
|
def self.resend_delay; @@resend_delay end
|
13
14
|
def self.resend_delay=(seconds); @@resend_delay = seconds end
|
14
15
|
|
@@ -25,10 +26,10 @@ module Yup
|
|
25
26
|
port = config[:listen_port] || 8080
|
26
27
|
status_code = config[:status_code] || 200
|
27
28
|
forward_to = config[:forward_to]
|
28
|
-
timeout = config[:timeout] ||
|
29
|
+
timeout = config[:timeout] || 60
|
29
30
|
|
30
|
-
|
31
|
-
|
31
|
+
EM.run do
|
32
|
+
EM.start_server(host, port, RequestHandler, forward_to, status_code, nil, timeout)
|
32
33
|
logger.info { "listening on #{host}:#{port}" }
|
33
34
|
end
|
34
35
|
end
|
@@ -41,7 +42,7 @@ module Yup
|
|
41
42
|
status_code = config[:status_code] || 200
|
42
43
|
forward_to = config[:forward_to]
|
43
44
|
dbpath = config[:persistent]
|
44
|
-
timeout = config[:timeout]
|
45
|
+
timeout = config[:timeout] || 60
|
45
46
|
feedback_channel = File.join(Dir.tmpdir, "yupd-#{$$}-feedback")
|
46
47
|
state = Yup::State.new(dbpath, forward_to, feedback_channel)
|
47
48
|
|
@@ -60,12 +61,12 @@ module Yup
|
|
60
61
|
Signal.trap("INT", &db_closer)
|
61
62
|
end
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
logger.info { "Listening on #{host}:#{port}" }
|
66
|
-
|
67
|
-
EventMachine.start_unix_domain_server(feedback_channel, State::FeedbackHandler, state)
|
64
|
+
EM.run do
|
65
|
+
EM.start_unix_domain_server(feedback_channel, State::FeedbackHandler, state)
|
68
66
|
logger.info { "Feedback through #{feedback_channel}" }
|
67
|
+
|
68
|
+
EM.start_server(host, port, RequestHandler, forward_to, status_code, state, timeout)
|
69
|
+
logger.info { "Listening on #{host}:#{port}" }
|
69
70
|
end
|
70
71
|
end
|
71
72
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'em-http-request'
|
2
|
-
require 'http_request'
|
3
2
|
|
4
3
|
module Yup
|
5
4
|
class RequestForwarder
|
@@ -10,40 +9,56 @@ module Yup
|
|
10
9
|
@body = body
|
11
10
|
@forward_to = forward_to
|
12
11
|
@timeout = timeout
|
12
|
+
@logger = Yup.logger.clone
|
13
13
|
|
14
|
-
@
|
14
|
+
@headers.merge!(
|
15
|
+
'Host' => @forward_to,
|
16
|
+
'Connection' => 'Close')
|
15
17
|
end
|
16
18
|
|
17
|
-
def
|
19
|
+
def perform
|
18
20
|
http_method = @http_method.to_sym
|
19
21
|
http_url = "http://#{@forward_to}#{@request_url}"
|
20
22
|
http = EventMachine::HttpRequest.
|
21
|
-
new(http_url
|
23
|
+
new(http_url,
|
24
|
+
:inactivity_timeout => @timeout).
|
22
25
|
send(http_method,
|
23
|
-
:
|
24
|
-
:head => @headers.merge('Host' => @forward_to),
|
26
|
+
:head => @headers,
|
25
27
|
:body => @body)
|
26
28
|
|
29
|
+
@logger.progname = "Yup::RequestForwarder (##{http.__id__.to_s(36)} received at #{Time.now.to_s})"
|
30
|
+
|
27
31
|
http.callback do
|
28
32
|
Yup.watermark += 1
|
29
33
|
|
30
34
|
if http.response_header.status / 100 == 2
|
31
|
-
|
35
|
+
log_response(http)
|
36
|
+
@logger.info "Success"
|
32
37
|
else
|
33
|
-
|
34
|
-
@logger.
|
35
|
-
@logger.debug { http.response_header.inspect }
|
36
|
-
@logger.debug { http.response.inspect }
|
38
|
+
log_response(http)
|
39
|
+
@logger.info "Fail; will not retry"
|
37
40
|
end
|
38
41
|
end
|
39
42
|
|
40
43
|
http.errback do
|
41
|
-
|
42
|
-
@logger.
|
43
|
-
@logger.debug { http.response_header.inspect }
|
44
|
-
@logger.debug { http.response.inspect }
|
44
|
+
log_response(http)
|
45
|
+
@logger.info "Error: #{http.error}; will retry after #{Yup.resend_delay} seconds"
|
45
46
|
|
46
|
-
EventMachine.add_timer(Yup.resend_delay
|
47
|
+
EventMachine.add_timer(Yup.resend_delay, &self.method(:retry))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def retry
|
52
|
+
self.perform
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def log_response(http)
|
57
|
+
@logger.info { "HTTP request: #{@http_method.upcase} #{@request_url} HTTP/1.1" }
|
58
|
+
if http.response_header.http_status
|
59
|
+
@logger.info { "HTTP response: HTTP/#{http.response_header.http_version} #{http.response_header.http_status} #{http.response_header.http_reason}" }
|
60
|
+
@logger.debug { "HTTP response headers" + (http.response_header.empty? ? " is empty" : "\n" + http.response_header.inspect) }
|
61
|
+
@logger.debug { "HTTP response body" + (http.response.empty? ? " is empty" : "\n" + http.response.inspect) }
|
47
62
|
end
|
48
63
|
end
|
49
64
|
end
|
@@ -53,11 +68,12 @@ module Yup
|
|
53
68
|
def initialize(state, forward_to, timeout)
|
54
69
|
@state = state
|
55
70
|
@forward_to = forward_to
|
56
|
-
@timeout
|
71
|
+
@timeout = timeout
|
72
|
+
@logger = Yup.logger.clone
|
73
|
+
@logger.progname = "Yup::State::RequestForwarder"
|
57
74
|
|
58
|
-
@
|
59
|
-
@yajl
|
60
|
-
@yajl.on_parse_complete = method(:make_request)
|
75
|
+
@yajl = Yajl::Parser.new(:symbolize_keys => true)
|
76
|
+
@yajl.on_parse_complete = self.method(:make_request)
|
61
77
|
end
|
62
78
|
|
63
79
|
def run_loop
|
@@ -73,44 +89,85 @@ module Yup
|
|
73
89
|
|
74
90
|
def make_request(req)
|
75
91
|
begin
|
76
|
-
http_method, request_url, headers, body = req
|
92
|
+
@http_method, @request_url, headers, body = req
|
77
93
|
headers = Hash[headers.to_a.flatten.map(&:to_s)]
|
94
|
+
headers["Host"] = @forward_to
|
95
|
+
headers["Connection"] = "Close"
|
96
|
+
|
97
|
+
req = "#{@http_method.upcase} #{@request_url} HTTP/1.1\r\n"
|
98
|
+
headers.each do |k, v|
|
99
|
+
req << "#{k}: #{v}\r\n"
|
100
|
+
end
|
101
|
+
req << body if !body.empty?
|
102
|
+
req << "\r\n"
|
103
|
+
raw_response = send_data(req.to_s, @forward_to)
|
104
|
+
|
105
|
+
response_body = ""
|
106
|
+
http = Http::Parser.new()
|
107
|
+
http.on_body = proc do |chunk|
|
108
|
+
response_body << chunk
|
109
|
+
end
|
110
|
+
http << raw_response
|
78
111
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
send(http_method,
|
83
|
-
:url => http_url,
|
84
|
-
:headers => headers.merge('Host' => @forward_to),
|
85
|
-
:parameters => body,
|
86
|
-
:timeout => timeout)
|
87
|
-
|
88
|
-
if http.code_2xx?
|
89
|
-
@logger.info '--- SUCCESS'
|
112
|
+
if http.status_code / 100 == 2
|
113
|
+
log_response(raw_response, response_body, http)
|
114
|
+
@logger.info "Success"
|
90
115
|
else
|
91
|
-
|
92
|
-
@logger.
|
93
|
-
@logger.debug { http.response_header.inspect }
|
94
|
-
@logger.debug { http.response.inspect }
|
116
|
+
log_response(raw_response, response_body, http)
|
117
|
+
@logger.info "Fail; will not retry"
|
95
118
|
end
|
96
|
-
rescue Exception => e
|
97
|
-
@logger.info '--- ERROR'
|
98
|
-
@logger.debug { e }
|
99
119
|
|
100
|
-
|
120
|
+
rescue Exception, Timeout::Error => e
|
121
|
+
log_response(raw_response, response_body, http)
|
122
|
+
@logger.info "Error: #{e.message}; will retry after #{Yup.resend_delay} seconds"
|
123
|
+
|
124
|
+
@state.to_feedback(Yajl::Encoder.encode([@http_method.downcase, @request_url, headers, body]))
|
101
125
|
|
102
126
|
sleep Yup.resend_delay
|
103
127
|
end
|
104
128
|
end
|
129
|
+
|
130
|
+
private
|
131
|
+
def log_response(raw_response, body, http)
|
132
|
+
@logger.info { "HTTP request: #{@http_method.upcase} #{@request_url} HTTP/1.1" }
|
133
|
+
if raw_response && !raw_response.empty?
|
134
|
+
@logger.info { "HTTP response: #{raw_response.lines.first.chomp}" }
|
135
|
+
@logger.debug { "HTTP response headers" + (http.headers.empty? ? " is empty" : "\n" + http.headers.inspect) }
|
136
|
+
@logger.debug { "HTTP response body" + (body.empty? ? " is empty" : "\n" + body.inspect) }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def send_data(data, host)
|
141
|
+
host, port = host.split(":")
|
142
|
+
addr = Socket.getaddrinfo(host, nil)
|
143
|
+
sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
|
144
|
+
|
145
|
+
secs = Integer(@timeout)
|
146
|
+
usecs = Integer((@timeout - secs) * 1_000_000)
|
147
|
+
optval = [secs, usecs].pack("l_2")
|
148
|
+
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval)
|
149
|
+
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval)
|
150
|
+
|
151
|
+
resp = Timeout::timeout(@timeout) do
|
152
|
+
sock.connect(Socket.pack_sockaddr_in(port, addr[0][3]))
|
153
|
+
sock.write(data)
|
154
|
+
sock.read()
|
155
|
+
end
|
156
|
+
return resp
|
157
|
+
ensure
|
158
|
+
sock.close()
|
159
|
+
end
|
105
160
|
end
|
106
161
|
|
107
162
|
class FeedbackHandler < EM::Connection
|
108
163
|
def initialize(state)
|
109
164
|
@state = state
|
110
165
|
|
111
|
-
@logger = Yup.logger
|
112
166
|
@yajl = Yajl::Parser.new(:symbolize_keys => true)
|
113
167
|
@yajl.on_parse_complete = method(:on_message)
|
168
|
+
|
169
|
+
@logger = Yup.logger.clone
|
170
|
+
@logger.progname = "Yup::State::FeedbackHandler"
|
114
171
|
end
|
115
172
|
|
116
173
|
def receive_data(data)
|
data/lib/yup/request_handler.rb
CHANGED
@@ -11,8 +11,10 @@ module Yup
|
|
11
11
|
@state = state
|
12
12
|
@timeout = timeout
|
13
13
|
|
14
|
-
@logger = Yup.logger
|
15
14
|
@chunks = []
|
15
|
+
|
16
|
+
@logger = Yup.logger.clone
|
17
|
+
@logger.progname = "Yup::RequestHandler"
|
16
18
|
end
|
17
19
|
|
18
20
|
def post_init
|
@@ -32,12 +34,15 @@ module Yup
|
|
32
34
|
end
|
33
35
|
|
34
36
|
def on_message_complete
|
35
|
-
@logger.info
|
36
|
-
|
37
|
-
|
38
|
-
@logger.
|
39
|
-
|
40
|
-
|
37
|
+
@logger.info {
|
38
|
+
"Processing a new request: #{@parser.http_method} #{@parser.request_url} HTTP/#{@parser.http_version.join('.')}"
|
39
|
+
}
|
40
|
+
@logger.debug {
|
41
|
+
"HTTP headers" + (@parser.headers.empty? ? " is empty" : "\n" + @parser.headers.inspect)
|
42
|
+
}
|
43
|
+
@logger.debug {
|
44
|
+
"HTTP body" + (@body.empty? ? " is empty" : "\n" + @body)
|
45
|
+
}
|
41
46
|
|
42
47
|
send_answer
|
43
48
|
shedule_request
|
@@ -49,6 +54,12 @@ module Yup
|
|
49
54
|
resp.status = @status_code
|
50
55
|
resp['Server'] = 'yupd'
|
51
56
|
send_data resp.to_s
|
57
|
+
close_connection_after_writing
|
58
|
+
|
59
|
+
@logger.info {
|
60
|
+
port, ip = Socket.unpack_sockaddr_in(get_peername)
|
61
|
+
"Sent the answer #{@status_code} to a client #{ip}:#{port}"
|
62
|
+
}
|
52
63
|
end
|
53
64
|
|
54
65
|
def shedule_request
|
@@ -57,12 +68,11 @@ module Yup
|
|
57
68
|
else
|
58
69
|
unless Yup.watermark.zero?
|
59
70
|
Yup.watermark -= 1
|
60
|
-
|
61
|
-
|
62
|
-
RequestForwarder.new(@parser.http_method.downcase, @parser.request_url, @parser.headers, @body, @forward_to, @timeout).run
|
71
|
+
EM.next_tick do
|
72
|
+
RequestForwarder.new(@parser.http_method.downcase, @parser.request_url, @parser.headers, @body, @forward_to, @timeout).perform
|
63
73
|
end
|
64
74
|
else
|
65
|
-
@logger.error "
|
75
|
+
@logger.error "Watermark is reached, drop the request"
|
66
76
|
end
|
67
77
|
end
|
68
78
|
end
|
data/lib/yup/state.rb
CHANGED
@@ -5,6 +5,8 @@ rescue LoadError
|
|
5
5
|
puts "Install bdb gem to use a persistent queue."
|
6
6
|
end
|
7
7
|
|
8
|
+
require "timeout"
|
9
|
+
|
8
10
|
module Yup
|
9
11
|
class State
|
10
12
|
RE_LEN = 1000
|
@@ -22,6 +24,10 @@ module Yup
|
|
22
24
|
@name = name
|
23
25
|
@feedback_channel = feedback_channel
|
24
26
|
|
27
|
+
@logger = Yup.logger.clone
|
28
|
+
@logger.progname = "Yup::State"
|
29
|
+
|
30
|
+
FileUtils.mkdir_p(@path)
|
25
31
|
@env = Bdb::Env.new(0)
|
26
32
|
@env = @env.open(@path, Bdb::DB_CREATE | Bdb::DB_INIT_MPOOL | Bdb::DB_INIT_CDB, 0)
|
27
33
|
@queue = @env.db
|
@@ -30,7 +36,7 @@ module Yup
|
|
30
36
|
end
|
31
37
|
|
32
38
|
def push(data)
|
33
|
-
|
39
|
+
@logger.debug { "Push: #{data}" }
|
34
40
|
i = 0
|
35
41
|
until (chunk = data.slice(i, RE_LEN)).nil?
|
36
42
|
@queue.put(nil, "", chunk, Bdb::DB_APPEND)
|
@@ -40,12 +46,12 @@ module Yup
|
|
40
46
|
|
41
47
|
def bpop
|
42
48
|
data = @queue.get(nil, "", nil, Bdb::DB_CONSUME_WAIT)
|
43
|
-
|
49
|
+
@logger.debug { "Bpoped: #{data.strip}" }
|
44
50
|
data
|
45
51
|
end
|
46
52
|
|
47
53
|
def to_feedback(data)
|
48
|
-
|
54
|
+
@logger.debug { "Push to the feedback channel: #{data.strip}" }
|
49
55
|
sock = UNIXSocket.new(@feedback_channel)
|
50
56
|
sock.send(data, 0)
|
51
57
|
sock.close
|
data/lib/yup/version.rb
ADDED
data/test/helper.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
ENV['RACK_ENV'] = 'test'
|
2
|
+
|
1
3
|
require 'rubygems'
|
2
4
|
require 'bundler'
|
3
5
|
begin
|
@@ -9,6 +11,19 @@ rescue Bundler::BundlerError => e
|
|
9
11
|
end
|
10
12
|
require 'minitest/unit'
|
11
13
|
|
14
|
+
require 'simplecov'
|
15
|
+
require 'simplecov-rcov'
|
16
|
+
|
17
|
+
class SimpleCov::Formatter::MergedFormatter
|
18
|
+
def format(result)
|
19
|
+
SimpleCov::Formatter::HTMLFormatter.new.format(result)
|
20
|
+
SimpleCov::Formatter::RcovFormatter.new.format(result)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
SimpleCov.formatter = SimpleCov::Formatter::MergedFormatter
|
25
|
+
SimpleCov.start
|
26
|
+
|
12
27
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
13
28
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
29
|
require 'yup'
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
require 'tmpdir'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'yup/state'
|
6
|
+
|
7
|
+
class Yup::State::FeedbackHandler
|
8
|
+
alias :on_message_original :on_message
|
9
|
+
def on_message(req)
|
10
|
+
on_message_original(req)
|
11
|
+
$attempts += 1
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class TestPersistenceYup < MiniTest::Unit::TestCase
|
16
|
+
class RequestHandlerMock < Yup::RequestHandler
|
17
|
+
end
|
18
|
+
|
19
|
+
class Service < EM::Connection
|
20
|
+
def post_init
|
21
|
+
@parser = Http::Parser.new(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
def receive_data(data)
|
25
|
+
@parser << data
|
26
|
+
end
|
27
|
+
|
28
|
+
def on_message_complete
|
29
|
+
$service_parser = @parser
|
30
|
+
|
31
|
+
case $attempts
|
32
|
+
when 0
|
33
|
+
when 1
|
34
|
+
send_data "HTTP/1.1 200 OK\r\nServer: test\r\n\r\n"
|
35
|
+
close_connection_after_writing
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def unbind
|
40
|
+
if $attempts > 0
|
41
|
+
EM.add_timer(1) do
|
42
|
+
Process.kill("KILL", $pid)
|
43
|
+
EM.stop_event_loop()
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module Client
|
50
|
+
def connection_completed
|
51
|
+
send_data("GET /foo HTTP/1.0\r\n\r\n")
|
52
|
+
end
|
53
|
+
|
54
|
+
def post_init
|
55
|
+
@parser = Http::Parser.new(self)
|
56
|
+
end
|
57
|
+
|
58
|
+
def receive_data(data)
|
59
|
+
@parser << data
|
60
|
+
end
|
61
|
+
|
62
|
+
def on_message_complete
|
63
|
+
$client_parser = @parser
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_request_handler
|
68
|
+
$attempts = 0
|
69
|
+
|
70
|
+
dbpath = Dir.mktmpdir("yupd-db")
|
71
|
+
feedback_channel = File.join(Dir.tmpdir, "yupd-#{$$}-feedback")
|
72
|
+
|
73
|
+
forward_to = "127.0.0.1:26785"
|
74
|
+
status_code = 200
|
75
|
+
state = Yup::State.new(dbpath, forward_to, feedback_channel)
|
76
|
+
timeout = 1
|
77
|
+
|
78
|
+
Yup.resend_delay = 1
|
79
|
+
|
80
|
+
$pid = Process.fork do
|
81
|
+
Yup::State::RequestForwarder.new(state, forward_to, timeout).run_loop
|
82
|
+
end
|
83
|
+
|
84
|
+
EM.run {
|
85
|
+
EM.start_server("127.0.0.1", 26785, Service)
|
86
|
+
EM.start_unix_domain_server(feedback_channel, Yup::State::FeedbackHandler, state)
|
87
|
+
EM.start_server("127.0.0.1", 26784, RequestHandlerMock, forward_to, status_code, state, timeout)
|
88
|
+
EM.connect("127.0.0.1", 26784, Client)
|
89
|
+
}
|
90
|
+
|
91
|
+
assert $client_parser
|
92
|
+
assert_equal 200, $client_parser.status_code
|
93
|
+
assert_equal "yupd", $client_parser.headers["Server"]
|
94
|
+
assert $service_parser
|
95
|
+
assert_equal "/foo", $service_parser.request_url
|
96
|
+
ensure
|
97
|
+
Process.kill("KILL", $pid) if $pid
|
98
|
+
state.close if state
|
99
|
+
FileUtils.remove_entry_secure(dbpath) if dbpath
|
100
|
+
end
|
101
|
+
end
|
data/test/test_yup.rb
CHANGED
@@ -1,11 +1,81 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
+
class Yup::RequestForwarder
|
4
|
+
alias :retry_original :retry
|
5
|
+
def retry()
|
6
|
+
$attempts += 1
|
7
|
+
retry_original()
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
3
11
|
class TestYup < MiniTest::Unit::TestCase
|
4
|
-
|
5
|
-
|
12
|
+
class RequestHandlerMock < Yup::RequestHandler
|
13
|
+
end
|
14
|
+
|
15
|
+
class Service < EM::Connection
|
16
|
+
def post_init
|
17
|
+
@parser = Http::Parser.new(self)
|
18
|
+
end
|
19
|
+
|
20
|
+
def receive_data(data)
|
21
|
+
@parser << data
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_message_complete
|
25
|
+
$service_parser = @parser
|
26
|
+
|
27
|
+
case $attempts
|
28
|
+
when 0
|
29
|
+
when 1
|
30
|
+
send_data "HTTP/1.1 200 OK\r\nServer: test\r\n\r\n"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def unbind
|
35
|
+
if $attempts > 0
|
36
|
+
EM.next_tick { EM.stop_event_loop }
|
37
|
+
end
|
38
|
+
end
|
6
39
|
end
|
7
40
|
|
8
|
-
|
9
|
-
|
41
|
+
module Client
|
42
|
+
def connection_completed
|
43
|
+
send_data "GET /foo HTTP/1.0\r\n\r\n"
|
44
|
+
end
|
45
|
+
|
46
|
+
def post_init
|
47
|
+
@parser = Http::Parser.new(self)
|
48
|
+
end
|
49
|
+
|
50
|
+
def receive_data(data)
|
51
|
+
@parser << data
|
52
|
+
end
|
53
|
+
|
54
|
+
def on_message_complete
|
55
|
+
$client_parser = @parser
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_request_handler
|
60
|
+
$attempts = 0
|
61
|
+
|
62
|
+
forward_to = "127.0.0.1:16785"
|
63
|
+
status_code = 200
|
64
|
+
state = nil
|
65
|
+
timeout = 1
|
66
|
+
|
67
|
+
Yup.resend_delay = 1
|
68
|
+
|
69
|
+
EM.run {
|
70
|
+
EM.start_server("127.0.0.1", 16785, Service)
|
71
|
+
EM.start_server("127.0.0.1", 16784, RequestHandlerMock, forward_to, status_code, state, timeout)
|
72
|
+
EM.connect("127.0.0.1", 16784, Client)
|
73
|
+
}
|
74
|
+
|
75
|
+
assert $client_parser
|
76
|
+
assert_equal 200, $client_parser.status_code
|
77
|
+
assert_equal "yupd", $client_parser.headers["Server"]
|
78
|
+
assert $service_parser
|
79
|
+
assert_equal "/foo", $service_parser.request_url
|
10
80
|
end
|
11
81
|
end
|
data/yup.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "yup"
|
8
|
-
s.version = "0.0
|
8
|
+
s.version = "0.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Denis Sukhonin"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-11-09"
|
13
13
|
s.description = "Just answers 200 (or specified) to a client and asynchronously forwards HTTP request to a configured host"
|
14
14
|
s.email = "d.sukhonin@gmail.com"
|
15
15
|
s.executables = ["yupd"]
|
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
]
|
20
20
|
s.files = [
|
21
21
|
".document",
|
22
|
+
".travis.yml",
|
22
23
|
"Gemfile",
|
23
24
|
"Gemfile.lock",
|
24
25
|
"LICENSE.txt",
|
@@ -30,7 +31,9 @@ Gem::Specification.new do |s|
|
|
30
31
|
"lib/yup/request_forwarder.rb",
|
31
32
|
"lib/yup/request_handler.rb",
|
32
33
|
"lib/yup/state.rb",
|
34
|
+
"lib/yup/version.rb",
|
33
35
|
"test/helper.rb",
|
36
|
+
"test/test_persistence_yup.rb",
|
34
37
|
"test/test_yup.rb",
|
35
38
|
"yup.gemspec"
|
36
39
|
]
|
@@ -38,11 +41,7 @@ Gem::Specification.new do |s|
|
|
38
41
|
s.licenses = ["MIT"]
|
39
42
|
s.require_paths = ["lib"]
|
40
43
|
s.rubygems_version = "1.8.24"
|
41
|
-
s.summary = "
|
42
|
-
s.test_files = [
|
43
|
-
"test/helper.rb",
|
44
|
-
"test/test_yup.rb"
|
45
|
-
]
|
44
|
+
s.summary = "HTTP forwarder"
|
46
45
|
|
47
46
|
if s.respond_to? :specification_version then
|
48
47
|
s.specification_version = 3
|
@@ -50,43 +49,46 @@ Gem::Specification.new do |s|
|
|
50
49
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
51
50
|
s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
|
52
51
|
s.add_runtime_dependency(%q<em-http-request>, [">= 0"])
|
53
|
-
s.add_runtime_dependency(%q<http_request.rb>, [">= 0"])
|
54
52
|
s.add_runtime_dependency(%q<http_parser.rb>, [">= 0"])
|
55
53
|
s.add_runtime_dependency(%q<tuple>, [">= 0"])
|
56
54
|
s.add_runtime_dependency(%q<yajl-ruby>, [">= 0"])
|
57
55
|
s.add_development_dependency(%q<bdb>, [">= 0"])
|
58
56
|
s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
|
59
57
|
s.add_development_dependency(%q<minitest>, [">= 0"])
|
60
|
-
s.add_development_dependency(%q<bundler>, ["~> 1.0
|
61
|
-
s.add_development_dependency(%q<jeweler>, ["~> 1.
|
62
|
-
s.add_development_dependency(%q<
|
58
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0"])
|
59
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
|
60
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
61
|
+
s.add_development_dependency(%q<simplecov-rcov>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<travis-lint>, [">= 0"])
|
63
63
|
else
|
64
64
|
s.add_dependency(%q<eventmachine>, [">= 0"])
|
65
65
|
s.add_dependency(%q<em-http-request>, [">= 0"])
|
66
|
-
s.add_dependency(%q<http_request.rb>, [">= 0"])
|
67
66
|
s.add_dependency(%q<http_parser.rb>, [">= 0"])
|
68
67
|
s.add_dependency(%q<tuple>, [">= 0"])
|
69
68
|
s.add_dependency(%q<yajl-ruby>, [">= 0"])
|
70
69
|
s.add_dependency(%q<bdb>, [">= 0"])
|
71
70
|
s.add_dependency(%q<yard>, ["~> 0.6.0"])
|
72
71
|
s.add_dependency(%q<minitest>, [">= 0"])
|
73
|
-
s.add_dependency(%q<bundler>, ["~> 1.0
|
74
|
-
s.add_dependency(%q<jeweler>, ["~> 1.
|
75
|
-
s.add_dependency(%q<
|
72
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
73
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
74
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
75
|
+
s.add_dependency(%q<simplecov-rcov>, [">= 0"])
|
76
|
+
s.add_dependency(%q<travis-lint>, [">= 0"])
|
76
77
|
end
|
77
78
|
else
|
78
79
|
s.add_dependency(%q<eventmachine>, [">= 0"])
|
79
80
|
s.add_dependency(%q<em-http-request>, [">= 0"])
|
80
|
-
s.add_dependency(%q<http_request.rb>, [">= 0"])
|
81
81
|
s.add_dependency(%q<http_parser.rb>, [">= 0"])
|
82
82
|
s.add_dependency(%q<tuple>, [">= 0"])
|
83
83
|
s.add_dependency(%q<yajl-ruby>, [">= 0"])
|
84
84
|
s.add_dependency(%q<bdb>, [">= 0"])
|
85
85
|
s.add_dependency(%q<yard>, ["~> 0.6.0"])
|
86
86
|
s.add_dependency(%q<minitest>, [">= 0"])
|
87
|
-
s.add_dependency(%q<bundler>, ["~> 1.0
|
88
|
-
s.add_dependency(%q<jeweler>, ["~> 1.
|
89
|
-
s.add_dependency(%q<
|
87
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
88
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
89
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
90
|
+
s.add_dependency(%q<simplecov-rcov>, [">= 0"])
|
91
|
+
s.add_dependency(%q<travis-lint>, [">= 0"])
|
90
92
|
end
|
91
93
|
end
|
92
94
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: eventmachine
|
@@ -43,22 +43,6 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
-
- !ruby/object:Gem::Dependency
|
47
|
-
name: http_request.rb
|
48
|
-
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
|
-
requirements:
|
51
|
-
- - ! '>='
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: '0'
|
54
|
-
type: :runtime
|
55
|
-
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
|
-
requirements:
|
59
|
-
- - ! '>='
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
46
|
- !ruby/object:Gem::Dependency
|
63
47
|
name: http_parser.rb
|
64
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -162,7 +146,7 @@ dependencies:
|
|
162
146
|
requirements:
|
163
147
|
- - ~>
|
164
148
|
- !ruby/object:Gem::Version
|
165
|
-
version: 1.0
|
149
|
+
version: '1.0'
|
166
150
|
type: :development
|
167
151
|
prerelease: false
|
168
152
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -170,7 +154,7 @@ dependencies:
|
|
170
154
|
requirements:
|
171
155
|
- - ~>
|
172
156
|
- !ruby/object:Gem::Version
|
173
|
-
version: 1.0
|
157
|
+
version: '1.0'
|
174
158
|
- !ruby/object:Gem::Dependency
|
175
159
|
name: jeweler
|
176
160
|
requirement: !ruby/object:Gem::Requirement
|
@@ -178,7 +162,7 @@ dependencies:
|
|
178
162
|
requirements:
|
179
163
|
- - ~>
|
180
164
|
- !ruby/object:Gem::Version
|
181
|
-
version: 1.
|
165
|
+
version: 1.8.4
|
182
166
|
type: :development
|
183
167
|
prerelease: false
|
184
168
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -186,9 +170,41 @@ dependencies:
|
|
186
170
|
requirements:
|
187
171
|
- - ~>
|
188
172
|
- !ruby/object:Gem::Version
|
189
|
-
version: 1.
|
173
|
+
version: 1.8.4
|
190
174
|
- !ruby/object:Gem::Dependency
|
191
|
-
name:
|
175
|
+
name: simplecov
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ! '>='
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
182
|
+
type: :development
|
183
|
+
prerelease: false
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ! '>='
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
190
|
+
- !ruby/object:Gem::Dependency
|
191
|
+
name: simplecov-rcov
|
192
|
+
requirement: !ruby/object:Gem::Requirement
|
193
|
+
none: false
|
194
|
+
requirements:
|
195
|
+
- - ! '>='
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
198
|
+
type: :development
|
199
|
+
prerelease: false
|
200
|
+
version_requirements: !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
202
|
+
requirements:
|
203
|
+
- - ! '>='
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: '0'
|
206
|
+
- !ruby/object:Gem::Dependency
|
207
|
+
name: travis-lint
|
192
208
|
requirement: !ruby/object:Gem::Requirement
|
193
209
|
none: false
|
194
210
|
requirements:
|
@@ -214,6 +230,7 @@ extra_rdoc_files:
|
|
214
230
|
- README.rdoc
|
215
231
|
files:
|
216
232
|
- .document
|
233
|
+
- .travis.yml
|
217
234
|
- Gemfile
|
218
235
|
- Gemfile.lock
|
219
236
|
- LICENSE.txt
|
@@ -225,7 +242,9 @@ files:
|
|
225
242
|
- lib/yup/request_forwarder.rb
|
226
243
|
- lib/yup/request_handler.rb
|
227
244
|
- lib/yup/state.rb
|
245
|
+
- lib/yup/version.rb
|
228
246
|
- test/helper.rb
|
247
|
+
- test/test_persistence_yup.rb
|
229
248
|
- test/test_yup.rb
|
230
249
|
- yup.gemspec
|
231
250
|
homepage: http://github.com/neglectedvalue/yup
|
@@ -243,7 +262,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
243
262
|
version: '0'
|
244
263
|
segments:
|
245
264
|
- 0
|
246
|
-
hash:
|
265
|
+
hash: 2564994728519902623
|
247
266
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
248
267
|
none: false
|
249
268
|
requirements:
|
@@ -255,7 +274,5 @@ rubyforge_project:
|
|
255
274
|
rubygems_version: 1.8.24
|
256
275
|
signing_key:
|
257
276
|
specification_version: 3
|
258
|
-
summary:
|
259
|
-
test_files:
|
260
|
-
- test/helper.rb
|
261
|
-
- test/test_yup.rb
|
277
|
+
summary: HTTP forwarder
|
278
|
+
test_files: []
|