kgio-sendfile 1.2.1.1

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