-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttp.go
More file actions
122 lines (97 loc) · 3.29 KB
/
Copy pathhttp.go
File metadata and controls
122 lines (97 loc) · 3.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*******************************************************************************
* HTTP server
*
* @author Lars Thoms <lars@thoms.io>
* @date 2023-04-17
******************************************************************************/
package main
import (
"Moodle_Maxima_Pool/controller"
"Moodle_Maxima_Pool/models"
"Moodle_Maxima_Pool/services"
"context"
"encoding/base64"
"fmt"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"net/http"
"path"
"strings"
)
var (
router *gin.Engine
errUnauthenticated = &models.ErrorResponseJSON{Status: http.StatusUnauthorized, Code: "unauthorized", Title: "Unauthorized", Details: "The request misses a valid API key."}
errUndefinedRequest = &models.ErrorResponseJSON{Status: http.StatusRequestedRangeNotSatisfiable, Code: "undefined_request", Title: "Undefined request", Details: "The type of request is undefined."}
)
func initHTTPConfig() {
// Set GIN mode
if logger.Level() == Debug {
gin.SetMode(gin.DebugMode)
} else {
gin.SetMode(gin.ReleaseMode)
}
// Check API key length
if len(viper.GetString("server.api_key")) < 16 {
logger.Warn("API key is very short")
}
}
func initHTTPRoutes() {
router = gin.Default()
router.Use(gin.CustomRecovery(errorHandlerGin))
router.Use(globalHeader())
router.GET("/openapi.json", controller.GetOpenAPI)
router.GET("/health", controller.GetHealth)
authorized := router.Group(path.Clean(viper.GetString("server.base_path")), validateAPIKey())
// Job
authorized.POST("/MaximaPool", controller.PostJob)
}
func startHTTPServer() {
waitGroup.Add(1)
defer waitGroup.Done()
logger.Debug("configure web server")
initHTTPConfig()
logger.Debug("create routes")
initHTTPRoutes()
logger.Debug("create web server")
server := &http.Server{Addr: fmt.Sprintf("%s:%d", viper.GetString("server.listen"), viper.GetInt("server.port")), Handler: router}
go func() {
logger.Infof("start web server and listen to http://%s:%d", viper.GetString("server.listen"), viper.GetInt("server.port"))
if err := server.ListenAndServe(); err == http.ErrServerClosed {
logger.Info(err)
} else {
logger.Fatal(err)
}
}()
<-terminator
ctx, cancel := context.WithTimeout(context.Background(), ctxTimeout)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
logger.Warn(err)
}
}
func validateAPIKey() gin.HandlerFunc {
return func(c *gin.Context) {
if c.Request.Header.Get("X-API-Key") == viper.GetString("server.api_key") {
return
}
basicAuthHeader := strings.SplitN(c.Request.Header.Get("Authorization"), " ", 2)
if len(basicAuthHeader) == 2 && strings.EqualFold(basicAuthHeader[0], "Basic") {
if basicAuthPayload, err := base64.StdEncoding.DecodeString(basicAuthHeader[1]); err == nil {
if basicAuthPair := strings.SplitN(string(basicAuthPayload), ":", 2); len(basicAuthPair) == 2 && basicAuthPair[1] == viper.GetString("server.api_key") {
return
}
}
}
c.Header("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`)
c.AbortWithStatusJSON(services.Error(errUnauthenticated))
}
}
func globalHeader() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Link", "</openapi.json>; rel=\"service-desc\"")
}
}
func errorHandlerGin(c *gin.Context, err any) {
logger.Warn(err)
c.AbortWithStatusJSON(services.Error(errUndefinedRequest))
}