sendfile 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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
+