writing a client over websocket in golang - go

I'm writing an client that dials a websocket, and waits to receive information. I can successfully dial the websocket, but I cannot figure out how to implement some kind of asynchronous callback using the "golang.org/x/net/websocket" package. Is this even possible, or should I use the Gorilla package?

Use a for loop to read a websocket using the Gorilla package:
for {
_, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
c.Close()
break
}
handleMessage(message)
}
Run the loop in a goroutine to make it asynchronous:
go func() {
for {
_, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
c.Close()
break
}
handleMessage(message)
}
}()

Related

how to gomock a server streaming grpc method?

I tried using the following code but the client hangs and never receives from the mock server.
I thought this part: Do(rs.EXPECT().Send(&proto.Response{Result: "steve"})would make the mock server response to the client, it never worked out.
For a server-streaming grpc mock, a typical approach like:
mockServer.EXPECT().FetchResponse(&proto.Request{Id: 123}, rs).Times(1).Return(...)
doesn't work, the return value is expected to be an error, not a streaming message.
Could someone with a kind heart please explain how this problem should be handled properly?
Code
func Test_Streaming(t *testing.T) {
// create listiner
lis, err := net.Listen("tcp", ":50005")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
// create grpc server
s := grpc.NewServer()
c := gomock.NewController(t)
mockServer := mock_proto.NewMockStreamServiceServer(c)
proto.RegisterStreamServiceServer(s, mockServer)
go func() {
log.Println("start server")
// and start...
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}()
rs := mock_proto.NewMockStreamService_FetchResponseServer(c)
mockServer.EXPECT().FetchResponse(&proto.Request{Id: 123}, rs).Times(1).Do(rs.EXPECT().Send(&proto.Response{Result: "steve"}))
// client call FetchResponse
ClientConnect()
}

Synchronizing a Test Server During Tests

Summary: I'm running into a race condition during testing where my server is not reliably ready to serve requests before making client requests against it. How can I block only until the listener is ready, and still maintain composable public APIs without requiring users to BYO net.Listener?
We see the following error as the goroutine that spins up our (blocking) server in the background isn't listening before we call client.Do(req) in the TestRun test function.
--- FAIL: TestRun/Server_accepts_HTTP_requests (0.00s)
/home/matt/repos/admission-control/server_test.go:64: failed to make a request: Get https://127.0.0.1:37877: dial tcp 127.0.0.1:37877: connect: connection refused
I'm not using httptest.Server directly as I'm attempting to test the blocking & cancellation characteristics of my own server componenent.
I create an httptest.NewUnstartedServer, clone its *tls.Config into a new http.Server after starting it with StartTLS(), and then close it, before calling *AdmissionServer.Run(). This also has the benefit of giving me a *http.Client with the matching RootCAs configured.
Testing TLS is important here as the daemon this exposes lives in a TLS-only environment.
func newTestServer(ctx context.Context, t *testing.T) *httptest.Server {
testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "OK")
})
testSrv := httptest.NewUnstartedServer(testHandler)
admissionServer, err := NewServer(nil, &noopLogger{})
if err != nil {
t.Fatalf("admission server creation failed: %s", err)
return nil
}
// We start the test server, copy its config out, and close it down so we can
// start our own server. This is because httptest.Server only generates a
// self-signed TLS config after starting it.
testSrv.StartTLS()
admissionServer.srv = &http.Server{
Addr: testSrv.Listener.Addr().String(),
Handler: testHandler,
TLSConfig: testSrv.TLS.Clone(),
}
testSrv.Close()
// We need a better synchronization primitive here that doesn't block
// but allows the underlying listener to be ready before
// serving client requests.
go func() {
if err := admissionServer.Run(ctx); err != nil {
t.Fatalf("server returned unexpectedly: %s", err)
}
}()
return testSrv
}
// Test that we can start a minimal AdmissionServer and handle a request.
func TestRun(t *testing.T) {
testSrv := newTestServer(context.TODO(), t)
t.Run("Server accepts HTTP requests", func(t *testing.T) {
client := testSrv.Client()
req, err := http.NewRequest(http.MethodGet, testSrv.URL, nil)
if err != nil {
t.Fatalf("request creation failed: %s", err)
}
resp, err := client.Do(req)
if err != nil {
t.Fatalf("failed to make a request: %s", err)
}
// Later sub-tests will test cancellation propagation, signal handling, etc.
For posterity, this is our composable Run function, that listens in a goroutine and then blocks on our cancellation & error channels in a for-select:
type AdmissionServer struct {
srv *http.Server
logger log.Logger
GracePeriod time.Duration
}
func (as *AdmissionServer) Run(ctx context.Context) error {
sigChan := make(chan os.Signal, 1)
defer close(sigChan)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
// run in goroutine
errs := make(chan error)
defer close(errs)
go func() {
as.logger.Log(
"msg", fmt.Sprintf("admission control listening on '%s'", as.srv.Addr),
)
if err := as.srv.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed {
errs <- err
as.logger.Log(
"err", err.Error(),
"msg", "the server exited",
)
return
}
return
}()
// Block indefinitely until we receive an interrupt, cancellation or error
// signal.
for {
select {
case sig := <-sigChan:
as.logger.Log(
"msg", fmt.Sprintf("signal received: %s", sig),
)
return as.shutdown(ctx, as.GracePeriod)
case err := <-errs:
as.logger.Log(
"msg", fmt.Sprintf("listener error: %s", err),
)
// We don't need to explictly call shutdown here, as
// *http.Server.ListenAndServe closes the listener when returning an error.
return err
case <-ctx.Done():
as.logger.Log(
"msg", fmt.Sprintf("cancellation received: %s", ctx.Err()),
)
return as.shutdown(ctx, as.GracePeriod)
}
}
}
Notes:
There is a (simple) constructor for an *AdmissionServer: I've left it out for brevity. The AdmissionServer is composable and accepts a *http.Server so that it can be plugged into existing applications easily.
The wrapped http.Server type that we create a listener from doesn't itself expose any way to tell if its listening; at best we can try to listen again and catch the error (e.g. port already bound to another listener), which does not seem robust as the net package doesn't expose a useful typed error for this.
You can just attempt to connect to the server before starting the test suite, as part of the initialization process.
For example, I usually have a function like this in my tests:
// waitForServer attempts to establish a TCP connection to localhost:<port>
// in a given amount of time. It returns upon a successful connection;
// ptherwise exits with an error.
func waitForServer(port string) {
backoff := 50 * time.Millisecond
for i := 0; i < 10; i++ {
conn, err := net.DialTimeout("tcp", ":"+port, 1*time.Second)
if err != nil {
time.Sleep(backoff)
continue
}
err = conn.Close()
if err != nil {
log.Fatal(err)
}
return
}
log.Fatalf("Server on port %s not up after 10 attempts", port)
}
Then in my TestMain() I do:
func TestMain(m *testing.M) {
go startServer()
waitForServer(serverPort)
// run the suite
os.Exit(m.Run())
}

Gorilla mux reuse/restart server after a shutdown

I am setting up an http server in a separate goroutine. This goroutine will set up a new server on a signal. However, after a shutdown request, I cannot restart that, Here is the code
for {
select {
case <-m.start:
fmt.Println("Start the server now")
go func() {
err := m.ListenAndServe()
if err != nil {
fmt.Println(err)
}
}()
case <-m.stop:
fmt.Println("Stop the server now")
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
err := m.Shutdown(ctx)
fmt.Println("Error is :", err)
if err != nil {
fmt.Println(err)
}
cancel()
}()
}
}
I am controlling this goroutine through separate handlers externally. In the documentation, it is mentioned that the server may not be reused after a shutdown. Is there any alternative?

How can I know when two servers are ready to get requests?

I have two servers
router := createServer()
loginServer := createLoginServerMock()
servLogin := &http.Server{
Addr: ":9333",
Handler: loginServer,
}
testServer := &http.Server{
Addr: ":9444",
Handler: router,
}
loginServer.ListenAndServe()
testServer.ListenAndServe()
And I want to send a request on both of them after they created? How can I detect that?
ListenAndServe has no feedback mechanism to let you know when the server is ready. You have to create the listener yourself and pass it to Server.Serve:
loginListener, err := net.Listen("tcp", ":9333")
if err != nil {
// TODO: handle err
}
testListener, err := net.Listen("tcp", ":9444")
if err != nil {
// TODO: handle err
}
// You may already start sending requests now. They will just wait until
// the servers call Accept on their respective listener (i.e. shortly after
// Serve is called).
servLogin := &http.Server{Handler: loginServer}
testServer := &http.Server{Handler: router}
go func() { log.Fatal(servLogin.Serve(loginListener)) }()
go func() { log.Fatal(testServer.Serve(testListener)) }()
ListenAndServe() will return an error when create server failed. Just handle it to known server start success or not. I can see your coding will only start login server while test server be hanging. Let's try
go func() {
log.Fatal(loginServer.ListenAndServe())
}
log.Fatal(testServer.ListenAndServe())
The program will wait at 1st ListenAndServe() until any error occurred. Because the ListenAndServe() method runs forever. So your second server will never started until the execution goes to the next ListenAndServe(). So use go in front of the 1st server's statement:
router := createServer()
loginServer := createLoginServerMock()
go func() {
if err := http.ListenAndServe(":9333", loginServer); err != nil {
log.Fatal("login server err: ", err)
}
}()
if err := http.ListenAndServe(":9444", router); err != nil {
log.Fatal("test server err: ", err)
}
Then access your loginServer at http://localhost:9333 and testServer at http://localhost:9444.

Golang grpc: how to recover the grpc server from panic?

I have a handler like this:
func (daemon *Daemon) List(item string) (map[string][]string, error) {
panic("this is a panic")
...
I just want to recover this potential panic, so in my grpc server I wrote:
// Serve serves gRPC request by goroutines
func (s *ServerRPC) Serve(addr string) error {
defer func() {
if err := recover(); err != nil {
glog.Errorf("Recovered from err: ", err)
}
}()
l, err := net.Listen("tcp", addr)
if err != nil {
glog.Fatalf("Failed to listen %s: %v", addr, err)
return err
}
return s.server.Serve(l)
}
But it does not capture the panic, I thought it was because my panic happened in child goroutines? If so, how can I recover my grpc server from crashing properly?
Take a look at the recovery interceptor in the Golang gRPC Middlewares package. It recovers from panics in the request Goroutine and returns them as an internal gRPC error.
myServer := grpc.NewServer(
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
grpc_zap.StreamServerInterceptor(zapLogger),
grpc_auth.StreamServerInterceptor(myAuthFunction),
grpc_recovery.StreamServerInterceptor(),
)),
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
grpc_zap.UnaryServerInterceptor(zapLogger),
grpc_auth.UnaryServerInterceptor(myAuthFunction),
grpc_recovery.UnaryServerInterceptor(),
)),
)

Resources