diff --git a/cli/cmd/flashnode.go b/cli/cmd/flashnode.go index caad870df..27d69e533 100644 --- a/cli/cmd/flashnode.go +++ b/cli/cmd/flashnode.go @@ -127,7 +127,7 @@ func newCmdFlashNodeHTTPStat(_ *master.MasterClient) *cobra.Command { Short: "show flashnode stat", Args: cobra.MinimumNArgs(1), RunE: func(_ *cobra.Command, args []string) (err error) { - stat, err := httpclient.New().WithAddr(addr2Prof(args[0])).FlashNode().Stat() + stat, err := httpclient.New().Addr(addr2Prof(args[0])).FlashNode().Stat() if err != nil { return } @@ -145,13 +145,13 @@ func newCmdFlashNodeHTTPEvict(_ *master.MasterClient) *cobra.Command { RunE: func(_ *cobra.Command, args []string) (err error) { addr := args[0] if len(args) == 1 { - if err = httpclient.New().WithAddr(addr2Prof(addr)).FlashNode().EvictAll(); err == nil { + if err = httpclient.New().Addr(addr2Prof(addr)).FlashNode().EvictAll(); err == nil { stdoutlnf("%s evicts all [OK]", addr) } return } volume := args[1] - if err = httpclient.New().WithAddr(addr2Prof(addr)).FlashNode().EvictVol(volume); err == nil { + if err = httpclient.New().Addr(addr2Prof(addr)).FlashNode().EvictVol(volume); err == nil { stdoutlnf("%s evicts volume(%s) [OK]", addr, volume) } return @@ -173,7 +173,7 @@ func showFlashNodesView(flashNodeViewInfos []*proto.FlashNodeViewInfo, showStat hitRate, evicts, limit := "N/A", "N/A", "N/A" if fn.IsActive && fn.IsEnable { - if stat, e := client.WithAddr(addr2Prof(fn.Addr)).FlashNode().Stat(); e == nil { + if stat, e := client.Addr(addr2Prof(fn.Addr)).FlashNode().Stat(); e == nil { hitRate = fmt.Sprintf("%.2f%%", stat.CacheStatus.HitRate*100) evicts = strconv.Itoa(stat.CacheStatus.Evicts) limit = strconv.FormatUint(stat.NodeLimit, 10) diff --git a/flashnode/serve_http_test.go b/flashnode/serve_http_test.go index dac2a7f86..0f487de53 100644 --- a/flashnode/serve_http_test.go +++ b/flashnode/serve_http_test.go @@ -31,27 +31,27 @@ func testHTTP(t *testing.T) { } func testHTTPStat(t *testing.T) { - st, err := httpCli.WithAddr(httpServer.Addr).FlashNode().Stat() + st, err := httpCli.Addr(httpServer.Addr).FlashNode().Stat() require.NoError(t, err) t.Logf("node status %+v", st) t.Logf("cache status %+v", *st.CacheStatus) } func testHTTPEvictVol(t *testing.T) { - require.Error(t, httpCli.WithAddr(httpServer.Addr).FlashNode().EvictVol("")) - st, err := httpCli.WithAddr(httpServer.Addr).FlashNode().Stat() + require.Error(t, httpCli.Addr(httpServer.Addr).FlashNode().EvictVol("")) + st, err := httpCli.Addr(httpServer.Addr).FlashNode().Stat() require.NoError(t, err) require.Equal(t, 1, len(st.CacheStatus.Keys)) require.Equal(t, cachengine.GenCacheBlockKey(_volume, _inode, _offset, _version), st.CacheStatus.Keys[0]) t.Logf("cache status before evicted, %+v", *st.CacheStatus) - require.NoError(t, httpCli.WithAddr(httpServer.Addr).FlashNode().EvictVol(_volume)) - st, err = httpCli.WithAddr(httpServer.Addr).FlashNode().Stat() + require.NoError(t, httpCli.Addr(httpServer.Addr).FlashNode().EvictVol(_volume)) + st, err = httpCli.Addr(httpServer.Addr).FlashNode().Stat() require.NoError(t, err) require.Equal(t, 0, len(st.CacheStatus.Keys)) t.Logf("cache status after evicted, %+v", *st.CacheStatus) } func testHTTPEvictAll(t *testing.T) { - require.NoError(t, httpCli.WithAddr(httpServer.Addr).FlashNode().EvictAll()) + require.NoError(t, httpCli.Addr(httpServer.Addr).FlashNode().EvictAll()) } diff --git a/sdk/httpclient/client.go b/sdk/httpclient/client.go index 33f5422c3..0a5b8ce7d 100644 --- a/sdk/httpclient/client.go +++ b/sdk/httpclient/client.go @@ -24,11 +24,13 @@ import ( "time" "github.com/cubefs/cubefs/proto" + "github.com/cubefs/cubefs/util/compressor" "github.com/cubefs/cubefs/util/log" ) const ( - requestTimeout = 30 * time.Second + get = http.MethodGet + post = http.MethodPost ) var ErrNoAddress = errors.New("no address") @@ -37,31 +39,58 @@ type Client struct { http.Client schema string addr []string + header map[string]string } func New() *Client { return &Client{schema: "http"} } -func (c *Client) WithAddr(addresses ...string) *Client { +func (c *Client) Addr(addresses ...string) *Client { c.addr = addresses[:] return c } -func (c *Client) WithTimeout(timeout time.Duration) *Client { +func (c *Client) Timeout(timeout time.Duration) *Client { c.Client.Timeout = timeout return c } -func (c *Client) WithSSL(ssl bool) *Client { +func (c *Client) SSL(ssl bool) *Client { + c.schema = "http" if ssl { c.schema = "https" - } else { - c.schema = "http" } return c } +// WithHeader return a new Client. +func (c *Client) WithHeader(headers map[string]string, added ...string) *Client { + if len(added)%2 == 1 { + added = added[:len(added)-1] + } + nc := &Client{ + Client: c.Client, + schema: c.schema, + addr: c.addr[:], + header: make(map[string]string, len(c.header)), + } + for k, v := range c.header { + nc.header[k] = v + } + for k, v := range headers { + nc.header[k] = v + } + for idx := 0; idx < len(added); idx += 2 { + nc.header[added[idx]] = added[idx+1] + } + return nc +} + +func (c *Client) EncodingGzip() *Client { + return c.WithHeader(nil, "x-cfs-Accept-Encoding", compressor.EncodingGzip) +} + func (c *Client) serve(r *request) (data []byte, err error) { err = ErrNoAddress for _, addr := range c.addr { @@ -74,6 +103,9 @@ func (c *Client) serve(r *request) (data []byte, err error) { } req.Header.Set("Content-Type", "application/json") req.Header.Set("Connection", "close") + for k, v := range c.header { + req.Header.Set(k, v) + } for k, v := range r.header { req.Header.Set(k, v) } @@ -91,36 +123,33 @@ func (c *Client) serve(r *request) (data []byte, err error) { continue } - switch resp.StatusCode { - case http.StatusOK: - body := &struct { - Code int32 `json:"code"` - Msg string `json:"msg"` - Data json.RawMessage `json:"data"` - }{} - if err = json.Unmarshal(respData, body); err != nil { - log.LogErrorf("unmarshal response body %v", err) - return nil, fmt.Errorf("unmarshal response body %v", err) - } - if body.Code != proto.ErrCodeSuccess { - log.LogWarnf("serve: body code[%d] msg[%s] data[%v] ", body.Code, body.Msg, body.Data) - if body.Code == proto.ErrCodeInternalError && len(body.Msg) > 0 { - return nil, errors.New(body.Msg) - } - return nil, proto.ParseErrorCode(body.Code) - } - return []byte(body.Data), nil - default: + if resp.StatusCode != http.StatusOK { err = fmt.Errorf("serve: url(%s) status(%d) body(%s)", resp.Request.URL.String(), resp.StatusCode, string(respData)) log.LogError(err) continue } + + respData, err = compressor.New(resp.Header.Get("x-cfs-Content-Encoding")).Decompress(respData) + if err != nil { + log.LogErrorf("decompress response body %v", err) + continue + } + reply := new(proto.HTTPReplyRaw) + if err = reply.Unmarshal(respData); err != nil { + log.LogError(err) + continue + } + if err = reply.Success(); err != nil { + log.LogError(err) + continue + } + return reply.Bytes(), nil } return } -func (c *Client) serveWith(r *request, rst interface{}) error { +func (c *Client) serveWith(rst interface{}, r *request) error { buf, err := c.serve(r) if err != nil { return err diff --git a/sdk/httpclient/flashnode.go b/sdk/httpclient/flashnode.go index 9c57d092e..5fb1b5614 100644 --- a/sdk/httpclient/flashnode.go +++ b/sdk/httpclient/flashnode.go @@ -15,8 +15,6 @@ package httpclient import ( - "net/http" - "github.com/cubefs/cubefs/proto" ) @@ -35,16 +33,16 @@ func (c *Client) FlashNode() FlashNode { } func (f *flashNode) EvictVol(volume string) error { - r := newRequest(http.MethodPost, "/evictVol") + r := newRequest(post, "/evictVol") r.params.Add("volume", volume) - return f.client.serveWith(r, nil) + return f.client.serveWith(nil, r) } func (f *flashNode) EvictAll() error { - return f.client.serveWith(newRequest(http.MethodPost, "/evictAll"), nil) + return f.client.serveWith(nil, newRequest(post, "/evictAll")) } func (f *flashNode) Stat() (st proto.FlashNodeStat, err error) { - err = f.client.serveWith(newRequest(http.MethodGet, "/stat"), &st) + err = f.client.serveWith(&st, newRequest(get, "/stat")) return } diff --git a/sdk/httpclient/request.go b/sdk/httpclient/request.go index 380538e94..352cedc78 100644 --- a/sdk/httpclient/request.go +++ b/sdk/httpclient/request.go @@ -21,6 +21,8 @@ import ( "github.com/cubefs/cubefs/proto" ) +// TODO: rename sdk/master/request.go to here. + type request struct { method string path string