Skip to content

Commit 41187c8

Browse files
committed
find response refactoring
1 parent bbe935d commit 41187c8

File tree

4 files changed

+178
-106
lines changed

4 files changed

+178
-106
lines changed

find/handler.go

Lines changed: 13 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,8 @@ import (
55
"net/http"
66
"strings"
77

8-
"github.com/gogo/protobuf/proto"
9-
10-
"github.com/lomik/graphite-clickhouse/carbonzipperpb"
118
"github.com/lomik/graphite-clickhouse/config"
129
"github.com/lomik/graphite-clickhouse/helper/clickhouse"
13-
"github.com/lomik/graphite-clickhouse/helper/pickle"
1410
)
1511

1612
type Handler struct {
@@ -24,16 +20,18 @@ func NewHandler(config *config.Config) *Handler {
2420
}
2521

2622
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
27-
q := r.URL.Query().Get("query")
23+
query := r.URL.Query().Get("query")
2824

29-
if strings.IndexByte(q, '\'') > -1 { // sql injection dumb fix
25+
if strings.IndexByte(query, '\'') > -1 { // sql injection dumb fix
3026
http.Error(w, "Bad request", http.StatusBadRequest)
3127
return
3228
}
3329

3430
var prefix string
3531
var err error
3632

33+
q := query
34+
3735
if h.config.ClickHouse.ExtraPrefix != "" {
3836
prefix, q, err = RemoveExtraPrefix(h.config.ClickHouse.ExtraPrefix, q)
3937
if err != nil {
@@ -42,7 +40,11 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
4240
}
4341

4442
if q == "" {
45-
h.Reply(w, r, prefix+".", "")
43+
if prefix == "" {
44+
h.Reply(w, r, NewEmptyResponse(query))
45+
} else {
46+
h.Reply(w, r, NewResponse([]byte(prefix+"."), "", query))
47+
}
4648
return
4749
}
4850
}
@@ -66,108 +68,14 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
6668
return
6769
}
6870

69-
h.Reply(w, r, string(data), prefix)
71+
h.Reply(w, r, NewResponse(data, prefix, query))
7072
}
7173

