├── go.mod ├── push.sh ├── network ├── http │ ├── commons │ │ ├── ParamEntity.go │ │ ├── Constants.go │ │ ├── BeeSession.go │ │ └── RequestAndResponse.go │ ├── websocket │ │ ├── MessageProcessing.go │ │ └── UpgradeWebSocket.go │ └── HTTPServer.go └── udp │ └── UDPServer.go ├── .gitignore ├── commons ├── DataType.go └── util │ ├── ArrayUtil.go │ ├── JsonUtil.go │ ├── SerializationUtil.go │ ├── HttpUtil.go │ ├── AesUtil.go │ ├── SnowflakeUtil.go │ ├── ByteUtil.go │ └── StringUtil.go ├── example ├── udp │ └── ExampleUDPMain.go └── web │ ├── ExampleMain.go │ ├── interceptor │ └── ExampleInterceptor.go │ └── routes │ ├── ExampleWebSocketRoutes.go │ ├── ExampleJsonModeRoutes.go │ └── ExampleNoJsonRoutes.go ├── Beerus.go ├── test ├── Aes_test.go └── BeeSession_test.go ├── application ├── web │ ├── params │ │ ├── ParameterUtil.go │ │ ├── ParameterConversion.go │ │ └── ParameterVerification.go │ ├── route │ │ ├── Route.go │ │ └── Interceptor.go │ └── ExecuteRoute.go └── websocket │ ├── wparams │ └── WebSocketSession.go │ ├── wroute │ └── WebSocketRoute.go │ └── ExecuteWebSocketRoute.go ├── LICENSE └── README.md /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Beerus-go/Beerus 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /push.sh: -------------------------------------------------------------------------------- 1 | git push bak master 2 | git push origin master 3 | 4 | -------------------------------------------------------------------------------- /network/http/commons/ParamEntity.go: -------------------------------------------------------------------------------- 1 | package commons 2 | 3 | import "mime/multipart" 4 | 5 | type BeeFile struct { 6 | File multipart.File 7 | FileHeader *multipart.FileHeader 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Output of the go coverage tool, specifically when used with LiteIDE 9 | *.out 10 | 11 | # Dependency directories (remove the comment below to include it) 12 | # vendor/ 13 | -------------------------------------------------------------------------------- /commons/DataType.go: -------------------------------------------------------------------------------- 1 | package data_type 2 | 3 | const ( 4 | Bool = "bool" 5 | Int = "int" 6 | Uint = "uint" 7 | Float = "float" 8 | String = "string" 9 | Slice = "slice" 10 | Struct = "struct" 11 | 12 | // BeeFile Beerus provides a struct to store files, if the request Content-Type is formData, then you can use this type to receive files from the client 13 | BeeFile = "beefile" 14 | 15 | BeeRequest = "beerequest" 16 | BeeResponse = "beeresponse" 17 | ) 18 | -------------------------------------------------------------------------------- /example/udp/ExampleUDPMain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/Beerus-go/Beerus" 5 | "github.com/Beerus-go/Beerus/commons/util" 6 | ) 7 | 8 | func main() { 9 | 10 | // Listening to a UDP service 11 | // The first parameter is the handler 12 | // The second parameter is the data separator 13 | // The third parameter is the port 14 | beerus.ListenUDP(updHandler, []byte("|"), 8080) 15 | 16 | } 17 | 18 | func updHandler(data []byte) { 19 | // data is the data you received 20 | println(util.BytesToString(data)) 21 | } 22 | -------------------------------------------------------------------------------- /commons/util/ArrayUtil.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "errors" 4 | 5 | // CopyOfRange Copy the data of the specified range in the array 6 | func CopyOfRange(src []byte, srcOffset int, size int) ([]byte, error) { 7 | srcLen := len(src) 8 | 9 | if srcOffset > srcLen || size > srcLen { 10 | return nil, errors.New("source buffer Index out of range") 11 | } 12 | 13 | dst := make([]byte, size-srcOffset) 14 | 15 | index := 0 16 | for i := srcOffset; i < size; i++ { 17 | dst[index] = src[i] 18 | index++ 19 | } 20 | return dst, nil 21 | } 22 | -------------------------------------------------------------------------------- /example/web/ExampleMain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/Beerus-go/Beerus" 5 | "github.com/Beerus-go/Beerus/example/web/interceptor" 6 | "github.com/Beerus-go/Beerus/example/web/routes" 7 | ) 8 | 9 | func main() { 10 | 11 | // Interceptors, routes, etc. Loading of data requires its own calls 12 | 13 | //routes.CreateRoute() 14 | routes.CreateJsonRoute() 15 | 16 | routes.CreateWebSocketRoute() 17 | interceptor.CreateInterceptor() 18 | 19 | // Listen the service and listen to port 8080 20 | beerus.ListenHTTP(8080) 21 | } 22 | -------------------------------------------------------------------------------- /example/web/interceptor/ExampleInterceptor.go: -------------------------------------------------------------------------------- 1 | package interceptor 2 | 3 | import ( 4 | "github.com/Beerus-go/Beerus/application/web/route" 5 | "github.com/Beerus-go/Beerus/network/http/commons" 6 | "log" 7 | ) 8 | 9 | func CreateInterceptor() { 10 | route.AddInterceptor("/example/*", loginInterceptorBefore) 11 | } 12 | 13 | func loginInterceptorBefore(req *commons.BeeRequest, res *commons.BeeResponse) bool { 14 | res.SetHeader("hello", "hello word").SetHeader("hello2", "word2") 15 | 16 | log.Println("exec interceptor") 17 | return true 18 | } 19 | -------------------------------------------------------------------------------- /commons/util/JsonUtil.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | ) 7 | 8 | // ToJSONString Structs to json strings 9 | func ToJSONString(stc interface{}) (string, error) { 10 | jsonString, err := json.Marshal(stc) 11 | if err != nil { 12 | log.Println(err) 13 | return "", err 14 | } 15 | 16 | return BytesToString(jsonString), nil 17 | } 18 | 19 | // ParseStruct Json string to struct 20 | func ParseStruct(jsonString string, result interface{}) error { 21 | err := json.Unmarshal(StrToBytes(jsonString), result) 22 | if err != nil { 23 | return err 24 | } 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /Beerus.go: -------------------------------------------------------------------------------- 1 | package beerus 2 | 3 | import ( 4 | "github.com/Beerus-go/Beerus/network/http" 5 | "github.com/Beerus-go/Beerus/network/udp" 6 | "strconv" 7 | ) 8 | 9 | // Port 10 | // Record the port number, 11 | // if there are other places that need to get the port of this service, you can use this variable directly 12 | var Port = 8080 13 | 14 | // ListenHTTP Start an udp service 15 | func ListenHTTP(port int) { 16 | Port = port 17 | http.StartHttpServer(strconv.Itoa(port)) 18 | } 19 | 20 | // ListenUDP Start an udp service 21 | func ListenUDP(handler func(data []byte), separator []byte, port int) { 22 | udp.StartUdpServer(handler, separator, port) 23 | } 24 | -------------------------------------------------------------------------------- /commons/util/SerializationUtil.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "bytes" 5 | "encoding/gob" 6 | ) 7 | 8 | // Serialization Serialize data to []byte 9 | func Serialization(data interface{}) ([]byte, error) { 10 | var result bytes.Buffer 11 | encoder := gob.NewEncoder(&result) 12 | err := encoder.Encode(data) 13 | if err != nil { 14 | return nil, err 15 | } 16 | return result.Bytes(), nil 17 | } 18 | 19 | // DeSerialization Deserialize source to dst 20 | func DeSerialization(source []byte, dst interface{}) error { 21 | decoder := gob.NewDecoder(bytes.NewReader(source)) 22 | err := decoder.Decode(dst) 23 | if err != nil { 24 | return err 25 | } 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /test/Aes_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/Beerus-go/Beerus/commons/util" 5 | "log" 6 | "testing" 7 | ) 8 | 9 | func TestAes(t *testing.T) { 10 | 11 | key := "12345678abcdefgh09876543alnkdjfh" 12 | iv := "hellowordwertyu8" 13 | 14 | data := "helloWord" 15 | 16 | res, err := util.EncryptionToString(data, iv, key) 17 | 18 | if err != nil { 19 | log.Println(err.Error()) 20 | return 21 | } 22 | 23 | res, err = util.DecryptionForString(res, iv, key) 24 | if err != nil { 25 | log.Println("Aes Test FAIL: " + err.Error()) 26 | return 27 | } 28 | if data != res { 29 | log.Println("Aes Test FAIL: Failed to decrypt") 30 | return 31 | } 32 | 33 | log.Println("Aes Test SUCCESS") 34 | } 35 | -------------------------------------------------------------------------------- /application/web/params/ParameterUtil.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | import ( 4 | "github.com/Beerus-go/Beerus/commons" 5 | "reflect" 6 | "strings" 7 | ) 8 | 9 | // GetFieldType 获取字段类型 10 | func GetFieldType(structField reflect.StructField) string { 11 | fieldType := structField.Type.Kind().String() 12 | if fieldType == "" || fieldType == data_type.Struct { 13 | fieldType = structField.Type.Name() 14 | } 15 | 16 | if fieldType == "" { 17 | return "" 18 | } 19 | 20 | fieldType = strings.ToLower(fieldType) 21 | 22 | if strings.HasPrefix(fieldType, data_type.Int) { 23 | return data_type.Int 24 | } 25 | 26 | if strings.HasPrefix(fieldType, data_type.Float) { 27 | return data_type.Float 28 | } 29 | 30 | if strings.HasPrefix(fieldType, data_type.Uint) { 31 | return data_type.Uint 32 | } 33 | 34 | return fieldType 35 | } 36 | -------------------------------------------------------------------------------- /example/web/routes/ExampleWebSocketRoutes.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "github.com/Beerus-go/Beerus/application/websocket/wparams" 5 | "github.com/Beerus-go/Beerus/application/websocket/wroute" 6 | ) 7 | 8 | // CreateWebSocketRoute Creating websocket routes 9 | func CreateWebSocketRoute() { 10 | wroute.AddWebSocketRoute("/ws/test", onConnection, onMessage, onClose) 11 | wroute.AddWebSocketRoute("/ws/test2", onConnection, onMessage, onClose) 12 | } 13 | 14 | // In order to save time, only three functions are used below. In practice, you can configure a set of functions for each wroute 15 | 16 | func onConnection(session *wparams.WebSocketSession, msg string) { 17 | println(msg + "-------------------------------") 18 | session.SendString("connection success") 19 | } 20 | 21 | func onMessage(session *wparams.WebSocketSession, msg string) { 22 | println(msg + "-------------------------------") 23 | session.SendString("I got the message.") 24 | } 25 | 26 | func onClose(session *wparams.WebSocketSession, msg string) { 27 | println(msg + "-------------------------------") 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Yuye 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/BeeSession_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/Beerus-go/Beerus/network/http/commons" 5 | "log" 6 | "testing" 7 | ) 8 | 9 | func TestBeeSession(t *testing.T) { 10 | session := new(commons.BeeSession) 11 | session.Secret = "12345678abcdefgh09876543alnkdjfh" 12 | session.InitializationVector = "12345678qwertyui" 13 | session.Timeout = 3000 14 | 15 | demo := Demo{} 16 | demo.Name = "Beerus" 17 | demo.Age = 18 18 | demo.Height = 180 19 | 20 | token, err := session.CreateToken(demo) 21 | if err != nil { 22 | log.Println("Test BeeSession FAIL: " + err.Error()) 23 | return 24 | } 25 | 26 | log.Println("token: " + token) 27 | 28 | demo2 := Demo{} 29 | err = session.RestoreToken(token, &demo2) 30 | if err != nil { 31 | log.Println("Test BeeSession FAIL: " + err.Error()) 32 | return 33 | } 34 | if demo2.Name != demo.Name || demo2.Age != demo.Age || demo2.Height != demo.Height { 35 | log.Println("Test BeeSession FAIL: Failed to restore token") 36 | return 37 | } 38 | 39 | log.Println("Test BeeSession SUCCESS") 40 | } 41 | 42 | type Demo struct { 43 | Name string 44 | Age int 45 | Height uint64 46 | } 47 | -------------------------------------------------------------------------------- /application/websocket/wparams/WebSocketSession.go: -------------------------------------------------------------------------------- 1 | package wparams 2 | 3 | import ( 4 | "errors" 5 | "github.com/Beerus-go/Beerus/commons/util" 6 | "net" 7 | ) 8 | 9 | // WebSocketSession WebSocket session, mainly used to send messages to the client 10 | type WebSocketSession struct { 11 | Id uint64 12 | Connection net.Conn 13 | } 14 | 15 | // SendString String message 16 | func (ws WebSocketSession) SendString(msg string) { 17 | ws.Send(util.StrToBytes(msg)) 18 | } 19 | 20 | // Send byte[] message 21 | func (ws WebSocketSession) Send(msg []byte) error { 22 | startIndex := 2 23 | var boardCastData []byte 24 | 25 | if len(msg) < 126 { 26 | boardCastData = make([]byte, 2+len(msg)) 27 | boardCastData[0] = 0x81 28 | boardCastData[1] = byte(len(msg)) 29 | } else if len(msg) >= 126 && len(msg) < 65535 { 30 | boardCastData = make([]byte, 4+len(msg)) 31 | bytes := util.IntToBytes(len(msg), 2) 32 | boardCastData[0] = 0x81 33 | boardCastData[1] = 126 34 | boardCastData[2] = bytes[0] 35 | boardCastData[3] = bytes[1] 36 | startIndex = 4 37 | } else { 38 | return errors.New("maximum supported message length is 65534 bytes") 39 | } 40 | 41 | index := 0 42 | for i := startIndex; i < len(boardCastData); i++ { 43 | boardCastData[i] = msg[index] 44 | index++ 45 | } 46 | 47 | ws.Connection.Write(boardCastData) 48 | 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /example/web/routes/ExampleJsonModeRoutes.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "github.com/Beerus-go/Beerus/application/web" 5 | "github.com/Beerus-go/Beerus/application/web/route" 6 | "github.com/Beerus-go/Beerus/network/http/commons" 7 | "io/ioutil" 8 | ) 9 | 10 | func CreateJsonRoute() { 11 | 12 | // Example of file download 13 | route.GET("/downLoad/file", func(req commons.BeeRequest, res commons.BeeResponse) string { 14 | file, err := ioutil.ReadFile("/Users/yeyu/Downloads/goland-2021.2.4.dmg") 15 | if err == nil { 16 | 17 | } 18 | //req.GetFile() 19 | res.SendStream("goland.dmg", file) 20 | 21 | return web.Download 22 | }) 23 | 24 | // Example of parameter conversion to struct and parameter checksum 25 | route.POST("/example/post", func(param DemoParam, req commons.BeeRequest, res commons.BeeResponse) map[string]string { 26 | 27 | println(param.TestStringReception) 28 | println(param.TestIntReception) 29 | println(param.TestInt64Reception) 30 | println(param.TestFloatReception) 31 | println(param.TestUintReception) 32 | println(param.TestUint64Reception) 33 | println(param.TestBoolReception) 34 | 35 | //print(param.TestBeeFileReception.FileHeader.Filename) 36 | //print(": ") 37 | //println(param.TestBeeFileReception.FileHeader.Size) 38 | 39 | msg := make(map[string]string) 40 | msg["msg"] = "success" 41 | return msg 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /commons/util/HttpUtil.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io" 7 | "net/http" 8 | "net/url" 9 | "strings" 10 | ) 11 | 12 | var client = new(http.Client) 13 | 14 | // RequestBody 15 | // Initiating a request with body parameters 16 | func RequestBody(reqUrl string, method string, header map[string]string, reqBody io.Reader) (*http.Response, error) { 17 | if strings.ToUpper(method) == "GET" { 18 | return nil, errors.New("") 19 | } 20 | 21 | req, err := http.NewRequest(method, reqUrl, reqBody) 22 | if err != nil { 23 | return nil, err 24 | } 25 | if header != nil { 26 | for key, val := range header { 27 | req.Header.Set(key, val) 28 | } 29 | } 30 | 31 | resp, err := client.Do(req) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | return resp, nil 37 | } 38 | 39 | // Get 40 | // Sending get requests 41 | func Get(reqUrl string, param interface{}) (*http.Response, error) { 42 | 43 | params := url.Values{} 44 | URL, err := url.Parse(reqUrl) 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | if param != nil { 50 | jsonByte, err := json.Marshal(param) 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | data := make(map[string]interface{}) 56 | 57 | err = json.Unmarshal(jsonByte, data) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | if len(data) > 0 { 63 | for key, val := range data { 64 | params.Set(key, ToString(val)) 65 | } 66 | } 67 | } 68 | 69 | URL.RawQuery = params.Encode() 70 | resp, err := http.Get(URL.String()) 71 | if err != nil { 72 | return nil, err 73 | } 74 | return resp, nil 75 | } 76 | -------------------------------------------------------------------------------- /network/http/commons/Constants.go: -------------------------------------------------------------------------------- 1 | package commons 2 | 3 | import "strings" 4 | 5 | const ( 6 | UrlEncode = "application/x-www-form-urlencoded" 7 | FormData = "multipart/form-data" 8 | JSON = "application/json" 9 | ContentType = "Content-Type" 10 | ContentType2 = "content-type" 11 | ContentDisposition = "Content-Disposition" 12 | ErrorMsg = "{\"code\":%s, \"msg\":\"%s\"}" 13 | CarriageReturn = "\r\n" 14 | ) 15 | 16 | const ( 17 | Connection = "Connection" 18 | Upgrade = "Upgrade" 19 | SecWebsocketKey = "Sec-WebSocket-Key" 20 | SecWebsocketProtocol = "Sec-WebSocket-Protocol" 21 | ResponseOnline = "HTTP/1.1 101 Switching Protocols" 22 | 23 | // SocketSecretKey SecretKey encryption secret key 24 | SocketSecretKey = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 25 | ) 26 | 27 | // ----------- Determine whether the Content-Type matches the requirements ----------- 28 | 29 | func IsJSON(contentType string) bool { 30 | contentType = strings.ToLower(contentType) 31 | 32 | if JSON == contentType || strings.HasPrefix(contentType, JSON) { 33 | return true 34 | } 35 | return false 36 | } 37 | 38 | func IsFormData(contentType string) bool { 39 | contentType = strings.ToLower(contentType) 40 | 41 | if FormData == contentType || strings.HasPrefix(contentType, FormData) { 42 | return true 43 | } 44 | return false 45 | } 46 | 47 | func IsUrlEncode(contentType string) bool { 48 | contentType = strings.ToLower(contentType) 49 | 50 | if UrlEncode == contentType || strings.HasPrefix(contentType, UrlEncode) { 51 | return true 52 | } 53 | return false 54 | } 55 | -------------------------------------------------------------------------------- /application/websocket/wroute/WebSocketRoute.go: -------------------------------------------------------------------------------- 1 | package wroute 2 | 3 | import "github.com/Beerus-go/Beerus/application/websocket/wparams" 4 | 5 | const ( 6 | OnConnection = "onConnection" 7 | OnMessage = "onMessage" 8 | OnClose = "onClose" 9 | ) 10 | 11 | // Save map of WebSocket routes 12 | var webSocketRouteMap = make(map[string]map[string]func(session *wparams.WebSocketSession, msg string)) 13 | 14 | // AddWebSocketRoute Add a wroute, the first function is triggered when the connection is successful, the second function is triggered when a message is received, and the third function is triggered when the link is broken 15 | func AddWebSocketRoute(routePath string, onConnection func(session *wparams.WebSocketSession, msg string), onMessage func(session *wparams.WebSocketSession, msg string), onClose func(session *wparams.WebSocketSession, msg string)) { 16 | funcMap := make(map[string]func(session *wparams.WebSocketSession, msg string)) 17 | funcMap[OnConnection] = onConnection 18 | funcMap[OnMessage] = onMessage 19 | funcMap[OnClose] = onClose 20 | 21 | webSocketRouteMap[routePath] = funcMap 22 | } 23 | 24 | // GetWebSocketRoute Get the required function based on the wroute 25 | func GetWebSocketRoute(routePath string, funcName string) func(session *wparams.WebSocketSession, msg string) { 26 | funcMap := webSocketRouteMap[routePath] 27 | 28 | if funcMap == nil || len(funcMap) <= 0 { 29 | return nil 30 | } 31 | return funcMap[funcName] 32 | } 33 | 34 | // WebSocketRouteExist Does routing exist 35 | func WebSocketRouteExist(routePath string) bool { 36 | funcMap := webSocketRouteMap[routePath] 37 | 38 | if funcMap == nil || len(funcMap) <= 0 { 39 | return false 40 | } 41 | 42 | return true 43 | } 44 | -------------------------------------------------------------------------------- /application/websocket/ExecuteWebSocketRoute.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | import ( 4 | "github.com/Beerus-go/Beerus/application/websocket/wparams" 5 | "github.com/Beerus-go/Beerus/application/websocket/wroute" 6 | "github.com/Beerus-go/Beerus/commons/util" 7 | "log" 8 | "net" 9 | ) 10 | 11 | // SessionMap Save each linked session 12 | var sessionMap = make(map[string]*wparams.WebSocketSession) 13 | 14 | var snowflake *util.SnowFlake 15 | 16 | // ExecuteConnection Triggers the onConnection function inside the wroute 17 | func ExecuteConnection(routePath string, conn net.Conn) { 18 | 19 | var snowflakeId uint64 20 | var err error 21 | if snowflake == nil { 22 | snowflake, err = util.New(2) 23 | if err != nil { 24 | log.Println(err.Error()) 25 | return 26 | } 27 | } 28 | 29 | snowflakeId, err = snowflake.Generate() 30 | if err != nil { 31 | log.Println(err.Error()) 32 | return 33 | } 34 | 35 | session := new(wparams.WebSocketSession) 36 | session.Connection = conn 37 | session.Id = snowflakeId 38 | sessionMap[routePath] = session 39 | 40 | wroute.GetWebSocketRoute(routePath, wroute.OnConnection)(session, "The client is already connected") 41 | } 42 | 43 | // ExecuteMessage Triggers the onMessage function inside the wroute 44 | func ExecuteMessage(routePath string, message string) { 45 | wroute.GetWebSocketRoute(routePath, wroute.OnMessage)(sessionMap[routePath], message) 46 | } 47 | 48 | // ExecuteClose Triggers the onClose function inside the wroute 49 | func ExecuteClose(routePath string) { 50 | if sessionMap[routePath] == nil { 51 | return 52 | } 53 | 54 | delete(sessionMap, routePath) 55 | wroute.GetWebSocketRoute(routePath, wroute.OnClose)(sessionMap[routePath], "The client is disconnected") 56 | } 57 | -------------------------------------------------------------------------------- /commons/util/AesUtil.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "encoding/base64" 7 | "errors" 8 | ) 9 | 10 | // Encryption Encrypt data to []byte 11 | func Encryption(data []byte, iv []byte, key []byte) ([]byte, error) { 12 | if len(key) != 32 { 13 | return nil, errors.New("length of key must = 32") 14 | } 15 | 16 | if len(iv) != 16 { 17 | return nil, errors.New("length of initialization vector must = 16") 18 | } 19 | 20 | block, err := aes.NewCipher(key) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | if len(iv) != block.BlockSize() { 26 | return nil, errors.New("length of the initialization vector must = length of the key cipher") 27 | } 28 | 29 | streams := cipher.NewCTR(block, iv) 30 | 31 | dataBytes := make([]byte, len(data)) 32 | streams.XORKeyStream(dataBytes, data) 33 | 34 | return dataBytes, nil 35 | } 36 | 37 | // Decryption Decrypt source to dst 38 | func Decryption(source []byte, iv []byte, key []byte) ([]byte, error) { 39 | return Encryption(source, iv, key) 40 | } 41 | 42 | // EncryptionToString Encrypt data to string 43 | func EncryptionToString(data string, iv string, key string) (string, error) { 44 | by, err := Encryption(StrToBytes(data), StrToBytes(iv), StrToBytes(key)) 45 | if err != nil { 46 | return "", err 47 | } 48 | return base64.StdEncoding.EncodeToString(by), nil 49 | } 50 | 51 | // DecryptionForString Decrypt source to dst 52 | func DecryptionForString(source string, iv string, key string) (string, error) { 53 | str, err := base64.StdEncoding.DecodeString(source) 54 | if err != nil { 55 | return "", err 56 | } 57 | 58 | str, err = Decryption(str, StrToBytes(iv), StrToBytes(key)) 59 | if err != nil { 60 | return "", err 61 | } 62 | return BytesToString(str), nil 63 | } 64 | -------------------------------------------------------------------------------- /commons/util/SnowflakeUtil.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | const ( 10 | epoch int64 = 1526285084373 11 | numWorkerBits = 10 12 | numSequenceBits = 12 13 | MaxWorkId = -1 ^ (-1 << numWorkerBits) 14 | MaxSequence = -1 ^ (-1 << numSequenceBits) 15 | ) 16 | 17 | type SnowFlake struct { 18 | lastTimestamp uint64 19 | sequence uint32 20 | workerId uint32 21 | lock sync.Mutex 22 | } 23 | 24 | func (sf *SnowFlake) pack() uint64 { 25 | uuid := (sf.lastTimestamp << (numWorkerBits + numSequenceBits)) | (uint64(sf.workerId) << numSequenceBits) | (uint64(sf.sequence)) 26 | return uuid 27 | } 28 | 29 | // New returns a new snowflake node that can be used to generate snowflake 30 | func New(workerId uint32) (*SnowFlake, error) { 31 | if workerId < 0 || workerId > MaxWorkId { 32 | return nil, errors.New("invalid worker Id") 33 | } 34 | return &SnowFlake{workerId: workerId}, nil 35 | } 36 | 37 | // Generate Next creates and returns a unique snowflake ID 38 | func (sf *SnowFlake) Generate() (uint64, error) { 39 | sf.lock.Lock() 40 | defer sf.lock.Unlock() 41 | 42 | ts := timestamp() 43 | if ts == sf.lastTimestamp { 44 | sf.sequence = (sf.sequence + 1) & MaxSequence 45 | if sf.sequence == 0 { 46 | ts = sf.waitNextMilli(ts) 47 | } 48 | } else { 49 | sf.sequence = 0 50 | } 51 | 52 | if ts < sf.lastTimestamp { 53 | return 0, errors.New("invalid system clock") 54 | } 55 | 56 | sf.lastTimestamp = ts 57 | return sf.pack(), nil 58 | } 59 | 60 | // waitNextMilli if that microsecond is full 61 | // wait for the next microsecond 62 | func (sf *SnowFlake) waitNextMilli(ts uint64) uint64 { 63 | for ts == sf.lastTimestamp { 64 | time.Sleep(100 * time.Microsecond) 65 | ts = timestamp() 66 | } 67 | return ts 68 | } 69 | 70 | // timestamp 71 | func timestamp() uint64 { 72 | return uint64(time.Now().UnixNano()/int64(1000000) - epoch) 73 | } 74 | -------------------------------------------------------------------------------- /application/web/route/Route.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | // JsonMode true means on, false means off, if on, then the entire framework will enter json mode 4 | var JsonMode = true 5 | 6 | // Store the map of the wroute 7 | var routeMap = make(map[string]interface{}) 8 | 9 | // GetRouteMap Get all routes 10 | func GetRouteMap() map[string]interface{} { 11 | return routeMap 12 | } 13 | 14 | // GetRoute Get routes 15 | func GetRoute(path string) interface{} { 16 | return routeMap[path] 17 | } 18 | 19 | // GET Add a route for GET request method 20 | func GET(path string, function interface{}) { 21 | AddRoute(path, "GET", function) 22 | } 23 | 24 | // POST Add a route for POST request method 25 | func POST(path string, function interface{}) { 26 | AddRoute(path, "POST", function) 27 | } 28 | 29 | // PUT Add a route for PUT request method 30 | func PUT(path string, function interface{}) { 31 | AddRoute(path, "PUT", function) 32 | } 33 | 34 | // DELETE Add a route for DELETE request method 35 | func DELETE(path string, function interface{}) { 36 | AddRoute(path, "DELETE", function) 37 | } 38 | 39 | // PATCH Add a route for PATCH request method 40 | func PATCH(path string, function interface{}) { 41 | AddRoute(path, "PATCH", function) 42 | } 43 | 44 | // HEAD Add a route for HEAD request method 45 | func HEAD(path string, function interface{}) { 46 | AddRoute(path, "HEAD", function) 47 | } 48 | 49 | // OPTIONS Add a route for OPTIONS request method 50 | func OPTIONS(path string, function interface{}) { 51 | AddRoute(path, "OPTIONS", function) 52 | } 53 | 54 | // Any add a router for all request method 55 | func Any(path string, function interface{}) { 56 | GET(path, function) 57 | POST(path, function) 58 | PUT(path, function) 59 | DELETE(path, function) 60 | PATCH(path, function) 61 | HEAD(path, function) 62 | OPTIONS(path, function) 63 | } 64 | 65 | // AddRoute Add a route, and if you need to use another request method, you can use this 66 | func AddRoute(path string, method string, function interface{}) { 67 | routeMap[path+"/"+method] = function 68 | } 69 | -------------------------------------------------------------------------------- /application/web/route/Interceptor.go: -------------------------------------------------------------------------------- 1 | package route 2 | 3 | import ( 4 | "github.com/Beerus-go/Beerus/commons/util" 5 | "github.com/Beerus-go/Beerus/network/http/commons" 6 | "strings" 7 | ) 8 | 9 | // interceptorMap 10 | //Store the map of the interceptor 11 | var interceptorMap = make(map[string]func(req *commons.BeeRequest, res *commons.BeeResponse) bool) 12 | 13 | // afterReloadingInterceptorMap 14 | //When the service is started, the interceptor pattern and wroute are matched and then stored here to improve the efficiency of getting the interceptor based on the wroute. 15 | var afterReloadingInterceptorMap = make(map[string][]func(req *commons.BeeRequest, res *commons.BeeResponse) bool) 16 | 17 | // AddInterceptor 18 | // Add an interceptor 19 | func AddInterceptor(pattern string, before func(req *commons.BeeRequest, res *commons.BeeResponse) bool) { 20 | interceptorMap[pattern] = before 21 | } 22 | 23 | // GetInterceptor 24 | // Get interceptors based on routes 25 | func GetInterceptor(path string) []func(req *commons.BeeRequest, res *commons.BeeResponse) bool { 26 | return afterReloadingInterceptorMap[path] 27 | } 28 | 29 | // ReloadMatchToUrl 30 | // When the service is started, the interceptor and the wroute are matched and then stored according to the wroute, so that it is easy to get the interceptor according to the wroute 31 | func ReloadMatchToUrl() { 32 | if len(interceptorMap) <= 0 || len(afterReloadingInterceptorMap) > 0 { 33 | return 34 | } 35 | 36 | for key, value := range interceptorMap { 37 | for routePath, _ := range routeMap { 38 | last := strings.LastIndex(routePath, "/") 39 | routePath = routePath[:last] 40 | 41 | if util.Match(routePath, key) == false { 42 | continue 43 | } 44 | 45 | var interceptorArray = afterReloadingInterceptorMap[routePath] 46 | if interceptorArray == nil || len(interceptorArray) <= 0 { 47 | interceptorArray = make([]func(req *commons.BeeRequest, res *commons.BeeResponse) bool, 0) 48 | } 49 | 50 | interceptorArray = append(interceptorArray, value) 51 | afterReloadingInterceptorMap[routePath] = interceptorArray 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /commons/util/ByteUtil.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "errors" 4 | 5 | // SubBytes Extract the byte[] between the specified coordinates 6 | func SubBytes(source []byte, startIndex int, endIndex int) ([]byte, error) { 7 | if startIndex > endIndex { 8 | return nil, errors.New("the start coordinate cannot be greater than the end coordinate") 9 | } 10 | 11 | if (len(source) - 1) < startIndex { 12 | return nil, errors.New("start coordinates are out of length") 13 | } 14 | 15 | if len(source) < (endIndex - startIndex) { 16 | return nil, errors.New("the length to be Extract is already greater than the data being Extract") 17 | } 18 | 19 | length := endIndex - startIndex 20 | bytes := make([]byte, length) 21 | 22 | i := startIndex 23 | for index := 0; index < length; index++ { 24 | if i > (len(source) - 1) { 25 | break 26 | } 27 | 28 | bytes[index] = source[i] 29 | i++ 30 | } 31 | 32 | return bytes, nil 33 | } 34 | 35 | // ByteIndexOf Find the coordinates of the corresponding data from byte[] 36 | func ByteIndexOf(source []byte, targetByte []byte) int { 37 | 38 | startIndex := 0 39 | endIndex := len(targetByte) 40 | 41 | for { 42 | index := 0 43 | exist := true 44 | 45 | if (len(source) - startIndex) < len(targetByte) { 46 | return -1 47 | } 48 | 49 | for j := startIndex; j < endIndex; j++ { 50 | if index > len(targetByte)-1 { 51 | return -1 52 | } 53 | 54 | if source[j] != targetByte[index] { 55 | startIndex++ 56 | endIndex++ 57 | 58 | if startIndex > (len(source)-1) || endIndex > len(source) { 59 | return -1 60 | } 61 | 62 | exist = false 63 | break 64 | } 65 | index++ 66 | } 67 | 68 | if exist { 69 | return startIndex 70 | } 71 | } 72 | return -1 73 | } 74 | 75 | // BytesToInt byte[] to int 76 | func BytesToInt(b []byte, start int, length int) int { 77 | sum := 0 78 | end := start + length 79 | 80 | for i := start; i < end; i++ { 81 | n := int(b[i]) & 0xff 82 | length-- 83 | n <<= length * 8 84 | sum += n 85 | } 86 | return sum 87 | } 88 | 89 | // IntToBytes int to byte[] 90 | func IntToBytes(n int, length int) []byte { 91 | b := make([]byte, length) 92 | 93 | for i := length; i > 0; i-- { 94 | b[(i - 1)] = (byte)(n >> 8 * (length - i) & 0xFF) 95 | } 96 | return b 97 | } 98 | -------------------------------------------------------------------------------- /network/udp/UDPServer.go: -------------------------------------------------------------------------------- 1 | package udp 2 | 3 | import ( 4 | "bytes" 5 | "github.com/Beerus-go/Beerus/commons/util" 6 | "log" 7 | "net" 8 | ) 9 | 10 | // StartUdpServer Start an udp service 11 | func StartUdpServer(handler func(data []byte), separator []byte, port int) { 12 | if separator == nil || len(separator) <= 0 { 13 | log.Println("The separator must not be empty") 14 | return 15 | } 16 | 17 | udpConn, err := net.ListenUDP("udp", &net.UDPAddr{ 18 | IP: net.IPv4(0, 0, 0, 0), 19 | Port: port, 20 | }) 21 | 22 | if err != nil { 23 | log.Println("Listen failed,", err) 24 | return 25 | } 26 | 27 | udpConnection(udpConn, separator, handler) 28 | } 29 | 30 | // udpConnection Handling UDP connections 31 | func udpConnection(conn *net.UDPConn, separator []byte, handler func(data []byte)) { 32 | defer conn.Close() 33 | 34 | buf := new(bytes.Buffer) 35 | readSizeCache := 0 36 | for { 37 | var data = make([]byte, 1024) 38 | ln, addr, err := conn.ReadFromUDP(data) 39 | 40 | if err != nil { 41 | log.Printf("Read from udp server:%s failed,err:%s", addr, err) 42 | break 43 | } 44 | 45 | if ln <= 0 { 46 | continue 47 | } 48 | 49 | readSizeCache += ln 50 | buf.Write(data) 51 | 52 | // It may be possible to read more than one message at a time, so a loop needs to be made here 53 | for { 54 | separatorIndex := util.ByteIndexOf(buf.Bytes(), separator) 55 | 56 | if separatorIndex <= 0 { 57 | break 58 | } 59 | 60 | message, errMsg := util.SubBytes(buf.Bytes(), 0, separatorIndex) 61 | 62 | if errMsg != nil { 63 | log.Printf("Read from udp server:%s failed,err:%s", addr, errMsg) 64 | break 65 | } 66 | 67 | handler(message) 68 | 69 | processedLength := len(message) + len(separator) 70 | 71 | if processedLength == readSizeCache { 72 | buf.Reset() 73 | readSizeCache = 0 74 | break 75 | } else { 76 | remaining, errorMsg := util.CopyOfRange(buf.Bytes(), processedLength, readSizeCache) 77 | if errorMsg != nil { 78 | log.Printf("Read from udp server:%s failed,err:%s", addr, errMsg) 79 | return 80 | } 81 | buf = bytes.NewBuffer(remaining) 82 | readSizeCache = readSizeCache - processedLength 83 | if readSizeCache < 0 { 84 | readSizeCache = 0 85 | } 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /commons/util/StringUtil.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "encoding/json" 5 | "regexp" 6 | "strconv" 7 | "strings" 8 | "unsafe" 9 | ) 10 | 11 | // StrToBytes string to byte[] 12 | func StrToBytes(val string) []byte { 13 | if val == "" { 14 | return nil 15 | } 16 | x := (*[2]uintptr)(unsafe.Pointer(&val)) 17 | h := [3]uintptr{x[0], x[1], x[1]} 18 | return *(*[]byte)(unsafe.Pointer(&h)) 19 | } 20 | 21 | // BytesToString byte[] to string 22 | func BytesToString(val []byte) string { 23 | if val == nil || len(val) < 1 { 24 | return "" 25 | } 26 | return *(*string)(unsafe.Pointer(&val)) 27 | } 28 | 29 | // Match Determines if the first parameter matches the second parameter with a wildcard 30 | func Match(source string, reg string) bool { 31 | 32 | var index = strings.Index(reg, "*") 33 | if index < 0 { 34 | return source == reg 35 | } 36 | 37 | if reg == "*" { 38 | return true 39 | } 40 | 41 | reg = strings.ReplaceAll(reg, "*", "([a-zA-Z1-9/]+)") 42 | regular := regexp.MustCompile("^" + reg + "$") 43 | 44 | return regular.MatchString(source) 45 | } 46 | 47 | // ToString 48 | // Unbox interface{} into string 49 | func ToString(value interface{}) string { 50 | if value == nil { 51 | return "" 52 | } 53 | 54 | switch value.(type) { 55 | case float64: 56 | ft := value.(float64) 57 | return strconv.FormatFloat(ft, 'f', -1, 64) 58 | case float32: 59 | ft := value.(float32) 60 | return strconv.FormatFloat(float64(ft), 'f', -1, 64) 61 | case int: 62 | it := value.(int) 63 | return strconv.Itoa(it) 64 | case uint: 65 | it := value.(uint) 66 | return strconv.Itoa(int(it)) 67 | case int8: 68 | it := value.(int8) 69 | return strconv.Itoa(int(it)) 70 | case uint8: 71 | it := value.(uint8) 72 | return strconv.Itoa(int(it)) 73 | case int16: 74 | it := value.(int16) 75 | return strconv.Itoa(int(it)) 76 | case uint16: 77 | it := value.(uint16) 78 | return strconv.Itoa(int(it)) 79 | case int32: 80 | it := value.(int32) 81 | return strconv.Itoa(int(it)) 82 | case uint32: 83 | it := value.(uint32) 84 | return strconv.Itoa(int(it)) 85 | case int64: 86 | it := value.(int64) 87 | return strconv.FormatInt(it, 10) 88 | case uint64: 89 | it := value.(uint64) 90 | return strconv.FormatUint(it, 10) 91 | case string: 92 | return value.(string) 93 | case []byte: 94 | return BytesToString(value.([]byte)) 95 | default: 96 | newValue, _ := json.Marshal(value) 97 | return BytesToString(newValue) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /network/http/websocket/MessageProcessing.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "github.com/Beerus-go/Beerus/application/websocket" 7 | "github.com/Beerus-go/Beerus/commons/util" 8 | ) 9 | 10 | const ( 11 | CLOSE = "connection close" 12 | READING = "reading" 13 | BLANK = "blank" 14 | ) 15 | 16 | // Processing Parsing Package Text and Calling Business Processes 17 | func Processing(buffer *bytes.Buffer, readSizeCache int, routePath string) (int, error) { 18 | 19 | message, isClose, size := readMessage(buffer, readSizeCache) 20 | 21 | // When the connection is properly disconnected, the wroute is called for service processing 22 | if isClose != nil { 23 | websocket.ExecuteClose(routePath) 24 | return 0, errors.New(CLOSE) 25 | } 26 | 27 | // When a complete message is parsed, the wroute is invoked for service processing 28 | if message != BLANK && message != READING { 29 | websocket.ExecuteMessage(routePath, message) 30 | return size, nil 31 | } 32 | 33 | return 0, nil 34 | } 35 | 36 | // readMessage Parsing messages 37 | func readMessage(buffer *bytes.Buffer, readSizeCache int) (string, error, int) { 38 | 39 | bytesData := buffer.Bytes() 40 | 41 | opcode := bytesData[0] & 15 42 | if opcode == 8 { 43 | return BLANK, errors.New(CLOSE), 0 44 | } 45 | if readSizeCache < 2 { 46 | return READING, nil, 0 47 | } 48 | 49 | payloadLength := int(bytesData[1] & 0x7f) 50 | if payloadLength < 1 { 51 | return READING, nil, 0 52 | } 53 | mask := bytesData[1] >> 7 54 | 55 | maskStartIndex := 2 56 | 57 | if payloadLength == 126 { 58 | length := getLength(bytesData, 2, 2) 59 | payloadLength = util.BytesToInt(length, 0, len(length)) 60 | maskStartIndex = 4 61 | } else if payloadLength == 127 { 62 | length := getLength(bytesData, 2, 8) 63 | payloadLength = util.BytesToInt(length, 0, len(length)) 64 | maskStartIndex = 10 65 | } 66 | 67 | maskEndIndex := maskStartIndex + 4 68 | 69 | if readSizeCache < (payloadLength + maskEndIndex) { 70 | return READING, nil, 0 71 | } 72 | 73 | maskByte, errMsg := util.CopyOfRange(bytesData, maskStartIndex, maskEndIndex) 74 | payloadData, errMsg2 := util.CopyOfRange(bytesData, maskEndIndex, payloadLength+maskEndIndex) 75 | 76 | if errMsg != nil || errMsg2 != nil { 77 | return BLANK, errors.New(CLOSE), 0 78 | } 79 | 80 | if len(payloadData) < payloadLength { 81 | return READING, nil, 0 82 | } 83 | 84 | if mask == 1 { 85 | for i := 0; i < len(payloadData); i++ { 86 | payloadData[i] = payloadData[i] ^ maskByte[i%4] 87 | } 88 | } 89 | 90 | return util.BytesToString(payloadData), nil, maskEndIndex + len(payloadData) 91 | } 92 | 93 | // getLength Parsing out the length of the message body from the message 94 | func getLength(bytesData []byte, start int, size int) []byte { 95 | index := 0 96 | length := make([]byte, size) 97 | 98 | for i := start; i < (start + size); i++ { 99 | length[index] = bytesData[i] 100 | index++ 101 | } 102 | return length 103 | } 104 | -------------------------------------------------------------------------------- /example/web/routes/ExampleNoJsonRoutes.go: -------------------------------------------------------------------------------- 1 | package routes 2 | 3 | import ( 4 | "github.com/Beerus-go/Beerus/application/web/params" 5 | "github.com/Beerus-go/Beerus/application/web/route" 6 | "github.com/Beerus-go/Beerus/network/http/commons" 7 | ) 8 | 9 | func CreateRoute() { 10 | 11 | route.JsonMode = false 12 | 13 | // Before request this route, the json mode needs to be turned off 14 | // Example of parameter conversion to struct + checksum in one step 15 | route.PUT("/example/put", func(req commons.BeeRequest, res commons.BeeResponse) { 16 | 17 | param := DemoParam{} 18 | 19 | // Extraction parameters, Generally used in scenarios where verification is not required or you want to verify manually 20 | params.ToStruct(req, ¶m) 21 | 22 | // Separate validation of data in struct, this feature can be used independently in any case and is not limited to the routing layer. 23 | // json mode does not require manual validation, this code can be omitted, here is used to demonstrate the non-json mode, how to validate the parameters 24 | var result = params.Validation(req, ¶m) 25 | if result != params.SUCCESS { 26 | 27 | // Non-json mode also can not be returned in this way, you need to call the Send function in the res object to return the result to the front end 28 | res.SendErrorMsg(500, result) 29 | return 30 | } 31 | 32 | // Extraction of parameters + validation 33 | // json mode does not require manual validation, this code can be omitted, here is used to demonstrate the non-json mode, how to validate the parameters 34 | result = params.ToStructAndValidation(req, ¶m) 35 | if result != params.SUCCESS { 36 | 37 | // Non-json mode also can not be returned in this way, you need to call the Send function in the res object to return the result to the front end 38 | res.SendErrorMsg(500, result) 39 | return 40 | } 41 | 42 | println(param.TestStringReception) 43 | println(param.TestIntReception) 44 | println(param.TestInt64Reception) 45 | println(param.TestFloatReception) 46 | println(param.TestUintReception) 47 | println(param.TestUint64Reception) 48 | println(param.TestBoolReception) 49 | 50 | res.SendJson(`{"msg":"hello word"}`) 51 | }) 52 | } 53 | 54 | // DemoParam If you have a struct like this, and you want to put all the parameters from the request into this struct 55 | type DemoParam struct { 56 | // You can customize any field 57 | // the name of the field must be exactly the same as the name of the requested parameter, and is case-sensitive 58 | TestStringReception string `notnull:"true" msg:"TestStringReception Cannot be empty" routes:"/example/put"` 59 | TestIntReception int `max:"123" min:"32" msg:"TestIntReception The value range must be between 32 - 123" routes:"/example/post"` 60 | TestInt64Reception int64 `max:"123" min:"32" msg:"TestInt64Reception The value range must be between 32 - 123"` 61 | TestUintReception uint `max:"123" min:"32" msg:"TestUintReception The value range must be between 32 - 123"` 62 | TestUint32Reception uint32 `max:"123" min:"32" msg:"TestUint32Reception The value range must be between 32 - 123"` 63 | TestUint64Reception uint64 `max:"123" min:"32" msg:"TestUint64Reception The value range must be between 32 - 123"` 64 | TestFloatReception float32 `max:"123" min:"32" msg:"TestFloatReception The value range must be between 32 - 123"` 65 | TestStringRegReception string `reg:"^[a-z]+$" msg:"TestStringRegReception Does not meet the regular"` 66 | TestBoolReception bool 67 | TestBeeFileReception commons.BeeFile 68 | 69 | TestArrayReception []string `notnull:"true" msg:"TestArrayReception Cannot be empty"` 70 | } 71 | -------------------------------------------------------------------------------- /network/http/commons/BeeSession.go: -------------------------------------------------------------------------------- 1 | package commons 2 | 3 | import ( 4 | "errors" 5 | "github.com/Beerus-go/Beerus/commons/util" 6 | "strconv" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | // BeeSession Session Management, Based on the aes algorithm 12 | // The basic principle of creating a token is to convert the data into a json string, then splice a timeout to the end, then perform aes encryption, and then convert the encrypted cipher text to base64 output 13 | // To restore the token, reverse the creation process, first convert the base64 string to cipher text, then decrypt the cipher text with aes, after decryption, get a string with a timeout at the end, split the string into json and timeout, and determine whether the timeout is over, if not, convert the json string to the specified type of data. 14 | type BeeSession struct { 15 | Timeout int64 16 | Secret string 17 | InitializationVector string 18 | } 19 | 20 | // CreateToken Create a token based on the parameters passed in 21 | // Parameters must be of type struct 22 | func (bs BeeSession) CreateToken(data interface{}) (string, error) { 23 | err := bs.validVariables() 24 | if err != nil { 25 | return "", err 26 | } 27 | 28 | if bs.Timeout <= 0 { 29 | bs.Timeout = 86400000 30 | } 31 | 32 | // Converting data to json strings 33 | jsonStr, err := util.ToJSONString(data) 34 | if err != nil { 35 | return "", err 36 | } 37 | 38 | // Splice the timeout at the end of the json string 39 | timeOut := bs.Timeout + time.Now().UnixMilli() 40 | jsonStr = jsonStr + CarriageReturn + strconv.FormatInt(timeOut, 10) 41 | 42 | // AES encryption and conversion to base64 return 43 | dat, err := util.EncryptionToString(jsonStr, bs.InitializationVector, bs.Secret) 44 | if err != nil { 45 | return "", err 46 | } 47 | return dat, nil 48 | } 49 | 50 | // RestoreToken Restore the token to the original data 51 | // The second parameter must be a pointer of type struct 52 | func (bs BeeSession) RestoreToken(token string, dst interface{}) error { 53 | if token == "" { 54 | return errors.New("token is incorrect") 55 | } 56 | 57 | err := bs.validVariables() 58 | if err != nil { 59 | return err 60 | } 61 | 62 | // Restore the base64 and decrypt it to the original data by AES (json spliced with timeout) 63 | dstStr, err := util.DecryptionForString(token, bs.InitializationVector, bs.Secret) 64 | if err != nil { 65 | return err 66 | } 67 | 68 | // Splitting data into json and timeout 69 | index := strings.LastIndex(dstStr, CarriageReturn) 70 | if index < 0 { 71 | return errors.New("token is incorrect") 72 | } 73 | 74 | jsonStr := dstStr[:index] 75 | timeOutStr := dstStr[(index + len(CarriageReturn)):] 76 | 77 | timeOut, errMsg := strconv.ParseInt(timeOutStr, 10, 64) 78 | if errMsg != nil { 79 | return errors.New("token is incorrect" + errMsg.Error()) 80 | } 81 | 82 | // If the timeout expires, the user is prompted 83 | if time.Now().UnixMilli() > timeOut { 84 | return errors.New("token is no longer valid") 85 | } 86 | 87 | // If it doesn't time out, the json string is converted to the specified struct 88 | err = util.ParseStruct(jsonStr, dst) 89 | if err != nil { 90 | return err 91 | } 92 | 93 | return nil 94 | } 95 | 96 | // validVariables Verify that the secret key and initialization vector are empty 97 | func (bs BeeSession) validVariables() error { 98 | if bs.Secret == "" { 99 | return errors.New("you need to set a secret key first before you can use BeeSession") 100 | } 101 | 102 | if bs.InitializationVector == "" { 103 | return errors.New("you need to set a initialization vector first before you can use BeeSession") 104 | } 105 | 106 | return nil 107 | } 108 | -------------------------------------------------------------------------------- /application/web/params/ParameterConversion.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/Beerus-go/Beerus/commons" 6 | "github.com/Beerus-go/Beerus/commons/util" 7 | "github.com/Beerus-go/Beerus/network/http/commons" 8 | "log" 9 | "reflect" 10 | "strconv" 11 | ) 12 | 13 | const ( 14 | Field = "field" 15 | ) 16 | 17 | // ToStruct Take out the parameters and wrap them in struct 18 | func ToStruct(request commons.BeeRequest, pointParamStruct interface{}) { 19 | 20 | contentType := request.ContentType() 21 | 22 | if commons.IsJSON(contentType) { 23 | if request.Json != "" { 24 | json.Unmarshal(util.StrToBytes(request.Json), pointParamStruct) 25 | } 26 | return 27 | } 28 | 29 | var paramElem = reflect.ValueOf(pointParamStruct).Elem() 30 | var paramType = paramElem.Type() 31 | 32 | fieldNum := paramType.NumField() 33 | for i := 0; i < fieldNum; i++ { 34 | SetValue(paramType, paramElem, request, i) 35 | } 36 | } 37 | 38 | // ToStructAndValidation Take the parameters out, wrap them in a struct and check the parameters 39 | func ToStructAndValidation(request commons.BeeRequest, pointParamStruct interface{}) string { 40 | ToStruct(request, pointParamStruct) 41 | var result = Validation(request, pointParamStruct) 42 | return result 43 | } 44 | 45 | // SetValue Assigning values to fields 46 | func SetValue(paramType reflect.Type, paramElem reflect.Value, request commons.BeeRequest, i int) { 47 | var structField = paramType.Field(i) 48 | fieldName := structField.Name 49 | fieldTag := structField.Tag 50 | fieldType := GetFieldType(structField) 51 | 52 | field := paramElem.FieldByName(fieldName) 53 | 54 | var paramValues []string 55 | 56 | if fieldTag != "" { 57 | fieldTagName := fieldTag.Get(Field) 58 | if fieldTagName != "" { 59 | paramValues = request.FormValues(fieldTagName) 60 | } 61 | } 62 | 63 | if paramValues == nil || len(paramValues) < 1 { 64 | paramValues = request.FormValues(fieldName) 65 | } 66 | 67 | if (paramValues == nil || len(paramValues) < 1) && fieldType != data_type.BeeFile { 68 | return 69 | } 70 | 71 | // In the actual scenario, most of the fields are definitely not slices, so here we determine if the first value is empty, and if it is, we don't need to go further 72 | oneParam := paramValues[0] 73 | if fieldType != data_type.BeeFile && oneParam == "" { 74 | return 75 | } 76 | 77 | switch fieldType { 78 | case data_type.Int: 79 | val, err := strconv.ParseInt(oneParam, 10, 64) 80 | if err != nil { 81 | errorPrint(fieldName, err) 82 | return 83 | } 84 | field.SetInt(val) 85 | case data_type.Uint: 86 | val, err := strconv.ParseUint(oneParam, 10, 64) 87 | if err != nil { 88 | errorPrint(fieldName, err) 89 | return 90 | } 91 | field.SetUint(val) 92 | break 93 | case data_type.Float: 94 | val, err := strconv.ParseFloat(oneParam, 64) 95 | if err != nil { 96 | errorPrint(fieldName, err) 97 | return 98 | } 99 | field.SetFloat(val) 100 | break 101 | case data_type.Bool: 102 | val, err := strconv.ParseBool(oneParam) 103 | if err != nil { 104 | errorPrint(fieldName, err) 105 | return 106 | } 107 | field.SetBool(val) 108 | break 109 | case data_type.String: 110 | field.SetString(oneParam) 111 | break 112 | case data_type.Slice: 113 | field.Set(reflect.ValueOf(paramValues)) 114 | break 115 | case data_type.BeeFile: 116 | contentType := request.ContentType() 117 | if commons.IsFormData(contentType) { 118 | beeFile, err := request.GetFile(fieldName) 119 | if err != nil { 120 | errorPrint(fieldName, err.(error)) 121 | return 122 | } 123 | field.Set(reflect.ValueOf(*beeFile)) 124 | } 125 | break 126 | } 127 | } 128 | 129 | // errorPrint 130 | func errorPrint(fieldName string, err error) { 131 | if err != nil { 132 | log.Println("field:" + fieldName + "Setting value Exception occurs, " + err.Error()) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /network/http/commons/RequestAndResponse.go: -------------------------------------------------------------------------------- 1 | package commons 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Beerus-go/Beerus/commons/util" 6 | "net/http" 7 | "net/url" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | // BeeRequest ----------- Secondary wrapping over the request object, mainly to facilitate the acquisition of json passing parameters ----------- 13 | // Secondary encapsulation of a part of the high-frequency use of the function, other functions can be taken from the Request inside 14 | type BeeRequest struct { 15 | Request *http.Request 16 | Json string 17 | RoutePath string 18 | Params map[string][]string 19 | } 20 | 21 | // FormValue Get request parameters 22 | func (req *BeeRequest) FormValue(key string) string { 23 | values := req.FormValues(key) 24 | if values == nil || len(values) < 1 { 25 | return "" 26 | } 27 | return values[0] 28 | } 29 | 30 | // FormValues Get request parameters 31 | func (req *BeeRequest) FormValues(key string) []string { 32 | values := req.Params[key] 33 | if values == nil || len(values) < 1 { 34 | values = make([]string, 1) 35 | values[0] = req.Request.FormValue(key) 36 | return values 37 | } 38 | return values 39 | } 40 | 41 | // HeaderValue Get request header 42 | func (req *BeeRequest) HeaderValue(key string) string { 43 | return req.Request.Header.Get(key) 44 | } 45 | 46 | // HeaderValues Get request headers 47 | func (req *BeeRequest) HeaderValues(key string) []string { 48 | return req.Request.Header.Values(key) 49 | } 50 | 51 | // GetFile Get request file 52 | func (req *BeeRequest) GetFile(key string) (*BeeFile, error) { 53 | file, fileHeader, error := req.Request.FormFile(key) 54 | 55 | var beeFile = new(BeeFile) 56 | beeFile.File = file 57 | beeFile.FileHeader = fileHeader 58 | 59 | return beeFile, error 60 | } 61 | 62 | // ContentType get Content-Type 63 | func (req *BeeRequest) ContentType() string { 64 | contentType := req.HeaderValue(ContentType) 65 | if contentType == "" { 66 | contentType = req.HeaderValue(ContentType2) 67 | } 68 | return contentType 69 | } 70 | 71 | // AddParam add param 72 | func (req *BeeRequest) AddParam(name string, val string) { 73 | name = strings.TrimSpace(name) 74 | val = strings.TrimSpace(val) 75 | 76 | paramArray := req.Params[name] 77 | 78 | if paramArray == nil { 79 | paramArray = make([]string, 0) 80 | } 81 | 82 | paramArray = append(paramArray, val) 83 | 84 | req.Params[name] = paramArray 85 | } 86 | 87 | // BeeResponse ----------- Secondary wrapping of the response object, the response part is enhanced a bit, providing some high-frequency use of the function ----------- 88 | type BeeResponse struct { 89 | Response http.ResponseWriter 90 | } 91 | 92 | // SetHeader Set the response header 93 | func (res *BeeResponse) SetHeader(key string, value string) *BeeResponse { 94 | res.Response.Header().Set(key, value) 95 | return res 96 | } 97 | 98 | // SendJson Send json string to client 99 | func (res *BeeResponse) SendJson(value string) { 100 | res.SetHeader(ContentType, "application/json;charset=UTF-8") 101 | res.Response.Write(util.StrToBytes(value)) 102 | } 103 | 104 | // SendText Send text to client 105 | func (res *BeeResponse) SendText(value string) { 106 | res.SetHeader(ContentType, "text/plain;charset=UTF-8") 107 | res.Response.Write(util.StrToBytes(value)) 108 | } 109 | 110 | // SendHtml Send html string to client 111 | func (res *BeeResponse) SendHtml(value string) { 112 | res.SetHeader(ContentType, "text/html;charset=UTF-8") 113 | res.Response.Write(util.StrToBytes(value)) 114 | } 115 | 116 | // SendStream Send file to client 117 | func (res *BeeResponse) SendStream(fileName string, file []byte) { 118 | res.SetHeader(ContentType, "application/octet-stream") 119 | res.SetHeader(ContentDisposition, "attachment; filename="+url.PathEscape(fileName)) 120 | res.Response.Write(file) 121 | } 122 | 123 | // SendData Sending other custom ContentType data to the client 124 | func (res *BeeResponse) SendData(value string) { 125 | res.Response.Write(util.StrToBytes(value)) 126 | } 127 | 128 | // SendErrorMsg Return error messages in json format 129 | func (res *BeeResponse) SendErrorMsg(code int, msg string) { 130 | res.SendJson(fmt.Sprintf(ErrorMsg, strconv.Itoa(code), msg)) 131 | } 132 | -------------------------------------------------------------------------------- /network/http/websocket/UpgradeWebSocket.go: -------------------------------------------------------------------------------- 1 | package websocket 2 | 3 | import ( 4 | "bytes" 5 | "crypto/sha1" 6 | "encoding/base64" 7 | "github.com/Beerus-go/Beerus/application/websocket" 8 | "github.com/Beerus-go/Beerus/application/websocket/wroute" 9 | "github.com/Beerus-go/Beerus/commons/util" 10 | "github.com/Beerus-go/Beerus/network/http/commons" 11 | "log" 12 | "net" 13 | "net/http" 14 | "strings" 15 | ) 16 | 17 | // UpgradeToWebSocket Upgrade to websocket 18 | func UpgradeToWebSocket(write http.ResponseWriter, request *commons.BeeRequest) { 19 | 20 | // Does routing exist 21 | if wroute.WebSocketRouteExist(request.RoutePath) == false { 22 | log.Println("WebSocket wroute does not exist, connection failed") 23 | return 24 | } 25 | 26 | h, ok := write.(http.Hijacker) 27 | 28 | if ok == false { 29 | return 30 | } 31 | 32 | netConn, brw, err := h.Hijack() 33 | if err != nil { 34 | log.Println(err) 35 | } 36 | 37 | if brw.Reader.Buffered() > 0 { 38 | log.Println("WebSocket client sent data before handshake is complete") 39 | netConn.Close() 40 | return 41 | } 42 | 43 | stringBuilder := strings.Builder{} 44 | 45 | stringBuilder.WriteString(commons.ResponseOnline) 46 | stringBuilder.WriteString(commons.CarriageReturn) 47 | stringBuilder.WriteString("Upgrade:websocket") 48 | stringBuilder.WriteString(commons.CarriageReturn) 49 | stringBuilder.WriteString("Connection:Upgrade") 50 | stringBuilder.WriteString(commons.CarriageReturn) 51 | stringBuilder.WriteString("Sec-WebSocket-Accept:" + getAccept(request)) 52 | stringBuilder.WriteString(commons.CarriageReturn) 53 | stringBuilder.WriteString(commons.CarriageReturn) 54 | 55 | _, err = netConn.Write(util.StrToBytes(stringBuilder.String())) 56 | 57 | if err != nil { 58 | log.Println("WebSocket Link establishment failure with client, " + err.Error()) 59 | netConn.Close() 60 | } 61 | 62 | websocket.ExecuteConnection(request.RoutePath, netConn) 63 | 64 | // Open a goroutine that listens to this link 65 | go connProcessing(netConn, request.RoutePath) 66 | } 67 | 68 | // connProcessing listens to this link 69 | func connProcessing(conn net.Conn, routePath string) { 70 | defer conn.Close() 71 | 72 | // Message data that has been read but not yet processed 73 | buf := new(bytes.Buffer) 74 | 75 | // Length of messages already read 76 | readSizeCache := 0 77 | 78 | for { 79 | // Read messages from the client 80 | readByte := make([]byte, 500) 81 | ln, err := conn.Read(readByte) 82 | if err != nil { 83 | websocket.ExecuteClose(routePath) 84 | log.Println("WebSocket client abnormally disconnected, " + err.Error()) 85 | break 86 | } 87 | 88 | if ln <= 0 { 89 | continue 90 | } 91 | 92 | readSizeCache += ln 93 | buf.Write(readByte) 94 | 95 | // Parse the message and call the corresponding handler for business processing 96 | size, errMsg := Processing(buf, readSizeCache, routePath) 97 | if errMsg != nil { 98 | websocket.ExecuteClose(routePath) 99 | log.Println("WebSocket Exceptions in parsing messages, " + errMsg.Error()) 100 | break 101 | } 102 | 103 | // Parsed data If = 0, it means that a complete message has not been read yet, so continue reading 104 | if size <= 0 { 105 | continue 106 | } 107 | 108 | // Remove used data from the cache 109 | if size == readSizeCache { 110 | buf.Reset() 111 | readSizeCache = 0 112 | } else { 113 | remaining, errorMsg := util.CopyOfRange(buf.Bytes(), size, readSizeCache) 114 | if errorMsg != nil { 115 | websocket.ExecuteClose(routePath) 116 | log.Println("WebSocket Removing exceptions from already processed data, " + errMsg.Error()) 117 | break 118 | } 119 | buf = bytes.NewBuffer(remaining) 120 | readSizeCache = readSizeCache - size 121 | if readSizeCache < 0 { 122 | readSizeCache = 0 123 | } 124 | } 125 | } 126 | } 127 | 128 | // Get the logo for connecting to the client 129 | func getAccept(request *commons.BeeRequest) string { 130 | secKey := request.HeaderValue(commons.SecWebsocketKey) 131 | 132 | swKey := secKey + commons.SocketSecretKey 133 | 134 | hash := sha1.New() 135 | 136 | hash.Write(util.StrToBytes(swKey)) 137 | 138 | hashResult := hash.Sum(nil) 139 | 140 | return base64.StdEncoding.EncodeToString(hashResult) 141 | } 142 | -------------------------------------------------------------------------------- /network/http/HTTPServer.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "github.com/Beerus-go/Beerus/application/web" 5 | "github.com/Beerus-go/Beerus/application/web/route" 6 | "github.com/Beerus-go/Beerus/commons/util" 7 | "github.com/Beerus-go/Beerus/network/http/commons" 8 | "github.com/Beerus-go/Beerus/network/http/websocket" 9 | "io/ioutil" 10 | "log" 11 | "net/http" 12 | "net/url" 13 | "strings" 14 | ) 15 | 16 | // StartHttpServer Start an http service 17 | func StartHttpServer(port string) { 18 | route.ReloadMatchToUrl() 19 | 20 | http.HandleFunc("/", handler) 21 | http.ListenAndServe("0.0.0.0:"+port, nil) 22 | 23 | } 24 | 25 | // handler 26 | func handler(write http.ResponseWriter, request *http.Request) { 27 | 28 | var req = new(commons.BeeRequest) 29 | var res = new(commons.BeeResponse) 30 | 31 | req.Request = request 32 | res.Response = write 33 | req.Params = make(map[string][]string) 34 | 35 | setRoutePath(req) 36 | 37 | // If WebSocket, upgrade the protocol 38 | if isWebSocket(req) { 39 | websocket.UpgradeToWebSocket(write, req) 40 | return 41 | } 42 | 43 | // Not WebSocket will handle http normally 44 | error := parsingParam(req) 45 | 46 | if error != nil { 47 | log.Println("[ERROR]: Parsing parameter exception, " + error.Error()) 48 | res.SendErrorMsg(500, error.Error()) 49 | return 50 | } 51 | 52 | web.ExecuteRoute(req, res) 53 | } 54 | 55 | // parsingParam Parsing json parameters 56 | func parsingParam(request *commons.BeeRequest) error { 57 | 58 | contentType := request.ContentType() 59 | 60 | if strings.ToUpper(request.Request.Method) == "GET" { 61 | url := request.Request.RequestURI 62 | paramIndex := strings.LastIndex(url, "?") 63 | 64 | if paramIndex > -1 { 65 | paramStr := url[(paramIndex + 1):] 66 | extractionParameters(paramStr, request) 67 | } 68 | 69 | } else { 70 | if commons.IsUrlEncode(contentType) { 71 | body := request.Request.Body 72 | if body == nil { 73 | return nil 74 | } 75 | 76 | resultFrom, err := ioutil.ReadAll(body) 77 | if err != nil { 78 | log.Print("Exception for parsing urlEncode parameters", err.Error()) 79 | return err 80 | } 81 | 82 | extractionParameters(util.BytesToString(resultFrom), request) 83 | } else if commons.IsJSON(contentType) { 84 | body := request.Request.Body 85 | if body == nil { 86 | return nil 87 | } 88 | 89 | resultJson, err := ioutil.ReadAll(body) 90 | if err != nil { 91 | log.Print("Exception for parsing json parameters", err.Error()) 92 | return err 93 | } 94 | 95 | request.Json = util.BytesToString(resultJson) 96 | } 97 | } 98 | 99 | return nil 100 | } 101 | 102 | // extractionParameters Extraction parameters 103 | func extractionParameters(paramStr string, request *commons.BeeRequest) { 104 | if paramStr == "" { 105 | return 106 | } 107 | 108 | paramStr, err := url.QueryUnescape(paramStr) 109 | if err != nil { 110 | // Here it just fails to decode, so there is no need to stop, as the user receives the parameters and can decode them themselves. 111 | log.Println(err.Error()) 112 | } 113 | 114 | paramArray := strings.Split(paramStr, "&") 115 | for _, param := range paramArray { 116 | if param == "" { 117 | continue 118 | } 119 | 120 | paramKeyAndVal := strings.Split(param, "=") 121 | if len(paramKeyAndVal) <= 0 { 122 | continue 123 | } 124 | 125 | request.AddParam(paramKeyAndVal[0], paramKeyAndVal[1]) 126 | } 127 | } 128 | 129 | // setRoutePath Set the route path to request 130 | func setRoutePath(request *commons.BeeRequest) { 131 | url := request.Request.RequestURI 132 | var lastIndex = strings.LastIndex(url, "?") 133 | if lastIndex > -1 { 134 | url = url[:lastIndex] 135 | } 136 | 137 | request.RoutePath = url 138 | } 139 | 140 | // isWebSocket Is it a websocket 141 | func isWebSocket(request *commons.BeeRequest) bool { 142 | upgrade := request.HeaderValue(commons.Upgrade) 143 | connection := request.HeaderValue(commons.Connection) 144 | secKey := request.HeaderValue(commons.SecWebsocketKey) 145 | 146 | if upgrade == "" || connection == "" || secKey == "" { 147 | return false 148 | } 149 | 150 | if strings.Index(strings.ToUpper(connection), strings.ToUpper(commons.Upgrade)) <= -1 { 151 | return false 152 | } 153 | 154 | return true 155 | } 156 | -------------------------------------------------------------------------------- /application/web/params/ParameterVerification.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | import ( 4 | "fmt" 5 | "github.com/Beerus-go/Beerus/commons" 6 | "github.com/Beerus-go/Beerus/commons/util" 7 | "github.com/Beerus-go/Beerus/network/http/commons" 8 | "reflect" 9 | "regexp" 10 | "strconv" 11 | "strings" 12 | ) 13 | 14 | const ( 15 | SUCCESS = "SUCCESS" 16 | ErrorMsg = "Field:[%s]->Tag:[%s] setting is incorrect,[%s]" 17 | ValidKeyNotNull = "notnull" 18 | ValidKeyReg = "reg" 19 | ValidKeyMax = "max" 20 | ValidKeyMin = "min" 21 | ValidKeyRoutes = "routes" 22 | ValidKeyMsg = "msg" 23 | ) 24 | 25 | // Validation Checking the parameters of the struct 26 | func Validation(request commons.BeeRequest, pointParamStruct interface{}) string { 27 | var paramElem = reflect.ValueOf(pointParamStruct).Elem() 28 | var paramType = paramElem.Type() 29 | 30 | return ValidationReflect(request, paramElem, paramType) 31 | } 32 | 33 | // ValidationReflect Checking the parameters of the struct 34 | func ValidationReflect(request commons.BeeRequest, paramElem reflect.Value, paramType reflect.Type) string { 35 | var requestPath = request.RoutePath 36 | 37 | fieldNum := paramType.NumField() 38 | for i := 0; i < fieldNum; i++ { 39 | var field = paramType.Field(i) 40 | var fieldName = field.Name 41 | var fieldType = GetFieldType(field) 42 | var fieldTag = field.Tag 43 | 44 | if fieldTag == "" { 45 | continue 46 | } 47 | 48 | // Get the tag information of the field and validate the field data based on this information 49 | var notNull = fieldTag.Get(ValidKeyNotNull) 50 | var max = fieldTag.Get(ValidKeyMax) 51 | var min = fieldTag.Get(ValidKeyMin) 52 | var reg = fieldTag.Get(ValidKeyReg) 53 | var msg = fieldTag.Get(ValidKeyMsg) 54 | var routes = fieldTag.Get(ValidKeyRoutes) 55 | 56 | if notNull == "" && reg == "" && max == "" && min == "" { 57 | continue 58 | } 59 | 60 | // Whether the route of this request is within the scope of this field check 61 | if routes != "" { 62 | var isContain = false 63 | 64 | var apisArray = strings.Split(routes, ",") 65 | for _, apiPath := range apisArray { 66 | if util.Match(requestPath, apiPath) { 67 | isContain = true 68 | } 69 | } 70 | 71 | if isContain == false { 72 | continue 73 | } 74 | } 75 | 76 | // If the user does not set a prompt message, give a default value 77 | if msg == "" { 78 | msg = "Parameters [" + fieldName + "] do not meet the calibration rules" 79 | } 80 | 81 | // Start checking the fields 82 | var fieldObj = paramElem.FieldByName(fieldName) 83 | var result = isSuccess(fieldType, fieldObj, fieldName, notNull, reg, max, min, msg) 84 | 85 | // If the verification does not pass, a message is returned directly 86 | if result != SUCCESS { 87 | return result 88 | } 89 | } 90 | 91 | return SUCCESS 92 | } 93 | 94 | // isSuccess Verify that the value of this field meets the requirements 95 | func isSuccess(fieldType string, field reflect.Value, fieldName string, notNull string, reg string, max string, min string, msg string) string { 96 | switch fieldType { 97 | case data_type.Int: 98 | val := field.Int() 99 | if min != "" { 100 | intMin, err := strconv.ParseInt(min, 10, 64) 101 | if err != nil { 102 | return fmt.Sprintf(ErrorMsg, fieldName, "min", "Should be set to int type") 103 | } 104 | 105 | if val < intMin { 106 | return msg 107 | } 108 | } 109 | if max != "" { 110 | intMax, err := strconv.ParseInt(max, 10, 64) 111 | if err != nil { 112 | return fmt.Sprintf(ErrorMsg, fieldName, "max", "Should be set to int type") 113 | } 114 | 115 | if val > intMax { 116 | return msg 117 | } 118 | } 119 | break 120 | case data_type.Uint: 121 | var val = field.Uint() 122 | if min != "" { 123 | intMin, err := strconv.ParseUint(min, 10, 64) 124 | if err != nil { 125 | return fmt.Sprintf(ErrorMsg, fieldName, "min", "Should be set to int type") 126 | } 127 | 128 | if val < intMin { 129 | return msg 130 | } 131 | } 132 | if max != "" { 133 | intMax, err := strconv.ParseUint(max, 10, 64) 134 | if err != nil { 135 | return fmt.Sprintf(ErrorMsg, fieldName, "max", "Should be set to int type") 136 | } 137 | 138 | if val > intMax { 139 | return msg 140 | } 141 | } 142 | break 143 | case data_type.Float: 144 | var val = field.Float() 145 | if min != "" { 146 | intMin, err := strconv.ParseFloat(min, 64) 147 | if err != nil { 148 | return fmt.Sprintf(ErrorMsg, fieldName, "min", "Should be set to float type") 149 | } 150 | 151 | if val < intMin { 152 | return msg 153 | } 154 | } 155 | if max != "" { 156 | intMax, err := strconv.ParseFloat(max, 64) 157 | if err != nil { 158 | return fmt.Sprintf(ErrorMsg, fieldName, "max", "Should be set to float type") 159 | } 160 | 161 | if val > intMax { 162 | return msg 163 | } 164 | } 165 | break 166 | case data_type.String: 167 | var val = field.String() 168 | if notNull != "" { 169 | isNotNull, err := strconv.ParseBool(notNull) 170 | if err != nil { 171 | return fmt.Sprintf(ErrorMsg, fieldName, "notnull", "Must be true or false") 172 | } 173 | 174 | if isNotNull && val == "" { 175 | return msg 176 | } 177 | } 178 | if reg != "" { 179 | regular := regexp.MustCompile(reg) 180 | var isMatch = regular.MatchString(val) 181 | if !isMatch { 182 | return msg 183 | } 184 | } 185 | break 186 | case data_type.Slice: 187 | if notNull != "" && field.IsNil() { 188 | return msg 189 | } 190 | break 191 | } 192 | 193 | return SUCCESS 194 | } 195 | -------------------------------------------------------------------------------- /application/web/ExecuteRoute.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/Beerus-go/Beerus/application/web/params" 6 | "github.com/Beerus-go/Beerus/application/web/route" 7 | "github.com/Beerus-go/Beerus/commons" 8 | "github.com/Beerus-go/Beerus/commons/util" 9 | "github.com/Beerus-go/Beerus/network/http/commons" 10 | "log" 11 | "reflect" 12 | "strings" 13 | ) 14 | 15 | const Download = "download-b7c39bbf-d22f-42f6-ad84-bd472dbc9e9a" 16 | 17 | // ExecuteRoute Execute the interceptor and the corresponding interface of the route 18 | func ExecuteRoute(request *commons.BeeRequest, response *commons.BeeResponse) { 19 | 20 | method := request.Request.Method 21 | routePath := request.RoutePath 22 | routeFunction := route.GetRoute(routePath + "/" + strings.ToUpper(method)) 23 | 24 | if routeFunction == nil { 25 | response.SendErrorMsg(400, "This route does not exist, please check if the route path and request method are correct") 26 | return 27 | } 28 | 29 | // exec interceptors 30 | var interceptors = route.GetInterceptor(routePath) 31 | for _, inter := range interceptors { 32 | var result = inter(request, response) 33 | if result == false { 34 | log.Println("[This is a friendly reminder, not an error or a warning]: your interceptor returns false, make sure you have called the res.SendXXX function to respond to the front end before returning false") 35 | return 36 | } 37 | } 38 | 39 | // Execute the routeFunction on the route 40 | executeFunction(request, response, routeFunction) 41 | } 42 | 43 | // Execute the routing function 44 | func executeFunction(request *commons.BeeRequest, response *commons.BeeResponse, routeFunction interface{}) { 45 | method := reflect.ValueOf(routeFunction) 46 | paramNum := method.Type().NumIn() 47 | 48 | paramArray := make([]reflect.Value, 0) 49 | 50 | for i := 0; i < paramNum; i++ { 51 | param := method.Type().In(i) 52 | paramObj := reflect.New(param) 53 | paramElem := paramObj.Elem() 54 | 55 | if strings.ToLower(param.Kind().String()) != data_type.Struct { 56 | paramArray = append(paramArray, paramElem) 57 | continue 58 | } 59 | 60 | if strings.ToLower(param.Name()) == data_type.BeeRequest { 61 | paramArray = append(paramArray, reflect.ValueOf(*request)) 62 | continue 63 | } 64 | 65 | if strings.ToLower(param.Name()) == data_type.BeeResponse { 66 | paramArray = append(paramArray, reflect.ValueOf(*response)) 67 | continue 68 | } 69 | 70 | // Assigning values to the fields inside the parameters 71 | if commons.IsJSON(request.ContentType()) { 72 | if request.Json != "" { 73 | json.Unmarshal(util.StrToBytes(request.Json), paramObj.Interface()) 74 | paramElem = paramObj.Elem() 75 | } 76 | 77 | paramArray = append(paramArray, paramElem) 78 | 79 | } else { 80 | for j := 0; j < param.NumField(); j++ { 81 | params.SetValue(param, paramElem, *request, j) 82 | } 83 | 84 | paramArray = append(paramArray, paramElem) 85 | } 86 | 87 | // If json mode is turned on, then automated parameter validation will be performed and the response message in json format will be given to the front-end based on the validation result. 88 | if route.JsonMode { 89 | result := params.ValidationReflect(*request, paramElem, param) 90 | if result != params.SUCCESS { 91 | response.SendErrorMsg(1128, result) 92 | return 93 | } 94 | } 95 | } 96 | 97 | // If json mode is turned off 98 | if route.JsonMode == false { 99 | method.Call(paramArray) 100 | return 101 | } 102 | 103 | // If json mode is turned on, then the return value of the function is converted to json and used as the response data 104 | if route.JsonMode { 105 | result := method.Call(paramArray) 106 | 107 | // Prompt the user if there is no return value, in JSON mode there must be 108 | if result == nil || len(result) < 1 { 109 | log.Println("[ERROR]: If you turn on json mode, then all routes must have a return value to give the front-end response through the return value") 110 | response.SendErrorMsg(500, "If you turn on json mode, then all routes must have a return value to give the front-end response through the return value") 111 | return 112 | } 113 | 114 | // If it returns a download, this is a file download route and the front-end response has already been given inside the route, so just return it here 115 | if result[0].String() == Download { 116 | return 117 | } 118 | 119 | // If there is more than one return value, then determine if the second value is of type error 120 | if len(result) > 1 { 121 | // If it is not of type error, then the user is prompted that it must be of type error 122 | if result[1].Type().Name() != "error" { 123 | log.Println("[ERROR]: In JSON mode, the second return value of the route must be of type error, or only one return value must be set") 124 | response.SendErrorMsg(500, "In JSON mode, the second return value of the route must be of type error, or only one return value must be set") 125 | return 126 | } 127 | 128 | // If it is an error type, then determine whether the value is empty, 129 | //if not, it means that there is an exception inside the route, and respond directly to the front-end with the error message 130 | errInterface := result[1].Interface() 131 | if errInterface != nil { 132 | err := errInterface.(error) 133 | if err != nil { 134 | response.SendErrorMsg(500, err.Error()) 135 | return 136 | } 137 | } 138 | } 139 | 140 | // If there is only one return value, or if the second return value is empty, 141 | // the request is normal and the first return value is converted into a json response to the front-end. 142 | resultJson, _ := util.ToJSONString(result[0].Interface()) 143 | response.SendJson(resultJson) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Beerus · 3 | 4 | 5 | 6 |

7 | 8 | Beerus is a web framework developed entirely in go, 9 | Based on net/http, it extends the management of routes, adds interceptors, session management, 10 | receiving parameters with struct, parameter validation, etc. 11 | It also provides WebSocket support to upgrade the http protocol to WebSocket and implement communication. 12 | 13 | ## Installation 14 | 15 | ```shell 16 | go get github.com/Beerus-go/Beerus@v1.1.9 17 | ``` 18 | 19 | ## Documentation 20 | 21 | [https://beeruscc.com/beerus](https://beeruscc.com/beerus) 22 | 23 | ## Examples 24 | 25 | ### HTTP example 26 | 27 | Create a function to manage the routing configuration 28 | 29 | ```go 30 | func CreateRoute() { 31 | 32 | // Any request method can use the parameters of the routing function to receive the request parameters 33 | // Routing functions must have a return value, supported types: struct, map, array 34 | route.POST("/example/post", func (param DemoParam, req commons.BeeRequest, res commons.BeeResponse) (map[string]string, error) { 35 | 36 | if xxx { 37 | return nil, errors.New("The error message you want to return to the front-end") 38 | } 39 | 40 | msg := make(map[string]string) 41 | msg["msg"] = "success" 42 | return param, nil 43 | }) 44 | } 45 | 46 | // DemoParam If you have a struct like this, and you want to put all the parameters from the request into this struct 47 | type DemoParam struct { 48 | // You can customize any field 49 | // the name of the field must be exactly the same as the name of the requested parameter, and is case-sensitive 50 | TestStringReception string `notnull:"true" msg:"TestStringReception Cannot be empty" routes:"/example/put"` 51 | TestIntReception int `max:"123" min:"32" msg:"TestIntReception The value range must be between 32 - 123" routes:"/example/post"` 52 | TestUintReception uint `max:"123" min:"32" msg:"TestUintReception The value range must be between 32 - 123"` 53 | TestFloatReception float32 `max:"123" min:"32" msg:"TestFloatReception The value range must be between 32 - 123"` 54 | TestBoolReception bool 55 | TestStringRegReception string `reg:"^[a-z]+$" msg:"TestStringRegReception Does not meet the regular"` 56 | TestBeeFileReception commons.BeeFile 57 | 58 | TestJsonReception []string 59 | } 60 | ``` 61 | 62 | Start Service 63 | 64 | ```go 65 | func main() { 66 | // Interceptors, routes, etc. Loading of data requires its own calls 67 | routes.CreateRoute() 68 | 69 | // Listen the service and listen to port 8080 70 | beerus.ListenHTTP(8080) 71 | } 72 | ``` 73 | 74 | Non-JSON modes 75 | 76 | ```go 77 | func CreateRoute() { 78 | 79 | // Turn off json mode, it is on by default 80 | route.JsonMode = false 81 | 82 | 83 | // In non-json mode, you need to call the Send function in the res object yourself to return the data 84 | route.POST("/example/post", func (param DemoParam, req commons.BeeRequest, res commons.BeeResponse) { 85 | 86 | // ----- Only non-json mode requires manual validation ----- 87 | 88 | // If you're in json mode, you don't need to write the following code 89 | 90 | // Separate validation of data in struct, this feature can be used independently in any case and is not limited to the routing layer. 91 | var result = params.Validation(req, ¶m, param) 92 | if result != params.SUCCESS { 93 | res.SendErrorMsg(1128, result) 94 | return 95 | } 96 | 97 | // It can respond to any type of data, but for demonstration purposes we are still using json here. 98 | res.SendJson(`{"msg":"SUCCESS"}`) 99 | }) 100 | } 101 | 102 | // DemoParam If you have a struct like this, and you want to put all the parameters from the request into this struct 103 | type DemoParam struct { 104 | // You can customize any field 105 | // the name of the field must be exactly the same as the name of the requested parameter, and is case-sensitive 106 | TestStringReception string `notnull:"true" msg:"TestStringReception Cannot be empty" routes:"/example/put"` 107 | TestIntReception int `max:"123" min:"32" msg:"TestIntReception The value range must be between 32 - 123" routes:"/example/post"` 108 | TestUintReception uint `max:"123" min:"32" msg:"TestUintReception The value range must be between 32 - 123"` 109 | TestFloatReception float32 `max:"123" min:"32" msg:"TestFloatReception The value range must be between 32 - 123"` 110 | TestBoolReception bool 111 | TestStringRegReception string `reg:"^[a-z]+$" msg:"TestStringRegReception Does not meet the regular"` 112 | TestBeeFileReception commons.BeeFile 113 | 114 | TestJsonReception []string 115 | } 116 | ``` 117 | 118 | ### WebSocket example 119 | 120 | CreateWebSocketRoute Creating websocket routes 121 | 122 | ```go 123 | func CreateWebSocketRoute() { 124 | wroute.AddWebSocketRoute("/ws/test", onConnection, onMessage, onClose) 125 | wroute.AddWebSocketRoute("/ws/test2", onConnection, onMessage, onClose) 126 | } 127 | 128 | // In order to save time, only three functions are used below. In practice, you can configure a set of functions for each wroute 129 | 130 | func onConnection(session *wparams.WebSocketSession, msg string) { 131 | session.SendString("connection success") 132 | } 133 | 134 | func onMessage(session *wparams.WebSocketSession, msg string) { 135 | session.SendString("I got the message.") 136 | } 137 | 138 | func onClose(session *wparams.WebSocketSession, msg string) { 139 | println(msg + "-------------------------------") 140 | } 141 | ``` 142 | 143 | Start Service 144 | 145 | ```go 146 | func main() { 147 | // Interceptors, routes, etc. Loading of data requires its own calls 148 | routes.CreateRoute() 149 | routes.CreateWebSocketRoute() 150 | 151 | // Listen the service and listen to port 8080 152 | beerus.ListenHTTP(8080) 153 | } 154 | ``` 155 | 156 | [Complete sample code](https://github.com/yuyenews/Beerus/tree/master/example) 157 | 158 | ## Database operations 159 | 160 | [Beerus-DB](https://github.com/yuyenews/Beerus-DB) 161 | 162 | ## License 163 | 164 | Beerus is [MIT licensed](https://github.com/yuyenews/Beerus/blob/master/LICENSE) 165 | --------------------------------------------------------------------------------