slowweb 0.1.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/README.md +47 -0
- data/lib/slow_web.rb +58 -0
- data/lib/slow_web/ext/net_http.rb +25 -0
- data/lib/slow_web/limit.rb +75 -0
- data/lib/slow_web/version.rb +3 -0
- data/lib/slowweb.rb +1 -0
- metadata +104 -0
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
Slow Web - An HTTP Request Governor
|
2
|
+
===================================
|
3
|
+
|
4
|
+
## DESCRIPTION
|
5
|
+
|
6
|
+
Slow Web is a way to limit the number of requests to a domain within a certain
|
7
|
+
period of time.
|
8
|
+
|
9
|
+
For example, the GitHub API only allows for 60 requests per minute. Slow Web can
|
10
|
+
monitor the number of calls to that domain and will sleep on the next request
|
11
|
+
that is over the limit.
|
12
|
+
|
13
|
+
Slow Web follows the rules of [Semantic Versioning](http://semver.org/).
|
14
|
+
|
15
|
+
|
16
|
+
## RUNNING
|
17
|
+
|
18
|
+
To install Slow Web, simply install the gem:
|
19
|
+
|
20
|
+
$ [sudo] gem install slowweb
|
21
|
+
|
22
|
+
And specify the domain to limit.
|
23
|
+
|
24
|
+
require 'slowweb'
|
25
|
+
SlowWeb.limit('github.com', 10, 60)
|
26
|
+
|
27
|
+
This restricts the `github.com` domain to only allowing `10` requests every
|
28
|
+
`60` seconds (or one minute).
|
29
|
+
|
30
|
+
|
31
|
+
## CONTRIBUTE
|
32
|
+
|
33
|
+
If you'd like to contribute to SlowWeb, start by forking the repository
|
34
|
+
on GitHub:
|
35
|
+
|
36
|
+
http://github.com/benbjohnson/slowweb
|
37
|
+
|
38
|
+
Then follow these steps to send your changes:
|
39
|
+
|
40
|
+
1. Clone down your fork
|
41
|
+
1. Create a topic branch to contain your change
|
42
|
+
1. Code
|
43
|
+
1. All code must have MiniTest::Unit test coverage.
|
44
|
+
1. If you are adding new functionality, document it in the README
|
45
|
+
1. If necessary, rebase your commits into logical chunks, without errors
|
46
|
+
1. Push the branch up to GitHub
|
47
|
+
1. Send me a pull request for your branch
|
data/lib/slow_web.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'slow_web/version'
|
2
|
+
require 'slow_web/limit'
|
3
|
+
require 'slow_web/ext/net_http'
|
4
|
+
|
5
|
+
class SlowWeb
|
6
|
+
##############################################################################
|
7
|
+
# Static Initialization
|
8
|
+
##############################################################################
|
9
|
+
|
10
|
+
# A look up of limits by host.
|
11
|
+
@limits = {}
|
12
|
+
|
13
|
+
|
14
|
+
##############################################################################
|
15
|
+
# Static Methods
|
16
|
+
##############################################################################
|
17
|
+
|
18
|
+
# Limits the number of requests that can occur within a specified number of
|
19
|
+
# seconds.
|
20
|
+
#
|
21
|
+
# @param [String] host the host to restrict.
|
22
|
+
# @param [Fixnum] count the number of requests that can occur within a time period.
|
23
|
+
# @param [Fixnum] period the number of seconds in the time period.
|
24
|
+
#
|
25
|
+
# @return [SlowWeb::Limit] the limit object.
|
26
|
+
def self.limit(host, count, period)
|
27
|
+
raise "Limit already exists for this host: #{host}" if @limits[host]
|
28
|
+
|
29
|
+
limit = Limit.new(host, count, period)
|
30
|
+
@limits[host] = limit
|
31
|
+
return limit
|
32
|
+
end
|
33
|
+
|
34
|
+
# Retrieves the limit object for a given host.
|
35
|
+
#
|
36
|
+
# @param [String] host the host associated with the limit.
|
37
|
+
#
|
38
|
+
# @return [SlowWeb::Limit] the limit object.
|
39
|
+
def self.get_limit(host)
|
40
|
+
return @limits[host]
|
41
|
+
end
|
42
|
+
|
43
|
+
# A flag stating if the limit for a given host has been exceeded.
|
44
|
+
#
|
45
|
+
# @param [String] host the host that is being limited.
|
46
|
+
#
|
47
|
+
# @return [Boolean] a flag stating if the limit has been exceeded.
|
48
|
+
def self.limit_exceeded?(host)
|
49
|
+
limit = @limits[host]
|
50
|
+
return !limit.nil? && limit.exceeded?
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
# Removes all limits.
|
55
|
+
def self.reset
|
56
|
+
@limits = {}
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
|
4
|
+
module Net
|
5
|
+
class HTTP
|
6
|
+
def request_with_slowweb(request, body = nil, &block)
|
7
|
+
host = self.address
|
8
|
+
limit = SlowWeb.get_limit(host)
|
9
|
+
|
10
|
+
# Wait until the request limit is no longer exceeded
|
11
|
+
while limit.exceeded?
|
12
|
+
sleep 1
|
13
|
+
end
|
14
|
+
|
15
|
+
# Add request to limiter
|
16
|
+
limit.add_request(request)
|
17
|
+
|
18
|
+
# Continue with the original request
|
19
|
+
request_without_slowweb(request, body, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
alias_method :request_without_slowweb, :request
|
23
|
+
alias_method :request, :request_with_slowweb
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'slow_web/version'
|
2
|
+
|
3
|
+
class SlowWeb
|
4
|
+
class Limit
|
5
|
+
############################################################################
|
6
|
+
# Constructor
|
7
|
+
############################################################################
|
8
|
+
|
9
|
+
# @param [String] host the host to restrict.
|
10
|
+
# @param [Fixnum] count the number of requests that can occur within a time
|
11
|
+
# period.
|
12
|
+
# @param [Fixnum] period the number of seconds in the time period.
|
13
|
+
def initialize(host, count, period)
|
14
|
+
@host = host
|
15
|
+
@count = count
|
16
|
+
@period = period
|
17
|
+
|
18
|
+
@requests = []
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
############################################################################
|
23
|
+
# Public Attributes
|
24
|
+
############################################################################
|
25
|
+
|
26
|
+
# The host to restrict.
|
27
|
+
attr_accessor :host
|
28
|
+
|
29
|
+
# The number of requests that are allowed within the time period.
|
30
|
+
attr_accessor :count
|
31
|
+
|
32
|
+
# The number of seconds in the time period.
|
33
|
+
attr_accessor :period
|
34
|
+
|
35
|
+
# The number of requests that have occurred within the current period.
|
36
|
+
def current_request_count
|
37
|
+
normalize_requests()
|
38
|
+
return @requests.length
|
39
|
+
end
|
40
|
+
|
41
|
+
# A flag stating if the number of requests within the period has been met.
|
42
|
+
def exceeded?
|
43
|
+
return current_request_count >= count
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
############################################################################
|
48
|
+
# Public Methods
|
49
|
+
############################################################################
|
50
|
+
|
51
|
+
# Adds a request that is associated with this limit
|
52
|
+
#
|
53
|
+
# @param [Net::HTTPRequest] request the request associated with this limit.
|
54
|
+
def add_request(request)
|
55
|
+
@requests << {:obj => request, :time => Time.now}
|
56
|
+
normalize_requests()
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
############################################################################
|
62
|
+
# Private Methods
|
63
|
+
############################################################################
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Removes all items in the request list that are outside of the current
|
68
|
+
# period.
|
69
|
+
def normalize_requests
|
70
|
+
@requests = @requests.find_all do |request|
|
71
|
+
(Time.now-request[:time]) < period
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/slowweb.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'slow_web'
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: slowweb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Ben Johnson
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-27 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 31
|
30
|
+
segments:
|
31
|
+
- 2
|
32
|
+
- 4
|
33
|
+
- 0
|
34
|
+
version: 2.4.0
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: fakeweb
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 27
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 3
|
49
|
+
- 0
|
50
|
+
version: 1.3.0
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
53
|
+
description:
|
54
|
+
email:
|
55
|
+
- benbjohnson@yahoo.com
|
56
|
+
executables: []
|
57
|
+
|
58
|
+
extensions: []
|
59
|
+
|
60
|
+
extra_rdoc_files: []
|
61
|
+
|
62
|
+
files:
|
63
|
+
- lib/slow_web/ext/net_http.rb
|
64
|
+
- lib/slow_web/limit.rb
|
65
|
+
- lib/slow_web/version.rb
|
66
|
+
- lib/slow_web.rb
|
67
|
+
- lib/slowweb.rb
|
68
|
+
- README.md
|
69
|
+
has_rdoc: true
|
70
|
+
homepage: http://github.com/benbjohnson/slowweb
|
71
|
+
licenses: []
|
72
|
+
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
hash: 3
|
84
|
+
segments:
|
85
|
+
- 0
|
86
|
+
version: "0"
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
hash: 3
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
version: "0"
|
96
|
+
requirements: []
|
97
|
+
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 1.4.1
|
100
|
+
signing_key:
|
101
|
+
specification_version: 3
|
102
|
+
summary: An HTTP Request Governor
|
103
|
+
test_files: []
|
104
|
+
|