This repository has been archived by the owner on Feb 27, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 774
/
url.go
165 lines (134 loc) · 5.06 KB
/
url.go
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
* Copyright The Dragonfly Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package protocol
import (
"context"
"errors"
"fmt"
"io"
"sync"
)
// Metadata defines how to operate the metadata.
type Metadata interface {
Get(key string) (interface{}, error)
Set(key string, value interface{})
Del(key string)
All() interface{}
}
// Resource defines the way how to get some information from remote resource.
// An instance should bind a resource.
// Developers can implement their own Resource which could support different protocol.
type Resource interface {
// Read gets range data from the binding resource.
Read(ctx context.Context, off int64, size int64) (io.ReadCloser, error)
// Length gets the length of binding resource.
Length(ctx context.Context) (int64, error)
// Metadata gets the metadata of binding resource.
Metadata(ctx context.Context) (Metadata, error)
// Expire gets if the binding resource is expired.
Expire(ctx context.Context) (bool, interface{}, error)
// Call allows user defined request.
Call(ctx context.Context, request interface{}) (response interface{}, err error)
// Close the resource.
io.Closer
}
// Client defines how to get resource.
type Client interface {
// GetResource gets resource by argument.
GetResource(url string, md Metadata) Resource
}
// ClientBuilder defines how to create an instance of Client.
type ClientBuilder interface {
// NewProtocolClient creates an instance of Client.
// Here have a suggestion that every implementation should have
// opts with WithMapInterface(map[string]interface),
// which may be easier for configuration with config file.
NewProtocolClient(opts ...func(client Client) error) (Client, error)
}
// ClientRegister defines how to register pair <protocol, ClientBuilder>.
type ClientRegister interface {
// ClientRegister registers pair <protocol, ClientBuilder>.
RegisterProtocol(protocol string, builder ClientBuilder)
// GetClientBuilder gets the ClientBuilder by protocol.
GetClientBuilder(protocol string) (ClientBuilder, error)
}
var (
ErrNotImplementation = errors.New("not implementation")
ErrProtocolNotRegister = errors.New("protocol not register")
register = &defaultClientRegister{
registerMap: make(map[string]ClientBuilder),
}
)
// RegisterProtocol registers pair <protocol, ClientBuilder> to defaultClientRegister.
func RegisterProtocol(protocol string, builder ClientBuilder) {
register.RegisterProtocol(protocol, builder)
}
// GetClientBuilder get ClientBuilder by protocol in defaultClientRegister.
func GetClientBuilder(protocol string) (ClientBuilder, error) {
return register.GetClientBuilder(protocol)
}
// defaultClientRegister is an implementation of ClientRegister.
type defaultClientRegister struct {
sync.RWMutex
registerMap map[string]ClientBuilder
}
func (cliRegister *defaultClientRegister) RegisterProtocol(protocol string, builder ClientBuilder) {
cliRegister.Lock()
defer cliRegister.Unlock()
_, ok := cliRegister.registerMap[protocol]
if ok {
panic(fmt.Sprintf("protocol %s has been register", protocol))
}
cliRegister.registerMap[protocol] = builder
}
func (cliRegister *defaultClientRegister) GetClientBuilder(protocol string) (ClientBuilder, error) {
cliRegister.RLock()
defer cliRegister.RUnlock()
builder, ok := cliRegister.registerMap[protocol]
if !ok {
return nil, ErrProtocolNotRegister
}
return builder, nil
}
// In order to be easier for configuration with config file, we provide Register for functional options
// in which argument is map[string]interface. On the other handle, we can easy to get functional options with map interface.
// By the way, developer could create protocol client easily by protocol name and no need to care about instance of
// protocol client.
var (
newOptMapMutex sync.RWMutex
newOptMap = map[string]MapInterfaceOptFunc{}
)
type MapInterfaceOptFunc func(map[string]interface{}) func(Client) error
// RegisterMapInterfaceOptFunc registers MapInterfaceOptFunc by protocol name.
func RegisterMapInterfaceOptFunc(protocol string, withMapInterfaceOpt MapInterfaceOptFunc) {
newOptMapMutex.Lock()
defer newOptMapMutex.Unlock()
_, ok := newOptMap[protocol]
if ok {
panic(fmt.Sprintf("protocol %s has been register", protocol))
}
newOptMap[protocol] = withMapInterfaceOpt
}
// GetRegisteredMapInterfaceOptFunc get MapInterfaceOptFunc by protocol name.
func GetRegisteredMapInterfaceOptFunc(protocol string) (MapInterfaceOptFunc, error) {
newOptMapMutex.Lock()
defer newOptMapMutex.Unlock()
opt, ok := newOptMap[protocol]
if !ok {
return nil, ErrProtocolNotRegister
}
return opt, nil
}