opal-up 0.0.2 → 0.0.3

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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +209 -0
  3. data/README.md +81 -28
  4. data/bin/up_ruby +4 -0
  5. data/bin/up_ruby_cluster +4 -0
  6. data/ext/up_ext/App.h +606 -0
  7. data/ext/up_ext/AsyncSocket.h +355 -0
  8. data/ext/up_ext/AsyncSocketData.h +87 -0
  9. data/ext/up_ext/BloomFilter.h +83 -0
  10. data/ext/up_ext/ChunkedEncoding.h +236 -0
  11. data/ext/up_ext/ClientApp.h +36 -0
  12. data/ext/up_ext/HttpContext.h +502 -0
  13. data/ext/up_ext/HttpContextData.h +56 -0
  14. data/ext/up_ext/HttpErrors.h +53 -0
  15. data/ext/up_ext/HttpParser.h +680 -0
  16. data/ext/up_ext/HttpResponse.h +578 -0
  17. data/ext/up_ext/HttpResponseData.h +95 -0
  18. data/ext/up_ext/HttpRouter.h +380 -0
  19. data/ext/up_ext/Loop.h +204 -0
  20. data/ext/up_ext/LoopData.h +112 -0
  21. data/ext/up_ext/MoveOnlyFunction.h +377 -0
  22. data/ext/up_ext/PerMessageDeflate.h +315 -0
  23. data/ext/up_ext/ProxyParser.h +163 -0
  24. data/ext/up_ext/QueryParser.h +120 -0
  25. data/ext/up_ext/TopicTree.h +363 -0
  26. data/ext/up_ext/Utilities.h +66 -0
  27. data/ext/up_ext/WebSocket.h +381 -0
  28. data/ext/up_ext/WebSocketContext.h +434 -0
  29. data/ext/up_ext/WebSocketContextData.h +109 -0
  30. data/ext/up_ext/WebSocketData.h +86 -0
  31. data/ext/up_ext/WebSocketExtensions.h +256 -0
  32. data/ext/up_ext/WebSocketHandshake.h +145 -0
  33. data/ext/up_ext/WebSocketProtocol.h +506 -0
  34. data/ext/up_ext/bsd.c +767 -0
  35. data/ext/up_ext/bsd.h +109 -0
  36. data/ext/up_ext/context.c +524 -0
  37. data/ext/up_ext/epoll_kqueue.c +458 -0
  38. data/ext/up_ext/epoll_kqueue.h +67 -0
  39. data/ext/up_ext/extconf.rb +5 -0
  40. data/ext/up_ext/internal.h +224 -0
  41. data/ext/up_ext/libusockets.h +350 -0
  42. data/ext/up_ext/libuwebsockets.cpp +1374 -0
  43. data/ext/up_ext/libuwebsockets.h +260 -0
  44. data/ext/up_ext/loop.c +386 -0
  45. data/ext/up_ext/loop_data.h +38 -0
  46. data/ext/up_ext/socket.c +231 -0
  47. data/ext/up_ext/up_ext.c +278 -0
  48. data/lib/up/node/rack_env.rb +2 -2
  49. data/lib/up/ruby/cluster_cli.rb +10 -0
  50. data/lib/up/ruby/rack_cluster.rb +26 -0
  51. data/lib/up/ruby/rack_env.rb +97 -0
  52. data/lib/up/ruby/rack_server.rb +26 -0
  53. data/lib/up/ruby/server_cli.rb +10 -0
  54. data/lib/up/u_web_socket/rack_env.rb +1 -1
  55. data/lib/up/version.rb +1 -1
  56. metadata +71 -18
  57. data/.gitignore +0 -5
  58. data/Gemfile +0 -2
  59. data/example_rack_app/Gemfile +0 -3
  60. data/example_rack_app/config.ru +0 -6
  61. data/example_rack_app/rack_app.rb +0 -5
  62. data/example_roda_app/Gemfile +0 -6
  63. data/example_roda_app/config.ru +0 -6
  64. data/example_roda_app/roda_app.rb +0 -37
  65. data/example_sinatra_app/Gemfile +0 -6
  66. data/example_sinatra_app/config.ru +0 -6
  67. data/example_sinatra_app/sinatra_app.rb +0 -7
  68. data/opal-up.gemspec +0 -27
  69. data/up_logo.svg +0 -256
