| Index: server/prpc/method.go
 | 
| diff --git a/server/prpc/method.go b/server/prpc/method.go
 | 
| index aa0b5c2f61779991a2fc375ca356a5b8a9974e91..b88599dbf58c7972d23a8e80f6535021adf04154 100644
 | 
| --- a/server/prpc/method.go
 | 
| +++ b/server/prpc/method.go
 | 
| @@ -5,15 +5,14 @@
 | 
|  package prpc
 | 
|  
 | 
|  import (
 | 
| -	"fmt"
 | 
|  	"net/http"
 | 
|  
 | 
|  	"github.com/golang/protobuf/proto"
 | 
| -	"github.com/julienschmidt/httprouter"
 | 
|  	"golang.org/x/net/context"
 | 
|  	"google.golang.org/grpc"
 | 
| +	"google.golang.org/grpc/codes"
 | 
|  
 | 
| -	"github.com/luci/luci-go/server/middleware"
 | 
| +	"github.com/luci/luci-go/common/grpcutil"
 | 
|  )
 | 
|  
 | 
|  type method struct {
 | 
| @@ -21,72 +20,46 @@ type method struct {
 | 
|  	desc    grpc.MethodDesc
 | 
|  }
 | 
|  
 | 
| -func (m *method) Name() string {
 | 
| -	return m.desc.MethodName
 | 
| -}
 | 
| -
 | 
| -// Handle decodes an input protobuf message from the HTTP request,
 | 
| +// handle decodes an input protobuf message from the HTTP request,
 | 
|  // delegates RPC handling to the inner implementation and
 | 
|  // encodes the output message back to the HTTP response.
 | 
|  //
 | 
|  // If the inner handler returns an error, HTTP status is determined using
 | 
| -// ErrorStatus.
 | 
| -// If the status is http.StatusInternalServerError, only "Internal server error"
 | 
| -// is printed.
 | 
| -// All errors with status >= 500 are logged.
 | 
| -func (m *method) Handle(c context.Context, w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
 | 
| -	if err := m.handle(c, w, r); err != nil {
 | 
| -		writeError(c, w, err)
 | 
| -	}
 | 
| -}
 | 
| -
 | 
| -// handle decodes an input protobuf message from the HTTP request,
 | 
| -// delegates RPC handling to the inner implementation and
 | 
| -// encodes the output message back to the HTTP response.
 | 
| -func (m *method) handle(c context.Context, w http.ResponseWriter, r *http.Request) *httpError {
 | 
| +// errorStatus.
 | 
| +// Prints only "Internal server error" if the code is Internal.
 | 
| +// Logs the error if code is Internal or Unknown.
 | 
| +func (m *method) handle(c context.Context, w http.ResponseWriter, r *http.Request) *response {
 | 
|  	defer r.Body.Close()
 | 
| -	format, err := responseFormat(r.Header.Get(headerAccept))
 | 
| -	if err != nil {
 | 
| -		return err
 | 
| +
 | 
| +	format, perr := responseFormat(r.Header.Get(headerAccept))
 | 
| +	if perr != nil {
 | 
| +		return respondProtocolError(perr)
 | 
|  	}
 | 
|  
 | 
| -	c, rawErr := parseHeader(c, r.Header)
 | 
| -	if rawErr != nil {
 | 
| -		return withStatus(rawErr, http.StatusBadRequest)
 | 
| +	c, err := parseHeader(c, r.Header)
 | 
| +	if err != nil {
 | 
| +		return respondProtocolError(withStatus(err, http.StatusBadRequest))
 | 
|  	}
 | 
|  
 | 
| -	res, rawErr := m.desc.Handler(m.service.impl, c, func(msg interface{}) error {
 | 
| -		if msg == nil {
 | 
| -			panicf("cannot decode to nil")
 | 
| +	out, err := m.desc.Handler(m.service.impl, c, func(in interface{}) error {
 | 
| +		if in == nil {
 | 
| +			return grpcutil.Errf(codes.Internal, "input message is nil")
 | 
|  		}
 | 
|  		// Do not collapse it to one line. There is implicit err type conversion.
 | 
| -		if err := readMessage(r, msg.(proto.Message)); err != nil {
 | 
| -			return err
 | 
| +		if perr := readMessage(r, in.(proto.Message)); perr != nil {
 | 
| +			return perr
 | 
|  		}
 | 
|  		return nil
 | 
|  	})
 | 
| -	if rawErr != nil {
 | 
| -		if err, ok := rawErr.(*httpError); ok {
 | 
| -			return err
 | 
| +	if err != nil {
 | 
| +		if perr, ok := err.(*protocolError); ok {
 | 
| +			return respondProtocolError(perr)
 | 
|  		}
 | 
| -		return withStatus(rawErr, ErrorStatus(rawErr))
 | 
| -	}
 | 
| -	if res == nil {
 | 
| -		return m.internalServerError("service returned nil message")
 | 
| -	}
 | 
| -	if err := writeMessage(w, res.(proto.Message), format); err != nil {
 | 
| -		return m.internalServerError("could not respond: %s", err)
 | 
| +		return errResponse(errorCode(err), 0, grpc.ErrorDesc(err))
 | 
|  	}
 | 
| -	return nil
 | 
| -}
 | 
| -
 | 
| -// InstallHandlers installs a POST HTTP handlers at /prpc/{service_name}/{method_name}.
 | 
| -func (m *method) InstallHandlers(r *httprouter.Router, base middleware.Base) {
 | 
| -	path := fmt.Sprintf("/prpc/%s/%s", m.service.Name(), m.Name())
 | 
| -	r.POST(path, base(m.Handle))
 | 
| -}
 | 
|  
 | 
| -func (m *method) internalServerError(format string, a ...interface{}) *httpError {
 | 
| -	format = fmt.Sprintf("%s.%s: ", m.service.Name(), m.Name()) + format
 | 
| -	return errorf(http.StatusInternalServerError, format, a...)
 | 
| +	if out == nil {
 | 
| +		return errResponse(codes.Internal, 0, "service returned nil message")
 | 
| +	}
 | 
| +	return respondMessage(out.(proto.Message), format)
 | 
|  }
 | 
| 
 |