Skip to content
This repository was archived by the owner on Dec 20, 2024. It is now read-only.

Commit cd9fa36

Browse files
committed
refactor: use package server/api to register supernode's API
Signed-off-by: lowzj <[email protected]>
1 parent f5cc28f commit cd9fa36

File tree

9 files changed

+175
-227
lines changed

9 files changed

+175
-227
lines changed

supernode/server/api/api.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ func newCategory(name, prefix string) *category {
4747
apiCategories[name] = &category{
4848
name: name,
4949
prefix: prefix,
50+
handlerSpecs: []*HandlerSpec{
51+
listHandler(name),
52+
},
5053
}
5154
return apiCategories[name]
5255
}
@@ -59,11 +62,12 @@ type category struct {
5962
}
6063

6164
// Register registers an API into this API category.
62-
func (c *category) Register(h *HandlerSpec) *category {
63-
if !validate(h) {
64-
return c
65+
func (c *category) Register(handlers ...*HandlerSpec) *category {
66+
for _, h := range handlers {
67+
if valid(h) {
68+
c.handlerSpecs = append(c.handlerSpecs, h)
69+
}
6570
}
66-
c.handlerSpecs = append(c.handlerSpecs, h)
6771
return c
6872
}
6973

@@ -82,8 +86,11 @@ func (c *category) Handlers() []*HandlerSpec {
8286
return c.handlerSpecs
8387
}
8488

85-
// -----------------------------------------------------------------------------
86-
87-
func validate(h *HandlerSpec) bool {
88-
return h != nil && h.HandlerFunc != nil && h.Method != ""
89+
// Range traverses all the handlers in this category.
90+
func (c *category) Range(f func(prefix string, h *HandlerSpec)) {
91+
for _, h := range c.handlerSpecs {
92+
if h != nil {
93+
f(c.prefix, h)
94+
}
95+
}
8996
}

supernode/server/api/utils.go

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"encoding/json"
2222
"io"
2323
"net/http"
24+
"time"
2425

2526
"github.com/go-openapi/strfmt"
2627
"github.com/sirupsen/logrus"
@@ -52,26 +53,28 @@ func ParseJSONRequest(req io.Reader, target interface{}, validator ValidateFunc)
5253
return nil
5354
}
5455

55-
// EncodeResponse encodes response in json.
56-
// The response body is empty if the data is nil or empty value.
57-
func EncodeResponse(w http.ResponseWriter, code int, data interface{}) error {
56+
// SendResponse encodes response in json.
57+
//
58+
// TODO:
59+
// Should the response body should be empty if the data is nil or empty
60+
// string? Now it's incompatible with the client.
61+
func SendResponse(w http.ResponseWriter, code int, data interface{}) error {
5862
w.Header().Set("Content-Type", "application/json")
5963
w.WriteHeader(code)
60-
if util.IsNil(data) || data == "" {
61-
return nil
62-
}
63-
return json.NewEncoder(w).Encode(data)
64+
enc := json.NewEncoder(w)
65+
enc.SetEscapeHTML(false)
66+
return enc.Encode(data)
6467
}
6568

6669
// HandleErrorResponse handles err from server side and constructs response
6770
// for client side.
68-
func HandleErrorResponse(w http.ResponseWriter, err error) {
71+
func HandleErrorResponse(w http.ResponseWriter, err error) error {
6972
switch e := err.(type) {
7073
case *errortypes.HTTPError:
71-
_ = EncodeResponse(w, e.Code, errResp(e.Code, e.Msg))
74+
return SendResponse(w, e.Code, errResp(e.Code, e.Msg))
7275
default:
7376
// By default, server side returns code 500 if error happens.
74-
_ = EncodeResponse(w, http.StatusInternalServerError,
77+
return SendResponse(w, http.StatusInternalServerError,
7578
errResp(http.StatusInternalServerError, e.Error()))
7679
}
7780
}
@@ -86,12 +89,17 @@ func WrapHandler(handler HandlerFunc) http.HandlerFunc {
8689
defer cancel()
8790

8891
// Start to handle request.
92+
start := time.Now()
8993
err := handler(ctx, w, req)
9094
if err != nil {
9195
// Handle error if request handling fails.
92-
HandleErrorResponse(w, err)
96+
if sendErr := HandleErrorResponse(w, err); sendErr != nil {
97+
logrus.Errorf("%s %v remote:%s cost:%v handleError:%v sendError:%v",
98+
req.Method, req.URL, req.RemoteAddr, time.Since(start), err, sendErr)
99+
}
93100
}
94-
logrus.Debugf("%s %v err:%v", req.Method, req.URL, err)
101+
logrus.Debugf("%s %v remote:%s cost:%v err:%v", req.Method, req.URL,
102+
req.RemoteAddr, time.Since(start), err)
95103
}
96104
}
97105

@@ -101,3 +109,36 @@ func errResp(code int, msg string) *types.ErrorResponse {
101109
Message: msg,
102110
}
103111
}
112+
113+
func valid(h *HandlerSpec) bool {
114+
return h != nil && h.HandlerFunc != nil && h.Method != ""
115+
}
116+
117+
func listHandler(name string) *HandlerSpec {
118+
h := &HandlerSpec{
119+
Method: http.MethodGet,
120+
Path: "/",
121+
HandlerFunc: func(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
122+
c := apiCategories[name]
123+
if c == nil {
124+
return errortypes.NewHTTPError(http.StatusBadRequest, "no such category")
125+
}
126+
127+
result := map[string]interface{}{
128+
"category": c.name,
129+
"prefix": c.prefix,
130+
}
131+
handlers := make([]map[string]string, len(c.handlerSpecs))
132+
for i, v := range c.handlerSpecs {
133+
handlers[i] = map[string]string{
134+
"method": v.Method,
135+
"path": v.Path,
136+
}
137+
}
138+
result["api"] = handlers
139+
140+
return SendResponse(rw, http.StatusOK, result)
141+
},
142+
}
143+
return h
144+
}

supernode/server/api/utils_test.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
"github.com/stretchr/testify/suite"
3030

3131
"github.com/dragonflyoss/Dragonfly/pkg/errortypes"
32-
"github.com/dragonflyoss/Dragonfly/pkg/util"
3332
)
3433

3534
func TestUtil(t *testing.T) {
@@ -89,7 +88,7 @@ func (s *TestUtilSuite) TestEncodeResponse() {
8988
err string
9089
}{
9190
{200, "", ""},
92-
{200, nil, ""},
91+
{200, (*testStruct)(nil), ""},
9392
{200, 0, ""},
9493
{200, newT(1), ""},
9594
{400, newT(1), ""},
@@ -98,12 +97,12 @@ func (s *TestUtilSuite) TestEncodeResponse() {
9897
for i, c := range cases {
9998
msg := fmt.Sprintf("case %d: %v", i, c)
10099
w := httptest.NewRecorder()
101-
e := EncodeResponse(w, c.code, c.data)
100+
e := SendResponse(w, c.code, c.data)
102101
if c.err == "" {
103102
s.Nil(e, msg)
104103
s.Equal(c.code, w.Code, msg)
105-
if util.IsNil(c.data) {
106-
s.Equal("", strings.TrimSpace(w.Body.String()), msg)
104+
if c.data == "" {
105+
s.Equal("\"\"", strings.TrimSpace(w.Body.String()), msg)
107106
} else {
108107
s.Equal(fmt.Sprintf("%v", c.data), strings.TrimSpace(w.Body.String()), msg)
109108
}
@@ -146,7 +145,7 @@ func (s *TestUtilSuite) TestWrapHandler() {
146145
case "POST":
147146
return errortypes.NewHTTPError(400, "test")
148147
}
149-
_ = EncodeResponse(rw, 200, "test")
148+
_ = SendResponse(rw, 200, "test")
150149
return nil
151150
}
152151
cases := []struct {
@@ -201,7 +200,7 @@ func (t *testStruct) validate(registry strfmt.Registry) error {
201200

202201
func (t *testStruct) String() string {
203202
if t == nil {
204-
return ""
203+
return "null"
205204
}
206205
return fmt.Sprintf("{\"A\":%d}", t.A)
207206
}

supernode/server/handler.go

Lines changed: 0 additions & 41 deletions
This file was deleted.

supernode/server/preheat_bridge.go

Lines changed: 13 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,27 @@ package server
1818

1919
import (
2020
"context"
21-
"encoding/json"
22-
"io"
2321
"net/http"
2422

2523
"github.com/dragonflyoss/Dragonfly/apis/types"
2624
"github.com/dragonflyoss/Dragonfly/pkg/errortypes"
25+
"github.com/dragonflyoss/Dragonfly/supernode/server/api"
2726

2827
"github.com/go-openapi/strfmt"
2928
"github.com/gorilla/mux"
30-
"github.com/sirupsen/logrus"
3129
)
3230

3331
// ---------------------------------------------------------------------------
3432
// handlers of preheat http apis
3533

3634
func (s *Server) createPreheatTask(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
3735
request := &types.PreheatCreateRequest{}
38-
if err := parseRequest(req.Body, request, request.Validate); err != nil {
36+
if err := api.ParseJSONRequest(req.Body, request, request.Validate); err != nil {
3937
return err
4038
}
4139
preheatID, err := s.PreheatMgr.Create(ctx, request)
4240
if err != nil {
43-
return err
41+
return httpErr(err)
4442
}
4543
resp := types.PreheatCreateResponse{ID: preheatID}
4644
return EncodeResponse(rw, http.StatusCreated, resp)
@@ -49,7 +47,7 @@ func (s *Server) createPreheatTask(ctx context.Context, rw http.ResponseWriter,
4947
func (s *Server) getAllPreheatTasks(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
5048
tasks, err := s.PreheatMgr.GetAll(ctx)
5149
if err != nil {
52-
return err
50+
return httpErr(err)
5351
}
5452
return EncodeResponse(rw, http.StatusOK, tasks)
5553
}
@@ -58,7 +56,7 @@ func (s *Server) getPreheatTask(ctx context.Context, rw http.ResponseWriter, req
5856
id := mux.Vars(req)["id"]
5957
task, err := s.PreheatMgr.Get(ctx, id)
6058
if err != nil {
61-
return err
59+
return httpErr(err)
6260
}
6361
resp := types.PreheatInfo{
6462
ID: task.ID,
@@ -72,84 +70,27 @@ func (s *Server) getPreheatTask(ctx context.Context, rw http.ResponseWriter, req
7270
func (s *Server) deletePreheatTask(ctx context.Context, rw http.ResponseWriter, req *http.Request) error {
7371
id := mux.Vars(req)["id"]
7472
if err := s.PreheatMgr.Delete(ctx, id); err != nil {
75-
return err
73+
return httpErr(err)
7674
}
7775
return EncodeResponse(rw, http.StatusOK, true)
7876
}
7977

8078
// ---------------------------------------------------------------------------
8179
// helper functions
8280

83-
type validateFunc func(registry strfmt.Registry) error
84-
85-
func parseRequest(body io.Reader, request interface{}, validator validateFunc) error {
86-
if err := json.NewDecoder(body).Decode(request); err != nil {
87-
if err == io.EOF {
88-
return errortypes.New(http.StatusBadRequest, "empty body")
89-
}
90-
return errortypes.New(http.StatusBadRequest, err.Error())
91-
}
92-
if validator != nil {
93-
if err := validator(strfmt.NewFormats()); err != nil {
94-
return errortypes.New(http.StatusBadRequest, err.Error())
95-
}
81+
func httpErr(err error) error {
82+
if e, ok := err.(*errortypes.DfError); ok {
83+
return errortypes.NewHTTPError(e.Code, e.Msg)
9684
}
97-
return nil
85+
return err
9886
}
9987

100-
// initPreheatHandlers register preheat apis
101-
func initPreheatHandlers(s *Server, r *mux.Router) {
102-
handlers := []*HandlerSpec{
88+
// preheatHandlers returns all the preheats handlers.
89+
func preheatHandlers(s *Server) []*api.HandlerSpec {
90+
return []*api.HandlerSpec{
10391
{Method: http.MethodPost, Path: "/preheats", HandlerFunc: s.createPreheatTask},
10492
{Method: http.MethodGet, Path: "/preheats", HandlerFunc: s.getAllPreheatTasks},
10593
{Method: http.MethodGet, Path: "/preheats/{id}", HandlerFunc: s.getPreheatTask},
10694
{Method: http.MethodDelete, Path: "/preheats/{id}", HandlerFunc: s.deletePreheatTask},
10795
}
108-
// register API
109-
for _, h := range handlers {
110-
if h != nil {
111-
r.Path(versionMatcher + h.Path).Methods(h.Method).
112-
Handler(m.instrumentHandler(h.Path, postPreheatHandler(h.HandlerFunc)))
113-
r.Path("/api/v1" + h.Path).Methods(h.Method).
114-
Handler(m.instrumentHandler(h.Path, postPreheatHandler(h.HandlerFunc)))
115-
r.Path(h.Path).Methods(h.Method).
116-
Handler(m.instrumentHandler(h.Path, postPreheatHandler(h.HandlerFunc)))
117-
}
118-
}
119-
}
120-
121-
func postPreheatHandler(h Handler) http.HandlerFunc {
122-
pctx := context.Background()
123-
124-
return func(w http.ResponseWriter, req *http.Request) {
125-
ctx, cancel := context.WithCancel(pctx)
126-
defer cancel()
127-
128-
// Start to handle request.
129-
err := h(ctx, w, req)
130-
if err != nil {
131-
// Handle error if request handling fails.
132-
handlePreheatErrorResponse(w, err)
133-
}
134-
logrus.Debugf("%s %v err:%v", req.Method, req.URL, err)
135-
}
136-
}
137-
138-
func handlePreheatErrorResponse(w http.ResponseWriter, err error) {
139-
var (
140-
code int
141-
errMsg string
142-
)
143-
144-
// By default, daemon side returns code 500 if error happens.
145-
code = http.StatusInternalServerError
146-
if e, ok := err.(*errortypes.DfError); ok {
147-
code = e.Code
148-
errMsg = e.Msg
149-
}
150-
151-
_ = EncodeResponse(w, code, types.ErrorResponse{
152-
Code: int64(code),
153-
Message: errMsg,
154-
})
15596
}

0 commit comments

Comments
 (0)