@@ -0,0 +1,38 @@
1
+ /*
2
+ * Authored by Alex Hultman, 2018-2019.
3
+ * Intellectual property of third-party.
4
+
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #ifndef LOOP_DATA_H
19
+ #define LOOP_DATA_H
20
+
21
+ struct us_internal_loop_data_t {
22
+ struct us_timer_t *sweep_timer;
23
+ struct us_internal_async *wakeup_async;
24
+ int last_write_failed;
25
+ struct us_socket_context_t *head;
26
+ struct us_socket_context_t *iterator;
27
+ char *recv_buf;
28
+ void *ssl_data;
29
+ void (*pre_cb)(struct us_loop_t *);
30
+ void (*post_cb)(struct us_loop_t *);
31
+ struct us_socket_t *closed_head;
32
+ struct us_socket_t *low_prio_head;
33
+ int low_prio_budget;
34
+ /* We do not care if this flips or not, it doesn't matter */
35
+ long long iteration_nr;
36
+ };
37
+
38
+ #endif // LOOP_DATA_H
@@ -0,0 +1,231 @@
1
+ /*
2
+ * Authored by Alex Hultman, 2018-2021.
3
+ * Intellectual property of third-party.
4
+
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #ifndef LIBUS_USE_IO_URING
19
+
20
+ #include "libusockets.h"
21
+ #include "internal.h"
22
+ #include <stdlib.h>
23
+ #include <string.h>
24
+ #include <stdint.h>
25
+
26
+ /* Shared with SSL */
27
+
28
+ int us_socket_local_port(int ssl, struct us_socket_t *s) {
29
+ struct bsd_addr_t addr;
30
+ if (bsd_local_addr(us_poll_fd(&s->p), &addr)) {
31
+ return -1;
32
+ } else {
33
+ return bsd_addr_get_port(&addr);
34
+ }
35
+ }
36
+
37
+ int us_socket_remote_port(int ssl, struct us_socket_t *s) {
38
+ struct bsd_addr_t addr;
39
+ if (bsd_remote_addr(us_poll_fd(&s->p), &addr)) {
40
+ return -1;
41
+ } else {
42
+ return bsd_addr_get_port(&addr);
43
+ }
44
+ }
45
+
46
+ void us_socket_shutdown_read(int ssl, struct us_socket_t *s) {
47
+ /* This syscall is idempotent so no extra check is needed */
48
+ bsd_shutdown_socket_read(us_poll_fd((struct us_poll_t *) s));
49
+ }
50
+
51
+ void us_socket_remote_address(int ssl, struct us_socket_t *s, char *buf, int *length) {
52
+ struct bsd_addr_t addr;
53
+ if (bsd_remote_addr(us_poll_fd(&s->p), &addr) || *length < bsd_addr_get_ip_length(&addr)) {
54
+ *length = 0;
55
+ } else {
56
+ *length = bsd_addr_get_ip_length(&addr);
57
+ memcpy(buf, bsd_addr_get_ip(&addr), *length);
58
+ }
59
+ }
60
+
61
+ struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s) {
62
+ return s->context;
63
+ }
64
+
65
+ void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds) {
66
+ if (seconds) {
67
+ s->timeout = ((unsigned int)s->context->timestamp + ((seconds + 3) >> 2)) % 240;
68
+ } else {
69
+ s->timeout = 255;
70
+ }
71
+ }
72
+
73
+ void us_socket_long_timeout(int ssl, struct us_socket_t *s, unsigned int minutes) {
74
+ if (minutes) {
75
+ s->long_timeout = ((unsigned int)s->context->long_timestamp + minutes) % 240;
76
+ } else {
77
+ s->long_timeout = 255;
78
+ }
79
+ }
80
+
81
+ void us_socket_flush(int ssl, struct us_socket_t *s) {
82
+ if (!us_socket_is_shut_down(0, s)) {
83
+ bsd_socket_flush(us_poll_fd((struct us_poll_t *) s));
84
+ }
85
+ }
86
+
87
+ int us_socket_is_closed(int ssl, struct us_socket_t *s) {
88
+ return s->prev == (struct us_socket_t *) s->context;
89
+ }
90
+
91
+ int us_socket_is_established(int ssl, struct us_socket_t *s) {
92
+ /* Everything that is not POLL_TYPE_SEMI_SOCKET is established */
93
+ return us_internal_poll_type((struct us_poll_t *) s) != POLL_TYPE_SEMI_SOCKET;
94
+ }
95
+
96
+ /* Exactly the same as us_socket_close but does not emit on_close event */
97
+ struct us_socket_t *us_socket_close_connecting(int ssl, struct us_socket_t *s) {
98
+ if (!us_socket_is_closed(0, s)) {
99
+ us_internal_socket_context_unlink_socket(s->context, s);
100
+ us_poll_stop((struct us_poll_t *) s, s->context->loop);
101
+ bsd_close_socket(us_poll_fd((struct us_poll_t *) s));
102
+
103
+ /* Link this socket to the close-list and let it be deleted after this iteration */
104
+ s->next = s->context->loop->data.closed_head;
105
+ s->context->loop->data.closed_head = s;
106
+
107
+ /* Any socket with prev = context is marked as closed */
108
+ s->prev = (struct us_socket_t *) s->context;
109
+
110
+ //return s->context->on_close(s, code, reason);
111
+ }
112
+ return s;
113
+ }
114
+
115
+ /* Same as above but emits on_close */
116
+ struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, void *reason) {
117
+ if (!us_socket_is_closed(0, s)) {
118
+ if (s->low_prio_state == 1) {
119
+ /* Unlink this socket from the low-priority queue */
120
+ if (!s->prev) s->context->loop->data.low_prio_head = s->next;
121
+ else s->prev->next = s->next;
122
+
123
+ if (s->next) s->next->prev = s->prev;
124
+
125
+ s->prev = 0;
126
+ s->next = 0;
127
+ s->low_prio_state = 0;
128
+ } else {
129
+ us_internal_socket_context_unlink_socket(s->context, s);
130
+ }
131
+ us_poll_stop((struct us_poll_t *) s, s->context->loop);
132
+ bsd_close_socket(us_poll_fd((struct us_poll_t *) s));
133
+
134
+ /* Link this socket to the close-list and let it be deleted after this iteration */
135
+ s->next = s->context->loop->data.closed_head;
136
+ s->context->loop->data.closed_head = s;
137
+
138
+ /* Any socket with prev = context is marked as closed */
139
+ s->prev = (struct us_socket_t *) s->context;
140
+
141
+ return s->context->on_close(s, code, reason);
142
+ }
143
+ return s;
144
+ }
145
+
146
+ /* Not shared with SSL */
147
+
148
+ void *us_socket_get_native_handle(int ssl, struct us_socket_t *s) {
149
+ #ifndef LIBUS_NO_SSL
150
+ if (ssl) {
151
+ return us_internal_ssl_socket_get_native_handle((struct us_internal_ssl_socket_t *) s);
152
+ }
153
+ #endif
154
+
155
+ return (void *) (uintptr_t) us_poll_fd((struct us_poll_t *) s);
156
+ }
157
+
158
+ /* This is not available for SSL sockets as it makes no sense. */
159
+ int us_socket_write2(int ssl, struct us_socket_t *s, const char *header, int header_length, const char *payload, int payload_length) {
160
+
161
+ if (us_socket_is_closed(ssl, s) || us_socket_is_shut_down(ssl, s)) {
162
+ return 0;
163
+ }
164
+
165
+ int written = bsd_write2(us_poll_fd(&s->p), header, header_length, payload, payload_length);
166
+ if (written != header_length + payload_length) {
167
+ us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
168
+ }
169
+
170
+ return written < 0 ? 0 : written;
171
+ }
172
+
173
+ int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more) {
174
+ #ifndef LIBUS_NO_SSL
175
+ if (ssl) {
176
+ return us_internal_ssl_socket_write((struct us_internal_ssl_socket_t *) s, data, length, msg_more);
177
+ }
178
+ #endif
179
+
180
+ if (us_socket_is_closed(ssl, s) || us_socket_is_shut_down(ssl, s)) {
181
+ return 0;
182
+ }
183
+
184
+ int written = bsd_send(us_poll_fd(&s->p), data, length, msg_more);
185
+ if (written != length) {
186
+ s->context->loop->data.last_write_failed = 1;
187
+ us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
188
+ }
189
+
190
+ return written < 0 ? 0 : written;
191
+ }
192
+
193
+ void *us_socket_ext(int ssl, struct us_socket_t *s) {
194
+ #ifndef LIBUS_NO_SSL
195
+ if (ssl) {
196
+ return us_internal_ssl_socket_ext((struct us_internal_ssl_socket_t *) s);
197
+ }
198
+ #endif
199
+
200
+ return s + 1;
201
+ }
202
+
203
+ int us_socket_is_shut_down(int ssl, struct us_socket_t *s) {
204
+ #ifndef LIBUS_NO_SSL
205
+ if (ssl) {
206
+ return us_internal_ssl_socket_is_shut_down((struct us_internal_ssl_socket_t *) s);
207
+ }
208
+ #endif
209
+
210
+ return us_internal_poll_type(&s->p) == POLL_TYPE_SOCKET_SHUT_DOWN;
211
+ }
212
+
213
+ void us_socket_shutdown(int ssl, struct us_socket_t *s) {
214
+ #ifndef LIBUS_NO_SSL
215
+ if (ssl) {
216
+ us_internal_ssl_socket_shutdown((struct us_internal_ssl_socket_t *) s);
217
+ return;
218
+ }
219
+ #endif
220
+
221
+ /* Todo: should we emit on_close if calling shutdown on an already half-closed socket?
222
+ * We need more states in that case, we need to track RECEIVED_FIN
223
+ * so far, the app has to track this and call close as needed */
224
+ if (!us_socket_is_closed(ssl, s) && !us_socket_is_shut_down(ssl, s)) {
225
+ us_internal_poll_set_type(&s->p, POLL_TYPE_SOCKET_SHUT_DOWN);
226
+ us_poll_change(&s->p, s->context->loop, us_poll_events(&s->p) & LIBUS_SOCKET_READABLE);
227
+ bsd_shutdown_socket(us_poll_fd((struct us_poll_t *) s));
228
+ }
229
+ }
230
+
231
+ #endif
@@ -0,0 +1,278 @@
1
+ #include <ruby.h>
2
+ #include <unistd.h>
3
+ #include "libusockets.h"
4
+ #include "libuwebsockets.h"
5
+
6
+ #define USE_SSL 0
7
+
8
+ static VALUE mUp;
9
+ static VALUE mRuby;
10
+ static VALUE cServer;
11
+ static VALUE cCluster;
12
+ static VALUE cRackEnv;
13
+ static VALUE cRequest;
14
+
15
+ static ID id_app;
16
+ static ID id_call;
17
+ static ID id_close;
18
+ static ID id_each;
19
+ static ID id_host;
20
+ static ID id_port;
21
+
22
+ const rb_data_type_t up_request_t = {.wrap_struct_name = "Up::Ruby::Request",
23
+ .function = {.dmark = NULL,
24
+ .dfree = NULL,
25
+ .dsize = NULL,
26
+ .dcompact = NULL,
27
+ .reserved = {0}},
28
+ .parent = NULL,
29
+ .data = NULL,
30
+ .flags = 0};
31
+
32
+ VALUE up_internal_handle_part(RB_BLOCK_CALL_FUNC_ARGLIST(rpart, res)) {
33
+ Check_Type(rpart, T_STRING);
34
+ uws_res_write(USE_SSL, (uws_res_t *)res, RSTRING_PTR(rpart), RSTRING_LEN(rpart));
35
+ return Qnil;
36
+ }
37
+
38
+ static void up_server_request_handler(uws_res_t *res, uws_req_t *req, void *rapp) {
39
+ VALUE rreq = TypedData_Wrap_Struct(cRequest, &up_request_t, req);
40
+ VALUE renv = rb_class_new_instance(1, (const VALUE *)&rreq, cRackEnv);
41
+ VALUE rres = rb_funcall((VALUE)rapp, id_call, 1, renv);
42
+ Check_Type(rres, T_ARRAY);
43
+ VALUE rparts = RARRAY_AREF(rres, 2);
44
+ VALUE rpart;
45
+ if (TYPE(rparts) == T_ARRAY) {
46
+ long i, l = RARRAY_LEN(rparts);
47
+ for (i = 0; i < l; i++) {
48
+ rpart = RARRAY_AREF(rparts, i);
49
+ Check_Type(rpart, T_STRING);
50
+ uws_res_write(USE_SSL, res, RSTRING_PTR(rpart), RSTRING_LEN(rpart));
51
+ }
52
+ } else if (rb_respond_to(rparts, id_each)) {
53
+ rb_block_call(rparts, id_each, 0, NULL, up_internal_handle_part, (VALUE)res);
54
+ } else if (rb_respond_to(rparts, id_call)) {
55
+ rpart = rb_funcall(rparts, id_call, 0);
56
+ Check_Type(rpart, T_STRING);
57
+ uws_res_write(USE_SSL, res, RSTRING_PTR(rpart), RSTRING_LEN(rpart));
58
+ }
59
+ if (rb_respond_to(rparts, id_close))
60
+ rb_funcall(rparts, id_close, 0);
61
+ FIX2INT(RARRAY_PTR(rres)[0]);
62
+ uws_res_end_without_body(USE_SSL, res, false);
63
+ }
64
+
65
+ static void up_server_listen_handler(struct us_listen_socket_t *listen_socket, uws_app_listen_config_t config, void *user_data) {
66
+ if (listen_socket)
67
+ fprintf(stderr, "Server is running on http://%s:%d\n", config.host, config.port);
68
+ }
69
+
70
+ static void up_server_t_free(void *p) {
71
+ if (p)
72
+ uws_app_destroy(USE_SSL, (uws_app_t *)p);
73
+ }
74
+
75
+ const rb_data_type_t up_server_t = {.wrap_struct_name = "Up::Ruby::Server",
76
+ .function = {.dmark = NULL,
77
+ .dfree = up_server_t_free,
78
+ .dsize = NULL,
79
+ .dcompact = NULL,
80
+ .reserved = {0}},
81
+ .parent = NULL,
82
+ .data = NULL,
83
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY};
84
+
85
+ static VALUE up_server_alloc(VALUE rclass) {
86
+ uws_app_t *app = NULL;
87
+ return TypedData_Wrap_Struct(rclass, &up_server_t, app);
88
+ }
89
+
90
+ static void up_internal_check_arg_types(VALUE rapp, VALUE *rhost, VALUE *rport) {
91
+ if (!rb_respond_to(rapp, id_call))
92
+ rb_raise(rb_eArgError, "app does not respond to #call");
93
+ if (*rhost == Qundef || *rhost == Qnil) {
94
+ *rhost = rb_str_new("localhost", 9);
95
+ }
96
+ Check_Type(*rhost, T_STRING);
97
+ if (*rport == Qundef || *rport == Qnil) {
98
+ *rport = INT2FIX(3000);
99
+ }
100
+ Check_Type(*rport, T_FIXNUM);
101
+ }
102
+
103
+ static VALUE up_server_init(int argc, VALUE *argv, VALUE self) {
104
+ if (!rb_keyword_given_p())
105
+ rb_raise(rb_eArgError, "no args given, must at least provide app:");
106
+ ID kwargs[] = {id_app, id_host, id_port};
107
+ VALUE rargs[3] = {Qnil, Qnil, Qnil};
108
+ VALUE options = Qnil;
109
+
110
+ rb_scan_args_kw(1, argc, argv, ":", &options);
111
+ rb_get_kwargs(options, kwargs, 1, 2, rargs);
112
+
113
+ VALUE rapp = rargs[0];
114
+ VALUE rhost = rargs[1];
115
+ VALUE rport = rargs[2];
116
+
117
+ up_internal_check_arg_types(rapp, &rhost, &rport);
118
+
119
+ rb_ivar_set(self, id_app, rapp);
120
+ rb_ivar_set(self, id_host, rhost);
121
+ rb_ivar_set(self, id_port, rport);
122
+ return self;
123
+ }
124
+
125
+ static VALUE up_server_listen(VALUE self) {
126
+ VALUE rapp = rb_ivar_get(self, id_app);
127
+ VALUE rhost = rb_ivar_get(self, id_host);
128
+ VALUE rport = rb_ivar_get(self, id_port);
129
+
130
+ up_internal_check_arg_types(rapp, &rhost, &rport);
131
+
132
+ struct us_socket_context_options_t options = {.key_file_name = NULL, .cert_file_name = NULL, .ca_file_name = NULL,
133
+ .passphrase = NULL, .dh_params_file_name = NULL, .ssl_ciphers = NULL};
134
+ DATA_PTR(self) = uws_create_app(USE_SSL, options);
135
+ uws_app_t *app = DATA_PTR(self);
136
+ if (!app)
137
+ rb_raise(rb_eRuntimeError, "could not init uws app");
138
+ uws_app_any(USE_SSL, app, "/*", up_server_request_handler, (void *)rapp);
139
+ uws_app_listen_config_t config = {.port=3000, .host="localhost", .options=0};
140
+ uws_app_listen_with_config(USE_SSL, app, config, up_server_listen_handler, NULL);
141
+ uws_app_run(USE_SSL, app);
142
+ return Qnil;
143
+ }
144
+
145
+ static VALUE up_server_stop(VALUE self) {
146
+ uws_app_t *app = DATA_PTR(self);
147
+ if (!app)
148
+ rb_raise(rb_eRuntimeError, "no uws, did initialize call super?");
149
+ uws_app_close(USE_SSL, app);
150
+ return Qnil;
151
+ }
152
+
153
+ static VALUE up_cluster_listen(VALUE self) {
154
+ VALUE rapp = rb_ivar_get(self, id_app);
155
+ VALUE rhost = rb_ivar_get(self, id_host);
156
+ VALUE rport = rb_ivar_get(self, id_port);
157
+
158
+ up_internal_check_arg_types(rapp, &rhost, &rport);
159
+ long i = sysconf(_SC_NPROCESSORS_ONLN);
160
+ pid_t pid;
161
+ for (; i > 1; i--) {
162
+ pid = fork();
163
+ if (pid > 0) {
164
+ // do nothing
165
+ } else if (pid == 0) {
166
+ struct us_socket_context_options_t options = {.key_file_name = NULL, .cert_file_name = NULL, .ca_file_name = NULL,
167
+ .passphrase = NULL, .dh_params_file_name = NULL, .ssl_ciphers = NULL};
168
+ DATA_PTR(self) = uws_create_app(USE_SSL, options);
169
+ uws_app_t *app = DATA_PTR(self);
170
+ if (!app)
171
+ rb_raise(rb_eRuntimeError, "could not init uws app");
172
+ uws_app_any(USE_SSL, app, "/*", up_server_request_handler, (void *)rapp);
173
+ uws_app_listen_config_t config = {.port=3000, .host="localhost", .options=0};
174
+ uws_app_listen_with_config(USE_SSL, app, config, up_server_listen_handler, NULL);
175
+ uws_app_run(USE_SSL, app);
176
+ }
177
+ }
178
+ if (pid > 0) {
179
+ struct us_socket_context_options_t options = {.key_file_name = NULL, .cert_file_name = NULL, .ca_file_name = NULL,
180
+ .passphrase = NULL, .dh_params_file_name = NULL, .ssl_ciphers = NULL};
181
+ DATA_PTR(self) = uws_create_app(USE_SSL, options);
182
+ uws_app_t *app = DATA_PTR(self);
183
+ if (!app)
184
+ rb_raise(rb_eRuntimeError, "could not init uws app");
185
+ uws_app_any(USE_SSL, app, "/*", up_server_request_handler, (void *)rapp);
186
+ uws_app_listen_config_t config = {.port=3000, .host="localhost", .options=0};
187
+ uws_app_listen_with_config(USE_SSL, app, config, up_server_listen_handler, NULL);
188
+ uws_app_run(USE_SSL, app);
189
+ }
190
+
191
+ return Qnil;
192
+ }
193
+
194
+ static VALUE up_cluster_stop(VALUE self) {
195
+ // uws_app_t *app = DATA_PTR(self);
196
+ // uws_app_close(USE_SSL, app);
197
+ return Qnil;
198
+ }
199
+
200
+ static VALUE up_request_alloc(VALUE rclass) {
201
+ return TypedData_Wrap_Struct(rclass, &up_request_t, (uws_req_t*)NULL);
202
+ }
203
+
204
+ static void up_internal_header_handler(const char *h, size_t h_len, const char *v, size_t v_len, void *rheaders) {
205
+ rb_hash_aset((VALUE)rheaders, rb_str_new(h, h_len), rb_str_new(v, v_len));
206
+ }
207
+
208
+ static VALUE up_request_headers(VALUE self) {
209
+ uws_req_t *req = DATA_PTR(self);
210
+ if (!req)
211
+ return Qnil;
212
+ VALUE rheaders = rb_hash_new();
213
+ uws_req_for_each_header(req, up_internal_header_handler, (void *)rheaders);
214
+ return rheaders;
215
+ }
216
+
217
+ static VALUE up_request_get_header(VALUE self, VALUE rheader) {
218
+ uws_req_t *req = DATA_PTR(self);
219
+ if (!req)
220
+ return Qnil;
221
+ Check_Type(rheader, T_STRING);
222
+ const char *header;
223
+ size_t len = uws_req_get_header(req, RSTRING_PTR(rheader), RSTRING_LEN(rheader), &header);
224
+ return rb_str_new(header, len);
225
+ }
226
+
227
+ static VALUE up_request_get_method(VALUE self) {
228
+ uws_req_t *req = DATA_PTR(self);
229
+ if (!req)
230
+ return Qnil;
231
+ const char *method;
232
+ size_t len = uws_req_get_method(req, &method);
233
+ return rb_str_new(method, len);
234
+ }
235
+
236
+ static VALUE up_request_get_query(VALUE self) {
237
+ return Qnil;
238
+ }
239
+
240
+ static VALUE up_request_get_url(VALUE self) {
241
+ uws_req_t *req = DATA_PTR(self);
242
+ if (!req)
243
+ return Qnil;
244
+ const char *url;
245
+ size_t len = uws_req_get_url(req, &url);
246
+ return rb_str_new(url, len);
247
+ }
248
+
249
+ void Init_up_ext(void) {
250
+ id_app = rb_intern("app");
251
+ id_call = rb_intern("call");
252
+ id_close = rb_intern("close");
253
+ id_each = rb_intern("each");
254
+ id_host = rb_intern("host");
255
+ id_port = rb_intern("port");
256
+
257
+ mUp = rb_define_module("Up");
258
+ mRuby = rb_define_module_under(mUp, "Ruby");
259
+
260
+ cServer = rb_define_class_under(mRuby, "Server", rb_cObject);
261
+ rb_define_alloc_func(cServer, up_server_alloc);
262
+ rb_define_method(cServer, "initialize", up_server_init, -1);
263
+ rb_define_method(cServer, "listen", up_server_listen, 0);
264
+ rb_define_method(cServer, "stop", up_server_stop, 0);
265
+
266
+ cCluster = rb_define_class_under(mRuby, "Cluster", cServer);
267
+ rb_define_method(cCluster, "listen", up_cluster_listen, 0);
268
+ rb_define_method(cCluster, "stop", up_cluster_stop, 0);
269
+
270
+ cRackEnv = rb_define_class_under(mRuby, "RackEnv", rb_cHash);
271
+ cRequest = rb_define_class_under(mRuby, "Request", rb_cObject);
272
+ rb_define_alloc_func(cRequest, up_request_alloc);
273
+ rb_define_method(cRequest, "each_header", up_request_headers, 0);
274
+ rb_define_method(cRequest, "get_header", up_request_get_header, 1);
275
+ rb_define_method(cRequest, "get_method", up_request_get_method, 0);
276
+ rb_define_method(cRequest, "get_query", up_request_get_query, 0);
277
+ rb_define_method(cRequest, "get_url", up_request_get_url, 0);
278
+ }
@@ -46,11 +46,11 @@ module Up
46
46
  when 'rack.url_scheme'
