kgio-sendfile 1.2.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 72978ae5f2835c3a37bd201aa17c4d18259fb9a7
4
+ data.tar.gz: 756d41104b237627b23b90e5f598096f2a036615
5
+ SHA512:
6
+ metadata.gz: bf9c72e3a21c2e0bdd542ff3780f7d5a2c8f2e5721ddd1e1870db3e5e8744d6b7f0ca0b77f07554271066bdcf472b9f2a3ab0e954d21f73b01992305c35d70c3
7
+ data.tar.gz: 6a682a3e4719b10421fadb891d552186a608791461840a68cd9e29170ad7c0eb0199096e2538bed75b9f520964ee5854c75f27be6db841fe2ab8ef17e8a2d665
data/ChangeLog ADDED
@@ -0,0 +1,21 @@
1
+ * sendfile 0.9.3 - 2008.11.30 <toby@cbcg.net>
2
+
3
+ - Included sys/stat.h regardless of platform to fix compilation on
4
+ OS X 10.5 (credit: Damon Morda <dmorda@andrew.cmu.edu>)
5
+ - Source formatting clean-up
6
+
7
+ * sendfile 0.9.2 - 2006.03.27 <toby@cbcg.net>
8
+
9
+ - Fixed typo: s/rv_sys_fail/rb_sys_fail
10
+
11
+ * sendfile 0.9.1 - 2006.03.24 <toby@cbcg.net>
12
+
13
+ - Improved interface with non-blocking sockets
14
+ - Added unit tests for blocking sends
15
+ - Added unit tests for nonblocking sends
16
+ - Added README
17
+ - Packaged in a RubyGem
18
+
19
+ * sendfile 0.0.1 - 2005.11.16 <toby@cbcg.net>
20
+
21
+ - Initial release
data/FILES ADDED
@@ -0,0 +1,10 @@
1
+ FILES
2
+ README.textile
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,2008 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.textile ADDED
@@ -0,0 +1,35 @@
1
+ kgio-sendfile is a (hopefully temporary) fork of the original "sendfile"
2
+ gem for Ruby 2.2.0dev compatibility. Only Linux (and maybe FreeBSD)
3
+ are supported in this version.
4
+
5
+ Send plain-text email to our mailing list at kgio@librelist.org for
6
+ anything related to this.
7
+
8
+
9
+ h1. Ruby sendfile(2) Interface
10
+
11
+ This module allows Ruby programs to access their OS's native <code>sendfile(2)</code> system call from any IO object. Your kernel must export a recognized signature for the <code>sendfile(2)</code> system call to use this module. Currently, that includes Linux, Solaris and FreeBSD.
12
+
13
+ h2. Installation
14
+
15
+ Download and install the latest package from the rubyforge.org RubyGems repository.
16
+
17
+ <code>
18
+ $ gem install sendfile
19
+ </code>
20
+
21
+ If the tests all pass, you're ready to start using sendfile!
22
+
23
+ h2. Usage
24
+
25
+ Here's a small example of a use of <code>IO#sendfile</code>.
26
+
27
+ bc.. require 'socket'
28
+ require 'rubygems'
29
+ require 'sendfile'
30
+ s = TCPSocket.new 'yourdomain.com', 5000
31
+ File.open 'somefile.txt' { |f| s.sendfile f }
32
+ s.close
33
+
34
+ p. See the test scripts for more examples on how to use this module.
35
+
data/ext/extconf.rb ADDED
@@ -0,0 +1,58 @@
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
+ when /darwin/
42
+ have_library( "c", "sendfile")
43
+ $config_h << "#define RUBY_PLATFORM_DARWIN"
44
+ end
45
+
46
+ File.open( "config.h", "w") do |f|
47
+ f.print <<EOF
48
+ #ifndef CONFIG_H
49
+ #define CONFIG_H
50
+ #{$config_h}
51
+ #endif /* CONFIG_H */
52
+ EOF
53
+ end
54
+
55
+ unless have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
56
+ have_func('rb_thread_blocking_region')
57
+ end
58
+ create_makefile( "sendfile")
data/ext/sendfile.c ADDED
@@ -0,0 +1,371 @@
1
+ /*
2
+ * ------------------------------------------------------------------------
3
+ *
4
+ * Copyright (c) 2005,2008 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.4 2006/03/27 19:14:53 codeslinger Exp $
33
+ */
34
+ #include <sys/stat.h>
35
+ #include <sys/types.h>
36
+ #include <limits.h>
37
+ #include "ruby.h"
38
+ #ifdef HAVE_RUBY_IO_H
39
+ # include "ruby/io.h"
40
+ #else
41
+ # include "rubyio.h"
42
+ #endif
43
+ #include <unistd.h>
44
+ #include <fcntl.h>
45
+ #include "config.h"
46
+ static VALUE sym_wait_writable;
47
+
48
+ #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) && defined(HAVE_RUBY_THREAD_H)
49
+ /* Ruby 2.0+ */
50
+ # include <ruby/thread.h>
51
+ typedef void * (*my_blocking_fn_t)(void*);
52
+ # define WITHOUT_GVL(fn,a) \
53
+ rb_thread_call_without_gvl((my_blocking_fn_t)(fn),(a),RUBY_UBF_IO,0)
54
+ #elif defined(HAVE_RB_THREAD_BLOCKING_REGION) /* Ruby 1.9 */
55
+ typedef VALUE (*my_blocking_fn_t)(void*);
56
+ # define WITHOUT_GVL(fn,a) \
57
+ rb_thread_blocking_region((my_blocking_fn_t)(fn),(a),RUBY_UBF_IO,0)
58
+ #else /* MRI 1.8 threads */
59
+ /*
60
+ * For non-natively threaded interpreters, do not monopolize the
61
+ * process and send in smaller chunks. 64K was chosen as it is
62
+ * half the typical max readahead size in Linux 2.6, giving the
63
+ * kernel some time to populate the page cache in between
64
+ * subsequent sendfile() calls.
65
+ */
66
+ # define MAX_SEND_SIZE ((off_t)(0x10000))
67
+
68
+ /* (very) partial emulation of the 1.9 rb_thread_blocking_region under 1.8 */
69
+ # include <rubysig.h>
70
+ typedef VALUE rb_blocking_function_t(void *);
71
+ static VALUE
72
+ WITHOUT_GVL(rb_blocking_function_t *fn, void *data1)
73
+ {
74
+ VALUE rv;
75
+
76
+ TRAP_BEG;
77
+ rv = fn(data1);
78
+ TRAP_END;
79
+
80
+ return rv;
81
+ }
82
+ #endif /* WITHOUT_GVL definitions */
83
+
84
+ #ifndef MAX_SEND_SIZE
85
+ /*
86
+ * We can release the GVL and block as long as we need to.
87
+ * Limit this to the maximum ssize_t anyways, since 32-bit machines with
88
+ * Large File Support can't send more than this number of bytes
89
+ * in one shot.
90
+ */
91
+ # define MAX_SEND_SIZE ((off_t)LONG_MAX)
92
+ #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
93
+
94
+ #if defined(RUBY_PLATFORM_FREEBSD)
95
+ # include <sys/socket.h>
96
+ # include <sys/uio.h>
97
+ #elif defined(RUBY_PLATFORM_LINUX)
98
+ # include <sys/sendfile.h>
99
+ # include <unistd.h>
100
+ #elif defined(RUBY_PLATFORM_SOLARIS)
101
+ # include <sys/sendfile.h>
102
+ #elif defined(RUBY_PLATFORM_DARWIN)
103
+ # include <sys/types.h>
104
+ # include <sys/socket.h>
105
+ # include <sys/uio.h>
106
+ #endif
107
+
108
+ static size_t count_max(off_t count)
109
+ {
110
+ return (size_t)(count > MAX_SEND_SIZE ? MAX_SEND_SIZE : count);
111
+ }
112
+
113
+ struct sendfile_args {
114
+ int out;
115
+ int in;
116
+ off_t off;
117
+ off_t count;
118
+ int eof;
119
+ };
120
+
121
+ #if ! HAVE_RB_IO_T
122
+ # define rb_io_t OpenFile
123
+ #endif
124
+
125
+ #ifdef GetReadFile
126
+ # define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
127
+ #else
128
+ # if !HAVE_RB_IO_T || (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
129
+ # define FPTR_TO_FD(fptr) fileno(fptr->f)
130
+ # else
131
+ # define FPTR_TO_FD(fptr) fptr->fd
132
+ # endif
133
+ #endif
134
+
135
+ static int my_rb_fileno(VALUE io)
136
+ {
137
+ rb_io_t *fptr;
138
+
139
+ GetOpenFile(io, fptr);
140
+
141
+ return FPTR_TO_FD(fptr);
142
+ }
143
+
144
+ #if defined(RUBY_PLATFORM_FREEBSD)
145
+ static VALUE nogvl_sendfile(void *data)
146
+ {
147
+ struct sendfile_args *args = data;
148
+ int rv;
149
+ off_t written = 0;
150
+ size_t w = count_max(args->count);
151
+
152
+ rv = sendfile(args->in, args->out, args->off, w, NULL, &written, 0);
153
+ if (written == 0 && rv == 0) {
154
+ args->eof = 1;
155
+ } else if (written > 0) {
156
+ args->off += written;
157
+ args->count -= written;
158
+ rv = 0; /* anything written is a success */
159
+ }
160
+
161
+ return (VALUE)rv;
162
+ }
163
+ #elif defined(RUBY_PLATFORM_DARWIN)
164
+ static VALUE nogvl_sendfile(void *data)
165
+ {
166
+ struct sendfile_args *args = data;
167
+ int rv;
168
+ off_t written = args->count;
169
+
170
+ rv = sendfile(args->in, args->out, args->off, &written,
171
+ NULL, 0);
172
+ if (written == 0 && rv == 0) {
173
+ args->eof = 1;
174
+ } else {
175
+ args->off += written;
176
+ args->count -= written;
177
+ }
178
+
179
+ return (VALUE)rv;
180
+ }
181
+ #else
182
+ static VALUE nogvl_sendfile(void *data)
183
+ {
184
+ ssize_t rv;
185
+ struct sendfile_args *args = data;
186
+ size_t w = count_max(args->count);
187
+
188
+ rv = sendfile(args->out, args->in, &args->off, w);
189
+ if (rv == 0)
190
+ args->eof = 1;
191
+ if (rv > 0)
192
+ args->count -= rv;
193
+
194
+ return (VALUE)rv;
195
+ }
196
+ #endif
197
+
198
+ static off_t sendfile_full(struct sendfile_args *args)
199
+ {
200
+ ssize_t rv;
201
+ off_t all = args->count;
202
+
203
+ while (1) {
204
+ rv = (ssize_t)WITHOUT_GVL(nogvl_sendfile, args);
205
+ if (!args->count)
206
+ break;
207
+ if (args->eof) {
208
+ if (all != args->count)
209
+ break;
210
+ rb_eof_error();
211
+ }
212
+ if (rv < 0 && ! rb_io_wait_writable(args->out))
213
+ rb_sys_fail("sendfile");
214
+ }
215
+ return all - args->count;
216
+ }
217
+
218
+ static VALUE sendfile_nonblock(struct sendfile_args *args, int try)
219
+ {
220
+ ssize_t rv;
221
+ off_t before = args->count;
222
+ int flags;
223
+
224
+ flags = fcntl(args->out, F_GETFL);
225
+ if (flags == -1)
226
+ rb_sys_fail("fcntl");
227
+ if ((flags & O_NONBLOCK) == 0) {
228
+ if (fcntl(args->out, F_SETFL, flags | O_NONBLOCK) == -1)
229
+ rb_sys_fail("fcntl");
230
+ }
231
+
232
+ rv = (ssize_t)WITHOUT_GVL(nogvl_sendfile, args);
233
+ if (rv < 0) {
234
+ if (try && errno == EAGAIN)
235
+ return sym_wait_writable;
236
+ rb_sys_fail("sendfile");
237
+ }
238
+ if (args->eof) {
239
+ if (try)
240
+ return Qnil;
241
+ rb_eof_error();
242
+ }
243
+
244
+ return OFFT2NUM(before - args->count);
245
+ }
246
+
247
+ static void convert_args(int argc, VALUE *argv, VALUE self,
248
+ struct sendfile_args *args)
249
+ {
250
+ VALUE in, offset, count;
251
+
252
+ /* get fds for files involved to pass to sendfile(2) */
253
+ rb_scan_args(argc, argv, "12", &in, &offset, &count);
254
+ in = rb_convert_type(in, T_FILE, "IO", "to_io");
255
+ args->out = my_rb_fileno(self);
256
+ args->in = my_rb_fileno(in);
257
+ args->eof = 0;
258
+
259
+ /* determine offset and count parameters */
260
+ args->off = (NIL_P(offset)) ? 0 : NUM2OFFT(offset);
261
+ if (NIL_P(count)) {
262
+ /* FreeBSD's sendfile() can take 0 as an indication to send
263
+ * until end of file, but Linux and Solaris can't, and anyway
264
+ * we need the file size to ensure we send it all in the case
265
+ * of a non-blocking fd */
266
+ struct stat s;
267
+ if (fstat(args->in, &s) == -1)
268
+ rb_sys_fail("sendfile");
269
+ args->count = s.st_size;
270
+ args->count -= args->off;
271
+ } else {
272
+ args->count = NUM2OFFT(count);
273
+ }
274
+ }
275
+
276
+ /* call-seq:
277
+ * writeIO.sendfile( readIO, offset=0, count=nil) => integer
278
+ *
279
+ * Transfers count bytes starting at offset from readIO directly to writeIO
280
+ * without copying (i.e. invoking the kernel to do it for you).
281
+ *
282
+ * If offset is omitted, transfer starts at the beginning of the file.
283
+ *
284
+ * If count is omitted, the full length of the file will be sent.
285
+ *
286
+ * Returns the number of bytes sent on success. Will throw system error
287
+ * exception on error. (check man sendfile(2) on your platform for
288
+ * information on what errors could result and how to handle them)
289
+ */
290
+ static VALUE rb_io_sendfile(int argc, VALUE *argv, VALUE self)
291
+ {
292
+ struct sendfile_args args;
293
+
294
+ convert_args(argc, argv, self, &args);
295
+
296
+ /* now send the file */
297
+ return OFFT2NUM(sendfile_full(&args));
298
+ }
299
+
300
+ /* call-seq:
301
+ * writeIO.sendfile_nonblock(readIO, offset=0, count=nil) => integer
302
+ *
303
+ * Transfers count bytes starting at offset from readIO directly to writeIO
304
+ * without copying (i.e. invoking the kernel to do it for you).
305
+ *
306
+ * Unlike IO#sendfile, this will set the O_NONBLOCK flag on writeIO
307
+ * before calling sendfile(2) and will raise Errno::EAGAIN instead
308
+ * of blocking. This method is intended for use with non-blocking
309
+ * event frameworks, including those that rely on Fibers.
310
+ *
311
+ * If offset is omitted, transfer starts at the beginning of the file.
312
+ *
313
+ * If count is omitted, the full length of the file will be sent.
314
+ *
315
+ * Returns the number of bytes sent on success. Will throw system error
316
+ * exception on error. (check man sendfile(2) on your platform for
317
+ * information on what errors could result and how to handle them)
318
+ */
319
+ static VALUE rb_io_sendfile_nonblock(int argc, VALUE *argv, VALUE self)
320
+ {
321
+ struct sendfile_args args;
322
+
323
+ convert_args(argc, argv, self, &args);
324
+
325
+ return sendfile_nonblock(&args, 0);
326
+ }
327
+
328
+ /* call-seq:
329
+ * writeIO.trysendfile(readIO, offset=0, count=nil) => integer, nil, or
330
+ * :wait_writable
331
+ *
332
+ * Transfers count bytes starting at offset from readIO directly to writeIO
333
+ * without copying (i.e. invoking the kernel to do it for you).
334
+ *
335
+ * Unlike IO#sendfile, this will set the O_NONBLOCK flag on writeIO
336
+ * before calling sendfile(2) and will return :wait_writable instead
337
+ * of blocking. This method is intended for use with non-blocking
338
+ * event frameworks, including those that rely on Fibers.
339
+ *
340
+ * If offset is omitted, transfer starts at the beginning of the file.
341
+ *
342
+ * If count is omitted, the full length of the file will be sent.
343
+ *
344
+ * Returns the number of bytes sent on success, nil on EOF, and
345
+ * :wait_writable on EAGAIN. Will throw system error exception on error.
346
+ * (check man sendfile(2) on your platform for
347
+ * information on what errors could result and how to handle them)
348
+ *
349
+ * This method is a faster alternative to sendfile_nonblock as it does
350
+ * not raise exceptions on common EAGAIN errors.
351
+ */
352
+ static VALUE rb_io_trysendfile(int argc, VALUE *argv, VALUE self)
353
+ {
354
+ struct sendfile_args args;
355
+
356
+ convert_args(argc, argv, self, &args);
357
+
358
+ return sendfile_nonblock(&args, 1);
359
+ }
360
+
361
+ /* Interface to the UNIX sendfile(2) system call. Should work on FreeBSD,
362
+ * Linux and Solaris systems that support the sendfile(2) system call.
363
+ */
364
+ void Init_sendfile(void)
365
+ {
366
+ sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
367
+ rb_define_method(rb_cIO, "sendfile", rb_io_sendfile, -1);
368
+ rb_define_method(rb_cIO, "sendfile_nonblock",
369
+ rb_io_sendfile_nonblock, -1);
370
+ rb_define_method(rb_cIO, "trysendfile", rb_io_trysendfile, -1);
371
+ }
data/sendfile.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # vim:set ts=4 sw=4 ai syntax=ruby:
2
+
3
+ spec = Gem::Specification.new do |gs|
4
+ gs.name = 'kgio-sendfile'
5
+ gs.version = '1.2.1.1'
6
+ gs.summary = 'Ruby interface to sendfile(2) system call'
7
+ gs.description = <<-EOF
8
+ This is a (hopefully temporary) fork of the original "sendfile" gem for
9
+ Ruby 2.2.0dev compatibility. Only Linux (and maybe FreeBSD) are
10
+ supported in this version.
11
+
12
+ Allows Ruby programs to access sendfile(2) functionality on
13
+ any IO object. Works on Linux, Solaris, FreeBSD and Darwin with
14
+ blocking and non-blocking sockets.
15
+ EOF
16
+ gs.authors = [ 'Eric Wong', 'Toby DiPasquale' ]
17
+ gs.email = 'kgio@librelist.org'
18
+ gs.homepage = 'http://bogomips.org/ruby-sendfile.git'
19
+ gs.files = File.read('FILES').split($/)
20
+ gs.test_files = Dir.glob 'test/test_*.rb'
21
+ gs.extensions << 'ext/extconf.rb'
22
+ gs.has_rdoc = true
23
+ gs.extra_rdoc_files = %w(README.textile)
24
+ gs.required_ruby_version = '>= 1.8.0'
25
+ end
26
+
data/test/large.gz ADDED
Binary file
data/test/small ADDED
@@ -0,0 +1 @@
1
+ I love Pocky!
@@ -0,0 +1,252 @@
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 "minitest/autorun"
13
+ require 'minitest'
14
+ require 'zlib'
15
+
16
+ class TestSendfile < MiniTest::Test
17
+ def __fork_server
18
+ # open pipe for backchannel
19
+ @rd, @wr = IO.pipe
20
+ # fork server child
21
+ @pid = fork do
22
+ # close read end in child
23
+ @rd.close
24
+
25
+ # start listening and send port back to parent
26
+ ss = TCPServer.new @host, 0
27
+ @wr.write( [ ss.addr[1] ].pack( "S"))
28
+ @wr.flush
29
+
30
+ # read what they send and push it back up the pipe
31
+ while s = ss.accept
32
+ data = s.read
33
+ s.close
34
+ @wr.write( [data.length].pack( "N"))
35
+ @wr.write data
36
+ @wr.flush
37
+ end
38
+ end
39
+
40
+ # close write end in parent and get server port
41
+ @wr.close
42
+ @port = @rd.read( 2).unpack( "S")[0]
43
+ end
44
+
45
+ def setup
46
+ @dir = File.dirname __FILE__
47
+ @host = '127.0.0.1'
48
+ __fork_server
49
+
50
+ @smallfile = "#{@dir}/small"
51
+ @small = File.open @smallfile
52
+ @small_data = File.read @smallfile
53
+
54
+ File.open( "#{@dir}/large.gz") do |f|
55
+ gzipped = Zlib::GzipReader.new f
56
+ @large_data = gzipped.read
57
+ end
58
+ @largefile = "/tmp/sendfiletest"
59
+ @large = File.open @largefile, 'w+'
60
+ @large.write @large_data
61
+ @large.flush
62
+ end
63
+
64
+ def teardown
65
+ @small.close
66
+ @large.close
67
+ File.unlink @largefile
68
+
69
+ Process.kill 'KILL', @pid
70
+ Process.wait
71
+ end
72
+
73
+ def __do_sendfile file, off=nil, count=nil
74
+ s = TCPSocket.new @host, @port
75
+ yield s if block_given?
76
+ sent = s.sendfile file, off, count
77
+ s.close
78
+ len = @rd.read( 4).unpack( "N")[0]
79
+ read = @rd.read len
80
+ [ sent, read ]
81
+ end
82
+
83
+ def test_blocking_full_small
84
+ sent, read = __do_sendfile( @small)
85
+ assert_equal @small_data.size, sent
86
+ assert_equal @small_data.size, read.size
87
+ assert_equal @small_data, read
88
+ end
89
+
90
+ def test_nonblocking_full_small
91
+ sent, read = __do_sendfile( @small) { |s| s.nonblock = true }
92
+ assert_equal @small_data.size, sent
93
+ assert_equal @small_data.size, read.size
94
+ assert_equal @small_data, read
95
+ end
96
+
97
+ def test_blocking_full_large
98
+ sent, read = __do_sendfile( @large)
99
+ assert_equal @large_data.size, sent
100
+ assert_equal @large_data.size, read.size
101
+ assert_equal @large_data, read
102
+ end
103
+
104
+ def test_nonblocking_full_large
105
+ sent, read = __do_sendfile( @large) { |s| s.nonblock = true }
106
+ assert_equal @large_data.size, sent
107
+ assert_equal @large_data.size, read.size
108
+ assert_equal @large_data, read
109
+ end
110
+
111
+ def test_blocking_from_offset
112
+ data = @large_data[4096..-1]
113
+ sent, read = __do_sendfile( @large, 4096)
114
+ assert_equal data.size, sent
115
+ assert_equal data.size, read.size
116
+ assert_equal data, read
117
+ end
118
+
119
+ def test_blocking_partial_from_beginning
120
+ data = @large_data[0, 1048576]
121
+ sent, read = __do_sendfile( @large, 0, 1048576)
122
+ assert_equal data.size, sent
123
+ assert_equal data.size, read.size
124
+ assert_equal data, read
125
+ end
126
+
127
+ def test_blocking_partial_from_middle
128
+ data = @large_data[2048, 1048576]
129
+ sent, read = __do_sendfile( @large, 2048, 1048576)
130
+ assert_equal data.size, sent
131
+ assert_equal data.size, read.size
132
+ assert_equal data, read
133
+ end
134
+
135
+ def test_blocking_partial_to_end
136
+ data = @large_data[-1048576, 1048576]
137
+ sent, read = __do_sendfile( @large, @large_data.size - 1048576, 1048576)
138
+ assert_equal data.size, sent
139
+ assert_equal data.size, read.size
140
+ assert_equal data, read
141
+ end
142
+
143
+ def test_nonblocking_from_offset
144
+ data = @large_data[4096..-1]
145
+ sent, read = __do_sendfile( @large, 4096) { |s| s.nonblock = true }
146
+ assert_equal data.size, sent
147
+ assert_equal data.size, read.size
148
+ assert_equal data, read
149
+ end
150
+
151
+ def test_nonblocking_partial_from_beginning
152
+ data = @large_data[0, 1048576]
153
+ sent, read = __do_sendfile( @large, 0, 1048576) { |s| s.nonblock = true }
154
+ assert_equal data.size, sent
155
+ assert_equal data.size, read.size
156
+ assert_equal data, read
157
+ end
158
+
159
+ def test_nonblocking_partial_from_middle
160
+ data = @large_data[2048, 1048576]
161
+ sent, read = __do_sendfile( @large, 2048, 1048576) { |s| s.nonblock = true }
162
+ assert_equal data.size, sent
163
+ assert_equal data.size, read.size
164
+ assert_equal data, read
165
+ end
166
+
167
+ def test_nonblocking_partial_to_end
168
+ data = @large_data[-1048576, 1048576]
169
+ sent, read = __do_sendfile( @large, @large_data.size - 1048576, 1048576) { |s| s.nonblock = true }
170
+ assert_equal data.size, sent
171
+ assert_equal data.size, read.size
172
+ assert_equal data, read
173
+ end
174
+
175
+ def test_sendfile_nonblock
176
+ c, s = UNIXSocket.pair
177
+ nr_sent = 0
178
+ assert_raises(Errno::EAGAIN) do
179
+ loop do
180
+ nr_sent += c.sendfile_nonblock @small
181
+ end
182
+ end
183
+ c.close
184
+ nr_read = s.read.size
185
+ s.close
186
+ assert_equal nr_read, nr_sent
187
+ end
188
+
189
+ def test_trysendfile
190
+ c, s = UNIXSocket.pair
191
+ nr_sent = 0
192
+ case rv = c.trysendfile(@small)
193
+ when :wait_writable
194
+ break
195
+ when Integer
196
+ nr_sent += rv
197
+ else
198
+ raise "Unexpected return: #{rv.inspect}"
199
+ end while true
200
+ assert nr_sent > 0, "nr_sent: #{nr_sent} <= 0"
201
+ c.close
202
+ nr_read = s.read.size
203
+ s.close
204
+ assert_equal nr_read, nr_sent
205
+ end
206
+
207
+ def test_tempfile
208
+ tmp = Tempfile.new ''
209
+ tmp.write @small_data
210
+ tmp.rewind
211
+ sent, read = __do_sendfile(tmp)
212
+ assert_equal @small_data.size, sent
213
+ assert_equal @small_data.size, read.size
214
+ assert_equal @small_data, read
215
+ end
216
+
217
+ def test_invalid_file
218
+ assert_raises(TypeError) { __do_sendfile(:hello) }
219
+ end
220
+
221
+ def test_sendfile_too_big_eof
222
+ sent = read = nil
223
+ count = @small_data.size * 2
224
+ s = TCPSocket.new @host, @port
225
+ sent = s.sendfile @small, nil, count
226
+ assert_raises(EOFError) do
227
+ s.sendfile @small, sent, 1
228
+ end
229
+ s.close
230
+ len = @rd.read( 4).unpack( "N")[0]
231
+ read = @rd.read len
232
+ assert_equal @small_data.size, sent
233
+ assert_equal @small_data.size, read.size
234
+ assert_equal @small_data, read
235
+ end
236
+
237
+ def test_sendfile_nonblock_eof
238
+ s = TCPSocket.new @host, @port
239
+ off = @small_data.size
240
+ assert_raises(EOFError) do
241
+ s.sendfile_nonblock @small, off, 1
242
+ end
243
+ s.close
244
+ end
245
+
246
+ def test_trysendfile_eof
247
+ s = TCPSocket.new @host, @port
248
+ off = @small_data.size
249
+ assert_nil s.trysendfile(@small, off, 1)
250
+ s.close
251
+ end
252
+ end # class TestSendfile
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kgio-sendfile
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Eric Wong
8
+ - Toby DiPasquale
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-03-13 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: "This is a (hopefully temporary) fork of the original \"sendfile\" gem
15
+ for\nRuby 2.2.0dev compatibility. Only Linux (and maybe FreeBSD) are\nsupported
16
+ in this version.\n\nAllows Ruby programs to access sendfile(2) functionality on
17
+ \nany IO object. Works on Linux, Solaris, FreeBSD and Darwin with\nblocking and
18
+ non-blocking sockets.\n"
19
+ email: kgio@librelist.org
20
+ executables: []
21
+ extensions:
22
+ - ext/extconf.rb
23
+ extra_rdoc_files:
24
+ - README.textile
25
+ files:
26
+ - ChangeLog
27
+ - FILES
28
+ - LICENSE
29
+ - README.textile
30
+ - ext/extconf.rb
31
+ - ext/sendfile.c
32
+ - sendfile.gemspec
33
+ - test/large.gz
34
+ - test/small
35
+ - test/test_sendfile.rb
36
+ homepage: http://bogomips.org/ruby-sendfile.git
37
+ licenses: []
38
+ metadata: {}
39
+ post_install_message:
40
+ rdoc_options: []
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.8.0
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project:
55
+ rubygems_version: 2.2.2
56
+ signing_key:
57
+ specification_version: 4
58
+ summary: Ruby interface to sendfile(2) system call
59
+ test_files:
60
+ - test/test_sendfile.rb