iodine 0.2.2 → 0.2.3

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6ad0af726b17fcc350121f1dbacbe34ae47724b9
4
- data.tar.gz: 2d30bc235638f7a2e4aa203fb3f25dfa2d78fafe
3
+ metadata.gz: 8757fa617add5c9d6d4c8b7feee4c3b97952a7e3
4
+ data.tar.gz: b66b35b5662458733a47d5358901278e428811b3
5
5
  SHA512:
6
- metadata.gz: 9f918ab4162ff89e65596bc3154d9bedaad2187a24fa4b2fe0707f06079afddad00e34e8879da340b47f3d5e4a900e83615effe8813db1cc5a99b9c83ff4d635
7
- data.tar.gz: cddb06efa79664971a33714811d4c824dd7398d7c1b9573ac0081495b140c7163ebc2b635da466abf7a3465fe3d7c0c73899a855c91c329b923397e662213eb2
6
+ metadata.gz: 65bc4f93c07a4e81eacdc5ce203bf16516c079a246ca4aee6bec078e3180b0d9aebeb653c711a5519d45f57ab817a188ad1c2509bb87b2587c50ab7763155e4b
7
+ data.tar.gz: ec57fa3e6674bb4393017f188671bf88ce851b6948b1c3b53917a5b78bef1b6822967e1b525e73250288e4d020a29aaa977f1167201ba22b3b12e300020070c7
@@ -8,6 +8,14 @@ Please notice that this change log contains changes for upcoming releases as wel
8
8
 
9
9
  ***
10
10
 
