Initial commit
This commit is contained in:
commit
0ceaf17c34
35
.editorconfig
Normal file
35
.editorconfig
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
end_of_line = LF
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.sh]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[{*.html,*.js,*.css,*.scss}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 8
|
||||||
|
|
||||||
|
[{{*.,}[Dd]ockerfile{.*,},{*.,}[Cc]ontainerfile{.*,}}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.proto]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[{*.go,go.mod}]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 8
|
25
LICENSE
Normal file
25
LICENSE
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
BSD 2-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2024, some
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module somehole.com/common/router
|
||||||
|
|
||||||
|
go 1.23.1
|
||||||
|
|
||||||
|
require somehole.com/common/log v0.1.3
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
somehole.com/common/log v0.1.3 h1:2PAui0+5EryTAHqVUZQeepLcJTGssHKz2OL+jBknHI0=
|
||||||
|
somehole.com/common/log v0.1.3/go.mod h1:NS2eHnN120GA6oFbBm3XhB5yHww0eXTbLuMQYZxYNyA=
|
36
header.go
Normal file
36
header.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
type Header map[string][]string
|
||||||
|
|
||||||
|
func (h Header) Get(key string) (value string) {
|
||||||
|
v, ok := h[key]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return strings.Join(v, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Header) Set(key string, value string) {
|
||||||
|
h[key] = []string{value}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Header) Add(key string, value string) {
|
||||||
|
h[key] = append(h[key], value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Header) Merge(header Header) {
|
||||||
|
for key, value := range header {
|
||||||
|
if _, exists := h[key]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
h[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Header) MergeOverwrite(header Header) {
|
||||||
|
for key, value := range header {
|
||||||
|
h[key] = value
|
||||||
|
}
|
||||||
|
}
|
43
prototype.go
Normal file
43
prototype.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
type Request interface {
|
||||||
|
Allowed(method string) ErrorResponse
|
||||||
|
Header() Header
|
||||||
|
ReadBody(body io.ReadCloser) ErrorResponse
|
||||||
|
ParseForm(values map[string][]string) ErrorResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrototypeRequest interface {
|
||||||
|
Request() Request
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response interface {
|
||||||
|
Header() Header
|
||||||
|
Response() (body []byte)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrototypeResponse interface {
|
||||||
|
Response() Response
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorResponse interface {
|
||||||
|
Ok() bool
|
||||||
|
HttpStatus() int
|
||||||
|
ErrorResponse() []byte
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrototypeErrorResponse interface {
|
||||||
|
ErrorResponse() ErrorResponse
|
||||||
|
AllowedError() ErrorResponse
|
||||||
|
ReadError() ErrorResponse
|
||||||
|
ParseError() ErrorResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type Prototype struct {
|
||||||
|
PrototypeRequest
|
||||||
|
PrototypeResponse
|
||||||
|
PrototypeErrorResponse
|
||||||
|
}
|
97
server.go
Normal file
97
server.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"somehole.com/common/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stage uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
pre stage = iota
|
||||||
|
main
|
||||||
|
post
|
||||||
|
numStages
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServeFunc func(req Request, res Response) (errRes ErrorResponse)
|
||||||
|
|
||||||
|
type server struct {
|
||||||
|
prototype Prototype
|
||||||
|
logger log.Logger
|
||||||
|
serve [numStages][]ServeFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(prototype Prototype, logger log.Logger, serve ServeFunc) (srv *server) {
|
||||||
|
srv = &server{
|
||||||
|
prototype: prototype,
|
||||||
|
logger: logger,
|
||||||
|
serve: [numStages][]ServeFunc{},
|
||||||
|
}
|
||||||
|
srv.serve[main] = append(srv.serve[main], serve)
|
||||||
|
return srv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *server) addServeFunc(when stage, serve ServeFunc) *server {
|
||||||
|
if srv.serve[when] == nil {
|
||||||
|
srv.serve[when] = make([]ServeFunc, 0)
|
||||||
|
}
|
||||||
|
srv.serve[when] = append(srv.serve[when], serve)
|
||||||
|
return srv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *server) PreServeFunc(serve ServeFunc) *server {
|
||||||
|
return srv.addServeFunc(pre, serve)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *server) AddServeFunc(serve ServeFunc) *server {
|
||||||
|
return srv.addServeFunc(main, serve)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *server) PostServeFunc(serve ServeFunc) *server {
|
||||||
|
return srv.addServeFunc(post, serve)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *server) handleError(errRes ErrorResponse, w http.ResponseWriter) (ok bool) {
|
||||||
|
if !errRes.Ok() {
|
||||||
|
w.WriteHeader(errRes.HttpStatus())
|
||||||
|
w.Write(errRes.ErrorResponse())
|
||||||
|
srv.logger.Logf(log.LevelError, errRes.String())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
pro := srv.prototype
|
||||||
|
req := pro.Request()
|
||||||
|
if ok := srv.handleError(req.Allowed(r.Method), w); !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header().MergeOverwrite(Header(r.Header))
|
||||||
|
if ok := srv.handleError(req.ReadBody(r.Body), w); !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.ParseForm()
|
||||||
|
if ok := srv.handleError(req.ParseForm(r.Form), w); !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res := pro.Response()
|
||||||
|
for _, stage := range srv.serve {
|
||||||
|
for _, s := range stage {
|
||||||
|
if ok := srv.handleError(s(req, res), w); !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header := res.Header()
|
||||||
|
if header != nil {
|
||||||
|
for key, value := range res.Header() {
|
||||||
|
for _, v := range value {
|
||||||
|
w.Header().Add(key, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.Write(res.Response())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user