midi-smtp-server 3.0.3 → 3.1.2

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: a57da18e0a784683fa297898113f75967fb3adb852270e22b733f7b8bd29e7d0
4
- data.tar.gz: 95d9bdb5940ddcd8d49fbf3dd0f7b8f023235d58de00ee1c49be0aeb97da3101
3
+ metadata.gz: 0c84077c17fd5b68cffd37292da0e473245f16662041fb9a17218cd897961730
4
+ data.tar.gz: 299e676ad777d512b53d6c81a2a3b696eca00fb2d11adcaf2a3f24b6075c5fe1
5
5
  SHA512:
6
- metadata.gz: 37260846efd563b0b66ecf25e50b61b521c21a653a100cac2b2bceb8d40ed02c622b056f2e7835715568fe82317e5527148a67da4ba7d2cb840c84dda655ca45
7
- data.tar.gz: bc79e07e38642e758b3658a2776afcd9605d75e8061d77c5dd4ea21829330ac02e65c1db07faf45a7916f00996dcba2799842ec2e1aafdb1eea3518dcff7ffed
6
+ metadata.gz: 68446f6bb248f579689091fa094752f95db702ca6fc9f6de5588b68c1c564ee45499d71acee7b674b8784b78403a275e15afb5249e867571542e96c93ae608bd
7
+ data.tar.gz: 65c78a6d3ec2241756befc2a80a03691e74b37eed18093c0796ebd5a420f522b44f514e77b904f1a2ab7a97fb5862daaf7da2bcc3a5c2ba20000079111c3e994
data/CHANGELOG.md CHANGED
@@ -4,9 +4,24 @@ We suggest everybody using MidiSmtpServer to switch at least to latest 2.3.y. or
4
4
 
