Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(134)

Side by Side Diff: go/src/infra/libs/gitiles/gitiles.go

Issue 662113003: Drover's back, baby! (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git/+/master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 package gitiles
2
3 import "bufio"
4 import "bytes"
5 import "encoding/base64"
6 import "encoding/json"
7 import "fmt"
8 import "io"
9 import "io/ioutil"
10 import "net/http"
11 import "reflect"
12 import "strings"
13
14 // Gitiles wraps one Gitiles server. Use NewGitiles to create one.
15 type Gitiles struct {
16 url string
17 requests chan<- request
18 }
19
20 // URL returns the base url for this Gitiles service wrapper
21 func (g Gitiles) URL() string { return g.url }
22
23 // NewGitiles creates a new Gitiles instance for the given |url|, and a maximum
24 // number of concurrent requests.
25 func NewGitiles(url string, maxConnections int) *Gitiles {
26 // TODO(riannucci): make a separate http.Client?
27 // TODO(riannucci): have a way to destroy the Gitiles instance?
28 requestChan := make(chan request, maxConnections)
29 ret := &Gitiles{
30 url: strings.TrimRight(url, "/"),
31 requests: requestChan,
32 }
33 for i := 0; i < maxConnections; i++ {
34 go ret.requestProcessor(requestChan)
35 }
36 return ret
37 }
38
39 // StatusError is a simple error type which wraps a Http StatusCode.
40 type StatusError int
41
42 // Bad returns true iff the status code is not 2XX
43 func (s StatusError) Bad() bool { return !(s >= 200 && s < 300) }
M-A Ruel 2014/10/18 00:47:06 return s < 200 || s >= 300 is actually shorter and
iannucci 2014/10/20 21:11:58 Done.
44 func (s StatusError) Error() string { return fmt.Sprintf("got status code %d", s ) }
45
46 // TextResult is object you get back from JSON(). If Err is nil, DataPtr
47 // contains an instance of whatever type you passed to |target|, unmarshalled
48 // from the json response from gitiles.
49 type TextResult struct {
50 Err error
51 Data []byte
52 }
53
54 // JSONResult is object you get back from JSON(). If Err is nil, DataPtr
55 // contains an instance of whatever type you passed to |target|, unmarshalled
56 // from the json response from gitiles.
57 type JSONResult struct {
M-A Ruel 2014/10/18 00:47:06 Personally I had used specialized handlers, e.g. l
iannucci 2014/10/20 21:11:58 Made this struct private. no reason to expose to c
58 Err error
59 DataPtr interface{}
60 }
61
62 // JSON Returns a channel to a JSONResult where DataPtr is a pointer to
63 // a datatype described by target.
64 //
65 // Example:
66 // data := map[string]int{}
67 // result := g.JSON(data, "some", "url", "pieces")
68 // if result.Err != nil { panic(result.Err) }
69 // data = *result.DataPtr.(*map[string]int)
70 func (g *Gitiles) JSON(target interface{}, pieces ...string) <-chan JSONResult {
71 reply := make(chan JSONResult, 1)
72 g.requests <- jsonRequest{
73 strings.Join(pieces, "/"),
74 reflect.TypeOf(target),
75 reply,
76 }
77 return reply
78 }
79
80 // Text returns a channel to a TextResult. It will load the given path with
81 // format=TEXT and return the decoded data.
82 func (g *Gitiles) Text(pieces ...string) <-chan TextResult {
83 reply := make(chan TextResult, 1)
84 g.requests <- textRequest{
85 strings.Join(pieces, "/"),
86 reply,
87 }
88 return reply
89 }
90
91 // private decoders
92
93 type request interface {
94 Process(rsp *http.Response, err error)
95 Method() string
96 URLPath() string
97 Body() io.Reader
98 }
99
100 type jsonRequest struct {
101 urlPath string
102 typ reflect.Type
103 resultChan chan<- JSONResult
104 }
105
106 func (j jsonRequest) URLPath() string { return j.urlPath + "?format=JSON" }
107 func (j jsonRequest) Method() string { return "GET" }
108 func (j jsonRequest) Body() io.Reader { return nil }
109
110 func (j jsonRequest) Process(resp *http.Response, err error) {
111 var rslt JSONResult
112 if err != nil {
113 rslt.Err = err
114 } else {
115 bufreader := bufio.NewReader(resp.Body)
116 firstFour, err := bufreader.Peek(4)
117 if err != nil {
118 rslt.Err = err
119 } else {
120 if bytes.Equal(firstFour, []byte(")]}'")) {
121 bufreader.Read(make([]byte, 4))
122 }
123
124 data := reflect.New(j.typ).Interface()
125 err = json.NewDecoder(bufreader).Decode(data)
126 if err != nil {
127 rslt.Err = err
128 } else {
129 rslt.DataPtr = data
130 }
131 }
132 }
133 j.resultChan <- rslt
134 }
135
136 type textRequest struct {
137 urlPath string
138 resultChan chan<- TextResult
139 }
140
141 func (t textRequest) URLPath() string { return t.urlPath + "?format=TEXT" }
142 func (t textRequest) Method() string { return "GET" }
143 func (t textRequest) Body() io.Reader { return nil }
144
145 func (t textRequest) internalProcess(rsp *http.Response, err error) ([]byte, err or) {
146 if err != nil {
147 return nil, err
148 }
149 return ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, rsp.Body))
150 }
151
152 func (t textRequest) Process(rsp *http.Response, err error) {
153 if data, err := t.internalProcess(rsp, err); err != nil {
154 t.resultChan <- TextResult{Err: err}
155 } else {
156 t.resultChan <- TextResult{Data: data}
157 }
158 }
159
160 func (g *Gitiles) requestProcessor(queue <-chan request) {
161 // Lanched as a goroutine to avoid blocking the request processor.
M-A Ruel 2014/10/18 00:47:06 Launched
iannucci 2014/10/20 21:11:58 Done.
162 for r := range queue {
163 r := r
164 if r == nil {
165 continue
166 }
167
168 var req *http.Request
169 req, err := http.NewRequest(r.Method(), g.url+"/"+r.URLPath(), r .Body())
170 if err != nil {
171 r.Process(nil, err)
172 continue
173 }
174
175 rsp, err := http.DefaultClient.Do(req)
176 if err != nil {
177 r.Process(nil, err)
178 continue
179 }
180
181 e := StatusError(rsp.StatusCode)
182 if e.Bad() {
183 err = &e
M-A Ruel 2014/10/18 00:47:06 why not? err = e
iannucci 2014/10/20 21:11:58 Oops, done.
184 }
185 func() {
186 defer rsp.Body.Close()
187 r.Process(rsp, err)
188 }()
189 }
190 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698