Too Many open files/ No such host error while running a go program which makes concurrent requests - go

I have a golang program which is supposed to call an API with different payloads, the web application is a drop wizard application which is running on localhost, and the go program is below
package main
import (
"bufio"
"encoding/json"
"log"
"net"
"net/http"
"os"
"strings"
"time"
)
type Data struct {
PersonnelId string `json:"personnel_id"`
DepartmentId string `json:"department_id"`
}
type PersonnelEvent struct {
EventType string `json:"event_type"`
Data `json:"data"`
}
const (
maxIdleConnections = 20
maxIdleConnectionsPerHost = 20
timeout = time.Duration(5 * time.Second)
)
var transport = http.Transport{
Dial: dialTimeout,
MaxIdleConns: maxIdleConnections,
MaxIdleConnsPerHost: 20,
}
var client = &http.Client{
Transport: &transport,
}
func dialTimeout(network, addr string) (net.Conn, error) {
return net.DialTimeout(network, addr, timeout)
}
func makeRequest(payload string) {
req, _ := http.NewRequest("POST", "http://localhost:9350/v1/provider-
location-personnel/index", strings.NewReader(payload))
req.Header.Set("X-App-Token", "TESTTOKEN1")
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
log.Println("Api invocation returned an error ", err)
} else {
defer resp.Body.Close()
log.Println(resp.Body)
}
}
func indexPersonnels(personnelPayloads []PersonnelEvent) {
for _, personnelEvent := range personnelPayloads {
payload, err := json.Marshal(personnelEvent)
if err != nil {
log.Println("Error while marshalling payload ", err)
}
log.Println(string(payload))
// go makeRequest(string(payload))
}
}
func main() {
ch := make(chan PersonnelEvent)
for i := 0; i < 20; i++ {
go func() {
for personnelEvent := range ch {
payload, err := json.Marshal(personnelEvent)
if err != nil {
log.Println("Error while marshalling payload", err)
}
go makeRequest(string(payload))
//log.Println("Payload ", string(payload))
}
}()
}
file, err := os.Open("/Users/tmp/Desktop/personnels.txt")
defer file.Close()
if err != nil {
log.Fatalf("Error opening personnel id file %v", err)
}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
go func() {
ch <- PersonnelEvent{EventType: "provider_location_department_personnel_linked", Data: Data{DepartmentId: "2a8d9687-aea8-4a2c-bc08-c64d7716d973", PersonnelId: scanner.Text()}}
}()
}
}
Its reading some ids from a file and then creating a payload out of it and invoking a post request on the web server, but when i run the program it gives too many open file errors/no such host errors, i feel that the program is too much concurrent how to make it run gracefully?

inside your 20 goroutines started in main(), "go makeRequest(...)" again created one goroutine for each event. you don't need start extra goroutine there.
Besides, I think you don't need start goroutine in your scan loop, either. buffered channel is enough,because bottleneck should be at doing http requests.

You can use a buffered channel, A.K.A. counting semaphore, to limit the parallelism.
// The capacity of the buffered channel is 10,
// which means you can have 10 goroutines to
// run the makeRequest function in parallel.
var tokens = make(chan struct{}, 10)
func makeRequest(payload string) {
tokens <- struct{}{} // acquire the token or block here
defer func() { <-tokens }() // release the token to awake another goroutine
// other code...
}

Related

How to convert string into protoreflect.ProtoMessage or into anypb