5
5
  For upgrades from previous versions or outdated _MiniSmtpServer_ gem you may follow the guides at [Appendix Upgrade](https://midi-smtp-server.readthedocs.io/appendix_upgrade/) to get your code ready for the latest releases.
6
6
 
7
+
8
+ #### 3.1.2 (2023-05-06)
9
+
10
+ 1. Minor fix for backwards compatibility to method `start` when not using pre_fork options.
11
+
12
+
13
+ #### 3.1.1 (2023-02-26)
14
+
15
+ 1. Add option to additional activate [pre-forking workers](https://midi-smtp-server.readthedocs.io/feature_load_balancing/#pre-forking) (beta) ([check issue 42](https://github.com/4commerce-technologies-AG/midi-smtp-server/issues/42))
16
+ 2. Adjust sleep idle time while in command and data loop to speedup processing ([check issue 47](https://github.com/4commerce-technologies-AG/midi-smtp-server/issues/47))
17
+ 3. Mark the current default value `0.1` for `io_waitreadable_sleep` as deprecated, will become `0.03` in a future version.
18
+ 4. Modify github workflow and apply testing of ruby 3.1 and ruby 3.2
19
+ 5. Generate updated openssl test certificates for TLS tests
20
+
21
+
7
22
  #### 3.0.3 (2022-02-12)
8
23
 
9
- 1. Critical fix for thread safety ([check issue 29](https://github.com/4commerce-technologies-AG/midi-smtp-server/issues/39))
24
+ 1. Critical fix for thread safety ([check issue 39](https://github.com/4commerce-technologies-AG/midi-smtp-server/issues/39))
10
25
  2. Fix tests using net/smtp '>= 0.3.1'
11
26
 
12
27
 
@@ -29,14 +44,13 @@ For upgrades from previous versions or outdated _MiniSmtpServer_ gem you may fol
29
44
  10. Dropped deprecated empty wildcard `""` support on initialize - please use specific hostnames and / or ip-addresses or star wildcard `"*"` only
30
45
  11. Align tests with Rubocop style and coding enforcements
31
46
  12. Added `rake` tasks for testing and linting, checkout `bundle exec rake -T`
32
- 13. Re-defined arguments of methods `new`, `join`, `stop` as keyword arguments, check [minor incompatability: upgrade to 3.x](https://midi-smtp-server.readthedocs.io/appendix_upgrade/#upgrade-to-3x)
47
+ 13. Re-defined arguments of methods `new`, `join`, `stop` as keyword arguments, check [minor incompatibility: upgrade to 3.x](https://midi-smtp-server.readthedocs.io/appendix_upgrade/#upgrade-to-3x)
33
48
  14. Enhance the slack recipe in cookbook for [Docker usage](https://github.com/4commerce-technologies-AG/midi-smtp-server/tree/master/cookbook/recipe-slack)
34
49
 
35
50
 
36
51
  #### 2.3.3 (2022-02-12)
37
52
 
38
- 1. Critical fix for thread safety ([check issue 29](https://github.com/4commerce-technologies-AG/midi-smtp-server/issues/39))
39
- 2. Fix tests using net/smtp '>= 0.3.1'
53
+ 1. Critical fix for thread safety ([check issue 39](https://github.com/4commerce-technologies-AG/midi-smtp-server/issues/39))
40
54
 
41
55
 
42
56
  #### 2.3.2 (2020-01-21)
@@ -61,7 +75,7 @@ For upgrades from previous versions or outdated _MiniSmtpServer_ gem you may fol
61
75
 
62
76
  1. Support [IPv4 and IPv6 (documentation)](https://midi-smtp-server.readthedocs.io/instantiate/#ipv4-and-ipv6-ready)
63
77
  2. Support binding of [multiple ports and hosts / ip addresses](https://midi-smtp-server.readthedocs.io/instantiate/#ports-and-addresses)
64
- 3. Handle [utilization of connections and processings](https://midi-smtp-server.readthedocs.io/feature_utilization/)
78
+ 3. Handle [utilization of connections and processings](https://midi-smtp-server.readthedocs.io/feature_load_balancing/)
65
79
  4. Support of RFC(2)822 [CR LF modes](https://midi-smtp-server.readthedocs.io/feature_cr_lf_modes/)
66
80
  5. Support (optionally) SMTP [PIPELINING](https://tools.ietf.org/html/rfc2920) extension
67
81
  6. Support (optionally) SMTP [8BITMIME](https://midi-smtp-server.readthedocs.io/feature_8bitmime_smtputf8/) extension
@@ -104,7 +118,7 @@ For upgrades from previous versions or outdated _MiniSmtpServer_ gem you may fol
104
118
 
105
119
  #### 2.x
106
120
 
107
- 1. Modulized
121
+ 1. Modularized
108
122
  2. Removed dependency to GServer
109
123
  3. Additional events to interact with
110
124
  4. Use logger to log several messages from severity :debug up to :fatal
data/MIT-LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2014 - 2022 Tom Freudenberg
3
+ Copyright (c) 2014 - 2023 Tom Freudenberg
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -19,7 +19,7 @@ MidiSmtpServer is the highly customizable ruby SMTP-Server and SMTP-Service libr
19
19
 
20
20
  As a library it is mainly designed to be integrated into your projects as serving a SMTP-Server service. The lib will do nothing with your mail and you have to create your own event functions to handle and operate on incoming mails. We are using this in conjunction with [Mikel Lindsaar](https://github.com/mikel) great Mail component (https://github.com/mikel/mail). Time to run your own SMTP-Server service.
21
21
 
22
- Checkout all the features and improvements (3.0.1 Logging enhancement, 2.3.x Multiple ports and addresses, 2.2.x Encryption [StartTLS], 2.1.0 Authentication [AUTH], 2.1.1 significant speed improvement, etc.) and get more details from section [changes and updates](https://github.com/4commerce-technologies-AG/midi-smtp-server#changes-and-updates).
22
+ Checkout all the features and improvements (3.1.1 Process parallelization, 3.0.1 Logging enhancement, 2.3.x Multiple ports and addresses, 2.2.x Encryption [StartTLS], 2.1.0 Authentication [AUTH], 2.1.1 significant speed improvement, etc.) and get more details from section [changes and updates](https://github.com/4commerce-technologies-AG/midi-smtp-server#changes-and-updates).
23
23
 
24
24
  MidiSmtpServer is an extremely flexible library and almost any aspect of SMTP communications can be handled by deriving its events and using its configuration options.
25
25
 
@@ -40,10 +40,10 @@ class MySmtpd < MidiSmtpServer::Smtpd
40
40
  logger.debug("[#{ctx[:envelope][:from]}] for recipient(s): [#{ctx[:envelope][:to]}]...")
41
41
 
42
42
  # Just decode message once to make sure, that this message ist readable
43
- @mail = Mail.read_from_string(ctx[:message][:data])
43
+ mail = Mail.read_from_string(ctx[:message][:data])
44
44
 
45
45
  # handle incoming mail, just show the message subject
46
- logger.debug(@mail.subject)
46
+ logger.debug(mail.subject)
47
47
  end
48
48
 
49
49
  end
@@ -64,10 +64,10 @@ This source code shows the example to receive messages via SMTP and store them t
64
64
  # get each message after DATA <message> .
65
65
  def on_message_data_event(ctx)
66
66
  # Just decode message once to make sure, that this message ist readable
67
- @mail = Mail.read_from_string(ctx[:message])
67
+ mail = Mail.read_from_string(ctx[:message])
68
68
 
69
69
  # Publish to rabbit
70
- @bunny_exchange.publish(@mail.to_s, :headers => { 'x-smtp' => @mail.header.to_s }, :routing_key => "to_queue")
70
+ @bunny_exchange.publish(mail.to_s, :headers => { 'x-smtp' => mail.header.to_s }, :routing_key => "to_queue")
71
71
  end
72
72
  ```
73
73
 
@@ -96,35 +96,35 @@ Read the [MidiSmtpServer Documentation](https://midi-smtp-server.readthedocs.io/
96
96
 
97
97
  ## Reliable code
98
98
 
99
- Since version 2.3 implementation and integration tests by minitest framework are added to this repository. While the implementation tests are mostly checking the components, the integration tests try to verify the correct exchange of messages for different scenarios. In addtion all sources are checked by rubocop to ensure they fit to the style guides.
99
+ Since version 2.3 implementation and integration tests by minitest framework are added to this repository. While the implementation tests are mostly checking the components, the integration tests try to verify the correct exchange of messages for different scenarios. Last but not least the stress tests do catch some rare conditions to make sure that no information is leaving its thread and process. In addition all sources are checked by rubocop to ensure they fit to the style guides.
100
100
 
101
101
  You may run all rubocop tests through the `rake` helper:
102
102
 
103
- ``` bash
103
+ ```bash
104
104
  bundle exec rake rubocop
105
105
  ```
106
106
 
107
107
  You may also run all tests through the `rake` helper:
108
108
 
109
- ``` bash
109
+ ```bash
110
110
  bundle exec rake test:all
111
111
  ```
112
112
 
113
113
  or with more verbose output:
114
114
 
115
- ``` bash
115
+ ```bash
116
116
  bundle exec rake test:all v=1
117
117
  ```
118
118
 
119
- To just run just a part of the tests, you may select the `specs`, `unit` or `integration` tests:
119
+ To just run just a part of the tests, you may select the `specs`, `unit`, `integration` or `stress` tests:
120
120
 
121
- ``` bash
121
+ ```bash
122
122
  bundle exec rake test:specs
123
123
  ```
124
124
 
125
125
  To just run some selected (by regular expression) tests, you may use the `T=filter` option. The example will run only the tests and specs containing the word _connections_ in their method_name or describe_text:
126
126
 
127
- ``` bash
127
+ ```bash
128
128
  bundle exec rake test:all v=1 T=connections
129
129
  ```
130
130
 
@@ -146,10 +146,10 @@ We suggest everybody using MidiSmtpServer to switch at least to latest 2.3.y. or
146
146
 
147
147
  For upgrades from previous versions or outdated _MiniSmtpServer_ gem you may follow the guides (see appendix) how to change your existing code to be compatible with the latest releases.
148
148
 
149
- #### Latest release: 3.0.3 (2022-02-12)
150
149
 
151
- 1. Critical fix for thread safety ([check issue 29](https://github.com/4commerce-technologies-AG/midi-smtp-server/issues/39))
152
- 2. Fix tests using net/smtp '>= 0.3.1'
150
+ #### Latest release: 3.1.2 (2023-05-06)
151
+
152
+ 1. Minor fix for backwards compatibility to method `start` when not using pre_fork options.
153
153
 
154
154
 
155
155
  #### Changelog history
@@ -171,6 +171,7 @@ Checkout the [Appendix Upgrade](https://midi-smtp-server.readthedocs.io/appendix
171
171
  You may find, use and download the gem package on [RubyGems.org](http://rubygems.org/gems/midi-smtp-server).
172
172
 
173
173
  [![Gem Version](https://badge.fury.io/rb/midi-smtp-server.svg)](http://badge.fury.io/rb/midi-smtp-server) &nbsp;
174
+ [![Ruby](https://github.com/4commerce-technologies-AG/midi-smtp-server/actions/workflows/push-and-pr-testing-ruby-ci.yml/badge.svg)](https://github.com/4commerce-technologies-AG/midi-smtp-server/actions/workflows/push-and-pr-testing-ruby-ci.yml) &nbsp;
174
175
 
175
176
  <br>
176
177
 
@@ -189,6 +190,6 @@ You may find, use and download the gem package on [RubyGems.org](http://rubygems
189
190
 
190
191
  Author: [Tom Freudenberg](http://about.me/tom.freudenberg)
191
192
 
192
- [MidiSmtpServer Class](https://github.com/4commerce-technologies-AG/midi-smtp-server/) is inspired from [MiniSmtpServer Class](https://github.com/aarongough/mini-smtp-server) and code written by [Aaron Gough](https://github.com/aarongough) and [Peter Cooper](http://peterc.org/)
193
+ [MidiSmtpServer Class](https://github.com/4commerce-technologies-AG/midi-smtp-server/) is inspired from [MiniSmtpServer Class](https://github.com/aarongough/mini-smtp-server) and code written by [Aaron Gough](https://github.com/aarongough) and [Peter Cooper](https://github.com/4commerce-technologies-AG/midi-smtp-server#author--credits)
193
194
 
194
- Copyright (c) 2014-2022 [Tom Freudenberg](http://www.4commerce.de/), [4commerce technologies AG](http://www.4commerce.de/), released under the MIT license
195
+ Copyright (c) 2014-2023 [Tom Freudenberg](https://github.com/TomFreudenberg), [4commerce technologies AG](https://www.4commerce.de/), released under the MIT license
@@ -10,7 +10,7 @@ module MidiSmtpServer
10
10
  class SmtpdIOTimeoutException < RuntimeError
11
11
  end
12
12
 
13
- # special internal exception to signal buffer size exceedance
13
+ # special internal exception to signal buffer size exceeding
14
14
  # while waiting for incoming data line
15
15
  class SmtpdIOBufferOverrunException < RuntimeError
16
16
  end
@@ -286,7 +286,7 @@ module MidiSmtpServer
286
286
 
287
287
  end
288
288
 
289
- # Status when expeting CRLF sequence as line breaks (RFC(2)822)
289
+ # Status when expecting CRLF sequence as line breaks (RFC(2)822)
290
290
 
291
291
  # 500 Bad input, missing CRLF line termination
292
292
  class Smtpd500CrLfSequenceException < SmtpdException
@@ -69,9 +69,9 @@ module MidiSmtpServer
69
69
  @ssl_context.cert.sign @ssl_context.key, OpenSSL::Digest.new('SHA256')
70
70
  logger.debug("SSL: generated test certificate\r\n#{@ssl_context.cert.to_text}")
71
71
  else
72
- # if any is set, test the pathes
73
- raise "File \”#{@cert_path}\" does not exist or is not a regular file. Could not load certificate." unless File.file?(@cert_path.to_s)
74
- raise "File \”#{@key_path}\" does not exist or is not a regular file. Could not load private key." unless @key_path.nil? || File.file?(@key_path.to_s)
72
+ # if any is set, test the paths
73
+ raise "File \"#{@cert_path}\" does not exist or is not a regular file. Could not load certificate." unless File.file?(@cert_path.to_s)
74
+ raise "File \"#{@key_path}\" does not exist or is not a regular file. Could not load private key." unless @key_path.nil? || File.file?(@key_path.to_s)
75
75
  # try to load certificate and key
76
76
  cert_lines = File.read(@cert_path.to_s).lines
77
77
  # check if the cert file contains a chain of certs
@@ -6,12 +6,12 @@ module MidiSmtpServer
6
6
  module VERSION
7
7
 
8
8
  MAJOR = 3
9
- MINOR = 0
10
- TINY = 3
9
+ MINOR = 1
10
+ TINY = 2
11
11
 
12
12
  STRING = [MAJOR, MINOR, TINY].compact.join('.')
13
13
 
14
- DATE = '2022-02-12'
14
+ DATE = '2023-05-06'
15
15
 
16
16
  end
17
17
 
@@ -17,15 +17,17 @@ module MidiSmtpServer
17
17
  # default values
18
18
  DEFAULT_SMTPD_HOST = '127.0.0.1'
19
19
  DEFAULT_SMTPD_PORT = 2525
20
+ DEFAULT_SMTPD_PRE_FORK = 0
20
21
  DEFAULT_SMTPD_MAX_PROCESSINGS = 4
21
22
 
22
- # default values for conformity to RFC(2)822 and addtionals
23
+ # default values for conformity to RFC(2)822 and additional
23
24
  # if interested in details, checkout discussion on issue queue at:
24
25
  # https://github.com/4commerce-technologies-AG/midi-smtp-server/issues/16
25
26
  CRLF_MODES = [:CRLF_ENSURE, :CRLF_LEAVE, :CRLF_STRICT].freeze
26
27
  DEFAULT_CRLF_MODE = :CRLF_ENSURE
27
28
 
28
29
  # default values for IO operations
30
+ DEFAULT_IO_WAITREADABLE_SLEEP = 0.1
29
31
  DEFAULT_IO_CMD_TIMEOUT = 30
30
32
  DEFAULT_IO_BUFFER_CHUNK_SIZE = 4 * 1024
31
33
  DEFAULT_IO_BUFFER_MAX_SIZE = 1 * 1024 * 1024
@@ -34,7 +36,7 @@ module MidiSmtpServer
34
36
  DEFAULT_PIPELINING_EXTENSION_ENABLED = false
35
37
  DEFAULT_INTERNATIONALIZATION_EXTENSIONS_ENABLED = false
36
38
 
37
- # Authentification modes
39
+ # Authentication modes
38
40
  AUTH_MODES = [:AUTH_FORBIDDEN, :AUTH_OPTIONAL, :AUTH_REQUIRED].freeze
39
41
  DEFAULT_AUTH_MODE = :AUTH_FORBIDDEN
40
42
 
@@ -43,31 +45,42 @@ module MidiSmtpServer
43
45
 
44
46
  public
45
47
 
46
- # Start the server
48
+ # Create the server
47
49
  def start
48
- serve_service
50
+ create_service
51
+ # immediately attach the threads to running single master process (default)
52
+ attach_threads unless pre_fork?
49
53
  end
50
54
 
51
55
  # Stop the server
52
56
  def stop(wait_seconds_before_close: 2, gracefully: true)
53
- # always signal shutdown
54
- shutdown if gracefully
55
- # wait if some connection(s) need(s) more time to handle shutdown
56
- sleep wait_seconds_before_close if connections?
57
- # drop tcp_servers while raising SmtpdStopServiceException
58
- @connections_mutex.synchronize do
59
- @tcp_server_threads.each do |tcp_server_thread|
60
- # use safe navigation (&.) to make sure that obj exists like ... if tcp_server_thread
61
- tcp_server_thread&.raise SmtpdStopServiceException
57
+ begin
58
+ # signal pre_forked workers to stop
59
+ @workers.each { |worker_pid| Process.kill(:TERM, worker_pid) } if pre_fork? && master?
60
+ # always signal shutdown
61
+ shutdown if gracefully
62
+ # wait if some connection(s) need(s) more time to handle shutdown
63
+ sleep wait_seconds_before_close if connections?
64
+ # drop tcp_servers while raising SmtpdStopServiceException
65
+ @connections_mutex.synchronize do
66
+ @tcp_server_threads.each do |tcp_server_thread|
67
+ # use safe navigation (&.) to make sure that obj exists like ... if tcp_server_thread
68
+ tcp_server_thread&.raise SmtpdStopServiceException
69
+ end
62
70
  end
71
+
72
+ ensure
73
+ # check for removing TCPServers
74
+ @tcp_servers.each { |tcp_server| remove_tcp_server(tcp_server) } if master?
63
75
  end
76
+
64
77
  # wait if some connection(s) still need(s) more time to come down
65
78
  sleep wait_seconds_before_close if connections? || !stopped?
66
79
  end
67
80
 
68
81
  # Returns true if the server has stopped.
69
82
  def stopped?
70
- @tcp_server_threads.empty? && @tcp_servers.empty?
83
+ master? ? @workers.empty? && @tcp_server_threads.empty? && @tcp_servers.empty? : @tcp_server_threads.empty?
71
84
  end
72
85
 
73
86
  # Schedule a shutdown for the server
@@ -100,34 +113,67 @@ module MidiSmtpServer
100
113
  @processings.any?
101
114
  end
102
115
 
116
+ # Return if in pre-fork mode
117
+ def pre_fork?
118
+ @pre_fork > 1
119
+ end
120
+
121
+ # Return if this is the master process
122
+ def master?
123
+ !@is_forked
124
+ end
125
+
126
+ # Return if this is a forked worker process
127
+ def worker?
128
+ @is_forked
129
+ end
130
+
131
+ # Return number of forked worker processes
132
+ def workers
133
+ @workers.size
134
+ end
135
+
136
+ # Return if has active forked worker processes
137
+ def workers?
138
+ @workers.any?
139
+ end
140
+
103
141
  # Join with the server thread(s)
104
142
  # before joining the server threads, check and wait optionally a few seconds
105
143
  # to let the service(s) come up
106
144
  def join(sleep_seconds_before_join: 1)
107
145
  # check already existing TCPServers
108
146
  return if @tcp_servers.empty?
109
- # wait some seconds before joininig the upcoming threads
110
- # and check that all TCPServers gots one thread
111
- while (@tcp_server_threads.length < @tcp_servers.length) && sleep_seconds_before_join.positive?
112
- sleep_seconds_before_join -= 1
113
- sleep 1
114
- end
115
- # try to join any thread
116
- begin
117
- @tcp_server_threads.each(&:join)
147
+ # check number of processes to pre-fork
148
+
149
+ if pre_fork?
150
+ # create a number of pre-fork processes and attach and join threads within workers
151
+ @pre_fork.times do
152
+ # append worker pid to list of workers
153
+ @workers << fork do
154
+ # set state for a forked process
155
+ @is_forked = true
156
+ # just attach and join the threads to forked worker process
157
+ attach_threads
158
+ join_threads(sleep_seconds_before_join: sleep_seconds_before_join)
159
+ end
160
+ end
161
+ # Blocking wait until each worker process has been finished
162
+ @workers.each { |pid| Process.waitpid(pid) }
118
163
 
119
- # catch ctrl-c to stop service
120
- rescue Interrupt
164
+ else
165
+ # just join the threads to running single master process (default)
166
+ join_threads(sleep_seconds_before_join: sleep_seconds_before_join)
121
167
  end
122
168
  end
123
169
 
124
- # Array of ports on which to bind, set as string seperated by commata like '2525, 3535' or '2525:3535, 2525'
170
+ # Array of ports on which to bind, set as string separated by commata like '2525, 3535' or '2525:3535, 2525'
125
171
  def ports
126
172
  # prevent original array from being changed
127
173
  @ports.dup
128
174
  end
129
175
 
130
- # Array of hosts / ip_addresses on which to bind, set as string seperated by commata like 'name.domain.com, 127.0.0.1, ::1'
176
+ # Array of hosts / ip_addresses on which to bind, set as string separated by commata like 'name.domain.com, 127.0.0.1, ::1'
131
177
  def hosts
132
178
  # prevent original array from being changed
133
179
  @hosts.dup
@@ -139,7 +185,7 @@ module MidiSmtpServer
139
185
  @addresses.dup
140
186
  end
141
187
 
142
- # Current TLS OpenSSL::SSL::SSLContext when initalized by :TLS_OPTIONAL, :TLS_REQUIRED
188
+ # Current TLS OpenSSL::SSL::SSLContext when initialized by :TLS_OPTIONAL, :TLS_REQUIRED
143
189
  def ssl_context
144
190
  @tls&.ssl_context
145
191
  end
@@ -150,15 +196,17 @@ module MidiSmtpServer
150
196
  attr_reader :max_connections
151
197
  # CRLF handling based on conformity to RFC(2)822
152
198
  attr_reader :crlf_mode
199
+ # Time in seconds to sleep on IO::WaitReadable exception
200
+ attr_reader :io_waitreadable_sleep
153
201
  # Maximum time in seconds to wait for a complete incoming data line, as a FixNum
154
202
  attr_reader :io_cmd_timeout
155
203
  # Bytes to read non-blocking from socket into buffer, as a FixNum
156
204
  attr_reader :io_buffer_chunk_size
157
- # Maximum bytes to read as buffer before expecting completet incoming data line, as a FixNum
205
+ # Maximum bytes to read as buffer before expecting completed incoming data line, as a FixNum
158
206
  attr_reader :io_buffer_max_size
159
207
  # Flag if should do reverse DNS lookups on incoming connections
160
208
  attr_reader :do_dns_reverse_lookup
161
- # Authentification mode
209
+ # Authentication mode
162
210
  attr_reader :auth_mode
163
211
  # Encryption mode
164
212
  attr_reader :encrypt_mode
@@ -167,17 +215,19 @@ module MidiSmtpServer
167
215
  # handle SMTP 8BITMIME and SMTPUTF8 extension
168
216
  attr_reader :internationalization_extensions
169
217
 
170
- # logging object, may be overrriden by special loggers like YELL or others
218
+ # logging object, may be overridden by special loggers like YELL or others
171
219
  attr_reader :logger
172
220
 
173
221
  # Initialize SMTP Server class
174
222
  #
175
223
  # +ports+:: ports to listen on. Allows multiple ports like "2525, 3535" or "2525:3535, 2525". Default value = DEFAULT_SMTPD_PORT
176
224
  # +hosts+:: interface ip or hostname to listen on or "*" to listen on all interfaces, allows multiple hostnames and ip_addresses like "name.domain.com, 127.0.0.1, ::1". Default value = DEFAULT_SMTPD_HOST
225
+ # +pre_fork+:: number of processes to pre-fork to enable load balancing over multiple cores. Default value = DEFAULT_SMTPD_PRE_FORK
177
226
  # +max_processings+:: maximum number of simultaneous processed connections, this does not limit the number of concurrent TCP connections. Default value = DEFAULT_SMTPD_MAX_PROCESSINGS
178
227
  # +max_connections+:: maximum number of connections, this does limit the number of concurrent TCP connections (not set or nil => unlimited)
179
228
  # +crlf_mode+:: CRLF handling support (:CRLF_ENSURE [default], :CRLF_LEAVE, :CRLF_STRICT)
180
229
  # +do_dns_reverse_lookup+:: flag if this smtp server should do reverse DNS lookups on incoming connections
230
+ # +io_waitreadable_sleep+:: seconds to sleep in loop when no input data is available (DEFAULT_IO_WAITREADABLE_SLEEP)
181
231
  # +io_cmd_timeout+:: time in seconds to wait until complete line of data is expected (DEFAULT_IO_CMD_TIMEOUT, nil => disabled test)
182
232
  # +io_buffer_chunk_size+:: size of chunks (bytes) to read non-blocking from socket (DEFAULT_IO_BUFFER_CHUNK_SIZE)
183
233
  # +io_buffer_max_size+:: max size of buffer (max line length) until \lf ist expected (DEFAULT_IO_BUFFER_MAX_SIZE, nil => disabled test)
@@ -185,7 +235,7 @@ module MidiSmtpServer
185
235
  # +internationalization_extensions+:: set to true for support of SMTP 8BITMIME and SMTPUTF8 extensions (DEFAULT_INTERNATIONALIZATION_EXTENSIONS_ENABLED)
186
236
  # +auth_mode+:: enable builtin authentication support (:AUTH_FORBIDDEN [default], :AUTH_OPTIONAL, :AUTH_REQUIRED)
187
237
  # +tls_mode+:: enable builtin TLS support (:TLS_FORBIDDEN [default], :TLS_OPTIONAL, :TLS_REQUIRED)
188
- # +tls_cert_path+:: path to tls cerificate chain file
238
+ # +tls_cert_path+:: path to tls certificate chain file
189
239
  # +tls_key_path+:: path to tls key file
190
240
  # +tls_ciphers+:: allowed ciphers for connection
191
241
  # +tls_methods+:: allowed methods for protocol
@@ -196,10 +246,12 @@ module MidiSmtpServer
196
246
  def initialize(
197
247
  ports: DEFAULT_SMTPD_PORT,
198
248
  hosts: DEFAULT_SMTPD_HOST,
249
+ pre_fork: DEFAULT_SMTPD_PRE_FORK,
199
250
  max_processings: DEFAULT_SMTPD_MAX_PROCESSINGS,
200
251
  max_connections: nil,
201
252
  crlf_mode: nil,
202
253
  do_dns_reverse_lookup: nil,
254
+ io_waitreadable_sleep: nil,
203
255
  io_cmd_timeout: nil,
204
256
  io_buffer_chunk_size: nil,
205
257
  io_buffer_max_size: nil,
@@ -216,7 +268,7 @@ module MidiSmtpServer
216
268
  logger: nil,
217
269
  logger_severity: nil
218
270
  )
219
- # create an exposed logger to forward loggings to the on_logging_event
271
+ # create an exposed logger to forward logging to the on_logging_event
220
272
  @logger = MidiSmtpServer::ForwardingLogger.new(method(:on_logging_event))
221
273
 
222
274
  # external logging
@@ -231,6 +283,11 @@ module MidiSmtpServer
231
283
  logger.warn('Deprecated: "logger" was set on new! Please use "on_logging_event" instead.')
232
284
  end
233
285
 
286
+ # initialize as master process
287
+ @is_forked = false
288
+ # no forked worker processes
289
+ @workers = []
290
+
234
291
  # list of TCPServers
235
292
  @tcp_servers = []
236
293
  # list of running threads
@@ -311,12 +368,16 @@ module MidiSmtpServer
311
368
  end
312
369
  end
313
370
 
371
+ # read pre_fork
372
+ @pre_fork = pre_fork
373
+ raise 'Number of processes to pre-fork (pre_fork) must be zero or a positive integer greater than 1!' unless @pre_fork.is_a?(Integer) && (@pre_fork.zero? || pre_fork?)
314
374
  # read max_processings
315
375
  @max_processings = max_processings
316
376
  raise 'Number of simultaneous processings (max_processings) must be a positive integer!' unless @max_processings.is_a?(Integer) && @max_processings.positive?
317
377
  # check max_connections
318
378
  @max_connections = max_connections
319
- raise 'Number of concurrent connections is lower than number of simultaneous processings!' if @max_connections && @max_connections < @max_processings
379
+ raise 'Number of concurrent connections (max_connections) must be nil or a positive integer!' unless @max_connections.nil? || (@max_connections.is_a?(Integer) && @max_connections.positive?)
380
+ raise 'Number of concurrent connections (max_connections) is lower than number of simultaneous processings (max_processings)!' if !@max_connections.nil? && @max_connections < @max_processings
320
381
 
321
382
  # check for crlf mode
322
383
  @crlf_mode = crlf_mode.nil? ? DEFAULT_CRLF_MODE : crlf_mode
@@ -328,6 +389,7 @@ module MidiSmtpServer
328
389
  @do_dns_reverse_lookup = do_dns_reverse_lookup.nil? ? true : do_dns_reverse_lookup
329
390
 
330
391
  # io and buffer settings
392
+ @io_waitreadable_sleep = io_waitreadable_sleep.nil? ? DEFAULT_IO_WAITREADABLE_SLEEP : io_waitreadable_sleep
331
393
  @io_cmd_timeout = io_cmd_timeout.nil? ? DEFAULT_IO_CMD_TIMEOUT : io_cmd_timeout
332
394
  @io_buffer_chunk_size = io_buffer_chunk_size.nil? ? DEFAULT_IO_BUFFER_CHUNK_SIZE : io_buffer_chunk_size
333
395
  @io_buffer_max_size = io_buffer_max_size.nil? ? DEFAULT_IO_BUFFER_MAX_SIZE : io_buffer_max_size
@@ -336,9 +398,9 @@ module MidiSmtpServer
336
398
  @pipelining_extension = pipelining_extension.nil? ? DEFAULT_PIPELINING_EXTENSION_ENABLED : pipelining_extension
337
399
  @internationalization_extensions = internationalization_extensions.nil? ? DEFAULT_INTERNATIONALIZATION_EXTENSIONS_ENABLED : internationalization_extensions
338
400
 
339
- # check for authentification
401
+ # check for authentication
340
402
  @auth_mode = auth_mode.nil? ? DEFAULT_AUTH_MODE : auth_mode
341
- raise "Unknown authentification mode #{@auth_mode} was given!" unless AUTH_MODES.include?(@auth_mode)
403
+ raise "Unknown authentication mode #{@auth_mode} was given!" unless AUTH_MODES.include?(@auth_mode)
342
404
 
343
405
  # check for encryption
344
406
  @encrypt_mode = tls_mode.nil? ? DEFAULT_ENCRYPT_MODE : tls_mode
@@ -409,7 +471,7 @@ module MidiSmtpServer
409
471
  on_logging_event(ctx, Logger::DEBUG, "Client connect from #{ctx[:server][:remote_ip]}:#{ctx[:server][:remote_port]} to #{ctx[:server][:local_ip]}:#{ctx[:server][:local_port]}")
410
472
  end
411
473
 
412
- # event before DISONNECT
474
+ # event before DISCONNECT
413
475
  def on_disconnect_event(ctx)
414
476
  on_logging_event(ctx, Logger::DEBUG, "Client disconnect from #{ctx[:server][:remote_ip]}:#{ctx[:server][:remote_port]} on #{ctx[:server][:local_ip]}:#{ctx[:server][:local_port]}")
415
477
  end
@@ -420,13 +482,13 @@ module MidiSmtpServer
420
482
  # the value is not allowed to return CR nor LF chars and will be stripped
421
483
  def on_helo_event(ctx, helo_data) end
422
484
 
423
- # check the authentification on AUTH
485
+ # check the authentication on AUTH
424
486
  # if any value returned, that will be used for ongoing processing
425
487
  # otherwise the original value will be used for authorization_id
426
488
  def on_auth_event(ctx, authorization_id, authentication_id, authentication)
427
- # if authentification is used, override this event
489
+ # if authentication is used, override this event
428
490
  # and implement your own user management.
429
- # otherwise all authentifications are blocked per default
491
+ # otherwise all authentications are blocked per default
430
492
  on_logging_event(ctx, Logger::DEBUG, "Deny access from #{ctx[:server][:remote_ip]}:#{ctx[:server][:remote_port]} for #{authentication_id}" + (authorization_id == '' ? '' : "/#{authorization_id}") + " with #{authentication}")
431
493
  raise Smtpd535Exception
432
494
  end
@@ -473,8 +535,8 @@ module MidiSmtpServer
473
535
 
474
536
  protected
475
537
 
476
- # Start the listeners for all hosts
477
- def serve_service
538
+ # Prepare all listeners for all hosts
539
+ def create_service
478
540
  raise 'Service was already started' unless stopped?
479
541
 
480
542
  # set flag to signal shutdown by stop / shutdown command
@@ -485,12 +547,12 @@ module MidiSmtpServer
485
547
  # break address into ip_address and port and serve service
486
548
  ip_address = address.rpartition(':').first
487
549
  port = address.rpartition(':').last
488
- serve_service_on_ip_address_and_port(ip_address, port)
550
+ create_service_on_ip_address_and_port(ip_address, port)
489
551
  end
490
552
  end
491
553
 
492
- # Start the listener thread on single ip_address and port
493
- def serve_service_on_ip_address_and_port(ip_address, port)
554
+ # Prepare a listener on single ip_address and port
555
+ def create_service_on_ip_address_and_port(ip_address, port)
494
556
  # log information
495
557
  logger.info("Starting service on #{ip_address}:#{port}")
496
558
  # check that there is a specific ip_address defined
@@ -499,7 +561,47 @@ module MidiSmtpServer
499
561
  tcp_server = TCPServer.new(ip_address, port)
500
562
  # append this server to the list of TCPServers
501
563
  @tcp_servers << tcp_server
564
+ end
565
+
566
+ # Close and remove the given tcp_server
567
+ def remove_tcp_server(tcp_server)
568
+ begin
569
+ # drop the service
570
+ tcp_server.close
571
+ # remove from list
572
+ @tcp_servers.delete(tcp_server)
573
+ rescue StandardError
574
+ # ignore any error from here
575
+ end
576
+ end
502
577
 
578
+ # Join with the server thread(s)
579
+ # before joining the server threads, wait optionally a few seconds
580
+ # to let the service(s) come up
581
+ def join_threads(sleep_seconds_before_join: 1)
582
+ # wait some seconds before joining the upcoming threads
583
+ # and check that all TCPServers got one thread
584
+ while (@tcp_server_threads.length < @tcp_servers.length) && sleep_seconds_before_join.positive?
585
+ sleep_seconds_before_join -= 1
586
+ sleep 1
587
+ end
588
+ # try to join any thread
589
+ begin
590
+ @tcp_server_threads.each(&:join)
591
+
592
+ # catch ctrl-c to stop service
593
+ rescue Interrupt
594
+ end
595
+ end
596
+
597
+ # Create and attach threads to all listeners
598
+ def attach_threads
599
+ # loop for all TCPServers listener
600
+ @tcp_servers.each { |tcp_server| attach_thread(tcp_server) }
601
+ end
602
+
603
+ # Create and attach a thread to a listener
604
+ def attach_thread(tcp_server)
503
605
  # run thread until shutdown
504
606
  @tcp_server_threads << Thread.new do
505
607
  begin
@@ -527,7 +629,7 @@ module MidiSmtpServer
527
629
  ensure
528
630
  begin
529
631
  # always gracefully shutdown connection.
530
- # if the io object was overriden by the
632
+ # if the io object was overridden by the
531
633
  # result from serve_client() due to ssl
532
634
  # io, the ssl + io socket will be closed
533
635
  io.close
@@ -552,16 +654,7 @@ module MidiSmtpServer
552
654
  # log fatal error while starting new thread
553
655
  on_logging_event(nil, Logger::FATAL, "#{e} (#{e.class})".strip, err: e.clone)
554
656
  ensure
555
- begin
556
- # drop the service
557
- tcp_server.close
558
- # remove from list
559
- @tcp_servers.delete(tcp_server)
560
- # reset local var
561
- tcp_server = nil
562
- rescue StandardError
563
- # ignore any error from here
564
- end
657
+ # proper connection down?
565
658
  if shutdown?
566
659
  # wait for finishing opened connections
567
660
  @connections_mutex.synchronize do
@@ -604,7 +697,7 @@ module MidiSmtpServer
604
697
  on_connect_event(session[:ctx])
605
698
 
606
699
  # drop connection (respond 421) if too busy
607
- raise 'Abort connection while too busy, exceeding max_connections!' if max_connections && connections > max_connections
700
+ raise Smtpd421Exception, 'Abort connection while too busy, exceeding max_connections!' if !max_connections.nil? && connections > max_connections
608
701
 
609
702
  # check active processings for new client
610
703
  @connections_mutex.synchronize do
@@ -656,7 +749,7 @@ module MidiSmtpServer
656
749
  raise SmtpdIOTimeoutException if @io_cmd_timeout && Time.now.to_i - timestamp_timeout > @io_cmd_timeout
657
750
  # read chunks of input data until line-feed
658
751
  io_buffer << io.read_nonblock(@io_buffer_chunk_size)
659
- # check for buffersize
752
+ # check for buffer size
660
753
  raise SmtpdIOBufferOverrunException if @io_buffer_max_size && io_buffer.length > @io_buffer_max_size
661
754
  # check for lf in current io_buffer
662
755
  io_buffer_line_lf = io_buffer.index("\n")
@@ -665,7 +758,7 @@ module MidiSmtpServer
665
758
  # ignore exception when no input data is available yet
666
759
  rescue IO::WaitReadable
667
760
  # but wait a few moment to slow down system utilization
668
- sleep 0.1
761
+ sleep @io_waitreadable_sleep
669
762
  end
670
763
 
671
764
  # check if io_buffer is filled and contains already a line-feed
@@ -684,7 +777,7 @@ module MidiSmtpServer
684
777
  # handle input line based on @crlf_mode
685
778
  case crlf_mode
686
779
  when :CRLF_ENSURE
687
- # remove any \r or \n occurence from line
780
+ # remove any \r or \n occurrence from line
688
781
  line.delete!("\r\n")
689
782
  # log line, verbosity based on log severity and command sequence
690
783
  on_logging_event(session[:ctx], Logger::DEBUG, +'<<< ' << line << "\n") if session[:cmd_sequence] != :CMD_DATA
@@ -1235,7 +1328,7 @@ module MidiSmtpServer
1235
1328
  )
1236
1329
  end
1237
1330
 
1238
- # handle plain authentification
1331
+ # handle plain authentication
1239
1332
  def process_auth_plain(session, encoded_auth_response)
1240
1333
  begin
1241
1334
  # extract auth id (and password)
metadata CHANGED
@@ -1,17 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: midi-smtp-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.3
4
+ version: 3.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Freudenberg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-12 00:00:00.000000000 Z
11
+ date: 2023-05-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: A small and highly customizable ruby SMTP-Server class with builtin support
14
- for AUTH and SSL/STARTTLS.
13
+ description: MidiSmtpServer is the highly customizable ruby SMTP-Server and SMTP-Service
14
+ library with builtin support for AUTH and SSL/STARTTLS, 8BITMIME and SMTPUTF8, IPv4
15
+ and IPv6 and additional features.
15
16
  email: develop.rb.midi-smtp-server@4commerce.net
16
17
  executables: []
17
18
  extensions: []
@@ -25,7 +26,7 @@ files:
25
26
  - lib/midi-smtp-server/logger.rb
26
27
  - lib/midi-smtp-server/tls-transport.rb
27
28
  - lib/midi-smtp-server/version.rb
28
- homepage:
29
+ homepage: https://4commerce-technologies-ag.github.io/midi-smtp-server
29
30
  licenses:
30
31
  - MIT
31
32
  metadata:
@@ -33,7 +34,7 @@ metadata:
33
34
  source_code_uri: https://github.com/4commerce-technologies-AG/midi-smtp-server
34
35
  changelog_uri: https://github.com/4commerce-technologies-AG/midi-smtp-server#changes-and-updates
35
36
  bug_tracker_uri: https://github.com/4commerce-technologies-AG/midi-smtp-server/issues
36
- documentation_uri: https://www.rubydoc.info/gems/midi-smtp-server/3.0.3
37
+ documentation_uri: https://www.rubydoc.info/gems/midi-smtp-server/3.1.2
37
38
  wiki_uri: https://midi-smtp-server.readthedocs.io/
38
39
  post_install_message:
39
40
  rdoc_options: []
@@ -50,7 +51,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
51
  - !ruby/object:Gem::Version
51
52
  version: '0'
52
53
  requirements: []
53
- rubygems_version: 3.2.15
54
+ rubygems_version: 3.4.1
54
55
  signing_key:
55
56
  specification_version: 4
56
57
  summary: MidiSmtpServer Class