Is it possible to send email from localhost? - go

My goal is to create a SMTP server to send an email from noreply#myname.com containing OTP.
The problem is, I code on my personal computer. Therefore, no public address or domain, yet. I tried to send email to myname#gmail.com, but I can't find it on the spam, or the inbox folders.
What I've did:
run the go-smtp server. $ go run cmd/server/main.go
run the go-smtp client. $ go run cmd/client/main.go
The go-smtp server output
(base) jason#Jasons-Mac-mini server % go run main.go
2022/09/23 13:35:38 Starting server at :1025
2022/09/23 13:56:06 Mail from: test#localhost
2022/09/23 13:56:06 Rcpt to: // email redacted for stackoverflow
2022/09/23 13:56:06 Data: This is the email body
The go-smtp client output
(base) jason#Jasons-Mac-mini client % go run main.go
2022/09/23 13:56:06 Mail sent! time elapsed: 1.988625ms
cmd/client/main.go
package main
import (
"fmt"
"log"
"time"
"github.com/emersion/go-smtp"
)
func main() {
start := time.Now()
// Connect to the remote SMTP server.
c, err := smtp.Dial("localhost:1025")
if err != nil {
log.Fatal(err)
}
// Set the sender and recipient first
if err := c.Mail("test#localhost", nil); err != nil {
log.Fatal(err)
}
if err := c.Rcpt("jasonong713#gmail.com"); err != nil {
log.Fatal(err)
}
// Send the email body.
wc, err := c.Data()
if err != nil {
log.Fatal(err)
}
_, err = fmt.Fprintf(wc, "This is the email body")
if err != nil {
log.Fatal(err)
}
err = wc.Close()
if err != nil {
log.Fatal(err)
}
// Send the QUIT command and close the connection.
err = c.Quit()
if err != nil {
log.Fatal(err)
}
log.Println("Mail sent! time elapsed:", time.Since(start))
}
cmd/server/main.go
package main
import (
"log"
"time"
"github.com/emersion/go-smtp"
"github.com/godataid/sendemail"
)
func main() {
be := &sendemail.Backend{}
s := smtp.NewServer(be)
s.Addr = ":1025"
s.Domain = "localhost"
s.ReadTimeout = 10 * time.Second
s.WriteTimeout = 10 * time.Second
s.MaxMessageBytes = 1024 * 1024
s.MaxRecipients = 50
s.AllowInsecureAuth = true
log.Println("Starting server at", s.Addr)
if err := s.ListenAndServe(); err != nil {
log.Fatalln(err)
}
}
backend.go
package sendemail
import "github.com/emersion/go-smtp"
type Backend struct{}
// Authenticate a user. Return smtp.ErrAuthUnsupported if you don't want to
// support this.
func (be *Backend) Login(state *smtp.ConnectionState, username, password string) (smtp.Session, error) {
return nil, smtp.ErrAuthUnsupported
}
// Called if the client attempts to send mail without logging in first.
// Return smtp.ErrAuthRequired if you don't want to support this.
func (be *Backend) AnonymousLogin(state *smtp.ConnectionState) (smtp.Session, error) {
return &Session{}, nil
}
session.go
package sendemail
import (
"io"
"log"
"github.com/emersion/go-smtp"
)
type Session struct{}
// Discard currently processed message.
func (s *Session) Reset() {}
// Free all resources associated with session.
func (s *Session) Logout() error {
return nil
}
// Set return path for currently processed message.
func (s *Session) Mail(from string, opts smtp.MailOptions) error {
log.Println("Mail from:", from)
return nil
}
// Add recipient for currently processed message.
func (s *Session) Rcpt(to string) error {
log.Println("Rcpt to:", to)
return nil
}
// Set currently processed message contents and send it.
func (s *Session) Data(r io.Reader) error {
if b, err := io.ReadAll(r); err != nil {
return err
} else {
log.Println("Data:", string(b))
}
return nil
}

Related

Send Push Notification - It shows successful response but not showing any notifcation

