Web server and listening nats at the same time - go

My code reads input from terminal and send those value to nats while it needs to have an http endpoint.
Separately it works but when I combine all of them it does not read from nats. If you could point me to a right direction I would appreciate.
package main
import (
"bufio"
"fmt"
nats "github.com/nats-io/nats.go"
"html/template"
"log"
"net/http"
"os"
)
func main() {
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
tmpl := template.Must(template.ParseFiles(wd + "/template/main.html"))
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
data := TodoPageData{
PageTitle: "Demo",
}
tmpl.Execute(w, data)
})
http.ListenAndServe(":8081", nil)
type message struct {
content string
}
var messages []message
nc, err := nats.Connect(
nats.DefaultURL,
)
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Subscribe
if _, err := nc.Subscribe("updates", func(m *nats.Msg) {
fmt.Printf("Received a message: %s\n", string(m.Data))
}); err != nil {
log.Fatal(err)
}
// io r/w
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
if err := nc.Publish("updates", []byte(scanner.Text())); err != nil {
log.Fatal(err)
}
messages = append(messages, message{scanner.Text()})
for _, message := range messages {
fmt.Println(message.content)
}
}
if scanner.Err() != nil {
// handle error.
}
}

http.ListenAndServe is a blocking call. Start it on a new goroutine:
go http.ListenAndServe(":8081", nil)

Related

Gorilla websocket is not closing

I want to open websocket server whenever i want to and close but server does not close after writing "exit" when i go back to my main() and i try to start again it fails saying "httpHandleFunc multiple registration '/' " how do i close websocket server permanently and go back to main and start server again like switch on/off.
Another issue i am facing is client side errors with Bad Handshake when using gorilla mux as a handler.
#Server-Side Code
https://go.dev/play/p/n_I4xzOomWz
package main
import (
"bufio"
"flag"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
log "github.com/sirupsen/logrus"
)
const (
// Time allowed to write a message to the peer.
writeWait = 10 * time.Second
// Maximum message size allowed from peer.
maxMessageSize = 8192
// Time allowed to read the next pong message from the peer.
pongWait = 60 * time.Second
// Send pings to peer with this period. Must be less than pongWait.
pingPeriod = (pongWait * 9) / 10
// Time to wait before force close on connection.
closeGracePeriod = 2 * time.Second
)
var (
server http.Server
addr = "0.0.0.0:443"
upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
)
func ping(ws *websocket.Conn, done chan struct{}) {
ticker := time.NewTicker(pingPeriod)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait)); err != nil {
log.Println("ping:", err)
}
case <-done:
return
}
}
}
// write message
func pumpStdout(ws *websocket.Conn, done chan struct{}) {
defer func() {
}()
for {
text := ReadInput("User220 > ")
if text == "exit" {
break
}
ws.SetWriteDeadline(time.Now().Add(writeWait))
if err := ws.WriteMessage(websocket.TextMessage, []byte(text)); err != nil {
ws.Close()
log.Errorln(err)
break
}
}
close(done)
ws.SetWriteDeadline(time.Now().Add(writeWait))
ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
time.Sleep(closeGracePeriod)
ws.Close()
}
// receive message
func pumpStdin(ws *websocket.Conn, done chan struct{}) {
defer ws.Close()
ws.SetReadLimit(maxMessageSize)
ws.SetReadDeadline(time.Now().Add(pongWait))
ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-done:
break
case <-ticker.C:
_, message, err := ws.ReadMessage()
if err != nil {
ws.CloseHandler()
err := ws.UnderlyingConn().Close()
fmt.Println(err)
main()
}
if len(message) > 0 {
fmt.Printf("\r\n%sUser220 > ", message)
}
}
}
}
func WebsocketHandle(w http.ResponseWriter, r *http.Request) {
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Fatalln(err)
}
defer c.Close()
stdoutDone := make(chan struct{})
go pumpStdout(c, stdoutDone)
go ping(c, stdoutDone)
pumpStdin(c, stdoutDone)
}
func start_socket() {
flag.Parse()
log.Printf("listen at %s", addr)
// ----------------------
mux := mux.NewRouter()
mux.HandleFunc("/", WebsocketHandle)
//-----------------------
http.HandleFunc("/", WebsocketHandle)
server = http.Server{Addr: addr}
log.Fatalln(server.ListenAndServe())
}
func stop_server() {
err := server.Close()
if err != nil {
log.Fatalln(err)
}
}
func ReadInput(promt string) string {
fmt.Printf("\n%s", promt)
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
return strings.TrimSpace(input)
}
func main() {
for {
read := ReadInput("Main : ")
switch read {
case "start":
start_socket()
case "stop":
stop_server()
default:
fmt.Println("Not Sure")
}
}
}
Client side i am just polling and writing back the same message right now.
#Client-Side Code
https://go.dev/play/p/y4nUkvMFYec
package main
import (
"fmt"
"net/url"
"github.com/gorilla/websocket"
log "github.com/sirupsen/logrus"
)
var addr = "localhost:443"
func main() {
dial := websocket.Dialer{}
u := url.URL{Scheme: "ws", Host: addr, Path: "/"}
log.Printf("connecting to %s", u.String())
c, _, err := dial.Dial(u.String(), nil)
if err != nil {
log.Fatalln(err)
}
defer c.Close()
for {
_, message, err := c.ReadMessage()
if err != nil {
log.Fatalln(err)
}
fmt.Println("Recevied : " + string(message))
err = c.WriteMessage(websocket.TextMessage, message)
if err != nil {
log.Fatalln(err)
}
}
}

