snapshot of https://github.com/traggo/server.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
build / build (push) Failing after 3s

This commit is contained in:
upstream-mirror
2026-09-16 05:24:45 +05:00
commit cda7c1a474
272 changed files with 31973 additions and 0 deletions

62
server/server.go Normal file
View 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
View 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
}
}