I was trying to send push notification through fcm. In the response, I get all successful response. But I didn't get any notification. In other way, I tried to send notification directly from firebase console, and I get the notfication with the same token. So, I don't know what is the bug in this code. Token is okay. Response is also OK. but after executing code, I don't get any notification.
package main
import (
"context"
"encoding/base64"
"log"
"server/config"
db "server/database"
firebase "firebase.google.com/go/v4"
"firebase.google.com/go/v4/messaging"
"github.com/gin-gonic/gin"
"google.golang.org/api/option"
)
func init() {
config.Init()
db.ConnectRedis()
}
var ctx = context.Background()
func getDecodedFireBaseKey() ([]byte, error) {
fireBaseAuthKey := config.Env.FirebaseAuthKey
decodedKey, err := base64.StdEncoding.DecodeString(fireBaseAuthKey)
if err != nil {
return nil, err
}
return decodedKey, nil
}
func SendPushNotification(deviceTokens []string) error {
decodedKey, err := getDecodedFireBaseKey()
if err != nil {
return err
}
opts := []option.ClientOption{option.WithCredentialsJSON(decodedKey)}
app, err := firebase.NewApp(ctx, nil, opts...)
if err != nil {
log.Println("Error in initializing firebase", err)
return err
}
fcmClient, err := app.Messaging(ctx)
if err != nil {
log.Fatalf("error getting Messaging client: %v\n", err)
}
response, err := fcmClient.SendMulticast(ctx, &messaging.MulticastMessage{
Notification: &messaging.Notification{
Title: "Congratulations!!",
Body: "You have just implement push notification",
},
Tokens: deviceTokens,
})
if err != nil {
return err
}
log.Println("Response success count : ", response.SuccessCount)
log.Println("Response failure count : ", response.FailureCount)
return nil
}
func main() {
r := gin.Default()
deviceTokens, _ := db.RedisClient.SMembers(ctx,"fcmToken").Result()
SendPushNotification(deviceTokens)
r.Run()
}

Go websocket test acting strange

So basically I'm writing a go test for my chat application and for some reason the if I write Test_saveMessage function in the top of this file my tests go through and they work fine, however if I write the Test_InitRouter in the top of this file - my server opens and the test doesn't finish. As if it would be listening for more requests. Does anyone know the reason of why this could be happening? Here is the that does not work code:
package messenger
import (
"fmt"
"github.com/gorilla/websocket"
"github.com/stretchr/testify/assert"
"net/http/httptest"
"strings"
"testing"
)
var testMessage = Message{
Username: "Name",
Message: "Test message"}
//Tests InitRouter both sending and receiving messages
func Test_InitRouter(t *testing.T) {
var receivedMessage Message
//Create test server with the InitRouter handler
s := httptest.NewServer(InitRouter())
defer s.Close()
// Convert URL from http to ws
u := "ws" + strings.TrimPrefix(s.URL, "http")
fmt.Println(u)
// Connect to the test server
ws, _, err := websocket.DefaultDialer.Dial(u, nil)
if err != nil {
t.Fatalf("%v", err)
}
defer ws.Close()
//Send message to the server read received message and see if it's the same
if err != ws.WriteJSON(testMessage) {
t.Fatalf("%v", err)
}
err = ws.ReadJSON(&receivedMessage)
if err != nil {
t.Fatalf("%v", err)
}
if receivedMessage != testMessage {
t.Fatalf("%v", err)
}
}
//Test for the saveMessage function
func Test_saveMessage(t *testing.T) {
saveMessage(testMessage)
assert.Equal(t, 1, len(messages), "Expected to have 1 message")
}
As soon as I move the Test_saveMessage function to the top it starts working properly.
Here is the code for the handler:
package messenger
import (
"fmt"
"github.com/go-chi/chi"
"github.com/gorilla/websocket"
log "github.com/sirupsen/logrus"
"net/http"
)
func InitRouter() http.Handler {
r := chi.NewRouter()
r.Get("/", GetWebsocket)
return r
}
var clients = make(map[*websocket.Conn]bool) // connected clients
var broadcast = make(chan Message) // broadcast channel
var messages = []Message{}
func GetWebsocket(w http.ResponseWriter, r *http.Request) {
// Upgrade initial GET request to a websocket
upgrader := websocket.Upgrader{}
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Error(err)
}
// Close the connection when the function returns
defer ws.Close()
// Register our new client and send him the chat history
clients[ws] = true
serveInitialMessages(ws)
//initialize message sending logic
sendMessages(ws)
}
// Sends messages from a particular websocket to the channel
func sendMessages(ws *websocket.Conn){
for {
var msg Message
// Read in a new message as JSON and map it to a Message object
err := ws.ReadJSON(&msg)
if err != nil {
log.Info(err)
delete(clients, ws)
break
}
// Send the newly received message to the broadcast channel
broadcast <- msg
saveMessage(msg)
}
}
func HandleMessages() {
for {
// Grab the next message from the broadcast channel
msg := <-broadcast
fmt.Println(msg)
// Send it out to every client that is currently connected
for client := range clients {
err := client.WriteJSON(msg)
if err != nil {
log.Printf("error: %v", err)
client.Close()
delete(clients, client)
}
}
}
}
func saveMessage(m Message) {
if len(messages) >= 50 {
messages = messages[1:]
}
messages = append(messages, m)
}
func serveInitialMessages(ws *websocket.Conn) {
for _, m := range messages {
err := ws.WriteJSON(m)
if err != nil {
log.Error(err)
}
}
}

