email-verifier
email-verifier copied to clipboard
Update Verify() with goroutine to speed up the request and some minor changes
Updates
- Changed the
getMD5Hash()
return order - Changed the struct's Verifier to make the enablers accessible outside the package
- Added goroutine channels inside
Verify()
to speed up the request.- in-addition I've added Verifier.VerifyTimeout to control the timeout (2seconds is already battle tested in fly.io using shared1x256)
In the meantime, if someone would like to incorporate this on their code, you may copy below
Source Code
package email_checker
import (
"context"
"time"
emailVerifier "github.com/AfterShip/email-verifier"
)
func GetEmailInformation(email string) (EmailInformation, error) {
verification, err := NewVerifier().Verify(email)
if err != nil {
return EmailInformation{}, err
}
return EmailInformation{
IsValidFormat: verification.Syntax.Valid,
Result: *verification,
}, nil
}
var (
reachableYes = "yes"
reachableNo = "no"
reachableUnknown = "unknown"
smtpCheckEnabled bool = true
catchAllCheckEnabled bool = true
gravatarCheckEnabled bool = true
domainSuggestEnabled bool = true
)
type Verifier struct {
*emailVerifier.Verifier
}
type EmailInformation struct {
IsValidFormat bool `json:"is_valid_format"`
emailVerifier.Result
}
func NewVerifier() *Verifier {
verifier := &Verifier{emailVerifier.NewVerifier()}
verifier.EnableAutoUpdateDisposable()
if smtpCheckEnabled {
verifier.EnableSMTPCheck()
}
if catchAllCheckEnabled {
verifier.EnableCatchAllCheck()
}
if gravatarCheckEnabled {
verifier.EnableGravatarCheck()
}
if domainSuggestEnabled {
verifier.EnableDomainSuggest()
}
return verifier
}
func (v *Verifier) Verify(email string) (*emailVerifier.Result, error) {
ret := emailVerifier.Result{
Email: email,
Reachable: reachableUnknown,
}
syntax := v.ParseAddress(email)
ret.Syntax = syntax
if !syntax.Valid {
return &ret, nil
}
ret.Free = v.IsFreeDomain(syntax.Domain)
ret.RoleAccount = v.IsRoleAccount(syntax.Username)
ret.Disposable = v.IsDisposable(syntax.Domain)
// If the domain name is disposable, mx and smtp are not checked.
if ret.Disposable {
return &ret, nil
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
mxCh := make(chan *emailVerifier.Mx)
smtpCh := make(chan *emailVerifier.SMTP)
errCh := make(chan error)
go func() {
mx, err := v.CheckMX(syntax.Domain)
if err != nil {
errCh <- err
return
}
mxCh <- mx
}()
go func() {
smtp, err := v.CheckSMTP(syntax.Domain, syntax.Username)
if err != nil {
errCh <- err
return
}
smtpCh <- smtp
}()
select {
case mx := <-mxCh:
ret.HasMxRecords = mx.HasMXRecord
case smtp := <-smtpCh:
ret.SMTP = smtp
ret.Reachable = v.calculateReachable(smtp)
case err := <-errCh:
return &ret, err
case <-ctx.Done():
return &ret, ctx.Err()
}
if gravatarCheckEnabled {
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
gravatarCh := make(chan *emailVerifier.Gravatar)
errCh = make(chan error)
go func() {
gravatar, err := v.CheckGravatar(email)
if err != nil {
errCh <- err
return
}
gravatarCh <- gravatar
}()
select {
case gravatar := <-gravatarCh:
ret.Gravatar = gravatar
case err := <-errCh:
return &ret, err
case <-ctx.Done():
return &ret, ctx.Err()
}
}
if domainSuggestEnabled {
ret.Suggestion = v.SuggestDomain(syntax.Domain)
}
return &ret, nil
}
func (v *Verifier) calculateReachable(s *emailVerifier.SMTP) string {
if !smtpCheckEnabled {
return reachableUnknown
}
if s.Deliverable {
return reachableYes
}
if s.CatchAll {
return reachableUnknown
}
return reachableNo
}