fast_send 1.1.2 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +51 -0
- data/Gemfile +2 -4
- data/Rakefile +3 -36
- data/fast_send.gemspec +7 -44
- data/lib/fast_send/socket_handler.rb +10 -13
- data/lib/fast_send/version.rb +3 -0
- data/lib/fast_send.rb +5 -7
- data/spec/fast_send_with_mocks_spec.rb +4 -2
- data/spec/fast_send_with_puma_spec.rb +1 -0
- metadata +4 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea16771c56f207d5c40555b4981d955e20724177
|
4
|
+
data.tar.gz: 691ef28f1a505a95c974c5060fc66eff6147ad22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a354a9e5b09f1d598147428351f824f39b42c1b29e48633ee334791bc68d3c95edc2bc551f2eb7087c8e29f37b20c9ff33360528f7424422e0de403575cda916
|
7
|
+
data.tar.gz: eae00df9f9beb28595c2de2023915245525a27892ed67e2b6ee192446e24822b9f80929051ad6f1bf1a8bb2ede2529bf3ae6364758db3166097ff8466d9ac7e3
|
data/.gitignore
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# rcov generated
|
2
|
+
coverage
|
3
|
+
coverage.data
|
4
|
+
|
5
|
+
# rdoc generated
|
6
|
+
rdoc
|
7
|
+
|
8
|
+
# yard generated
|
9
|
+
doc
|
10
|
+
.yardoc
|
11
|
+
|
12
|
+
# bundler
|
13
|
+
.bundle
|
14
|
+
Gemfile.lock
|
15
|
+
.ruby-version
|
16
|
+
|
17
|
+
# jeweler generated
|
18
|
+
pkg
|
19
|
+
|
20
|
+
# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
|
21
|
+
#
|
22
|
+
# * Create a file at ~/.gitignore
|
23
|
+
# * Include files you want ignored
|
24
|
+
# * Run: git config --global core.excludesfile ~/.gitignore
|
25
|
+
#
|
26
|
+
# After doing this, these files will be ignored in all your git projects,
|
27
|
+
# saving you from having to 'pollute' every project you touch with them
|
28
|
+
#
|
29
|
+
# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
|
30
|
+
#
|
31
|
+
# For MacOS:
|
32
|
+
#
|
33
|
+
#.DS_Store
|
34
|
+
|
35
|
+
# For TextMate
|
36
|
+
#*.tmproj
|
37
|
+
#tmtags
|
38
|
+
|
39
|
+
# For emacs:
|
40
|
+
#*~
|
41
|
+
#\#*
|
42
|
+
#.\#*
|
43
|
+
|
44
|
+
# For vim:
|
45
|
+
#*.swp
|
46
|
+
|
47
|
+
# For redcar:
|
48
|
+
#.redcar
|
49
|
+
|
50
|
+
# For rubinius:
|
51
|
+
#*.rbc
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1,38 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'rubygems'
|
4
|
-
require 'bundler'
|
5
|
-
begin
|
6
|
-
Bundler.setup(:default, :development)
|
7
|
-
rescue Bundler::BundlerError => e
|
8
|
-
$stderr.puts e.message
|
9
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
-
exit e.status_code
|
11
|
-
end
|
12
|
-
require 'rake'
|
13
|
-
|
14
|
-
require 'jeweler'
|
15
|
-
require_relative 'lib/fast_send'
|
16
|
-
Jeweler::Tasks.new do |gem|
|
17
|
-
gem.version = FastSend::VERSION
|
18
|
-
gem.name = "fast_send"
|
19
|
-
gem.homepage = "https://github.com/WeTransfer/fast_send"
|
20
|
-
gem.license = "MIT"
|
21
|
-
gem.description = %Q{Send bursts of large files quickly via Rack}
|
22
|
-
gem.summary = %Q{and do so bypassing the Ruby VM}
|
23
|
-
gem.email = "me@julik.nl"
|
24
|
-
gem.authors = ["Julik Tarkhanov"]
|
25
|
-
# dependencies defined in Gemfile
|
26
|
-
end
|
27
|
-
|
28
|
-
Jeweler::RubygemsDotOrgTasks.new
|
29
|
-
|
30
|
-
require 'rspec/core'
|
1
|
+
require 'bundler/gem_tasks'
|
31
2
|
require 'rspec/core/rake_task'
|
32
|
-
RSpec::Core::RakeTask.new(:spec) do |spec|
|
33
|
-
spec.rspec_opts = ["-c"]
|
34
|
-
spec.pattern = FileList['spec/**/*_spec.rb']
|
35
|
-
end
|
36
|
-
|
37
|
-
task :default => :spec
|
38
3
|
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
task default: :spec
|
data/fast_send.gemspec
CHANGED
@@ -1,13 +1,9 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
1
|
# -*- encoding: utf-8 -*-
|
5
2
|
# stub: fast_send 1.1.2 ruby lib
|
6
|
-
|
3
|
+
require File.dirname(__FILE__) + '/lib/fast_send/version'
|
7
4
|
Gem::Specification.new do |s|
|
8
5
|
s.name = "fast_send"
|
9
|
-
s.version =
|
10
|
-
|
6
|
+
s.version = FastSend::VERSION
|
11
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
8
|
s.require_paths = ["lib"]
|
13
9
|
s.authors = ["Julik Tarkhanov"]
|
@@ -18,47 +14,14 @@ Gem::Specification.new do |s|
|
|
18
14
|
"LICENSE.txt",
|
19
15
|
"README.md"
|
20
16
|
]
|
21
|
-
s.files =
|
22
|
-
"Gemfile",
|
23
|
-
"LICENSE.txt",
|
24
|
-
"README.md",
|
25
|
-
"Rakefile",
|
26
|
-
"config.ru",
|
27
|
-
"fast_send.gemspec",
|
28
|
-
"lib/fast_send.rb",
|
29
|
-
"lib/fast_send/null_logger.rb",
|
30
|
-
"lib/fast_send/socket_handler.rb",
|
31
|
-
"spec/fast_send_with_mocks_spec.rb",
|
32
|
-
"spec/fast_send_with_puma_spec.rb",
|
33
|
-
"spec/test_app.ru"
|
34
|
-
]
|
17
|
+
s.files = `git ls-files -z`.split("\x0")
|
35
18
|
s.homepage = "https://github.com/WeTransfer/fast_send"
|
36
19
|
s.licenses = ["MIT"]
|
37
20
|
s.rubygems_version = "2.4.5.1"
|
38
21
|
s.summary = "and do so bypassing the Ruby VM"
|
39
22
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
s.add_development_dependency(%q<rake>, [">= 0"])
|
45
|
-
s.add_development_dependency(%q<jeweler>, ["> 2"])
|
46
|
-
s.add_development_dependency(%q<rspec>, ["~> 3"])
|
47
|
-
s.add_development_dependency(%q<puma>, [">= 0"])
|
48
|
-
s.add_development_dependency(%q<sendfile>, [">= 0"])
|
49
|
-
else
|
50
|
-
s.add_dependency(%q<rake>, [">= 0"])
|
51
|
-
s.add_dependency(%q<jeweler>, ["> 2"])
|
52
|
-
s.add_dependency(%q<rspec>, ["~> 3"])
|
53
|
-
s.add_dependency(%q<puma>, [">= 0"])
|
54
|
-
s.add_dependency(%q<sendfile>, [">= 0"])
|
55
|
-
end
|
56
|
-
else
|
57
|
-
s.add_dependency(%q<rake>, [">= 0"])
|
58
|
-
s.add_dependency(%q<jeweler>, ["> 2"])
|
59
|
-
s.add_dependency(%q<rspec>, ["~> 3"])
|
60
|
-
s.add_dependency(%q<puma>, [">= 0"])
|
61
|
-
s.add_dependency(%q<sendfile>, [">= 0"])
|
62
|
-
end
|
23
|
+
s.add_development_dependency("rake", [">= 0"])
|
24
|
+
s.add_development_dependency("rspec", ["~> 3"])
|
25
|
+
s.add_development_dependency("puma", [">= 0"])
|
26
|
+
s.add_development_dependency("sendfile", [">= 0"])
|
63
27
|
end
|
64
|
-
|
@@ -12,6 +12,10 @@ class FastSend::SocketHandler < Struct.new(:stream, :logger, :started_proc, :abo
|
|
12
12
|
# Is raised when it is not possible to send a chunk of data
|
13
13
|
# to the client using non-blocking sends for longer than the preset timeout
|
14
14
|
SlowLoris = Class.new(StandardError)
|
15
|
+
|
16
|
+
# Exceptions that indicate a client being too slow or dropping out
|
17
|
+
# due to failing reads/writes
|
18
|
+
CLIENT_DISCONNECT_EXCEPTIONS = [SlowLoris] + ::FastSend::CLIENT_DISCONNECTS
|
15
19
|
|
16
20
|
# Whether we are forced to use blocking IO for sendfile()
|
17
21
|
USE_BLOCKING_SENDFILE = !!(RUBY_PLATFORM =~ /darwin/)
|
@@ -55,13 +59,14 @@ class FastSend::SocketHandler < Struct.new(:stream, :logger, :started_proc, :abo
|
|
55
59
|
|
56
60
|
logger.info { "Response written in full - %d bytes" % bytes_written }
|
57
61
|
done_proc.call(bytes_written)
|
58
|
-
rescue *
|
62
|
+
rescue *CLIENT_DISCONNECT_EXCEPTIONS => e
|
59
63
|
logger.warn { "Client closed connection: #{e.class}(#{e.message})" }
|
60
64
|
aborted_proc.call(e)
|
61
65
|
rescue Exception => e
|
62
|
-
logger.fatal { "Aborting response due to error: #{e.class}(#{e.message})" }
|
66
|
+
logger.fatal { "Aborting response due to error: #{e.class}(#{e.message}) and will propagate" }
|
63
67
|
aborted_proc.call(e)
|
64
68
|
error_proc.call(e)
|
69
|
+
raise e unless StandardError === e # Re-raise system errors, signals and other Exceptions
|
65
70
|
ensure
|
66
71
|
# With rack.hijack the consensus seems to be that the hijack
|
67
72
|
# proc is responsible for closing the socket. We also use no-keepalive
|
@@ -71,14 +76,6 @@ class FastSend::SocketHandler < Struct.new(:stream, :logger, :started_proc, :abo
|
|
71
76
|
cleanup_proc.call(bytes_written)
|
72
77
|
end
|
73
78
|
end
|
74
|
-
|
75
|
-
# Returns an array of Exception classes we can rescue from (using a splat)
|
76
|
-
#
|
77
|
-
# @return [Array<Class>] the classes
|
78
|
-
def client_disconnect_exeptions
|
79
|
-
[SlowLoris] + ::FastSend::CLIENT_DISCONNECTS
|
80
|
-
end
|
81
|
-
|
82
79
|
|
83
80
|
# This is majorly useful - if the socket is not selectable after a certain
|
84
81
|
# timeout, it might be a slow loris or a connection that hung up on us. So if
|
@@ -110,7 +107,7 @@ class FastSend::SocketHandler < Struct.new(:stream, :logger, :started_proc, :abo
|
|
110
107
|
#
|
111
108
|
# @param socket[Socket] the socket to write to
|
112
109
|
# @param file[File] the IO you can read from
|
113
|
-
# @yields num_bytes_written[
|
110
|
+
# @yields num_bytes_written[Integer] the number of bytes written on each `IO.copy_stream() call`
|
114
111
|
# @return [void]
|
115
112
|
def sendfile(socket, file)
|
116
113
|
chunk = SENDFILE_CHUNK_SIZE
|
@@ -152,7 +149,7 @@ class FastSend::SocketHandler < Struct.new(:stream, :logger, :started_proc, :abo
|
|
152
149
|
#
|
153
150
|
# @param socket[Socket] the socket to write to
|
154
151
|
# @param file[File] the IO you can read from
|
155
|
-
# @yields num_bytes_written[
|
152
|
+
# @yields num_bytes_written[Integer] the number of bytes written on each `IO.copy_stream() call`
|
156
153
|
# @return [void]
|
157
154
|
def copy_stream(socket, file)
|
158
155
|
chunk = SENDFILE_CHUNK_SIZE
|
@@ -177,7 +174,7 @@ class FastSend::SocketHandler < Struct.new(:stream, :logger, :started_proc, :abo
|
|
177
174
|
#
|
178
175
|
# @param socket[Socket] the socket to write to
|
179
176
|
# @param file[File] the IO you can read from
|
180
|
-
# @yields num_bytes_written[
|
177
|
+
# @yields num_bytes_written[Integer] the number of bytes written on each `IO.copy_stream() call`
|
181
178
|
# @return [void]
|
182
179
|
def copy_nio(socket, file)
|
183
180
|
chunk = SENDFILE_CHUNK_SIZE
|
data/lib/fast_send.rb
CHANGED
@@ -31,11 +31,6 @@
|
|
31
31
|
# `fast_send.error' => ->(exception) { } # the response is not sent completely due to an error in the application
|
32
32
|
# `fast_send.cleanup' => ->(sent_total) { } # Called at the end of the response, in an ensure block
|
33
33
|
class FastSend
|
34
|
-
require_relative 'fast_send/socket_handler'
|
35
|
-
require_relative 'fast_send/null_logger'
|
36
|
-
|
37
|
-
VERSION = '1.1.2'
|
38
|
-
|
39
34
|
# All exceptions that get raised when the client closes a connection before receiving the entire response
|
40
35
|
CLIENT_DISCONNECTS = [Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN, Errno::EPROTOTYPE]
|
41
36
|
|
@@ -43,8 +38,11 @@ class FastSend
|
|
43
38
|
require 'java'
|
44
39
|
CLIENT_DISCONNECTS << Java::JavaIo::IOException
|
45
40
|
end
|
46
|
-
|
47
|
-
|
41
|
+
|
42
|
+
require_relative 'fast_send/version'
|
43
|
+
require_relative 'fast_send/socket_handler'
|
44
|
+
require_relative 'fast_send/null_logger'
|
45
|
+
|
48
46
|
# Gets raised if a fast_send.something is mentioned in
|
49
47
|
# the response headers but is not supported as a callback
|
50
48
|
# (the dangers of hashmaps as datastructures is that you
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require_relative '../lib/fast_send'
|
2
2
|
require 'logger'
|
3
|
+
require 'tempfile'
|
4
|
+
|
3
5
|
describe 'FastSend when used with a mock Socket' do
|
4
6
|
let(:logger) {
|
5
7
|
Logger.new(nil).tap{|l| l.level = Logger::DEBUG }
|
@@ -327,8 +329,8 @@ describe 'FastSend when used with a mock Socket' do
|
|
327
329
|
|
328
330
|
bytes_sent_cbs = callbacks[1..-3]
|
329
331
|
bytes_sent_cbs.each_with_index do | c, i |
|
330
|
-
expect(c[1]).to be_kind_of(
|
331
|
-
expect(c[2]).to be_kind_of(
|
332
|
+
expect(c[1]).to be_kind_of(Integer)
|
333
|
+
expect(c[2]).to be_kind_of(Integer)
|
332
334
|
end
|
333
335
|
end
|
334
336
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fast_send
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julik Tarkhanov
|
@@ -24,20 +24,6 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: jeweler
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '2'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '2'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: rspec
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -88,6 +74,7 @@ extra_rdoc_files:
|
|
88
74
|
- LICENSE.txt
|
89
75
|
- README.md
|
90
76
|
files:
|
77
|
+
- ".gitignore"
|
91
78
|
- Gemfile
|
92
79
|
- LICENSE.txt
|
93
80
|
- README.md
|
@@ -97,6 +84,7 @@ files:
|
|
97
84
|
- lib/fast_send.rb
|
98
85
|
- lib/fast_send/null_logger.rb
|
99
86
|
- lib/fast_send/socket_handler.rb
|
87
|
+
- lib/fast_send/version.rb
|
100
88
|
- spec/fast_send_with_mocks_spec.rb
|
101
89
|
- spec/fast_send_with_puma_spec.rb
|
102
90
|
- spec/test_app.ru
|
@@ -120,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
108
|
version: '0'
|
121
109
|
requirements: []
|
122
110
|
rubyforge_project:
|
123
|
-
rubygems_version: 2.
|
111
|
+
rubygems_version: 2.6.11
|
124
112
|
signing_key:
|
125
113
|
specification_version: 4
|
126
114
|
summary: and do so bypassing the Ruby VM
|