kgio 1.0.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/ISSUES ADDED
@@ -0,0 +1,36 @@
1
+ = Issues
2
+
3
+ The Unicorn {mailing list}[mailto:mongrel-unicorn@rubyforge.org] is the best
4
+ place to report bugs, submit patches and/or obtain support after you
5
+ have searched the mailing list archives and
6
+ {documentation}[http://unicorn.bogomips.org/kgio].
7
+
8
+ * No subscription is needed to post to the mailing list,
9
+ let us know that we need to Cc: replies to you if you're unsubscribed.
10
+ * Do not {top post}[http://catb.org/jargon/html/T/top-post.html] in replies
11
+ * Quote only the relevant portions of the message you're replying to
12
+ * Do not send HTML mail
13
+
14
+ If your issue is of a sensitive nature or you're just shy in public,
15
+ then feel free to email us privately at mailto:unicorn@bogomips.org
16
+ instead and your issue will be handled discreetly.
17
+
18
+ If you don't get a response within a few days, we may have forgotten
19
+ about it so feel free to ask again.
20
+
21
+ == Submitting Patches
22
+
23
+ See the HACKING document (and additionally, the
24
+ Documentation/SubmittingPatches document distributed with git) on
25
+ guidelines for patch submission.
26
+
27
+ == Mailing List Info
28
+
29
+ * subscribe: http://rubyforge.org/mailman/listinfo/mongrel-unicorn
30
+ * post: mailto:mongrel-unicorn@rubyforge.org
31
+ * private: mailto:unicorn@bogomips.org
32
+
33
+ == Mailing List Archives
34
+
35
+ * nntp://news.gmane.org/gmane.comp.lang.ruby.unicorn.general
36
+ * http://rubyforge.org/pipermail/mongrel-unicorn
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ kgio is copyrighted Free Software by all contributors, see logs in
2
+ revision control for names and email addresses of all of them.
3
+
4
+ You can redistribute it and/or modify it under the terms of the GNU
5
+ Lesser General Public License (LGPL) as published by the Free Software
6
+ Foundation, version {2.1}[http://www.gnu.org/licenses/lgpl-2.1.txt] or
7
+ or {3}[http://www.gnu.org/licenses/lgpl-3.0.txt] (see link:COPYING).
8
+ The kgio project leader (Eric Wong) reserves the right to
9
+ relicense kgio under future versions of the LGPL.
10
+
11
+ kgio is distributed in the hope that it will be useful, but WITHOUT
12
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14
+ License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with the GNU C Library; if not, write to the Free Software
18
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
data/README ADDED
@@ -0,0 +1,68 @@
1
+ = kgio - kinder, gentler I/O for Ruby
2
+
3
+ kgio provides non-blocking I/O methods for Ruby without raising
4
+ exceptions on EAGAIN and EINPROGRESS. It is intended for use with the
5
+ Unicorn and Rainbows! Rack servers, but may be used by other
6
+ applications.
7
+
8
+ == Features
9
+
10
+ * Can avoid expensive exceptions on common EAGAIN/EINPROGRESS errors,
11
+ returning Kgio::WaitReadable or Kgio::WaitWritable instead.
12
+ These exceptions got more expensive to hit under Ruby 1.9.2
13
+ (but should be fixed in Ruby 1.9.3 to 1.9.1 performance levels)
14
+
15
+ * Returns the unwritten portion of the string on partial writes,
16
+ making it ideal for buffering unwritten data.
17
+
18
+ * May be assigned Kgio.wait_writable= and Kgio.wait_readable=
19
+ methods to allow socket/pipe objects to make custom callbacks
20
+ (such as adding the file descriptor to a poll set and yielding
21
+ the current Fiber).
22
+
23
+ * Uses
24
+ {accept4}[http://kernel.org/doc/man-pages/online/pages/man2/accept4.2.html]
25
+ on new GNU/Linux systems to avoid unnecessary fcntl() calls
26
+
27
+ * Uses MSG_DONTWAIT on GNU/Linux to further avoid unnecessary fcntl() calls
28
+
29
+ * Compatible with existing Ruby IO objects and Ruby threading.
30
+
31
+ == Install
32
+
33
+ The library consists of a C extension so you'll need a C compiler
34
+ and Ruby development libraries/headers.
35
+
36
+ You may download the tarball from the Mongrel project page on Rubyforge
37
+ and run setup.rb after unpacking it:
38
+
39
+ http://rubyforge.org/frs/?group_id=8977
40
+
41
+ You may also install it via RubyGems on Gemcutter:
42
+
43
+ gem install kgio
44
+
45
+ You can get the latest source via git from the following locations
46
+ (these versions may not be stable):
47
+
48
+ git://git.bogomips.org/kgio.git
49
+ git://repo.or.cz/kgio.git (mirror)
50
+
51
+ You may browse the code from the web and download the latest snapshot
52
+ tarballs here:
53
+
54
+ * http://git.bogomips.org/cgit/kgio.git (cgit)
55
+ * http://repo.or.cz/w/kgio.git (gitweb)
56
+
57
+ See the HACKING guide on how to contribute and build prerelease gems
58
+ from git.
59
+
60
+ == Contact
61
+
62
+ All feedback (bug reports, user/development dicussion, patches, pull
63
+ requests) go to the mailing list/newsgroup. See the ISSUES document for
64
+ information on the
65
+ {Unicorn mailing list}[mailto:mongrel-unicorn@rubyforge.org].
66
+
67
+ For the latest on kgio releases, you may check our NEWS page (and
68
+ subscribe to our Atom feed).
@@ -0,0 +1,169 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ # most tasks are in the GNUmakefile which offers better parallelism
4
+
5
+ def tags
6
+ timefmt = '%Y-%m-%dT%H:%M:%SZ'
7
+ @tags ||= `git tag -l`.split(/\n/).map do |tag|
8
+ if %r{\Av[\d\.]+\z} =~ tag
9
+ header, subject, body = `git cat-file tag #{tag}`.split(/\n\n/, 3)
10
+ header = header.split(/\n/)
11
+ tagger = header.grep(/\Atagger /).first
12
+ body ||= "initial"
13
+ {
14
+ :time => Time.at(tagger.split(/ /)[-2].to_i).utc.strftime(timefmt),
15
+ :tagger_name => %r{^tagger ([^<]+)}.match(tagger)[1].strip,
16
+ :tagger_email => %r{<([^>]+)>}.match(tagger)[1].strip,
17
+ :id => `git rev-parse refs/tags/#{tag}`.chomp!,
18
+ :tag => tag,
19
+ :subject => subject,
20
+ :body => body,
21
+ }
22
+ end
23
+ end.compact.sort { |a,b| b[:time] <=> a[:time] }
24
+ end
25
+
26
+ cgit_url = "http://git.bogomips.org/cgit/kgio.git"
27
+ git_url = ENV['GIT_URL'] || 'git://git.bogomips.org/kgio.git'
28
+ web_url = "http://unicorn.bogomips.org/kgio"
29
+
30
+ desc 'prints news as an Atom feed'
31
+ task :news_atom do
32
+ require 'nokogiri'
33
+ new_tags = tags[0,10]
34
+ puts(Nokogiri::XML::Builder.new do
35
+ feed :xmlns => "http://www.w3.org/2005/Atom" do
36
+ id! "#{web_url}NEWS.atom.xml"
37
+ title "kgio news"
38
+ subtitle "socket methods using MSG_DONTWAIT and more"
39
+ link! :rel => "alternate", :type => "text/html",
40
+ :href => "#{web_url}NEWS.html"
41
+ updated(new_tags.empty? ? "1970-01-01T00:00:00Z" : new_tags.first[:time])
42
+ new_tags.each do |tag|
43
+ entry do
44
+ title tag[:subject]
45
+ updated tag[:time]
46
+ published tag[:time]
47
+ author {
48
+ name tag[:tagger_name]
49
+ email tag[:tagger_email]
50
+ }
51
+ url = "#{cgit_url}/tag/?id=#{tag[:tag]}"
52
+ link! :rel => "alternate", :type => "text/html", :href =>url
53
+ id! url
54
+ message_only = tag[:body].split(/\n.+\(\d+\):\n {6}/s).first.strip
55
+ content({:type =>:text}, message_only)
56
+ content(:type =>:xhtml) { pre tag[:body] }
57
+ end
58
+ end
59
+ end
60
+ end.to_xml)
61
+ end
62
+
63
+ desc 'prints RDoc-formatted news'
64
+ task :news_rdoc do
65
+ tags.each do |tag|
66
+ time = tag[:time].tr!('T', ' ').gsub!(/:\d\dZ/, ' UTC')
67
+ puts "=== #{tag[:tag].sub(/^v/, '')} / #{time}"
68
+ puts ""
69
+
70
+ body = tag[:body]
71
+ puts tag[:body].gsub(/^/sm, " ").gsub(/[ \t]+$/sm, "")
72
+ puts ""
73
+ end
74
+ end
75
+
76
+ desc "print release changelog for Rubyforge"
77
+ task :release_changes do
78
+ version = ENV['VERSION'] or abort "VERSION= needed"
79
+ version = "v#{version}"
80
+ vtags = tags.map { |tag| tag[:tag] =~ /\Av/ and tag[:tag] }.sort
81
+ prev = vtags[vtags.index(version) - 1]
82
+ if prev
83
+ system('git', 'diff', '--stat', prev, version) or abort $?
84
+ puts ""
85
+ system('git', 'log', "#{prev}..#{version}") or abort $?
86
+ else
87
+ system('git', 'log', version) or abort $?
88
+ end
89
+ end
90
+
91
+ desc "print release notes for Rubyforge"
92
+ task :release_notes do
93
+ spec = Gem::Specification.load('kgio.gemspec')
94
+ puts spec.description.strip
95
+ puts ""
96
+ puts "* #{spec.homepage}"
97
+ puts "* #{spec.email}"
98
+ puts "* #{git_url}"
99
+
100
+ _, _, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3)
101
+ print "\nChanges:\n\n"
102
+ puts body
103
+ end
104
+
105
+ desc "post news article to rubyforge"
106
+ task :publish_news do
107
+ require 'rubyforge'
108
+ spec = Gem::Specification.load('kgio.gemspec')
109
+ tmp = Tempfile.new('rf-news')
110
+ _, subject, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3)
111
+ tmp.puts subject
112
+ tmp.puts
113
+ tmp.puts spec.description.strip
114
+ tmp.puts ""
115
+ tmp.puts "* #{spec.homepage}"
116
+ tmp.puts "* #{spec.email}"
117
+ tmp.puts "* #{git_url}"
118
+ tmp.print "\nChanges:\n\n"
119
+ tmp.puts body
120
+ tmp.flush
121
+ system(ENV["VISUAL"], tmp.path) or abort "#{ENV["VISUAL"]} failed: #$?"
122
+ msg = File.readlines(tmp.path)
123
+ subject = msg.shift
124
+ blank = msg.shift
125
+ blank == "\n" or abort "no newline after subject!"
126
+ subject.strip!
127
+ body = msg.join("").strip!
128
+
129
+ rf = RubyForge.new.configure
130
+ rf.login
131
+ rf.post_news('rainbows', subject, body)
132
+ end
133
+
134
+ desc "post to RAA"
135
+ task :raa_update do
136
+ require 'net/http'
137
+ require 'net/netrc'
138
+ rc = Net::Netrc.locate('kgio-raa') or abort "~/.netrc not found"
139
+ password = rc.password
140
+
141
+ s = Gem::Specification.load('kgio.gemspec')
142
+ desc = [ s.description.strip ]
143
+ desc << ""
144
+ desc << "* #{s.email}"
145
+ desc << "* #{git_url}"
146
+ desc << "* #{cgit_url}"
147
+ desc = desc.join("\n")
148
+ uri = URI.parse('http://raa.ruby-lang.org/regist.rhtml')
149
+ form = {
150
+ :name => s.name,
151
+ :short_description => s.summary,
152
+ :version => s.version.to_s,
153
+ :status => 'experimental',
154
+ :owner => s.authors.first,
155
+ :email => s.email,
156
+ :category_major => 'Library',
157
+ :category_minor => 'System',
158
+ :url => s.homepage,
159
+ :download => 'http://rubyforge.org/frs/?group_id=8977',
160
+ :license => "Ruby's",
161
+ :description_style => 'Plain',
162
+ :description => desc,
163
+ :pass => password,
164
+ :submit => 'Update',
165
+ }
166
+ res = Net::HTTP.post_form(uri, form)
167
+ p res
168
+ puts res.body
169
+ end
data/TODO ADDED
@@ -0,0 +1 @@
1
+ * SSL/TLS support with SNI
@@ -0,0 +1,21 @@
1
+ require 'mkmf'
2
+ $CPPFLAGS << ' -D_GNU_SOURCE'
3
+
4
+ have_func('accept4', %w(sys/socket.h))
5
+ if have_header('ruby/io.h')
6
+ have_struct_member("rb_io_t", "fd", "ruby/io.h")
7
+ have_struct_member("rb_io_t", "mode", "ruby/io.h")
8
+ else
9
+ rubyio = %w(ruby.h rubyio.h)
10
+ rb_io_t = have_type("OpenFile", rubyio) ? "OpenFile" : "rb_io_t"
11
+ have_struct_member(rb_io_t, "f", rubyio)
12
+ have_struct_member(rb_io_t, "f2", rubyio)
13
+ have_struct_member(rb_io_t, "mode", rubyio)
14
+ have_func('rb_fdopen')
15
+ end
16
+ have_func('rb_io_ascii8bit_binmode')
17
+ have_func('rb_thread_blocking_region')
18
+ have_func('rb_str_set_len')
19
+
20
+ dir_config('kgio')
21
+ create_makefile('kgio_ext')
@@ -0,0 +1,1066 @@
1
+ #include <ruby.h>
2
+ #ifdef HAVE_RUBY_IO_H
3
+ # include <ruby/io.h>
4
+ #else
5
+ # include <rubyio.h>
6
+ #endif
7
+ #include <errno.h>
8
+ #include <sys/types.h>
9
+ #include <sys/socket.h>
10
+ #include <sys/un.h>
11
+ #include <netinet/in.h>
12
+ #include <fcntl.h>
13
+ #include <unistd.h>
14
+ #include <arpa/inet.h>
15
+ #include <assert.h>
16
+
17
+ #include "missing/accept4.h"
18
+ #include "missing/ancient_ruby.h"
19
+ #include "nonblock.h"
20
+ #include "my_fileno.h"
21
+ #include "sock_for_fd.h"
22
+
23
+ #if defined(__linux__)
24
+ /*
25
+ * we know MSG_DONTWAIT works properly on all stream sockets under Linux
26
+ * we can define this macro for other platforms as people care and
27
+ * notice.
28
+ */
29
+ # define USE_MSG_DONTWAIT
30
+ static int accept4_flags = A4_SOCK_CLOEXEC;
31
+ #else /* ! linux */
32
+ static int accept4_flags = A4_SOCK_CLOEXEC | A4_SOCK_NONBLOCK;
33
+ #endif /* ! linux */
34
+
35
+ static VALUE cSocket;
36
+ static VALUE localhost;
37
+ static VALUE mKgio_WaitReadable, mKgio_WaitWritable;
38
+ static ID io_wait_rd, io_wait_wr;
39
+ static ID iv_kgio_addr;
40
+
41
+ struct io_args {
42
+ VALUE io;
43
+ VALUE buf;
44
+ char *ptr;
45
+ long len;
46
+ int fd;
47
+ };
48
+
49
+ struct accept_args {
50
+ int fd;
51
+ struct sockaddr *addr;
52
+ socklen_t *addrlen;
53
+ };
54
+
55
+ static void wait_readable(VALUE io, int fd)
56
+ {
57
+ if (io_wait_rd) {
58
+ (void)rb_funcall(io, io_wait_rd, 0, 0);
59
+ } else {
60
+ if (!rb_io_wait_readable(fd))
61
+ rb_sys_fail("wait readable");
62
+ }
63
+ }
64
+
65
+ static void wait_writable(VALUE io, int fd)
66
+ {
67
+ if (io_wait_wr) {
68
+ (void)rb_funcall(io, io_wait_wr, 0, 0);
69
+ } else {
70
+ if (!rb_io_wait_writable(fd))
71
+ rb_sys_fail("wait writable");
72
+ }
73
+ }
74
+
75
+ static void prepare_read(struct io_args *a, int argc, VALUE *argv, VALUE io)
76
+ {
77
+ VALUE length;
78
+
79
+ a->io = io;
80
+ a->fd = my_fileno(io);
81
+ rb_scan_args(argc, argv, "11", &length, &a->buf);
82
+ a->len = NUM2LONG(length);
83
+ if (NIL_P(a->buf)) {
84
+ a->buf = rb_str_new(NULL, a->len);
85
+ } else {
86
+ StringValue(a->buf);
87
+ rb_str_resize(a->buf, a->len);
88
+ }
89
+ a->ptr = RSTRING_PTR(a->buf);
90
+ }
91
+
92
+ static int read_check(struct io_args *a, long n, const char *msg, int io_wait)
93
+ {
94
+ if (n == -1) {
95
+ if (errno == EINTR)
96
+ return -1;
97
+ rb_str_set_len(a->buf, 0);
98
+ if (errno == EAGAIN) {
99
+ if (io_wait) {
100
+ wait_readable(a->io, a->fd);
101
+
102
+ /* buf may be modified in other thread/fiber */
103
+ rb_str_resize(a->buf, a->len);
104
+ a->ptr = RSTRING_PTR(a->buf);
105
+ return -1;
106
+ } else {
107
+ a->buf = mKgio_WaitReadable;
108
+ return 0;
109
+ }
110
+ }
111
+ rb_sys_fail(msg);
112
+ }
113
+ rb_str_set_len(a->buf, n);
114
+ if (n == 0)
115
+ a->buf = Qnil;
116
+ return 0;
117
+ }
118
+
119
+ static VALUE my_read(int io_wait, int argc, VALUE *argv, VALUE io)
120
+ {
121
+ struct io_args a;
122
+ long n;
123
+
124
+ prepare_read(&a, argc, argv, io);
125
+ set_nonblocking(a.fd);
126
+ retry:
127
+ n = (long)read(a.fd, a.ptr, a.len);
128
+ if (read_check(&a, n, "read", io_wait) != 0)
129
+ goto retry;
130
+ return a.buf;
131
+ }
132
+
133
+ /*
134
+ * call-seq:
135
+ *
136
+ * io.kgio_read(maxlen) -> buffer
137
+ * io.kgio_read(maxlen, buffer) -> buffer
138
+ *
139
+ * Reads at most maxlen bytes from the stream socket. Returns with a
140
+ * newly allocated buffer, or may reuse an existing buffer if supplied.
141
+ *
142
+ * Calls the method assigned to Kgio.wait_readable, or blocks in a
143
+ * thread-safe manner for writability.
144
+ *
145
+ * Returns nil on EOF.
146
+ *
147
+ * This behaves like read(2) and IO#readpartial, NOT fread(3) or
148
+ * IO#read which possess read-in-full behavior.
149
+ */
150
+ static VALUE kgio_read(int argc, VALUE *argv, VALUE io)
151
+ {
152
+ return my_read(1, argc, argv, io);
153
+ }
154
+
155
+ /*
156
+ * call-seq:
157
+ *
158
+ * io.kgio_tryread(maxlen) -> buffer
159
+ * io.kgio_tryread(maxlen, buffer) -> buffer
160
+ *
161
+ * Reads at most maxlen bytes from the stream socket. Returns with a
162
+ * newly allocated buffer, or may reuse an existing buffer if supplied.
163
+ *
164
+ * Returns nil on EOF.
165
+ *
166
+ * Returns Kgio::WaitReadable if EAGAIN is encountered.
167
+ */
168
+ static VALUE kgio_tryread(int argc, VALUE *argv, VALUE io)
169
+ {
170
+ return my_read(0, argc, argv, io);
171
+ }
172
+
173
+ #ifdef USE_MSG_DONTWAIT
174
+ static VALUE my_recv(int io_wait, int argc, VALUE *argv, VALUE io)
175
+ {
176
+ struct io_args a;
177
+ long n;
178
+
179
+ prepare_read(&a, argc, argv, io);
180
+ retry:
181
+ n = (long)recv(a.fd, a.ptr, a.len, MSG_DONTWAIT);
182
+ if (read_check(&a, n, "recv", io_wait) != 0)
183
+ goto retry;
184
+ return a.buf;
185
+ }
186
+
187
+ /*
188
+ * This method may be optimized on some systems (e.g. GNU/Linux) to use
189
+ * MSG_DONTWAIT to avoid explicitly setting the O_NONBLOCK flag via fcntl.
190
+ * Otherwise this is the same as Kgio::PipeMethods#kgio_read
191
+ */
192
+ static VALUE kgio_recv(int argc, VALUE *argv, VALUE io)
193
+ {
194
+ return my_recv(1, argc, argv, io);
195
+ }
196
+
197
+ /*
198
+ * This method may be optimized on some systems (e.g. GNU/Linux) to use
199
+ * MSG_DONTWAIT to avoid explicitly setting the O_NONBLOCK flag via fcntl.
200
+ * Otherwise this is the same as Kgio::PipeMethods#kgio_tryread
201
+ */
202
+ static VALUE kgio_tryrecv(int argc, VALUE *argv, VALUE io)
203
+ {
204
+ return my_recv(0, argc, argv, io);
205
+ }
206
+ #else /* ! USE_MSG_DONTWAIT */
207
+ # define kgio_recv kgio_read
208
+ # define kgio_tryrecv kgio_tryread
209
+ #endif /* USE_MSG_DONTWAIT */
210
+
211
+ static void prepare_write(struct io_args *a, VALUE io, VALUE str)
212
+ {
213
+ a->buf = (TYPE(str) == T_STRING) ? str : rb_obj_as_string(str);
214
+ a->ptr = RSTRING_PTR(a->buf);
215
+ a->len = RSTRING_LEN(a->buf);
216
+ a->io = io;
217
+ a->fd = my_fileno(io);
218
+ }
219
+
220
+ static int write_check(struct io_args *a, long n, const char *msg, int io_wait)
221
+ {
222
+ if (a->len == n) {
223
+ done:
224
+ a->buf = Qnil;
225
+ } else if (n == -1) {
226
+ if (errno == EINTR)
227
+ return -1;
228
+ if (errno == EAGAIN) {
229
+ if (io_wait) {
230
+ long written = RSTRING_LEN(a->buf) - a->len;
231
+
232
+ wait_writable(a->io, a->fd);
233
+
234
+ /* buf may be modified in other thread/fiber */
235
+ a->len = RSTRING_LEN(a->buf) - written;
236
+ if (a->len <= 0)
237
+ goto done;
238
+ a->ptr = RSTRING_PTR(a->buf) + written;
239
+ return -1;
240
+ } else {
241
+ a->buf = mKgio_WaitWritable;
242
+ return 0;
243
+ }
244
+ }
245
+ rb_sys_fail(msg);
246
+ } else {
247
+ assert(n >= 0 && n < a->len && "write/send syscall broken?");
248
+ if (io_wait) {
249
+ a->ptr += n;
250
+ a->len -= n;
251
+ return -1;
252
+ }
253
+ a->buf = rb_str_new(a->ptr + n, a->len - n);
254
+ }
255
+ return 0;
256
+ }
257
+
258
+ static VALUE my_write(VALUE io, VALUE str, int io_wait)
259
+ {
260
+ struct io_args a;
261
+ long n;
262
+
263
+ prepare_write(&a, io, str);
264
+ set_nonblocking(a.fd);
265
+ retry:
266
+ n = (long)write(a.fd, a.ptr, a.len);
267
+ if (write_check(&a, n, "write", io_wait) != 0)
268
+ goto retry;
269
+ return a.buf;
270
+ }
271
+
272
+ /*
273
+ * call-seq:
274
+ *
275
+ * io.kgio_write(str) -> nil
276
+ *
277
+ * Returns nil when the write completes.
278
+ *
279
+ * Calls the method Kgio.wait_writable if it is set. Otherwise this
280
+ * blocks in a thread-safe manner until all data is written or a
281
+ * fatal error occurs.
282
+ */
283
+ static VALUE kgio_write(VALUE io, VALUE str)
284
+ {
285
+ return my_write(io, str, 1);
286
+ }
287
+
288
+ /*
289
+ * call-seq:
290
+ *
291
+ * io.kgio_trywrite(str) -> nil or Kgio::WaitWritable
292
+ *
293
+ * Returns nil if the write was completed in full.
294
+ *
295
+ * Returns a String containing the unwritten portion if there was a
296
+ * partial write.
297
+ *
298
+ * Returns Kgio::WaitWritable if EAGAIN is encountered.
299
+ */
300
+ static VALUE kgio_trywrite(VALUE io, VALUE str)
301
+ {
302
+ return my_write(io, str, 0);
303
+ }
304
+
305
+ #ifdef USE_MSG_DONTWAIT
306
+ /*
307
+ * This method behaves like Kgio::PipeMethods#kgio_write, except
308
+ * it will use send(2) with the MSG_DONTWAIT flag on sockets to
309
+ * avoid unnecessary calls to fcntl(2).
310
+ */
311
+ static VALUE my_send(VALUE io, VALUE str, int io_wait)
312
+ {
313
+ struct io_args a;
314
+ long n;
315
+
316
+ prepare_write(&a, io, str);
317
+ retry:
318
+ n = (long)send(a.fd, a.ptr, a.len, MSG_DONTWAIT);
319
+ if (write_check(&a, n, "send", io_wait) != 0)
320
+ goto retry;
321
+ return a.buf;
322
+ }
323
+
324
+ /*
325
+ * This method may be optimized on some systems (e.g. GNU/Linux) to use
326
+ * MSG_DONTWAIT to avoid explicitly setting the O_NONBLOCK flag via fcntl.
327
+ * Otherwise this is the same as Kgio::PipeMethods#kgio_write
328
+ */
329
+ static VALUE kgio_send(VALUE io, VALUE str)
330
+ {
331
+ return my_send(io, str, 1);
332
+ }
333
+
334
+ /*
335
+ * This method may be optimized on some systems (e.g. GNU/Linux) to use
336
+ * MSG_DONTWAIT to avoid explicitly setting the O_NONBLOCK flag via fcntl.
337
+ * Otherwise this is the same as Kgio::PipeMethods#kgio_trywrite
338
+ */
339
+ static VALUE kgio_trysend(VALUE io, VALUE str)
340
+ {
341
+ return my_send(io, str, 0);
342
+ }
343
+ #else /* ! USE_MSG_DONTWAIT */
344
+ # define kgio_send kgio_write
345
+ # define kgio_trysend kgio_trywrite
346
+ #endif /* ! USE_MSG_DONTWAIT */
347
+
348
+ /*
349
+ * call-seq:
350
+ *
351
+ * Kgio.wait_readable = :method_name
352
+ * Kgio.wait_readable = nil
353
+ *
354
+ * Sets a method for kgio_read to call when a read would block.
355
+ * This is useful for non-blocking frameworks that use Fibers,
356
+ * as the method referred to this may cause the current Fiber
357
+ * to yield execution.
358
+ *
359
+ * A special value of nil will cause Ruby to wait using the
360
+ * rb_io_wait_readable() function.
361
+ */
362
+ static VALUE set_wait_rd(VALUE mod, VALUE sym)
363
+ {
364
+ switch (TYPE(sym)) {
365
+ case T_SYMBOL:
366
+ io_wait_rd = SYM2ID(sym);
367
+ return sym;
368
+ case T_NIL:
369
+ io_wait_rd = 0;
370
+ return sym;
371
+ }
372
+ rb_raise(rb_eTypeError, "must be a symbol or nil");
373
+ return sym;
374
+ }
375
+
376
+ /*
377
+ * call-seq:
378
+ *
379
+ * Kgio.wait_writable = :method_name
380
+ * Kgio.wait_writable = nil
381
+ *
382
+ * Sets a method for kgio_write to call when a read would block.
383
+ * This is useful for non-blocking frameworks that use Fibers,
384
+ * as the method referred to this may cause the current Fiber
385
+ * to yield execution.
386
+ *
387
+ * A special value of nil will cause Ruby to wait using the
388
+ * rb_io_wait_writable() function.
389
+ */
390
+ static VALUE set_wait_wr(VALUE mod, VALUE sym)
391
+ {
392
+ switch (TYPE(sym)) {
393
+ case T_SYMBOL:
394
+ io_wait_wr = SYM2ID(sym);
395
+ return sym;
396
+ case T_NIL:
397
+ io_wait_wr = 0;
398
+ return sym;
399
+ }
400
+ rb_raise(rb_eTypeError, "must be a symbol or nil");
401
+ return sym;
402
+ }
403
+
404
+ /*
405
+ * call-seq:
406
+ *
407
+ * Kgio.wait_writable -> Symbol or nil
408
+ *
409
+ * Returns the symbolic method name of the method assigned to
410
+ * call when EAGAIN is occurs on a Kgio::PipeMethods#kgio_write
411
+ * or Kgio::SocketMethods#kgio_write call
412
+ */
413
+ static VALUE wait_wr(VALUE mod)
414
+ {
415
+ return io_wait_wr ? ID2SYM(io_wait_wr) : Qnil;
416
+ }
417
+
418
+ /*
419
+ * call-seq:
420
+ *
421
+ * Kgio.wait_readable -> Symbol or nil
422
+ *
423
+ * Returns the symbolic method name of the method assigned to
424
+ * call when EAGAIN is occurs on a Kgio::PipeMethods#kgio_read
425
+ * or Kgio::SocketMethods#kgio_read call.
426
+ */
427
+ static VALUE wait_rd(VALUE mod)
428
+ {
429
+ return io_wait_rd ? ID2SYM(io_wait_rd) : Qnil;
430
+ }
431
+
432
+ static VALUE xaccept(void *ptr)
433
+ {
434
+ struct accept_args *a = ptr;
435
+
436
+ return (VALUE)accept4(a->fd, a->addr, a->addrlen, accept4_flags);
437
+ }
438
+
439
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
440
+ # include <time.h>
441
+ /*
442
+ * Try to use a (real) blocking accept() since that can prevent
443
+ * thundering herds under Linux:
444
+ * http://www.citi.umich.edu/projects/linux-scalability/reports/accept.html
445
+ *
446
+ * So we periodically disable non-blocking, but not too frequently
447
+ * because other processes may set non-blocking (especially during
448
+ * a process upgrade) with Rainbows! concurrency model changes.
449
+ */
450
+ static int thread_accept(struct accept_args *a, int force_nonblock)
451
+ {
452
+ if (force_nonblock)
453
+ set_nonblocking(a->fd);
454
+ return (int)rb_thread_blocking_region(xaccept, a, RUBY_UBF_IO, 0);
455
+ }
456
+
457
+ static void set_blocking_or_block(int fd)
458
+ {
459
+ static time_t last_set_blocking;
460
+ time_t now = time(NULL);
461
+
462
+ if (last_set_blocking == 0) {
463
+ last_set_blocking = now;
464
+ (void)rb_io_wait_readable(fd);
465
+ } else if ((now - last_set_blocking) <= 5) {
466
+ (void)rb_io_wait_readable(fd);
467
+ } else {
468
+ int flags = fcntl(fd, F_GETFL);
469
+ if (flags == -1)
470
+ rb_sys_fail("fcntl(F_GETFL)");
471
+ if (flags & O_NONBLOCK) {
472
+ flags = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
473
+ if (flags == -1)
474
+ rb_sys_fail("fcntl(F_SETFL)");
475
+ }
476
+ last_set_blocking = now;
477
+ }
478
+ }
479
+ #else /* ! HAVE_RB_THREAD_BLOCKING_REGION */
480
+ # include <rubysig.h>
481
+ static int thread_accept(struct accept_args *a, int force_nonblock)
482
+ {
483
+ int rv;
484
+
485
+ /* always use non-blocking accept() under 1.8 for green threads */
486
+ set_nonblocking(a->fd);
487
+ TRAP_BEG;
488
+ rv = (int)xaccept(a);
489
+ TRAP_END;
490
+ return rv;
491
+ }
492
+ #define set_blocking_or_block(fd) (void)rb_io_wait_readable(fd)
493
+ #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
494
+
495
+ static VALUE
496
+ my_accept(VALUE io, struct sockaddr *addr, socklen_t *addrlen, int nonblock)
497
+ {
498
+ int client;
499
+ struct accept_args a;
500
+
501
+ a.fd = my_fileno(io);
502
+ a.addr = addr;
503
+ a.addrlen = addrlen;
504
+ retry:
505
+ client = thread_accept(&a, nonblock);
506
+ if (client == -1) {
507
+ switch (errno) {
508
+ case EAGAIN:
509
+ if (nonblock)
510
+ return Qnil;
511
+ set_blocking_or_block(a.fd);
512
+ #ifdef ECONNABORTED
513
+ case ECONNABORTED:
514
+ #endif /* ECONNABORTED */
515
+ #ifdef EPROTO
516
+ case EPROTO:
517
+ #endif /* EPROTO */
518
+ case EINTR:
519
+ goto retry;
520
+ case ENOMEM:
521
+ case EMFILE:
522
+ case ENFILE:
523
+ #ifdef ENOBUFS
524
+ case ENOBUFS:
525
+ #endif /* ENOBUFS */
526
+ errno = 0;
527
+ rb_gc();
528
+ client = thread_accept(&a, nonblock);
529
+ }
530
+ if (client == -1) {
531
+ if (errno == EINTR)
532
+ goto retry;
533
+ rb_sys_fail("accept");
534
+ }
535
+ }
536
+ return sock_for_fd(cSocket, client);
537
+ }
538
+
539
+ static void in_addr_set(VALUE io, struct sockaddr_in *addr)
540
+ {
541
+ VALUE host = rb_str_new(0, INET_ADDRSTRLEN);
542
+ socklen_t addrlen = (socklen_t)INET_ADDRSTRLEN;
543
+ const char *name;
544
+
545
+ name = inet_ntop(AF_INET, &addr->sin_addr, RSTRING_PTR(host), addrlen);
546
+ if (name == NULL)
547
+ rb_sys_fail("inet_ntop");
548
+ rb_str_set_len(host, strlen(name));
549
+ rb_ivar_set(io, iv_kgio_addr, host);
550
+ }
551
+
552
+ /*
553
+ * call-seq:
554
+ *
555
+ * server = Kgio::TCPServer.new('0.0.0.0', 80)
556
+ * server.kgio_tryaccept -> Kgio::Socket or nil
557
+ *
558
+ * Initiates a non-blocking accept and returns a generic Kgio::Socket
559
+ * object with the kgio_addr attribute set to the IP address of the
560
+ * connected client on success.
561
+ *
562
+ * Returns nil on EAGAIN, and raises on other errors.
563
+ */
564
+ static VALUE tcp_tryaccept(VALUE io)
565
+ {
566
+ struct sockaddr_in addr;
567
+ socklen_t addrlen = sizeof(struct sockaddr_in);
568
+ VALUE rv = my_accept(io, (struct sockaddr *)&addr, &addrlen, 1);
569
+
570
+ if (!NIL_P(rv))
571
+ in_addr_set(rv, &addr);
572
+ return rv;
573
+ }
574
+
575
+ /*
576
+ * call-seq:
577
+ *
578
+ * server = Kgio::TCPServer.new('0.0.0.0', 80)
579
+ * server.kgio_accept -> Kgio::Socket or nil
580
+ *
581
+ * Initiates a blocking accept and returns a generic Kgio::Socket
582
+ * object with the kgio_addr attribute set to the IP address of
583
+ * the client on success.
584
+ *
585
+ * On Ruby implementations using native threads, this can use a blocking
586
+ * accept(2) (or accept4(2)) system call to avoid thundering herds.
587
+ */
588
+ static VALUE tcp_accept(VALUE io)
589
+ {
590
+ struct sockaddr_in addr;
591
+ socklen_t addrlen = sizeof(struct sockaddr_in);
592
+ VALUE rv = my_accept(io, (struct sockaddr *)&addr, &addrlen, 0);
593
+
594
+ in_addr_set(rv, &addr);
595
+ return rv;
596
+ }
597
+
598
+ /*
599
+ * call-seq:
600
+ *
601
+ * server = Kgio::UNIXServer.new("/path/to/unix/socket")
602
+ * server.kgio_tryaccept -> Kgio::Socket or nil
603
+ *
604
+ * Initiates a non-blocking accept and returns a generic Kgio::Socket
605
+ * object with the kgio_addr attribute set (to the value of
606
+ * Kgio::LOCALHOST) on success.
607
+ *
608
+ * Returns nil on EAGAIN, and raises on other errors.
609
+ */
610
+ static VALUE unix_tryaccept(VALUE io)
611
+ {
612
+ VALUE rv = my_accept(io, NULL, NULL, 1);
613
+
614
+ if (!NIL_P(rv))
615
+ rb_ivar_set(rv, iv_kgio_addr, localhost);
616
+ return rv;
617
+ }
618
+
619
+ /*
620
+ * call-seq:
621
+ *
622
+ * server = Kgio::UNIXServer.new("/path/to/unix/socket")
623
+ * server.kgio_accept -> Kgio::Socket or nil
624
+ *
625
+ * Initiates a blocking accept and returns a generic Kgio::Socket
626
+ * object with the kgio_addr attribute set (to the value of
627
+ * Kgio::LOCALHOST) on success.
628
+ *
629
+ * On Ruby implementations using native threads, this can use a blocking
630
+ * accept(2) (or accept4(2)) system call to avoid thundering herds.
631
+ */
632
+ static VALUE unix_accept(VALUE io)
633
+ {
634
+ VALUE rv = my_accept(io, NULL, NULL, 0);
635
+
636
+ rb_ivar_set(rv, iv_kgio_addr, localhost);
637
+ return rv;
638
+ }
639
+
640
+ /*
641
+ * call-seq:
642
+ *
643
+ * Kgio.accept_cloexec? -> true or false
644
+ *
645
+ * Returns true if newly accepted Kgio::Sockets are created with the
646
+ * FD_CLOEXEC file descriptor flag, false if not.
647
+ */
648
+ static VALUE get_cloexec(VALUE mod)
649
+ {
650
+ return (accept4_flags & A4_SOCK_CLOEXEC) ==
651
+ A4_SOCK_CLOEXEC ? Qtrue : Qfalse;
652
+ }
653
+
654
+ /*
655
+ *
656
+ * call-seq:
657
+ *
658
+ * Kgio.accept_nonblock? -> true or false
659
+ *
660
+ * Returns true if newly accepted Kgio::Sockets are created with the
661
+ * O_NONBLOCK file status flag, false if not.
662
+ */
663
+ static VALUE get_nonblock(VALUE mod)
664
+ {
665
+ return (accept4_flags & A4_SOCK_NONBLOCK) ==
666
+ A4_SOCK_NONBLOCK ? Qtrue : Qfalse;
667
+ }
668
+
669
+ /*
670
+ * call-seq:
671
+ *
672
+ * Kgio.accept_cloexec = true
673
+ * Kgio.accept_clocexec = false
674
+ *
675
+ * Sets whether or not Kgio::Socket objects created by
676
+ * TCPServer#kgio_accept,
677
+ * TCPServer#kgio_tryaccept,
678
+ * UNIXServer#kgio_accept,
679
+ * and UNIXServer#kgio_tryaccept
680
+ * are created with the FD_CLOEXEC file descriptor flag.
681
+ *
682
+ * This is on by default, as there is little reason to deal to enable
683
+ * it for client sockets on a socket server.
684
+ */
685
+ static VALUE set_cloexec(VALUE mod, VALUE boolean)
686
+ {
687
+ switch (TYPE(boolean)) {
688
+ case T_TRUE:
689
+ accept4_flags |= A4_SOCK_CLOEXEC;
690
+ return boolean;
691
+ case T_FALSE:
692
+ accept4_flags &= ~A4_SOCK_CLOEXEC;
693
+ return boolean;
694
+ }
695
+ rb_raise(rb_eTypeError, "not true or false");
696
+ return Qnil;
697
+ }
698
+
699
+ /*
700
+ * call-seq:
701
+ *
702
+ * Kgio.accept_nonblock = true
703
+ * Kgio.accept_nonblock = false
704
+ *
705
+ * Sets whether or not Kgio::Socket objects created by
706
+ * TCPServer#kgio_accept,
707
+ * TCPServer#kgio_tryaccept,
708
+ * UNIXServer#kgio_accept,
709
+ * and UNIXServer#kgio_tryaccept
710
+ * are created with the O_NONBLOCK file status flag.
711
+ *
712
+ * This defaults to +false+ for GNU/Linux where MSG_DONTWAIT is
713
+ * available (and on newer GNU/Linux, accept4() may also set
714
+ * the non-blocking flag. This defaults to +true+ on non-GNU/Linux
715
+ * systems.
716
+ */
717
+ static VALUE set_nonblock(VALUE mod, VALUE boolean)
718
+ {
719
+ switch (TYPE(boolean)) {
720
+ case T_TRUE:
721
+ accept4_flags |= A4_SOCK_NONBLOCK;
722
+ return boolean;
723
+ case T_FALSE:
724
+ accept4_flags &= ~A4_SOCK_NONBLOCK;
725
+ return boolean;
726
+ }
727
+ rb_raise(rb_eTypeError, "not true or false");
728
+ return Qnil;
729
+ }
730
+
731
+ static void close_fail(int fd, const char *msg)
732
+ {
733
+ int saved_errno = errno;
734
+ (void)close(fd);
735
+ errno = saved_errno;
736
+ rb_sys_fail(msg);
737
+ }
738
+
739
+ #ifdef SOCK_NONBLOCK
740
+ # define MY_SOCK_STREAM (SOCK_STREAM|SOCK_NONBLOCK)
741
+ #else
742
+ # define MY_SOCK_STREAM SOCK_STREAM
743
+ #endif /* ! SOCK_NONBLOCK */
744
+
745
+ static VALUE
746
+ my_connect(VALUE klass, int io_wait, int domain, void *addr, socklen_t addrlen)
747
+ {
748
+ int fd = socket(domain, MY_SOCK_STREAM, 0);
749
+
750
+ if (fd == -1) {
751
+ switch (errno) {
752
+ case EMFILE:
753
+ case ENFILE:
754
+ #ifdef ENOBUFS
755
+ case ENOBUFS:
756
+ #endif /* ENOBUFS */
757
+ errno = 0;
758
+ rb_gc();
759
+ fd = socket(domain, MY_SOCK_STREAM, 0);
760
+ }
761
+ if (fd == -1)
762
+ rb_sys_fail("socket");
763
+ }
764
+
765
+ #ifndef SOCK_NONBLOCK
766
+ if (fcntl(fd, F_SETFL, O_RDWR | O_NONBLOCK) == -1)
767
+ close_fail(fd, "fcntl(F_SETFL, O_RDWR | O_NONBLOCK)");
768
+ #endif /* SOCK_NONBLOCK */
769
+
770
+ if (connect(fd, addr, addrlen) == -1) {
771
+ if (errno == EINPROGRESS) {
772
+ VALUE io = sock_for_fd(klass, fd);
773
+
774
+ if (io_wait) {
775
+ errno = EAGAIN;
776
+ wait_writable(io, fd);
777
+ }
778
+ return io;
779
+ }
780
+ close_fail(fd, "connect");
781
+ }
782
+ return sock_for_fd(klass, fd);
783
+ }
784
+
785
+ static VALUE tcp_connect(VALUE klass, VALUE ip, VALUE port, int io_wait)
786
+ {
787
+ struct sockaddr_in addr = { 0 };
788
+
789
+ addr.sin_family = AF_INET;
790
+ addr.sin_port = htons((unsigned short)NUM2INT(port));
791
+
792
+ switch (inet_pton(AF_INET, StringValuePtr(ip), &addr.sin_addr)) {
793
+ case 1:
794
+ return my_connect(klass, io_wait, PF_INET, &addr, sizeof(addr));
795
+ case -1:
796
+ rb_sys_fail("inet_pton");
797
+ }
798
+ rb_raise(rb_eArgError, "invalid address: %s", StringValuePtr(ip));
799
+
800
+ return Qnil;
801
+ }
802
+
803
+ /*
804
+ * call-seq:
805
+ *
806
+ * Kgio::TCPSocket.new('127.0.0.1', 80) -> socket
807
+ *
808
+ * Creates a new Kgio::TCPSocket object and initiates a
809
+ * non-blocking connection.
810
+ *
811
+ * This may block and call any method assigned to Kgio.wait_writable.
812
+ *
813
+ * Unlike the TCPSocket.new in Ruby, this does NOT perform DNS
814
+ * lookups (which is subject to a different set of timeouts and
815
+ * best handled elsewhere).
816
+ */
817
+ static VALUE kgio_tcp_connect(VALUE klass, VALUE ip, VALUE port)
818
+ {
819
+ return tcp_connect(klass, ip, port, 1);
820
+ }
821
+
822
+ /*
823
+ * call-seq:
824
+ *
825
+ * Kgio::TCPSocket.start('127.0.0.1', 80) -> socket
826
+ *
827
+ * Creates a new Kgio::TCPSocket object and initiates a
828
+ * non-blocking connection. The caller should select/poll
829
+ * on the socket for writability before attempting to write
830
+ * or optimistically attempt a write and handle Kgio::WaitWritable
831
+ * or Errno::EAGAIN.
832
+ *
833
+ * Unlike the TCPSocket.new in Ruby, this does NOT perform DNS
834
+ * lookups (which is subject to a different set of timeouts and
835
+ * best handled elsewhere).
836
+ */
837
+ static VALUE kgio_tcp_start(VALUE klass, VALUE ip, VALUE port)
838
+ {
839
+ return tcp_connect(klass, ip, port, 0);
840
+ }
841
+
842
+ static VALUE unix_connect(VALUE klass, VALUE path, int io_wait)
843
+ {
844
+ struct sockaddr_un addr = { 0 };
845
+ long len;
846
+
847
+ StringValue(path);
848
+ len = RSTRING_LEN(path);
849
+ if (sizeof(addr.sun_path) <= len)
850
+ rb_raise(rb_eArgError,
851
+ "too long unix socket path (max: %dbytes)",
852
+ (int)sizeof(addr.sun_path)-1);
853
+
854
+ memcpy(addr.sun_path, RSTRING_PTR(path), len);
855
+ addr.sun_family = AF_UNIX;
856
+
857
+ return my_connect(klass, io_wait, PF_UNIX, &addr, sizeof(addr));
858
+ }
859
+
860
+ /*
861
+ * call-seq:
862
+ *
863
+ * Kgio::UNIXSocket.new("/path/to/unix/socket") -> socket
864
+ *
865
+ * Creates a new Kgio::UNIXSocket object and initiates a
866
+ * non-blocking connection.
867
+ *
868
+ * This may block and call any method assigned to Kgio.wait_writable.
869
+ */
870
+ static VALUE kgio_unix_connect(VALUE klass, VALUE path)
871
+ {
872
+ return unix_connect(klass, path, 1);
873
+ }
874
+
875
+ /*
876
+ * call-seq:
877
+ *
878
+ * Kgio::UNIXSocket.start("/path/to/unix/socket") -> socket
879
+ *
880
+ * Creates a new Kgio::UNIXSocket object and initiates a
881
+ * non-blocking connection. The caller should select/poll
882
+ * on the socket for writability before attempting to write
883
+ * or optimistically attempt a write and handle Kgio::WaitWritable
884
+ * or Errno::EAGAIN.
885
+ */
886
+ static VALUE kgio_unix_start(VALUE klass, VALUE path)
887
+ {
888
+ return unix_connect(klass, path, 0);
889
+ }
890
+
891
+ static VALUE stream_connect(VALUE klass, VALUE addr, int io_wait)
892
+ {
893
+ int domain;
894
+ socklen_t addrlen;
895
+ struct sockaddr *sockaddr;
896
+
897
+ if (TYPE(addr) == T_STRING) {
898
+ sockaddr = (struct sockaddr *)(RSTRING_PTR(addr));
899
+ addrlen = (socklen_t)RSTRING_LEN(addr);
900
+ } else {
901
+ rb_raise(rb_eTypeError, "invalid address");
902
+ }
903
+ switch (((struct sockaddr_in *)(sockaddr))->sin_family) {
904
+ case AF_UNIX: domain = PF_UNIX; break;
905
+ case AF_INET: domain = PF_INET; break;
906
+ #ifdef AF_INET6 /* IPv6 support incomplete */
907
+ case AF_INET6: domain = PF_INET6; break;
908
+ #endif /* AF_INET6 */
909
+ default:
910
+ rb_raise(rb_eArgError, "invalid address family");
911
+ }
912
+
913
+ return my_connect(klass, io_wait, domain, sockaddr, addrlen);
914
+ }
915
+
916
+ /* call-seq:
917
+ *
918
+ * addr = Socket.pack_sockaddr_in(80, 'example.com')
919
+ * Kgio::Socket.connect(addr) -> socket
920
+ *
921
+ * addr = Socket.pack_sockaddr_un("/path/to/unix/socket")
922
+ * Kgio::Socket.connect(addr) -> socket
923
+ *
924
+ * Creates a generic Kgio::Socket object and initiates a
925
+ * non-blocking connection.
926
+ *
927
+ * This may block and call any method assigned to Kgio.wait_writable.
928
+ */
929
+ static VALUE kgio_connect(VALUE klass, VALUE addr)
930
+ {
931
+ return stream_connect(klass, addr, 1);
932
+ }
933
+
934
+ /* call-seq:
935
+ *
936
+ * addr = Socket.pack_sockaddr_in(80, 'example.com')
937
+ * Kgio::Socket.start(addr) -> socket
938
+ *
939
+ * addr = Socket.pack_sockaddr_un("/path/to/unix/socket")
940
+ * Kgio::Socket.start(addr) -> socket
941
+ *
942
+ * Creates a generic Kgio::Socket object and initiates a
943
+ * non-blocking connection. The caller should select/poll
944
+ * on the socket for writability before attempting to write
945
+ * or optimistically attempt a write and handle Kgio::WaitWritable
946
+ * or Errno::EAGAIN.
947
+ */
948
+ static VALUE kgio_start(VALUE klass, VALUE addr)
949
+ {
950
+ return stream_connect(klass, addr, 0);
951
+ }
952
+
953
+ void Init_kgio_ext(void)
954
+ {
955
+ VALUE mKgio = rb_define_module("Kgio");
956
+ VALUE mPipeMethods, mSocketMethods;
957
+ VALUE cUNIXServer, cTCPServer, cUNIXSocket, cTCPSocket;
958
+
959
+ rb_require("socket");
960
+
961
+ /*
962
+ * Document-module: Kgio::Socket
963
+ *
964
+ * A generic socket class with Kgio::SocketMethods included.
965
+ * This is returned by all Kgio methods that accept(2) a connected
966
+ * stream socket.
967
+ */
968
+ cSocket = rb_const_get(rb_cObject, rb_intern("Socket"));
969
+ cSocket = rb_define_class_under(mKgio, "Socket", cSocket);
970
+
971
+ localhost = rb_str_new2("127.0.0.1");
972
+
973
+ /*
974
+ * The IPv4 address of UNIX domain sockets, useful for creating
975
+ * Rack (and CGI) servers that also serve HTTP traffic over
976
+ * UNIX domain sockets.
977
+ */
978
+ rb_const_set(mKgio, rb_intern("LOCALHOST"), localhost);
979
+
980
+ /*
981
+ * Document-module: Kgio::WaitReadable
982
+ *
983
+ * PipeMethods#kgio_tryread and SocketMethods#kgio_tryread will
984
+ * return this constant when waiting for a read is required.
985
+ */
986
+ mKgio_WaitReadable = rb_define_module_under(mKgio, "WaitReadable");
987
+
988
+ /*
989
+ * Document-module: Kgio::WaitWritable
990
+ *
991
+ * PipeMethods#kgio_trywrite and SocketMethods#kgio_trywrite will
992
+ * return this constant when waiting for a read is required.
993
+ */
994
+ mKgio_WaitWritable = rb_define_module_under(mKgio, "WaitWritable");
995
+
996
+ rb_define_singleton_method(mKgio, "wait_readable=", set_wait_rd, 1);
997
+ rb_define_singleton_method(mKgio, "wait_writable=", set_wait_wr, 1);
998
+ rb_define_singleton_method(mKgio, "wait_readable", wait_rd, 0);
999
+ rb_define_singleton_method(mKgio, "wait_writable", wait_wr, 0);
1000
+ rb_define_singleton_method(mKgio, "accept_cloexec?", get_cloexec, 0);
1001
+ rb_define_singleton_method(mKgio, "accept_cloexec=", set_cloexec, 1);
1002
+ rb_define_singleton_method(mKgio, "accept_nonblock?", get_nonblock, 0);
1003
+ rb_define_singleton_method(mKgio, "accept_nonblock=", set_nonblock, 1);
1004
+
1005
+ /*
1006
+ * Document-module: Kgio::PipeMethods
1007
+ *
1008
+ * This module may be used used to create classes that respond to
1009
+ * various Kgio methods for reading and writing. This is included
1010
+ * in Kgio::Pipe by default.
1011
+ */
1012
+ mPipeMethods = rb_define_module_under(mKgio, "PipeMethods");
1013
+ rb_define_method(mPipeMethods, "kgio_read", kgio_read, -1);
1014
+ rb_define_method(mPipeMethods, "kgio_write", kgio_write, 1);
1015
+ rb_define_method(mPipeMethods, "kgio_tryread", kgio_tryread, -1);
1016
+ rb_define_method(mPipeMethods, "kgio_trywrite", kgio_trywrite, 1);
1017
+
1018
+ /*
1019
+ * Document-module: Kgio::SocketMethods
1020
+ *
1021
+ * This method behaves like Kgio::PipeMethods, but contains
1022
+ * optimizations for sockets on certain operating systems
1023
+ * (e.g. GNU/Linux).
1024
+ */
1025
+ mSocketMethods = rb_define_module_under(mKgio, "SocketMethods");
1026
+ rb_define_method(mSocketMethods, "kgio_read", kgio_recv, -1);
1027
+ rb_define_method(mSocketMethods, "kgio_write", kgio_send, 1);
1028
+ rb_define_method(mSocketMethods, "kgio_tryread", kgio_tryrecv, -1);
1029
+ rb_define_method(mSocketMethods, "kgio_trywrite", kgio_trysend, 1);
1030
+
1031
+ /*
1032
+ * Returns the client IPv4 address of the socket in dotted quad
1033
+ * form as a string. This is always the value of the
1034
+ * Kgio::LOCALHOST constant for UNIX domain sockets.
1035
+ */
1036
+ rb_define_attr(mSocketMethods, "kgio_addr", 1, 1);
1037
+
1038
+ rb_include_module(cSocket, mSocketMethods);
1039
+ rb_define_singleton_method(cSocket, "new", kgio_connect, 1);
1040
+ rb_define_singleton_method(cSocket, "start", kgio_start, 1);
1041
+
1042
+ cUNIXServer = rb_const_get(rb_cObject, rb_intern("UNIXServer"));
1043
+ cUNIXServer = rb_define_class_under(mKgio, "UNIXServer", cUNIXServer);
1044
+ rb_define_method(cUNIXServer, "kgio_tryaccept", unix_tryaccept, 0);
1045
+ rb_define_method(cUNIXServer, "kgio_accept", unix_accept, 0);
1046
+
1047
+ cTCPServer = rb_const_get(rb_cObject, rb_intern("TCPServer"));
1048
+ cTCPServer = rb_define_class_under(mKgio, "TCPServer", cTCPServer);
1049
+ rb_define_method(cTCPServer, "kgio_tryaccept", tcp_tryaccept, 0);
1050
+ rb_define_method(cTCPServer, "kgio_accept", tcp_accept, 0);
1051
+
1052
+ cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
1053
+ cTCPSocket = rb_define_class_under(mKgio, "TCPSocket", cTCPSocket);
1054
+ rb_include_module(cTCPSocket, mSocketMethods);
1055
+ rb_define_singleton_method(cTCPSocket, "new", kgio_tcp_connect, 2);
1056
+ rb_define_singleton_method(cTCPSocket, "start", kgio_tcp_start, 2);
1057
+
1058
+ cUNIXSocket = rb_const_get(rb_cObject, rb_intern("UNIXSocket"));
1059
+ cUNIXSocket = rb_define_class_under(mKgio, "UNIXSocket", cUNIXSocket);
1060
+ rb_include_module(cUNIXSocket, mSocketMethods);
1061
+ rb_define_singleton_method(cUNIXSocket, "new", kgio_unix_connect, 1);
1062
+ rb_define_singleton_method(cUNIXSocket, "start", kgio_unix_start, 1);
1063
+
1064
+ iv_kgio_addr = rb_intern("@kgio_addr");
1065
+ init_sock_for_fd();
1066
+ }