slow_server 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +38 -13
- data/exe/slow_client +2 -2
- data/exe/slow_server +2 -2
- data/lib/slow_server.rb +4 -9
- data/lib/slow_server/client.rb +29 -19
- data/lib/slow_server/config.rb +53 -11
- data/lib/slow_server/server.rb +48 -19
- data/lib/slow_server/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e993e4056d8bcf3c4f2aad4e5a826a9ff13a27f
|
4
|
+
data.tar.gz: 2ffe6af0c24b470cb1dd1d462dbacc5e3405c48b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc65204f942fa8d813b831688f247f59371baa37de6cb593a4ba8d4cdee5f8100d47d3b8b92a2eba003e93086c40613cdd1a7f04534da9b8b3bef1d31dca1128
|
7
|
+
data.tar.gz: 9ffdf2608647676aec94d9172cae6a0c720cd89551b771fb65675288b25c502a65cd1c925585e14f1234ce31df1f8fa1547797cc2af101aff967707a02ef1fc1
|
data/README.md
CHANGED
@@ -1,28 +1,53 @@
|
|
1
1
|
# SlowServer
|
2
2
|
|
3
|
-
|
3
|
+
This Gem has two parts, slow_server and slow_client, together they can be used to
|
4
|
+
test timeouts in various stages of connections.
|
4
5
|
|
5
|
-
|
6
|
+
Currently there are two types of delays supported by both client and server,
|
7
|
+
initial delay and chunk delay.
|
8
|
+
|
9
|
+
Initial delay (--delay) causes both client and server to wait some amount of
|
10
|
+
seconds after the connection is opened before sending anything.
|
11
|
+
|
12
|
+
Chunk delay (--chunk-delay) works together with chunk count (--chunks) to split the
|
13
|
+
message into chunks and send them one chunk at a time with some delay between. You
|
14
|
+
can think of this similar to Chunked Encoding for HTTP, but it's not actually using
|
15
|
+
Chunked Encoding
|
6
16
|
|
7
17
|
## Installation
|
8
18
|
|
9
|
-
|
19
|
+
$ gem install slow_server
|
10
20
|
|
11
|
-
|
12
|
-
gem 'slow_server'
|
13
|
-
```
|
21
|
+
## Usage
|
14
22
|
|
15
|
-
|
23
|
+
###slow_client
|
16
24
|
|
17
|
-
|
25
|
+
slow_client is somewhat similar to slowloris, it has tunable delays for various different phases of connection.
|
18
26
|
|
19
|
-
|
27
|
+
```
|
28
|
+
$ slow_client --help
|
29
|
+
Usage: slow_client [OPTIONS] [URI]
|
30
|
+
-X, --method METHOD Request Method (default: GET)
|
31
|
+
-p, --port NUMBER Listen Port (default: 4000)
|
32
|
+
-c, --chunks BYTES Chunks (default: 1)
|
33
|
+
-d, --delay SECONDS Transmission delay after connecting (default: 0)
|
34
|
+
-k, --chunk-delay SECONDS Delay between chunks (default: 0)
|
35
|
+
-v, --version Show Version
|
36
|
+
```
|
20
37
|
|
21
|
-
|
38
|
+
###slow_server
|
22
39
|
|
23
|
-
|
40
|
+
slow_server is the counterpart to slow_client, by default it will respond with the story of The Tortoise and The Hare, but it too has tunable delays.
|
24
41
|
|
25
|
-
|
42
|
+
```
|
43
|
+
$ slow_server --help
|
44
|
+
Usage: slow_server [OPTIONS] [RESPONSE]
|
45
|
+
-p, --port NUMBER Listen Port (default: 4000)
|
46
|
+
-c, --chunks BYTES Chunks (default: 1)
|
47
|
+
-d, --delay SECONDS Transmission delay after connecting (default: 0)
|
48
|
+
-k, --chunk-delay SECONDS Delay between chunks (default: 0)
|
49
|
+
-v, --version Show Version
|
50
|
+
```
|
26
51
|
|
27
52
|
## Development
|
28
53
|
|
@@ -32,7 +57,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
32
57
|
|
33
58
|
## Contributing
|
34
59
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
60
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/jscholl/slow_server.
|
36
61
|
|
37
62
|
|
38
63
|
## License
|
data/exe/slow_client
CHANGED
data/exe/slow_server
CHANGED
data/lib/slow_server.rb
CHANGED
@@ -6,17 +6,12 @@ require "slow_server/config"
|
|
6
6
|
module SlowServer
|
7
7
|
class << self
|
8
8
|
|
9
|
-
def
|
10
|
-
@
|
9
|
+
def server
|
10
|
+
@server ||= Server.new
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
def configure!
|
18
|
-
@config = Config.new
|
19
|
-
yield config
|
13
|
+
def client
|
14
|
+
@client ||= Client.new
|
20
15
|
end
|
21
16
|
|
22
17
|
end
|
data/lib/slow_server/client.rb
CHANGED
@@ -1,41 +1,51 @@
|
|
1
1
|
require 'socket'
|
2
|
+
require 'uri'
|
2
3
|
|
3
4
|
module SlowServer
|
4
5
|
class Client
|
5
6
|
|
6
7
|
def config
|
7
|
-
|
8
|
+
@config ||= ClientConfig.new
|
8
9
|
end
|
9
10
|
|
10
11
|
def chunk_size
|
11
|
-
(
|
12
|
+
(request_body.size / config.chunks.to_f).round
|
12
13
|
end
|
13
14
|
|
14
15
|
def chunks
|
15
|
-
|
16
|
+
request_body.scan(/.{1,#{chunk_size}}/m)
|
16
17
|
end
|
17
18
|
|
18
|
-
def
|
19
|
-
|
19
|
+
def request_body
|
20
|
+
[].tap do |req|
|
21
|
+
req << [config.request_method, config.request_uri].join(" ")
|
22
|
+
req += config.request_headers
|
23
|
+
end.join("\n")
|
24
|
+
end
|
25
|
+
|
26
|
+
def send_request(socket)
|
27
|
+
chunks.each do |chunk|
|
28
|
+
STDERR.puts "Sending #{chunk_size} bytes"
|
29
|
+
socket.print chunk
|
30
|
+
STDERR.puts "Waiting for #{config.chunk_delay} seconds"
|
31
|
+
sleep config.chunk_delay
|
32
|
+
end
|
33
|
+
socket.print("\r\n\r\n")
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_response(socket)
|
37
|
+
response = socket.read
|
38
|
+
headers,body = response.split("\r\n\r\n", 2)
|
39
|
+
STDERR.puts "\n"
|
40
|
+
print body
|
20
41
|
end
|
21
42
|
|
22
43
|
def start
|
23
|
-
TCPSocket.open(
|
44
|
+
TCPSocket.open(config.host, config.port) do |socket|
|
24
45
|
STDERR.puts "Waiting for #{config.response_delay} seconds"
|
25
46
|
sleep config.response_delay
|
26
|
-
|
27
|
-
|
28
|
-
STDERR.puts "Sending #{chunk_size} bytes"
|
29
|
-
socket.print chunk
|
30
|
-
STDERR.puts "Waiting for #{config.chunk_delay} seconds"
|
31
|
-
sleep config.chunk_delay
|
32
|
-
end
|
33
|
-
socket.print("\r\n\r\n")
|
34
|
-
|
35
|
-
response = socket.read
|
36
|
-
headers,body = response.split("\r\n\r\n", 2)
|
37
|
-
STDERR.puts "\n"
|
38
|
-
print body
|
47
|
+
send_request(socket)
|
48
|
+
get_response(socket)
|
39
49
|
end
|
40
50
|
end
|
41
51
|
|
data/lib/slow_server/config.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'optparse'
|
2
|
+
require 'uri'
|
2
3
|
|
3
4
|
module SlowServer
|
5
|
+
|
4
6
|
class Config
|
5
|
-
attr_accessor :
|
7
|
+
attr_accessor :port, :chunks, :response_delay, :chunk_delay
|
6
8
|
|
7
9
|
def initialize
|
8
|
-
# set defaults
|
9
|
-
self.response = File.read(File.expand_path("../../../files/response.txt", __FILE__))
|
10
10
|
self.port = 4000
|
11
11
|
self.chunks = 1
|
12
12
|
self.response_delay = 0
|
@@ -16,17 +16,59 @@ module SlowServer
|
|
16
16
|
def opts
|
17
17
|
@opts ||= OptionParser.new do |opt|
|
18
18
|
opt.banner = "Usage: #{File.basename($PROGRAM_NAME)} [OPTIONS] [RESPONSE]"
|
19
|
-
opt.on("-p", "--port NUMBER", Integer, "Listen Port
|
20
|
-
opt.on("-c", "--chunks BYTES", Integer, "Chunks
|
21
|
-
opt.on("-
|
22
|
-
opt.on("-k", "--chunk-delay SECONDS", Float, "Delay between chunks
|
23
|
-
opt.on("-v", "--version", "Show Version")
|
19
|
+
opt.on("-p", "--port NUMBER", Integer, "Listen Port (default: #{self.port})") { |v| self.port = v }
|
20
|
+
opt.on("-c", "--chunks BYTES", Integer, "Chunks (default: #{self.chunks})") { |v| self.chunks = v }
|
21
|
+
opt.on("-d", "--delay SECONDS", Integer, "Transmission delay after connecting (default: #{self.response_delay})") { |v| self.response_delay = v }
|
22
|
+
opt.on("-k", "--chunk-delay SECONDS", Float, "Delay between chunks (default: #{self.chunk_delay})") { |v| self.chunk_delay = v }
|
23
|
+
opt.on("-v", "--version", "Show Version") { puts SlowServer::VERSION; throw :exit }
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
27
|
+
def args
|
28
|
+
opts.parse!
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class ServerConfig < Config
|
33
|
+
attr_accessor :response_body
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
super
|
37
|
+
self.response_body = File.read(File.expand_path("../../../files/response.txt", __FILE__))
|
38
|
+
end
|
39
|
+
|
40
|
+
def parse
|
41
|
+
response = args.join(" ")
|
42
|
+
self.response_body = response unless response.empty?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class ClientConfig < Config
|
47
|
+
attr_accessor :request_method, :host, :request_uri, :request_headers
|
48
|
+
|
49
|
+
def initialize
|
50
|
+
super
|
51
|
+
self.request_method = 'GET'
|
52
|
+
self.host = 'localhost'
|
53
|
+
self.request_uri = "/"
|
54
|
+
self.request_headers = []
|
55
|
+
end
|
56
|
+
|
57
|
+
def opts
|
58
|
+
opts = super
|
59
|
+
opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} [OPTIONS] [URI]"
|
60
|
+
opts.on_head("-p", "--port NUMBER", Integer, "Listen Port (default: #{self.port})") { |v| @port_override = v }
|
61
|
+
opts.on_head("-X", "--method METHOD", String, "Request Method (default: #{self.request_method})") { |v| self.request_method = v }
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse
|
65
|
+
uri = URI.parse(args[0])
|
66
|
+
if !uri.respond_to?(:request_uri) && uri !~ %r{^\w+:\/\/}
|
67
|
+
uri = URI.parse("http://#{uri}")
|
68
|
+
end
|
69
|
+
self.host = uri.host
|
70
|
+
self.port = @port_override || uri.port
|
71
|
+
self.request_uri = uri.request_uri if uri.request_uri
|
30
72
|
end
|
31
73
|
|
32
74
|
end
|
data/lib/slow_server/server.rb
CHANGED
@@ -4,7 +4,7 @@ module SlowServer
|
|
4
4
|
class Server
|
5
5
|
|
6
6
|
def config
|
7
|
-
|
7
|
+
@config ||= ServerConfig.new
|
8
8
|
end
|
9
9
|
|
10
10
|
def server
|
@@ -12,35 +12,64 @@ module SlowServer
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def chunk_size
|
15
|
-
config.
|
15
|
+
(config.response_body.size / config.chunks.to_f).round
|
16
16
|
end
|
17
17
|
|
18
|
+
|
18
19
|
def chunks
|
19
|
-
config.
|
20
|
+
#config.response_body.scan(/.{1,#{chunk_size}}/m)
|
21
|
+
# NOTE: to be accurate, chunk_size needs to be variable, or at least increase in a giant burst at the end
|
22
|
+
#
|
23
|
+
|
24
|
+
offset = 0
|
25
|
+
length = 0
|
26
|
+
out = []
|
27
|
+
config.chunks.times do |i|
|
28
|
+
length = ((config.response_body.size - out.join.size) / (config.chunks - i).to_f).floor
|
29
|
+
out << config.response_body[offset..(offset+length)]
|
30
|
+
offset += length + 1
|
31
|
+
end
|
32
|
+
out
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def response_headers
|
37
|
+
[].tap do |h|
|
38
|
+
h << "HTTP/1.1 200 OK"
|
39
|
+
h << "Content-Type: text/plain"
|
40
|
+
h << "Content-Length: #{config.response_body.size}"
|
41
|
+
h << "Connection: close"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_request(socket)
|
46
|
+
STDERR.puts socket.gets
|
47
|
+
STDERR.puts "Waiting for #{config.response_delay} seconds"
|
48
|
+
end
|
49
|
+
|
50
|
+
def send_response(socket)
|
51
|
+
STDERR.puts "Sending response headers"
|
52
|
+
socket.print response_headers.join("\n")
|
53
|
+
socket.print "\r\n\r\n"
|
54
|
+
chunks.each do |chunk|
|
55
|
+
STDERR.puts "Sending #{chunk_size} bytes"
|
56
|
+
socket.print chunk
|
57
|
+
STDERR.puts "Waiting for #{config.chunk_delay} seconds"
|
58
|
+
sleep config.chunk_delay
|
59
|
+
end
|
20
60
|
end
|
21
61
|
|
22
62
|
def start
|
23
63
|
loop do
|
24
64
|
Thread.start(server.accept) do |socket|
|
65
|
+
STDERR.puts "Accepted Connection"
|
66
|
+
# NOTE: consider putting a delay in here too
|
67
|
+
get_request(socket)
|
25
68
|
|
26
|
-
STDERR.puts socket.gets
|
27
|
-
STDERR.puts "Waiting for #{config.response_delay} seconds"
|
28
69
|
sleep config.response_delay
|
29
70
|
|
30
|
-
|
31
|
-
|
32
|
-
socket.print "HTTP/1.1 200 OK\r\n" +
|
33
|
-
"Content-Type: text/plain\r\n" +
|
34
|
-
"Content-Length: #{config.response.bytesize}\r\n" +
|
35
|
-
"Connection: close\r\n\r\n"
|
36
|
-
|
37
|
-
chunks.each do |chunk|
|
38
|
-
STDERR.puts "Sending #{chunk_size} bytes"
|
39
|
-
socket.print chunk
|
40
|
-
STDERR.puts "Waiting for #{config.chunk_delay} seconds"
|
41
|
-
sleep config.chunk_delay
|
42
|
-
end
|
43
|
-
|
71
|
+
send_response(socket)
|
72
|
+
STDERR.print "\n"
|
44
73
|
socket.close
|
45
74
|
end
|
46
75
|
end
|
data/lib/slow_server/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slow_server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Scholl
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|