How to terminate a console input request when a new input is requested

I need to terminate an existing console input request when a new one is requested. The following code is an attempt to close an existing request using a channel but it does not seem to terminate the input request.
package main
import (
"bufio"
"fmt"
"log"
"os"
"strings"
"time"
)
func main() {
go Confirm("you are a programmer, aint you?")
time.Sleep(2 * time.Second)
Confirm("do you love go?")
}
var cancelChannel chan struct{}
func Confirm(s string) bool {
//check if channel type holds a value then close the channel to remove previous confirmation input
if cancelChannel != nil {
fmt.Println("channel to be closed")
close(cancelChannel)
}
cancelChannel = make(chan struct{})
reader := bufio.NewReader(os.Stdin)
for {
fmt.Printf("%s [y/n]: ", s)
response, err := reader.ReadString('\n')
if err != nil {
log.Fatal(err)
}
response = strings.ToLower(strings.TrimSpace(response))
if response == "y" || response == "yes" {
return true
} else if response == "n" || response == "no" {
return false
}
if _, ok := <-cancelChannel; !ok {
fmt.Println("channel closed")
return false
}
}
}
As #JimB mentioned in comment you can't interrupt read on stdin although there is kinda shady trick how you can achieve it. It's possible to duplicate os.Stdin file descriptor using syscall (not recommended) and open it as non blocking file.
package main
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"io/fs"
"io/ioutil"
"os"
"syscall"
"time"
)
func setNonblock(f *os.File) error {
c, err := f.SyscallConn()
if err != nil {
return err
}
var err2 error
err = c.Control(func(fd uintptr) {
err2 = syscall.SetNonblock(int(fd), true)
})
if err != nil {
return err
}
return err2
}
func nonBlockingFile(f *os.File) (*os.File, error) {
if err := setNonblock(f); err != nil {
return nil, err
}
fd, err := syscall.Dup(int(f.Fd()))
if err != nil {
return nil, err
}
f2 := os.NewFile(uintptr(fd), f.Name())
return f2, nil
}
func read(ctx context.Context, f *os.File) (io.Reader, error) {
r, err := nonBlockingFile(f)
if err != nil {
return nil, err
}
go func() {
defer r.Close()
<-ctx.Done()
}()
buff := bytes.NewBuffer([]byte{})
for {
_, err := io.Copy(buff, r)
if err != nil {
if errors.Is(err, fs.ErrClosed) {
break
}
panic(err)
}
}
return buff, nil
}
func main() {
ctx1, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(time.Second * 2)
cancel()
}()
buf1, err := read(ctx1, os.Stdin)
if err != nil {
panic(err)
}
ctx2, _ := context.WithTimeout(context.Background(), time.Second*2)
buf2, err := read(ctx2, os.Stdin)
fmt.Println("buf1")
fmt.Println(ioutil.ReadAll(buf1))
fmt.Println("buf2")
fmt.Println(ioutil.ReadAll(buf2))
}
Go on and explore the simplicity offer by go
https://pkg.go.dev/context#WithCancel
You can have a context that returning CancelFunc then you use context.WithCancel.
And execute cancel func if you want to terminate.
This is the good practice way, you can also do a dirty os.Exit(0) in another case.

http: read on closed response body - httptest.NewServer

