In my code I want to do the following:
- Recieve data from inputs as
event
andmessage
- Format the received data based on the
event
I thought to use something close to the method in the OOP, but it looks I meesed things up.
What I wrote is:
// Define the structs that contains the channels
type sseData struct {
event, message string
}
type DataPasser struct {
data chan sseData
logs chan string
connection chan struct{} // To control maximum allowed clients connections
}
// DEfine the struct's reciever that do the formating based on the input date
func (p *DataPasser) Format() {
data := <-p.data
switch {
case len(data.event) > 0:
p.logs <- fmt.Sprintf("event: %v\ndata: %v\n\n", data.event, data.message)
case len(data.event) == 0:
p.logs <- fmt.Sprintf("data: %v\n\n", data.message)
}
}
Then I've the below:
func (p *DataPasser) HandleSignal(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
setupCORS(&w, r)
fmt.Println("Client connected from IP:", r.RemoteAddr)
p.connection <- struct{}{}
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Internal error", 500)
return
}
fmt.Fprint(w, "event: notification\ndata: Connection to WhatsApp server ...\n\n")
flusher.Flush()
// Connect to the WhatsApp client
go Connect()
// Prepare dataParser `p` to recieve data through its sseData channel
go p.Format()
for {
select {
case c := <-p.logs:
fmt.Fprint(w, c)
flusher.Flush()
case <-r.Context().Done():
<-p.connection
fmt.Println("Connection closed")
return
}
}
}
func setupCORS(w *http.ResponseWriter, req *http.Request) {
(*w).Header().Set("Cache-Control", "no-cache")
(*w).Header().Set("Access-Control-Allow-Origin", "*")
(*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
(*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}
Anf in the connect function, I've the:
package main
import (
"context"
"fmt"
)
var err error
func Connect() {
fmt.Println("Connected")
if client.IsConnected() {
client.Disconnect()
passer.data <- sseData{
event: "notification",
message: "Reconnecting to WhatsApp server ...",
}
}
if client.Store.ID == nil {
// No ID stored, new login
GetQR:
qrChan, _ := client.GetQRChannel(context.Background())
err = client.Connect()
if err != nil {
// panic(err)
//passer.logs <- "Can not connect with WhatApp server, try again later"
passer.data <- sseData{
event: "notification",
message: "Can not connect with WhatApp server, try again later",
}
fmt.Println("Sorry", err)
}
for evt := range qrChan {
switch evt.Event {
case "success":
{
//passer.logs <- "success"
passer.data <- sseData{
event: "notification",
message: "success",
}
fmt.Println("Login event: success")
}
case "timeout":
{
//passer.logs <- "timeout/Refreshing"
passer.data <- sseData{
event: "notification",
message: "timeout/Refreshing",
}
fmt.Println("Login event: timeout")
goto GetQR
}
case "code":
{
fmt.Println("new code recieved")
fmt.Println(evt.Code)
//passer.logs <- evt.Code
passer.data <- sseData{
event: "qrCode",
message: evt.Code,
}
}
}
}
} else {
// Already logged in, just connect
//passer.logs <- "Already logged"
passer.data <- sseData{
event: "notification",
message: "Already logged in",
}
fmt.Println("Already logged")
err = client.Connect()
if err != nil {
panic(err)
}
}
/*
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
<-c
passer.data <- sseData{
event: "notification",
message: "Server got shut down",
}
*/
}
In the main file, I do have:
var passer *DataPasser
const maxClients = 1
func init() {
passer = &DataPasser{
data: make(chan sseData),
logs: make(chan string),
connection: make(chan struct{}, maxClients),
}
}
func main() {
http.HandleFunc("/sse", passer.HandleSignal)
go http.ListenAndServe(":1234", nil)
// Listen to Ctrl C (you can also do something else that prevents the program from exiting)
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
<-c
if client.IsConnected() {
client.Disconnect()
}
}
What is happeneing is that the server sending the first SSE correctly only, and it looks it hangs somewhere in the channel communication.
Any thought?
CodePudding user response:
I solved it by writting:
// Connect to the WhatsApp client
go Connect()
for {
select {
case data := <-p.data:
fmt.Println("recieved")
switch {
case len(data.event) > 0:
fmt.Fprintf(w, "event: %v\ndata: %v\n\n", data.event, data.message)
case len(data.event) == 0:
fmt.Fprintf(w, "data: %v\n\n", data.message)
}
flusher.Flush()
case <-r.Context().Done():
<-p.connection
fmt.Println("Connection closed")
return
}
}
But I still interested in splitting the action and using the reciever, I can not accept this as an answer, as it is a solution for the problem, but not an answer to the question. Any thought?
CodePudding user response:
When you are sending data to passer.data
from the go connect()
routine, the routine go p.Format()
is not listening. Because you are using an unbuffered channel for parser.data
but no receiver is listening, your code is stuck. Either use a buffered channel for parser.data
or make sure that your routine listening for incoming message from the data chan is started and listening before actually sending data to the channel. In your case, I guess starting the Format
routine before the Connect
routine should be sufficient.