I'm trying to use server sent event eventsoource through gRPC gateway, so read this
and this and came out with the below proto
syntax = "proto3";
package protobuf;
option go_package = "/proto";
import "google/protobuf/any.proto";
import "google/protobuf/empty.proto";
import "google/api/annotations.proto";
service Events {
rpc StreamEvents (google.protobuf.Empty) returns (stream EventSource) {
option (google.api.http) = {
get: "/v1/rawdata/stream"
};
}
}
message Empty{}
message EventSource {
string event = 1;
google.protobuf.Any data = 2;
}
And generated all required gRPC files (Message/Service/Gateway) using:
protoc -I ./proto \
--go_out ./proto --go_opt paths=source_relative \
--go-grpc_out ./proto --go-grpc_opt paths=source_relative \
--grpc-gateway_out ./proto --grpc-gateway_opt paths=source_relative \
./proto/data.proto
And trying to registered the generated service and gateway at my main app, as:
package main
import (
"context"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
_ "github.com/mattn/go-sqlite3"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "walistner/proto"
)
func main() {
gRPcPort := ":50005"
// Create a gRPC listener on TCP port
lis, err := net.Listen("tcp", gRPcPort)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
// Create a gRPC server object
s := grpc.NewServer()
// Attach the Greeter service to the server
pb.RegisterEventsServer(s, server{})
log.Println("Serving gRPC server on 0.0.0.0:50005")
// Serve gRPC server
grpcTerminated := make(chan struct{})
go func() {
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
close(grpcTerminated) // In case server is terminated without us requesting this
}
}()
// Create a client connection to the gRPC server we just started
// This is where the gRPC-Gateway proxies the requests
// gateWayTarget := fmt.Sprintf("0.0.0.0%s", gRPcPort)
conn, err := grpc.DialContext(
context.Background(),
gRPcPort,
grpc.WithBlock(),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
log.Fatalln("Failed to dial server:", err)
}
gwmux := runtime.NewServeMux()
// This handle to use JSON ServerSentEvent, not the gRPC one
//http.HandleFunc("/sse", passer.HandleSignal)
// Register custom route for GET /hello/{name}
//err = gwmux.HandlePath("GET", "/sse", passer.HandleSignal)
//if err != nil {
// fmt.Println("Error:", err)
//}
// This handler just to confirm server is up and running
// Register custom route for GET /hello/{name}
err = gwmux.HandlePath("GET", "/hello/{name}", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
w.Write([]byte("hello " + pathParams["name"]))
})
if err != nil {
fmt.Println("Error:", err)
}
// Register the gRPC ServerSentEvent
err = pb.RegisterEventsHandler(context.Background(), gwmux, conn)
if err != nil {
log.Fatalln("Failed to register gateway:", err)
}
gwServer := &http.Server{
Addr: ":8090",
Handler: allowCORS(gwmux),
}
log.Println("Serving gRPC-Gateway on http://localhost:8090")
fmt.Println("run POST request of: http://localhost:8090/v1/rawdata/stream")
fmt.Println("or run curl -X GET -k http://localhost:8090/v1/rawdata/stream")
log.Fatal(gwServer.ListenAndServe()) // <- This line alone could be enough ang no need for all the lines after,
// the application is probably doing other things and you will want to be
// able to shutdown cleanly; passing in a context is a good method..
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // Ensure cancel function is called eventually
grpcWebTerminated := make(chan struct{})
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
if err := gwServer.ListenAndServe(); err != nil {
fmt.Printf("Web server (GRPC) shutdown: %s", err)
}
close(grpcWebTerminated) // In case server is terminated without us requesting this
}()
// Wait for the web server to shutdown OR the context to be cancelled...
select {
case <-ctx.Done():
// Shutdown the servers (there are shutdown commands to request this)
case <-grpcTerminated:
// You may want to exit if this happens (will be due to unexpected error)
case <-grpcWebTerminated:
// You may want to exit if this happens (will be due to unexpected error)
}
// Wait for the goRoutines to complete
<-grpcTerminated
<-grpcWebTerminated
// 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() {
passer.data <- sseData{
event: "notification",
message: "Server is shut down at the host machine...",
}
client.Disconnect()
}
}
// allowCORS allows Cross Origin Resoruce Sharing from any origin.
// Don't do this without consideration in production systems.
func allowCORS(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if origin := r.Header.Get("Origin"); origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
}
h.ServeHTTP(w, r)
})
}
And the main part that is having an issue, is ther gateway registration:
package main
import (
"fmt"
"log"
"sync"
pb "walistner/proto"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/emptypb"
)
type server struct {
pb.UnimplementedEventsServer
}
func (s server) StreamEvents(_ *emptypb.Empty, srv pb.Events_StreamEventsServer) error {
if p, ok := peer.FromContext(srv.Context()); ok {
fmt.Println("Client ip is:", p.Addr.String())
}
md := metadata.New(map[string]string{"Content-Type": "text/event-stream", "Connection": "keep-alive"})
srv.SetHeader(md)
//use wait group to allow process to be concurrent
var wg sync.WaitGroup
for {
incomingInput := <-passer.data
wg.Add(1)
//go func(count int64) {
go func() {
defer wg.Done()
myData := incomingInput.data <---------------This is a string
// Convert data to *anypb.Any
data, err := anypb.New(myData.(protoreflect.ProtoMessage)) <----- tried this but it failed
if err != nil {
//...
}
resp := pb.EventSource{
Event: incomingInput.event,
Data: data, // <---------- this is required to be of type *anypb.Any
}
if err := srv.Send(&resp); err != nil {
log.Printf("send error %v", err)
}
}()
}
wg.Wait()
return nil
}
My data structure inside the go app is:
type sseData struct {
event, message string
}
type DataPasser struct {
data chan sseData
logs chan string
connection chan struct{} // To control maximum allowed clients connections
}
var passer *DataPasser
func init() {
passer = &DataPasser{
data: make(chan sseData),
logs: make(chan string),
connection: make(chan struct{}, maxClients),
}
}

