router/server.go

207 lines
4.3 KiB
Go

package router
import (
"io"
"net/http"
"net/url"
"somehole.com/common/log"
)
type Error interface {
Error(e Error) (err Error)
Status() (code int)
String() (out string)
Response() (res *GenericResponse)
}
type GenericRequest struct {
Url url.URL
Header Header
Values Values
Body Body
}
type Request[RQB RequestBuilder] struct {
GenericRequest
requestBuilder RQB
}
func NewRequest[RQB RequestBuilder](rqb RQB) *Request[RQB] {
return &Request[RQB]{
GenericRequest: *rqb.Request(),
requestBuilder: rqb,
}
}
func (*Request[RQB]) RequestBuilder() RequestBuilder {
var rqb RQB
return rqb.New()
}
type RequestBuilder interface {
mustEmbedDefaultRequestBuilder()
New() (rqb RequestBuilder)
SetAllowed(method string) (err Error)
SetUrl(url url.URL) (err Error)
SetHeader(map[string][]string) (err Error)
SetValues(map[string][]string) (err Error)
SetBody(body io.ReadCloser) (err Error)
Request() *GenericRequest
}
func NewRequestBuilder[RQB RequestBuilder]() RQB {
var rqb RQB
return rqb.New().(RQB)
}
type GenericResponse struct {
Status int
Header Header
Body Body
}
type Response[RSB ResponseBuilder] struct {
GenericResponse
responseBuilder RSB
}
func NewResponse[RSB ResponseBuilder](rsb RSB) *Response[RSB] {
return &Response[RSB]{
GenericResponse: *rsb.Response(),
responseBuilder: rsb,
}
}
func (res *Response[RSB]) ResponseBuilder() RSB {
return res.responseBuilder
}
type ResponseBuilder interface {
mustEmbedDefaultResponseBuilder()
New() (rsb ResponseBuilder)
SetHeader(map[string][]string) (err Error)
SetBody(body []byte) (err Error)
Response() *GenericResponse
}
func NewResponseBuilder[RSB ResponseBuilder]() RSB {
var rsb RSB
return rsb.New().(RSB)
}
type writer struct {
http.ResponseWriter
log.Logger
}
func (w *writer) handleError(err Error) (ok bool) {
if err != nil {
res := err.Response()
for key, value := range res.Header {
for _, v := range value {
w.Header().Add(key, v)
}
}
w.WriteHeader(res.Status)
w.Write(res.Body)
w.Logf(log.LevelError, err.String())
return false
}
return true
}
type serveStage uint8
const (
servePre serveStage = iota
serveMain
servePost
numServeStages
)
type ServeFunc[RQB RequestBuilder, RSB ResponseBuilder] func(req *Request[RQB], rsb RSB) (err Error)
type server[RQB RequestBuilder, RSB ResponseBuilder] struct {
logger log.Logger
serve [numServeStages][]ServeFunc[RQB, RSB]
}
func NewServer[RQB RequestBuilder, RSB ResponseBuilder](serve ServeFunc[RQB, RSB]) (srv *server[RQB, RSB]) {
srv = &server[RQB, RSB]{
serve: [numServeStages][]ServeFunc[RQB, RSB]{},
}
srv.serve[serveMain] = append(srv.serve[serveMain], serve)
return srv
}
func (srv *server[RQB, RSB]) SetLogger(logger log.Logger) *server[RQB, RSB] {
srv.logger = logger
return srv
}
func (srv *server[RQB, RSB]) addServeFunc(stage serveStage, serve ServeFunc[RQB, RSB]) *server[RQB, RSB] {
if srv.serve[stage] == nil {
srv.serve[stage] = make([]ServeFunc[RQB, RSB], 0)
}
srv.serve[stage] = append(srv.serve[stage], serve)
return srv
}
func (srv *server[RQB, RSB]) PreServe(serve ServeFunc[RQB, RSB]) *server[RQB, RSB] {
return srv.addServeFunc(servePre, serve)
}
func (srv *server[RQB, RSB]) AddServe(serve ServeFunc[RQB, RSB]) *server[RQB, RSB] {
return srv.addServeFunc(serveMain, serve)
}
func (srv *server[RQB, RSB]) PostServe(serve ServeFunc[RQB, RSB]) *server[RQB, RSB] {
return srv.addServeFunc(servePost, serve)
}
func (srv *server[RQB, RSB]) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var (
rqb = NewRequestBuilder[RQB]()
rsb = NewResponseBuilder[RSB]()
)
wr := writer{ResponseWriter: w, Logger: srv.logger}
if ok := wr.handleError(rqb.SetAllowed(r.Method)); !ok {
return
}
if ok := wr.handleError(rqb.SetUrl(*r.URL)); !ok {
return
}
if ok := wr.handleError(rqb.SetHeader(r.Header)); !ok {
return
}
if ok := wr.handleError(rqb.SetBody(r.Body)); !ok {
return
}
r.ParseForm()
if ok := wr.handleError(rqb.SetValues(r.Form)); !ok {
return
}
req := NewRequest(rqb)
for _, stage := range srv.serve {
for _, serve := range stage {
err := serve(req, rsb)
if ok := wr.handleError(err); !ok {
return
}
}
}
res := NewResponse(rsb)
if res.Header != nil {
for key, value := range res.Header {
for _, v := range value {
wr.Header().Add(key, v)
}
}
}
wr.WriteHeader(res.Status)
if len(res.Body) > 0 {
wr.Write(res.Body)
}
}