iodine 0.7.37 → 0.7.42

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d09283cad97b84847493794f2245f7b477b6f0170a8332112c3836106d660373
4
- data.tar.gz: 3727db00462421260d06689ac5c578cda9203d2dc2c3d4daed53b4f9dc6b84f5
3
+ metadata.gz: a3ae02e70ea4439584f432552b08cab2cb26ee33c70b2a7263fd56f75bbf7f40
4
+ data.tar.gz: 8fa79e4b09b1f7d9d889038a9e091da2632759683d6410c318c1d965f9768b4d
5
5
  SHA512:
6
- metadata.gz: db93d224de064141cf4a422e05bba2e67c74ef0344dfce19579be48489cc16b070e24527e30aa4c081f9c38ca4aeb2c737f90d1edd8d157cf0d334b5f083965e
7
- data.tar.gz: 8b6e496da5af91bf59225798c41d00776caafb3aec55a57c5ee43a1ca943a0312e676e119a9a41387c4beba5350d0f71c1ec70868d29933be6b140ac1d814b64
6
+ metadata.gz: 063baf66e22412189a923b4f8bb566ab9cfd14b267460bced8a6774abcc4ae9ae028a737a9e4c4963c84c3629e71bd147fb16d24d836b47eee09940a8a30f9a3
7
+ data.tar.gz: b16abe85cfebb592c05e9e87be4401fbaac436791489da5aab14199c30b3cfd9bd187ef4a382e2f6c6fc4024949b8b283d0879cf41d63123ca0845327b8b54c5
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ ### System Information
11
+
12
+ - **OS**: [e.g. macOS 10.15.4]
13
+ - **Ruby**: [e.g. 2.7.0]
14
+ - **Version**: [e.g. 0.7.38]
15
+ - **OpenSSL**: [OpenSSL 1.1.1f 20 Mar 2020]
16
+
17
+ ### Description
18
+
19
+ A clear and concise description of what the bug is.
20
+
21
+ ### Rack App to Reproduce
22
+
23
+ ```ruby
24
+ APP = {|env| [200, {}, "Hello World"] }
25
+ run APP
26
+ ```
27
+
28
+ ### Testing code
29
+
30
+ ```sh
31
+ curl http://localhost:3000/
32
+ ```
33
+
34
+ ### Expected behavior
35
+
36
+ A clear and concise description of what you expected to happen.
37
+
38
+ ### Actual behavior
39
+
40
+ A clear and concise description of what actually happened.
@@ -1,4 +1,7 @@
1
1
  language: ruby
2
+ arch:
3
+ - amd64
4
+ - arm64
2
5
  os:
3
6
  - linux
4
7
  # - osx
@@ -6,15 +9,16 @@ before_install:
6
9
  - gem install bundler -v 1.10.6
7
10
  - bundle install
8
11
  rvm:
9
- - 2.6.0
10
- - 2.5.3
11
- - 2.4.5
12
+ - 2.7.1
13
+ - 2.6.5
14
+ - 2.5.7
15
+ - 2.4.9
12
16
  - 2.3.8
13
17
  - 2.2.2
14
18
  notifications:
15
19
  email: false
16
20
  sudo: required
17
- dist: trusty
21
+ dist: xenial
18
22
  addons:
19
23
  apt:
20
24
  sources:
@@ -24,7 +28,5 @@ addons:
24
28
  script:
25
29
  - echo CFLAGS = $CFLAGS
26
30
  - echo cflags = $cflags
