sendfile 0.9.1

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/ChangeLog ADDED
@@ -0,0 +1,11 @@
1
+ * sendfile 0.9.1 - 2006.03.24 <toby@cbcg.net>
2
+
3
+ - Improved interface with non-blocking sockets
4
+ - Added unit tests for blocking sends
5
+ - Added unit tests for nonblocking sends
6
+ - Added README
7
+ - Packaged in a RubyGem
8
+
9
+ * sendfile 0.0.1 - 2005.11.16 <toby@cbcg.net>
10
+
11
+ - Initial release
data/FILES ADDED
@@ -0,0 +1,10 @@
1
+ FILES
2
+ README
3
+ LICENSE
4
+ ChangeLog
5
+ ext/extconf.rb
6
+ ext/sendfile.c
7
+ test/test_sendfile.rb
8
+ test/large.gz
9
+ test/small
10
+ sendfile.gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+
2
+ Copyright (c) 2005 Tobias DiPasquale
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a
5
+ copy of this software and associated documentation files (the "Software"),
6
+ to deal in the Software without restriction, including without limitation
7
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
+ and/or sell copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included
12
+ in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20
+ DEALINGS IN THE SOFTWARE.
21
+
data/README ADDED
@@ -0,0 +1,57 @@
1
+ = Ruby sendfile(2) Interface
2
+
3
+ This module allows Ruby programs to access their OS's native
4
+ sendfile(2) system call from any IO object. Your kernel must
5
+ export a recognized signature for the sendfile(2) system call
6
+ to use this module. Currently, that includes Linux, Solaris
7
+ and FreeBSD.
8
+
9
+ == Installation
10
+
11
+ Download and install the latest package from the rubyforge.org
12
+ RubyGems repository.
13
+
14
+ gem install sendfile --remote
15
+ gem check sendfile --test
16
+
17
+ If the tests all pass, you're ready to start using sendfile.
18
+
19
+ Or, if you don't have rubygems installed, you can install by
20
+ hand by downloading the tarball:
21
+
22
+ tar xzvf ruby-sendfile-<latest>.tar.gz
23
+ cd ruby-sendfile-<latest>/ext
24
+ ruby extconf.rb
25
+ make
26
+ sudo make install
27
+
28
+ You can then run the tests with:
29
+
30
+ ruby test/test_*.rb
31
+
32
+ == Usage
33
+
34
+ Here's a small example of a use of IO#sendfile.
35
+
36
+ require 'socket'
37
+ require 'rubygems'
38
+ require 'sendfile'
39
+
40
+ s = TCPSocket.new 'yourdomain.com', 5000
41
+ File.open 'somefile.txt' { |f| s.sendfile f }
42
+ s.close
43
+
44
+ See the test scripts for more examples on how to use this
45
+ module.
46
+
47
+ == Contact Information
48
+
49
+ This project's homepage is:
50
+
51
+ http://rubyforge.org/projects/ruby-sendfile
52
+
53
+ Thereupon are additional resources, such as news and forums
54
+ can be found for working out any issues you may have with this
55
+ module. In the last case, you can email questions or patches
56
+ to toby@cbcg.net.
57
+
data/ext/extconf.rb ADDED
@@ -0,0 +1,53 @@
1
+ #
2
+ # Copyright (c) 2005 Tobias DiPasquale
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a
5
+ # copy of this software and associated documentation files (the "Software"),
6
+ # to deal in the Software without restriction, including without limitation
7
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
+ # and/or sell copies of the Software, and to permit persons to whom the
9
+ # Software is furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included
12
+ # in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20
+ # DEALINGS IN THE SOFTWARE.
21
+ #
22
+
23
+ #
24
+ # $Id: extconf.rb,v 1.1 2006/03/25 01:42:02 codeslinger Exp $
25
+ #
26
+ require 'mkmf'
27
+
28
+ $config_h = ""
29
+ case RUBY_PLATFORM
30
+ when /solaris/
31
+ have_header( "sys/sendfile.h")
32
+ have_library( "sendfile", "sendfile")
33
+ $config_h << "#define RUBY_PLATFORM_SOLARIS"
34
+ when /linux/
35
+ have_header( "sys/sendfile.h")
36
+ have_library( "c", "sendfile")
37
+ $config_h << "#define RUBY_PLATFORM_LINUX"
38
+ when /freebsd/
39
+ have_library( "c", "sendfile")
40
+ $config_h << "#define RUBY_PLATFORM_FREEBSD"
41
+ end
42
+
43
+ File.open( "config.h", "w") do |f|
44
+ f.print <<EOF
45
+ #ifndef CONFIG_H
46
+ #define CONFIG_H
47
+ #{$config_h}
48
+ #endif /* CONFIG_H */
49
+ EOF
50
+ end
51
+
52
+ create_makefile( "sendfile")
53
+
data/ext/sendfile.c ADDED
@@ -0,0 +1,161 @@
1
+ /*
2
+ * ------------------------------------------------------------------------
3
+ *
4
+ * Copyright (c) 2005 Tobias DiPasquale
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a
7
+ * copy of this software and associated documentation files (the "Software"),
8
+ * to deal in the Software without restriction, including without limitation
9
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
+ * and/or sell copies of the Software, and to permit persons to whom the
11
+ * Software is furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included
14
+ * in all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
+ * DEALINGS IN THE SOFTWARE.
23
+ *
24
+ * ------------------------------------------------------------------------
25
+ *
26
+ * Ruby binding for the UNIX sendfile(2) facility. Should work on FreeBSD,
27
+ * Linux and Solaris systems that support sendfile(2).
28
+ *
29
+ * Original Author: Toby DiPasquale <toby@cbcg.net>
30
+ * Current Maintainer: Toby DiPasquale <toby@cbcg.net>
31
+ *
32
+ * $Id: sendfile.c,v 1.3 2006/03/26 16:03:59 codeslinger Exp $
33
+ */
34
+ #include "ruby.h"
35
+ #include "rubyio.h"
36
+ #include "rubysig.h"
37
+ #include "config.h"
38
+
39
+ #if defined(RUBY_PLATFORM_FREEBSD)
40
+ # include <sys/socket.h>
41
+ # include <sys/stat.h>
42
+ # include <sys/types.h>
43
+ # include <sys/uio.h>
44
+ #elif defined(RUBY_PLATFORM_LINUX)
45
+ # include <sys/sendfile.h>
46
+ # include <sys/stat.h>
47
+ # include <sys/types.h>
48
+ # include <unistd.h>
49
+ #elif defined(RUBY_PLATFORM_SOLARIS)
50
+ # include <sys/sendfile.h>
51
+ # include <sys/stat.h>
52
+ # include <sys/types.h>
53
+ #endif
54
+
55
+ #define SENDFILE_PAUSE_SEC 0
56
+ #define SENDFILE_PAUSE_USEC 500
57
+
58
+ #if defined(RUBY_PLATFORM_FREEBSD)
59
+ static off_t __sendfile( int out, int in, off_t off, size_t count, struct timeval *tv)
60
+ {
61
+ int rv;
62
+ off_t written, initial = off;
63
+
64
+ while (1) {
65
+ TRAP_BEG;
66
+ rv = sendfile( in, out, off, count, NULL, &written, 0);
67
+ TRAP_END;
68
+ off += written;
69
+ count -= written;
70
+ if (rv < 0 && errno != EAGAIN)
71
+ rv_sys_fail( "sendfile");
72
+ if (!rv)
73
+ break;
74
+ rb_thread_select( 0, NULL, NULL, NULL, tv);
75
+ }
76
+ return off - initial;
77
+ }
78
+ #else
79
+ static size_t __sendfile( int out, int in, off_t off, size_t count, struct timeval *tv)
80
+ {
81
+ ssize_t rv, remaining = count;
82
+
83
+ while (1) {
84
+ TRAP_BEG;
85
+ rv = sendfile( out, in, &off, remaining);
86
+ TRAP_END;
87
+ if (rv < 0 && errno != EAGAIN)
88
+ rb_sys_fail( "sendfile");
89
+ if (rv > 0)
90
+ remaining -= rv;
91
+ if (!remaining)
92
+ break;
93
+ rb_thread_select( 0, NULL, NULL, NULL, tv);
94
+ }
95
+ return count;
96
+ }
97
+ #endif
98
+
99
+ /* call-seq:
100
+ * writeIO.sendfile( readIO, offset=0, count=nil) => integer
101
+ *
102
+ * Transfers count bytes starting at offset from readIO directly to writeIO
103
+ * without copying (i.e. invoking the kernel to do it for you).
104
+ *
105
+ * If offset is omitted, transfer starts at the beginning of the file.
106
+ *
107
+ * If count is omitted, the full length of the file will be sent.
108
+ *
109
+ * Returns the number of bytes sent on success. Will throw system error
110
+ * exception on error. (check man sendfile(2) on your platform for
111
+ * information on what errors could result and how to handle them)
112
+ */
113
+ static VALUE rb_io_sendfile( int argc, VALUE *argv, VALUE self)
114
+ {
115
+ int i, o;
116
+ size_t c;
117
+ off_t off;
118
+ OpenFile *iptr, *optr;
119
+ VALUE in, offset, count;
120
+ struct timeval _sendfile_sleep;
121
+
122
+ /* get fds for files involved to pass to sendfile(2) */
123
+ rb_scan_args( argc, argv, "12", &in, &offset, &count);
124
+ if (TYPE( in) != T_FILE)
125
+ rb_raise( rb_eArgError, "invalid first argument\n");
126
+ GetOpenFile( self, optr);
127
+ GetOpenFile( in, iptr);
128
+ o = fileno( optr->f);
129
+ i = fileno( iptr->f);
130
+
131
+ _sendfile_sleep.tv_sec = SENDFILE_PAUSE_SEC;
132
+ _sendfile_sleep.tv_usec = SENDFILE_PAUSE_USEC;
133
+
134
+ /* determine offset and count parameters */
135
+ off = (NIL_P( offset)) ? 0 : NUM2ULONG( offset);
136
+ if (NIL_P( count)) {
137
+ /* FreeBSD's sendfile() can take 0 as an indication to send
138
+ * until end of file, but Linux and Solaris can't, and anyway
139
+ * we need the file size to ensure we send it all in the case
140
+ * of a non-blocking fd */
141
+ struct stat s;
142
+ if (fstat( i, &s) == -1)
143
+ rb_sys_fail( "sendfile");
144
+ c = s.st_size;
145
+ c -= off;
146
+ } else {
147
+ c = NUM2ULONG( count);
148
+ }
149
+
150
+ /* now send the file */
151
+ return INT2FIX( __sendfile( o, i, off, c, &_sendfile_sleep));
152
+ }
153
+
154
+ /* Interface to the UNIX sendfile(2) system call. Should work on FreeBSD,
155
+ * Linux and Solaris systems that support the sendfile(2) system call.
156
+ */
157
+ void Init_sendfile( void)
158
+ {
159
+ rb_define_method( rb_cIO, "sendfile", rb_io_sendfile, -1);
160
+ }
161
+
data/sendfile.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # vim:set ts=4 sw=4 ai syntax=ruby:
2
+
3
+ spec = Gem::Specification.new do |gs|
4
+ gs.name = 'sendfile'
5
+ gs.version = '0.9.1'
6
+ gs.summary = 'Ruby interface to sendfile(2) system call'
7
+ gs.description = <<-EOF
8
+ Allows Ruby programs to access sendfile(2) functionality on
9
+ any IO object. Works on Linux, Solaris and FreeBSD with
10
+ blocking and non-blocking sockets.
11
+ EOF
12
+ gs.author = 'Toby DiPasquale'
13
+ gs.email = 'toby@cbcg.net'
14
+ gs.rubyforge_project = 'ruby-sendfile'
15
+
16
+ gs.autorequire = 'sendfile'
17
+ gs.files = File.read( 'FILES').split( $/)
18
+ gs.test_files = Dir.glob 'test/test_*.rb'
19
+ gs.extensions << 'ext/extconf.rb'
20
+
21
+ gs.has_rdoc = true
22
+ gs.extra_rdoc_files = %w( README )
23
+ gs.required_ruby_version = '>= 1.8.0'
24
+ end
25
+
data/test/large.gz ADDED
Binary file
data/test/small ADDED
@@ -0,0 +1 @@
1
+ I love Pocky!
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env ruby
2
+ # vim:set ts=4 sw=4 ai:
3
+ require 'io/nonblock'
4
+ begin
5
+ require 'rubygems'
6
+ rescue
7
+ nil
8
+ end
9
+ require 'sendfile'
10
+ require 'socket'
11
+ require 'tempfile'
12
+ require 'test/unit'
13
+ require 'zlib'
14
+
15
+ class TestSendfile < Test::Unit::TestCase
16
+ def __fork_server
17
+ # open pipe for backchannel
18
+ @rd, @wr = IO.pipe
19
+ # fork server child
20
+ @pid = fork do
21
+ # close read end in child
22
+ @rd.close
23
+
24
+ # start listening and send port back to parent
25
+ ss = TCPServer.new @host, 0
26
+ @wr.write( [ ss.addr[1] ].pack( "S"))
27
+ @wr.flush
28
+
29
+ # read what they send and push it back up the pipe
30
+ while s = ss.accept
31
+ data = s.read
32
+ s.close
33
+ @wr.write( [data.length].pack( "N"))
34
+ @wr.write data
35
+ @wr.flush
36
+ end
37
+ end
38
+
39
+ # close write end in parent and get server port
40
+ @wr.close
41
+ @port = @rd.read( 2).unpack( "S")[0]
42
+ end
43
+
44
+ def setup
45
+ @dir = File.dirname __FILE__
46
+ @host = '127.0.0.1'
47
+ __fork_server
48
+
49
+ @smallfile = "#{@dir}/small"
50
+ @small = File.open @smallfile
51
+ @small_data = File.read @smallfile
52
+
53
+ File.open( "#{@dir}/large.gz") do |f|
54
+ gzipped = Zlib::GzipReader.new f
55
+ @large_data = gzipped.read
56
+ end
57
+ @largefile = "/tmp/sendfiletest"
58
+ @large = File.open @largefile, 'w+'
59
+ @large.write @large_data
60
+ @large.flush
61
+ end
62
+
63
+ def teardown
64
+ @small.close
65
+ @large.close
66
+ File.unlink @largefile
67
+
68
+ Process.kill 'KILL', @pid
69
+ Process.wait
70
+ end
71
+
72
+ def __do_sendfile file, off=nil, count=nil
73
+ s = TCPSocket.new @host, @port
74
+ yield s if block_given?
75
+ sent = s.sendfile file, off, count
76
+ s.close
77
+ len = @rd.read( 4).unpack( "N")[0]
78
+ read = @rd.read len
79
+ [ sent, read ]
80
+ end
81
+
82
+ def test_blocking_full_small
83
+ sent, read = __do_sendfile( @small)
84
+ assert_equal @small_data.size, sent
85
+ assert_equal @small_data.size, read.size
86
+ assert_equal @small_data, read
87
+ end
88
+
89
+ def test_nonblocking_full_small
90
+ sent, read = __do_sendfile( @small) { |s| s.nonblock = true }
91
+ assert_equal @small_data.size, sent
92
+ assert_equal @small_data.size, read.size
93
+ assert_equal @small_data, read
94
+ end
95
+
96
+ def test_blocking_full_large
97
+ sent, read = __do_sendfile( @large)
98
+ assert_equal @large_data.size, sent
99
+ assert_equal @large_data.size, read.size
100
+ assert_equal @large_data, read
101
+ end
102
+
103
+ def test_nonblocking_full_large
104
+ sent, read = __do_sendfile( @large) { |s| s.nonblock = true }
105
+ assert_equal @large_data.size, sent
106
+ assert_equal @large_data.size, read.size
107
+ assert_equal @large_data, read
108
+ end
109
+
110
+ def test_blocking_from_offset
111
+ data = @large_data[4096..-1]
112
+ sent, read = __do_sendfile( @large, 4096)
113
+ assert_equal data.size, sent
114
+ assert_equal data.size, read.size
115
+ assert_equal data, read
116
+ end
117
+
118
+ def test_blocking_partial_from_beginning
119
+ data = @large_data[0, 1048576]
120
+ sent, read = __do_sendfile( @large, 0, 1048576)
121
+ assert_equal data.size, sent
122
+ assert_equal data.size, read.size
123
+ assert_equal data, read
124
+ end
125
+
126
+ def test_blocking_partial_from_middle
127
+ data = @large_data[2048, 1048576]
128
+ sent, read = __do_sendfile( @large, 2048, 1048576)
129
+ assert_equal data.size, sent
130
+ assert_equal data.size, read.size
131
+ assert_equal data, read
132
+ end
133
+
134
+ def test_blocking_partial_to_end
135
+ data = @large_data[-1048576, 1048576]
136
+ sent, read = __do_sendfile( @large, @large_data.size - 1048576, 1048576)
137
+ assert_equal data.size, sent
138
+ assert_equal data.size, read.size
139
+ assert_equal data, read
140
+ end
141
+
142
+ def test_nonblocking_from_offset
143
+ data = @large_data[4096..-1]
144
+ sent, read = __do_sendfile( @large, 4096) { |s| s.nonblock = true }
145
+ assert_equal data.size, sent
146
+ assert_equal data.size, read.size
147
+ assert_equal data, read
148
+ end
149
+
150
+ def test_nonblocking_partial_from_beginning
151
+ data = @large_data[0, 1048576]
152
+ sent, read = __do_sendfile( @large, 0, 1048576) { |s| s.nonblock = true }
153
+ assert_equal data.size, sent
154
+ assert_equal data.size, read.size
155
+ assert_equal data, read
156
+ end
157
+
158
+ def test_nonblocking_partial_from_middle
159
+ data = @large_data[2048, 1048576]
160
+ sent, read = __do_sendfile( @large, 2048, 1048576) { |s| s.nonblock = true }
161
+ assert_equal data.size, sent
162
+ assert_equal data.size, read.size
163
+ assert_equal data, read
164
+ end
165
+
166
+ def test_nonblocking_partial_to_end
167
+ data = @large_data[-1048576, 1048576]
168
+ sent, read = __do_sendfile( @large, @large_data.size - 1048576, 1048576) { |s| s.nonblock = true }
169
+ assert_equal data.size, sent
170
+ assert_equal data.size, read.size
171
+ assert_equal data, read
172
+ end
173
+ end # class TestSendfile
174
+
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.11
3
+ specification_version: 1
4
+ name: sendfile
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.9.1
7
+ date: 2006-03-26 00:00:00 -05:00
8
+ summary: Ruby interface to sendfile(2) system call
9
+ require_paths:
10
+ - lib
11
+ email: toby@cbcg.net
12
+ homepage:
13
+ rubyforge_project: ruby-sendfile
14
+ description: Allows Ruby programs to access sendfile(2) functionality on any IO object. Works on Linux, Solaris and FreeBSD with blocking and non-blocking sockets.
15
+ autorequire: sendfile
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.8.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ authors:
29
+ - Toby DiPasquale
30
+ files:
31
+ - FILES
32
+ - README
33
+ - LICENSE
34
+ - ChangeLog
35
+ - ext/extconf.rb
36
+ - ext/sendfile.c
37
+ - test/test_sendfile.rb
38
+ - test/large.gz
39
+ - test/small
40
+ - sendfile.gemspec
41
+ test_files:
42
+ - test/test_sendfile.rb
43
+ rdoc_options: []
44
+
45
+ extra_rdoc_files:
46
+ - README
47
+ executables: []
48
+
49
+ extensions:
50
+ - ext/extconf.rb
51
+ requirements: []
52
+
53
+ dependencies: []
54
+