project initialized
This commit is contained in:
162
web/fasthttp/server-handler.go
Normal file
162
web/fasthttp/server-handler.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package fasthttp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
olog "git.loafle.net/overflow/log-go"
|
||||
"git.loafle.net/overflow/server-go"
|
||||
"git.loafle.net/overflow/server-go/web"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
type ServerHandler interface {
|
||||
web.ServerHandler
|
||||
|
||||
OnError(serverCtx server.ServerCtx, ctx *fasthttp.RequestCtx, err *web.Error)
|
||||
|
||||
RegisterServlet(path string, servlet Servlet)
|
||||
Servlet(serverCtx server.ServerCtx, ctx *fasthttp.RequestCtx) Servlet
|
||||
|
||||
CheckOrigin(ctx *fasthttp.RequestCtx) bool
|
||||
}
|
||||
|
||||
type ServerHandlers struct {
|
||||
web.ServerHandlers
|
||||
|
||||
ErrorServelt Servlet `json:"-"`
|
||||
|
||||
// path = context only.
|
||||
// ex) /auth => /auth, /auth/member => /auth
|
||||
servlets map[string]Servlet
|
||||
|
||||
validated atomic.Value
|
||||
}
|
||||
|
||||
func (sh *ServerHandlers) Init(serverCtx server.ServerCtx) error {
|
||||
if err := sh.ServerHandlers.Init(serverCtx); nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
if nil != sh.servlets {
|
||||
for _, servlet := range sh.servlets {
|
||||
if err := servlet.Init(serverCtx); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sh *ServerHandlers) OnStart(serverCtx server.ServerCtx) error {
|
||||
if err := sh.ServerHandlers.OnStart(serverCtx); nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
if nil != sh.servlets {
|
||||
for _, servlet := range sh.servlets {
|
||||
if err := servlet.OnStart(serverCtx); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sh *ServerHandlers) OnStop(serverCtx server.ServerCtx) {
|
||||
if nil != sh.servlets {
|
||||
for _, servlet := range sh.servlets {
|
||||
servlet.OnStop(serverCtx)
|
||||
}
|
||||
}
|
||||
|
||||
sh.ServerHandlers.OnStop(serverCtx)
|
||||
}
|
||||
|
||||
func (sh *ServerHandlers) Destroy(serverCtx server.ServerCtx) {
|
||||
if nil != sh.servlets {
|
||||
for _, servlet := range sh.servlets {
|
||||
servlet.Destroy(serverCtx)
|
||||
}
|
||||
}
|
||||
|
||||
sh.ServerHandlers.Destroy(serverCtx)
|
||||
}
|
||||
|
||||
func (sh *ServerHandlers) OnError(serverCtx server.ServerCtx, ctx *fasthttp.RequestCtx, err *web.Error) {
|
||||
if nil != sh.ErrorServelt {
|
||||
servletCtx := sh.ErrorServelt.ServletCtx(serverCtx)
|
||||
servletCtx.SetAttribute(web.ErrorKey, err)
|
||||
sh.ErrorServelt.Handle(servletCtx, ctx)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Error(err.Cause.Error(), err.Code)
|
||||
}
|
||||
|
||||
func (sh *ServerHandlers) RegisterServlet(contextPath string, servlet Servlet) {
|
||||
if nil == sh.servlets {
|
||||
sh.servlets = make(map[string]Servlet)
|
||||
}
|
||||
servlet.setContextPath(contextPath)
|
||||
sh.servlets[contextPath] = servlet
|
||||
}
|
||||
|
||||
func (sh *ServerHandlers) Servlet(serverCtx server.ServerCtx, ctx *fasthttp.RequestCtx) Servlet {
|
||||
path := string(ctx.Path())
|
||||
contextPath, err := getContextPath(path)
|
||||
if nil != err {
|
||||
olog.Logger().Warnf("Bad Request %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
var servlet Servlet
|
||||
if servlet = sh.servlets[contextPath]; nil == servlet {
|
||||
olog.Logger().Warnf("Servlet is not exist for url[%s]", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
return servlet
|
||||
}
|
||||
|
||||
func (sh *ServerHandlers) CheckOrigin(ctx *fasthttp.RequestCtx) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (sh *ServerHandlers) Validate() error {
|
||||
if nil != sh.validated.Load() {
|
||||
return nil
|
||||
}
|
||||
sh.validated.Store(true)
|
||||
|
||||
if err := sh.ServerHandlers.Validate(); nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getContextPath(path string) (string, error) {
|
||||
p := strings.TrimSpace(path)
|
||||
|
||||
if !strings.HasPrefix(p, "/") {
|
||||
return "", fmt.Errorf("path[%s] must started /", path)
|
||||
}
|
||||
p = p[1:]
|
||||
|
||||
if strings.HasSuffix(p, "/") {
|
||||
cpl := len(p) - 1
|
||||
p = p[:cpl]
|
||||
}
|
||||
|
||||
components := strings.Split(p, "/")
|
||||
if 0 == len(components) {
|
||||
return "", fmt.Errorf("path[%s] is not invalid", path)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("/%s", components[0]), nil
|
||||
}
|
||||
159
web/fasthttp/server.go
Normal file
159
web/fasthttp/server.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package fasthttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
olog "git.loafle.net/overflow/log-go"
|
||||
"git.loafle.net/overflow/server-go"
|
||||
"git.loafle.net/overflow/server-go/web"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
ServerHandler ServerHandler
|
||||
|
||||
ctx server.ServerCtx
|
||||
stopChan chan struct{}
|
||||
stopWg sync.WaitGroup
|
||||
|
||||
hs *fasthttp.Server
|
||||
}
|
||||
|
||||
func (s *Server) ListenAndServe() error {
|
||||
var (
|
||||
err error
|
||||
listener net.Listener
|
||||
)
|
||||
if nil == s.ServerHandler {
|
||||
return fmt.Errorf("%s server handler must be specified", s.logHeader())
|
||||
}
|
||||
s.ServerHandler.Validate()
|
||||
|
||||
if s.stopChan != nil {
|
||||
return fmt.Errorf("%s already running. Stop it before starting it again", s.logHeader())
|
||||
}
|
||||
|
||||
s.ctx = s.ServerHandler.ServerCtx()
|
||||
if nil == s.ctx {
|
||||
return fmt.Errorf("%s ServerCtx is nil", s.logHeader())
|
||||
}
|
||||
|
||||
s.hs = &fasthttp.Server{
|
||||
Handler: s.httpHandler,
|
||||
Name: s.ServerHandler.GetName(),
|
||||
Concurrency: s.ServerHandler.GetConcurrency(),
|
||||
ReadBufferSize: s.ServerHandler.GetReadBufferSize(),
|
||||
WriteBufferSize: s.ServerHandler.GetWriteBufferSize(),
|
||||
ReadTimeout: s.ServerHandler.GetReadTimeout(),
|
||||
WriteTimeout: s.ServerHandler.GetWriteTimeout(),
|
||||
}
|
||||
|
||||
if err = s.ServerHandler.Init(s.ctx); nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
if listener, err = s.ServerHandler.Listener(s.ctx); nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
s.stopChan = make(chan struct{})
|
||||
|
||||
s.stopWg.Add(1)
|
||||
return s.handleServer(listener)
|
||||
}
|
||||
|
||||
func (s *Server) Shutdown(ctx context.Context) error {
|
||||
if s.stopChan == nil {
|
||||
return fmt.Errorf("%s must be started before stopping it", s.logHeader())
|
||||
}
|
||||
close(s.stopChan)
|
||||
s.stopWg.Wait()
|
||||
|
||||
s.ServerHandler.Destroy(s.ctx)
|
||||
|
||||
s.stopChan = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) logHeader() string {
|
||||
return fmt.Sprintf("Server[%s]:", s.ServerHandler.GetName())
|
||||
}
|
||||
|
||||
func (s *Server) handleServer(listener net.Listener) error {
|
||||
var (
|
||||
err error
|
||||
stopping atomic.Value
|
||||
)
|
||||
|
||||
defer func() {
|
||||
if nil != listener {
|
||||
listener.Close()
|
||||
}
|
||||
s.ServerHandler.OnStop(s.ctx)
|
||||
|
||||
olog.Logger().Infof("%s Stopped", s.logHeader())
|
||||
|
||||
s.stopWg.Done()
|
||||
}()
|
||||
|
||||
if err = s.ServerHandler.OnStart(s.ctx); nil != err {
|
||||
return err
|
||||
}
|
||||
|
||||
hsCloseChan := make(chan error)
|
||||
go func() {
|
||||
if err := s.hs.Serve(listener); nil != err {
|
||||
if nil == stopping.Load() {
|
||||
hsCloseChan <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
hsCloseChan <- nil
|
||||
}()
|
||||
|
||||
olog.Logger().Infof("%s Started", s.logHeader())
|
||||
|
||||
select {
|
||||
case err, _ := <-hsCloseChan:
|
||||
if nil != err {
|
||||
return err
|
||||
}
|
||||
case <-s.stopChan:
|
||||
stopping.Store(true)
|
||||
listener.Close()
|
||||
<-hsCloseChan
|
||||
listener = nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) httpHandler(ctx *fasthttp.RequestCtx) {
|
||||
var (
|
||||
servlet Servlet
|
||||
)
|
||||
|
||||
if s.ServerHandler.CheckOrigin(ctx) {
|
||||
return
|
||||
}
|
||||
|
||||
if servlet = s.ServerHandler.Servlet(s.ctx, ctx); nil == servlet {
|
||||
s.onError(ctx, web.NewError(fasthttp.StatusNotFound, fmt.Errorf("Not Found")))
|
||||
return
|
||||
}
|
||||
|
||||
servletCtx := servlet.ServletCtx(s.ctx)
|
||||
|
||||
if err := servlet.Handle(servletCtx, ctx); nil != err {
|
||||
s.onError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) onError(ctx *fasthttp.RequestCtx, err *web.Error) {
|
||||
s.ServerHandler.OnError(s.ctx, ctx, err)
|
||||
}
|
||||
55
web/fasthttp/servlet.go
Normal file
55
web/fasthttp/servlet.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package fasthttp
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.loafle.net/overflow/server-go"
|
||||
"git.loafle.net/overflow/server-go/web"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
type Servlet interface {
|
||||
web.Servlet
|
||||
|
||||
Handle(servletCtx server.ServletCtx, ctx *fasthttp.RequestCtx) *web.Error
|
||||
RequestPath(ctx *fasthttp.RequestCtx) string
|
||||
setContextPath(contextPath string)
|
||||
}
|
||||
|
||||
type Servlets struct {
|
||||
Servlet
|
||||
|
||||
ContextPath string
|
||||
}
|
||||
|
||||
func (s *Servlets) ServletCtx(serverCtx server.ServerCtx) server.ServletCtx {
|
||||
return server.NewServletContext(nil, serverCtx)
|
||||
}
|
||||
|
||||
func (s *Servlets) Init(serverCtx server.ServerCtx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Servlets) OnStart(serverCtx server.ServerCtx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Servlets) OnStop(serverCtx server.ServerCtx) {
|
||||
//
|
||||
}
|
||||
|
||||
func (s *Servlets) Destroy(serverCtx server.ServerCtx) {
|
||||
//
|
||||
}
|
||||
|
||||
func (s *Servlets) Handle(servletCtx server.ServletCtx, ctx *fasthttp.RequestCtx) *web.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Servlets) setContextPath(contextPath string) {
|
||||
s.ContextPath = contextPath
|
||||
}
|
||||
|
||||
func (s *Servlets) RequestPath(ctx *fasthttp.RequestCtx) string {
|
||||
return strings.Replace(string(ctx.Path()), s.ContextPath, "", -1)
|
||||
}
|
||||
Reference in New Issue
Block a user