11
+ Change log v.0.2.3
12
+
13
+ **Update** The `write` system call is now deferred when resources allow, meaning that (as long as the `write` buffer isn't full) `write` is not only non-blocking, but it's performed as a separate event, outside of the Ruby GIL.
14
+
15
+ **Update**: The global socket `write` buffer was increased to ~16Mb (from ~4Mb), allowing for more concurrent `write` operations. However, the `write` buffer is still limited and `write` might block while the buffer is full. Blocking and "hanging" the server until there's enough room in the buffer for the requested `write` will slow the server down while keeping it healthy and more secure. IMHO, it is the lesser of two evils.
16
+
17
+ ***
18
+
11
19
  Change log v.0.2.2
12
20
 
13
21
  **Update** The static file service now supports `ETag` caching, sending a 304 (not changed) response for valid ETags.
data/README.md CHANGED
@@ -28,7 +28,7 @@ Iodine is a C extension for Ruby, developed for Ruby MRI 2.2.2 and up... it shou
28
28
 
29
29
  ## Iodine::Rack - an HTTP and Websockets server
30
30
 
31
- Iodine includes a light and fast HTTP and Websocket server written in C that was written according to the [Rack interface specifications](http://www.rubydoc.info/github/rack/rack/master/file/SPEC).
31
+ Iodine includes a light and fast HTTP and Websocket server written in C that was written according to the [Rack interface specifications](http://www.rubydoc.info/github/rack/rack/master/file/SPEC) and the Websocket draft extension.
32
32
 
33
33
  ### Running the web server
34
34
 
@@ -47,7 +47,19 @@ Puma's model of 16 threads and 4 processes is easily adopted and proved to provi
47
47
  bundler exec iodine -p $PORT -t 16 -w 4
48
48
  ```
49
49
 
50
- It should be noted that Websocket support means that no automatic process scaling is provided... It is important to use `iodine` with the `-w` option and set the number of desired processes (ideally equal to the number of CPU cores).
50
+ It should be noted that Websocket support means that no automatic process scaling is provided (since Websockets don't share data across processes without your help)... It is important to use `iodine` with the `-w` option and set the number of desired processes (ideally equal to the number of CPU cores).
51
+
52
+ ### Writing data to the network layer
53
+
54
+ Iodine allows Ruby to write strings to the network layer. This includes HTTP and Websocket responses.
55
+
56
+ Iodine will handle an internal buffer (~4 to ~16 Mb, version depending) so that `write` can return immediately (non-blocking).
57
+
58
+ However, when the buffer is full, `write` will block until enough space in he buffer becomes available. Sending up to 16Kb of data (a single buffer "packet") is optimal. Sending a larger response might effect concurrency. Best Websocket response length is ~1Kb (1 TCP / IP packet) and allows for faster transmissions.
59
+
60
+ When using the Iodine's web server (`Iodine::Rack`), the static file service offered by Iodine streams files (instead of using the buffer). Every file response will require up to 2 buffer "packets" (~32Kb), one for the header and the other for file streaming.
61
+
62
+ This means that even a Gigabyte long response will use ~32Kb of memory, as long as it uses the static file service or the `X-Sendfile` extension (Iodine's static file service can be invoked by the Ruby application using the `X-Sendfile` header).
51
63
 
52
64
  ### Static file serving support
53
65
 
@@ -73,6 +85,32 @@ app = Proc.new { out }
73
85
  run app
74
86
  ```
75
87
 
88
+ #### X-Sendfile
89
+
90
+ Sending can be performed by using the `X-Sendfile` header in the Ruby application response.
91
+
92
+ i.e. (example `config.ru` for Iodine):
93
+
94
+ ```ruby
95
+ app = proc do |env|
96
+ request = Rack::Request.new(env)
97
+ if request.path_info == '/source'.freeze
98
+ [200, { 'X-Sendfile' => File.expand_path(__FILE__) }, []]
99
+ elsif request.path_info == '/file'.freeze
100
+ [200, { 'X-Header' => 'This was a Rack::Sendfile response sent as text' }, File.open(__FILE__)]
101
+ else
102
+ [200, { 'Content-Type'.freeze => 'text/html'.freeze,
103
+ 'Content-Length'.freeze => request.path_info.length.to_s },
104
+ [request.path_info]]
105
+ end
106
+ end
107
+ # # optional:
108
+ # use Rack::Sendfile
109
+ run app
110
+ ```
111
+
112
+ Go to [localhost:3000/source](http://localhost:3000/source) to download the `config.ru` file using the `X-Sendfile` extension.
113
+
76
114
  ### Special HTTP `Upgrade` support
77
115
 
78
116
  Iodine's HTTP server includes special support for the Upgrade directive using Rack's `env` Hash, allowing the application to focus on services and data while Iodine takes care of the network layer.
@@ -0,0 +1,83 @@
1
+ ## Rack Websockets
2
+
3
+ This is the proposed Websocket support extension for Rack servers.
4
+
5
+ Servers that publish Websocket support using the `env['upgrade.websocket?']` value are assume by users to follow the requirements set in this document and thus should follow the requirements set in herein.
6
+
7
+ This document reserves the Rack `env` Hash keys of `upgrade.websocket?` and `upgrade.websocket`.
8
+
9
+ ## The Websocket Callback Object
10
+
11
+ Websocket connection upgrade and handling is performed using a Websocket Callback Object.
12
+
13
+ The Websocket Callback Object should be a class (or an instance of such class) who's instances implement any of the following callbacks:
14
+
15
+ * `on_open()` WILL be called once the upgrade had completed.
16
+
17
+ * `on_message(data)` WILL be called when incoming Websocket data is received. `data` will be a String with an encoding of UTF-8 for text messages and `binary` encoding for non-text messages (as specified by the Websocket Protocol).
18
+
19
+ The *client* **must** assume that the `data` String will be a **recyclable buffer** and that it's content will be corrupted the moment the `on_message` callback returns.
20
+
21
+ * `on_ready()` MAY be called when the state of the out-going socket buffer changes from full to not full (data can be sent to the socket). **If** `has_pending?` returns `true`, the `on_ready` callback **must** be called once the buffer state changes.
22
+
23
+ * `on_shutdown()` MAY be called during the server's graceful shutdown process, _before_ the connection is closed and in addition to the `on_close` function (which is called _after_ the connection is closed.
24
+
25
+ * `on_close()` WILL be called _after_ the connection was closed for whatever reason (socket errors, parsing errors, timeouts, client disconnection, `close` being called, etc').
26
+
27
+ * `on_open`, `on_ready`, `on_shutdown` and `on_close` shouldn't expect any arguments (`arity == 0`).
28
+
29
+ The following method names are reserved for the network implementation: `write`, `close` and `has_pending?`.
30
+
31
+ The server **must** extend the Websocket Callback Object's *class* using `extend`, so that the Websocket Callback Object inherits the following methods:
32
+
33
+ * `write(data)` will attempt to send the data through the websocket connection. `data` **must** be a String. If `data` is UTF-8 encoded, the data will be sent as text. If `data` is binary encoded it will be sent as non-text (as specified by the Websocket Protocol).
34
+
35
+ `write` has the same delivery promise as `Socket#write` (a successful `write` does **not** mean any of the data will reach the other side).
36
+
37
+ `write` shall return `true` on success and `false` if the websocket is closed.
38
+
39
+ A server **should** document whether `write` will block or return immediately. It is **recommended** that servers implement buffered IO, allowing `write` to return immediately when resources allow and block (or, possibly, disconnect) when the IO buffer is full.
40
+
41
+ * `close` closes the connection once all the data in the outgoing queue was sent. If `close` is called while there is still data to be sent, `close` will only take effect once the data was sent.
42
+
43
+ `close` shall always return `nil`.
44
+
45
+ * `has_pending?` queries the state of the server's buffer for the specific connection (i.e., if the server has any data it is waiting to send through the socket).
46
+
47
+ `has_pending?`, shall return `true` **if** the server has data waiting to be written to the socket **and** the server promises to call the `on_ready` callback once the buffer is empty and the socket is writable. Otherwise (i.e., if the server doesn't support the `on_ready` callback), `has_pending?` shall return `false`.
48
+
49
+ To clarify: **implementing `has_pending?` is semi-optional**, meaning that a server may choose to always return `false`, no matter the actual state of the socket's buffer.
50
+
51
+ The following keywords (both as method names and instance variable names) are reserved for the internal server implementation: `_server_ws` and `conn_id`.
52
+
53
+ * The `_server_ws` object is private and shouldn't be accessed by the client.
54
+
55
+ * The `conn_id` object may be used as a connection ID for any functionality not specified herein.
56
+
57
+ Connection `ping` / `pong`, timeouts and network considerations should be implemented by the server. It is **recommended** (but not required) that the server send `ping`s to prevent connection timeouts and detect network failure.
58
+
59
+ Server settings **may** (not required) be provided to allow for customization and adaptation for different network environments or websocket extensions. It is **recommended** that any settings be available as command line arguments and **not** incorporated into the application's logic.
60
+
61
+ ## Upgrading
62
+
63
+ * **Server**: When an upgrade request is received, the server will set the `env['upgrade.websockets?']` flag to `true`, indicating that: 1. this specific request is upgradable; and 2. this server supports specification.
64
+
65
+ * **Client**: When a client decides to upgrade a request, they will place a Websocket Callback Object (either a class or an instance) in the `env['upgrade.websockets']` Hash key.
66
+
67
+ * **Server**: The server will review the `env` Hash *before* sending the response. If the `env['upgrade.websockets']` was set, the server will perform the upgrade.
68
+
69
+ * **Server**: The server will send the correct response status and headers, as will as any headers present in the response. The server will also perform any required housekeeping, such as closing the response body, if exists.
70
+
71
+ The response status provided by the response object shall be ignored and the correct response status shall be set by the server.
72
+
73
+ * **Server**: Once the upgrade had completed, The server will add the required websocket/network functions to the callback handler or it's class (as aforementioned). If the callback handler is a Class object, the server will create a new instance of that class.
74
+
75
+ * **Server**: The server will call the `on_open` callback.
76
+
77
+ No other callbacks shall be called until the `on_open` callback had returned.
78
+
79
+ Websocket messages shall be handled by the `on_message` callback in the same order in which they arrive and the `on_message` will **not** be executed concurrently for the same connection.
80
+
81
+ The `on_close` callback will **not** be called while `on_message` or `on_open` callbacks are running.
82
+
83
+ The `on_ready` callback might be called concurrently with the `on_message` callback, allowing data to be sent even while other data is being processed. Multi-threading considerations may apply.
@@ -295,8 +295,8 @@ int http_response_sendfile2(http_response_s *response, http_request_s *request,
295
295
 
296
296
  response->last_modified = file_data.st_mtime;
297
297
  http_response_write_header(response, .name = "Cache-Control",
298
- .name_length = 13, .value = "public, max-age=3600",
299
- .value_length = 20);
298
+ .name_length = 13, .value = "max-age=3600",
299
+ .value_length = 12);
300
300
 
301
301
  /* check etag */
302
302
  if ((ext = http_request_find_header(request, "if-none-match", 13)) &&
@@ -558,7 +558,8 @@ static void *srv_start_no_gvl(void *_) {
558
558
  // print a warnning if settings are sub optimal
559
559
  #ifdef _SC_NPROCESSORS_ONLN
560
560
  size_t cpu_count = sysconf(_SC_NPROCESSORS_ONLN);
561
- if ((processes << 1) < cpu_count || processes > (cpu_count << 1))
561
+ if (cpu_count > 0 &&
562
+ ((processes << 1) < cpu_count || processes > (cpu_count << 1)))
562
563
  fprintf(
563
564
  stderr, "* Performance warnning:\n"
564
565
  " - This computer has %lu CPUs available and you'll be "
@@ -116,14 +116,22 @@ static VALUE iodine_ws_close(VALUE self) {
116
116
  return self;
117
117
  }
118
118
 
119
- /** Writes data to the websocket. Returns `self` (the websocket object). */
119
+ /**
120
+ * Writes data to the websocket.
121
+ *
122
+ * Returns `true` on success or `false if the websocket was closed or an error
123
+ * occurred.
124
+ *
125
+ * `write` will return immediately UNLESS resources are insufficient. If the
126
+ * global `write` buffer is full, `write` will block until a buffer "packet"
127
+ * becomes available and can be assigned to the socket. */
120
128
  static VALUE iodine_ws_write(VALUE self, VALUE data) {
121
129
  ws_s *ws = get_ws(self);
122
130
  if (((protocol_s *)ws)->service != WEBSOCKET_ID_STR)
123
131
  return Qfalse;
124
132
  websocket_write(ws, RSTRING_PTR(data), RSTRING_LEN(data),
125
133
  rb_enc_get(data) == UTF8Encoding);
126
- return self;
134
+ return Qtrue;
127
135
  }
128
136
 
129
137
  /** Returns the number of active websocket connections (including connections
@@ -80,10 +80,11 @@ These macros help prevent code changes when changing the data struct.
80
80
  // run through any open sockets and call the shutdown handler
81
81
  static inline void server_on_shutdown(void) {
82
82
  if (server_data.fds && server_data.capacity > 0) {
83
+ intptr_t uuid;
83
84
  for (size_t i = 0; i < server_data.capacity; i++) {
84
85
  if (server_data.fds[i].protocol == NULL)
85
86
  continue;
86
- intptr_t uuid = sock_fd2uuid(i);
87
+ uuid = sock_fd2uuid(i);
87
88
  if (uuid != -1) {
88
89
  if (server_data.fds[i].protocol->on_shutdown != NULL)
89
90
  server_data.fds[i].protocol->on_shutdown(uuid,
@@ -547,12 +548,26 @@ ssize_t server_run(struct ServerSettings settings) {
547
548
  async_join();
548
549
  else
549
550
  async_perform();
551
+
552
+ /*
553
+ * start a single worker thread for shutdown tasks and async close operations
554
+ */
555
+ async_start(1);
556
+
550
557
  listener_on_server_shutdown();
551
558
  reactor_review();
552
559
  server_on_shutdown();
560
+ /* cycle until no IO events occure. */
561
+ while (reactor_review() > 0)
562
+ ;
553
563
  if (settings.on_finish)
554
564
  settings.on_finish();
555
565
 
566
+ /*
567
+ * Wait for any unfinished tasks.
568
+ */
569
+ async_finish();
570
+
556
571
  if (children) {
557
572
  if (rootpid == getpid()) {
558
573
  while (waitpid(-1, NULL, 0) >= 0)
@@ -625,6 +640,7 @@ void server_set_timeout(intptr_t fd, uint8_t timeout) {
625
640
  if (valid_uuid(fd) == 0)
626
641
  return;
627
642
  lock_uuid(fd);
643
+ uuid_data(fd).active = server_data.last_tick;
628
644
  uuid_data(fd).timeout = timeout;
629
645
  unlock_uuid(fd);
630
646
  }
@@ -5,7 +5,10 @@ license: MIT
5
5
  Feel free to copy, use and enjoy according to the license provided.
6
6
  */
7
7
  #ifndef LIB_SERVER
8
- #define LIB_SERVER "0.4.0"
8
+ #define LIB_SERVER "0.4.1"
9
+ #define LIB_SERVER_VERSION_MAJOR 0
10
+ #define LIB_SERVER_VERSION_MINOR 4
11
+ #define LIB_SERVER_VERSION_PATCH 1
9
12
 
10
13
  #ifndef _GNU_SOURCE
11
14
  #define _GNU_SOURCE
@@ -30,7 +33,7 @@ messages regarding the server state (start / finish / listen messages).
30
33
  #define SERVER_PRINT_STATE 1
31
34
  #endif
32
35
 
33
- #if LIB_ASYNC_VERSION_MINOR != 4 || LIB_REACT_VERSION_MINOR != 3 || \
36
+ #if LIB_ASYNC_VERSION_MINOR != 4 || LIB_REACT_VERSION_MINOR != 3 || \
34
37
  LIB_SOCK_VERSION_MINOR != 2
35
38
  #warning Lib-Server dependency versions are not in sync. Please review API versions.
36
39
  #endif
@@ -194,18 +197,18 @@ struct Protocol {
194
197
  * The string should be a global constant, only a pointer comparison will be
195
198
  * made (not `strcmp`).
196
199
  */
197
- const char* service;
200
+ const char *service;
198
201
  /** called when a data is available, but will not run concurrently */
199
- void (*on_data)(intptr_t fduuid, protocol_s* protocol);
202
+ void (*on_data)(intptr_t fduuid, protocol_s *protocol);
200
203
  /** called when the socket is ready to be written to. */
201
- void (*on_ready)(intptr_t fduuid, protocol_s* protocol);
204
+ void (*on_ready)(intptr_t fduuid, protocol_s *protocol);
202
205
  /** called when the server is shutting down,
203
206
  * but before closing the connection. */
204
- void (*on_shutdown)(intptr_t fduuid, protocol_s* protocol);
207
+ void (*on_shutdown)(intptr_t fduuid, protocol_s *protocol);
205
208
  /** called when the connection was closed, but will not run concurrently */
206
- void (*on_close)(protocol_s* protocol);
209
+ void (*on_close)(protocol_s *protocol);
207
210
  /** called when a connection's timeout was reached */
208
- void (*ping)(intptr_t fduuid, protocol_s* protocol);
211
+ void (*ping)(intptr_t fduuid, protocol_s *protocol);
209
212
  /** private metadata used for object protection */
210
213
  spn_lock_i callback_lock;
211
214
  };
@@ -218,21 +221,21 @@ These settings will be used to setup listenning sockets.
218
221
  struct ServerServiceSettings {
219
222
  /** Called whenever a new connection is accepted. Should return a pointer to
220
223
  * the connection's protocol. */
221
- protocol_s* (*on_open)(intptr_t fduuid, void* udata);
224
+ protocol_s *(*on_open)(intptr_t fduuid, void *udata);
222
225
  /** The network service / port. Defaults to "3000". */
223
- const char* port;
226
+ const char *port;
224
227
  /** The socket binding address. Defaults to the recommended NULL. */
225
- const char* address;
228
+ const char *address;
226
229
  /** Opaque user data. */
227
- void* udata;
230
+ void *udata;
228
231
  /**
229
232
  * Called when the server starts, allowing for further initialization, such as
230
233
  * timed event scheduling.
231
234
  *
232
235
  * This will be called seperately for every process. */
233
- void (*on_start)(void* udata);
236
+ void (*on_start)(void *udata);
234
237
  /** called when the server is done, to clean up any leftovers. */
235
- void (*on_finish)(void* udata);
238
+ void (*on_finish)(void *udata);
236
239
  };
237
240
 
238
241
  /**************************************************************************/ /**
@@ -273,7 +276,31 @@ struct ServerSettings {
273
276
  */
274
277
 
275
278
  /**
276
- Listens to a server with any of the following server settings:
279
+ Listens to a server with any of the available service settings:
280
+
281
+ * `.on_open` called whenever a new connection is accepted.
282
+
283
+ Should return a pointer to the connection's protocol.
284
+
285
+ * `.port` the network service / port. Defaults to "3000".
286
+
287
+ * `.address` the socket binding address. Defaults to the recommended NULL.
288
+
289
+ * `.udata`opaque user data.
290
+
291
+ * `.on_start` called when the server starts, allowing for further
292
+ initialization, such as timed event scheduling.
293
+
294
+ This will be called seperately for every process.
295
+
296
+ * `.on_finish` called when the server is done, to clean up any leftovers.
297
+
298
+ */
299
+ int server_listen(struct ServerServiceSettings);
300
+ #define server_listen(...) \
301
+ server_listen((struct ServerServiceSettings){__VA_ARGS__})
302
+ /**
303
+ Runs a server with any of the following server settings:
277
304
 
278
305
  * `.threads` the number of threads to initiate in the server's thread pool.
279
306
 
@@ -292,12 +319,11 @@ initiate a `fork`).
292
319
  This method blocks the current thread until the server is stopped when a
293
320
  SIGINT/SIGTERM is received.
294
321
 
295
- To kill the server use the `kill` function with a SIGINT.
322
+ To shutdown the server use the `kill` function with a SIGINT.
323
+
324
+ This function only returns after the server had completed it's shutdown process.
325
+
296
326
  */
297
- int server_listen(struct ServerServiceSettings);
298
- #define server_listen(...) \
299
- server_listen((struct ServerServiceSettings){__VA_ARGS__})
300
- /** runs the server, hanging the current process and thread. */
301
327
  ssize_t server_run(struct ServerSettings);
302
328
  #define server_run(...) server_run((struct ServerSettings){__VA_ARGS__})
303
329
  /** Stops the server, shouldn't be called unless int's impossible to send an
@@ -317,7 +343,7 @@ Gets the active protocol object for the requested file descriptor.
317
343
  Returns NULL on error (i.e. connection closed), otherwise returns a `protocol_s`
318
344
  pointer.
319
345
  */
320
- protocol_s* server_get_protocol(intptr_t uuid);
346
+ protocol_s *server_get_protocol(intptr_t uuid);
321
347
  /**
322
348
  Sets a new active protocol object for the requested file descriptor.
323
349
 
@@ -326,7 +352,7 @@ all resources are released.
326
352
 
327
353
  Returns -1 on error (i.e. connection closed), otherwise returns 0.
328
354
  */
329
- ssize_t server_switch_protocol(intptr_t fd, protocol_s* new_protocol);
355
+ ssize_t server_switch_protocol(intptr_t fd, protocol_s *new_protocol);
330
356
  /**
331
357
  Sets a connection's timeout.
332
358
 
@@ -340,7 +366,7 @@ based resources asynchronously (i.e. database resources etc').
340
366
 
341
367
  On failure the fduuid_u.data.fd value will be -1.
342
368
  */
343
- intptr_t server_attach(int fd, protocol_s* protocol);
369
+ intptr_t server_attach(int fd, protocol_s *protocol);
344
370
  /** Hijack a socket (file descriptor) from the server, clearing up it's
345
371
  resources and calling the protocol's `on_close` callback (making sure allocated
346
372
  resources are freed).
@@ -355,7 +381,7 @@ The returned value is the fd for the socket, or -1 on error.
355
381
  int server_hijack(intptr_t uuid);
356
382
  /** Counts the number of connections for the specified protocol (NULL = all
357
383
  protocols). */
358
- long server_count(char* service);
384
+ long server_count(char *service);
359
385
 
360
386
  /****************************************************************************
361
387
  * Read and Write
@@ -390,13 +416,10 @@ callback.
390
416
  It is recommended the `on_finish` callback is only used to perform any
391
417
  resource cleanup necessary.
392
418
  */
393
- void server_each(intptr_t origin_uuid,
394
- const char* service,
395
- void (*task)(intptr_t uuid, protocol_s* protocol, void* arg),
396
- void* arg,
397
- void (*on_finish)(intptr_t origin_uuid,
398
- protocol_s* protocol,
399
- void* arg));
419
+ void server_each(intptr_t origin_uuid, const char *service,
420
+ void (*task)(intptr_t uuid, protocol_s *protocol, void *arg),
421
+ void *arg, void (*on_finish)(intptr_t origin_uuid,
422
+ protocol_s *protocol, void *arg));
400
423
  /** Schedules a specific task to run asyncronously for a specific connection.
401
424
 
402
425
  returns -1 on failure, 0 on success (success being scheduling the task).
@@ -409,26 +432,22 @@ and call the fallback function from within the main task, but other designes
409
432
  are valid as well.
410
433
  */
411
434
  void server_task(intptr_t uuid,
412
- void (*task)(intptr_t uuid, protocol_s* protocol, void* arg),
413
- void* arg,
414
- void (*fallback)(intptr_t uuid, void* arg));
435
+ void (*task)(intptr_t uuid, protocol_s *protocol, void *arg),
436
+ void *arg, void (*fallback)(intptr_t uuid, void *arg));
415
437
  /** Creates a system timer (at the cost of 1 file descriptor) and pushes the
416
438
  timer to the reactor. The task will repeat `repetitions` times. if
417
439
  `repetitions` is set to 0, task will repeat forever. Returns -1 on error
418
440
  or the new file descriptor on succeess.
419
441
  */
420
- int server_run_every(size_t milliseconds,
421
- size_t repetitions,
422
- void (*task)(void*),
423
- void* arg,
424
- void (*on_finish)(void*));
442
+ int server_run_every(size_t milliseconds, size_t repetitions,
443
+ void (*task)(void *), void *arg,
444
+ void (*on_finish)(void *));
425
445
 
426
446
  /** Creates a system timer (at the cost of 1 file descriptor) and pushes the
427
447
  timer to the reactor. The task will NOT repeat. Returns -1 on error or the
428
448
  new file descriptor on succeess. */
429
449
  __unused static inline int server_run_after(size_t milliseconds,
430
- void task(void*),
431
- void* arg) {
450
+ void task(void *), void *arg) {
432
451
  return server_run_every(milliseconds, 1, task, arg, NULL);
433
452
  }
434
453
 
@@ -44,6 +44,12 @@ Support timeout setting.
44
44
  #pragma weak sock_touch
45
45
  void sock_touch(intptr_t uuid) {}
46
46
 
47
+ /* *****************************************************************************
48
+ Support event based `write` scheduling.
49
+ */
50
+ #pragma weak async_run
51
+ int async_run(void (*task)(void *), void *arg) { return -1; }
52
+
47
53
  /* *****************************************************************************
48
54
  OS Sendfile settings.
49
55
  */
@@ -72,6 +78,19 @@ Buffer and socket map memory allocation. Defaults to mmap.
72
78
  #define USE_MALLOC 0
73
79
  #endif
74
80
 
81
+ /* *****************************************************************************
82
+ The system call to `write` (non-blocking) can be defered when using `libasync`.
83
+
84
+ However, this will not prevent `sock_write` from cycling through the sockets and
85
+ flushing them (block emulation) when both the system and the user level buffers
86
+ are full.
87
+
88
+ Defaults to 1 (defered).
89
+ */
90
+ #ifndef SOCK_DELAY_WRITE
91
+ #define SOCK_DELAY_WRITE 1
92
+ #endif
93
+
75
94
  /* *****************************************************************************
76
95
  Library related helper functions
77
96
  */
@@ -188,7 +207,8 @@ static inline void set_fd(int fd, unsigned int state) {
188
207
  };
189
208
  // unlock
190
209
  spn_unlock(&fd_info[fd].lock);
191
- // should be called within the lock? - no function calling within a spinlock.
210
+ // should be called within the lock? - no function calling within a
211
+ // spinlock.
192
212
  if (old_data.rw_hooks && old_data.rw_hooks->on_clear)
193
213
  old_data.rw_hooks->on_clear(old_data.fduuid.uuid, old_data.rw_hooks);
194
214
  // clear old data
@@ -213,6 +233,9 @@ Call this function before calling any `libsock` functions.
213
233
  static void destroy_lib_data(void) {
214
234
  if (fd_info) {
215
235
  while (fd_capacity--) { // include 0 in countdown
236
+ if (fd_info[fd_capacity].open) {
237
+ fprintf(stderr, "Socket %lu is marked as open\n", fd_capacity);
238
+ }
216
239
  set_fd(fd_capacity, LIB_SOCK_STATE_CLOSED);
217
240
  }
218
241
  #if USE_MALLOC == 1
@@ -463,12 +486,30 @@ static void sock_flush_unsafe(int fd) {
463
486
  }
464
487
  }
465
488
 
489
+ #if SOCK_DELAY_WRITE == 1
490
+
491
+ static inline void sock_flush_schd(intptr_t uuid) {
492
+ if (async_run((void *)sock_flush, (void *)uuid) == -1)
493
+ goto fallback;
494
+ return;
495
+ fallback:
496
+ sock_flush_unsafe(sock_uuid2fd(uuid));
497
+ }
498
+
499
+ #define _write_to_sock() sock_flush_schd(sfd->fduuid.uuid)
500
+
501
+ #else
502
+
503
+ #define _write_to_sock() sock_flush_unsafe(fd)
504
+
505
+ #endif
506
+
466
507
  static inline void sock_send_packet_unsafe(int fd, sock_packet_s *packet) {
467
508
  fd_info_s *sfd = fd_info + fd;
468
509
  if (sfd->packet == NULL) {
469
510
  /* no queue, nothing to check */
470
511
  sfd->packet = packet;
471
- sock_flush_unsafe(fd);
512
+ _write_to_sock();
472
513
  return;
473
514
 
474
515
  } else if (packet->metadata.urgent == 0) {
@@ -477,7 +518,7 @@ static inline void sock_send_packet_unsafe(int fd, sock_packet_s *packet) {
477
518
  while (pos->metadata.next)
478
519
  pos = pos->metadata.next;
479
520
  pos->metadata.next = packet;
480
- sock_flush_unsafe(fd);
521
+ _write_to_sock();
481
522
  return;
482
523
 
483
524
  } else {
@@ -494,7 +535,7 @@ static inline void sock_send_packet_unsafe(int fd, sock_packet_s *packet) {
494
535
  *pos = tail;
495
536
  }
496
537
  }
497
- sock_flush_unsafe(fd);
538
+ _write_to_sock();
498
539
  }
499
540
 
500
541
  /* *****************************************************************************
@@ -676,7 +717,8 @@ static inline sock_packet_s *sock_try_checkout_packet(void) {
676
717
 
677
718
  /**
678
719
  Checks out a `sock_packet_s` from the packet pool, transfering the
679
- ownership of the memory to the calling function. The function will hang until a
720
+ ownership of the memory to the calling function. The function will hang until
721
+ a
680
722
  packet becomes available, so never check out more then a single packet at a
681
723
  time.
682
724
  */
@@ -713,7 +755,8 @@ ssize_t sock_send_packet(intptr_t uuid, sock_packet_s *packet) {
713
755
  }
714
756
 
715
757
  /**
716
- Returns TRUE (non 0) if there is data waiting to be written to the socket in the
758
+ Returns TRUE (non 0) if there is data waiting to be written to the socket in
759
+ the
717
760
  user-land buffer.
718
761
  */
719
762
  _Bool sock_packets_pending(intptr_t uuid) {
@@ -765,7 +808,8 @@ ssize_t sock_read(intptr_t uuid, void *buf, size_t count) {
765
808
  }
766
809
  if (i_read == -1 && (ERR_OK || ERR_TRY_AGAIN))
767
810
  return 0;
768
- // fprintf(stderr, "Read Error for %lu bytes from fd %d (closing))\n", count,
811
+ // fprintf(stderr, "Read Error for %lu bytes from fd %d (closing))\n",
812
+ // count,
769
813
  // sock_uuid2fd(uuid));
770
814
  sock_close(uuid);
771
815
  return -1;
@@ -778,9 +822,12 @@ Flushing
778
822
  ssize_t sock_flush(intptr_t uuid) {
779
823
  if (!fd_info || !is_valid(uuid))
780
824
  return -1;
825
+ if (uuid2info(uuid).packet == NULL)
826
+ goto no_packet;
781
827
  spn_lock(&uuid2info(uuid).lock);
782
828
  sock_flush_unsafe(sock_uuid2fd(uuid));
783
829
  spn_unlock(&uuid2info(uuid).lock);
830
+ no_packet:
784
831
  if (uuid2info(uuid).close) {
785
832
  sock_force_close(uuid);
786
833
  return -1;
@@ -5,10 +5,10 @@ license: MIT
5
5
  Feel free to copy, use and enjoy according to the license provided.
6
6
  */
7
7
  #ifndef LIB_SOCK
8
- #define LIB_SOCK "0.2.0"
8
+ #define LIB_SOCK "0.2.2"
9
9
  #define LIB_SOCK_VERSION_MAJOR 0
10
10
  #define LIB_SOCK_VERSION_MINOR 2
11
- #define LIB_SOCK_VERSION_PATCH 0
11
+ #define LIB_SOCK_VERSION_PATCH 2
12
12
 
13
13
  /** \file
14
14
  The libsock is a non-blocking socket helper library, using a user level buffer,
@@ -42,7 +42,7 @@ This information is also useful when implementing read / write hooks.
42
42
  #define BUFFER_FILE_READ_SIZE BUFFER_PACKET_SIZE
43
43
  #endif
44
44
  #ifndef BUFFER_PACKET_POOL
45
- #define BUFFER_PACKET_POOL 248 /* hard limit unless BUFFER_ALLOW_MALLOC */
45
+ #define BUFFER_PACKET_POOL 1024
46
46
  #endif
47
47
 
48
48
  /* *****************************************************************************
@@ -218,12 +218,17 @@ void sock_touch(intptr_t uuid);
218
218
  `sock_read` attempts to read up to count bytes from the socket into the buffer
219
219
  starting at buf.
220
220
 
221
- It's behavior should conform to the native `read` implementations, except some
222
- data might be available in the kernel's buffer while it is not available to be
223
- read using sock_read (i.e., when using a transport layer, such as TLS).
221
+ `sock_read`'s return values are wildly different then the native return values
222
+ and they aim at making far simpler sense.
224
223
 
225
- Also, some internal buffering will might be used in cases where the transport
226
- layer data available is larger then the data requested.
224
+ `sock_read` returns the number of bytes read (0 is a valid return value which
225
+ simply means that no bytes were read from the buffer).
226
+
227
+ On a connection error (NOT EAGAIN or EWOULDBLOCK) or signal interrupt,
228
+ `sock_read` returns -1.
229
+
230
+ Data might be available in the kernel's buffer while it is not available to be
231
+ read using `sock_read` (i.e., when using a transport layer, such as TLS).
227
232
  */
228
233
  ssize_t sock_read(intptr_t uuid, void *buf, size_t count);
229
234
 
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ['Boaz Segev']
10
10
  spec.email = ['Boaz@2be.co.il']
11
11
 
12
- spec.summary = ' Iodine - writing C servers in Ruby.'
13
- spec.description = ' Iodine - writing C servers in Ruby.'
12
+ spec.summary = ' Iodine - leveraging C for Ruby servers.'
13
+ spec.description = ' Iodine - leveraging C for Ruby servers.'
14
14
  spec.homepage = 'https://github.com/boazsegev/iodine'
15
15
  spec.license = 'MIT'
16
16
 
@@ -34,9 +34,10 @@ Gem::Specification.new do |spec|
34
34
  spec.add_dependency 'rack'
35
35
  spec.add_dependency 'rake-compiler'
36
36
 
37
- spec.requirements << 'A Unix based system, i.e.: Linux / OS X / BSD.'
38
- spec.requirements << 'An updated C compiler, with support for C11 (i.e. gcc 4.9 or later).'
37
+ spec.requirements << 'A Unix based system: Linux / macOS / BSD.'
38
+ spec.requirements << 'An updated C compiler.'
39
39
  spec.requirements << 'Ruby >= 2.2.2'
40
+ spec.requirements << 'Ruby >= 2.0.0 is recommended.'
40
41
 
41
42
  spec.add_development_dependency 'bundler', '~> 1.10'
42
43
  spec.add_development_dependency 'rake', '~> 10.0'
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.2.2'.freeze
2
+ VERSION = '0.2.3'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-09-13 00:00:00.000000000 Z
11
+ date: 2016-10-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -80,7 +80,7 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- description: " Iodine - writing C servers in Ruby."
83
+ description: " Iodine - leveraging C for Ruby servers."
84
84
  email:
85
85
  - Boaz@2be.co.il
86
86
  executables:
@@ -96,6 +96,7 @@ files:
96
96
  - LICENSE.txt
97
97
  - README.md
98
98
  - Rakefile
99
+ - SPEC-Websocket-Draft.md
99
100
  - bin/config.ru
100
101
  - bin/console
101
102
  - bin/echo
@@ -194,12 +195,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
195
  - !ruby/object:Gem::Version
195
196
  version: '0'
196
197
  requirements:
197
- - 'A Unix based system, i.e.: Linux / OS X / BSD.'
198
- - An updated C compiler, with support for C11 (i.e. gcc 4.9 or later).
198
+ - 'A Unix based system: Linux / macOS / BSD.'
199
+ - An updated C compiler.
199
200
  - Ruby >= 2.2.2
201
+ - Ruby >= 2.0.0 is recommended.
200
202
  rubyforge_project:
201
203
  rubygems_version: 2.5.1
202
204
  signing_key:
203
205
  specification_version: 4
204
- summary: Iodine - writing C servers in Ruby.
206
+ summary: Iodine - leveraging C for Ruby servers.
205
207
  test_files: []