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

Side by Side Diff: server/prpc/encoding.go

Issue 1605363002: common/prpc, tools/cmd/cproto: prpc client (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-go@master
Patch Set: use text template for ast generation Created 4 years, 11 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
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package prpc 5 package prpc
6 6
7 // This file implements encoding of RPC results to HTTP responses. 7 // This file implements encoding of RPC results to HTTP responses.
8 8
9 import ( 9 import (
10 "bytes" 10 "bytes"
11 "io" 11 "io"
12 "net/http" 12 "net/http"
13 "sort" 13 "sort"
14 "strconv"
14 15
15 "github.com/golang/protobuf/jsonpb" 16 "github.com/golang/protobuf/jsonpb"
16 "github.com/golang/protobuf/proto" 17 "github.com/golang/protobuf/proto"
17 "golang.org/x/net/context" 18 "golang.org/x/net/context"
18 "google.golang.org/grpc" 19 "google.golang.org/grpc"
19 "google.golang.org/grpc/codes" 20 "google.golang.org/grpc/codes"
20 21
22 "github.com/luci/luci-go/common/errors"
21 "github.com/luci/luci-go/common/logging" 23 "github.com/luci/luci-go/common/logging"
24 "github.com/luci/luci-go/common/prpc"
22 ) 25 )
23 26
24 const ( 27 const (
25 » headerAccept = "Accept" 28 » headerAccept = "Accept"
29 » headerGRPCCode = "X-Prpc-Grpc-Code"
dnj 2016/01/21 07:44:28 Make this exported and reuse it in client? No poin
nodir 2016/01/22 00:47:24 Done, but vice-versa: common cannot import server
26 ) 30 )
27 31
28 // responseFormat returns the format to be used in a response. 32 // responseFormat returns the format to be used in a response.
29 // Can return only formatBinary (preferred), formatJSONPB or formatText. 33 // Can return only formatBinary (preferred), formatJSONPB or formatText.
30 // In case of an error, format is undefined and the error has an HTTP status. 34 // In case of an error, format is undefined and the error has an HTTP status.
31 func responseFormat(acceptHeader string) (format, *httpError) { 35 func responseFormat(acceptHeader string) (format, *httpError) {
32 if acceptHeader == "" { 36 if acceptHeader == "" {
33 return formatBinary, nil 37 return formatBinary, nil
34 } 38 }
35 39
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
107 res = buf.Bytes() 111 res = buf.Bytes()
108 } 112 }
109 if err != nil { 113 if err != nil {
110 return err 114 return err
111 } 115 }
112 w.Header().Set(headerContentType, contentType) 116 w.Header().Set(headerContentType, contentType)
113 _, err = w.Write(res) 117 _, err = w.Write(res)
114 return err 118 return err
115 } 119 }
116 120
117 // codeToStatus maps gRPC codes to HTTP statuses.
118 // This map may need to be corrected when
119 // https://github.com/grpc/grpc-common/issues/210
120 // is closed.
121 var codeToStatus = map[codes.Code]int{
122 codes.OK: http.StatusOK,
123 codes.Canceled: http.StatusNoContent,
124 codes.Unknown: http.StatusInternalServerError,
125 codes.InvalidArgument: http.StatusBadRequest,
126 codes.DeadlineExceeded: http.StatusServiceUnavailable,
127 codes.NotFound: http.StatusNotFound,
128 codes.AlreadyExists: http.StatusConflict,
129 codes.PermissionDenied: http.StatusForbidden,
130 codes.Unauthenticated: http.StatusUnauthorized,
131 codes.ResourceExhausted: http.StatusServiceUnavailable,
132 codes.FailedPrecondition: http.StatusPreconditionFailed,
133 codes.Aborted: http.StatusInternalServerError,
134 codes.OutOfRange: http.StatusBadRequest,
135 codes.Unimplemented: http.StatusNotImplemented,
136 codes.Internal: http.StatusInternalServerError,
137 codes.Unavailable: http.StatusServiceUnavailable,
138 codes.DataLoss: http.StatusInternalServerError,
139 }
140
141 // ErrorStatus returns HTTP status for an error. 121 // ErrorStatus returns HTTP status for an error.
142 // In particular, it maps gRPC codes to HTTP statuses. 122 // In particular, it maps gRPC codes to HTTP statuses.
143 // Status of nil is 200. 123 // Status of nil is 200.
144 // 124 //
145 // See also grpc.Code. 125 // See also grpc.Code.
146 func ErrorStatus(err error) int { 126 func ErrorStatus(err error) int {
147 if err, ok := err.(*httpError); ok { 127 if err, ok := err.(*httpError); ok {
148 return err.status 128 return err.status
149 } 129 }
150 130
151 » status, ok := codeToStatus[grpc.Code(err)] 131 » status, ok := prpc.CodeStatus(grpc.Code(err))
152 if !ok { 132 if !ok {
153 status = http.StatusInternalServerError 133 status = http.StatusInternalServerError
154 } 134 }
155 return status 135 return status
156 } 136 }
157 137
158 // ErrorDesc returns the error description of err if it was produced by pRPC or gRPC. 138 // ErrorDesc returns the error description of err if it was produced by pRPC or gRPC.
159 // Otherwise, it returns err.Error() or empty string when err is nil. 139 // Otherwise, it returns err.Error() or empty string when err is nil.
160 // 140 //
161 // See also grpc.ErrorDesc. 141 // See also grpc.ErrorDesc.
(...skipping 17 matching lines...) Expand all
179 func writeError(c context.Context, w http.ResponseWriter, err error) { 159 func writeError(c context.Context, w http.ResponseWriter, err error) {
180 if err == nil { 160 if err == nil {
181 panic("err is nil") 161 panic("err is nil")
182 } 162 }
183 163
184 status := ErrorStatus(err) 164 status := ErrorStatus(err)
185 if status >= 500 { 165 if status >= 500 {
186 logging.Errorf(c, "HTTP %d: %s", status, ErrorDesc(err)) 166 logging.Errorf(c, "HTTP %d: %s", status, ErrorDesc(err))
187 } 167 }
188 168
169 w.Header().Set(headerGRPCCode, strconv.Itoa(int(grpcCode(err))))
189 w.Header().Set(headerContentType, "text/plain") 170 w.Header().Set(headerContentType, "text/plain")
190 w.WriteHeader(status) 171 w.WriteHeader(status)
191 172
192 var body string 173 var body string
193 if status == http.StatusInternalServerError { 174 if status == http.StatusInternalServerError {
194 body = "Internal server error" 175 body = "Internal server error"
195 } else { 176 } else {
196 body = ErrorDesc(err) 177 body = ErrorDesc(err)
197 } 178 }
198 if _, err := io.WriteString(w, body+"\n"); err != nil { 179 if _, err := io.WriteString(w, body+"\n"); err != nil {
199 logging.Errorf(c, "could not write error: %s", err) 180 logging.Errorf(c, "could not write error: %s", err)
200 } 181 }
201 } 182 }
202 183
184 // returns the most appropriate gRPC code for an error.
185 func grpcCode(err error) codes.Code {
186 if err == nil {
187 return codes.OK
188 }
189
190 for ; err != nil; err = errors.Unwrap(err) {
191 if code := grpc.Code(err); code != codes.Unknown {
192 return code
193 }
194
195 switch err {
196
197 case context.Canceled:
198 return codes.Canceled
199
200 case context.DeadlineExceeded:
201 return codes.DeadlineExceeded
202
203 }
204 }
205 return codes.Unknown
206 }
207
203 func assert(condition bool) { 208 func assert(condition bool) {
204 if !condition { 209 if !condition {
205 panicf("assertion failed") 210 panicf("assertion failed")
206 } 211 }
207 } 212 }
OLDNEW
« common/prpc/options.go ('K') | « common/retry/limited.go ('k') | server/prpc/error.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698