72-
func (h *Handler) Reply(w http.ResponseWriter, r *http.Request, chResponse, prefix string) {
74+
func (h *Handler) Reply(w http.ResponseWriter, r *http.Request, res *Response) {
7375
switch r.URL.Query().Get("format") {
7476
case "pickle":
75-
h.ReplyPickle(w, r, chResponse, prefix)
77+
res.WritePickle(w)
7678
case "protobuf":
77-
h.ReplyProtobuf(w, r, chResponse, prefix)
78-
}
79-
}
80-
81-
func (h *Handler) ReplyPickle(w http.ResponseWriter, r *http.Request, chResponse, prefix string) {
82-
rows := strings.Split(string(chResponse), "\n")
83-
84-
if len(rows) == 0 { // empty
85-
w.Write(pickle.EmptyList)
86-
return
87-
}
88-
89-
p := pickle.NewWriter(w)
90-
91-
p.List()
92-
93-
var metricPath string
94-
var isLeaf bool
95-
96-
for _, metricPath = range rows {
97-
if len(metricPath) == 0 {
98-
continue
99-
}
100-
101-
if prefix != "" {
102-
metricPath = prefix + "." + metricPath
103-
}
104-
105-
if metricPath[len(metricPath)-1] == '.' {
106-
metricPath = metricPath[:len(metricPath)-1]
107-
isLeaf = false
108-
} else {
109-
isLeaf = true
110-
}
111-
112-
p.Dict()
113-
114-
p.String("metric_path")
115-
p.String(metricPath)
116-
p.SetItem()
117-
118-
p.String("isLeaf")
119-
p.Bool(isLeaf)
120-
p.SetItem()
121-
122-
p.Append()
79+
res.WriteProtobuf(w)
12380
}
124-
125-
p.Stop()
126-
}
127-
128-
func (h *Handler) ReplyProtobuf(w http.ResponseWriter, r *http.Request, chResponse, prefix string) {
129-
rows := strings.Split(string(chResponse), "\n")
130-
131-
name := r.URL.Query().Get("query")
132-
133-
// message GlobMatch {
134-
// required string path = 1;
135-
// required bool isLeaf = 2;
136-
// }
137-
138-
// message GlobResponse {
139-
// required string name = 1;
140-
// repeated GlobMatch matches = 2;
141-
// }
142-
143-
var response carbonzipperpb.GlobResponse
144-
response.Name = proto.String(name)
145-
146-
var metricPath string
147-
var isLeaf bool
148-
149-
for _, metricPath = range rows {
150-
if len(metricPath) == 0 {
151-
continue
152-
}
153-
154-
if prefix != "" {
155-
metricPath = prefix + "." + metricPath
156-
}
157-
158-
if metricPath[len(metricPath)-1] == '.' {
159-
metricPath = metricPath[:len(metricPath)-1]
160-
isLeaf = false
161-
} else {
162-
isLeaf = true
163-
}
164-
165-
response.Matches = append(response.Matches, &carbonzipperpb.GlobMatch{
166-
Path: proto.String(metricPath),
167-
IsLeaf: &isLeaf,
168-
})
169-
}
170-
171-
body, _ := proto.Marshal(&response)
172-
w.Write(body)
17381
}

find/handler_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func TestFind(t *testing.T) {
4747
)
4848

4949
logger := zap.New(zap.NewJSONEncoder())
50-
logger.SetLevel(-1000)
50+
// logger.SetLevel(-1000)
5151
r = r.WithContext(context.WithValue(r.Context(), "logger", logger))
5252
handler.ServeHTTP(w, r)
5353

find/response.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package find
2+
3+
import (
4+
"bytes"
5+
"io"
6+
"unsafe"
7+
8+
"github.com/gogo/protobuf/proto"
9+
10+
"github.com/lomik/graphite-clickhouse/carbonzipperpb"
11+
"github.com/lomik/graphite-clickhouse/helper/pickle"
12+
)
13+
14+
// Find result
15+
type Response struct {
16+
body []byte // raw bytes from clickhouse
17+
extraPrefix string
18+
query string
19+
}
20+
21+
func NewResponse(body []byte, extraPrefix string, query string) *Response {
22+
if body == nil {
23+
body = []byte{}
24+
}
25+
return &Response{
26+
body: body,
27+
extraPrefix: extraPrefix,
28+
}
29+
}
30+
31+
func NewEmptyResponse(query string) *Response {
32+
return NewResponse(nil, "", query)
33+
}
34+
35+
func unsafeString(b []byte) string {
36+
return *(*string)(unsafe.Pointer(&b))
37+
}
38+
39+
func (r *Response) WritePickle(w io.Writer) error {
40+
rows := bytes.Split(r.body, []byte{'\n'})
41+
42+
if len(rows) == 0 { // empty
43+
w.Write(pickle.EmptyList)
44+
return nil
45+
}
46+
47+
p := pickle.NewWriter(w)
48+
49+
p.List()
50+
51+
var row []byte
52+
var isLeaf bool
53+
var metricPath string
54+
55+
for _, row = range rows {
56+
if len(row) == 0 {
57+
continue
58+
}
59+
60+
isLeaf = true
61+
if row[len(row)-1] == '.' {
62+
row = row[:len(row)-1]
63+
isLeaf = false
64+
}
65+
66+
if r.extraPrefix != "" {
67+
metricPath = r.extraPrefix + "." + unsafeString(row)
68+
} else {
69+
metricPath = unsafeString(row)
70+
}
71+
72+
p.Dict()
73+
74+
p.String("metric_path")
75+
p.String(metricPath)
76+
p.SetItem()
77+
78+
p.String("isLeaf")
79+
p.Bool(isLeaf)
80+
p.SetItem()
81+
82+
p.Append()
83+
}
84+
85+
p.Stop()
86+
return nil
87+
}
88+
89+
func (r *Response) WriteProtobuf(w io.Writer) error {
90+
rows := bytes.Split(r.body, []byte{'\n'})
91+
92+
if len(rows) == 0 { // empty
93+
return nil
94+
}
95+
96+
// message GlobMatch {
97+
// required string path = 1;
98+
// required bool isLeaf = 2;
99+
// }
100+
101+
// message GlobResponse {
102+
// required string name = 1;
103+
// repeated GlobMatch matches = 2;
104+
// }
105+
106+
var response carbonzipperpb.GlobResponse
107+
response.Name = proto.String(r.query)
108+
109+
var metricPath string
110+
var isLeaf bool
111+
var row []byte
112+
113+
for _, row = range rows {
114+
if len(row) == 0 {
115+
continue
116+
}
117+
118+
isLeaf = true
119+
if row[len(row)-1] == '.' {
120+
row = row[:len(row)-1]
121+
isLeaf = false
122+
}
123+
124+
if r.extraPrefix != "" {
125+
metricPath = r.extraPrefix + "." + string(row)
126+
} else {
127+
metricPath = string(row)
128+
}
129+
130+
response.Matches = append(response.Matches, &carbonzipperpb.GlobMatch{
131+
Path: proto.String(metricPath),
132+
IsLeaf: &isLeaf,
133+
})
134+
}
135+
136+
body, err := proto.Marshal(&response)
137+
if err != nil {
138+
return err
139+
}
140+
141+
w.Write(body)
142+
143+
return nil
144+
}

find/response_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package find
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
)
7+
8+
func TestPickleEmpty(t *testing.T) {
9+
10+
r := NewResponse([]byte{}, "", "")
11+
12+
w := bytes.NewBuffer(nil)
13+
14+
r.WritePickle(w)
15+
16+
if w.String() != "(l." {
17+
t.FailNow()
18+
}
19+
// fmt.Printf("%#v\n", string(w.Bytes()))
20+
}

0 commit comments

Comments
 (0)