snapshot of https://github.com/traggo/server.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
build / build (push) Failing after 3s
Some checks failed
build / build (push) Failing after 3s
This commit is contained in:
62
server/server.go
Normal file
62
server/server.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
var notifySignal = signal.Notify
|
||||
var serverShutdown = func(server *http.Server, ctx context.Context) error {
|
||||
return server.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// Start starts the http server
|
||||
func Start(mux *mux.Router, port int) error {
|
||||
server, shutdown := startServer(mux, port)
|
||||
shutdownOnInterruptSignal(server, 2*time.Second, shutdown)
|
||||
return waitForServerToClose(shutdown)
|
||||
}
|
||||
|
||||
func startServer(mux *mux.Router, port int) (*http.Server, chan error) {
|
||||
srv := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", port),
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
shutdown := make(chan error)
|
||||
go func() {
|
||||
err := srv.ListenAndServe()
|
||||
shutdown <- err
|
||||
}()
|
||||
return srv, shutdown
|
||||
}
|
||||
|
||||
func shutdownOnInterruptSignal(server *http.Server, timeout time.Duration, shutdown chan<- error) {
|
||||
interrupt := make(chan os.Signal, 1)
|
||||
notifySignal(interrupt, os.Interrupt)
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-interrupt:
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
if err := serverShutdown(server, ctx); err != nil {
|
||||
shutdown <- err
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func waitForServerToClose(shutdown <-chan error) error {
|
||||
err := <-shutdown
|
||||
if err == http.ErrServerClosed {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
105
server/server_test.go
Normal file
105
server/server_test.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/phayes/freeport"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/traggo/server/test"
|
||||
)
|
||||
|
||||
func TestShutdownOnErrorWhileShutdown(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
disposeInterrupt := fakeInterrupt(t)
|
||||
defer disposeInterrupt()
|
||||
|
||||
shutdownError := errors.New("shutdown error")
|
||||
disposeShutdown := fakeShutdownError(shutdownError)
|
||||
defer disposeShutdown()
|
||||
|
||||
finished := make(chan error)
|
||||
|
||||
go func() {
|
||||
finished <- Start(mux.NewRouter(), freeport.GetPort())
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("Server should be closed")
|
||||
case err := <-finished:
|
||||
assert.Equal(t, shutdownError, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShutdownAfterError(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
finished := make(chan error)
|
||||
|
||||
go func() {
|
||||
finished <- Start(mux.NewRouter(), -5)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("Server should be closed")
|
||||
case err := <-finished:
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShutdown(t *testing.T) {
|
||||
db := test.InMemoryDB(t)
|
||||
defer db.Close()
|
||||
|
||||
dispose := fakeInterrupt(t)
|
||||
defer dispose()
|
||||
|
||||
finished := make(chan error)
|
||||
|
||||
go func() {
|
||||
finished <- Start(mux.NewRouter(), freeport.GetPort())
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("Server should be closed")
|
||||
case err := <-finished:
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func fakeInterrupt(t *testing.T) func() {
|
||||
oldNotify := notifySignal
|
||||
notifySignal = func(c chan<- os.Signal, sig ...os.Signal) {
|
||||
assert.Contains(t, sig, os.Interrupt)
|
||||
go func() {
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
c <- os.Interrupt
|
||||
}
|
||||
}()
|
||||
}
|
||||
return func() {
|
||||
notifySignal = oldNotify
|
||||
}
|
||||
}
|
||||
|
||||
func fakeShutdownError(err error) func() {
|
||||
old := serverShutdown
|
||||
serverShutdown = func(server *http.Server, ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
return func() {
|
||||
serverShutdown = old
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user