Handling multiple websocket connections

I'm trying to create a program that will connect to several servers though gorilla web-sockets. I currently have a program that will iterate over a list of server addresses and create a new goroutine that will create its own Websocket.conn and handle reading and writing.
The problem is that every time a new goroutine is created the previous goroutines are blocked and only the last one can continue. I believe this is because the gorilla websocket library is blocking each gorotutine, but I might be mistaken.
I have tried putting a timer in the server list iterator and each goroutine will work perfectly but then the moment a new goroutine is made with another address the previous goroutine is blocked.
The relevant bits of my code:
In my main.go
for _, server := range servers {
go control(ctx, server, port)
}
In control()
func control(ctx context.Context, server, port string) {
url := url.URL{
Scheme: "ws",
Host: server + ":" + port,
Path: "",
}
conn, _, err := websocket.DefaultDialer.Dial(url.String(), nil)
if err != nil {
panic(err)
}
defer conn.Close()
go sendHandler(ctx, conn)
go readHandler(ctx, conn)
}
readHandler(ctx context.Context, conn *websocket.Con) {
for {
_, p, err := conn.ReadMessage(); if err != nil {
panic(err)
}
select {
case <-ctx.Done():
goto TERM
default:
// do nothing
}
}
TERM:
// do termination
}
sendHandler(ctx context.Context, conn *websocket.Con) {
for _, msg := range msges {
err = conn.WriteMessage(websocket.TextMessage, msg)
if err != nil {
panic(err)
}
}
<-ctx.Done()
}
I removed the parts where I add waitgroups and other unnecessary pieces of code.
So what I expect is for there to be 3n goroutines running (where n is the number of servers) without blocking but right now I see only 3 goroutines running which are the ones called by the last iteration of the server list.
Thanks!
EDIT 14/06/2019:
I spent some time making a small working example and in the example the bug did not occur - none of the threads blocked each other. I'm still unsure what was causing it but here is my small working example:
main.go
package main
import (
"context"
"fmt"
"os"
"time"
"os/signal"
"syscall"
"sync"
"net/url"
"github.com/gorilla/websocket"
)
func main() {
servers := []string{"5555","5556", "5557"}
comms := make(chan os.Signal, 1)
signal.Notify(comms, os.Interrupt, syscall.SIGTERM)
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
var wg sync.WaitGroup
for _, server := range servers {
wg.Add(1)
go control(server,
ctx,
&wg)
}
<-comms
cancel()
wg.Wait()
}
func control(server string, ctx context.Context, wg *sync.WaitGroup) {
fmt.Printf("Started control for %s\n", server)
url := url.URL {
Scheme: "ws",
Host: "0.0.0.0" + ":" + server,
Path: "",
}
conn, _, err := websocket.DefaultDialer.Dial(url.String(), nil)
if err != nil {
panic(err)
}
defer conn.Close()
var localwg sync.WaitGroup
localwg.Add(1)
go sendHandler(ctx, conn, &localwg, server)
localwg.Add(1)
go readHandler(ctx, conn, &localwg, server)
<- ctx.Done()
localwg.Wait()
wg.Done()
return
}
func sendHandler(ctx context.Context, conn *websocket.Conn, wg *sync.WaitGroup, server string) {
for i := 0; i < 50; i++ {
err := conn.WriteMessage(websocket.TextMessage, []byte("ping"))
if err != nil {
panic(err)
}
fmt.Printf("sent msg to %s\n", server)
time.Sleep(1 * time.Second)
}
<- ctx.Done()
wg.Done()
}
func readHandler(ctx context.Context, conn *websocket.Conn, wg *sync.WaitGroup, server string) {
for {
select {
case <- ctx.Done():
wg.Done()
return
default:
_, p, err := conn.ReadMessage()
if err != nil {
wg.Done()
fmt.Println("done")
}
fmt.Printf("Got [%s] from %s\n", string(p), server)
}
}
}
I tested it with dpallot's simple-websocket-server by a server on 5555, 5556 and 5557 respectively.
This part of your code is causing the problem:
conn, _, err := websocket.DefaultDialer.Dial(url.String(), nil)
if err != nil {
panic(err)
}
defer conn.Close()
go sendHandler(ctx, conn)
go readHandler(ctx, conn)
You create the connection, defer the close of it, start two other goroutines and then end the function. The function end closes the socket due to your defer.