27
- - bundle exec rake compile && bundle exec rspec --format documentation
28
- - gem uninstall -x iodine
29
- - rake build
30
- - find pkg/iodine-*.gem -exec gem install -V {} +
31
+ - bundle exec rake install
32
+ - env VERBOSE=1 bundle exec rspec --format documentation
@@ -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.42 (2020-08-02)
10
+
11
+ Fix: Implement fix suggested by @Shelvak (Néstor Coppi) in issue #98.
12
+
13
+ #### Change log v.0.7.41 (2020-07-24)
14
+
15
+ **Fix**: Hot Restart failed because listening sockets were cleared away. Credit to Néstor Coppi (@Shelvak) for exposing issue #97.
16
+
17
+ **Fix**: CLI argument parsing is now only active when using the iodine CLI (or if defining `IODINE_PARSE_CLI` before requiring `iodine`). Credit to Aldis Berjoza (@graudeejs) for exposing issue #96.
18
+
19
+ #### Change log v.0.7.40 (2020-05-23)
20
+
21
+ **Fix**: fixed TLS logging and performance issues exposed by Franck Gille (@fgi) in issue #93.
22
+
23
+ #### Change log v.0.7.39 (2020-05-18)
24
+
25
+ **Security**: a request smuggling attack vector and Transfer Encoding attack vector in the HTTP/1.1 parser were exposed by Sam Sanoop from [the Snyk Security team (snyk.io)](https://snyk.io). The parser was updated to deal with these potential issues.
26
+
27
+ **Fix**: (`http`) fixes an issue with date calculation - exposed by Franck Gille (@fgi) in issue #92.
28
+
29
+ **Fix**: (`extconf`) fixes an installation concern raised by Benoit Daloze (@eregon), in issue #91. The default compiler is now used.
30
+
31
+ #### Change log v.0.7.38
32
+
33
+ **Fix**: (`http`) fixes an issue and improves support for chunked encoded payloads. Credit to Ian Ker-Seymer ( @ianks ) for exposing this, writing tests and opening both the issue #87 and the PR #88.
34
+
9
35
  #### Change log v.0.7.37
10
36
 
11
37
  **Fix**: requests will fail when the path contains a dangling `?` (empty query). Credit to @adam12 for exposing this and opening issue #86.
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
- # iodine - a fast HTTP / Websocket Server with native Pub/Sub support for the new web
1
+ # iodine - Why Settle for a fast HTTP / WebSocket Server with native Pub/Sub?
2
+
2
3
  [![Gem](https://img.shields.io/gem/dt/iodine.svg)](https://rubygems.org/gems/iodine)
3
4
  [![Build Status](https://travis-ci.org/boazsegev/iodine.svg?branch=master)](https://travis-ci.org/boazsegev/iodine)
4
5
  [![Gem Version](https://badge.fury.io/rb/iodine.svg)](https://badge.fury.io/rb/iodine)
@@ -7,28 +8,30 @@
7
8
 
8
9
  [![Logo](https://github.com/boazsegev/iodine/raw/master/logo.png)](https://github.com/boazsegev/iodine)
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
+ Iodine is a fast concurrent web application server for real-time Ruby applications, with native support for WebSockets and Pub/Sub services - but it's also so much more.
11
12
 
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
+ Iodine is a Ruby wrapper for many of the [facil.io](https://facil.io) C framework, leveraging the speed of C for many common web application tasks. In addition, iodine abstracts away all network concerns, so you never need to worry about the transport layer, free to concentrate on your application logic.
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
+ Iodine includes native support for:
15
16
 
16
17
  * HTTP, WebSockets and EventSource (SSE) Services (server);
17
18
  * WebSocket connections (server / client);
18
19
  * Pub/Sub (with optional Redis Pub/Sub scaling);
19
- * Static file service (with automatic `gzip` support for pre-compressed versions);
20
- * HTTP/1.1 keep-alive and pipelining;
20
+ * Fast(!) builtin Mustache template engine.
21
+ * Static file service (with automatic `gzip` support for pre-compressed assets);
22
+ * Optimized Logging to `stderr`.
21
23
  * Asynchronous event scheduling and timers;
22
- * Hot Restart (using the USR1 signal);
24
+ * HTTP/1.1 keep-alive and pipelining;
25
+ * Heap Fragmentation Protection.
23
26
  * TLS 1.2 and above (Requires OpenSSL >= 1.1.0);
24
27
  * TCP/IP server and client connectivity;
25
28
  * Unix Socket server and client connectivity;
29
+ * Hot Restart (using the USR1 signal and without hot deployment);
26
30
  * Custom protocol authoring;
27
- * Optimized Logging to `stderr`.
28
31
  * [Sequel](https://github.com/jeremyevans/sequel) and ActiveRecord forking protection.
29
32
  * and more!
30
33
 
31
- Iodine is an **evented** framework with a simple API that ports much of the [C facil.io framework](https://github.com/boazsegev/facil.io) to Ruby. This means that:
34
+ Since iodine wraps much of the [C facil.io framework](https://github.com/boazsegev/facil.io) to Ruby:
32
35
 
33
36
  * Iodine can handle **thousands of concurrent connections** (tested with more then 20K connections on Linux)!
34
37
 
@@ -36,9 +39,9 @@ Iodine is an **evented** framework with a simple API that ports much of the [C f
36
39
 
37
40
  Iodine is a C extension for Ruby, developed and optimized for Ruby MRI 2.2.2 and up... it should support the whole Ruby 2.0 MRI family, but CI tests start at Ruby 2.2.2.
38
41
 
39
- **Note**: iodine does **not** support the streaming when using Rack. Streaming over Rack should be avoided on any server, WebSockets, SSE and `Range` requests should always be preferred. On iodine no data will be sent before the whole of the data is available.
42
+ **Note**: iodine does **not** support streaming when using Rack. It's recommended to avoid blocking the server when using `body.each` since the `each` loop will block the iodine's thread until it's finished and iodine won't send any data before the loop is done.
40
43
 
41
- ## Iodine - a fast & powerful HTTP + Websockets server with native Pub/Sub
44
+ ## Iodine - a fast & powerful HTTP + WebSockets server with native Pub/Sub
42
45
 
43
46
  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](./SPEC-Websocket-Draft.md).
44
47
 
@@ -46,6 +49,10 @@ With `Iodine.listen service: :http` it's possible to run multiple HTTP applicati
46
49
 
47
50
  Iodine also supports native process cluster Pub/Sub and a native RedisEngine to easily scale iodine's Pub/Sub horizontally.
48
51
 
52
+ ### Known Issues and Reporting Issues
53
+
54
+ See the [GitHub Open Issues](https://github.com/boazsegev/iodine/issues) list for known issues and to report new issues.
55
+
49
56
  ### Installing and Running Iodine
50
57
 
51
58
  Install iodine on any Linux / BSD / macOS system using:
@@ -68,6 +75,8 @@ bundler exec iodine
68
75
 
69
76
  #### Installing with SSL/TLS
70
77
 
78
+ **Note**: iodine has known issues with the TLS/SSL support. TLS/SSL should **NOT** be used in production (see issues #95 and #94).
79
+
71
80
  Make sure to update OpenSSL to the latest version **before installing Ruby** (`rbenv` should do this automatically).
72
81
 
73
82
  To avoid name resolution conflicts, iodine will bind to the same OpenSSL version Ruby is bound to. To use SSL/TLS this should be OpenSSL >= 1.1.0 or LibreSSL >= 2.7.4.
@@ -85,9 +94,7 @@ Confirmed OpenSSL to be version 1.1.0 or above (OpenSSL 1.1.0j 20 Nov 2018)...
85
94
  ...
86
95
  ```
87
96
 
88
- **KNOWN ISSUE:**
89
-
90
- The installation script tests for OpenSSL 1.1.0 and above. However, this testing approach sometimes provides false positives. If TLS isn't required, install with `NO_SSL=1`. i.e.:
97
+ The installation script tests for OpenSSL 1.1.0 and above. However, this testing approach sometimes provides false positives. **If TLS isn't required, install with `NO_SSL=1`**. i.e.:
91
98
 
92
99
  ```bash
93
100
  NO_SSL=1 bundler exec iodine
@@ -108,17 +115,19 @@ On Rails:
108
115
  if(defined?(Iodine))
109
116
  Iodine.threads = ENV.fetch("RAILS_MAX_THREADS", 5).to_i if Iodine.threads.zero?
110
117
  Iodine.workers = ENV.fetch("WEB_CONCURRENCY", 2).to_i if Iodine.workers.zero?
111
- Iodine::DEFAULT_SETTINGS[:port] = ENV.fetch("PORT") if ENV.fetch("PORT")
118
+ Iodine::DEFAULT_SETTINGS[:port] ||= ENV.fetch("PORT") if ENV.fetch("PORT")
112
119
  end
113
120
  ```
114
121
 
115
122
  When using native WebSockets with Rails, middle-ware is probably the best approach. A guide for this approach will, hopefully, get published in the future.
116
123
 
124
+ **Note**: command-line instructions (CLI) should be the preferred way for configuring iodine, allowing for code-less configuration updates.
125
+
117
126
  ### Optimizing Iodine's Concurrency
118
127
 
119
128
  To get the most out of iodine, consider the amount of CPU cores available and the concurrency level the application requires.
120
129
 
121
- Iodine will calculate, when possible, a good enough default concurrency model. See if this works for your application or customize according to the application's needs.
130
+ Iodine will calculate, when possible, a good enough default concurrency model for fast applications. See if this works for your application or customize according to the application's needs.
122
131
 
123
132
  Command line arguments allow easy access to different options, including concurrency levels. i.e., to set up 16 threads and 4 processes:
124
133
 
@@ -134,13 +143,19 @@ export WORKERS=-1 # negative values are fractions of CPU cores.
134
143
  bundler exec iodine -p $PORT
135
144
  ```
136
145
 
146
+ Negative values are evaluated as "CPU Cores / abs(Value)". i.e., on an 8 core CPU machine, this will produce 4 worker processes with 2 threads per worker:
147
+
148
+ ```bash
149
+ bundler exec iodine -p $PORT -t 2 -w -2
150
+ ```
151
+
137
152
  ### Heap Fragmentation Protection
138
153
 
139
154
  Iodine includes a fast, network oriented, custom memory allocator, optimizing away some of the work usually placed on the Ruby Garbage Collector (GC).
140
155
 
141
156
  This approach helps to minimize heap fragmentation for long running processes, by grouping many short-lived objects into a common memory space.
142
157
 
143
- It's still recommended to consider [jemalloc](http://jemalloc.net) or other allocators that also help mitigate heap fragmentation issues.
158
+ It is still recommended to consider [jemalloc](http://jemalloc.net) or other allocators that also help mitigate heap fragmentation issues.
144
159
 
145
160
  ### Static file serving support
146
161
 
@@ -433,11 +448,11 @@ Iodine.connect url: "wss://echo.websocket.org", handler: EchoClient.new, ping: 4
433
448
  Iodine.start
434
449
  ```
435
450
 
436
- ### TLS 1.2 support
451
+ ### TLS >= 1.2 support
437
452
 
438
453
  > Requires OpenSSL >= `1.1.0`. On Heroku, requires `heroku-18`.
439
454
 
440
- Iodine supports secure connections fore TLS version 1.2 and up (depending on the OpenSSL version).
455
+ Iodine supports secure connections fore TLS version 1.2 **and up** (depending on the OpenSSL version).
441
456
 
442
457
  A self signed certificate is available using the `-tls` flag from the command-line.
443
458
 
@@ -478,31 +493,11 @@ run APP
478
493
 
479
494
  ### How does it compare to other servers?
480
495
 
481
- The honest answer is "I don't know". I recommend that you perform your own tests.
482
-
483
- In my tests, pitching Iodine against Puma, Iodine was anywhere between x1.5 and x7 faster than Puma (depending on use-case). such a big difference is suspect and I recommend that you test it yourself.
496
+ In my tests, pitching Iodine against Puma, Iodine was anywhere between x1.5 and more than x10 faster than Puma (depending on use-case and settings).
484
497
 
485
- Also, performing benchmarks on a single machine isn't very reliable... but it's all I've got.
498
+ Such a big difference is suspect and I recommend that you test it yourself - even better if you test performance using your own application and a number of possible different settings (how many threads per CPU core? how many worker processes? middleware vs. server request logging, etc').
486
499
 
487
- When benchmarking with `wrk`, on the same local machine with similar settings for both Puma and Iodine (4 workers, 16 threads each, 200 concurrent connections), I calculated Iodine to be x1.52 faster::
488
-
489
- * Iodine performed at 74,786.27 req/sec, consuming ~68.4Mb of memory.
490
-
491
- * Puma performed at 48,994.59 req/sec, consuming ~79.6Mb of memory.
492
-
493
- When benchmarking using a VM (crossing machine boundaries, 16 threads, 4 workers, 200 concurrent connections), I calculated Iodine to be x2.3 faster:
494
-
495
- * Iodine performed at 23,559.56 req/sec, consuming ~88.8Mb of memory.
496
-
497
- * Puma performed at 9,935.31 req/sec, consuming ~84.0Mb of memory.
498
-
499
- When benchmarking using a VM (crossing machine boundaries, single thread, single worker, 200 concurrent connections), I calculated Iodine to be x7.3 faster:
500
-
501
- * Iodine performed at 18,444.31 req/sec, consuming ~25.6Mb of memory.
502
-
503
- * Puma performed at 2,521.56 req/sec, consuming ~27.5Mb of memory.
504
-
505
- I have doubts about my own benchmarks and I recommend benchmarking the performance for yourself using `wrk` or `ab`:
500
+ I recommend benchmarking the performance for yourself using `wrk` or `ab`:
506
501
 
507
502
  ```bash
508
503
  $ wrk -c200 -d4 -t2 http://localhost:3000/
@@ -510,7 +505,7 @@ $ wrk -c200 -d4 -t2 http://localhost:3000/
510
505
  $ ab -n 100000 -c 200 -k http://127.0.0.1:3000/
511
506
  ```
512
507
 
513
- Create a simple `config.ru` file with a hello world app:
508
+ The best application to use for benchmarking is your actual application. Or, you could create a simple `config.ru` file with a __hello world__ app:
514
509
 
515
510
  ```ruby
516
511
  App = Proc.new do |env|
@@ -523,17 +518,21 @@ end
523
518
  run App
524
519
  ```
525
520
 
526
- Then start comparing servers. Here are the settings I used to compare iodine and Puma (4 processes, 16 threads):
521
+ Then start comparing servers. Here are the settings I used to compare iodine and Puma (4 processes, 4 threads):
527
522
 
528
523
  ```bash
529
- $ RACK_ENV=production iodine -p 3000 -t 16 -w 4
524
+ $ RACK_ENV=production iodine -p 3000 -t 4 -w 4
530
525
  # vs.
531
- $ RACK_ENV=production puma -p 3000 -t 16 -w 4
526
+ $ RACK_ENV=production puma -p 3000 -t 4 -w 4
532
527
  # Review the `iodine -?` help for more command line options.
533
528
  ```
534
529
 
535
530
  It's recommended that the servers (Iodine/Puma) and the client (`wrk`/`ab`) run on separate machines.
536
531
 
532
+ It is worth noting that iodine can also speed up logging by replacing the logging middleware with `iodine -v`. This approach uses less memory and improves performance at the expense of fuzzy timing and some string caching.
533
+
534
+ On my machine, testing with the logging functionality enabled, iodine was more then 10 times faster than puma (60.9K req/sec vs. 5.3K req/sec)
535
+
537
536
  ### A few notes
538
537
 
539
538
  Iodine's upgrade / callback design has a number of benefits, some of them related to better IO handling, resource optimization (no need for two IO polling systems), etc. This also allows us to use middleware without interfering with connection upgrades and provides backwards compatibility.
@@ -770,6 +769,14 @@ Yes, please, here are some thoughts:
770
769
 
771
770
  * If you love the project or thought the code was nice, maybe helped you in your own project, drop me a line. I'd love to know.
772
771
 
772
+ ### Running the Tests
773
+
774
+ Running this task will compile the C extensions then run RSpec tests:
775
+
776
+ ```sh
777
+ bundle exec rake spec
778
+ ```
779
+
773
780
  ## License
774
781
 
775
782
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -3,10 +3,12 @@ require "rake/extensiontask"
3
3
 
4
4
  begin
5
5
  require 'rspec/core/rake_task'
6
- RSpec::Core::RakeTask.new(:spec)
6
+ RSpec::Core::RakeTask.new(:rspec)
7
7
  rescue LoadError
8
8
  end
9
9
 
10
+ task :spec => [:compile, :rspec]
11
+
10
12
  task :default => [:compile, :spec]
11
13
 
12
14
  Rake::ExtensionTask.new "iodine" do |ext|
@@ -22,6 +22,14 @@ module ShootoutApp
22
22
  len += out[-1].length
23
23
  request.params.each { |k,v| out << "#{k}: #{v}\n" ; len += out[-1].length }
24
24
  end
25
+ if(env['rack.input'])
26
+ env['rack.input'].rewind
27
+ out << "Body\n\n"
28
+ out << env['rack.input'].read
29
+ len += out[-1].length + 6
30
+ out << "\n\nBody Length: #{out[-1].length}\n\n"
31
+ len += out[-1].length
32
+ end
25
33
  [200, { 'Content-Length' => len.to_s, 'Content-Type' => 'text/plain; charset=UTF-8;' }, out]
26
34
  end
27
35
  # We'll base the shootout on the internal Pub/Sub service.
data/exe/iodine CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
+ IODINE_PARSE_CLI = true
3
4
  require 'iodine'
4
5
 
5
6
  # Load Rack if available (assume it will be used).
@@ -1,34 +1,5 @@
1
1
  require 'mkmf'
2
2
 
3
- if ENV['CC']
4
- ENV['CPP'] ||= ENV['CC']
5
- puts "detected user prefered compiler (#{ENV['CC']}):", `#{ENV['CC']} -v`
6
- elsif find_executable('clang') && puts('testing clang for stdatomic support...').nil? && system("printf \"\#include <stdatomic.h>\nint main(void) {}\" | clang -include stdatomic.h -xc -o /dev/null -", out: '/dev/null')
7
- $CC = ENV['CC'] = 'clang'
8
- $CPP = ENV['CPP'] = 'clang'
9
- puts "using clang compiler v. #{`clang -dumpversion`}."
10
- elsif find_executable('gcc') && (`gcc -dumpversion 2>&1`.to_i >= 5)
11
- $CC = ENV['CC'] = 'gcc'
12
- $CPP = ENV['CPP'] = find_executable('g++') ? 'g++' : 'gcc'
13
- puts "using gcc #{ `gcc -dumpversion 2>&1`.to_i }"
14
- elsif find_executable('gcc-6')
15
- $CC = ENV['CC'] = 'gcc-6'
16
- $CPP = ENV['CPP'] = find_executable('g++-6') ? 'g++-6' : 'gcc-6'
17
- puts 'using gcc-6 compiler.'
18
- elsif find_executable('gcc-5')
19
- $CC = ENV['CC'] = 'gcc-5'
20
- $CPP = ENV['CPP'] = find_executable('g++-5') ? 'g++-5' : 'gcc-5'
21
- puts 'using gcc-5 compiler.'
22
- elsif find_executable('gcc-4.9')
23
- $CC = ENV['CC'] = 'gcc-4.9'
24
- $CPP = ENV['CPP'] = find_executable('g++-4.9') ? 'g++-4.9' : 'gcc-4.9'
25
- puts 'using gcc-4.9 compiler.'
26
- else
27
- puts 'using an unknown (old?) compiler... who knows if this will work out... we hope.'
28
- end
29
-
30
-
31
-
32
3
  # Test polling
33
4
  def iodine_test_polling_support
34
5
  iodine_poll_test_kqueue = <<EOS
@@ -129,10 +100,5 @@ EOS
129
100
  end
130
101
  end
131
102
  end
132
- # $defs << "-DFIO_USE_RISKY_HASH"
133
-
134
- RbConfig::MAKEFILE_CONFIG['CFLAGS'] = $CFLAGS = "-std=c11 -DFIO_PRINT_STATE=0 #{$CFLAGS} #{$CFLAGS == ENV['CFLAGS'] ? "" : ENV['CFLAGS']}"
135
- RbConfig::MAKEFILE_CONFIG['CC'] = $CC = ENV['CC'] if ENV['CC']
136
- RbConfig::MAKEFILE_CONFIG['CPP'] = $CPP = ENV['CPP'] if ENV['CPP']
137
103
 
138
104
  create_makefile 'iodine/iodine'
@@ -36,6 +36,12 @@ Feel free to copy, use and enjoy according to the license provided.
36
36
 
37
37
  #include <arpa/inet.h>
38
38
 
39
+ #if HAVE_OPENSSL
40
+ #include <openssl/bio.h>
41
+ #include <openssl/err.h>
42
+ #include <openssl/ssl.h>
43
+ #endif
44
+
39
45
  /* force poll for testing? */
40
46
  #ifndef FIO_ENGINE_POLL
41
47
  #define FIO_ENGINE_POLL 0
@@ -277,26 +283,6 @@ static inline fio_packet_s *fio_packet_alloc(void) {
277
283
  Core Connection Data Clearing
278
284
  ***************************************************************************** */
279
285
 
280
- /* set the minimal max_protocol_fd */
281
- static void fio_max_fd_min(uint32_t fd) {
282
- if (fio_data->max_protocol_fd > fd)
283
- return;
284
- fio_lock(&fio_data->lock);
285
- if (fio_data->max_protocol_fd < fd)
286
- fio_data->max_protocol_fd = fd;
287
- fio_unlock(&fio_data->lock);
288
- }
289
-
290
- /* set the minimal max_protocol_fd */
291
- static void fio_max_fd_shrink(void) {
292
- fio_lock(&fio_data->lock);
293
- uint32_t fd = fio_data->max_protocol_fd;
294
- while (fd && fd_data(fd).protocol == NULL)
295
- --fd;
296
- fio_data->max_protocol_fd = fd;
297
- fio_unlock(&fio_data->lock);
298
- }
299
-
300
286
  /* resets connection data, marking it as either open or closed. */
301
287
  static inline int fio_clear_fd(intptr_t fd, uint8_t is_open) {
302
288
  fio_packet_s *packet;
@@ -318,6 +304,13 @@ static inline int fio_clear_fd(intptr_t fd, uint8_t is_open) {
318
304
  .counter = fd_data(fd).counter + 1,
319
305
  .packet_last = &fd_data(fd).packet,
320
306
  };
307
+ if (fio_data->max_protocol_fd < fd) {
308
+ fio_data->max_protocol_fd = fd;
309
+ } else {
310
+ while (fio_data->max_protocol_fd &&
311
+ !fd_data(fio_data->max_protocol_fd).open)
312
+ --fio_data->max_protocol_fd;
313
+ }
321
314
  fio_unlock(&(fd_data(fd).sock_lock));
322
315
  if (rw_hooks && rw_hooks->cleanup)
323
316
  rw_hooks->cleanup(rw_udata);
@@ -336,8 +329,8 @@ static inline int fio_clear_fd(intptr_t fd, uint8_t is_open) {
336
329
  if (protocol && protocol->on_close) {
337
330
  fio_defer(deferred_on_close, (void *)fd2uuid(fd), protocol);
338
331
  }
339
- if (is_open)
340
- fio_max_fd_min(fd);
332
+ FIO_LOG_DEBUG("FD %d re-initialized (state: %p-%s).", (int)fd,
333
+ (void *)fd2uuid(fd), (is_open ? "open" : "closed"));
341
334
  return 0;
342
335
  }
343
336
 
@@ -2887,7 +2880,7 @@ void fio_close(intptr_t uuid) {
2887
2880
  }
2888
2881
  if (uuid_data(uuid).packet || uuid_data(uuid).sock_lock) {
2889
2882
  uuid_data(uuid).close = 1;
2890
- fio_poll_add_write(fio_uuid2fd(uuid));
2883
+ fio_force_event(uuid, FIO_EVENT_ON_READY);
2891
2884
  return;
2892
2885
  }
2893
2886
  fio_force_close(uuid);
@@ -3019,6 +3012,7 @@ test_errno:
3019
3012
  case ENOSPC: /* fallthrough */
3020
3013
  case EADDRNOTAVAIL: /* fallthrough */
3021
3014
  case EINTR:
3015
+ case 0:
3022
3016
  return 1;
3023
3017
  case EFAULT:
3024
3018
  FIO_LOG_ERROR("fio_flush EFAULT - possible memory address error sent to "
@@ -3032,8 +3026,8 @@ test_errno:
3032
3026
  fio_force_close(uuid);
3033
3027
  return -1;
3034
3028
  }
3035
- fprintf(stderr, "UUID error: %p (%d)\n", (void *)uuid, errno);
3036
- perror("No errno handler");
3029
+ FIO_LOG_DEBUG("UUID error: %p (%d): %s\n", (void *)uuid, errno,
3030
+ strerror(errno));
3037
3031
  return 0;
3038
3032
 
3039
3033
  invalid:
@@ -3099,6 +3093,19 @@ const fio_rw_hook_s FIO_DEFAULT_RW_HOOKS = {
3099
3093
  .cleanup = fio_hooks_default_cleanup,
3100
3094
  };
3101
3095
 
3096
+ static inline void fio_rw_hook_validate(fio_rw_hook_s *rw_hooks) {
3097
+ if (!rw_hooks->read)
3098
+ rw_hooks->read = fio_hooks_default_read;
3099
+ if (!rw_hooks->write)
3100
+ rw_hooks->write = fio_hooks_default_write;
3101
+ if (!rw_hooks->flush)
3102
+ rw_hooks->flush = fio_hooks_default_flush;
3103
+ if (!rw_hooks->before_close)
3104
+ rw_hooks->before_close = fio_hooks_default_before_close;
3105
+ if (!rw_hooks->cleanup)
3106
+ rw_hooks->cleanup = fio_hooks_default_cleanup;
3107
+ }
3108
+
3102
3109
  /**
3103
3110
  * Replaces an existing read/write hook with another from within a read/write
3104
3111
  * hook callback.
@@ -3112,19 +3119,10 @@ int fio_rw_hook_replace_unsafe(intptr_t uuid, fio_rw_hook_s *rw_hooks,
3112
3119
  int replaced = -1;
3113
3120
  uint8_t was_locked;
3114
3121
  intptr_t fd = fio_uuid2fd(uuid);
3115
- if (!rw_hooks->read)
3116
- rw_hooks->read = fio_hooks_default_read;
3117
- if (!rw_hooks->write)
3118
- rw_hooks->write = fio_hooks_default_write;
3119
- if (!rw_hooks->flush)
3120
- rw_hooks->flush = fio_hooks_default_flush;
3121
- if (!rw_hooks->before_close)
3122
- rw_hooks->before_close = fio_hooks_default_before_close;
3123
- if (!rw_hooks->cleanup)
3124
- rw_hooks->cleanup = fio_hooks_default_cleanup;
3122
+ fio_rw_hook_validate(rw_hooks);
3125
3123
  /* protect against some fulishness... but not all of it. */
3126
3124
  was_locked = fio_trylock(&fd_data(fd).sock_lock);
3127
- if (fd2uuid(fd) == uuid) {
3125
+ if (uuid_is_valid(uuid)) {
3128
3126
  fd_data(fd).rw_hooks = rw_hooks;
3129
3127
  fd_data(fd).rw_udata = udata;
3130
3128
  replaced = 0;
@@ -3138,16 +3136,7 @@ int fio_rw_hook_replace_unsafe(intptr_t uuid, fio_rw_hook_s *rw_hooks,
3138
3136
  int fio_rw_hook_set(intptr_t uuid, fio_rw_hook_s *rw_hooks, void *udata) {
3139
3137
  if (fio_is_closed(uuid))
3140
3138
  goto invalid_uuid;
3141
- if (!rw_hooks->read)
3142
- rw_hooks->read = fio_hooks_default_read;
3143
- if (!rw_hooks->write)
3144
- rw_hooks->write = fio_hooks_default_write;
3145
- if (!rw_hooks->flush)
3146
- rw_hooks->flush = fio_hooks_default_flush;
3147
- if (!rw_hooks->before_close)
3148
- rw_hooks->before_close = fio_hooks_default_before_close;
3149
- if (!rw_hooks->cleanup)
3150
- rw_hooks->cleanup = fio_hooks_default_cleanup;
3139
+ fio_rw_hook_validate(rw_hooks);
3151
3140
  intptr_t fd = fio_uuid2fd(uuid);
3152
3141
  fio_rw_hook_s *old_rw_hooks;
3153
3142
  void *old_udata;
@@ -3249,7 +3238,6 @@ static int fio_attach__internal(void *uuid_, void *protocol_) {
3249
3238
  /* adding a new uuid to the reactor */
3250
3239
  fio_poll_add(fio_uuid2fd(uuid));
3251
3240
  }
3252
- fio_max_fd_min(fio_uuid2fd(uuid));
3253
3241
  return 0;
3254
3242
 
3255
3243
  invalid_uuid:
@@ -3510,18 +3498,19 @@ static void fio_on_fork(void) {
3510
3498
  fio_poll_init();
3511
3499
  fio_state_callback_on_fork();
3512
3500
 
3501
+ /* don't pass open connections belonging to the parent onto the child. */
3513
3502
  const size_t limit = fio_data->capa;
3514
3503
  for (size_t i = 0; i < limit; ++i) {
3515
3504
  fd_data(i).sock_lock = FIO_LOCK_INIT;
3516
3505
  fd_data(i).protocol_lock = FIO_LOCK_INIT;
3517
- if (fd_data(i).protocol) {
3506
+ if (fd_data(i).protocol && fd_data(i).open) {
3507
+ /* open without protocol might be waiting for the child (listening) */
3518
3508
  fd_data(i).protocol->rsv = 0;
3519
3509
  fio_force_close(fd2uuid(i));
3520
3510
  }
3521
3511
  }
3522
3512
 
3523
3513
  fio_pubsub_on_fork();
3524
- fio_max_fd_shrink();
3525
3514
  uint16_t old_active = fio_data->active;
3526
3515
  fio_data->active = 0;
3527
3516
  fio_defer_perform();
@@ -3681,24 +3670,30 @@ static void fio_review_timeout(void *arg, void *ignr) {
3681
3670
  uint16_t timeout = fd_data(fd).timeout;
3682
3671
  if (!timeout)
3683
3672
  timeout = 300; /* enforced timout settings */
3684
- if (!fd_data(fd).protocol || (fd_data(fd).active + timeout >= review))
3673
+ if (!fd_data(fd).open || fd_data(fd).active + timeout >= review)
3685
3674
  goto finish;
3686
- tmp = protocol_try_lock(fd, FIO_PR_LOCK_STATE);
3687
- if (!tmp) {
3688
- if (errno == EBADF)
3689
- goto finish;
3690
- goto reschedule;
3675
+ if (fd_data(fd).protocol) {
3676
+ tmp = protocol_try_lock(fd, FIO_PR_LOCK_STATE);
3677
+ if (!tmp) {
3678
+ if (errno == EBADF)
3679
+ goto finish;
3680
+ goto reschedule;
3681
+ }
3682
+ if (prt_meta(tmp).locks[FIO_PR_LOCK_TASK] ||
3683
+ prt_meta(tmp).locks[FIO_PR_LOCK_WRITE])
3684
+ goto unlock;
3685
+ fio_defer_push_task(deferred_ping, (void *)fio_fd2uuid((int)fd), NULL);
3686
+ unlock:
3687
+ protocol_unlock(tmp, FIO_PR_LOCK_STATE);
3688
+ } else {
3689
+ /* open FD but no protocol? RW hook thing or listening sockets? */
3690
+ if (fd_data(fd).rw_hooks != &FIO_DEFAULT_RW_HOOKS)
3691
+ fio_close(fd2uuid(fd));
3691
3692
  }
3692
- if (prt_meta(tmp).locks[FIO_PR_LOCK_TASK] ||
3693
- prt_meta(tmp).locks[FIO_PR_LOCK_WRITE])
3694
- goto unlock;
3695
- fio_defer_push_task(deferred_ping, (void *)fio_fd2uuid((int)fd), NULL);
3696
- unlock:
3697
- protocol_unlock(tmp, FIO_PR_LOCK_STATE);
3698
3693
  finish:
3699
3694
  do {
3700
3695
  fd++;
3701
- } while (!fd_data(fd).protocol && (fd <= fio_data->max_protocol_fd));
3696
+ } while (!fd_data(fd).open && (fd <= fio_data->max_protocol_fd));
3702
3697
 
3703
3698
  if (fio_data->max_protocol_fd < fd) {
3704
3699
  fio_data->need_review = 1;
@@ -3714,7 +3709,6 @@ static void fio_cycle_schedule_events(void) {
3714
3709
  static time_t last_to_review = 0;
3715
3710
  fio_mark_time();
3716
3711
  fio_timer_schedule();
3717
- fio_max_fd_shrink();
3718
3712
  if (fio_signal_children_flag) {
3719
3713
  /* hot restart support */
3720
3714
  fio_signal_children_flag = 0;
@@ -3923,16 +3917,22 @@ void fio_start FIO_IGNORE_MACRO(struct fio_start_args args) {
3923
3917
  fio_data->is_worker = 0;
3924
3918
 
3925
3919
  fio_state_callback_force(FIO_CALL_PRE_START);
3926
-
3927
3920
  FIO_LOG_INFO(
3928
3921
  "Server is running %u %s X %u %s with facil.io " FIO_VERSION_STRING
3929
3922
  " (%s)\n"
3923
+ #if HAVE_OPENSSL
3924
+ "* Linked to %s\n"
3925
+ #endif
3930
3926
  "* Detected capacity: %d open file limit\n"
3931
3927
  "* Root pid: %d\n"
3932
3928
  "* Press ^C to stop\n",
3933
3929
  fio_data->workers, fio_data->workers > 1 ? "workers" : "worker",
3934
3930
  fio_data->threads, fio_data->threads > 1 ? "threads" : "thread",
3935
- fio_engine(), fio_data->capa, (int)fio_data->parent);
3931
+ fio_engine(),
3932
+ #if HAVE_OPENSSL
3933
+ OpenSSL_version(0),
3934
+ #endif
3935
+ fio_data->capa, (int)fio_data->parent);
3936
3936
 
3937
3937
  if (args.workers > 1) {
3938
3938
  for (int i = 0; i < args.workers && fio_data->active; ++i) {