47
47
  @config[:scheme]
48
48
  when 'HTTP_VERSION'
49
- `#@reg.httpVersion`
49
+ `#@req.httpVersion`
50
50
  when 'PATH_INFO'
51
51
  `#@req.url`
52
52
  when 'QUERY_STRING'
53
- ""
53
+ nil
54
54
  when 'RACK_ERRORS'
55
55
  self['rack.errors']
56
56
  when 'RACK_LOGGER'
@@ -0,0 +1,10 @@
1
+ require 'rack/builder'
2
+ require 'up/ruby/rack_cluster'
3
+
4
+ module Up
5
+ module CLI
6
+ def self.call
7
+ Up::Ruby::RackCluster.run(get_app, get_options)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,26 @@
1
+ require 'up/ruby/rack_env'
2
+ require 'up_ext'
3
+
4
+ module Up
5
+ module Ruby
6
+ module RackCluster
7
+ def self.run(app, options = {})
8
+ raise "already running" if @server
9
+ @server = Up::Ruby::Cluster.new(app: app, **options).listen
10
+ true
11
+ end
12
+
13
+ def self.shutdown
14
+ @server&.stop
15
+ @server = nil
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ ENV['RACK_HANDLER'] ||= 'up'
22
+
23
+ begin
24
+ ::Rackup::Handler.register('up', Up::Ruby::RackCluster) if defined?(::Rackup::Handler)
25
+ rescue StandardError
26
+ end
@@ -0,0 +1,97 @@
1
+ require 'logger'
2
+ require 'up/version'
3
+
4
+ module Up
5
+ module Ruby
6
+ class RackEnv < ::Hash
7
+ RACK_VARS = %w[rack.errors rack.hijack rack.hijack? rack.input rack.logger
8
+ rack.multipart.buffer_size rack.multipart.tempfile_factory
9
+ rack.response_finished
10
+ rack.session rack.upgrade rack.upgrade? rack.url_scheme
11
+ HTTP_ACCEPT HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE
12
+ HTTP_CONNECTION HTTP_HOST HTTP_USER_AGENT PATH_INFO QUERY_STRING REQUEST_METHOD
13
+ SCRIPT_NAME SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE]
14
+ def initialize(req, config = {})
15
+ @req = req
16
+ @config = config
17
+ end
18
+
19
+ def [](key)
20
+ return super(key) if key?(key)
21
+ self[key] = case key
22
+ when 'rack.errors'
23
+ STDERR
24
+ when 'rack.hijack'
25
+ nil
26
+ when 'rack.hijack?'
27
+ false
28
+ when 'rack.input'
29
+ nil # ::IO.new
30
+ when 'rack.logger'
31
+ ::Logger.new(self['rack.errors'])
32
+ when 'rack.multipart.buffer_size'
33
+ 4096
34
+ when 'rack.multipart.tempfile_factory'
35
+ proc { |_filename, _content_type| File.new }
36
+ when 'rack.response_finished'
37
+ []
38
+ when 'rack.session'
39
+ {}
40
+ when 'rack.upgrade'
41
+ nil
42
+ when 'rack.upgrade?'
43
+ nil
44
+ when 'rack.url_scheme'
45
+ @config[:scheme]
46
+ when 'PATH_INFO'
47
+ @req.get_url
48
+ when 'QUERY_STRING'
49
+ @req.get_query
50
+ when 'RACK_ERRORS'
51
+ self['rack.errors']
52
+ when 'RACK_LOGGER'
53
+ self['rack.logger']
54
+ when 'REQUEST_METHOD'
55
+ @req.get_method
56
+ when 'SCRIPT_NAME'
57
+ ""
58
+ when 'SERVER_NAME'
59
+ @config[:host]
60
+ when 'SERVER_PORT'
61
+ @config[:port].to_s
62
+ when 'SERVER_PROTOCOL'
63
+ @req.get_header('protocol')
64
+ when 'SERVER_SOFTWARE'
65
+ "#{@config[:handler]}/#{Up::VERSION} #{@config[:engine]}"
66
+ else
67
+ if key.start_with?('HTTP_')
68
+ key = key[5..].gsub(/_/, '-')
69
+ @req.get_header(key.downcase)
70
+ else
71
+ nil
72
+ end
73
+ end
74
+ end
75
+
76
+ def req_headers
77
+ @req.headers
78
+ end
79
+
80
+ def each
81
+ unless @got_them_all
82
+ RACK_VARS.each { |k| self[k] unless self.key?(k) }
83
+ @got_them_all = true
84
+ end
85
+ super
86
+ end
87
+
88
+ def to_s
89
+ unless @got_them_all
90
+ RACK_VARS.each { |k| self[k] unless self.key?(k) }
91
+ @got_them_all = true
92
+ end
93
+ super
94
+ end
95
+ end
96
+ end
97
+ end