iodine 0.1.21 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -2
  3. data/.travis.yml +23 -2
  4. data/CHANGELOG.md +9 -2
  5. data/README.md +232 -179
  6. data/Rakefile +13 -1
  7. data/bin/config.ru +63 -0
  8. data/bin/console +6 -0
  9. data/bin/echo +42 -32
  10. data/bin/http-hello +62 -0
  11. data/bin/http-playground +124 -0
  12. data/bin/playground +62 -0
  13. data/bin/poc/Gemfile.lock +23 -0
  14. data/bin/poc/README.md +37 -0
  15. data/bin/poc/config.ru +66 -0
  16. data/bin/poc/gemfile +1 -0
  17. data/bin/poc/www/index.html +57 -0
  18. data/bin/raw-rbhttp +35 -0
  19. data/bin/raw_broadcast +66 -0
  20. data/bin/test_with_faye +40 -0
  21. data/bin/ws-broadcast +108 -0
  22. data/bin/ws-echo +108 -0
  23. data/exe/iodine +59 -0
  24. data/ext/iodine/base64.c +264 -0
  25. data/ext/iodine/base64.h +72 -0
  26. data/ext/iodine/bscrypt-common.h +109 -0
  27. data/ext/iodine/bscrypt.h +49 -0
  28. data/ext/iodine/extconf.rb +41 -0
  29. data/ext/iodine/hex.c +123 -0
  30. data/ext/iodine/hex.h +70 -0
  31. data/ext/iodine/http.c +200 -0
  32. data/ext/iodine/http.h +128 -0
  33. data/ext/iodine/http1.c +402 -0
  34. data/ext/iodine/http1.h +56 -0
  35. data/ext/iodine/http1_simple_parser.c +473 -0
  36. data/ext/iodine/http1_simple_parser.h +59 -0
  37. data/ext/iodine/http_request.h +128 -0
  38. data/ext/iodine/http_response.c +1606 -0
  39. data/ext/iodine/http_response.h +393 -0
  40. data/ext/iodine/http_response_http1.h +374 -0
  41. data/ext/iodine/iodine_core.c +641 -0
  42. data/ext/iodine/iodine_core.h +70 -0
  43. data/ext/iodine/iodine_http.c +615 -0
  44. data/ext/iodine/iodine_http.h +19 -0
  45. data/ext/iodine/iodine_websocket.c +430 -0
  46. data/ext/iodine/iodine_websocket.h +21 -0
  47. data/ext/iodine/libasync.c +552 -0
  48. data/ext/iodine/libasync.h +117 -0
  49. data/ext/iodine/libreact.c +347 -0
  50. data/ext/iodine/libreact.h +244 -0
  51. data/ext/iodine/libserver.c +912 -0
  52. data/ext/iodine/libserver.h +435 -0
  53. data/ext/iodine/libsock.c +950 -0
  54. data/ext/iodine/libsock.h +478 -0
  55. data/ext/iodine/misc.c +181 -0
  56. data/ext/iodine/misc.h +76 -0
  57. data/ext/iodine/random.c +193 -0
  58. data/ext/iodine/random.h +48 -0
  59. data/ext/iodine/rb-call.c +127 -0
  60. data/ext/iodine/rb-call.h +60 -0
  61. data/ext/iodine/rb-libasync.h +79 -0
  62. data/ext/iodine/rb-rack-io.c +389 -0
  63. data/ext/iodine/rb-rack-io.h +17 -0
  64. data/ext/iodine/rb-registry.c +213 -0
  65. data/ext/iodine/rb-registry.h +33 -0
  66. data/ext/iodine/sha1.c +359 -0
  67. data/ext/iodine/sha1.h +85 -0
  68. data/ext/iodine/sha2.c +825 -0
  69. data/ext/iodine/sha2.h +138 -0
  70. data/ext/iodine/siphash.c +136 -0
  71. data/ext/iodine/siphash.h +15 -0
  72. data/ext/iodine/spnlock.h +235 -0
  73. data/ext/iodine/websockets.c +696 -0
  74. data/ext/iodine/websockets.h +120 -0
  75. data/ext/iodine/xor-crypt.c +189 -0
  76. data/ext/iodine/xor-crypt.h +107 -0
  77. data/iodine.gemspec +25 -18
  78. data/lib/iodine.rb +57 -58
  79. data/lib/iodine/http.rb +0 -189
  80. data/lib/iodine/protocol.rb +36 -245
  81. data/lib/iodine/version.rb +1 -1
  82. data/lib/rack/handler/iodine.rb +145 -2
  83. metadata +115 -37
  84. data/bin/core_http_test +0 -51
  85. data/bin/em playground +0 -56
  86. data/bin/hello_world +0 -75
  87. data/bin/setup +0 -7
  88. data/lib/iodine/client.rb +0 -5
  89. data/lib/iodine/core.rb +0 -102
  90. data/lib/iodine/core_init.rb +0 -143
  91. data/lib/iodine/http/hpack.rb +0 -553
  92. data/lib/iodine/http/http1.rb +0 -251
  93. data/lib/iodine/http/http2.rb +0 -507
  94. data/lib/iodine/http/rack_support.rb +0 -108
  95. data/lib/iodine/http/request.rb +0 -462
  96. data/lib/iodine/http/response.rb +0 -474
  97. data/lib/iodine/http/session.rb +0 -143
  98. data/lib/iodine/http/websocket_client.rb +0 -335
  99. data/lib/iodine/http/websocket_handler.rb +0 -101
  100. data/lib/iodine/http/websockets.rb +0 -336
  101. data/lib/iodine/io.rb +0 -56
  102. data/lib/iodine/logging.rb +0 -46
  103. data/lib/iodine/settings.rb +0 -158
  104. data/lib/iodine/ssl_connector.rb +0 -48
  105. data/lib/iodine/timers.rb +0 -95