How to isolate a close 1006 (abnormal closure): unexpected EOF

I've been trying to subscribe to this API feed: https://www.cryptofacilities.com/resources/hc/en-us/articles/360000578294-Fills
Whenever I try to subscribe, it looks like I'm able to send the payload with WRITEJSON, but what I receive from my Read method is
websocket: close 1006 (abnormal closure): unexpected EOF.
The full terminal message is:
2019/08/04 22:20:31 recv: {"event":"info","version":1}
2019/08/04 22:20:31 recv: {"event":"subscribed","feed":"heartbeat"}
2019/08/04 22:20:31 recv: {"event":"challenge","message":"c6e55c07-d07a-4560-9283-be75ee458433"}
^C2019/08/04 22:21:50 interrupt
2019/08/04 22:21:50 write close: write tcp 192.168.1.6:49624->104.16.51.17:443: write: broken pipe
I understand from here that the status code means that my client closed the connection. I'm unable to track down this problem.
I've tried running race detection, turning off firewall (I have a Mac, and tried to turn off the antivirus - found out it's built in. Would this be worth pursuing?) increasing the handshake timeout, handling the error with a close frame, a different OS, increasing the buffer size, and max message size. A lot of it in reference to this post:
https://github.com/gorilla/websocket/issues/321
This is my client:
package websocket
import (
"crypto/hmac"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"net/http"
"time"
"github.com/gorilla/websocket"
)
var addr = "wss://www.cryptofacilities.com/ws/v1"
var Wait = 50000 * time.Second
var MaxMessageSize int64 = 100000
//WebSocket connection struct to pass into other methods
type WebSocket struct {
Conn *websocket.Conn
}
//Message represents the public server push messages
type Message struct {
Event string
Feed string
Message interface{}
}
type ChallengeSub struct {
Event string `json:"event"`
Message string `json:"message"`
}
type HeartBeat struct {
Event string
Message string
}
type FillSubscribe struct {
Event string `json:"event"`
Feed string `json:"feed"`
APIKey string `json:"api_key"`
OriginalChallenge string `json:"original_challenge"`
SignedChallenge string `json:"signed_challenge"`
}
//OpenWebSocket Connects to kraken API and returns a connection
func OpenWebSocket() (*WebSocket, error) {
conn, response, err := websocket.DefaultDialer.Dial(addr, nil)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusSwitchingProtocols {
return nil, fmt.Errorf("failed to upgrade protocol to websocket")
}
return &WebSocket{Conn: conn}, nil
}
//HeartBeat subscribes to the pingpong feed to keep the connection alive
func (c *WebSocket) HeartBeat() error {
ping := map[string]interface{}{
"event": "subscribe",
"feed": "heartbeat",
}
c.Conn.SetWriteDeadline(time.Now().Add(Wait))
err := c.Conn.WriteJSON(ping)
return err
}
func (c *WebSocket) Next() ([]byte, error) {
_, payload, err := c.Conn.ReadMessage()
return payload, err
}
//Challenge requests the UUID from kraken API for auth handshake
func (c *WebSocket) Challenge() error {
challengeRequest := map[string]interface{}{
"event": "challenge",
"api_key": "rhsqfT66dxTF7g2O7/t5Cluubjw4MlEz1UoBrZBjf8JocQ/q49j9rH9m",
}
c.Conn.SetWriteDeadline(time.Now().Add(Wait))
err := c.Conn.WriteJSON(challengeRequest)
if err != nil {
log.Println("write:", err)
return err
}
return err
}
func (c *WebSocket) Decode(payload []byte) (string, string) { //Decode takes in a connection and reference to Message struct
var msg json.RawMessage
env := Message{
Message: &msg,
}
if err := json.Unmarshal([]byte(payload), &env); err != nil {
log.Fatal(err)
}
switch env.Event {
case "challenge":
var s ChallengeSub
if err := json.Unmarshal(msg, &s.Message); err != nil {
log.Fatal(err)
}
message, signed := c.Signature(s.Message)
c.FillSubscribe(message, signed)
return message, signed
case "info":
{
fmt.Println("Connected:", env.Event)
}
case "subscribed":
{
fmt.Println("Connected to Heartbeat")
}
default:
switch env.Feed {
case "heartbeat":
fmt.Println("Live")
}
}
return "No messages to Decode...", ""
}
func (c *WebSocket) Signature(message string) (string, string) {
secret64, _ := base64.StdEncoding.DecodeString("rhsqfT66dxTF7g2O7/t5Cluubjw4MlEz1UoBrZBjf8JocQ/q49j9rH9m")
hash := sha256.New()
hash.Write([]byte(message))
challenge256 := hash.Sum(nil)
hmacHash := hmac.New(sha512.New, secret64)
hmacHash.Write(challenge256)
secretChallenge := hmacHash.Sum(nil)
signed := base64.StdEncoding.EncodeToString(secretChallenge)
return message, signed
}
//FillSubscribe populates message struct and sends out the JSON message
func (c *WebSocket) FillSubscribe(challenge string, signed string) error {
fillMessage := map[string]interface{}{
"event": "subscribe",
"feed": "fills",
"api_key": "rhsqfT66dxTF7g2O7/t5Cluubjw4MlEz1UoBrZBjf8JocQ/q49j9rH9m",
"original_challenge": challenge,
"signed_challenge": signed,
}
c.Conn.SetWriteDeadline(time.Now().Add(Wait))
err := c.Conn.WriteJSON(fillMessage)
if err != nil {
log.Println("write:", err)
return err
}
return err
}
Here is my main program:
package main
import (
"fmt"
"log"
"github.com/Mjavala/KrakenAPI/websocket"
)
var message string
var signed string
func main() {
ws, err := websocket.OpenWebSocket()
if err != nil {
log.Fatal(err)
}
ws.HeartBeat()
ws.Challenge()
fmt.Println(message, signed)
for {
// We first read in a raw message. An error here is a socket level
// error.
payload, err := ws.Next()
if err != nil {
log.Fatalf("socket error: %+v\n", err)
}
log.Printf("recv: %s", payload)
message, signed = ws.Decode(payload)
if err != nil {
log.Fatalf("decode error: %+v\n", err)
}
}
}
I believe that even if the API Keys are wrong a return message should still be sent, as per the API; but instead I get that 1006 close frame.
I'm new to WebSockets. I'm able to get the challenge messaged and heartbeat subscription from the API, the problem is specific to the fill subscription.
Also if anyone wants to replicate this, I can give the full code/Git link.

