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

Commit 7fc0988

Browse files
add implementation of http/https protocol
Signed-off-by: allen.wq <[email protected]>
1 parent 54f0a67 commit 7fc0988

File tree

6 files changed

+522
-0
lines changed

6 files changed

+522
-0
lines changed

pkg/protocol/http/http.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright The Dragonfly Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package http
18+
19+
import (
20+
"fmt"
21+
"net/http"
22+
23+
"github.com/dragonflyoss/Dragonfly/pkg/errortypes"
24+
"github.com/dragonflyoss/Dragonfly/pkg/protocol"
25+
)
26+
27+
var (
28+
// DefaultClientOpt is default implementation of ClientOpt.
29+
DefaultClientOpt = &ClientOpt{
30+
transport: http.DefaultTransport,
31+
}
32+
33+
// DefaultClient is default implementation of Client.
34+
DefaultClient = &Client{
35+
client: &http.Client{Transport: DefaultClientOpt.transport},
36+
transport: DefaultClientOpt.transport,
37+
}
38+
)
39+
40+
const (
41+
// http protocol name
42+
ProtocolHTTPName = "http"
43+
44+
// https protocol name
45+
ProtocolHTTPSName = "https"
46+
)
47+
48+
func init() {
49+
protocol.RegisterProtocol(ProtocolHTTPName, &ClientBuilder{})
50+
protocol.RegisterProtocol(ProtocolHTTPSName, &ClientBuilder{supportHTTPS: true})
51+
}
52+
53+
// ClientOpt is the argument of NewProtocolClient.
54+
type ClientOpt struct {
55+
transport http.RoundTripper
56+
}
57+
58+
var _ protocol.Client = &Client{}
59+
60+
// Client is an implementation of protocol.Client for http protocol.
61+
type Client struct {
62+
client *http.Client
63+
transport http.RoundTripper
64+
}
65+
66+
func (cli *Client) GetResource(url string, md protocol.Metadata) protocol.Resource {
67+
var (
68+
hd *Headers
69+
)
70+
71+
if md != nil {
72+
h, ok := md.(*Headers)
73+
if ok {
74+
hd = h
75+
}
76+
}
77+
78+
return &Resource{
79+
url: url,
80+
hd: hd,
81+
client: cli,
82+
}
83+
}
84+
85+
// ClientBuilder is an implementation of protocol.ClientBuilder for http protocol.
86+
type ClientBuilder struct {
87+
supportHTTPS bool
88+
}
89+
90+
func (cb *ClientBuilder) NewProtocolClient(clientOpt interface{}) (protocol.Client, error) {
91+
if clientOpt == nil {
92+
if cb.supportHTTPS {
93+
return nil, fmt.Errorf("clientOpt should be set for https")
94+
}
95+
96+
clientOpt = DefaultClientOpt
97+
}
98+
99+
opt, ok := clientOpt.(*ClientOpt)
100+
if !ok {
101+
return nil, errortypes.ErrConvertFailed
102+
}
103+
104+
return &Client{
105+
client: &http.Client{Transport: opt.transport},
106+
transport: opt.transport,
107+
}, nil
108+
}

pkg/protocol/http/md.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright The Dragonfly Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package http
18+
19+
import (
20+
"net/http"
21+
22+
"github.com/dragonflyoss/Dragonfly/pkg/protocol"
23+
)
24+
25+
// NewHTTPMetaData generates an instance of protocol.Metadata.
26+
func NewHTTPMetaData() protocol.Metadata {
27+
return &Headers{
28+
Header: make(http.Header),
29+
}
30+
}
31+
32+
// Headers is an implementation of protocol.Metadata.
33+
type Headers struct {
34+
http.Header
35+
}
36+
37+
func (hd *Headers) Get(key string) (interface{}, error) {
38+
return hd.Header.Get(key), nil
39+
}
40+
41+
func (hd *Headers) Set(key string, value interface{}) {
42+
hd.Header.Set(key, value.(string))
43+
}
44+
45+
func (hd *Headers) Del(key string) {
46+
hd.Header.Del(key)
47+
}
48+
49+
func (hd *Headers) All() interface{} {
50+
return hd.Header
51+
}

