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