I am trying to get to grips with testing using the httptest.NewServer and I am hitting a roadblock.
In my code I am making a GET request to an external API and I want to write a test for this using httptest.NewServer.
Here is my code making the request (main.go):
package main
import (
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
)
type HTTPClient interface {
Do(req *http.Request) (*http.Response, error)
}
type NewRequest interface {
NewRequest(method string, url string, body io.Reader) (*http.Request, error)
}
var (
Client HTTPClient
)
func init() {
Client = &http.Client{}
}
func main() {
url := "https://httpbin.org/get"
GetData(url)
}
func GetData(url string) (*http.Response, error) {
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
log.Fatalln(err)
return nil, err
}
resp, err := Client.Do(req)
if err != nil {
log.Fatalln(err)
return nil, err
}
defer resp.Body.Close()
responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
return nil, err
}
fmt.Println(resp.Status)
fmt.Println(string(responseBody))
return resp, nil
}
When I run this it works fine.
Here is my test file:
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"testing"
)
func TestYourHTTPGet(t *testing.T){
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, `response from the mock server goes here`)
}))
defer ts.Close()
mockServerURL := ts.URL
resp, err := GetData(mockServerURL)
if err != nil {
fmt.Println("Error 1: ", err)
}
defer resp.Body.Close()
responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal("Error 2: ", err)
}
fmt.Println(resp.Status)
fmt.Println(string(responseBody))
}
When I run go test I receive the error: http: read on closed response body. If I remove defer resp.Body.Close() from main.go the test passes correctly.
I am not sure why this is happening and was hoping that someone could explain what is going on here?
As #Cerise Limón says you call resp.Body.Close() twice and then try to read closed body. To fix yor code you can remove body processing from GetData function and do it outside GetData or return the body and do not read it in test.
main.go:
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
)
var Client = &http.Client{}
func main() {
url := "https://httpbin.org/get"
status, data, err := GetData(url)
if err != nil {
log.Fatalln(err)
}
fmt.Println(status)
fmt.Println(string(data))
}
func GetData(url string) (status string, body []byte, err error) {
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return
}
resp, err := Client.Do(req)
if err != nil {
return
}
defer resp.Body.Close()
body, err = ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalln(err)
}
return resp.Status, body, nil
}
main_test.go:
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
)
func TestYourHTTPGet(t *testing.T){
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, `response from the mock server goes here`)
}))
defer ts.Close()
mockServerURL := ts.URL
status, data, err := GetData(mockServerURL)
if err != nil {
fmt.Println("Error 1: ", err)
}
fmt.Println(status)
fmt.Println(string(data))
}
Your GetData()'s return is a pointer. You run GetData() in main.go, when retun, it will close the resp.body. And if you read it again, it cause http: read on closed response body
So if you want read the body again, you should not return *http.Response, you should clone the resp.body to return

Golang websocket client, close connection after getting result

How I can implement this kind of scenario:
1.I have LoginHandler which receives some user data - email and signedXml:
func LoginHandler(c *gin.Context) {
var (
err error
data LoginPost
)
if err = c.BindJSON(&data); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"status": "error"})
return
}
...
c.JSON(http.StatusOK, gin.H{"status": "ok"})
}
2.I need to send signedXml to another server via websocket
3.Save result (success or error)
4.Close connection
Every HTTP request will open connection, send 1 message, get 1 result and finally close socket. I was trying with channel, but no success. Is this possible to implement my case?
UPDATE
package main
import (
"log"
"net/url"
"github.com/gorilla/mux"
"github.com/gorilla/websocket"
"net/http"
)
func indexHandler(w http.ResponseWriter, r *http.Request) {
message := r.FormValue("message")
w.Write([]byte(message))
}
func postHandler(w http.ResponseWriter, r *http.Request) {
var (
message = r.FormValue("message")
u = url.URL{Scheme: "ws", Host: "echo.websocket.org", Path: "/"}
err error
out []byte
conn *websocket.Conn
)
log.Printf("message: %s\n", message)
log.Printf("connecting to %s\n", u.String())
conn, _, err = websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
log.Fatal("dial:", err)
}
log.Println("write")
if err = conn.WriteMessage(websocket.TextMessage, []byte(message)); err != nil {
log.Fatal("write:", err)
}
log.Println("read")
if _, out, err = conn.ReadMessage(); err != nil {
log.Fatal("read:", err)
}
w.Write(out)
log.Println("close")
conn.Close()
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/", indexHandler).Methods("GET")
r.HandleFunc("/post", postHandler).Methods("POST")
http.Handle("/", r)
http.ListenAndServe(":8080", nil)
}
Call Dial, WriteMessage, ReadMessage and Close in sequence.
c, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
// handle error
}
err := c.WriteMessage(websocket.TextMessage, signedXML)
if err != nil {
// handle error
}
_, p, err := c.ReadMessage()
if err != nil {
// handle error
}
c.Close()
// p is a []byte with the first received message.

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