Golang concurrently reading from a tcp connectoin

I am having some issue with a Go project. The code is way too big to copy and paste so I will try to explain as well as I can.
My program first connects to a TCP server, then it starts a goroutine passing as argument the connection object.
What I'm trying to achieve is having the client to read infinitely from the tcp connection while at the same time to take user input and communicate to the server by sending a retrieving data. I've tried using another goroutine but the program blocks whenever trying to retrieve data from the server.
Here is a reproduction of the error on go playground.
https://play.golang.org/p/OD5ozCRmy_4 server
https://play.golang.org/p/t1r_BAQM-jn client
Basically whenever the client tries to read from the connection it gets stuck.
Thank you for your help.
You should use channel
here is a sample which can receive some connection and each connection could send data as wish
package tcp
import (
"bufio"
"fmt"
"net"
"strconv"
"../log"
"../config"
"../controllers"
h "../helpers"
)
type msgFormat struct {
text []byte
net.Conn
}
var accounts = make(map[net.Conn]int)
var conns = make(chan net.Conn)
var dconns = make(chan net.Conn)
var msgs = make(chan msgFormat)
var i int
//Init is first point
func Init() {
startserver()
for {
select {
case conn := <-conns:
handleconnect(conn)
case msg := <-msgs:
go handlemsg(msg)
case dconn := <-dconns:
handlediscounect(dconn)
}
}
}
func handlemsg(incomemsg msgFormat) {
logger.Log.Println(string(incomemsg.text))
resp, err := controllers.Do(incomemsg.text)
if err != nil {
logger.Log.Println(err.Error())
}
strLen := []byte(h.Lpad(string(fmt.Sprintf("%v", len(resp))), "0", 4))
//
fresponse := append(strLen, resp...)
incomemsg.Write(fresponse)
logger.Log.Println("response is %v" , string(fresponse))
}
func startserver() {
conf := config.GetConfigInstance()
ln, err := net.Listen(conf.SERVER.Nettype, conf.SERVER.Address)
if err != nil {
logger.Log.Println(err.Error())
}
logger.Log.Printf("server is serving at %v", conf.SERVER.Address)
go func() {
for {
conn, err := ln.Accept()
if err != nil {
logger.Log.Println(err.Error())
}
conns <- conn
}
}()
}
func readdate(conn net.Conn, i int) {
for {
rd := bufio.NewReader(conn)
dataLen := make([]byte, 4)
_, err := rd.Read(dataLen)
if err != nil {
break
}
intLen, _ := strconv.Atoi(string(dataLen))
data := make([]byte, intLen)
_, err = rd.Read(data)
if err != nil {
break
}
msgs <- msgFormat{data, conn}
}
dconns <- conn
}
func handleconnect(newconnection net.Conn) {
accounts[newconnection] = i
i++
// if addr , ok := newconnection.RemoteAddr().str
logger.Log.Printf("Action: Client_Connected %v is connected via %v \n", i, newconnection.RemoteAddr().(*net.TCPAddr).IP)
go readdate(newconnection, i)
}
func handlediscounect(disconnection net.Conn) {
logger.Log.Printf("Action: Client_Disconnected %v / %v is gone\n", accounts[disconnection] + 1, disconnection.RemoteAddr().(*net.TCPAddr).IP)
delete(accounts, disconnection)
}

using channels with google pubsub poll subscriber