@@ -0,0 +1,48 @@
1
+ /*
2
+ (un)copyright: Boaz segev, 2016
3
+ License: Public Domain except for any non-public-domain algorithms, which are
4
+ subject to their own licenses.
5
+
6
+ Feel free to copy, use and enjoy in accordance with to the license(s).
7
+ */
8
+ #ifndef bscrypt_RANDOM_H
9
+ #define bscrypt_RANDOM_H
10
+ #include "bscrypt-common.h"
11
+ /* *****************************************************************************
12
+ C++ extern
13
+ */
14
+ #if defined(__cplusplus)
15
+ extern "C" {
16
+ #endif
17
+
18
+ /* ***************************************************************************
19
+ Random stuff... (why is this not a system call?)
20
+ */
21
+
22
+ /** returns 32 random bits. */
23
+ uint32_t bscrypt_rand32(void);
24
+
25
+ /** returns 64 random bits. */
26
+ uint64_t bscrypt_rand64(void);
27
+
28
+ /** returns 128 random bits. */
29
+ bits128_u bscrypt_rand128(void);
30
+
31
+ /** returns 256 random bits. */
32
+ bits256_u bscrypt_rand256(void);
33
+
34
+ /** returns a variable length string of random bytes. */
35
+ void bscrypt_rand_bytes(void *target, size_t length);
36
+
37
+ #if defined(DEBUG) && DEBUG == 1
38
+ void bscrypt_test_random(void);
39
+ #endif
40
+
41
+ /* *****************************************************************************
42
+ C++ extern finish
43
+ */
44
+ #if defined(__cplusplus)
45
+ }
46
+ #endif
47
+
48
+ #endif
@@ -0,0 +1,127 @@
1
+ #include "rb-call.h"
2
+ #include <ruby.h>
3
+ #include <ruby/thread.h>
4
+ #include <pthread.h>
5
+
6
+ ///////////////
7
+ // this is a simple helper that calls Ruby methods on Ruby objects while within
8
+ // a non-GVL ruby thread zone.
9
+
10
+ // a structure for Ruby API calls
11
+ struct RubySimpleCall {
12
+ VALUE obj;
13
+ VALUE returned;
14
+ ID method;
15
+ };
16
+ struct RubyArgCall {
17
+ VALUE obj;
18
+ int argc;
19
+ VALUE *argv;
20
+ VALUE returned;
21
+ ID method;
22
+ };
23
+
24
+ #if __STDC_VERSION__ < 201112L || __STDC_NO_THREADS__
25
+ #define _Thread_local __thread
26
+ #endif
27
+
28
+ // a thread specific global variable that lets us know if we're in the GVL
29
+ static _Thread_local char in_gvl = 0;
30
+ static char check_in_gvl(void) { return in_gvl; }
31
+
32
+ ////////////////////////////////////////////////////////////////////////////
33
+ // Calling C functions.
34
+ static void *call_c(void *(*func)(void *), void *arg) {
35
+ if (in_gvl) {
36
+ return func(arg);
37
+ }
38
+ void *ret;
39
+ in_gvl = 1;
40
+ ret = rb_thread_call_with_gvl(func, arg);
41
+ in_gvl = 0;
42
+ return ret;
43
+ }
44
+
45
+ ////////////////////////////////////////////////////////////////////////////
46
+ // Handling exceptions (printing the backtrace doesn't really work well).
47
+ static void *handle_exception(void *_) {
48
+ VALUE exc = rb_errinfo();
49
+ if (exc != Qnil) {
50
+ VALUE msg = RubyCaller.call(exc, rb_intern("message"));
51
+ VALUE exc_class = rb_class_name(CLASS_OF(exc));
52
+ VALUE bt = RubyCaller.call(exc, rb_intern("backtrace"));
53
+ if (TYPE(bt) == T_ARRAY) {
54
+ bt = rb_ary_join(bt, rb_str_new_literal("\n"));
55
+ fprintf(stderr, "Iodine caught an unprotected exception - %.*s: %.*s\n%s",
56
+ (int)RSTRING_LEN(exc_class), RSTRING_PTR(exc_class),
57
+ (int)RSTRING_LEN(msg), RSTRING_PTR(msg), StringValueCStr(bt));
58
+ } else {
59
+ fprintf(stderr, "Iodine caught an unprotected exception - %.*s: %.*s\n"
60
+ "No backtrace available.\n",
61
+ (int)RSTRING_LEN(exc_class), RSTRING_PTR(exc_class),
62
+ (int)RSTRING_LEN(msg), RSTRING_PTR(msg));
63
+ }
64
+ rb_backtrace();
65
+ rb_set_errinfo(Qnil);
66
+ }
67
+ return (void *)Qnil;
68
+ }
69
+
70
+ ////////////////////////////////////////////////////////////////////////////
71
+ // A simple (and a bit lighter) design for when there's no need for arguments.
72
+
73
+ // running the actual method call
74
+ static VALUE run_ruby_method_unsafe(VALUE _tsk) {
75
+ struct RubySimpleCall *task = (void *)_tsk;
76
+ return rb_funcall2(task->obj, task->method, 0, NULL);
77
+ }
78
+
79
+ // GVL gateway
80
+ static void *run_ruby_method_within_gvl(void *_tsk) {
81
+ struct RubySimpleCall *task = _tsk;
82
+ int state = 0;
83
+ task->returned = rb_protect(run_ruby_method_unsafe, (VALUE)(task), &state);
84
+ if (state)
85
+ handle_exception(NULL);
86
+ return task;
87
+ }
88
+
89
+ // wrapping any API calls for exception management AND GVL entry
90
+ static VALUE call(VALUE obj, ID method) {
91
+ struct RubySimpleCall task = {.obj = obj, .method = method};
92
+ call_c(run_ruby_method_within_gvl, &task);
93
+ return task.returned;
94
+ }
95
+
96
+ ////////////////////////////////////////////////////////////////////////////
97
+ // A heavier (memory) design for when we're passing arguments around.
98
+
99
+ // running the actual method call
100
+ static VALUE run_argv_method_unsafe(VALUE _tsk) {
101
+ struct RubyArgCall *task = (void *)_tsk;
102
+ return rb_funcall2(task->obj, task->method, task->argc, task->argv);
103
+ }
104
+
105
+ // GVL gateway
106
+ static void *run_argv_method_within_gvl(void *_tsk) {
107
+ struct RubyArgCall *task = _tsk;
108
+ int state = 0;
109
+ task->returned = rb_protect(run_argv_method_unsafe, (VALUE)(task), &state);
110
+ if (state)
111
+ handle_exception(NULL);
112
+ return task;
113
+ }
114
+
115
+ // wrapping any API calls for exception management AND GVL entry
116
+ static VALUE call_arg(VALUE obj, ID method, int argc, VALUE *argv) {
117
+ struct RubyArgCall task = {
118
+ .obj = obj, .method = method, .argc = argc, .argv = argv};
119
+ call_c(run_argv_method_within_gvl, &task);
120
+ return task.returned;
121
+ }
122
+
123
+ ////////////////////////////////////////////////////////////////////////////
124
+ // the API interface
125
+ struct _Ruby_Method_Caller_Class_ RubyCaller = {
126
+ .call = call, .call2 = call_arg, .call_c = call_c, .in_gvl = check_in_gvl,
127
+ };
@@ -0,0 +1,60 @@
1
+ /*
2
+ copyright: Boaz segev, 2016
3
+ license: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef RB_CALL_H
8
+ #define RB_CALL_H
9
+ #include <ruby.h>
10
+
11
+ #define RB_CALL_VERSION "0.2.0"
12
+
13
+ /**
14
+ The Ruby framework manages it's own contextc switching and memory...
15
+ this means that when we make calls to Ruby (i.e. creating Ruby objects),
16
+ we risk currupting the Ruby framework. Also, Ruby uses all these signals (for
17
+ it's context switchings) and long-jumps (i.e. Ruby exceptions) that can drive
18
+ the C code a little nuts...
19
+
20
+ Seperation is reqiured :-)
21
+
22
+ This is a simple helper that calls Ruby methods on Ruby objects and C
23
+ functions that require acess to the Ruby API, while managing the GVL lock
24
+ status as required.
25
+
26
+ To call a Ruby object's method, simply use:
27
+
28
+ RubyCaller.call(object, method_id);
29
+
30
+ To pass arguments to the ruby object's method, use the `call2` method. i.e.:
31
+
32
+ RubyCaller.call2(object, method_id, argc, argv);
33
+
34
+
35
+ This library keeps track of the thread, adjusting the method to be called in
36
+ case the thread is already within the GVL.
37
+
38
+ The library assums that it is within a Ruby thread that is was released of
39
+ the GVL using `rb_thread_call_without_gvl2` or `rb_thread_call_without_gvl`.
40
+ */
41
+ extern struct _Ruby_Method_Caller_Class_ {
42
+ /** calls a Object's ruby method, adjusting for the GVL if needed */
43
+ VALUE (*call)(VALUE object, ID method_id);
44
+ /**
45
+ calls a Object's ruby method with arguments, adjusting for the GVL if needed
46
+ */
47
+ VALUE (*call2)(VALUE obj, ID method, int argc, VALUE *argv);
48
+ /**
49
+ calls a C method that requires the GVL for access to the Ruby API, managing the
50
+ GVL state as required.
51
+ */
52
+ void *(*call_c)(void *(*func)(void *), void *arg);
53
+ /**
54
+ returns the thread's GVL status (1 if inside a GVL and 0 if free from the
55
+ GVL).
56
+ */
57
+ char (*in_gvl)(void);
58
+ } RubyCaller;
59
+
60
+ #endif /* RB_CALL_H */
@@ -0,0 +1,79 @@
1
+ /*
2
+ copyright: Boaz segev, 2016
3
+ license: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+ #ifndef RB_ASYNC_EXT_H
8
+ #define RB_ASYNC_EXT_H
9
+ #include <ruby.h>
10
+ #include <ruby/thread.h>
11
+ #include "rb-registry.h"
12
+
13
+ /******************************************************************************
14
+ Portability - used to help port this to different frameworks (i.e. Ruby).
15
+ */
16
+
17
+ #define THREAD_TYPE VALUE
18
+
19
+ /* Don't use sentinals with Ruby */
20
+ #ifndef ASYNC_USE_SENTINEL
21
+ #define ASYNC_USE_SENTINEL 0
22
+ #endif
23
+
24
+ /* The unused directive */
25
+ #ifndef __unused
26
+ #define __unused __attribute__((unused))
27
+ #endif
28
+
29
+ /* used here but declared elsewhere */
30
+ void async_signal();
31
+
32
+ /* used here but declared elsewhere */
33
+ void call_async_signal(void *_) { async_signal(); }
34
+
35
+ /* protect the call to join from any exceptions */
36
+ static void *_inner_join_with_rbthread(void *rbt) {
37
+ return (void *)rb_funcall((VALUE)rbt, rb_intern("join"), 0);
38
+ }
39
+
40
+ /* join a ruby thread */
41
+ __unused static void *join_thread(THREAD_TYPE thr) {
42
+ void *ret = rb_thread_call_with_gvl(_inner_join_with_rbthread, (void *)thr);
43
+ Registry.remove(thr);
44
+ return ret;
45
+ }
46
+ /* used to create Ruby threads and pass them the information they need */
47
+ struct CreateThreadArgs {
48
+ void *(*thread_func)(void *);
49
+ void *arg;
50
+ };
51
+
52
+ /* the thread's GVL release */
53
+ static VALUE thread_loop(void *_args) {
54
+ struct CreateThreadArgs *args = _args;
55
+ void *(*thread_func)(void *) = args->thread_func;
56
+ void *arg = args->arg;
57
+ free(_args);
58
+ rb_thread_call_without_gvl2(thread_func, arg,
59
+ (void (*)(void *))call_async_signal, arg);
60
+ return Qnil;
61
+ }
62
+
63
+ /* Within the GVL, creates a Ruby thread using an API call */
64
+ static void *create_ruby_thread_gvl(void *_args) {
65
+ return (void *)Registry.add(rb_thread_create(thread_loop, _args));
66
+ }
67
+
68
+ /* create a ruby thread */
69
+ __unused static int create_thread(THREAD_TYPE *thr,
70
+ void *(*thread_func)(void *), void *arg) {
71
+ struct CreateThreadArgs *data = malloc(sizeof(*data));
72
+ if (!data)
73
+ return -1;
74
+ *data = (struct CreateThreadArgs){.thread_func = thread_func, .arg = arg};
75
+ *thr = (VALUE)rb_thread_call_with_gvl(create_ruby_thread_gvl, data);
76
+ return *thr == Qnil;
77
+ }
78
+
79
+ #endif
@@ -0,0 +1,389 @@
1
+ #include "rb-rack-io.h"
2
+ #include "rb-call.h"
3
+ #include "iodine_core.h"
4
+ #include <ruby/encoding.h>
5
+ #include <ruby/io.h>
6
+ #include <ruby/io.h>
7
+
8
+ /* RackIO manages a minimal interface to act as an IO wrapper according to
9
+ these Rack specifications:
10
+
11
+ The input stream is an IO-like object which contains the raw HTTP POST data.
12
+ When applicable, its external encoding must be “ASCII-8BIT” and it must be
13
+ opened in binary mode, for Ruby 1.9 compatibility. The input stream must respond
14
+ to gets, each, read and rewind.
15
+
16
+ gets must be called without arguments and return a string, or nil on EOF.
17
+
18
+ read behaves like IO#read. Its signature is read([length, [buffer]]). If given,
19
+ length must be a non-negative Integer (>= 0) or nil, and buffer must be a String
20
+ and may not be nil. If length is given and not nil, then this method reads at
21
+ most length bytes from the input stream. If length is not given or nil, then
22
+ this method reads all data until EOF. When EOF is reached, this method returns
23
+ nil if length is given and not nil, or “” if length is not given or is nil. If
24
+ buffer is given, then the read data will be placed into buffer instead of a
25
+ newly created String object.
26
+
27
+ each must be called without arguments and only yield Strings.
28
+
29
+ rewind must be called without arguments. It rewinds the input stream back to the
30
+ beginning. It must not raise Errno::ESPIPE: that is, it may not be a pipe or a
31
+ socket. Therefore, handler developers must buffer the input data into some
32
+ rewindable object if the underlying input stream is not rewindable.
33
+
34
+ close must never be called on the input stream.
35
+
36
+ */
37
+
38
+ /* *****************************************************************************
39
+ Core data / helpers
40
+ */
41
+
42
+ static VALUE rRackStrIO;
43
+ static VALUE rRackFileIO;
44
+
45
+ static ID pos_id;
46
+ static ID end_id;
47
+ static ID env_id;
48
+ static ID io_id;
49
+
50
+ static VALUE TCPSOCKET_CLASS;
51
+ static ID for_fd_id;
52
+
53
+ #define set_uuid(object, request) \
54
+ rb_ivar_set((object), fd_var_id, ULONG2NUM((request)->metadata.fd))
55
+
56
+ inline static intptr_t get_uuid(VALUE obj) {
57
+ VALUE i = rb_ivar_get(obj, fd_var_id);
58
+ return (intptr_t)FIX2ULONG(i);
59
+ }
60
+
61
+ #define set_pos(object, pos) rb_ivar_set((object), pos_id, ULONG2NUM(pos))
62
+
63
+ inline static size_t get_pos(VALUE obj) {
64
+ VALUE i = rb_ivar_get(obj, pos_id);
65
+ return (size_t)FIX2ULONG(i);
66
+ }
67
+
68
+ inline static size_t get_end(VALUE obj) {
69
+ VALUE i = rb_ivar_get(obj, end_id);
70
+ return (size_t)FIX2ULONG(i);
71
+ }
72
+
73
+ /* *****************************************************************************
74
+ StrIO API
75
+ */
76
+
77
+ // a macro helper to get the server pointer embeded in an object
78
+ inline static char *get_str(VALUE obj) {
79
+ VALUE i = rb_ivar_get(obj, io_id);
80
+ return (char *)FIX2ULONG(i);
81
+ }
82
+
83
+ /**
84
+ Gets returns a line. this is okay for small lines,
85
+ but shouldn't really be used.
86
+
87
+ Limited to ~ 1Mb of a line length.
88
+ */
89
+ static VALUE strio_gets(VALUE self) {
90
+ char *str = get_str(self);
91
+ size_t pos = get_pos(self);
92
+ size_t end = get_end(self);
93
+ if (str == NULL || pos == end)
94
+ return Qnil;
95
+ size_t pos_e = pos;
96
+
97
+ while ((pos_e < end) && str[pos_e] != '\n')
98
+ pos_e++;
99
+ set_pos(self, pos_e + 1);
100
+ return rb_enc_str_new(str + pos, pos_e - pos, BinaryEncoding);
101
+ }
102
+
103
+ // Reads data from the IO, according to the Rack specifications for `#read`.
104
+ static VALUE strio_read(int argc, VALUE *argv, VALUE self) {
105
+ char *str = get_str(self);
106
+ size_t pos = get_pos(self);
107
+ size_t end = get_end(self);
108
+ VALUE buffer = Qnil;
109
+ char ret_nil = 0;
110
+ ssize_t len = 0;
111
+ // get the buffer object if given
112
+ if (argc == 2) {
113
+ Check_Type(argv[1], T_STRING);
114
+ buffer = argv[1];
115
+ }
116
+ // get the length object, if given
117
+ if (argc > 0 && argv[0] != Qnil) {
118
+ Check_Type(argv[0], T_FIXNUM);
119
+ len = FIX2LONG(argv[0]);
120
+ if (len < 0)
121
+ rb_raise(rb_eRangeError, "length should be bigger then 0.");
122
+ ret_nil = 1;
123
+ }
124
+ // return if we're at the EOF.
125
+ if (str == NULL)
126
+ goto no_data;
127
+ // calculate length if it wasn't specified.
128
+ if (len == 0) {
129
+ // make sure we're not reading more then we have (string buffer)
130
+ len = end - pos;
131
+ // set position for future reads
132
+ set_pos(self, end);
133
+ if (len == 0)
134
+ goto no_data;
135
+ } else {
136
+ // set position for future reads
137
+ set_pos(self, pos + len);
138
+ }
139
+ if (len + pos > end)
140
+ len = end - pos;
141
+ // create the buffer if we don't have one.
142
+ if (buffer == Qnil) {
143
+ buffer = rb_str_buf_new(len);
144
+ // make sure the buffer is binary encoded.
145
+ rb_enc_associate(buffer, BinaryEncoding);
146
+ } else {
147
+ // make sure the buffer is binary encoded.
148
+ rb_enc_associate(buffer, BinaryEncoding);
149
+ if (rb_str_capacity(buffer) < len)
150
+ rb_str_resize(buffer, len);
151
+ }
152
+ // read the data.
153
+ memcpy(RSTRING_PTR(buffer), str + pos, len);
154
+ rb_str_set_len(buffer, len);
155
+ return buffer;
156
+ no_data:
157
+ if (ret_nil)
158
+ return Qnil;
159
+ else
160
+ return rb_str_buf_new(0);
161
+ }
162
+
163
+ // Does nothing - this is controlled by the server.
164
+ static VALUE strio_close(VALUE self) { return Qnil; }
165
+
166
+ // Rewinds the IO, so that it is read from the begining.
167
+ static VALUE rio_rewind(VALUE self) {
168
+ set_pos(self, 0);
169
+ return self;
170
+ }
171
+
172
+ // Passes each line of the input to the block. This should be avoided.
173
+ static VALUE strio_each(VALUE self) {
174
+ rb_need_block();
175
+ rio_rewind(self);
176
+ VALUE str = Qnil;
177
+ while ((str = strio_gets(self)) != Qnil) {
178
+ rb_yield(str);
179
+ }
180
+ return self;
181
+ }
182
+
183
+ /* *****************************************************************************
184
+ TempFileIO API
185
+ */
186
+
187
+ // a macro helper to get the server pointer embeded in an object
188
+ inline static int get_tmpfile(VALUE obj) {
189
+ VALUE i = rb_ivar_get(obj, io_id);
190
+ return (int)FIX2INT(i);
191
+ }
192
+
193
+ /**
194
+ Gets returns a line. this is okay for small lines,
195
+ but shouldn't really be used.
196
+
197
+ Limited to ~ 1Mb of a line length.
198
+ */
199
+ static VALUE tfio_gets(VALUE self) {
200
+ int fd = get_tmpfile(self);
201
+ size_t pos = get_pos(self);
202
+ size_t end = get_end(self);
203
+ if (pos == end)
204
+ return Qnil;
205
+ size_t pos_e = pos;
206
+ char c;
207
+ int ret;
208
+ VALUE buffer;
209
+
210
+ do {
211
+ ret = pread(fd, &c, 1, pos_e);
212
+ } while (ret > 0 && c != '\n' && (++pos_e < end));
213
+ set_pos(self, pos_e + 1);
214
+ if (pos > pos_e) {
215
+ buffer = rb_str_buf_new(pos_e - pos);
216
+ // make sure the buffer is binary encoded.
217
+ rb_enc_associate(buffer, BinaryEncoding);
218
+ if (pread(fd, RSTRING_PTR(buffer), pos_e - pos, pos) < 0)
219
+ return Qnil;
220
+ rb_str_set_len(buffer, pos_e - pos);
221
+ return buffer;
222
+ }
223
+ return Qnil;
224
+ }
225
+
226
+ // Reads data from the IO, according to the Rack specifications for `#read`.
227
+ static VALUE tfio_read(int argc, VALUE *argv, VALUE self) {
228
+ int fd = get_tmpfile(self);
229
+ size_t pos = get_pos(self);
230
+ size_t end = get_end(self);
231
+ VALUE buffer = Qnil;
232
+ char ret_nil = 0;
233
+ ssize_t len = 0;
234
+ // get the buffer object if given
235
+ if (argc == 2) {
236
+ Check_Type(argv[1], T_STRING);
237
+ buffer = argv[1];
238
+ }
239
+ // get the length object, if given
240
+ if (argc > 0 && argv[0] != Qnil) {
241
+ Check_Type(argv[0], T_FIXNUM);
242
+ len = FIX2LONG(argv[0]);
243
+ if (len < 0)
244
+ rb_raise(rb_eRangeError, "length should be bigger then 0.");
245
+ ret_nil = 1;
246
+ }
247
+ // return if we're at the EOF.
248
+ if (pos == end)
249
+ goto no_data;
250
+ // calculate length if it wasn't specified.
251
+ if (len == 0) {
252
+ // make sure we're not reading more then we have
253
+ len = end - pos;
254
+ // set position for future reads
255
+ set_pos(self, end);
256
+ if (len == 0)
257
+ goto no_data;
258
+ } else {
259
+ // set position for future reads
260
+ set_pos(self, pos + len);
261
+ }
262
+ // limit read to what we have
263
+ if (len + pos > end)
264
+ len = end - pos;
265
+ // create the buffer if we don't have one.
266
+ if (buffer == Qnil) {
267
+ buffer = rb_str_buf_new(len);
268
+ // make sure the buffer is binary encoded.
269
+ rb_enc_associate(buffer, BinaryEncoding);
270
+ } else {
271
+ // make sure the buffer is binary encoded.
272
+ rb_enc_associate(buffer, BinaryEncoding);
273
+ if (rb_str_capacity(buffer) < len)
274
+ rb_str_resize(buffer, len);
275
+ }
276
+ // read the data.
277
+ if (pread(fd, RSTRING_PTR(buffer), len, pos) <= 0)
278
+ goto no_data;
279
+ rb_str_set_len(buffer, len);
280
+ return buffer;
281
+ no_data:
282
+ if (ret_nil)
283
+ return Qnil;
284
+ else
285
+ return rb_str_buf_new(0);
286
+ }
287
+
288
+ // Does nothing - this is controlled by the server.
289
+ static VALUE tfio_close(VALUE self) { return Qnil; }
290
+
291
+ // Passes each line of the input to the block. This should be avoided.
292
+ static VALUE tfio_each(VALUE self) {
293
+ rb_need_block();
294
+ rio_rewind(self);
295
+ VALUE str = Qnil;
296
+ while ((str = tfio_gets(self)) != Qnil) {
297
+ rb_yield(str);
298
+ }
299
+ return self;
300
+ }
301
+
302
+ /* *****************************************************************************
303
+ Hijacking
304
+ */
305
+
306
+ // defined by iodine_http
307
+ extern VALUE R_HIJACK; // for Rack: rack.hijack
308
+ extern VALUE R_HIJACK_CB; // for Rack: rack.hijack
309
+ extern VALUE R_HIJACK_IO; // for Rack: rack.hijack_io
310
+
311
+ static VALUE rio_get_io(int argc, VALUE *argv, VALUE self) {
312
+ if (TCPSOCKET_CLASS == Qnil)
313
+ return Qfalse;
314
+ intptr_t fduuid = get_uuid(self);
315
+ // hijack the IO object
316
+ VALUE fd = INT2FIX(sock_uuid2fd(fduuid));
317
+ VALUE env = rb_ivar_get(self, env_id);
318
+ // make sure we're not repeating ourselves
319
+ VALUE new_io = rb_hash_aref(env, R_HIJACK_IO);
320
+ if (new_io != Qnil)
321
+ return new_io;
322
+ // VALUE new_io = how the fuck do we create a new IO from the fd?
323
+ new_io = RubyCaller.call2(TCPSOCKET_CLASS, for_fd_id, 1,
324
+ &fd); // TCPSocket.for_fd(fd) ... cool...
325
+ rb_hash_aset(env, R_HIJACK_IO, new_io);
326
+ if (argc)
327
+ rb_hash_aset(env, R_HIJACK_CB, *argv);
328
+ return new_io;
329
+ }
330
+
331
+ /* *****************************************************************************
332
+ C land API
333
+ */
334
+
335
+ // new object
336
+ static VALUE new_rack_io(http_request_s *request, VALUE env) {
337
+ VALUE rack_io;
338
+ if (request->body_file > 0) {
339
+ rack_io = rb_funcall2(rRackFileIO, new_func_id, 0, NULL);
340
+ rb_ivar_set(rack_io, io_id, ULONG2NUM(request->body_file));
341
+ lseek(request->body_file, 0, SEEK_SET);
342
+ } else {
343
+ rack_io = rb_funcall2(rRackStrIO, new_func_id, 0, NULL);
344
+ rb_ivar_set(rack_io, io_id, ULONG2NUM(((intptr_t)request->body_str)));
345
+ // fprintf(stderr, "rack body IO (%lu, %p):%.*s\n", request->content_length,
346
+ // request->body_str, (int)request->content_length,
347
+ // request->body_str);
348
+ }
349
+ set_uuid(rack_io, request);
350
+ set_pos(rack_io, 0);
351
+ rb_ivar_set(rack_io, end_id, ULONG2NUM(request->content_length));
352
+ rb_ivar_set(rack_io, env_id, env);
353
+
354
+ return rack_io;
355
+ }
356
+
357
+ // initialize library
358
+ static void init_rack_io(void) {
359
+ rRackStrIO = rb_define_class_under(IodineBase, "RackStrIO", rb_cObject);
360
+ rRackFileIO = rb_define_class_under(IodineBase, "RackTmpFileIO", rb_cObject);
361
+
362
+ pos_id = rb_intern("pos");
363
+ end_id = rb_intern("io_end");
364
+ io_id = rb_intern("rack_io");
365
+ env_id = rb_intern("env");
366
+ for_fd_id = rb_intern("for_fd");
367
+
368
+ TCPSOCKET_CLASS = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
369
+ // IO methods
370
+ rb_define_method(rRackStrIO, "rewind", rio_rewind, 0);
371
+ rb_define_method(rRackStrIO, "gets", strio_gets, 0);
372
+ rb_define_method(rRackStrIO, "read", strio_read, -1);
373
+ rb_define_method(rRackStrIO, "close", strio_close, 0);
374
+ rb_define_method(rRackStrIO, "each", strio_each, 0);
375
+ rb_define_method(rRackStrIO, "_hijack", rio_get_io, -1);
376
+
377
+ rb_define_method(rRackFileIO, "rewind", rio_rewind, 0);
378
+ rb_define_method(rRackFileIO, "gets", tfio_gets, 0);
379
+ rb_define_method(rRackFileIO, "read", tfio_read, -1);
380
+ rb_define_method(rRackFileIO, "close", tfio_close, 0);
381
+ rb_define_method(rRackFileIO, "each", tfio_each, 0);
382
+ rb_define_method(rRackFileIO, "_hijack", rio_get_io, -1);
383
+ }
384
+
385
+ ////////////////////////////////////////////////////////////////////////////
386
+ // the API interface
387
+ struct _RackIO_ RackIO = {
388
+ .new = new_rack_io, .init = init_rack_io,
389
+ };