mailyak
mailyak copied to clipboard
Feature request: Expose smtpExchange function
I would like to establish the raw TCP connections myself in my application and simply rely on smtpExchange
to handle the SMTP protocol. Are there any negative consequences for exposing SmtpExchange
or SMTPExchange
?
I think this would require exposing quite a few internals to make it usable - it's definitely more an implementation detail rather than a public API, and I think I'd need a good reason to change it!
One alternative is to create a NewWithRawConn()
constructor that initialises an emailSender
implementation that simply passes the connection through - see this simple example that dials the connection and passes it to the caller, which for your use case would just return the connection you provided.
What would the error story look like for this? If the connection dies/becomes unusable, is that clearly propagated to the user? What about authentication and/or TLS?
It's actually usable on its own if you simply create a connection yourself, just like Send
on senderWithStartTLS
does. I am currently vendoring the package with a replace
in my go.mod
to expose it. My use case is to clearly separate an error while creating the connection itself (most likely port closed) from the SMTP protocol handling. While both would return an error from Send
, I don't have an exhaustive pattern matching to determine where something went wrong.
The error would simply be the same as currently, just that the user has to handle his own TCP connection, I think. If the connection dies, it will just error as well - it's simply easier for most users to call Send
again. Auth is implemented on sendableMail
, which should probably be made public as well - that's actually why just exposing this single function works out of the box right now. TLS is optionally handled with the tryTLSUpgrade
argument in the current implementation.
The code is very well-structured, and it would be awesome to be able to use this library to handle the actual SMTP, auth and TLS but implement my connection or the whole sendableMail
myself. Most users probably don't need and want to, but it seems a pretty inexpensive quality of life change - at least for me that is ^^
I'm curious - why do you want to mange the TCP connection yourself? What's the driving force behind the feature that makes it necessary? I am intrigued!
I'll be honest - I'm a little apprehensive exposing an internal implementation detail (smtpExchange
), but I'm definitely interested in finding a solution!
I have built and am still working on PingPong-Mail, a service that auto-replies to any incoming email. I use this tool to periodically test email deliverability and receivability using another tool of mine: Uptime-Robot. (These two tools are my first ever try at go, so don't expect too much in terms of clean code ^^)
With this PingPong-Mail service, I resolve the MX hosts for any given domain to deliver the email. With the current way mailyak works, I only get to specify the host when creating a new instance, which makes it harder to fail forward to another host with less priority in case of any connection issues. I therefore created: https://github.com/domodwyer/mailyak/issues/82
Using the smtpExchange
function, I can easily handle the MX priority fallbacks myself.
After just looking at your response in https://github.com/domodwyer/mailyak/issues/82 and the example that you provided (which uses a way of "copying" the data, I haven't yet thought of as I'm very new to go) I will try out your approach and maybe clean up my code doing so :) As I am currently on a short trip as well I'll probably get around to it this weekend at the earliest.
Is there also an easy way to copy all the metadata, e.g. headers, MAIL FROM and RCPT TO?
Thank you again for your support, and I hope my response was insightful.
Cheers :)
@Coronon that's pretty cool! And if that's your first go at... Go... (🥁) you're doing just fine!
How did your experiments with reusing the MailYak
instance go? Is there a reason you need to dial the TCP connection yourself? If not, you might not find it makes a noticeable difference initialising the instances on demand - they're lightweight by default and the only meaningful resource usage comes from the body payload you put in it (which you'll be doing even with the TCP connection reuse!)
It's definitely more on the "specialist" side of use cases, so I'm kinda leaning towards not exposing the internals - I try to keep the API somewhat tight and relatively simple - it helps people of all experience levels use it without too much trouble.
Hi, sorry for the very late reply, this time I was on holiday ;)
First of all: What a banger joke ;)
While your proposed solution works fine, it makes the code look a little "ugly" and obscure. I totally get that you don't want to expose this implementation details, so how about #82? If you could simply let me set the host and auth at a later point (after creating and configuring the rest of the instance), my problem would be solved, your implementation would stay private and mailyak would be easier to use :)