I'm trying to create a google pubsub subscriber in golang where I take 100 messages at at a time and then write them to influx. I'm trying to use channels to do this like this:
package main
import (
"os"
"fmt"
"cloud.google.com/go/pubsub"
"log"
"sync"
"golang.org/x/net/context"
"encoding/json"
clnt "github.com/influxdata/influxdb/client/v2"
"time"
)
type SensorData struct {
Pressure float64 `json:"pressure"`
Temperature float64 `json:"temperature"`
Dewpoint float64 `json:"dewpoint"`
Timecollected int64 `json:"timecollected"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
Humidity float64 `json:"humidity"`
SensorID string `json:"sensorId"`
Zipcode int `json:"zipcode"`
Warehouse string `json:"warehouse"`
Area string `json:"area"`
}
type SensorPoints struct {
SensorData []SensorData
}
func main () {
messages := make(chan SensorData, 100)
// Create a new Influx HTTPClient
c, err := clnt.NewHTTPClient(clnt.HTTPConfig{
Addr: "http://localhost:8086",
Username: "user",
Password: "pass",
})
if err != nil {
log.Fatal(err)
}
// Create pubsub subscriber
ctx := context.Background()
proj := os.Getenv("GOOGLE_CLOUD_PROJECT")
if proj == "" {
fmt.Fprintf(os.Stderr, "GOOGLE_CLOUD_PROJECT environment variable must be set.\n")
os.Exit(1)
}
client, err := pubsub.NewClient(ctx, proj)
if err != nil {
log.Fatalf("Could not create pubsub Client: %v", err)
}
const sub = "influxwriter"
//create influx a blank batchpoint set
bp, err := clnt.NewBatchPoints(clnt.BatchPointsConfig{
Database: "sensordata",
Precision: "s",
})
if err != nil {
log.Fatal(err)
}
// Pull messages via the subscription.
go pullMsgs(client, sub, messages)
if err != nil {
log.Fatal(err)
}
writeInflux(messages, bp)
c.Close()
}
func pullMsgs(client *pubsub.Client, name string, messages chan<- SensorData) {
ctx := context.Background()
// [START pubsub_subscriber_async_pull]
// [START pubsub_quickstart_subscriber]
// Consume 10 messages.
var mu sync.Mutex
var sensorinfos SensorPoints
sensorinfo := &SensorData{}
received := 0
sub := client.Subscription(name)
cctx, _ := context.WithCancel(ctx)
err := sub.Receive(cctx, func(ctx context.Context, msg *pubsub.Message) {
msg.Ack()
json.Unmarshal(msg.Data, sensorinfo)
//fmt.Println(string(msg.Data))
//fmt.Println(sensorinfo.SensorID)
sensorinfos.SensorData = append(sensorinfos.SensorData, *sensorinfo)
mu.Lock()
defer mu.Unlock()
received++
fmt.Println("rcv: ", received)
messages <- *sensorinfo
})
if err != nil {
fmt.Println(err)
}
// [END pubsub_subscriber_async_pull]
// [END pubsub_quickstart_subscriber]
}
func writeInflux(sensorpoints <- chan SensorData, bp clnt.BatchPoints) {
for p := range sensorpoints {
// Create a point and add to batch
tags := map[string]string{
"sensorId": p.SensorID,
"warehouse": p.Warehouse,
"area": p.Area,
"zipcode": string(p.Zipcode),
}
fields := map[string]interface{}{
"pressure": p.Pressure,
"humidity": p.Humidity,
"temperature": p.Temperature,
"dewpoint": p.Dewpoint,
"longitude": p.Longitude,
"latitude": p.Latitude,
}
pt, err := clnt.NewPoint("sensordata", tags, fields, time.Unix(p.Timecollected, 0))
if err != nil {
log.Fatal(err)
}
bp.AddPoint(pt)
}
}
but it doesn't see to every get past the initial pullMsgs function and just keeps printing the output in there:
rcv: 1
rcv: 2
rcv: 3
rcv: 4
rcv: 5
rcv: 6
rcv: 7
I thought that once the channel get full, it should block until the channel is emptied out
this is the pubsub pull code I'm using as a reference.
When you've sent the desired number of messages on the channel, close the channel and cancel the context. Try using the technique demonstrated in the documentation of canceling after some number of messages. Since your buffer is 100 and you're trying to consume 100 messages at a time, that's the number. If you want your program to exit, close the channel so that the for e := range ch loop in writeInflux hits a stopping point and doesn't block waiting for more elements to be added to the channel.
Note this in the Go pubsub API doc:
To terminate a call to Receive, cancel its context.
That is not what's stalling your main goroutine, but your pullMsgs goroutine will not exit on its own without that cancel.
Also, check for errors on Unmarshal. If you don't want to handle unmarshal errors at this point in the code, consider changing the channel type and sending msg or msg.Data instead and unmarshaling upon channel receipt.
cctx, cancel := context.WithCancel(ctx)
err := sub.Receive(cctx, func(ctx context.Context, msg *pubsub.Message) {
msg.Ack()
err := json.Unmarshal(msg.Data, sensorinfo)
if err != nil {
fmt.Printf("Failed to unmarshal: %s\n", err)
}
mu.Lock()
defer mu.Unlock()
received++
fmt.Println("rcv: ", received)
messages <- *sensorinfo
if received == 100 {
close(messages) // no more messages will be sent on channel
cancel()
}

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