iodine 0.7.16 → 0.7.17
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 +4 -4
- data/.travis.yml +5 -4
- data/.yardopts +8 -0
- data/CHANGELOG.md +26 -0
- data/LICENSE.txt +1 -1
- data/LIMITS.md +6 -0
- data/README.md +93 -13
- data/{SPEC-Websocket-Draft.md → SPEC-WebSocket-Draft.md} +0 -0
- data/examples/tcp_client.rb +66 -0
- data/examples/x-sendfile.ru +14 -0
- data/exe/iodine +3 -3
- data/ext/iodine/extconf.rb +21 -0
- data/ext/iodine/fio.c +659 -69
- data/ext/iodine/fio.h +350 -95
- data/ext/iodine/fio_cli.c +4 -3
- data/ext/iodine/fio_json_parser.h +1 -1
- data/ext/iodine/fio_siphash.c +13 -11
- data/ext/iodine/fio_siphash.h +6 -3
- data/ext/iodine/fio_tls.h +129 -0
- data/ext/iodine/fio_tls_missing.c +634 -0
- data/ext/iodine/fio_tls_openssl.c +1011 -0
- data/ext/iodine/fio_tmpfile.h +1 -1
- data/ext/iodine/fiobj.h +1 -1
- data/ext/iodine/fiobj_ary.c +1 -1
- data/ext/iodine/fiobj_ary.h +1 -1
- data/ext/iodine/fiobj_data.c +1 -1
- data/ext/iodine/fiobj_data.h +1 -1
- data/ext/iodine/fiobj_hash.c +1 -1
- data/ext/iodine/fiobj_hash.h +1 -1
- data/ext/iodine/fiobj_json.c +18 -16
- data/ext/iodine/fiobj_json.h +1 -1
- data/ext/iodine/fiobj_mustache.c +4 -0
- data/ext/iodine/fiobj_mustache.h +4 -0
- data/ext/iodine/fiobj_numbers.c +1 -1
- data/ext/iodine/fiobj_numbers.h +1 -1
- data/ext/iodine/fiobj_str.c +3 -3
- data/ext/iodine/fiobj_str.h +1 -1
- data/ext/iodine/fiobject.c +1 -1
- data/ext/iodine/fiobject.h +8 -2
- data/ext/iodine/http.c +128 -337
- data/ext/iodine/http.h +11 -18
- data/ext/iodine/http1.c +6 -6
- data/ext/iodine/http1.h +1 -1
- data/ext/iodine/http1_parser.c +1 -1
- data/ext/iodine/http1_parser.h +1 -1
- data/ext/iodine/http_internal.c +10 -8
- data/ext/iodine/http_internal.h +13 -3
- data/ext/iodine/http_mime_parser.h +1 -1
- data/ext/iodine/iodine.c +806 -22
- data/ext/iodine/iodine.h +33 -0
- data/ext/iodine/iodine_connection.c +23 -18
- data/ext/iodine/iodine_http.c +239 -225
- data/ext/iodine/iodine_http.h +4 -1
- data/ext/iodine/iodine_mustache.c +59 -54
- data/ext/iodine/iodine_pubsub.c +1 -1
- data/ext/iodine/iodine_tcp.c +34 -100
- data/ext/iodine/iodine_tcp.h +4 -0
- data/ext/iodine/iodine_tls.c +267 -0
- data/ext/iodine/iodine_tls.h +13 -0
- data/ext/iodine/mustache_parser.h +1 -1
- data/ext/iodine/redis_engine.c +14 -6
- data/ext/iodine/redis_engine.h +1 -1
- data/ext/iodine/resp_parser.h +1 -1
- data/ext/iodine/websocket_parser.h +1 -1
- data/ext/iodine/websockets.c +1 -1
- data/ext/iodine/websockets.h +1 -1
- data/iodine.gemspec +2 -1
- data/lib/iodine.rb +19 -5
- data/lib/iodine/connection.rb +13 -0
- data/lib/iodine/mustache.rb +7 -24
- data/lib/iodine/tls.rb +16 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/rack/handler/iodine.rb +1 -1
- metadata +15 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 909a4b34102fd6e636eab59eccf82deaf315bcc50131ee9f4d476f249df2fe2a
|
4
|
+
data.tar.gz: dded0a5249df8dcd0854198ea6df03661e40b69ddcba35d0bd693c1a6acf6cb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9cdaf95feee9f8de5daedf472a3bd5318931c1aa1b5a5ccc95178e62053ba6e97e021f856ec863e586f22d0b760dee1a66a6204c1cd71267545f164cd9ce378f
|
7
|
+
data.tar.gz: 24d6b5d326fd11cb69beeb215bfc80b4f062d269d3734bc78710654c5444d370346793da2f18c6dba955f53f9754063206399d9de1aef5d76d04299a8ee29aeb
|
data/.travis.yml
CHANGED
@@ -7,9 +7,9 @@ before_install:
|
|
7
7
|
- bundle install
|
8
8
|
rvm:
|
9
9
|
- 2.6.0
|
10
|
-
- 2.5.
|
11
|
-
- 2.4.
|
12
|
-
- 2.3.
|
10
|
+
- 2.5.3
|
11
|
+
- 2.4.5
|
12
|
+
- 2.3.8
|
13
13
|
- 2.2.2
|
14
14
|
notifications:
|
15
15
|
email: false
|
@@ -22,6 +22,7 @@ addons:
|
|
22
22
|
packages:
|
23
23
|
- gcc-4.9
|
24
24
|
- gcc-5
|
25
|
+
- clang
|
25
26
|
script:
|
26
27
|
- echo CFLAGS = $CFLAGS
|
27
28
|
- echo cflags = $cflags
|
@@ -34,5 +35,5 @@ script:
|
|
34
35
|
- find pkg/iodine-*.gem -exec gem install -V {} +
|
35
36
|
- gem uninstall -x iodine
|
36
37
|
- export CFLAGS="-Wall"
|
37
|
-
- export CC=gcc-5
|
38
|
+
- export CC="gcc-5"
|
38
39
|
- find pkg/iodine-*.gem -exec gem install -V {} +
|
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,32 @@ Please notice that this change log contains changes for upcoming releases as wel
|
|
6
6
|
|
7
7
|
## Changes:
|
8
8
|
|
9
|
+
#### Change log v.0.7.17
|
10
|
+
|
11
|
+
**Security**: (`fio`) improved security against hash flooding attacks.
|
12
|
+
|
13
|
+
**Update**: (`iodine`) SSL/TLS support!
|
14
|
+
|
15
|
+
**Update**: (`iodine`) WebSocket client connections are now supported using `Iodine.connect` (both `ws://` and `wss://`)!
|
16
|
+
|
17
|
+
**Deprecation**: (`iodine`) deprecated `DEFAULT_HTTP_ARGS` in favor of `DEFAULT_SETTEINGS`.
|
18
|
+
|
19
|
+
**Deprecation**: (`iodine`) deprecated `Iodine.listen2http` in favor of `Iodine.listen service: :http`.
|
20
|
+
|
21
|
+
**Fix**: (`iodine` / `pubsub`) fixed possible issue with global subscriptions (non-connection bound subscriptions).
|
22
|
+
|
23
|
+
**Fix**: (`Iodine::Mustache`) fixed support for named argument, documentation and loading template from memory (rather than file) when creating a new `Iodine::Mustache` object.
|
24
|
+
|
25
|
+
**Fix**: (`redis`) fixed an issue where destroying the Redis engine and exiting pre-maturely, could cause a segmentation fault during cleanup.
|
26
|
+
|
27
|
+
**Fix**: (`iodine`, `fio`) fixed logging message when listening to Unix Sockets.
|
28
|
+
|
29
|
+
**Fix**: (`iodine`) fixed CLI argument recognition for WebSocket message limits and HTTP header limits. Typos in the CLI argument names prevented the CLI from effecting the default values.
|
30
|
+
|
31
|
+
**Fix**: (`fio`) fixed unaligned memory access in SipHash implementation and added secret randomization for each application restart.
|
32
|
+
|
33
|
+
**Optimization**: (`iodine`) caching common header names to decrease Ruby memory allocations per request.
|
34
|
+
|
9
35
|
#### Change log v.0.7.16
|
10
36
|
|
11
37
|
**Security**: (`fio`) security fixes from the facil.io core library (updated to 0.7.0.beta6).
|
data/LICENSE.txt
CHANGED
data/LIMITS.md
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
I will, at some point, document these... here's the key points:
|
4
4
|
|
5
|
+
## SSL/TLS
|
6
|
+
|
7
|
+
* TLS support requires OpenSSL 1.1.0 and above. On Heroku, this requires `heroku-18`.
|
8
|
+
|
9
|
+
* Iodine supports TLS 1.2 and above (depending on the OpenSSL version used).
|
10
|
+
|
5
11
|
## HTTP limits
|
6
12
|
|
7
13
|
* Uploads are adjustable and limited to ~50Mib by default.
|
data/README.md
CHANGED
@@ -9,17 +9,20 @@
|
|
9
9
|
|
10
10
|
I believe that network concerns should be separated from application concerns - application developers really shouldn't need to worry about the transport layer.
|
11
11
|
|
12
|
-
And I know that these network concerns are more than just about the web server
|
12
|
+
And I know that these network concerns are more than just about the web server, which is why iodine is more than just an HTTP server.
|
13
13
|
|
14
14
|
Iodine is a fast concurrent web server for real-time Ruby applications, but it's also so much more. Iodine includes native support for:
|
15
15
|
|
16
|
-
* WebSockets and EventSource (SSE);
|
16
|
+
* HTTP, WebSockets and EventSource (SSE) Services (server);
|
17
|
+
* WebSocket connections (server / client);
|
17
18
|
* Pub/Sub (with optional Redis Pub/Sub scaling);
|
18
19
|
* Static file service (with automatic `gzip` support for pre-compressed versions);
|
19
20
|
* HTTP/1.1 keep-alive and pipelining;
|
20
21
|
* Asynchronous event scheduling and timers;
|
21
22
|
* Hot Restart (using the USR1 signal);
|
22
|
-
*
|
23
|
+
* TLS 1.2 and above (Requires OpenSSL >= 1.1.0);
|
24
|
+
* TCP/IP server and client connectivity;
|
25
|
+
* Unix Socket server and client connectivity;
|
23
26
|
* Custom protocol authoring;
|
24
27
|
* Optimized Logging to `stderr`.
|
25
28
|
* [Sequel](https://github.com/jeremyevans/sequel) and ActiveRecord forking protection.
|
@@ -210,7 +213,7 @@ Iodine.subscribe(:chat) {|ch, msg| puts msg if Iodine.master? }
|
|
210
213
|
# By default, Pub/Sub performs in process cluster mode.
|
211
214
|
Iodine.workers = 4
|
212
215
|
# # in irb:
|
213
|
-
Iodine.
|
216
|
+
Iodine.listen service: :http, public: "www/public", handler: APP
|
214
217
|
Iodine.start
|
215
218
|
# # or in config.ru
|
216
219
|
run APP
|
@@ -311,6 +314,73 @@ With iodine, there's no need to worry.
|
|
311
314
|
|
312
315
|
Iodine provides built-in `fork` handling for both ActiveRecord and [Sequel](https://github.com/jeremyevans/sequel), in order to protect against these possible errors.
|
313
316
|
|
317
|
+
### Client Support
|
318
|
+
|
319
|
+
Iodine supports raw (TCP/IP and Unix Sockets) client connections as well as WebSocket connections.
|
320
|
+
|
321
|
+
This can be utilized for communicating across micro services or taking advantage of persistent connection APIs such as ActionCable APIs, socket.io APIs etc'.
|
322
|
+
|
323
|
+
Here is an example WebSocket client that will connect to the [WebSocket.org echo test service](https://www.websocket.org/echo.html) and send a number of pre-programmed messages.
|
324
|
+
|
325
|
+
```ruby
|
326
|
+
require 'iodine'
|
327
|
+
|
328
|
+
# The client class
|
329
|
+
class EchoClient
|
330
|
+
|
331
|
+
def on_open(connection)
|
332
|
+
@messages = [ "Hello World!",
|
333
|
+
"I'm alive and sending messages",
|
334
|
+
"I also receive messages",
|
335
|
+
"now that we all know this...",
|
336
|
+
"I can stop.",
|
337
|
+
"Goodbye." ]
|
338
|
+
send_one_message(connection)
|
339
|
+
end
|
340
|
+
|
341
|
+
def on_message(connection, message)
|
342
|
+
puts "Received: #{message}"
|
343
|
+
send_one_message(connection)
|
344
|
+
end
|
345
|
+
|
346
|
+
def on_close(connection)
|
347
|
+
# in this example, we stop iodine once the client is closed
|
348
|
+
puts "* Client closed."
|
349
|
+
Iodine.stop
|
350
|
+
end
|
351
|
+
|
352
|
+
# We use this method to pop messages from the queue and send them
|
353
|
+
#
|
354
|
+
# When the queue is empty, we disconnect the client.
|
355
|
+
def send_one_message(connection)
|
356
|
+
msg = @messages.shift
|
357
|
+
if(msg)
|
358
|
+
connection.write msg
|
359
|
+
else
|
360
|
+
connection.close
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
Iodine.threads = 1
|
366
|
+
Iodine.connect url: "wss://echo.websocket.org", handler: EchoClient.new, ping: 40
|
367
|
+
Iodine.start
|
368
|
+
```
|
369
|
+
|
370
|
+
### TLS 1.2 support
|
371
|
+
|
372
|
+
> Requires OpenSSL >= `1.1.0`. On Heroku, requires `heroku-18`.
|
373
|
+
|
374
|
+
Iodine supports secure connections fore TLS version 1.2 and up (depending on the OpenSSL version).
|
375
|
+
|
376
|
+
A self signed certificate is available using the `-tls` flag from the command-line.
|
377
|
+
|
378
|
+
PEM encoded certificates (which is probably the most common format) can be loaded from the command-line (`-tls-cert` and `-tls-key`) or dynamically (using `Iodine::TLS`).
|
379
|
+
|
380
|
+
The TLS API is simplified but powerful, supporting the ALPN extension and peer verification (which client connections really should leverage).
|
381
|
+
|
382
|
+
When enabling peer verification for server connections (using `Iodine::TLS#trust`), clients will be required to submit a trusted certificate in order to connect to the server.
|
383
|
+
|
314
384
|
### TCP/IP (raw) sockets
|
315
385
|
|
316
386
|
Upgrading to a custom protocol (i.e., in order to implement your own WebSocket protocol with special extensions) is available when neither WebSockets nor SSE connection upgrades were requested. In the following (terminal) example, we'll use an echo server without direct socket echo:
|
@@ -333,7 +403,7 @@ APP = Proc.new do |env|
|
|
333
403
|
end
|
334
404
|
end
|
335
405
|
# # in irb:
|
336
|
-
Iodine.
|
406
|
+
Iodine.listen service: :http, public: "www/public", handler: APP
|
337
407
|
Iodine.threads = 1
|
338
408
|
Iodine.start
|
339
409
|
# # or in config.ru
|
@@ -421,6 +491,12 @@ Iodine is written in C and allows some compile-time customizations, such as:
|
|
421
491
|
|
422
492
|
* `FIO_MAX_SOCK_CAPACITY` - limits iodine's maximum client capacity. Defaults to 131,072 clients.
|
423
493
|
|
494
|
+
* `FIO_USE_RISKY_HASH` - replaces SipHash with RiskyHash for iodine's internal hash maps.
|
495
|
+
|
496
|
+
Since iodine hash maps have internal protection against collisions and hash flooding attacks, it's possible for iodine to leverage RiskyHash, which is faster than SipHash.
|
497
|
+
|
498
|
+
By default, SipHash will be used. This is a community related choice, since the community seems to believe a hash function should protect the hash map rather than it being enough for a hash map implementation to be attack resistance.
|
499
|
+
|
424
500
|
* `HTTP_MAX_HEADER_COUNT` - limits the number of headers the HTTP server will accept before disconnecting a client (security). Defaults to 128 headers (permissive).
|
425
501
|
|
426
502
|
* `HTTP_MAX_HEADER_LENGTH` - limits the number of bytes allowed for a single header (pre-allocated memory per connection + security). Defaults to 8Kb per header line (normal).
|
@@ -431,6 +507,8 @@ Iodine is written in C and allows some compile-time customizations, such as:
|
|
431
507
|
|
432
508
|
* `FIO_LOG_LENGTH_LIMIT` - sets the limit on iodine's logging messages (uses stack memory, so limits must be reasonable. Defaults to 2048.
|
433
509
|
|
510
|
+
* `FIO_TLS_PRINT_SECRET` - if true, the OpenSSL master key will be printed as debug message level log. Use only for testing (with WireShark etc'), never in production! Default: false.
|
511
|
+
|
434
512
|
These options can be used, for example, like so:
|
435
513
|
|
436
514
|
```bash
|
@@ -467,14 +545,14 @@ def run_server
|
|
467
545
|
end
|
468
546
|
```
|
469
547
|
|
470
|
-
In pure Ruby (without using C extensions or Java), it's possible to do the same by using `select`... and although `select` has some issues, it
|
548
|
+
In pure Ruby (without using C extensions or Java), it's possible to do the same by using `select`... and although `select` has some issues, it could work well for lighter loads.
|
471
549
|
|
472
550
|
The server events are fairly fast and fragmented (longer code is fragmented across multiple events), so one thread is enough to run the server including it's static file service and everything...
|
473
551
|
|
474
552
|
...but single threaded mode should probably be avoided.
|
475
553
|
|
476
554
|
|
477
|
-
It's very common that the application's code will run slower and require external resources (i.e., databases, a custom pub/sub service, etc'). This slow code could "starve" the server, which is patiently waiting to run it's tasks on the same thread.
|
555
|
+
It's very common that the application's code will run slower and require external resources (i.e., databases, a custom pub/sub service, etc'). This slow code could "starve" the server, which is patiently waiting to run it's short tasks on the same thread.
|
478
556
|
|
479
557
|
The thread pool is there to help slow user code.
|
480
558
|
|
@@ -505,6 +583,8 @@ Iodine allows custom TCP/IP server authoring, for those cases where we need raw
|
|
505
583
|
Here's a short and sweet echo server - No HTTP, just use `telnet`:
|
506
584
|
|
507
585
|
```ruby
|
586
|
+
USE_TLS = false
|
587
|
+
|
508
588
|
require 'iodine'
|
509
589
|
|
510
590
|
# an echo protocol with asynchronous notifications.
|
@@ -526,8 +606,10 @@ class EchoProtocol
|
|
526
606
|
end
|
527
607
|
end
|
528
608
|
|
609
|
+
tls = USE_TLS ? Iodine::TLS.new("localhost") : nil
|
610
|
+
|
529
611
|
# listen on port 3000 for the echo protocol.
|
530
|
-
Iodine.listen(port: "3000") { EchoProtocol.new }
|
612
|
+
Iodine.listen(port: "3000", tls: tls) { EchoProtocol.new }
|
531
613
|
Iodine.threads = 1
|
532
614
|
Iodine.workers = 1
|
533
615
|
Iodine.start
|
@@ -603,13 +685,11 @@ Iodine.start
|
|
603
685
|
|
604
686
|
### Why not EventMachine?
|
605
687
|
|
606
|
-
|
607
|
-
|
608
|
-
EventMachine also offers some really great optimization features and it was vastly improved upon in the last few years (When I started Iodine, it was far more annoying to work with).
|
688
|
+
EventMachine attempts to give the developer access to the network layer while Iodine attempts to abstract the network layer away and offer the developer a distraction free platform.
|
609
689
|
|
610
|
-
|
690
|
+
You can go ahead and use EventMachine if you like. They're doing amazing work on that one and it's been used a lot in Ruby-land... really, tons of good developers and people on that project.
|
611
691
|
|
612
|
-
|
692
|
+
But why not take iodine out for a spin and see for yourself?
|
613
693
|
|
614
694
|
## Can I contribute?
|
615
695
|
|
File without changes
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#! ruby
|
2
|
+
|
3
|
+
# A raw TCP/IP client example using iodine.
|
4
|
+
#
|
5
|
+
# The client will connect to a remote server and send a simple HTTP/1.1 GET request.
|
6
|
+
#
|
7
|
+
# Once some data was recieved, a delayed closure and shutdown signal will be sent to iodine.
|
8
|
+
|
9
|
+
# use a secure connection?
|
10
|
+
USE_TLS = true
|
11
|
+
|
12
|
+
# remote server details
|
13
|
+
$port = USE_TLS ? 443 : 80
|
14
|
+
$address = "google.com"
|
15
|
+
|
16
|
+
|
17
|
+
# require iodine
|
18
|
+
require 'iodine'
|
19
|
+
|
20
|
+
# Iodine runtime settings
|
21
|
+
Iodine.threads = 1
|
22
|
+
Iodine.workers = 1
|
23
|
+
Iodine.verbosity = 3 # warnings only
|
24
|
+
|
25
|
+
|
26
|
+
# a client callback handler
|
27
|
+
module Client
|
28
|
+
|
29
|
+
def self.on_open(client)
|
30
|
+
# Set a connection timeout
|
31
|
+
client.timeout = 10
|
32
|
+
# subscribe to the chat channel.
|
33
|
+
puts "* Sending request..."
|
34
|
+
client.write "GET / HTTP/1.1\r\nHost: #{$address}\r\n\r\n"
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.on_message(client, data)
|
38
|
+
# publish the data we received
|
39
|
+
STDOUT.write data
|
40
|
+
# close the client after a second... we're not really parsing anything, so it's a guess.
|
41
|
+
Iodine.run_after(1000) { client.close }
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.on_close(client)
|
45
|
+
# stop iodine
|
46
|
+
Iodine.stop
|
47
|
+
puts "Done."
|
48
|
+
end
|
49
|
+
|
50
|
+
# returns the callback object (self).
|
51
|
+
def self.call
|
52
|
+
self
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
if(USE_TLS)
|
59
|
+
tls = Iodine::TLS.new
|
60
|
+
tls.on_protocol("http/1.1") { Client }
|
61
|
+
end
|
62
|
+
# we use can both the `handler` keyword or a block, anything that answers #call.
|
63
|
+
Iodine.connect(address: $address, port: $port, handler: Client, tls: tls)
|
64
|
+
|
65
|
+
# start the iodine reactor
|
66
|
+
Iodine.start
|
@@ -0,0 +1,14 @@
|
|
1
|
+
app = proc do |env|
|
2
|
+
request = Rack::Request.new(env)
|
3
|
+
if request.path_info == '/source'.freeze
|
4
|
+
[200, { 'X-Sendfile' => File.expand_path(__FILE__), 'Content-Type' => 'text/plain'}, []]
|
5
|
+
elsif request.path_info == '/file'.freeze
|
6
|
+
[200, { 'X-Header' => 'This was a Rack::Sendfile response sent as text.' }, File.open(__FILE__)]
|
7
|
+
else
|
8
|
+
[200, { 'Content-Type' => 'text/html',
|
9
|
+
'Content-Length' => request.path_info.length.to_s },
|
10
|
+
[request.path_info]]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
run app
|
data/exe/iodine
CHANGED
@@ -20,7 +20,7 @@ module Iodine
|
|
20
20
|
|
21
21
|
def self.get_app_opts
|
22
22
|
app, opt = nil, nil
|
23
|
-
filename = Iodine::
|
23
|
+
filename = Iodine::DEFAULT_SETTINGS[:filename_]
|
24
24
|
if filename
|
25
25
|
app, opt = try_file filename
|
26
26
|
app, opt = try_file "#{filename}.ru" unless opt
|
@@ -34,7 +34,7 @@ module Iodine
|
|
34
34
|
|
35
35
|
unless opt
|
36
36
|
puts "WARNING: Ruby application not found#{ filename ? " - missing both #{filename} and config.ru" : " - missing config.ru"}."
|
37
|
-
if Iodine::
|
37
|
+
if Iodine::DEFAULT_SETTINGS[:public]
|
38
38
|
puts " Running only static file service."
|
39
39
|
opt = ::Rack::Server::Options.new.parse!([])
|
40
40
|
else
|
@@ -67,7 +67,7 @@ module Iodine
|
|
67
67
|
|
68
68
|
def self.call
|
69
69
|
app, opt = get_app_opts
|
70
|
-
perform_warmup if Iodine::
|
70
|
+
perform_warmup if Iodine::DEFAULT_SETTINGS[:warmup_]
|
71
71
|
Iodine::Rack.run(app, opt)
|
72
72
|
end
|
73
73
|
end
|
data/ext/iodine/extconf.rb
CHANGED
@@ -29,6 +29,27 @@ else
|
|
29
29
|
puts 'using an unknown (old?) compiler... who knows if this will work out... we hope.'
|
30
30
|
end
|
31
31
|
|
32
|
+
|
33
|
+
# Test for OpenSSL version equal to 1.0.0 or greater.
|
34
|
+
unless ENV['NO_SSL']
|
35
|
+
begin
|
36
|
+
require 'openssl'
|
37
|
+
rescue LoadError
|
38
|
+
else
|
39
|
+
if have_library('crypto') && have_library('ssl')
|
40
|
+
puts "Detected OpenSSL library, testing for version."
|
41
|
+
if try_compile("\#include <openssl/ssl.h>\r\#if OPENSSL_VERSION_NUMBER < 0x10100000L\r\#error \"OpenSSL version too small\"\r\#endif\rint main(void) { SSL_library_init(); }")
|
42
|
+
# if ((OpenSSL::OPENSSL_VERSION_NUMBER >> 24) > 16) || (((OpenSSL::OPENSSL_VERSION_NUMBER >> 24) == 16) && (((OpenSSL::OPENSSL_VERSION_NUMBER >> 16) & 255) >= 16))
|
43
|
+
$defs << "-DHAVE_OPENSSL"
|
44
|
+
puts "Confirmed OpenSSL to be version 1.1.0 or above (#{OpenSSL::OPENSSL_LIBRARY_VERSION})...\n* Compiling with HAVE_OPENSSL."
|
45
|
+
else
|
46
|
+
puts "FAILED: OpenSSL version not supported (#{OpenSSL::OPENSSL_LIBRARY_VERSION} is too old)."
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
# $defs << "-DFIO_USE_RISKY_HASH"
|
52
|
+
|
32
53
|
RbConfig::MAKEFILE_CONFIG['CFLAGS'] = $CFLAGS = "-std=c11 -DFIO_PRINT_STATE=0 #{$CFLAGS} #{$CFLAGS == ENV['CFLAGS'] ? "" : ENV['CFLAGS']}"
|
33
54
|
RbConfig::MAKEFILE_CONFIG['CC'] = $CC = ENV['CC'] if ENV['CC']
|
34
55
|
RbConfig::MAKEFILE_CONFIG['CPP'] = $CPP = ENV['CPP'] if ENV['CPP']
|
data/ext/iodine/fio.c
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/* *****************************************************************************
|
2
|
-
Copyright: Boaz Segev, 2018
|
2
|
+
Copyright: Boaz Segev, 2018-2019
|
3
3
|
License: MIT
|
4
4
|
|
5
5
|
Feel free to copy, use and enjoy according to the license provided.
|
@@ -80,6 +80,10 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
80
80
|
#define __thread _Thread_value
|
81
81
|
#endif
|
82
82
|
|
83
|
+
#ifndef FIO_TLS_WEAK
|
84
|
+
#define FIO_TLS_WEAK __attribute__((weak))
|
85
|
+
#endif
|
86
|
+
|
83
87
|
/* *****************************************************************************
|
84
88
|
Event deferring (declarations)
|
85
89
|
***************************************************************************** */
|
@@ -447,6 +451,47 @@ fio_str_info_s fio_peer_addr(intptr_t uuid) {
|
|
447
451
|
.capa = 0};
|
448
452
|
}
|
449
453
|
|
454
|
+
/**
|
455
|
+
* Writes the local machine address (qualified host name) to the buffer.
|
456
|
+
*
|
457
|
+
* Returns the amount of data written (excluding the NUL byte).
|
458
|
+
*
|
459
|
+
* `limit` is the maximum number of bytes in the buffer, including the NUL byte.
|
460
|
+
*
|
461
|
+
* If the returned value == limit - 1, the result might have been truncated.
|
462
|
+
*
|
463
|
+
* If 0 is returned, an erro might have occured (see `errno`) and the contents
|
464
|
+
* of `dest` is undefined.
|
465
|
+
*/
|
466
|
+
size_t fio_local_addr(char *dest, size_t limit) {
|
467
|
+
if (gethostname(dest, limit))
|
468
|
+
return 0;
|
469
|
+
|
470
|
+
struct addrinfo hints, *info;
|
471
|
+
memset(&hints, 0, sizeof hints);
|
472
|
+
hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
|
473
|
+
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
|
474
|
+
hints.ai_flags = AI_CANONNAME; // get cannonical name
|
475
|
+
|
476
|
+
if (getaddrinfo(dest, "http", &hints, &info) != 0)
|
477
|
+
return 0;
|
478
|
+
|
479
|
+
for (struct addrinfo *pos = info; pos; pos = pos->ai_next) {
|
480
|
+
if (pos->ai_canonname) {
|
481
|
+
size_t len = strlen(pos->ai_canonname);
|
482
|
+
if (len >= limit)
|
483
|
+
len = limit - 1;
|
484
|
+
memcpy(dest, pos->ai_canonname, len);
|
485
|
+
dest[len] = 0;
|
486
|
+
freeaddrinfo(info);
|
487
|
+
return len;
|
488
|
+
}
|
489
|
+
}
|
490
|
+
|
491
|
+
freeaddrinfo(info);
|
492
|
+
return 0;
|
493
|
+
}
|
494
|
+
|
450
495
|
/* *****************************************************************************
|
451
496
|
UUID attachments (linking objects to the UUID's lifetime)
|
452
497
|
***************************************************************************** */
|
@@ -1792,10 +1837,11 @@ static size_t fio_poll(void) {
|
|
1792
1837
|
// left in the buffer... not that the edge case matters.
|
1793
1838
|
if (events[i].flags & (EV_EOF | EV_ERROR)) {
|
1794
1839
|
// errors are hendled as disconnections (on_close)
|
1795
|
-
//
|
1796
|
-
//
|
1797
|
-
//
|
1798
|
-
//
|
1840
|
+
// FIO_LOG_DEBUG("%p: %s\n", events[i].udata,
|
1841
|
+
// (events[i].flags & EV_EOF)
|
1842
|
+
// ? "EV_EOF"
|
1843
|
+
// : (events[i].flags & EV_ERROR) ? "EV_ERROR" :
|
1844
|
+
// "WTF?");
|
1799
1845
|
// uuid_data(events[i].udata).open = 0;
|
1800
1846
|
fio_force_close_in_poll(fd2uuid(events[i].udata));
|
1801
1847
|
}
|
@@ -2021,7 +2067,6 @@ static void mock_ping(intptr_t uuid, fio_protocol_s *protocol) {
|
|
2021
2067
|
}
|
2022
2068
|
static void mock_ping2(intptr_t uuid, fio_protocol_s *protocol) {
|
2023
2069
|
(void)protocol;
|
2024
|
-
|
2025
2070
|
touchfd(fio_uuid2fd(uuid));
|
2026
2071
|
if (uuid_data(uuid).timeout == 255)
|
2027
2072
|
return;
|
@@ -2172,6 +2217,8 @@ Forcing / Suspending IO events
|
|
2172
2217
|
***************************************************************************** */
|
2173
2218
|
|
2174
2219
|
void fio_force_event(intptr_t uuid, enum fio_io_event ev) {
|
2220
|
+
if (!uuid_is_valid(uuid))
|
2221
|
+
return;
|
2175
2222
|
switch (ev) {
|
2176
2223
|
case FIO_EVENT_ON_DATA:
|
2177
2224
|
fio_trylock(&uuid_data(uuid).scheduled);
|
@@ -2816,6 +2863,7 @@ void fio_force_close(intptr_t uuid) {
|
|
2816
2863
|
errno = EBADF;
|
2817
2864
|
return;
|
2818
2865
|
}
|
2866
|
+
// FIO_LOG_DEBUG("fio_force_close called for uuid %p", (void *)uuid);
|
2819
2867
|
/* make sure the close marker is set */
|
2820
2868
|
if (!uuid_data(uuid).close)
|
2821
2869
|
uuid_data(uuid).close = 1;
|
@@ -3099,6 +3147,8 @@ static int fio_attach__internal(void *uuid_, void *protocol_) {
|
|
3099
3147
|
}
|
3100
3148
|
prt_meta(protocol) = (protocol_metadata_s){.rsv = 0};
|
3101
3149
|
}
|
3150
|
+
if (!uuid_is_valid(uuid))
|
3151
|
+
goto invalid_uuid_unlocked;
|
3102
3152
|
fio_lock(&uuid_data(uuid).protocol_lock);
|
3103
3153
|
if (!uuid_is_valid(uuid)) {
|
3104
3154
|
goto invalid_uuid;
|
@@ -3125,6 +3175,8 @@ static int fio_attach__internal(void *uuid_, void *protocol_) {
|
|
3125
3175
|
|
3126
3176
|
invalid_uuid:
|
3127
3177
|
fio_unlock(&uuid_data(uuid).protocol_lock);
|
3178
|
+
invalid_uuid_unlocked:
|
3179
|
+
// FIO_LOG_DEBUG("fio_attach failed for invalid uuid %p", (void *)uuid);
|
3128
3180
|
if (protocol)
|
3129
3181
|
fio_defer_push_task(deferred_on_close, (void *)uuid, protocol);
|
3130
3182
|
if (uuid == -1)
|
@@ -3491,6 +3543,7 @@ static void __attribute__((constructor)) fio_lib_init(void) {
|
|
3491
3543
|
#endif
|
3492
3544
|
fio_data->parent = getpid();
|
3493
3545
|
fio_data->connection_count = 0;
|
3546
|
+
fio_mark_time();
|
3494
3547
|
|
3495
3548
|
for (ssize_t i = 0; i < capa; ++i) {
|
3496
3549
|
fio_clear_fd(i, 0);
|
@@ -4211,6 +4264,100 @@ Section Start Marker
|
|
4211
4264
|
|
4212
4265
|
|
4213
4266
|
|
4267
|
+
SSL/TLS Weak Symbols for TLS Support
|
4268
|
+
|
4269
|
+
|
4270
|
+
|
4271
|
+
|
4272
|
+
|
4273
|
+
|
4274
|
+
|
4275
|
+
|
4276
|
+
***************************************************************************** */
|
4277
|
+
|
4278
|
+
/**
|
4279
|
+
* Returns the number of registered ALPN protocol names.
|
4280
|
+
*
|
4281
|
+
* This could be used when deciding if protocol selection should be delegated to
|
4282
|
+
* the ALPN mechanism, or whether a protocol should be immediately assigned.
|
4283
|
+
*
|
4284
|
+
* If no ALPN protocols are registered, zero (0) is returned.
|
4285
|
+
*/
|
4286
|
+
uintptr_t FIO_TLS_WEAK fio_tls_alpn_count(void *tls) {
|
4287
|
+
return 0;
|
4288
|
+
(void)tls;
|
4289
|
+
}
|
4290
|
+
|
4291
|
+
/**
|
4292
|
+
* Establishes an SSL/TLS connection as an SSL/TLS Server, using the specified
|
4293
|
+
* context / settings object.
|
4294
|
+
*
|
4295
|
+
* The `uuid` should be a socket UUID that is already connected to a peer (i.e.,
|
4296
|
+
* the result of `fio_accept`).
|
4297
|
+
*
|
4298
|
+
* The `udata` is an opaque user data pointer that is passed along to the
|
4299
|
+
* protocol selected (if any protocols were added using `fio_tls_alpn_add`).
|
4300
|
+
*/
|
4301
|
+
void FIO_TLS_WEAK fio_tls_accept(intptr_t uuid, void *tls, void *udata) {
|
4302
|
+
FIO_LOG_FATAL("No supported SSL/TLS library available.");
|
4303
|
+
exit(-1);
|
4304
|
+
return;
|
4305
|
+
(void)uuid;
|
4306
|
+
(void)tls;
|
4307
|
+
(void)udata;
|
4308
|
+
}
|
4309
|
+
|
4310
|
+
/**
|
4311
|
+
* Establishes an SSL/TLS connection as an SSL/TLS Client, using the specified
|
4312
|
+
* context / settings object.
|
4313
|
+
*
|
4314
|
+
* The `uuid` should be a socket UUID that is already connected to a peer (i.e.,
|
4315
|
+
* one received by a `fio_connect` specified callback `on_connect`).
|
4316
|
+
*
|
4317
|
+
* The `udata` is an opaque user data pointer that is passed along to the
|
4318
|
+
* protocol selected (if any protocols were added using `fio_tls_alpn_add`).
|
4319
|
+
*/
|
4320
|
+
void FIO_TLS_WEAK fio_tls_connect(intptr_t uuid, void *tls, void *udata) {
|
4321
|
+
FIO_LOG_FATAL("No supported SSL/TLS library available.");
|
4322
|
+
exit(-1);
|
4323
|
+
return;
|
4324
|
+
(void)uuid;
|
4325
|
+
(void)tls;
|
4326
|
+
(void)udata;
|
4327
|
+
}
|
4328
|
+
|
4329
|
+
/**
|
4330
|
+
* Increase the reference count for the TLS object.
|
4331
|
+
*
|
4332
|
+
* Decrease with `fio_tls_destroy`.
|
4333
|
+
*/
|
4334
|
+
void FIO_TLS_WEAK fio_tls_dup(void *tls) {
|
4335
|
+
FIO_LOG_FATAL("No supported SSL/TLS library available.");
|
4336
|
+
exit(-1);
|
4337
|
+
return;
|
4338
|
+
(void)tls;
|
4339
|
+
}
|
4340
|
+
|
4341
|
+
/**
|
4342
|
+
* Destroys the SSL/TLS context / settings object and frees any related
|
4343
|
+
* resources / memory.
|
4344
|
+
*/
|
4345
|
+
void FIO_TLS_WEAK fio_tls_destroy(void *tls) {
|
4346
|
+
FIO_LOG_FATAL("No supported SSL/TLS library available.");
|
4347
|
+
exit(-1);
|
4348
|
+
return;
|
4349
|
+
(void)tls;
|
4350
|
+
}
|
4351
|
+
|
4352
|
+
/* *****************************************************************************
|
4353
|
+
Section Start Marker
|
4354
|
+
|
4355
|
+
|
4356
|
+
|
4357
|
+
|
4358
|
+
|
4359
|
+
|
4360
|
+
|
4214
4361
|
|
4215
4362
|
|
4216
4363
|
|
@@ -4253,10 +4400,13 @@ typedef struct {
|
|
4253
4400
|
char *addr;
|
4254
4401
|
size_t port_len;
|
4255
4402
|
size_t addr_len;
|
4403
|
+
void *tls;
|
4256
4404
|
} fio_listen_protocol_s;
|
4257
4405
|
|
4258
4406
|
static void fio_listen_cleanup_task(void *pr_) {
|
4259
4407
|
fio_listen_protocol_s *pr = pr_;
|
4408
|
+
if (pr->tls)
|
4409
|
+
fio_tls_destroy(pr->tls);
|
4260
4410
|
if (pr->on_finish) {
|
4261
4411
|
pr->on_finish(pr->uuid, pr->udata);
|
4262
4412
|
}
|
@@ -4297,6 +4447,27 @@ static void fio_listen_on_data(intptr_t uuid, fio_protocol_s *pr_) {
|
|
4297
4447
|
}
|
4298
4448
|
}
|
4299
4449
|
|
4450
|
+
static void fio_listen_on_data_tls(intptr_t uuid, fio_protocol_s *pr_) {
|
4451
|
+
fio_listen_protocol_s *pr = (fio_listen_protocol_s *)pr_;
|
4452
|
+
for (int i = 0; i < 4; ++i) {
|
4453
|
+
intptr_t client = fio_accept(uuid);
|
4454
|
+
if (client == -1)
|
4455
|
+
return;
|
4456
|
+
fio_tls_accept(client, pr->tls, pr->udata);
|
4457
|
+
pr->on_open(client, pr->udata);
|
4458
|
+
}
|
4459
|
+
}
|
4460
|
+
|
4461
|
+
static void fio_listen_on_data_tls_alpn(intptr_t uuid, fio_protocol_s *pr_) {
|
4462
|
+
fio_listen_protocol_s *pr = (fio_listen_protocol_s *)pr_;
|
4463
|
+
for (int i = 0; i < 4; ++i) {
|
4464
|
+
intptr_t client = fio_accept(uuid);
|
4465
|
+
if (client == -1)
|
4466
|
+
return;
|
4467
|
+
fio_tls_accept(client, pr->tls, pr->udata);
|
4468
|
+
}
|
4469
|
+
}
|
4470
|
+
|
4300
4471
|
/* stub for editor - unused */
|
4301
4472
|
void fio_listen____(void);
|
4302
4473
|
/**
|
@@ -4306,7 +4477,8 @@ void fio_listen____(void);
|
|
4306
4477
|
*/
|
4307
4478
|
intptr_t fio_listen FIO_IGNORE_MACRO(struct fio_listen_args args) {
|
4308
4479
|
// ...
|
4309
|
-
if (!args.on_open
|
4480
|
+
if ((!args.on_open && (!args.tls || !fio_tls_alpn_count(args.tls))) ||
|
4481
|
+
(!args.address && !args.port)) {
|
4310
4482
|
errno = EINVAL;
|
4311
4483
|
goto error;
|
4312
4484
|
}
|
@@ -4315,8 +4487,19 @@ intptr_t fio_listen FIO_IGNORE_MACRO(struct fio_listen_args args) {
|
|
4315
4487
|
size_t port_len = 0;
|
4316
4488
|
if (args.address)
|
4317
4489
|
addr_len = strlen(args.address);
|
4318
|
-
if (args.port)
|
4490
|
+
if (args.port) {
|
4319
4491
|
port_len = strlen(args.port);
|
4492
|
+
char *tmp = (char *)args.port;
|
4493
|
+
if (!fio_atol(&tmp)) {
|
4494
|
+
port_len = 0;
|
4495
|
+
args.port = NULL;
|
4496
|
+
}
|
4497
|
+
if (*tmp) {
|
4498
|
+
/* port format was invalid, should be only numerals */
|
4499
|
+
errno = EINVAL;
|
4500
|
+
goto error;
|
4501
|
+
}
|
4502
|
+
}
|
4320
4503
|
const intptr_t uuid = fio_socket(args.address, args.port, 1);
|
4321
4504
|
if (uuid == -1)
|
4322
4505
|
goto error;
|
@@ -4324,18 +4507,26 @@ intptr_t fio_listen FIO_IGNORE_MACRO(struct fio_listen_args args) {
|
|
4324
4507
|
fio_listen_protocol_s *pr = malloc(sizeof(*pr) + addr_len + port_len +
|
4325
4508
|
((addr_len + port_len) ? 2 : 0));
|
4326
4509
|
FIO_ASSERT_ALLOC(pr);
|
4510
|
+
|
4511
|
+
if (args.tls)
|
4512
|
+
fio_tls_dup(args.tls);
|
4513
|
+
|
4327
4514
|
*pr = (fio_listen_protocol_s){
|
4328
4515
|
.pr =
|
4329
4516
|
{
|
4330
4517
|
.on_close = fio_listen_on_close,
|
4331
4518
|
.ping = mock_ping_eternal,
|
4332
|
-
.on_data =
|
4519
|
+
.on_data = (args.tls ? (fio_tls_alpn_count(args.tls)
|
4520
|
+
? fio_listen_on_data_tls_alpn
|
4521
|
+
: fio_listen_on_data_tls)
|
4522
|
+
: fio_listen_on_data),
|
4333
4523
|
},
|
4334
4524
|
.uuid = uuid,
|
4335
4525
|
.udata = args.udata,
|
4336
4526
|
.on_open = args.on_open,
|
4337
4527
|
.on_start = args.on_start,
|
4338
4528
|
.on_finish = args.on_finish,
|
4529
|
+
.tls = args.tls,
|
4339
4530
|
.addr_len = addr_len,
|
4340
4531
|
.port_len = port_len,
|
4341
4532
|
.addr = (char *)(pr + 1),
|
@@ -4411,6 +4602,7 @@ typedef struct {
|
|
4411
4602
|
fio_protocol_s pr;
|
4412
4603
|
intptr_t uuid;
|
4413
4604
|
void *udata;
|
4605
|
+
void *tls;
|
4414
4606
|
void (*on_connect)(intptr_t uuid, void *udata);
|
4415
4607
|
void (*on_fail)(intptr_t uuid, void *udata);
|
4416
4608
|
} fio_connect_protocol_s;
|
@@ -4419,12 +4611,16 @@ static void fio_connect_on_close(intptr_t uuid, fio_protocol_s *pr_) {
|
|
4419
4611
|
fio_connect_protocol_s *pr = (fio_connect_protocol_s *)pr_;
|
4420
4612
|
if (pr->on_fail)
|
4421
4613
|
pr->on_fail(uuid, pr->udata);
|
4614
|
+
if (pr->tls)
|
4615
|
+
fio_tls_destroy(pr->tls);
|
4422
4616
|
fio_free(pr);
|
4423
4617
|
(void)uuid;
|
4424
4618
|
}
|
4425
4619
|
|
4426
4620
|
static void fio_connect_on_ready(intptr_t uuid, fio_protocol_s *pr_) {
|
4427
4621
|
fio_connect_protocol_s *pr = (fio_connect_protocol_s *)pr_;
|
4622
|
+
if (pr->pr.on_ready == mock_on_ev)
|
4623
|
+
return; /* Don't call on_connect more than once */
|
4428
4624
|
pr->pr.on_ready = mock_on_ev;
|
4429
4625
|
pr->on_fail = NULL;
|
4430
4626
|
pr->on_connect(uuid, pr->udata);
|
@@ -4432,8 +4628,35 @@ static void fio_connect_on_ready(intptr_t uuid, fio_protocol_s *pr_) {
|
|
4432
4628
|
(void)uuid;
|
4433
4629
|
}
|
4434
4630
|
|
4631
|
+
static void fio_connect_on_ready_tls(intptr_t uuid, fio_protocol_s *pr_) {
|
4632
|
+
fio_connect_protocol_s *pr = (fio_connect_protocol_s *)pr_;
|
4633
|
+
if (pr->pr.on_ready == mock_on_ev)
|
4634
|
+
return; /* Don't call on_connect more than once */
|
4635
|
+
pr->pr.on_ready = mock_on_ev;
|
4636
|
+
pr->on_fail = NULL;
|
4637
|
+
fio_tls_connect(uuid, pr->tls, pr->udata);
|
4638
|
+
pr->on_connect(uuid, pr->udata);
|
4639
|
+
fio_poll_add(fio_uuid2fd(uuid));
|
4640
|
+
(void)uuid;
|
4641
|
+
}
|
4642
|
+
|
4643
|
+
static void fio_connect_on_ready_tls_alpn(intptr_t uuid, fio_protocol_s *pr_) {
|
4644
|
+
fio_connect_protocol_s *pr = (fio_connect_protocol_s *)pr_;
|
4645
|
+
if (pr->pr.on_ready == mock_on_ev)
|
4646
|
+
return; /* Don't call on_connect more than once */
|
4647
|
+
pr->pr.on_ready = mock_on_ev;
|
4648
|
+
pr->on_fail = NULL;
|
4649
|
+
fio_tls_connect(uuid, pr->tls, pr->udata);
|
4650
|
+
fio_poll_add(fio_uuid2fd(uuid));
|
4651
|
+
(void)uuid;
|
4652
|
+
}
|
4653
|
+
|
4654
|
+
/* stub for sublime text function navigation */
|
4655
|
+
intptr_t fio_connect___(struct fio_connect_args args);
|
4656
|
+
|
4435
4657
|
intptr_t fio_connect FIO_IGNORE_MACRO(struct fio_connect_args args) {
|
4436
|
-
if (!args.on_connect
|
4658
|
+
if ((!args.on_connect && (!args.tls || !fio_tls_alpn_count(args.tls))) ||
|
4659
|
+
(!args.address && !args.port)) {
|
4437
4660
|
errno = EINVAL;
|
4438
4661
|
goto error;
|
4439
4662
|
}
|
@@ -4444,13 +4667,21 @@ intptr_t fio_connect FIO_IGNORE_MACRO(struct fio_connect_args args) {
|
|
4444
4667
|
|
4445
4668
|
fio_connect_protocol_s *pr = fio_malloc(sizeof(*pr));
|
4446
4669
|
FIO_ASSERT_ALLOC(pr);
|
4670
|
+
|
4671
|
+
if (args.tls)
|
4672
|
+
fio_tls_dup(args.tls);
|
4673
|
+
|
4447
4674
|
*pr = (fio_connect_protocol_s){
|
4448
4675
|
.pr =
|
4449
4676
|
{
|
4450
|
-
.on_ready =
|
4677
|
+
.on_ready = (args.tls ? (fio_tls_alpn_count(args.tls)
|
4678
|
+
? fio_connect_on_ready_tls_alpn
|
4679
|
+
: fio_connect_on_ready_tls)
|
4680
|
+
: fio_connect_on_ready),
|
4451
4681
|
.on_close = fio_connect_on_close,
|
4452
4682
|
},
|
4453
4683
|
.uuid = uuid,
|
4684
|
+
.tls = args.tls,
|
4454
4685
|
.udata = args.udata,
|
4455
4686
|
.on_connect = args.on_connect,
|
4456
4687
|
.on_fail = args.on_fail,
|
@@ -4463,6 +4694,253 @@ error:
|
|
4463
4694
|
return -1;
|
4464
4695
|
}
|
4465
4696
|
|
4697
|
+
/* *****************************************************************************
|
4698
|
+
URL address parsing
|
4699
|
+
***************************************************************************** */
|
4700
|
+
|
4701
|
+
/**
|
4702
|
+
* Parses the URI returning it's components and their lengths (no decoding
|
4703
|
+
* performed, doesn't accept decoded URIs).
|
4704
|
+
*
|
4705
|
+
* The returned string are NOT NUL terminated, they are merely locations within
|
4706
|
+
* the original string.
|
4707
|
+
*
|
4708
|
+
* This function expects any of the following formats:
|
4709
|
+
*
|
4710
|
+
* * `/complete_path?query#target`
|
4711
|
+
*
|
4712
|
+
* i.e.: /index.html?page=1#list
|
4713
|
+
*
|
4714
|
+
* * `host:port/complete_path?query#target`
|
4715
|
+
*
|
4716
|
+
* i.e.:
|
4717
|
+
* example.com/index.html
|
4718
|
+
* example.com:8080/index.html
|
4719
|
+
*
|
4720
|
+
* * `schema://user:password@host:port/path?query#target`
|
4721
|
+
*
|
4722
|
+
* i.e.: http://example.com/index.html?page=1#list
|
4723
|
+
*
|
4724
|
+
* Invalid formats might produce unexpected results. No error testing performed.
|
4725
|
+
*/
|
4726
|
+
fio_url_s fio_url_parse(const char *url, size_t length) {
|
4727
|
+
/*
|
4728
|
+
Intention:
|
4729
|
+
[schema://][user[:]][password[@]][host.com[:/]][:port/][/path][?quary][#target]
|
4730
|
+
*/
|
4731
|
+
const char *end = url + length;
|
4732
|
+
const char *pos = url;
|
4733
|
+
fio_url_s r = {.scheme = {.data = (char *)url}};
|
4734
|
+
if (length == 0) {
|
4735
|
+
goto finish;
|
4736
|
+
}
|
4737
|
+
|
4738
|
+
if (pos[0] == '/') {
|
4739
|
+
/* start at path */
|
4740
|
+
goto start_path;
|
4741
|
+
}
|
4742
|
+
|
4743
|
+
while (pos < end && pos[0] != ':' && pos[0] != '/' && pos[0] != '@' &&
|
4744
|
+
pos[0] != '#' && pos[0] != '?')
|
4745
|
+
++pos;
|
4746
|
+
|
4747
|
+
if (pos == end) {
|
4748
|
+
/* was only host (path starts with '/') */
|
4749
|
+
r.host = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4750
|
+
goto finish;
|
4751
|
+
}
|
4752
|
+
switch (pos[0]) {
|
4753
|
+
case '@':
|
4754
|
+
/* username@[host] */
|
4755
|
+
r.user = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4756
|
+
++pos;
|
4757
|
+
goto start_host;
|
4758
|
+
case '/':
|
4759
|
+
/* host[/path] */
|
4760
|
+
r.host = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4761
|
+
goto start_path;
|
4762
|
+
case '?':
|
4763
|
+
/* host?[query] */
|
4764
|
+
r.host = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4765
|
+
++pos;
|
4766
|
+
goto start_query;
|
4767
|
+
case '#':
|
4768
|
+
/* host#[target] */
|
4769
|
+
r.host = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4770
|
+
++pos;
|
4771
|
+
goto start_target;
|
4772
|
+
case ':':
|
4773
|
+
if (pos + 2 <= end && pos[1] == '/' && pos[2] == '/') {
|
4774
|
+
/* scheme:// */
|
4775
|
+
r.scheme.len = pos - url;
|
4776
|
+
pos += 3;
|
4777
|
+
} else {
|
4778
|
+
/* username:[password] OR */
|
4779
|
+
/* host:[port] */
|
4780
|
+
r.user = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4781
|
+
++pos;
|
4782
|
+
goto start_password;
|
4783
|
+
}
|
4784
|
+
break;
|
4785
|
+
}
|
4786
|
+
|
4787
|
+
// start_username:
|
4788
|
+
url = pos;
|
4789
|
+
while (pos < end && pos[0] != ':' && pos[0] != '/' && pos[0] != '@'
|
4790
|
+
/* && pos[0] != '#' && pos[0] != '?' */)
|
4791
|
+
++pos;
|
4792
|
+
|
4793
|
+
if (pos >= end) { /* scheme://host */
|
4794
|
+
r.host = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4795
|
+
goto finish;
|
4796
|
+
}
|
4797
|
+
|
4798
|
+
switch (pos[0]) {
|
4799
|
+
case '/':
|
4800
|
+
/* scheme://host[/path] */
|
4801
|
+
r.host = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4802
|
+
goto start_path;
|
4803
|
+
case '@':
|
4804
|
+
/* scheme://username@[host]... */
|
4805
|
+
r.user = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4806
|
+
++pos;
|
4807
|
+
goto start_host;
|
4808
|
+
case ':':
|
4809
|
+
/* scheme://username:[password]@[host]... OR */
|
4810
|
+
/* scheme://host:[port][/...] */
|
4811
|
+
r.user = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4812
|
+
++pos;
|
4813
|
+
break;
|
4814
|
+
}
|
4815
|
+
|
4816
|
+
start_password:
|
4817
|
+
url = pos;
|
4818
|
+
while (pos < end && pos[0] != '/' && pos[0] != '@')
|
4819
|
+
++pos;
|
4820
|
+
|
4821
|
+
if (pos >= end) {
|
4822
|
+
/* was host:port */
|
4823
|
+
r.port = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4824
|
+
r.host = r.user;
|
4825
|
+
r.user.len = 0;
|
4826
|
+
goto finish;
|
4827
|
+
;
|
4828
|
+
}
|
4829
|
+
|
4830
|
+
switch (pos[0]) {
|
4831
|
+
case '/':
|
4832
|
+
r.port = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4833
|
+
r.host = r.user;
|
4834
|
+
r.user.len = 0;
|
4835
|
+
goto start_path;
|
4836
|
+
case '@':
|
4837
|
+
r.password = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4838
|
+
++pos;
|
4839
|
+
break;
|
4840
|
+
}
|
4841
|
+
|
4842
|
+
start_host:
|
4843
|
+
url = pos;
|
4844
|
+
while (pos < end && pos[0] != '/' && pos[0] != ':' && pos[0] != '#' &&
|
4845
|
+
pos[0] != '?')
|
4846
|
+
++pos;
|
4847
|
+
|
4848
|
+
r.host = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4849
|
+
if (pos >= end) {
|
4850
|
+
goto finish;
|
4851
|
+
}
|
4852
|
+
switch (pos[0]) {
|
4853
|
+
case '/':
|
4854
|
+
/* scheme://[...@]host[/path] */
|
4855
|
+
goto start_path;
|
4856
|
+
case '?':
|
4857
|
+
/* scheme://[...@]host?[query] (bad)*/
|
4858
|
+
++pos;
|
4859
|
+
goto start_query;
|
4860
|
+
case '#':
|
4861
|
+
/* scheme://[...@]host#[target] (bad)*/
|
4862
|
+
++pos;
|
4863
|
+
goto start_target;
|
4864
|
+
// case ':':
|
4865
|
+
/* scheme://[...@]host:[port] */
|
4866
|
+
}
|
4867
|
+
++pos;
|
4868
|
+
|
4869
|
+
// start_port:
|
4870
|
+
url = pos;
|
4871
|
+
while (pos < end && pos[0] != '/' && pos[0] != '#' && pos[0] != '?')
|
4872
|
+
++pos;
|
4873
|
+
|
4874
|
+
r.port = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4875
|
+
|
4876
|
+
if (pos >= end) {
|
4877
|
+
/* scheme://[...@]host:port */
|
4878
|
+
goto finish;
|
4879
|
+
}
|
4880
|
+
switch (pos[0]) {
|
4881
|
+
case '?':
|
4882
|
+
/* scheme://[...@]host:port?[query] (bad)*/
|
4883
|
+
++pos;
|
4884
|
+
goto start_query;
|
4885
|
+
case '#':
|
4886
|
+
/* scheme://[...@]host:port#[target] (bad)*/
|
4887
|
+
++pos;
|
4888
|
+
goto start_target;
|
4889
|
+
// case '/':
|
4890
|
+
/* scheme://[...@]host:port[/path] */
|
4891
|
+
}
|
4892
|
+
|
4893
|
+
start_path:
|
4894
|
+
url = pos;
|
4895
|
+
while (pos < end && pos[0] != '#' && pos[0] != '?')
|
4896
|
+
++pos;
|
4897
|
+
|
4898
|
+
r.path = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4899
|
+
|
4900
|
+
if (pos >= end) {
|
4901
|
+
goto finish;
|
4902
|
+
}
|
4903
|
+
++pos;
|
4904
|
+
if (pos[-1] == '#')
|
4905
|
+
goto start_target;
|
4906
|
+
|
4907
|
+
start_query:
|
4908
|
+
url = pos;
|
4909
|
+
while (pos < end && pos[0] != '#')
|
4910
|
+
++pos;
|
4911
|
+
|
4912
|
+
r.query = (fio_str_info_s){.data = (char *)url, .len = pos - url};
|
4913
|
+
++pos;
|
4914
|
+
|
4915
|
+
if (pos >= end)
|
4916
|
+
goto finish;
|
4917
|
+
|
4918
|
+
start_target:
|
4919
|
+
r.target = (fio_str_info_s){.data = (char *)pos, .len = end - pos};
|
4920
|
+
|
4921
|
+
finish:
|
4922
|
+
|
4923
|
+
/* set any empty values to NULL */
|
4924
|
+
if (!r.scheme.len)
|
4925
|
+
r.scheme.data = NULL;
|
4926
|
+
if (!r.user.len)
|
4927
|
+
r.user.data = NULL;
|
4928
|
+
if (!r.password.len)
|
4929
|
+
r.password.data = NULL;
|
4930
|
+
if (!r.host.len)
|
4931
|
+
r.host.data = NULL;
|
4932
|
+
if (!r.port.len)
|
4933
|
+
r.port.data = NULL;
|
4934
|
+
if (!r.path.len)
|
4935
|
+
r.path.data = NULL;
|
4936
|
+
if (!r.query.len)
|
4937
|
+
r.query.data = NULL;
|
4938
|
+
if (!r.target.len)
|
4939
|
+
r.target.data = NULL;
|
4940
|
+
|
4941
|
+
return r;
|
4942
|
+
}
|
4943
|
+
|
4466
4944
|
/* *****************************************************************************
|
4467
4945
|
Section Start Marker
|
4468
4946
|
|
@@ -4894,7 +5372,8 @@ static channel_s *fio_channel_dup_lock(fio_str_info_s name) {
|
|
4894
5372
|
.parent = &fio_postoffice.pubsub,
|
4895
5373
|
.ref = 8, /* avoid freeing stack memory */
|
4896
5374
|
};
|
4897
|
-
uint64_t hashed_name =
|
5375
|
+
uint64_t hashed_name = FIO_HASH_FN(
|
5376
|
+
name.data, name.len, &fio_postoffice.pubsub, &fio_postoffice.pubsub);
|
4898
5377
|
channel_s *ch_p =
|
4899
5378
|
fio_filter_dup_lock_internal(&ch, hashed_name, &fio_postoffice.pubsub);
|
4900
5379
|
if (fio_ls_embd_is_empty(&ch_p->subscriptions)) {
|
@@ -4913,7 +5392,8 @@ static channel_s *fio_channel_match_dup_lock(fio_str_info_s name,
|
|
4913
5392
|
.match = match,
|
4914
5393
|
.ref = 8, /* avoid freeing stack memory */
|
4915
5394
|
};
|
4916
|
-
uint64_t hashed_name =
|
5395
|
+
uint64_t hashed_name = FIO_HASH_FN(
|
5396
|
+
name.data, name.len, &fio_postoffice.pubsub, &fio_postoffice.pubsub);
|
4917
5397
|
channel_s *ch_p =
|
4918
5398
|
fio_filter_dup_lock_internal(&ch, hashed_name, &fio_postoffice.patterns);
|
4919
5399
|
if (fio_ls_embd_is_empty(&ch_p->subscriptions)) {
|
@@ -4980,7 +5460,8 @@ void fio_unsubscribe(subscription_s *s) {
|
|
4980
5460
|
/* check if channel is done for */
|
4981
5461
|
if (fio_ls_embd_is_empty(&ch->subscriptions)) {
|
4982
5462
|
fio_collection_s *c = ch->parent;
|
4983
|
-
uint64_t hashed =
|
5463
|
+
uint64_t hashed = FIO_HASH_FN(
|
5464
|
+
ch->name, ch->name_len, &fio_postoffice.pubsub, &fio_postoffice.pubsub);
|
4984
5465
|
/* lock collection */
|
4985
5466
|
fio_lock(&c->lock);
|
4986
5467
|
/* test again within lock */
|
@@ -5179,7 +5660,8 @@ static channel_s *fio_filter_find_dup(uint32_t filter) {
|
|
5179
5660
|
/** Finds a pubsub channel, increasing it's reference count if it exists. */
|
5180
5661
|
static channel_s *fio_channel_find_dup(fio_str_info_s name) {
|
5181
5662
|
channel_s tmp = {.name = name.data, .name_len = name.len};
|
5182
|
-
uint64_t hashed_name =
|
5663
|
+
uint64_t hashed_name = FIO_HASH_FN(
|
5664
|
+
name.data, name.len, &fio_postoffice.pubsub, &fio_postoffice.pubsub);
|
5183
5665
|
channel_s *ch =
|
5184
5666
|
fio_channel_find_dup_internal(&tmp, hashed_name, &fio_postoffice.pubsub);
|
5185
5667
|
return ch;
|
@@ -5645,7 +6127,11 @@ static void fio_cluster_server_handler(struct cluster_pr_s *pr) {
|
|
5645
6127
|
fio_str_s tmp = FIO_STR_INIT_EXISTING(
|
5646
6128
|
pr->msg->channel.data, pr->msg->channel.len, 0); // don't free
|
5647
6129
|
fio_lock(&pr->lock);
|
5648
|
-
fio_sub_hash_insert(&pr->pubsub,
|
6130
|
+
fio_sub_hash_insert(&pr->pubsub,
|
6131
|
+
FIO_HASH_FN(pr->msg->channel.data, pr->msg->channel.len,
|
6132
|
+
&fio_postoffice.pubsub,
|
6133
|
+
&fio_postoffice.pubsub),
|
6134
|
+
tmp, s, NULL);
|
5649
6135
|
fio_unlock(&pr->lock);
|
5650
6136
|
break;
|
5651
6137
|
}
|
@@ -5653,7 +6139,11 @@ static void fio_cluster_server_handler(struct cluster_pr_s *pr) {
|
|
5653
6139
|
fio_str_s tmp = FIO_STR_INIT_EXISTING(
|
5654
6140
|
pr->msg->channel.data, pr->msg->channel.len, 0); // don't free
|
5655
6141
|
fio_lock(&pr->lock);
|
5656
|
-
fio_sub_hash_remove(&pr->pubsub,
|
6142
|
+
fio_sub_hash_remove(&pr->pubsub,
|
6143
|
+
FIO_HASH_FN(pr->msg->channel.data, pr->msg->channel.len,
|
6144
|
+
&fio_postoffice.pubsub,
|
6145
|
+
&fio_postoffice.pubsub),
|
6146
|
+
tmp, NULL);
|
5657
6147
|
fio_unlock(&pr->lock);
|
5658
6148
|
break;
|
5659
6149
|
}
|
@@ -5666,7 +6156,11 @@ static void fio_cluster_server_handler(struct cluster_pr_s *pr) {
|
|
5666
6156
|
fio_str_s tmp = FIO_STR_INIT_EXISTING(
|
5667
6157
|
pr->msg->channel.data, pr->msg->channel.len, 0); // don't free
|
5668
6158
|
fio_lock(&pr->lock);
|
5669
|
-
fio_sub_hash_insert(&pr->patterns,
|
6159
|
+
fio_sub_hash_insert(&pr->patterns,
|
6160
|
+
FIO_HASH_FN(pr->msg->channel.data, pr->msg->channel.len,
|
6161
|
+
&fio_postoffice.pubsub,
|
6162
|
+
&fio_postoffice.pubsub),
|
6163
|
+
tmp, s, NULL);
|
5670
6164
|
fio_unlock(&pr->lock);
|
5671
6165
|
break;
|
5672
6166
|
}
|
@@ -5675,7 +6169,11 @@ static void fio_cluster_server_handler(struct cluster_pr_s *pr) {
|
|
5675
6169
|
fio_str_s tmp = FIO_STR_INIT_EXISTING(
|
5676
6170
|
pr->msg->channel.data, pr->msg->channel.len, 0); // don't free
|
5677
6171
|
fio_lock(&pr->lock);
|
5678
|
-
fio_sub_hash_remove(&pr->patterns,
|
6172
|
+
fio_sub_hash_remove(&pr->patterns,
|
6173
|
+
FIO_HASH_FN(pr->msg->channel.data, pr->msg->channel.len,
|
6174
|
+
&fio_postoffice.pubsub,
|
6175
|
+
&fio_postoffice.pubsub),
|
6176
|
+
tmp, NULL);
|
5679
6177
|
fio_unlock(&pr->lock);
|
5680
6178
|
break;
|
5681
6179
|
}
|
@@ -7005,23 +7503,6 @@ Section Start Marker
|
|
7005
7503
|
|
7006
7504
|
***************************************************************************** */
|
7007
7505
|
|
7008
|
-
/** 32Bit left rotation, inlined. */
|
7009
|
-
#define fio_lrot32(i, bits) \
|
7010
|
-
(((uint32_t)(i) << (bits)) | ((uint32_t)(i) >> (32 - (bits))))
|
7011
|
-
/** 32Bit right rotation, inlined. */
|
7012
|
-
#define fio_rrot32(i, bits) \
|
7013
|
-
(((uint32_t)(i) >> (bits)) | ((uint32_t)(i) << (32 - (bits))))
|
7014
|
-
/** 64Bit left rotation, inlined. */
|
7015
|
-
#define fio_lrot64(i, bits) \
|
7016
|
-
(((uint64_t)(i) << (bits)) | ((uint64_t)(i) >> (64 - (bits))))
|
7017
|
-
/** 64Bit right rotation, inlined. */
|
7018
|
-
#define fio_rrot64(i, bits) \
|
7019
|
-
(((uint64_t)(i) >> (bits)) | ((uint64_t)(i) << (64 - (bits))))
|
7020
|
-
/** unknown size element - left rotation, inlined. */
|
7021
|
-
#define fio_lrot(i, bits) (((i) << (bits)) | ((i) >> (sizeof((i)) - (bits))))
|
7022
|
-
/** unknown size element - right rotation, inlined. */
|
7023
|
-
#define fio_rrot(i, bits) (((i) >> (bits)) | ((i) << (sizeof((i)) - (bits))))
|
7024
|
-
|
7025
7506
|
/* *****************************************************************************
|
7026
7507
|
SipHash
|
7027
7508
|
***************************************************************************** */
|
@@ -7033,13 +7514,13 @@ SipHash
|
|
7033
7514
|
#endif
|
7034
7515
|
|
7035
7516
|
static inline uint64_t fio_siphash_xy(const void *data, size_t len, size_t x,
|
7036
|
-
size_t y) {
|
7517
|
+
size_t y, uint64_t key1, uint64_t key2) {
|
7037
7518
|
/* initialize the 4 words */
|
7038
|
-
uint64_t v0 = (0x0706050403020100ULL ^ 0x736f6d6570736575ULL);
|
7039
|
-
uint64_t v1 = (0x0f0e0d0c0b0a0908ULL ^ 0x646f72616e646f6dULL);
|
7040
|
-
uint64_t v2 = (0x0706050403020100ULL ^ 0x6c7967656e657261ULL);
|
7041
|
-
uint64_t v3 = (0x0f0e0d0c0b0a0908ULL ^ 0x7465646279746573ULL);
|
7042
|
-
const
|
7519
|
+
uint64_t v0 = (0x0706050403020100ULL ^ 0x736f6d6570736575ULL) ^ key1;
|
7520
|
+
uint64_t v1 = (0x0f0e0d0c0b0a0908ULL ^ 0x646f72616e646f6dULL) ^ key2;
|
7521
|
+
uint64_t v2 = (0x0706050403020100ULL ^ 0x6c7967656e657261ULL) ^ key1;
|
7522
|
+
uint64_t v3 = (0x0f0e0d0c0b0a0908ULL ^ 0x7465646279746573ULL) ^ key2;
|
7523
|
+
const uint8_t *w8 = data;
|
7043
7524
|
uint8_t len_mod = len & 255;
|
7044
7525
|
union {
|
7045
7526
|
uint64_t i;
|
@@ -7061,19 +7542,18 @@ static inline uint64_t fio_siphash_xy(const void *data, size_t len, size_t x,
|
|
7061
7542
|
} while (0);
|
7062
7543
|
|
7063
7544
|
while (len >= 8) {
|
7064
|
-
word.i = sip_local64(
|
7545
|
+
word.i = sip_local64(fio_str2u64(w8));
|
7065
7546
|
v3 ^= word.i;
|
7066
7547
|
/* Sip Rounds */
|
7067
7548
|
for (size_t i = 0; i < x; ++i) {
|
7068
7549
|
hash_map_SipRound;
|
7069
7550
|
}
|
7070
7551
|
v0 ^= word.i;
|
7071
|
-
|
7552
|
+
w8 += 8;
|
7072
7553
|
len -= 8;
|
7073
7554
|
}
|
7074
7555
|
word.i = 0;
|
7075
7556
|
uint8_t *pos = word.str;
|
7076
|
-
uint8_t *w8 = (void *)w64;
|
7077
7557
|
switch (len) { /* fallthrough is intentional */
|
7078
7558
|
case 7:
|
7079
7559
|
pos[6] = w8[6];
|
@@ -7119,12 +7599,14 @@ static inline uint64_t fio_siphash_xy(const void *data, size_t len, size_t x,
|
|
7119
7599
|
return v0;
|
7120
7600
|
}
|
7121
7601
|
|
7122
|
-
uint64_t fio_siphash24(const void *data, size_t len
|
7123
|
-
|
7602
|
+
uint64_t fio_siphash24(const void *data, size_t len, uint64_t key1,
|
7603
|
+
uint64_t key2) {
|
7604
|
+
return fio_siphash_xy(data, len, 2, 4, key1, key2);
|
7124
7605
|
}
|
7125
7606
|
|
7126
|
-
uint64_t fio_siphash13(const void *data, size_t len
|
7127
|
-
|
7607
|
+
uint64_t fio_siphash13(const void *data, size_t len, uint64_t key1,
|
7608
|
+
uint64_t key2) {
|
7609
|
+
return fio_siphash_xy(data, len, 1, 3, key1, key2);
|
7128
7610
|
}
|
7129
7611
|
|
7130
7612
|
/* *****************************************************************************
|
@@ -9122,7 +9604,7 @@ FIO_FUNC void fio_ary_test(void) {
|
|
9122
9604
|
Set data-structure Testing
|
9123
9605
|
***************************************************************************** */
|
9124
9606
|
|
9125
|
-
#define
|
9607
|
+
#define FIO_SET_TEST_COUNT 524288UL
|
9126
9608
|
|
9127
9609
|
#define FIO_SET_NAME fio_set_test
|
9128
9610
|
#define FIO_SET_OBJ_TYPE uintptr_t
|
@@ -9133,13 +9615,18 @@ Set data-structure Testing
|
|
9133
9615
|
#define FIO_SET_OBJ_TYPE uintptr_t
|
9134
9616
|
#include <fio.h>
|
9135
9617
|
|
9618
|
+
#define FIO_SET_NAME fio_set_attack
|
9619
|
+
#define FIO_SET_OBJ_COMPARE(a, b) ((a) == (b))
|
9620
|
+
#define FIO_SET_OBJ_TYPE uintptr_t
|
9621
|
+
#include <fio.h>
|
9622
|
+
|
9136
9623
|
FIO_FUNC void fio_set_test(void) {
|
9137
9624
|
fio_set_test_s s = FIO_SET_INIT;
|
9138
9625
|
fio_hash_test_s h = FIO_SET_INIT;
|
9139
9626
|
fprintf(
|
9140
9627
|
stderr,
|
9141
9628
|
"=== Testing Core ordered Set (re-including fio.h with FIO_SET_NAME)\n");
|
9142
|
-
fprintf(stderr, "* Inserting %lu items\n",
|
9629
|
+
fprintf(stderr, "* Inserting %lu items\n", FIO_SET_TEST_COUNT);
|
9143
9630
|
|
9144
9631
|
FIO_ASSERT(fio_set_test_count(&s) == 0, "empty set should have zero objects");
|
9145
9632
|
FIO_ASSERT(fio_set_test_capa(&s) == 0, "empty set should have no capacity");
|
@@ -9152,7 +9639,7 @@ FIO_FUNC void fio_set_test(void) {
|
|
9152
9639
|
FIO_ASSERT(!fio_hash_test_last(&h).key && !fio_hash_test_last(&h).obj,
|
9153
9640
|
"empty hash shouldn't have a last object");
|
9154
9641
|
|
9155
|
-
for (uintptr_t i = 1; i <
|
9642
|
+
for (uintptr_t i = 1; i < FIO_SET_TEST_COUNT; ++i) {
|
9156
9643
|
fio_set_test_insert(&s, i, i);
|
9157
9644
|
fio_hash_test_insert(&h, i, i, i + 1, NULL);
|
9158
9645
|
FIO_ASSERT(fio_set_test_find(&s, i, i), "set find failed after insert");
|
@@ -9160,8 +9647,8 @@ FIO_FUNC void fio_set_test(void) {
|
|
9160
9647
|
FIO_ASSERT(i == fio_set_test_find(&s, i, i), "set insertion != find");
|
9161
9648
|
FIO_ASSERT(i + 1 == fio_hash_test_find(&h, i, i), "hash insertion != find");
|
9162
9649
|
}
|
9163
|
-
fprintf(stderr, "* Seeking %lu items\n",
|
9164
|
-
for (unsigned long i = 1; i <
|
9650
|
+
fprintf(stderr, "* Seeking %lu items\n", FIO_SET_TEST_COUNT);
|
9651
|
+
for (unsigned long i = 1; i < FIO_SET_TEST_COUNT; ++i) {
|
9165
9652
|
FIO_ASSERT((i == fio_set_test_find(&s, i, i)),
|
9166
9653
|
"set insertion != find (seek)");
|
9167
9654
|
FIO_ASSERT((i + 1 == fio_hash_test_find(&h, i, i)),
|
@@ -9169,7 +9656,7 @@ FIO_FUNC void fio_set_test(void) {
|
|
9169
9656
|
}
|
9170
9657
|
{
|
9171
9658
|
fprintf(stderr, "* Testing order for %lu items in set\n",
|
9172
|
-
|
9659
|
+
FIO_SET_TEST_COUNT);
|
9173
9660
|
uintptr_t i = 1;
|
9174
9661
|
FIO_SET_FOR_LOOP(&s, pos) {
|
9175
9662
|
FIO_ASSERT(pos->obj == i, "object order mismatch %lu != %lu.",
|
@@ -9179,7 +9666,7 @@ FIO_FUNC void fio_set_test(void) {
|
|
9179
9666
|
}
|
9180
9667
|
{
|
9181
9668
|
fprintf(stderr, "* Testing order for %lu items in hash\n",
|
9182
|
-
|
9669
|
+
FIO_SET_TEST_COUNT);
|
9183
9670
|
uintptr_t i = 1;
|
9184
9671
|
FIO_SET_FOR_LOOP(&h, pos) {
|
9185
9672
|
FIO_ASSERT(pos->obj.obj == i + 1 && pos->obj.key == i,
|
@@ -9189,8 +9676,8 @@ FIO_FUNC void fio_set_test(void) {
|
|
9189
9676
|
}
|
9190
9677
|
}
|
9191
9678
|
|
9192
|
-
fprintf(stderr, "* Removing odd items from %lu items\n",
|
9193
|
-
for (unsigned long i = 1; i <
|
9679
|
+
fprintf(stderr, "* Removing odd items from %lu items\n", FIO_SET_TEST_COUNT);
|
9680
|
+
for (unsigned long i = 1; i < FIO_SET_TEST_COUNT; i += 2) {
|
9194
9681
|
fio_set_test_remove(&s, i, i, NULL);
|
9195
9682
|
fio_hash_test_remove(&h, i, i, NULL);
|
9196
9683
|
FIO_ASSERT(!(fio_set_test_find(&s, i, i)),
|
@@ -9199,7 +9686,7 @@ FIO_FUNC void fio_set_test(void) {
|
|
9199
9686
|
"Removal failed in hash (still exists).");
|
9200
9687
|
}
|
9201
9688
|
{
|
9202
|
-
fprintf(stderr, "* Testing for %lu / 2 holes\n",
|
9689
|
+
fprintf(stderr, "* Testing for %lu / 2 holes\n", FIO_SET_TEST_COUNT);
|
9203
9690
|
uintptr_t i = 1;
|
9204
9691
|
FIO_SET_FOR_LOOP(&s, pos) {
|
9205
9692
|
if (pos->hash == 0) {
|
@@ -9274,11 +9761,11 @@ FIO_FUNC void fio_set_test(void) {
|
|
9274
9761
|
"Re-removing a re-added item should update the item!");
|
9275
9762
|
}
|
9276
9763
|
}
|
9277
|
-
fprintf(stderr, "* Compacting HashMap to %lu\n",
|
9764
|
+
fprintf(stderr, "* Compacting HashMap to %lu\n", FIO_SET_TEST_COUNT >> 1);
|
9278
9765
|
fio_set_test_compact(&s);
|
9279
9766
|
{
|
9280
9767
|
fprintf(stderr, "* Testing that %lu items are continuous\n",
|
9281
|
-
|
9768
|
+
FIO_SET_TEST_COUNT >> 1);
|
9282
9769
|
uintptr_t i = 0;
|
9283
9770
|
FIO_SET_FOR_LOOP(&s, pos) {
|
9284
9771
|
FIO_ASSERT(pos->hash != 0, "Found a hole after compact.");
|
@@ -9292,14 +9779,14 @@ FIO_FUNC void fio_set_test(void) {
|
|
9292
9779
|
FIO_ASSERT(!s.map && !s.ordered && !s.pos && !s.capa,
|
9293
9780
|
"HashMap not re-initialized after free.");
|
9294
9781
|
|
9295
|
-
fio_set_test_capa_require(&s,
|
9782
|
+
fio_set_test_capa_require(&s, FIO_SET_TEST_COUNT);
|
9296
9783
|
|
9297
9784
|
FIO_ASSERT(
|
9298
|
-
s.map && s.ordered && !s.pos && s.capa >=
|
9785
|
+
s.map && s.ordered && !s.pos && s.capa >= FIO_SET_TEST_COUNT,
|
9299
9786
|
"capa_require changes state in a bad way (%p, %p, %zu, %zu ?>= %zu)",
|
9300
|
-
(void *)s.map, (void *)s.ordered, s.pos, s.capa,
|
9787
|
+
(void *)s.map, (void *)s.ordered, s.pos, s.capa, FIO_SET_TEST_COUNT);
|
9301
9788
|
|
9302
|
-
for (unsigned long i = 1; i <
|
9789
|
+
for (unsigned long i = 1; i < FIO_SET_TEST_COUNT; ++i) {
|
9303
9790
|
fio_set_test_insert(&s, i, i);
|
9304
9791
|
FIO_ASSERT(fio_set_test_find(&s, i, i),
|
9305
9792
|
"find failed after insert (2nd round)");
|
@@ -9309,6 +9796,109 @@ FIO_FUNC void fio_set_test(void) {
|
|
9309
9796
|
s.count);
|
9310
9797
|
}
|
9311
9798
|
fio_set_test_free(&s);
|
9799
|
+
/* full/partial collision attack against set and test response */
|
9800
|
+
if (1) {
|
9801
|
+
fio_set_attack_s as = FIO_SET_INIT;
|
9802
|
+
time_t start_ok = clock();
|
9803
|
+
for (uintptr_t i = 0; i < FIO_SET_TEST_COUNT; ++i) {
|
9804
|
+
fio_set_attack_insert(&as, i, i + 1);
|
9805
|
+
FIO_ASSERT(fio_set_attack_find(&as, i, i + 1) == i + 1,
|
9806
|
+
"set attack verctor failed sanity test (seek != insert)");
|
9807
|
+
}
|
9808
|
+
time_t end_ok = clock();
|
9809
|
+
FIO_ASSERT(fio_set_attack_count(&as) == FIO_SET_TEST_COUNT,
|
9810
|
+
"set attack verctor failed sanity test (count error %zu != %zu)",
|
9811
|
+
fio_set_attack_count(&as), FIO_SET_TEST_COUNT);
|
9812
|
+
fio_set_attack_free(&as);
|
9813
|
+
|
9814
|
+
/* full collision attack */
|
9815
|
+
time_t start_bad = clock();
|
9816
|
+
for (uintptr_t i = 0; i < FIO_SET_TEST_COUNT; ++i) {
|
9817
|
+
fio_set_attack_insert(&as, 1, i + 1);
|
9818
|
+
}
|
9819
|
+
time_t end_bad = clock();
|
9820
|
+
FIO_ASSERT(fio_set_attack_count(&as) != FIO_SET_TEST_COUNT,
|
9821
|
+
"set attack success! too many full-collisions inserts!");
|
9822
|
+
FIO_LOG_DEBUG("set full-collision attack final count/capa = %zu / %zu",
|
9823
|
+
fio_set_attack_count(&as), fio_set_attack_capa(&as));
|
9824
|
+
FIO_LOG_DEBUG("set full-collision attack timing impact (attack vs. normal) "
|
9825
|
+
"%zu vs. %zu",
|
9826
|
+
end_bad - start_bad, end_ok - start_ok);
|
9827
|
+
fio_set_attack_free(&as);
|
9828
|
+
|
9829
|
+
/* partial collision attack */
|
9830
|
+
start_bad = clock();
|
9831
|
+
for (uintptr_t i = 0; i < FIO_SET_TEST_COUNT; ++i) {
|
9832
|
+
fio_set_attack_insert(&as, ((i << 20) | 1), i + 1);
|
9833
|
+
}
|
9834
|
+
end_bad = clock();
|
9835
|
+
FIO_ASSERT(fio_set_attack_count(&as) == FIO_SET_TEST_COUNT,
|
9836
|
+
"partial collision resolusion failed, not enough inserts!");
|
9837
|
+
FIO_LOG_DEBUG("set partial collision attack final count/capa = %zu / %zu",
|
9838
|
+
fio_set_attack_count(&as), fio_set_attack_capa(&as));
|
9839
|
+
FIO_LOG_DEBUG("set partial collision attack timing impact (attack vs. "
|
9840
|
+
"normal) %zu vs. %zu",
|
9841
|
+
end_bad - start_bad, end_ok - start_ok);
|
9842
|
+
fio_set_attack_free(&as);
|
9843
|
+
}
|
9844
|
+
}
|
9845
|
+
|
9846
|
+
/* *****************************************************************************
|
9847
|
+
Bad Hash (risky hash) tests
|
9848
|
+
***************************************************************************** */
|
9849
|
+
|
9850
|
+
FIO_FUNC void fio_riskyhash_speed_test(void) {
|
9851
|
+
/* test based on code from BearSSL with credit to Thomas Pornin */
|
9852
|
+
uint8_t buffer[8192];
|
9853
|
+
memset(buffer, 'T', sizeof(buffer));
|
9854
|
+
/* warmup */
|
9855
|
+
uint64_t hash = 0;
|
9856
|
+
for (size_t i = 0; i < 4; i++) {
|
9857
|
+
hash += fio_risky_hash(buffer, 8192, 1);
|
9858
|
+
memcpy(buffer, &hash, sizeof(hash));
|
9859
|
+
}
|
9860
|
+
/* loop until test runs for more than 2 seconds */
|
9861
|
+
for (uint64_t cycles = 8192;;) {
|
9862
|
+
clock_t start, end;
|
9863
|
+
start = clock();
|
9864
|
+
for (size_t i = cycles; i > 0; i--) {
|
9865
|
+
hash += fio_risky_hash(buffer, 8192, 1);
|
9866
|
+
__asm__ volatile("" ::: "memory");
|
9867
|
+
}
|
9868
|
+
end = clock();
|
9869
|
+
memcpy(buffer, &hash, sizeof(hash));
|
9870
|
+
if ((end - start) >= (2 * CLOCKS_PER_SEC) ||
|
9871
|
+
cycles >= ((uint64_t)1 << 62)) {
|
9872
|
+
fprintf(stderr, "%-20s %8.2f MB/s\n", "fio_risky_hash",
|
9873
|
+
(double)(sizeof(buffer) * cycles) /
|
9874
|
+
(((end - start) * 1000000.0 / CLOCKS_PER_SEC)));
|
9875
|
+
break;
|
9876
|
+
}
|
9877
|
+
cycles <<= 2;
|
9878
|
+
}
|
9879
|
+
}
|
9880
|
+
|
9881
|
+
FIO_FUNC void fio_riskyhash_test(void) {
|
9882
|
+
fprintf(stderr, "===================================\n");
|
9883
|
+
#if NODEBUG
|
9884
|
+
fio_riskyhash_speed_test();
|
9885
|
+
#else
|
9886
|
+
fprintf(stderr, "fio_risky_hash speed test skipped (debug mode is slow)\n");
|
9887
|
+
fio_str_info_s str1 =
|
9888
|
+
(fio_str_info_s){.data = "nothing_is_really_here1", .len = 23};
|
9889
|
+
fio_str_info_s str2 =
|
9890
|
+
(fio_str_info_s){.data = "nothing_is_really_here2", .len = 23};
|
9891
|
+
fio_str_s copy = FIO_STR_INIT;
|
9892
|
+
FIO_ASSERT(fio_risky_hash(str1.data, str1.len, 1) !=
|
9893
|
+
fio_risky_hash(str2.data, str2.len, 1),
|
9894
|
+
"Different strings should have a different risky hash");
|
9895
|
+
fio_str_write(©, str1.data, str1.len);
|
9896
|
+
FIO_ASSERT(fio_risky_hash(str1.data, str1.len, 1) ==
|
9897
|
+
fio_risky_hash(fio_str_data(©), fio_str_len(©), 1),
|
9898
|
+
"Same string values should have the same risky hash");
|
9899
|
+
fio_str_free(©);
|
9900
|
+
(void)fio_riskyhash_speed_test;
|
9901
|
+
#endif
|
9312
9902
|
}
|
9313
9903
|
|
9314
9904
|
/* *****************************************************************************
|
@@ -9322,7 +9912,7 @@ FIO_FUNC void fio_siphash_speed_test(void) {
|
|
9322
9912
|
/* warmup */
|
9323
9913
|
uint64_t hash = 0;
|
9324
9914
|
for (size_t i = 0; i < 4; i++) {
|
9325
|
-
hash += fio_siphash24(buffer, sizeof(buffer));
|
9915
|
+
hash += fio_siphash24(buffer, sizeof(buffer), 0, 0);
|
9326
9916
|
memcpy(buffer, &hash, sizeof(hash));
|
9327
9917
|
}
|
9328
9918
|
/* loop until test runs for more than 2 seconds */
|
@@ -9330,7 +9920,7 @@ FIO_FUNC void fio_siphash_speed_test(void) {
|
|
9330
9920
|
clock_t start, end;
|
9331
9921
|
start = clock();
|
9332
9922
|
for (size_t i = cycles; i > 0; i--) {
|
9333
|
-
hash += fio_siphash24(buffer, sizeof(buffer));
|
9923
|
+
hash += fio_siphash24(buffer, sizeof(buffer), 0, 0);
|
9334
9924
|
__asm__ volatile("" ::: "memory");
|
9335
9925
|
}
|
9336
9926
|
end = clock();
|
@@ -9349,7 +9939,7 @@ FIO_FUNC void fio_siphash_speed_test(void) {
|
|
9349
9939
|
clock_t start, end;
|
9350
9940
|
start = clock();
|
9351
9941
|
for (size_t i = cycles; i > 0; i--) {
|
9352
|
-
hash += fio_siphash13(buffer, sizeof(buffer));
|
9942
|
+
hash += fio_siphash13(buffer, sizeof(buffer), 0, 0);
|
9353
9943
|
__asm__ volatile("" ::: "memory");
|
9354
9944
|
}
|
9355
9945
|
end = clock();
|
@@ -9374,7 +9964,6 @@ FIO_FUNC void fio_siphash_test(void) {
|
|
9374
9964
|
(void)fio_siphash_speed_test;
|
9375
9965
|
#endif
|
9376
9966
|
}
|
9377
|
-
|
9378
9967
|
/* *****************************************************************************
|
9379
9968
|
SHA-1 tests
|
9380
9969
|
***************************************************************************** */
|
@@ -10362,6 +10951,7 @@ void fio_test(void) {
|
|
10362
10951
|
fio_socket_test();
|
10363
10952
|
fio_uuid_link_test();
|
10364
10953
|
fio_cycle_test();
|
10954
|
+
fio_riskyhash_test();
|
10365
10955
|
fio_siphash_test();
|
10366
10956
|
fio_sha1_test();
|
10367
10957
|
fio_sha2_test();
|