rpc.ServerCodec Still Serving?

I was performing some RPC tests, and stumbled across a problem I can't seem to solve. In my testing I create three separate RPC servers, all of which I try to close and shutdown. However upon performing my last test (TestRpcCodecServerClientComm), it seems my client connection is connecting to the first RPC server I started (I know this because I at some point attached IDs to the RPCHandlers), even though I attempted everything I could to make sure it was shutdown. Though the code is not there I have attempted to inspect every single error I could, but that did not bring about anything.
rpc.go
package rbot
import (
"io"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
func RpcCodecClientWithPort(port string) (rpc.ClientCodec, error) {
conn, err := net.Dial("tcp", "localhost:"+port)
if err != nil {
return nil, err
}
return jsonrpc.NewClientCodec(conn), nil
}
func RpcCodecServer(conn io.ReadWriteCloser) rpc.ServerCodec {
return jsonrpc.NewServerCodec(conn)
}
rpc_test.go
package rbot
import (
"errors"
"fmt"
"net"
"net/rpc"
"testing"
)
type RPCHandler struct {
RPCServer net.Listener
conn rpc.ServerCodec
done chan bool
TestPort string
stop bool
GotRPC bool
}
func (r *RPCHandler) SetupTest() {
r.stop = false
r.GotRPC = false
r.done = make(chan bool)
r.TestPort = "5556"
}
// TODO: Create separate function to handle erroring
func (r *RPCHandler) CreateRPCServer() error {
rpc.RegisterName("TestMaster", TestAPI{r})
var err error
r.RPCServer, err = net.Listen("tcp", ":"+r.TestPort)
if err != nil {
return err
}
go func() {
for {
conn, err := r.RPCServer.Accept()
if err != nil || r.stop {
r.done <- true
return
}
r.conn = RpcCodecServer(conn)
rpc.ServeCodec(r.conn)
}
}()
return nil
}
func (r *RPCHandler) CloseRPCServer() error {
r.stop = true
if r.conn != nil {
err := r.conn.Close()
if err != nil {
fmt.Println(err)
}
}
err := r.RPCServer.Close()
<-r.done
return err
}
type TestAPI struct {
t *RPCHandler
}
func (tapi TestAPI) Send(msg string, result *string) error {
if msg == "Got RPC?" {
tapi.t.GotRPC = true
return nil
}
return errors.New("Didn't receive right message")
}
// Check if we can create and close an RPC server successfully using the RPC server codec.
func TestRpcCodecServer(t *testing.T) {
r := RPCHandler{}
r.SetupTest()
err := r.CreateRPCServer()
if err != nil {
t.Fatalf("Could not create rpc server! %s:", err.Error())
}
err = r.CloseRPCServer()
if err != nil {
t.Fatalf("Could not close RPC server! %s:", err.Error())
}
}
// Check if we can create a client without erroring.
func TestRpcCodecClientWithPortt(t *testing.T) {
r := RPCHandler{}
r.SetupTest()
r.CreateRPCServer()
defer r.CloseRPCServer()
RPCClient, err := RpcCodecClientWithPort(r.TestPort)
defer RPCClient.Close()
if err != nil {
t.Fatalf("Could not create an RPC client! %s:", err.Error())
}
}
// Let's double check and make sure our server and client can speak to each other
func TestRpcCodecServerClientComm(t *testing.T) {
r := RPCHandler{}
r.SetupTest()
r.CreateRPCServer()
defer r.CloseRPCServer()
RPCCodec, _ := RpcCodecClientWithPort(r.TestPort)
RPCClient := rpc.NewClientWithCodec(RPCCodec)
defer RPCClient.Close()
var result string
err := RPCClient.Call("TestMaster.Send", "Got RPC?", &result)
if err != nil {
t.Fatalf("Error while trying to send RPC message: %s", err.Error())
}
if !r.GotRPC {
t.Fatalf("Could not send correct message over RPC")
}
}
Not sure if I'm just mishandling the connection or something of the like, any help would be much appreciated.
For the Record The RPC api does receive the correct string message
While not the source of your problems, your test configuration has a few race conditions which you should take care of before they cause problems. Always check for issues with the -race option. You should also let the OS allocate the port so you don't run into conflicts. See for example how httptest.Server works.
Your failure here is that you're not creating a new rpc.Server for each test, you're reusing the rpc.DefaultServer. The first call to CreateRPCServer registers a TestAPI under the name TestMaster. Each subsequent call uses the already registered instance.
If you create a new rpc.Server each time you setup the test and register a new TestAPI, the final test will pass.
srv := rpc.NewServer()
srv.RegisterName("TestMaster", testAPI)
...
// and then use srv to handle the new connection
srv.ServeCodec(RpcCodecServer(conn))

go routine - why websocket reports the connection as closed?

I'm trying to create a client and a server using Go but for some reason the server reports the connection as "closed". As the code is trivial I can't think of anything wrong with my code. Any help is appreciated.
package main
import (
log "github.com/golang/glog"
"net/http"
"golang.org/x/net/websocket"
"time"
"flag"
)
type server struct {
payload chan string
}
// srv pushes the messages received via ws into srv.payload
func (srv *server) serve(ws *websocket.Conn) {
go func() {
var msg string
if err := websocket.Message.Receive(ws, &msg); err != nil {
log.Exit(err)
}
srv.payload <- msg
}()
return
}
// This example demonstrates a trivial client/ server.
func main() {
flag.Parse()
srv := server{payload: make(chan string, 10)}
http.Handle("/echo", websocket.Handler(srv.serve))
go func() {
err := http.ListenAndServe(":12345", nil)
if err != nil {
log.Errorf("ListenAndServe: " + err.Error())
}
}()
// give the server some time to start listening
time.Sleep(3 *time.Second)
//dial and test the response.
ws, err := websocket.Dial("ws://localhost:12345/echo", "", "http://localhost/?x=45")
if err != nil {
log.Exit(err)
}
ms := "test"
if err := websocket.Message.Send(ws, ms); err != nil {
log.Exit(err)
}
msg := <-srv.payload
if msg != ms{
log.Errorf("msg %v is not %v", ms)
}
}
Error
t.go:21] read tcp 127.0.0.1:12345->127.0.0.1:43135:
Edit:
After some try and error I've found that if I remove the go routine from the serve method it works but it doesn't make sense to me. Any idea why it doesn't work when websocket.Message.Receive is in a separate go routine?
package main
import (
log "github.com/golang/glog"
"net/http"
"golang.org/x/net/websocket"
"time"
"flag"
)
type server struct {
payload chan string
}
// srv pushes the messages received via ws into srv.payload
func (srv *server) serve(ws *websocket.Conn) {
var msg string
if err := websocket.Message.Receive(ws, &msg); err != nil {
log.Exit(err)
}
srv.payload <- msg
return
}
// This example demonstrates a trivial client/ server.
func main() {
flag.Parse()
srv := server{payload: make(chan string, 10)}
go func() {
http.Handle("/echo", websocket.Handler(srv.serve))
err := http.ListenAndServe(":12345", nil)
if err != nil {
log.Errorf("ListenAndServe: " + err.Error())
}
}()
// give the server some time to start listening
time.Sleep(3 *time.Second)
//dial and test the response.
ws, err := websocket.Dial("ws://localhost:12345/echo", "", "http://localhost/?x=45")
if err != nil {
log.Exit(err)
}
ms := "test"
if err := websocket.Message.Send(ws, ms); err != nil {
log.Exit(err)
}
msg := <-srv.payload
if msg != ms{
log.Errorf("msg %v is not %v", ms)
}
}
The websocket server closes the connection when the handler returns.
Removing the Go routine is the correct fix.

Resources