-
Notifications
You must be signed in to change notification settings - Fork 572
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
REFACTOR: Use Async and family #467
Open
Ahmedgagan
wants to merge
35
commits into
sj26:main
Choose a base branch
from
Ahmedgagan:use-async
base: main
Could not load branches
Branch not found: {{ refName }}
Could not load tags
Nothing to show
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 33 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
0ec243c
REFACTOR: uses async and family
Ahmedgagan 4b65ae4
DEV: uses relative require paths
Ahmedgagan 236bdc6
DEV: does not daemonize server
Ahmedgagan 49ff9f0
DEV: removes unused variable
Ahmedgagan 2a8f197
DEV: adds method to check OS
Ahmedgagan 94c1824
DEV: Always sync the output
Ahmedgagan bbf9327
DEV: Daemonises server & changes as suggested
Ahmedgagan 480ff44
DEV: better handle websocket connections
Ahmedgagan 7f37002
DEV: removes unwanted code
Ahmedgagan 9098775
Fix verbose logging
sj26 0ed0306
Use a message bus
sj26 d08ba9f
Why does the data store need websockets?
sj26 1fe62ca
DEV: Opens port before daemonizing server
Ahmedgagan 652f9df
Merge branch 'main' of https://github.com/sj26/mailcatcher into use-a…
Ahmedgagan a64a914
DEV: Convers Mintest to RSpec
Ahmedgagan 811a3b5
CI: setup chrome & chromedriver in github CI
Ahmedgagan f28a102
CI: changes chrome & chromedriver setup
Ahmedgagan 8427572
CI: changes actions folder path
Ahmedgagan 45393ce
CI: Removes custom chrome & chromedriver setup
Ahmedgagan cf4df41
DEV: update smtp.rb as suggested & change dependency order
Ahmedgagan c35e63a
DEV: Adds SMTP::URLEndpoint spec
Ahmedgagan 27e3aeb
DEV: Resolves merge conflicts
Ahmedgagan fad3fa2
DEV: adds more test for SMTP::URLEndpoint class
Ahmedgagan 4be30e9
Resolves merge conflicts
Ahmedgagan ae2e7f8
RSPEC: Removes method and uses let
Ahmedgagan 56e6bfa
DEV: Removes methods from smtp_spec file
Ahmedgagan 865dbb1
DEV: Adds rspec test for SMTP::Protocol::SMTP::Server class
Ahmedgagan c1d19e0
DEV: Adds rspec for SMTP::Protocol
Ahmedgagan 7f4bd8f
Merge branch 'main' into use-async
Ahmedgagan 6fb8c25
DEV: Removes CHUNKING
Ahmedgagan a06b7e2
DEV: Linting
Ahmedgagan adbc161
DEV: fix ruby 3.0 working
Ahmedgagan ae93635
DEV: always convert command to upcase
Ahmedgagan eb6f322
Merge branch 'main' into use-async
Ahmedgagan 6815f29
Update spec/smtp_protocol_spec.rb
Ahmedgagan File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
|
||
require "fileutils" | ||
require "rubygems" | ||
require 'rspec/core/rake_task' | ||
|
||
require "mail_catcher/version" | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,22 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'async/io/address_endpoint' | ||
require 'async/http/endpoint' | ||
require 'async/websocket/adapters/rack' | ||
require 'async/io/shared_endpoint' | ||
require 'falcon' | ||
require "open3" | ||
require "optparse" | ||
require "rbconfig" | ||
|
||
require "eventmachine" | ||
require "thin" | ||
|
||
module EventMachine | ||
# Monkey patch fix for 10deb4 | ||
# See https://github.com/eventmachine/eventmachine/issues/569 | ||
def self.reactor_running? | ||
(@reactor_running || false) | ||
end | ||
end | ||
require 'socket' | ||
require 'mail' | ||
|
||
require "mail_catcher/version" | ||
|
||
module MailCatcher extend self | ||
autoload :Bus, "mail_catcher/bus" | ||
autoload :Mail, "mail_catcher/mail" | ||
autoload :Smtp, "mail_catcher/smtp" | ||
autoload :SMTP, "mail_catcher/smtp" | ||
autoload :Web, "mail_catcher/web" | ||
|
||
def env | ||
|
@@ -173,75 +169,90 @@ def run! options=nil | |
|
||
puts "Starting MailCatcher v#{VERSION}" | ||
|
||
Thin::Logging.debug = development? | ||
Thin::Logging.silent = !development? | ||
Async.run do | ||
@smtp_address = Async::IO::Address.tcp(options[:smtp_ip], options[:smtp_port]) | ||
@smtp_endpoint = Async::IO::AddressEndpoint.new(@smtp_address) | ||
@smtp_socket = rescue_port(options[:smtp_port]) { @smtp_endpoint.bind } | ||
puts "==> #{smtp_url}" | ||
|
||
@http_address = Async::IO::Address.tcp(options[:http_ip], options[:http_port]) | ||
@http_endpoint = Async::IO::AddressEndpoint.new(@http_address) | ||
@http_socket = rescue_port(options[:http_port]) { @http_endpoint.bind } | ||
puts "==> #{http_url}" | ||
end | ||
|
||
# One EventMachine loop... | ||
EventMachine.run do | ||
# Set up an SMTP server to run within EventMachine | ||
rescue_port options[:smtp_port] do | ||
EventMachine.start_server options[:smtp_ip], options[:smtp_port], Smtp | ||
puts "==> #{smtp_url}" | ||
Async.logger.level = :debug if options[:verbose] | ||
|
||
if options[:daemon] | ||
if quittable? | ||
puts "*** MailCatcher runs as a daemon by default. Go to the web interface to quit." | ||
else | ||
puts "*** MailCatcher is now running as a daemon that cannot be quit." | ||
end | ||
Process.daemon | ||
sj26 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
end | ||
|
||
# Let Thin set itself up inside our EventMachine loop | ||
# (Skinny/WebSockets just works on the inside) | ||
rescue_port options[:http_port] do | ||
Thin::Server.start(options[:http_ip], options[:http_port], Web) | ||
puts "==> #{http_url}" | ||
Async::Reactor.run do |task| | ||
smtp_endpoint = MailCatcher::SMTP::URLEndpoint.new(URI.parse(smtp_url), @smtp_endpoint) | ||
smtp_server = MailCatcher::SMTP::Server.new(smtp_endpoint) do |envelope| | ||
MailCatcher::Mail.add_message(sender: envelope.sender, recipients: envelope.recipients, | ||
source: envelope.content) | ||
end | ||
|
||
# Open the web browser before detatching console | ||
if options[:browse] | ||
EventMachine.next_tick do | ||
browse http_url | ||
smtp_task = task.async do |task| | ||
task.annotate "binding to #{@smtp_socket.local_address.inspect}" | ||
|
||
begin | ||
@smtp_socket.listen(Socket::SOMAXCONN) | ||
@smtp_socket.accept_each(task: task, &smtp_server.method(:accept)) | ||
ensure | ||
@smtp_socket.close | ||
end | ||
end | ||
|
||
# Daemonize, if we should, but only after the servers have started. | ||
if options[:daemon] | ||
EventMachine.next_tick do | ||
if quittable? | ||
puts "*** MailCatcher runs as a daemon by default. Go to the web interface to quit." | ||
else | ||
puts "*** MailCatcher is now running as a daemon that cannot be quit." | ||
end | ||
Process.daemon | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why was daemonisation removed? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding it in the next commit. Keeping it in the async block was blocking the process instead of daemonisation. I got the solution for this 😅. |
||
http_endpoint = Async::HTTP::Endpoint.new(URI.parse(http_url), @http_endpoint) | ||
http_app = Falcon::Adapters::Rack.new(Web.app) | ||
http_server = Falcon::Server.new(http_app, http_endpoint) | ||
|
||
task.async do |task| | ||
task.annotate "binding to #{@http_socket.local_address.inspect}" | ||
|
||
begin | ||
@http_socket.listen(Socket::SOMAXCONN) | ||
@http_socket.accept_each(task: task, &http_server.method(:accept)) | ||
ensure | ||
@http_socket.close | ||
end | ||
end | ||
|
||
browse(http_url) if options[:browse] | ||
end | ||
rescue Interrupt | ||
# Cool story | ||
end | ||
|
||
def quit! | ||
MailCatcher::Bus.push(type: "quit") | ||
Async::Task.current.reactor.stop | ||
end | ||
|
||
EventMachine.next_tick { EventMachine.stop_event_loop } | ||
def http_url | ||
"http://#{@@options[:http_ip]}:#{@@options[:http_port]}#{@@options[:http_path]}" | ||
end | ||
|
||
protected | ||
protected | ||
|
||
def smtp_url | ||
"smtp://#{@@options[:smtp_ip]}:#{@@options[:smtp_port]}" | ||
end | ||
|
||
def http_url | ||
"http://#{@@options[:http_ip]}:#{@@options[:http_port]}#{@@options[:http_path]}".chomp("/") | ||
end | ||
|
||
def rescue_port port | ||
begin | ||
yield | ||
|
||
# XXX: EventMachine only spits out RuntimeError with a string description | ||
rescue RuntimeError | ||
if $!.to_s =~ /\bno acceptor\b/ | ||
puts "~~> ERROR: Something's using port #{port}. Are you already running MailCatcher?" | ||
puts "==> #{smtp_url}" | ||
puts "==> #{http_url}" | ||
exit -1 | ||
else | ||
raise | ||
end | ||
rescue Errno::EADDRINUSE | ||
sj26 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
puts "~~> ERROR: Something's using port #{port}. Are you already running MailCatcher?" | ||
puts "==> #{smtp_url}" | ||
puts "==> #{http_url}" | ||
exit(-1) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,48 @@ | ||
# frozen_string_literal: true | ||
|
||
require "eventmachine" | ||
|
||
module MailCatcher | ||
Bus = EventMachine::Channel.new | ||
# Async-friendly broadcast channel | ||
class Channel | ||
def initialize | ||
@subscription_id = 0 | ||
@subscriptions = {} | ||
end | ||
|
||
def subscriber_count | ||
@subscriptions.size | ||
end | ||
|
||
def push(*values) | ||
Async.run do | ||
values.each do |value| | ||
@subscriptions.each_value do |subscription| | ||
Async do | ||
subscription.call(value) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
def subscribe(&block) | ||
subscription_id = next_subscription_id | ||
|
||
@subscriptions[subscription_id] = block | ||
|
||
subscription_id | ||
end | ||
|
||
def unsubscribe(subscription_id) | ||
@subscriptions.delete(subscription_id) | ||
end | ||
|
||
private | ||
|
||
def next_subscription_id | ||
@subscription_id += 1 | ||
end | ||
end | ||
|
||
# Then we instantiate a global one | ||
Bus = Channel.new | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, nice.