pkg/protocol/http/md_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright The Dragonfly Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package http
18+
19+
import (
20+
"net/http"
21+
"testing"
22+
23+
"github.com/dragonflyoss/Dragonfly/dfget/core/helper"
24+
25+
"github.com/go-check/check"
26+
)
27+
28+
func Test(t *testing.T) {
29+
check.TestingT(t)
30+
}
31+
32+
type HTTPSuite struct {
33+
host string
34+
port int
35+
server *helper.MockFileServer
36+
}
37+
38+
func init() {
39+
check.Suite(&HTTPSuite{})
40+
}
41+
42+
func (suite *HTTPSuite) TestMd(c *check.C) {
43+
md := NewHTTPMetaData()
44+
md.Set("k1", "v1")
45+
md.Set("k2", "v2")
46+
47+
v1, err := md.Get("k1")
48+
c.Assert(err, check.IsNil)
49+
c.Assert(v1.(string), check.Equals, "v1")
50+
51+
v2, err := md.Get("k2")
52+
c.Assert(err, check.IsNil)
53+
c.Assert(v2.(string), check.Equals, "v2")
54+
55+
md.Del("k1")
56+
v1, err = md.Get("k1")
57+
c.Assert(err, check.IsNil)
58+
c.Assert(v1.(string), check.Equals, "")
59+
60+
hd, ok := md.All().(http.Header)
61+
c.Assert(ok, check.Equals, true)
62+
63+
v2 = hd.Get("k2")
64+
c.Assert(v2, check.Equals, "v2")
65+
}

pkg/protocol/http/resource.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Copyright The Dragonfly Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package http
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"io"
23+
"net/http"
24+
"strconv"
25+
"time"
26+
27+
"github.com/dragonflyoss/Dragonfly/dfget/config"
28+
"github.com/dragonflyoss/Dragonfly/pkg/protocol"
29+
)
30+
31+
// Resource is an implementation of protocol.Resource for http protocol.
32+
type Resource struct {
33+
url string
34+
hd *Headers
35+
client *Client
36+
}
37+
38+
func (rs *Resource) Read(ctx context.Context, off int64, size int64) (rc io.ReadCloser, err error) {
39+
req, err := rs.newRequest(ctx, off, size)
40+
if err != nil {
41+
return nil, err
42+
}
43+
44+
res, err := rs.doRequest(req)
45+
if err != nil {
46+
return nil, err
47+
}
48+
49+
defer func() {
50+
if err != nil && res.Body != nil {
51+
res.Body.Close()
52+
}
53+
}()
54+
55+
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusPartialContent {
56+
return nil, fmt.Errorf("respnose code is not 200 or 206")
57+
}
58+
59+
return res.Body, nil
60+
}
61+
62+
func (rs *Resource) Length(ctx context.Context) (int64, error) {
63+
timeoutCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
64+
defer cancel()
65+
66+
req, err := rs.newRequest(timeoutCtx, 0, 0)
67+
if err != nil {
68+
return 0, err
69+
}
70+
71+
res, err := rs.doRequest(req)
72+
if err != nil {
73+
return 0, err
74+
}
75+
76+
defer res.Body.Close()
77+
lenStr := res.Header.Get(config.StrContentLength)
78+
if lenStr == "" {
79+
return 0, fmt.Errorf("failed to get content length")
80+
}
81+
82+
length, err := strconv.ParseInt(lenStr, 10, 64)
83+
if err != nil {
84+
return 0, fmt.Errorf("failed to prase %s to length: %sv", lenStr, err)
85+
}
86+
87+
return length, nil
88+
}
89+
90+
func (rs *Resource) Metadata(ctx context.Context) (protocol.Metadata, error) {
91+
return rs.hd, nil
92+
}
93+
94+
func (rs *Resource) Expire(ctx context.Context) (bool, interface{}, error) {
95+
// need to implementation
96+
return false, nil, nil
97+
}
98+
99+
func (rs *Resource) Call(ctx context.Context, request interface{}) (response interface{}, err error) {
100+
return nil, protocol.ErrNotImplementation
101+
}
102+
103+
func (rs *Resource) Close() error {
104+
return nil
105+
}
106+
107+
func (rs *Resource) newRequest(ctx context.Context, off, size int64) (*http.Request, error) {
108+
// off == 0 && size == 0 means all data.
109+
if (off < 0 || size < 0) || (off > 0 && size == 0) {
110+
return nil, fmt.Errorf("invalid argument")
111+
}
112+
113+
req, err := http.NewRequest(http.MethodGet, rs.url, nil)
114+
if err != nil {
115+
return nil, err
116+
}
117+
118+
if rs.hd != nil {
119+
for k, v := range rs.hd.Header {
120+
req.Header.Set(k, v[0])
121+
}
122+
}
123+
124+
if size > 0 {
125+
req.Header.Set(config.StrRange, fmt.Sprintf("bytes=%d-%d", off, off+size-1))
126+
}
127+
128+
req = req.WithContext(ctx)
129+
return req, nil
130+
}
131+
132+
func (rs *Resource) doRequest(req *http.Request) (*http.Response, error) {
133+
return rs.client.client.Do(req)
134+
}

0 commit comments

Comments
 (0)