slow_server 0.1.2 → 0.1.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ea6a54afb4bf9a70253d27ce0acff0b0dd5178fa
4
- data.tar.gz: 3718dcc9e924ab3d5788a0af1fef4564f5ab94aa
3
+ metadata.gz: 7e993e4056d8bcf3c4f2aad4e5a826a9ff13a27f
4
+ data.tar.gz: 2ffe6af0c24b470cb1dd1d462dbacc5e3405c48b
5
5
  SHA512:
6
- metadata.gz: 54def5b25784072b04d30b44f01dd53c3fcfa785b4b816004591a6b905d3e7426d3bffde040c50c8425d8bfba840dcdccfecfd048f1567b9d8ecda261324e7ed
7
- data.tar.gz: 56e4395d19cd46f95754d6be0df4a03206e3284280f58bb0d64797f413cf41c8bbdc92bbce1406edbaeac293d58cc898c8475f42a8fccf176dd72135a4b57cd1
6
+ metadata.gz: cc65204f942fa8d813b831688f247f59371baa37de6cb593a4ba8d4cdee5f8100d47d3b8b92a2eba003e93086c40613cdd1a7f04534da9b8b3bef1d31dca1128
7
+ data.tar.gz: 9ffdf2608647676aec94d9172cae6a0c720cd89551b771fb65675288b25c502a65cd1c925585e14f1234ce31df1f8fa1547797cc2af101aff967707a02ef1fc1
data/README.md CHANGED
@@ -1,28 +1,53 @@
1
1
  # SlowServer
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/slow_server`. To experiment with that code, run `bin/console` for an interactive prompt.
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
- TODO: Delete this and the text above, and describe your gem
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
- Add this line to your application's Gemfile:
19
+ $ gem install slow_server
10
20
 
11
- ```ruby
12
- gem 'slow_server'
13
- ```
21
+ ## Usage
14
22
 
15
- And then execute:
23
+ ###slow_client
16
24
 
17
- $ bundle
25
+ slow_client is somewhat similar to slowloris, it has tunable delays for various different phases of connection.
18
26
 
19
- Or install it yourself as:
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
- $ gem install slow_server
38
+ ###slow_server
22
39
 
23
- ## Usage
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
- TODO: Write usage instructions here
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/[USERNAME]/slow_server.
60
+ Bug reports and pull requests are welcome on GitHub at https://github.com/jscholl/slow_server.
36
61
 
37
62
 
38
63
  ## License
@@ -2,6 +2,6 @@
2
2
  require 'slow_server'
3
3
 
4
4
  catch :exit do
5
- SlowServer.config.parse_opts
6
- SlowServer::Client.new().start
5
+ SlowServer.client.config.parse
6
+ SlowServer.client.start
7
7
  end
@@ -2,6 +2,6 @@
2
2
  require 'slow_server'
3
3
 
4
4
  catch :exit do
5
- SlowServer.config.parse_opts
6
- SlowServer::Server.new().start
5
+ SlowServer.server.config.parse
6
+ SlowServer.server.start
7
7
  end
@@ -6,17 +6,12 @@ require "slow_server/config"
6
6
  module SlowServer
7
7
  class << self
8
8
 
9
- def config
10
- @config ||= Config.new
9
+ def server
10
+ @server ||= Server.new
11
11
  end
12
12
 
13
- def configure
14
- yield config
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
@@ -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
- SlowServer.config
8
+ @config ||= ClientConfig.new
8
9
  end
9
10
 
10
11
  def chunk_size
11
- (request.size / config.chunks.to_f).ceil
12
+ (request_body.size / config.chunks.to_f).round
12
13
  end
13
14
 
14
15
  def chunks
15
- request.scan(/.{1,#{chunk_size}}/m)
16
+ request_body.scan(/.{1,#{chunk_size}}/m)
16
17
  end
17
18
 
18
- def request
19
- "GET /"
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("127.0.0.1", 4000) do |socket|
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
- 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
-
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
 
@@ -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 :response, :port, :chunks, :response_delay, :chunk_delay
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 (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("-r", "--response-delay SECONDS", Integer, "Response 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 }
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 parse_opts
28
- response = opts.parse!.join(" ")
29
- self.response = response unless response.empty?
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
@@ -4,7 +4,7 @@ module SlowServer
4
4
  class Server
5
5
 
6
6
  def config
7
- SlowServer.config
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.response.size / config.chunks
15
+ (config.response_body.size / config.chunks.to_f).round
16
16
  end
17
17
 
18
+
18
19
  def chunks
19
- config.response.scan(/.{1,#{chunk_size}}/m)
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
- STDERR.puts "Sending response headers"
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
@@ -1,3 +1,3 @@
1
1
  module SlowServer
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
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.2
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-06-06 00:00:00.000000000 Z
11
+ date: 2016-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler