Skip to content

Commit

Permalink
Add HTTP server versions.
Browse files Browse the repository at this point in the history
  • Loading branch information
winlinvip committed Aug 26, 2024
1 parent 7351a08 commit 2ca64f0
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 24 deletions.
2 changes: 1 addition & 1 deletion proxy/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ clean:
rm -f srs-proxy

run: fmt
go run main.go
go run .
86 changes: 86 additions & 0 deletions proxy/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (c) 2024 Winlin
//
// SPDX-License-Identifier: MIT
package main

import (
"context"
"fmt"
"net/http"
"os"
"srs-proxy/logger"
"strings"
"time"

"srs-proxy/errors"
)

type httpServer struct {
server *http.Server
}

func NewHttpServer() *httpServer {
return &httpServer{}
}

func (v *httpServer) Close() error {
return v.server.Close()
}

func (v *httpServer) ListenAndServe(ctx context.Context) error {
// Parse the gracefully quit timeout.
var gracefulQuitTimeout time.Duration
if t, err := time.ParseDuration(envGraceQuitTimeout()); err != nil {
return errors.Wrapf(err, "parse duration %v", envGraceQuitTimeout())
} else {
gracefulQuitTimeout = t
}

// Parse address to listen.
addr := envHttpServer()
if !strings.Contains(addr, ":") {
addr = ":" + addr
}

// Create server and handler.
mux := http.NewServeMux()
v.server = &http.Server{Addr: addr, Handler: mux}

// Shutdown the server gracefully when quiting.
go func() {
ctxParent := ctx
<-ctxParent.Done()

ctx, cancel := context.WithTimeout(context.Background(), gracefulQuitTimeout)
defer cancel()

v.server.Shutdown(ctx)
}()

// The basic version handler, also can be used as health check API.
logger.Df(ctx, "Handle /api/v1/versions by %v", addr)
mux.HandleFunc("/api/v1/versions", func(w http.ResponseWriter, r *http.Request) {
res := struct {
Code int `json:"code"`
PID string `json:"pid"`
Data struct {
Major int `json:"major"`
Minor int `json:"minor"`
Revision int `json:"revision"`
Version string `json:"version"`
} `json:"data"`
}{}

res.Code = 0
res.PID = fmt.Sprintf("%v", os.Getpid())
res.Data.Major = VersionMajor()
res.Data.Minor = VersionMinor()
res.Data.Revision = VersionRevision()
res.Data.Version = Version()

apiResponse(ctx, w, r, &res)
})

// Run HTTP server.
return v.server.ListenAndServe()
}
75 changes: 53 additions & 22 deletions proxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,38 @@ import (
"os"
"os/signal"
"path"
"srs-proxy/errors"
"srs-proxy/logger"
"syscall"
"time"

"srs-proxy/errors"
"srs-proxy/logger"

"github.com/joho/godotenv"
)

func main() {
ctx := logger.WithContext(context.Background())
logger.Df(ctx, "SRS %v/%v started", Signature(), Version())

if err := doMain(ctx); err != nil {
// Install signals.
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
ctx, cancel := context.WithCancel(ctx)
go func() {
for s := range sc {
logger.Df(ctx, "Got signal %v", s)
cancel()
}
}()

// Start the main loop, ignore the user cancel error.
err := doMain(ctx)
if err != nil && ctx.Err() == context.Canceled {
logger.Ef(ctx, "main: %v", err)
os.Exit(-1)
}

logger.Df(ctx, "Server %v done", Signature())
}

func doMain(ctx context.Context) error {
Expand All @@ -40,35 +56,43 @@ func doMain(ctx context.Context) error {
}
}

// Install signals.
sc := make(chan os.Signal, 1)
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
ctx, cancel := context.WithCancel(ctx)
go func() {
for s := range sc {
logger.Df(ctx, "Got signal %v", s)
cancel()
}
}()
// Whether enable the Go pprof.
setEnvDefault("GO_PPROF", "")
// Force shutdown timeout.
setEnvDefault("PROXY_FORCE_QUIT_TIMEOUT", "30s")
// Graceful quit timeout.
setEnvDefault("PROXY_GRACE_QUIT_TIMEOUT", "20s")

// The HTTP API server.
setEnvDefault("PROXY_HTTP_API", "1985")
// The HTTP web server.
setEnvDefault("PROXY_HTTP_SERVER", "8080")

logger.Df(ctx, "load .env as GO_PPROF=%v, "+
"PROXY_FORCE_QUIT_TIMEOUT=%v, PROXY_GRACE_QUIT_TIMEOUT=%v, "+
"PROXY_HTTP_API=%v, PROXY_HTTP_SERVER=%v",
envGoPprof(),
envForceQuitTimeout(), envGraceQuitTimeout(),
envHttpAPI(), envHttpServer(),
)

// When cancelled, the program is forced to exit due to a timeout. Normally, this doesn't occur
// because the main thread exits after the context is cancelled. However, sometimes the main thread
// may be blocked for some reason, so a forced exit is necessary to ensure the program terminates.
var forceTimeout time.Duration
if t, err := time.ParseDuration(envForceQuitTimeout()); err != nil {
return errors.Wrapf(err, "parse force timeout %v", envForceQuitTimeout())
} else {
forceTimeout = t
}

go func() {
<-ctx.Done()
time.Sleep(30 * time.Second)
time.Sleep(forceTimeout)
logger.Wf(ctx, "Force to exit by timeout")
os.Exit(1)
}()

// Whether enable the Go pprof.
setEnvDefault("GO_PPROF", "")

// The HTTP API server.
setEnvDefault("PROXY_HTTP_API", "1985")

logger.Df(ctx, "load .env as GO_PPROF=%v, PROXY_HTTP_API=%v", envGoPprof(), envHttpAPI())

// Start the Go pprof if enabled.
if addr := envGoPprof(); addr != "" {
go func() {
Expand All @@ -77,5 +101,12 @@ func doMain(ctx context.Context) error {
}()
}

// Start the HTTP web server.
httpServer := NewHttpServer()
defer httpServer.Close()
if err := httpServer.ListenAndServe(ctx); err != nil {
return errors.Wrapf(err, "http server")
}

return nil
}
32 changes: 31 additions & 1 deletion proxy/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
// SPDX-License-Identifier: MIT
package main

import "os"
import (
"context"
"encoding/json"
"net/http"
"os"
"srs-proxy/logger"
)

// setEnvDefault set env key=value if not set.
func setEnvDefault(key, value string) {
Expand All @@ -16,6 +22,30 @@ func envHttpAPI() string {
return os.Getenv("PROXY_HTTP_API")
}

func envHttpServer() string {
return os.Getenv("PROXY_HTTP_SERVER")
}

func envGoPprof() string {
return os.Getenv("GO_PPROF")
}

func envForceQuitTimeout() string {
return os.Getenv("PROXY_FORCE_QUIT_TIMEOUT")
}

func envGraceQuitTimeout() string {
return os.Getenv("PROXY_GRACE_QUIT_TIMEOUT")
}

func apiResponse(ctx context.Context, w http.ResponseWriter, r *http.Request, data any) {
b, err := json.Marshal(data)
if err != nil {
logger.Wf(ctx, "marshal %v err %v", data, err)
return
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(b)
}

0 comments on commit 2ca64f0

Please sign in to comment.