Chromium Code Reviews| OLD | NEW | 
|---|---|
| 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 import ( | 7 import ( | 
| 8 "fmt" | |
| 9 "net/http" | 8 "net/http" | 
| 10 | 9 | 
| 11 "github.com/golang/protobuf/proto" | 10 "github.com/golang/protobuf/proto" | 
| 12 "github.com/julienschmidt/httprouter" | |
| 13 "golang.org/x/net/context" | 11 "golang.org/x/net/context" | 
| 14 "google.golang.org/grpc" | 12 "google.golang.org/grpc" | 
| 15 | 13 » "google.golang.org/grpc/codes" | 
| 16 » "github.com/luci/luci-go/server/middleware" | |
| 17 ) | 14 ) | 
| 18 | 15 | 
| 19 type method struct { | 16 type method struct { | 
| 20 service *service | 17 service *service | 
| 21 desc grpc.MethodDesc | 18 desc grpc.MethodDesc | 
| 22 } | 19 } | 
| 23 | 20 | 
| 24 func (m *method) Name() string { | |
| 25 return m.desc.MethodName | |
| 26 } | |
| 27 | |
| 28 // Handle decodes an input protobuf message from the HTTP request, | |
| 29 // delegates RPC handling to the inner implementation and | |
| 30 // encodes the output message back to the HTTP response. | |
| 31 // | |
| 32 // If the inner handler returns an error, HTTP status is determined using | |
| 33 // ErrorStatus. | |
| 34 // If the status is http.StatusInternalServerError, only "Internal server error" | |
| 35 // is printed. | |
| 36 // All errors with status >= 500 are logged. | |
| 37 func (m *method) Handle(c context.Context, w http.ResponseWriter, r *http.Reques t, _ httprouter.Params) { | |
| 38 if err := m.handle(c, w, r); err != nil { | |
| 39 writeError(c, w, err) | |
| 40 } | |
| 41 } | |
| 42 | |
| 43 // handle decodes an input protobuf message from the HTTP request, | 21 // handle decodes an input protobuf message from the HTTP request, | 
| 44 // delegates RPC handling to the inner implementation and | 22 // delegates RPC handling to the inner implementation and | 
| 45 // encodes the output message back to the HTTP response. | 23 // encodes the output message back to the HTTP response. | 
| 46 func (m *method) handle(c context.Context, w http.ResponseWriter, r *http.Reques t) *httpError { | 24 // | 
| 25 // If the inner handler returns an error, HTTP status is determined using | |
| 26 // errorStatus. | |
| 27 // Prints only "Internal server error" if the code is Internal. | |
| 28 // Logs the error if code is Internal or Unknown. | |
| 29 func (m *method) handle(c context.Context, w http.ResponseWriter, r *http.Reques t) *response { | |
| 47 defer r.Body.Close() | 30 defer r.Body.Close() | 
| 48 » format, err := responseFormat(r.Header.Get(headerAccept)) | 31 | 
| 49 » if err != nil { | 32 » format, perr := responseFormat(r.Header.Get(headerAccept)) | 
| 50 » » return err | 33 » if perr != nil { | 
| 34 » » return respondProtocolError(perr) | |
| 51 } | 35 } | 
| 52 | 36 | 
| 53 » c, rawErr := parseHeader(c, r.Header) | 37 » c, err := parseHeader(c, r.Header) | 
| 54 » if rawErr != nil { | 38 » if err != nil { | 
| 55 » » return withStatus(rawErr, http.StatusBadRequest) | 39 » » return respondProtocolError(withStatus(err, http.StatusBadReques t)) | 
| 56 } | 40 } | 
| 57 | 41 | 
| 58 » res, rawErr := m.desc.Handler(m.service.impl, c, func(msg interface{}) e rror { | 42 » out, err := m.desc.Handler(m.service.impl, c, func(in interface{}) error { | 
| 59 » » if msg == nil { | 43 » » if in == nil { | 
| 
 
dnj (Google)
2016/01/26 16:13:56
Panic in response to input is bad. Let's just have
 
nodir
2016/01/26 18:01:07
Note that it is not server input. "in" is passed b
 
dnj
2016/01/26 18:50:27
Yep.
 
 | |
| 60 panicf("cannot decode to nil") | 44 panicf("cannot decode to nil") | 
| 61 } | 45 } | 
| 62 // Do not collapse it to one line. There is implicit err type co nversion. | 46 // Do not collapse it to one line. There is implicit err type co nversion. | 
| 63 » » if err := readMessage(r, msg.(proto.Message)); err != nil { | 47 » » if perr := readMessage(r, in.(proto.Message)); perr != nil { | 
| 64 » » » return err | 48 » » » return perr | 
| 65 } | 49 } | 
| 66 return nil | 50 return nil | 
| 67 }) | 51 }) | 
| 68 » if rawErr != nil { | 52 » if err != nil { | 
| 69 » » if err, ok := rawErr.(*httpError); ok { | 53 » » if perr, ok := err.(*protocolError); ok { | 
| 70 » » » return err | 54 » » » return respondProtocolError(perr) | 
| 71 } | 55 } | 
| 72 » » return withStatus(rawErr, ErrorStatus(rawErr)) | 56 » » return errResponse(errorCode(err), 0, grpc.ErrorDesc(err)) | 
| 73 } | 57 } | 
| 74 » if res == nil { | 58 | 
| 75 » » return m.internalServerError("service returned nil message") | 59 » if out == nil { | 
| 60 » » return errResponse(codes.Internal, 0, "service returned nil mess age") | |
| 76 } | 61 } | 
| 77 » if err := writeMessage(w, res.(proto.Message), format); err != nil { | 62 » return respondMessage(out.(proto.Message), format) | 
| 78 » » return m.internalServerError("could not respond: %s", err) | |
| 79 » } | |
| 80 » return nil | |
| 81 } | 63 } | 
| 82 | |
| 83 // InstallHandlers installs a POST HTTP handlers at /prpc/{service_name}/{method _name}. | |
| 84 func (m *method) InstallHandlers(r *httprouter.Router, base middleware.Base) { | |
| 85 path := fmt.Sprintf("/prpc/%s/%s", m.service.Name(), m.Name()) | |
| 86 r.POST(path, base(m.Handle)) | |
| 87 } | |
| 88 | |
| 89 func (m *method) internalServerError(format string, a ...interface{}) *httpError { | |
| 90 format = fmt.Sprintf("%s.%s: ", m.service.Name(), m.Name()) + format | |
| 91 return errorf(http.StatusInternalServerError, format, a...) | |
| 92 } | |
| OLD | NEW |