-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttp.go
127 lines (109 loc) · 2.76 KB
/
http.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
package gas
import (
"fmt"
"github.com/Ox1O5/gas/consistenthash"
"io/ioutil"
"log"
"net/http"
"net/url"
"strings"
"sync"
)
const (
defaultBasePath = "/_gascache/"
defaultReplicas = 50
)
//HTTPPool implements PeerPicker for a pool of HTTP peers
type HTTPPool struct {
// this peer's base URL, e.g. "https://example.net:8000"
self string
basePath string
mu sync.Mutex
peers *consistenthash.Map
httpGetter map[string]*httpGetter
}
//NewHTTPPool initializes an HTTP Pool of peers.
func NewHTTPPool(self string) *HTTPPool {
return &HTTPPool{
self: self,
basePath: defaultBasePath,
}
}
//Log with server name
func (p *HTTPPool) Log(format string, v ...interface{}) {
log.Printf("[Sever %s]%s", p.self, fmt.Sprintf(format, v...))
}
//ServerHTTP handle all http requests
func (p *HTTPPool) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if !strings.HasPrefix(r.URL.Path, p.basePath) {
panic("HTTPPool serving unexpected: " + r.URL.Path)
}
p.Log("%s %s", r.Method, r.URL.Path)
// /<basePath>/<groupName>/<key> required
parts := strings.SplitN(r.URL.Path[len(p.basePath):], "/", 2)
if len(parts) != 2 {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
groupName := parts[0]
key := parts[1]
group := GetGroup(groupName)
if group == nil {
http.Error(w, "no such group: "+groupName, http.StatusNotFound)
return
}
view, err := group.Get(key)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Context-Type", "application/octet-stream")
w.Write(view.ByteSlice())
}
type httpGetter struct {
baseURL string
}
func (h *httpGetter) Get (group string, key string) ([]byte, error) {
u := fmt.Sprintf(
"%v%v/%v",
h.baseURL,
url.QueryEscape(group),
url.QueryEscape(key),
)
res, err := http.Get(u)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("server returned: %v", res.Status)
}
bytes, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("reading respones body: %v", err)
}
return bytes, nil
}
var _PeerGetter = (*httpGetter)(nil)
//Set updates the pool's peers
func (p *HTTPPool)Set(peers...string) {
p.mu.Lock()
defer p.mu.Unlock()
p.peers = consistenthash.NewMap(defaultReplicas, nil)
p.peers.Add(peers...)
p.httpGetter = make(map[string]*httpGetter, len(peers))
for _, peer := range peers{
p.httpGetter[peer] = &httpGetter{baseURL: peer + p.basePath}
}
}
//PickPeer picks a peer according to key
func (p *HTTPPool)PickPeer(key string) (PeerGetter, bool) {
p.mu.Lock()
defer p.mu.Unlock()
if peer := p.peers.Get(key) ; peer != "" && peer != p.self {
p.Log("Pick peer %s", peer)
return p.httpGetter[peer], true
}
return nil, false
}
var _PickPeer = (*PeerPicker)(nil)