Initial
This commit is contained in:
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Code coverage profiles and other test artifacts
|
||||
*.out
|
||||
coverage.*
|
||||
*.coverprofile
|
||||
profile.cov
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
# env file
|
||||
.env
|
||||
|
||||
# Editor/IDE
|
||||
# .idea/
|
||||
.vscode/
|
||||
!.vscode/settings.json
|
||||
23
cmd/apiserver/main.go
Normal file
23
cmd/apiserver/main.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
|
||||
"git.nwaifu.su/sergey/MyGoServer/internal/app/apiserver"
|
||||
)
|
||||
|
||||
var (
|
||||
configPath string
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&configPath, "config-path", "/go/bin/configs/server.toml", "path to config")
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if err := apiserver.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
14
go.mod
Normal file
14
go.mod
Normal file
@@ -0,0 +1,14 @@
|
||||
module git.nwaifu.su/sergey/MyGoServer
|
||||
|
||||
go 1.25.4
|
||||
|
||||
require (
|
||||
github.com/felixge/httpsnoop v1.0.4
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.6.0
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||
)
|
||||
21
go.sum
Normal file
21
go.sum
Normal file
@@ -0,0 +1,21 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
29
internal/app/apiserver/apiserver.go
Normal file
29
internal/app/apiserver/apiserver.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package apiserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type contextKey struct {
|
||||
key string
|
||||
}
|
||||
|
||||
var connContextKey = &contextKey{"http-conn"}
|
||||
|
||||
func saveConnInContext(ctx context.Context, c net.Conn) context.Context {
|
||||
return context.WithValue(ctx, connContextKey, c)
|
||||
}
|
||||
|
||||
// Start the server
|
||||
func Start() error {
|
||||
srv := newServer()
|
||||
server := http.Server{
|
||||
Addr: ":8080",
|
||||
ConnContext: saveConnInContext,
|
||||
Handler: srv,
|
||||
}
|
||||
|
||||
return server.ListenAndServe()
|
||||
}
|
||||
39
internal/app/apiserver/responsewriter.go
Normal file
39
internal/app/apiserver/responsewriter.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package apiserver
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/felixge/httpsnoop"
|
||||
)
|
||||
|
||||
// Custom RW implementation
|
||||
type ResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
code int
|
||||
Hijacker http.Hijacker
|
||||
}
|
||||
|
||||
// Write status code to header
|
||||
func (w *ResponseWriter) WriteHeader(statusCode int) {
|
||||
w.code = statusCode
|
||||
w.ResponseWriter.WriteHeader(statusCode)
|
||||
}
|
||||
|
||||
// Get new RW
|
||||
func newResponseWriter(w http.ResponseWriter) *ResponseWriter {
|
||||
hijacker, _ := w.(http.Hijacker)
|
||||
return &ResponseWriter{
|
||||
ResponseWriter: httpsnoop.Wrap(w, httpsnoop.Hooks{}),
|
||||
Hijacker: hijacker,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
if w.Hijacker == nil {
|
||||
return nil, nil, errors.New("http.Hijacker not implemented by underlying http.ResponseWriter")
|
||||
}
|
||||
return w.Hijacker.Hijack()
|
||||
}
|
||||
81
internal/app/apiserver/server.go
Normal file
81
internal/app/apiserver/server.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package apiserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
ctxKeyRequestID = iota
|
||||
)
|
||||
|
||||
type server struct {
|
||||
router *mux.Router
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
s.router.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func newServer() *server {
|
||||
s := &server{
|
||||
router: mux.NewRouter(),
|
||||
logger: logrus.New(),
|
||||
}
|
||||
s.configureRouter()
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *server) configureRouter() {
|
||||
s.router.Use(s.setRequestID)
|
||||
s.router.Use(s.logRequest)
|
||||
s.router.HandleFunc("/", s.handleHome())
|
||||
}
|
||||
|
||||
func (s *server) handleHome() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("ok"))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) logRequest(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
logger := logrus.WithFields(logrus.Fields{
|
||||
"remote_addr": r.RemoteAddr,
|
||||
"request_id": r.Context().Value(ctxKeyRequestID),
|
||||
})
|
||||
logger.Infof("started %s %s", r.Method, r.RequestURI)
|
||||
start := time.Now()
|
||||
rw := newResponseWriter(w)
|
||||
rw.code = http.StatusOK
|
||||
next.ServeHTTP(rw, r)
|
||||
logger.Infof("completed with %d %s in %v", rw.code, http.StatusText(rw.code), time.Since(start))
|
||||
})
|
||||
}
|
||||
|
||||
func (s *server) setRequestID(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
id := uuid.New().String()
|
||||
w.Header().Set("X-Request-ID", id)
|
||||
next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), ctxKeyRequestID, id)))
|
||||
})
|
||||
}
|
||||
|
||||
// Helpers
|
||||
func (s *server) error(w http.ResponseWriter, r *http.Request, status int, err error) {
|
||||
s.respond(w, r, status, map[string]string{"error": err.Error()})
|
||||
}
|
||||
|
||||
func (s *server) respond(w http.ResponseWriter, r *http.Request, status int, data interface{}) {
|
||||
w.WriteHeader(status)
|
||||
if data != nil {
|
||||
json.NewEncoder(w).Encode(data)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user