| 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" |
| 13 "google.golang.org/grpc/codes" |
| 15 | 14 |
| 16 » "github.com/luci/luci-go/server/middleware" | 15 » "github.com/luci/luci-go/common/grpcutil" |
| 17 ) | 16 ) |
| 18 | 17 |
| 19 type method struct { | 18 type method struct { |
| 20 service *service | 19 service *service |
| 21 desc grpc.MethodDesc | 20 desc grpc.MethodDesc |
| 22 } | 21 } |
| 23 | 22 |
| 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, | 23 // handle decodes an input protobuf message from the HTTP request, |
| 44 // delegates RPC handling to the inner implementation and | 24 // delegates RPC handling to the inner implementation and |
| 45 // encodes the output message back to the HTTP response. | 25 // 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 { | 26 // |
| 27 // If the inner handler returns an error, HTTP status is determined using |
| 28 // errorStatus. |
| 29 // Prints only "Internal server error" if the code is Internal. |
| 30 // Logs the error if code is Internal or Unknown. |
| 31 func (m *method) handle(c context.Context, w http.ResponseWriter, r *http.Reques
t) *response { |
| 47 defer r.Body.Close() | 32 defer r.Body.Close() |
| 48 » format, err := responseFormat(r.Header.Get(headerAccept)) | 33 |
| 49 » if err != nil { | 34 » format, perr := responseFormat(r.Header.Get(headerAccept)) |
| 50 » » return err | 35 » if perr != nil { |
| 36 » » return respondProtocolError(perr) |
| 51 } | 37 } |
| 52 | 38 |
| 53 » c, rawErr := parseHeader(c, r.Header) | 39 » c, err := parseHeader(c, r.Header) |
| 54 » if rawErr != nil { | 40 » if err != nil { |
| 55 » » return withStatus(rawErr, http.StatusBadRequest) | 41 » » return respondProtocolError(withStatus(err, http.StatusBadReques
t)) |
| 56 } | 42 } |
| 57 | 43 |
| 58 » res, rawErr := m.desc.Handler(m.service.impl, c, func(msg interface{}) e
rror { | 44 » out, err := m.desc.Handler(m.service.impl, c, func(in interface{}) error
{ |
| 59 » » if msg == nil { | 45 » » if in == nil { |
| 60 » » » panicf("cannot decode to nil") | 46 » » » return grpcutil.Errf(codes.Internal, "input message is n
il") |
| 61 } | 47 } |
| 62 // Do not collapse it to one line. There is implicit err type co
nversion. | 48 // Do not collapse it to one line. There is implicit err type co
nversion. |
| 63 » » if err := readMessage(r, msg.(proto.Message)); err != nil { | 49 » » if perr := readMessage(r, in.(proto.Message)); perr != nil { |
| 64 » » » return err | 50 » » » return perr |
| 65 } | 51 } |
| 66 return nil | 52 return nil |
| 67 }) | 53 }) |
| 68 » if rawErr != nil { | 54 » if err != nil { |
| 69 » » if err, ok := rawErr.(*httpError); ok { | 55 » » if perr, ok := err.(*protocolError); ok { |
| 70 » » » return err | 56 » » » return respondProtocolError(perr) |
| 71 } | 57 } |
| 72 » » return withStatus(rawErr, ErrorStatus(rawErr)) | 58 » » return errResponse(errorCode(err), 0, grpc.ErrorDesc(err)) |
| 73 } | 59 } |
| 74 » if res == nil { | 60 |
| 75 » » return m.internalServerError("service returned nil message") | 61 » if out == nil { |
| 62 » » return errResponse(codes.Internal, 0, "service returned nil mess
age") |
| 76 } | 63 } |
| 77 » if err := writeMessage(w, res.(proto.Message), format); err != nil { | 64 » return respondMessage(out.(proto.Message), format) |
| 78 » » return m.internalServerError("could not respond: %s", err) | |
| 79 » } | |
| 80 » return nil | |
| 81 } | 65 } |
| 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 |