github.com/vcilabs/webrpc@v0.5.2-0.20201116131534-162e27b1b33b/_examples/golang-imports/api.gen.go (about)

     1  // example-api-service v1.0.0 0d503533eece70559645300ec8c6afd39be48810
     2  // --
     3  // This file has been generated by https://github.com/webrpc/webrpc using gen/golang
     4  // Do not edit by hand. Update your webrpc schema and re-generate.
     5  package main
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"encoding/json"
    11  	"errors"
    12  	"fmt"
    13  	"io"
    14  	"io/ioutil"
    15  	"net/http"
    16  	"net/url"
    17  	"strings"
    18  )
    19  
    20  // WebRPC description and code-gen version
    21  func WebRPCVersion() string {
    22  	return "v1"
    23  }
    24  
    25  // Schema version of your RIDL schema
    26  func WebRPCSchemaVersion() string {
    27  	return "v1.0.0"
    28  }
    29  
    30  // Schema hash generated from your RIDL schema
    31  func WebRPCSchemaHash() string {
    32  	return "0d503533eece70559645300ec8c6afd39be48810"
    33  }
    34  
    35  //
    36  // Types
    37  //
    38  
    39  type User struct {
    40  	Username string `json:"username"`
    41  	Age      uint32 `json:"age"`
    42  }
    43  
    44  type Location uint32
    45  
    46  const (
    47  	Location_TORONTO  Location = 0
    48  	Location_NEW_YORK Location = 1
    49  )
    50  
    51  var Location_name = map[uint32]string{
    52  	0: "TORONTO",
    53  	1: "NEW_YORK",
    54  }
    55  
    56  var Location_value = map[string]uint32{
    57  	"TORONTO":  0,
    58  	"NEW_YORK": 1,
    59  }
    60  
    61  func (x Location) String() string {
    62  	return Location_name[uint32(x)]
    63  }
    64  
    65  func (x Location) MarshalJSON() ([]byte, error) {
    66  	buf := bytes.NewBufferString(`"`)
    67  	buf.WriteString(Location_name[uint32(x)])
    68  	buf.WriteString(`"`)
    69  	return buf.Bytes(), nil
    70  }
    71  
    72  func (x *Location) UnmarshalJSON(b []byte) error {
    73  	var j string
    74  	err := json.Unmarshal(b, &j)
    75  	if err != nil {
    76  		return err
    77  	}
    78  	*x = Location(Location_value[j])
    79  	return nil
    80  }
    81  
    82  type ExampleAPI interface {
    83  	Ping(ctx context.Context) error
    84  	Status(ctx context.Context) (bool, error)
    85  	GetUsers(ctx context.Context) ([]*User, *Location, error)
    86  }
    87  
    88  var WebRPCServices = map[string][]string{
    89  	"ExampleAPI": {
    90  		"Ping",
    91  		"Status",
    92  		"GetUsers",
    93  	},
    94  }
    95  
    96  //
    97  // Server
    98  //
    99  
   100  type WebRPCServer interface {
   101  	http.Handler
   102  }
   103  
   104  type exampleAPIServer struct {
   105  	ExampleAPI
   106  }
   107  
   108  func NewExampleAPIServer(svc ExampleAPI) WebRPCServer {
   109  	return &exampleAPIServer{
   110  		ExampleAPI: svc,
   111  	}
   112  }
   113  
   114  func (s *exampleAPIServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   115  	ctx := r.Context()
   116  	ctx = context.WithValue(ctx, HTTPResponseWriterCtxKey, w)
   117  	ctx = context.WithValue(ctx, HTTPRequestCtxKey, r)
   118  	ctx = context.WithValue(ctx, ServiceNameCtxKey, "ExampleAPI")
   119  
   120  	if r.Method != "POST" {
   121  		err := Errorf(ErrBadRoute, "unsupported method %q (only POST is allowed)", r.Method)
   122  		RespondWithError(w, err)
   123  		return
   124  	}
   125  
   126  	switch r.URL.Path {
   127  	case "/rpc/ExampleAPI/Ping":
   128  		s.servePing(ctx, w, r)
   129  		return
   130  	case "/rpc/ExampleAPI/Status":
   131  		s.serveStatus(ctx, w, r)
   132  		return
   133  	case "/rpc/ExampleAPI/GetUsers":
   134  		s.serveGetUsers(ctx, w, r)
   135  		return
   136  	default:
   137  		err := Errorf(ErrBadRoute, "no handler for path %q", r.URL.Path)
   138  		RespondWithError(w, err)
   139  		return
   140  	}
   141  }
   142  
   143  func (s *exampleAPIServer) servePing(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   144  	header := r.Header.Get("Content-Type")
   145  	i := strings.Index(header, ";")
   146  	if i == -1 {
   147  		i = len(header)
   148  	}
   149  
   150  	switch strings.TrimSpace(strings.ToLower(header[:i])) {
   151  	case "application/json":
   152  		s.servePingJSON(ctx, w, r)
   153  	default:
   154  		err := Errorf(ErrBadRoute, "unexpected Content-Type: %q", r.Header.Get("Content-Type"))
   155  		RespondWithError(w, err)
   156  	}
   157  }
   158  
   159  func (s *exampleAPIServer) servePingJSON(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   160  	var err error
   161  	ctx = context.WithValue(ctx, MethodNameCtxKey, "Ping")
   162  
   163  	// Call service method
   164  	func() {
   165  		defer func() {
   166  			// In case of a panic, serve a 500 error and then panic.
   167  			if rr := recover(); rr != nil {
   168  				RespondWithError(w, ErrorInternal("internal service panic"))
   169  				panic(rr)
   170  			}
   171  		}()
   172  		err = s.ExampleAPI.Ping(ctx)
   173  	}()
   174  
   175  	if err != nil {
   176  		RespondWithError(w, err)
   177  		return
   178  	}
   179  
   180  	w.Header().Set("Content-Type", "application/json")
   181  	w.WriteHeader(http.StatusOK)
   182  }
   183  
   184  func (s *exampleAPIServer) serveStatus(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   185  	header := r.Header.Get("Content-Type")
   186  	i := strings.Index(header, ";")
   187  	if i == -1 {
   188  		i = len(header)
   189  	}
   190  
   191  	switch strings.TrimSpace(strings.ToLower(header[:i])) {
   192  	case "application/json":
   193  		s.serveStatusJSON(ctx, w, r)
   194  	default:
   195  		err := Errorf(ErrBadRoute, "unexpected Content-Type: %q", r.Header.Get("Content-Type"))
   196  		RespondWithError(w, err)
   197  	}
   198  }
   199  
   200  func (s *exampleAPIServer) serveStatusJSON(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   201  	var err error
   202  	ctx = context.WithValue(ctx, MethodNameCtxKey, "Status")
   203  
   204  	// Call service method
   205  	var ret0 bool
   206  	func() {
   207  		defer func() {
   208  			// In case of a panic, serve a 500 error and then panic.
   209  			if rr := recover(); rr != nil {
   210  				RespondWithError(w, ErrorInternal("internal service panic"))
   211  				panic(rr)
   212  			}
   213  		}()
   214  		ret0, err = s.ExampleAPI.Status(ctx)
   215  	}()
   216  	respContent := struct {
   217  		Ret0 bool `json:"status"`
   218  	}{ret0}
   219  
   220  	if err != nil {
   221  		RespondWithError(w, err)
   222  		return
   223  	}
   224  	respBody, err := json.Marshal(respContent)
   225  	if err != nil {
   226  		err = WrapError(ErrInternal, err, "failed to marshal json response")
   227  		RespondWithError(w, err)
   228  		return
   229  	}
   230  
   231  	w.Header().Set("Content-Type", "application/json")
   232  	w.WriteHeader(http.StatusOK)
   233  	w.Write(respBody)
   234  }
   235  
   236  func (s *exampleAPIServer) serveGetUsers(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   237  	header := r.Header.Get("Content-Type")
   238  	i := strings.Index(header, ";")
   239  	if i == -1 {
   240  		i = len(header)
   241  	}
   242  
   243  	switch strings.TrimSpace(strings.ToLower(header[:i])) {
   244  	case "application/json":
   245  		s.serveGetUsersJSON(ctx, w, r)
   246  	default:
   247  		err := Errorf(ErrBadRoute, "unexpected Content-Type: %q", r.Header.Get("Content-Type"))
   248  		RespondWithError(w, err)
   249  	}
   250  }
   251  
   252  func (s *exampleAPIServer) serveGetUsersJSON(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   253  	var err error
   254  	ctx = context.WithValue(ctx, MethodNameCtxKey, "GetUsers")
   255  
   256  	// Call service method
   257  	var ret0 []*User
   258  	var ret1 *Location
   259  	func() {
   260  		defer func() {
   261  			// In case of a panic, serve a 500 error and then panic.
   262  			if rr := recover(); rr != nil {
   263  				RespondWithError(w, ErrorInternal("internal service panic"))
   264  				panic(rr)
   265  			}
   266  		}()
   267  		ret0, ret1, err = s.ExampleAPI.GetUsers(ctx)
   268  	}()
   269  	respContent := struct {
   270  		Ret0 []*User   `json:"users"`
   271  		Ret1 *Location `json:"location"`
   272  	}{ret0, ret1}
   273  
   274  	if err != nil {
   275  		RespondWithError(w, err)
   276  		return
   277  	}
   278  	respBody, err := json.Marshal(respContent)
   279  	if err != nil {
   280  		err = WrapError(ErrInternal, err, "failed to marshal json response")
   281  		RespondWithError(w, err)
   282  		return
   283  	}
   284  
   285  	w.Header().Set("Content-Type", "application/json")
   286  	w.WriteHeader(http.StatusOK)
   287  	w.Write(respBody)
   288  }
   289  
   290  func RespondWithError(w http.ResponseWriter, err error) {
   291  	rpcErr, ok := err.(Error)
   292  	if !ok {
   293  		rpcErr = WrapError(ErrInternal, err, "webrpc error")
   294  	}
   295  
   296  	statusCode := HTTPStatusFromErrorCode(rpcErr.Code())
   297  
   298  	w.Header().Set("Content-Type", "application/json")
   299  	w.WriteHeader(statusCode)
   300  
   301  	respBody, _ := json.Marshal(rpcErr.Payload())
   302  	w.Write(respBody)
   303  }
   304  
   305  //
   306  // Client
   307  //
   308  
   309  const ExampleAPIPathPrefix = "/rpc/ExampleAPI/"
   310  
   311  type exampleAPIClient struct {
   312  	client HTTPClient
   313  	urls   [3]string
   314  }
   315  
   316  func NewExampleAPIClient(addr string, client HTTPClient) ExampleAPI {
   317  	prefix := urlBase(addr) + ExampleAPIPathPrefix
   318  	urls := [3]string{
   319  		prefix + "Ping",
   320  		prefix + "Status",
   321  		prefix + "GetUsers",
   322  	}
   323  	return &exampleAPIClient{
   324  		client: client,
   325  		urls:   urls,
   326  	}
   327  }
   328  
   329  func (c *exampleAPIClient) Ping(ctx context.Context) error {
   330  
   331  	err := doJSONRequest(ctx, c.client, c.urls[0], nil, nil)
   332  	return err
   333  }
   334  
   335  func (c *exampleAPIClient) Status(ctx context.Context) (bool, error) {
   336  	out := struct {
   337  		Ret0 bool `json:"status"`
   338  	}{}
   339  
   340  	err := doJSONRequest(ctx, c.client, c.urls[1], nil, &out)
   341  	return out.Ret0, err
   342  }
   343  
   344  func (c *exampleAPIClient) GetUsers(ctx context.Context) ([]*User, *Location, error) {
   345  	out := struct {
   346  		Ret0 []*User   `json:"users"`
   347  		Ret1 *Location `json:"location"`
   348  	}{}
   349  
   350  	err := doJSONRequest(ctx, c.client, c.urls[2], nil, &out)
   351  	return out.Ret0, out.Ret1, err
   352  }
   353  
   354  // HTTPClient is the interface used by generated clients to send HTTP requests.
   355  // It is fulfilled by *(net/http).Client, which is sufficient for most users.
   356  // Users can provide their own implementation for special retry policies.
   357  type HTTPClient interface {
   358  	Do(req *http.Request) (*http.Response, error)
   359  }
   360  
   361  // urlBase helps ensure that addr specifies a scheme. If it is unparsable
   362  // as a URL, it returns addr unchanged.
   363  func urlBase(addr string) string {
   364  	// If the addr specifies a scheme, use it. If not, default to
   365  	// http. If url.Parse fails on it, return it unchanged.
   366  	url, err := url.Parse(addr)
   367  	if err != nil {
   368  		return addr
   369  	}
   370  	if url.Scheme == "" {
   371  		url.Scheme = "http"
   372  	}
   373  	return url.String()
   374  }
   375  
   376  // newRequest makes an http.Request from a client, adding common headers.
   377  func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType string) (*http.Request, error) {
   378  	req, err := http.NewRequest("POST", url, reqBody)
   379  	if err != nil {
   380  		return nil, err
   381  	}
   382  	req.Header.Set("Accept", contentType)
   383  	req.Header.Set("Content-Type", contentType)
   384  	if headers, ok := HTTPRequestHeaders(ctx); ok {
   385  		for k := range headers {
   386  			for _, v := range headers[k] {
   387  				req.Header.Add(k, v)
   388  			}
   389  		}
   390  	}
   391  	return req, nil
   392  }
   393  
   394  // doJSONRequest is common code to make a request to the remote service.
   395  func doJSONRequest(ctx context.Context, client HTTPClient, url string, in, out interface{}) error {
   396  	reqBody, err := json.Marshal(in)
   397  	if err != nil {
   398  		return clientError("failed to marshal json request", err)
   399  	}
   400  	if err = ctx.Err(); err != nil {
   401  		return clientError("aborted because context was done", err)
   402  	}
   403  
   404  	req, err := newRequest(ctx, url, bytes.NewBuffer(reqBody), "application/json")
   405  	if err != nil {
   406  		return clientError("could not build request", err)
   407  	}
   408  	resp, err := client.Do(req)
   409  	if err != nil {
   410  		return clientError("request failed", err)
   411  	}
   412  
   413  	defer func() {
   414  		cerr := resp.Body.Close()
   415  		if err == nil && cerr != nil {
   416  			err = clientError("failed to close response body", cerr)
   417  		}
   418  	}()
   419  
   420  	if err = ctx.Err(); err != nil {
   421  		return clientError("aborted because context was done", err)
   422  	}
   423  
   424  	if resp.StatusCode != 200 {
   425  		return errorFromResponse(resp)
   426  	}
   427  
   428  	if out != nil {
   429  		respBody, err := ioutil.ReadAll(resp.Body)
   430  		if err != nil {
   431  			return clientError("failed to read response body", err)
   432  		}
   433  
   434  		err = json.Unmarshal(respBody, &out)
   435  		if err != nil {
   436  			return clientError("failed to unmarshal json response body", err)
   437  		}
   438  		if err = ctx.Err(); err != nil {
   439  			return clientError("aborted because context was done", err)
   440  		}
   441  	}
   442  
   443  	return nil
   444  }
   445  
   446  // errorFromResponse builds a webrpc Error from a non-200 HTTP response.
   447  func errorFromResponse(resp *http.Response) Error {
   448  	respBody, err := ioutil.ReadAll(resp.Body)
   449  	if err != nil {
   450  		return clientError("failed to read server error response body", err)
   451  	}
   452  
   453  	var respErr ErrorPayload
   454  	if err := json.Unmarshal(respBody, &respErr); err != nil {
   455  		return clientError("failed unmarshal error response", err)
   456  	}
   457  
   458  	errCode := ErrorCode(respErr.Code)
   459  
   460  	if HTTPStatusFromErrorCode(errCode) == 0 {
   461  		return ErrorInternal("invalid code returned from server error response: %s", respErr.Code)
   462  	}
   463  
   464  	return &rpcErr{
   465  		code:  errCode,
   466  		msg:   respErr.Msg,
   467  		cause: errors.New(respErr.Cause),
   468  	}
   469  }
   470  
   471  func clientError(desc string, err error) Error {
   472  	return WrapError(ErrInternal, err, desc)
   473  }
   474  
   475  func WithHTTPRequestHeaders(ctx context.Context, h http.Header) (context.Context, error) {
   476  	if _, ok := h["Accept"]; ok {
   477  		return nil, errors.New("provided header cannot set Accept")
   478  	}
   479  	if _, ok := h["Content-Type"]; ok {
   480  		return nil, errors.New("provided header cannot set Content-Type")
   481  	}
   482  
   483  	copied := make(http.Header, len(h))
   484  	for k, vv := range h {
   485  		if vv == nil {
   486  			copied[k] = nil
   487  			continue
   488  		}
   489  		copied[k] = make([]string, len(vv))
   490  		copy(copied[k], vv)
   491  	}
   492  
   493  	return context.WithValue(ctx, HTTPClientRequestHeadersCtxKey, copied), nil
   494  }
   495  
   496  func HTTPRequestHeaders(ctx context.Context) (http.Header, bool) {
   497  	h, ok := ctx.Value(HTTPClientRequestHeadersCtxKey).(http.Header)
   498  	return h, ok
   499  }
   500  
   501  //
   502  // Helpers
   503  //
   504  
   505  type ErrorPayload struct {
   506  	Status int    `json:"status"`
   507  	Code   string `json:"code"`
   508  	Cause  string `json:"cause,omitempty"`
   509  	Msg    string `json:"msg"`
   510  	Error  string `json:"error"`
   511  }
   512  
   513  type Error interface {
   514  	// Code is of the valid error codes
   515  	Code() ErrorCode
   516  
   517  	// Msg returns a human-readable, unstructured messages describing the error
   518  	Msg() string
   519  
   520  	// Cause is reason for the error
   521  	Cause() error
   522  
   523  	// Error returns a string of the form "webrpc error <Code>: <Msg>"
   524  	Error() string
   525  
   526  	// Error response payload
   527  	Payload() ErrorPayload
   528  }
   529  
   530  func Errorf(code ErrorCode, msgf string, args ...interface{}) Error {
   531  	msg := fmt.Sprintf(msgf, args...)
   532  	if IsValidErrorCode(code) {
   533  		return &rpcErr{code: code, msg: msg}
   534  	}
   535  	return &rpcErr{code: ErrInternal, msg: "invalid error type " + string(code)}
   536  }
   537  
   538  func WrapError(code ErrorCode, cause error, format string, args ...interface{}) Error {
   539  	msg := fmt.Sprintf(format, args...)
   540  	if IsValidErrorCode(code) {
   541  		return &rpcErr{code: code, msg: msg, cause: cause}
   542  	}
   543  	return &rpcErr{code: ErrInternal, msg: "invalid error type " + string(code), cause: cause}
   544  }
   545  
   546  func Failf(format string, args ...interface{}) Error {
   547  	return Errorf(ErrFail, format, args...)
   548  }
   549  
   550  func WrapFailf(cause error, format string, args ...interface{}) Error {
   551  	return WrapError(ErrFail, cause, format, args...)
   552  }
   553  
   554  func ErrorNotFound(format string, args ...interface{}) Error {
   555  	return Errorf(ErrNotFound, format, args...)
   556  }
   557  
   558  func ErrorInvalidArgument(argument string, validationMsg string) Error {
   559  	return Errorf(ErrInvalidArgument, argument+" "+validationMsg)
   560  }
   561  
   562  func ErrorRequiredArgument(argument string) Error {
   563  	return ErrorInvalidArgument(argument, "is required")
   564  }
   565  
   566  func ErrorInternal(format string, args ...interface{}) Error {
   567  	return Errorf(ErrInternal, format, args...)
   568  }
   569  
   570  type ErrorCode string
   571  
   572  const (
   573  	// Unknown error. For example when handling errors raised by APIs that do not
   574  	// return enough error information.
   575  	ErrUnknown ErrorCode = "unknown"
   576  
   577  	// Fail error. General failure error type.
   578  	ErrFail ErrorCode = "fail"
   579  
   580  	// Canceled indicates the operation was cancelled (typically by the caller).
   581  	ErrCanceled ErrorCode = "canceled"
   582  
   583  	// InvalidArgument indicates client specified an invalid argument. It
   584  	// indicates arguments that are problematic regardless of the state of the
   585  	// system (i.e. a malformed file name, required argument, number out of range,
   586  	// etc.).
   587  	ErrInvalidArgument ErrorCode = "invalid argument"
   588  
   589  	// DeadlineExceeded means operation expired before completion. For operations
   590  	// that change the state of the system, this error may be returned even if the
   591  	// operation has completed successfully (timeout).
   592  	ErrDeadlineExceeded ErrorCode = "deadline exceeded"
   593  
   594  	// NotFound means some requested entity was not found.
   595  	ErrNotFound ErrorCode = "not found"
   596  
   597  	// BadRoute means that the requested URL path wasn't routable to a webrpc
   598  	// service and method. This is returned by the generated server, and usually
   599  	// shouldn't be returned by applications. Instead, applications should use
   600  	// NotFound or Unimplemented.
   601  	ErrBadRoute ErrorCode = "bad route"
   602  
   603  	// AlreadyExists means an attempt to create an entity failed because one
   604  	// already exists.
   605  	ErrAlreadyExists ErrorCode = "already exists"
   606  
   607  	// PermissionDenied indicates the caller does not have permission to execute
   608  	// the specified operation. It must not be used if the caller cannot be
   609  	// identified (Unauthenticated).
   610  	ErrPermissionDenied ErrorCode = "permission denied"
   611  
   612  	// Unauthenticated indicates the request does not have valid authentication
   613  	// credentials for the operation.
   614  	ErrUnauthenticated ErrorCode = "unauthenticated"
   615  
   616  	// ResourceExhausted indicates some resource has been exhausted, perhaps a
   617  	// per-user quota, or perhaps the entire file system is out of space.
   618  	ErrResourceExhausted ErrorCode = "resource exhausted"
   619  
   620  	// FailedPrecondition indicates operation was rejected because the system is
   621  	// not in a state required for the operation's execution. For example, doing
   622  	// an rmdir operation on a directory that is non-empty, or on a non-directory
   623  	// object, or when having conflicting read-modify-write on the same resource.
   624  	ErrFailedPrecondition ErrorCode = "failed precondition"
   625  
   626  	// Aborted indicates the operation was aborted, typically due to a concurrency
   627  	// issue like sequencer check failures, transaction aborts, etc.
   628  	ErrAborted ErrorCode = "aborted"
   629  
   630  	// OutOfRange means operation was attempted past the valid range. For example,
   631  	// seeking or reading past end of a paginated collection.
   632  	//
   633  	// Unlike InvalidArgument, this error indicates a problem that may be fixed if
   634  	// the system state changes (i.e. adding more items to the collection).
   635  	//
   636  	// There is a fair bit of overlap between FailedPrecondition and OutOfRange.
   637  	// We recommend using OutOfRange (the more specific error) when it applies so
   638  	// that callers who are iterating through a space can easily look for an
   639  	// OutOfRange error to detect when they are done.
   640  	ErrOutOfRange ErrorCode = "out of range"
   641  
   642  	// Unimplemented indicates operation is not implemented or not
   643  	// supported/enabled in this service.
   644  	ErrUnimplemented ErrorCode = "unimplemented"
   645  
   646  	// Internal errors. When some invariants expected by the underlying system
   647  	// have been broken. In other words, something bad happened in the library or
   648  	// backend service. Do not confuse with HTTP Internal Server Error; an
   649  	// Internal error could also happen on the client code, i.e. when parsing a
   650  	// server response.
   651  	ErrInternal ErrorCode = "internal"
   652  
   653  	// Unavailable indicates the service is currently unavailable. This is a most
   654  	// likely a transient condition and may be corrected by retrying with a
   655  	// backoff.
   656  	ErrUnavailable ErrorCode = "unavailable"
   657  
   658  	// DataLoss indicates unrecoverable data loss or corruption.
   659  	ErrDataLoss ErrorCode = "data loss"
   660  
   661  	// ErrNone is the zero-value, is considered an empty error and should not be
   662  	// used.
   663  	ErrNone ErrorCode = ""
   664  )
   665  
   666  func HTTPStatusFromErrorCode(code ErrorCode) int {
   667  	switch code {
   668  	case ErrCanceled:
   669  		return 408 // RequestTimeout
   670  	case ErrUnknown:
   671  		return 400 // Bad Request
   672  	case ErrFail:
   673  		return 422 // Unprocessable Entity
   674  	case ErrInvalidArgument:
   675  		return 400 // BadRequest
   676  	case ErrDeadlineExceeded:
   677  		return 408 // RequestTimeout
   678  	case ErrNotFound:
   679  		return 404 // Not Found
   680  	case ErrBadRoute:
   681  		return 404 // Not Found
   682  	case ErrAlreadyExists:
   683  		return 409 // Conflict
   684  	case ErrPermissionDenied:
   685  		return 403 // Forbidden
   686  	case ErrUnauthenticated:
   687  		return 401 // Unauthorized
   688  	case ErrResourceExhausted:
   689  		return 403 // Forbidden
   690  	case ErrFailedPrecondition:
   691  		return 412 // Precondition Failed
   692  	case ErrAborted:
   693  		return 409 // Conflict
   694  	case ErrOutOfRange:
   695  		return 400 // Bad Request
   696  	case ErrUnimplemented:
   697  		return 501 // Not Implemented
   698  	case ErrInternal:
   699  		return 500 // Internal Server Error
   700  	case ErrUnavailable:
   701  		return 503 // Service Unavailable
   702  	case ErrDataLoss:
   703  		return 500 // Internal Server Error
   704  	case ErrNone:
   705  		return 200 // OK
   706  	default:
   707  		return 0 // Invalid!
   708  	}
   709  }
   710  
   711  func IsErrorCode(err error, code ErrorCode) bool {
   712  	if rpcErr, ok := err.(Error); ok {
   713  		if rpcErr.Code() == code {
   714  			return true
   715  		}
   716  	}
   717  	return false
   718  }
   719  
   720  func IsValidErrorCode(code ErrorCode) bool {
   721  	return HTTPStatusFromErrorCode(code) != 0
   722  }
   723  
   724  type rpcErr struct {
   725  	code  ErrorCode
   726  	msg   string
   727  	cause error
   728  }
   729  
   730  func (e *rpcErr) Code() ErrorCode {
   731  	return e.code
   732  }
   733  
   734  func (e *rpcErr) Msg() string {
   735  	return e.msg
   736  }
   737  
   738  func (e *rpcErr) Cause() error {
   739  	return e.cause
   740  }
   741  
   742  func (e *rpcErr) Error() string {
   743  	if e.cause != nil && e.cause.Error() != "" {
   744  		if e.msg != "" {
   745  			return fmt.Sprintf("webrpc %s error: %s -- %s", e.code, e.cause.Error(), e.msg)
   746  		} else {
   747  			return fmt.Sprintf("webrpc %s error: %s", e.code, e.cause.Error())
   748  		}
   749  	} else {
   750  		return fmt.Sprintf("webrpc %s error: %s", e.code, e.msg)
   751  	}
   752  }
   753  
   754  func (e *rpcErr) Payload() ErrorPayload {
   755  	statusCode := HTTPStatusFromErrorCode(e.Code())
   756  	errPayload := ErrorPayload{
   757  		Status: statusCode,
   758  		Code:   string(e.Code()),
   759  		Msg:    e.Msg(),
   760  		Error:  e.Error(),
   761  	}
   762  	if e.Cause() != nil {
   763  		errPayload.Cause = e.Cause().Error()
   764  	}
   765  	return errPayload
   766  }
   767  
   768  type contextKey struct {
   769  	name string
   770  }
   771  
   772  func (k *contextKey) String() string {
   773  	return "webrpc context value " + k.name
   774  }
   775  
   776  var (
   777  	// For Client
   778  	HTTPClientRequestHeadersCtxKey = &contextKey{"HTTPClientRequestHeaders"}
   779  
   780  	// For Server
   781  	HTTPResponseWriterCtxKey = &contextKey{"HTTPResponseWriter"}
   782  
   783  	HTTPRequestCtxKey = &contextKey{"HTTPRequest"}
   784  
   785  	ServiceNameCtxKey = &contextKey{"ServiceName"}
   786  
   787  	MethodNameCtxKey = &contextKey{"MethodName"}
   788  )