Home > Software engineering >  How can I use multiple channels in the same struct
How can I use multiple channels in the same struct

Time:07-14

In my code I want to do the following:

  1. Recieve data from inputs as event and message
  2. 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.

  •  Tags:  
  • go
  • Related