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

     1  // example v0.0.1 b421904d19b997e555df17cba464343c3ed53e03
     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 "v0.0.1"
    28  }
    29  
    30  // Schema hash generated from your RIDL schema
    31  func WebRPCSchemaHash() string {
    32  	return "b421904d19b997e555df17cba464343c3ed53e03"
    33  }
    34  
    35  //
    36  // Types
    37  //
    38  
    39  type Kind uint32
    40  
    41  const (
    42  	Kind_USER  Kind = 0
    43  	Kind_ADMIN Kind = 1
    44  )
    45  
    46  var Kind_name = map[uint32]string{
    47  	0: "USER",
    48  	1: "ADMIN",
    49  }
    50  
    51  var Kind_value = map[string]uint32{
    52  	"USER":  0,
    53  	"ADMIN": 1,
    54  }
    55  
    56  func (x Kind) String() string {
    57  	return Kind_name[uint32(x)]
    58  }
    59  
    60  func (x Kind) MarshalJSON() ([]byte, error) {
    61  	buf := bytes.NewBufferString(`"`)
    62  	buf.WriteString(Kind_name[uint32(x)])
    63  	buf.WriteString(`"`)
    64  	return buf.Bytes(), nil
    65  }
    66  
    67  func (x *Kind) UnmarshalJSON(b []byte) error {
    68  	var j string
    69  	err := json.Unmarshal(b, &j)
    70  	if err != nil {
    71  		return err
    72  	}
    73  	*x = Kind(Kind_value[j])
    74  	return nil
    75  }
    76  
    77  type Empty struct {
    78  }
    79  
    80  type User struct {
    81  	ID       uint64 `json:"id" db:"id"`
    82  	Username string `json:"USERNAME" db:"username"`
    83  	Role     string `json:"role" db:"-"`
    84  }
    85  
    86  type SearchFilter struct {
    87  	Q string `json:"q"`
    88  }
    89  
    90  type Version struct {
    91  	WebrpcVersion string `json:"webrpcVersion"`
    92  	SchemaVersion string `json:"schemaVersion"`
    93  	SchemaHash    string `json:"schemaHash"`
    94  }
    95  
    96  type ComplexType struct {
    97  	Meta              map[string]interface{}       `json:"meta"`
    98  	MetaNestedExample map[string]map[string]uint32 `json:"metaNestedExample"`
    99  	NamesList         []string                     `json:"namesList"`
   100  	NumsList          []int64                      `json:"numsList"`
   101  	DoubleArray       [][]string                   `json:"doubleArray"`
   102  	ListOfMaps        []map[string]uint32          `json:"listOfMaps"`
   103  	ListOfUsers       []*User                      `json:"listOfUsers"`
   104  	MapOfUsers        map[string]*User             `json:"mapOfUsers"`
   105  	User              *User                        `json:"user"`
   106  }
   107  
   108  type ExampleService interface {
   109  	Ping(ctx context.Context) error
   110  	Status(ctx context.Context) (bool, error)
   111  	Version(ctx context.Context) (*Version, error)
   112  	GetUser(ctx context.Context, header map[string]string, userID uint64) (uint32, *User, error)
   113  	FindUser(ctx context.Context, s *SearchFilter) (string, *User, error)
   114  }
   115  
   116  var WebRPCServices = map[string][]string{
   117  	"ExampleService": {
   118  		"Ping",
   119  		"Status",
   120  		"Version",
   121  		"GetUser",
   122  		"FindUser",
   123  	},
   124  }
   125  
   126  //
   127  // Server
   128  //
   129  
   130  type WebRPCServer interface {
   131  	http.Handler
   132  }
   133  
   134  type exampleServiceServer struct {
   135  	ExampleService
   136  }
   137  
   138  func NewExampleServiceServer(svc ExampleService) WebRPCServer {
   139  	return &exampleServiceServer{
   140  		ExampleService: svc,
   141  	}
   142  }
   143  
   144  func (s *exampleServiceServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   145  	ctx := r.Context()
   146  	ctx = context.WithValue(ctx, HTTPResponseWriterCtxKey, w)
   147  	ctx = context.WithValue(ctx, HTTPRequestCtxKey, r)
   148  	ctx = context.WithValue(ctx, ServiceNameCtxKey, "ExampleService")
   149  
   150  	if r.Method != "POST" {
   151  		err := Errorf(ErrBadRoute, "unsupported method %q (only POST is allowed)", r.Method)
   152  		RespondWithError(w, err)
   153  		return
   154  	}
   155  
   156  	switch r.URL.Path {
   157  	case "/rpc/ExampleService/Ping":
   158  		s.servePing(ctx, w, r)
   159  		return
   160  	case "/rpc/ExampleService/Status":
   161  		s.serveStatus(ctx, w, r)
   162  		return
   163  	case "/rpc/ExampleService/Version":
   164  		s.serveVersion(ctx, w, r)
   165  		return
   166  	case "/rpc/ExampleService/GetUser":
   167  		s.serveGetUser(ctx, w, r)
   168  		return
   169  	case "/rpc/ExampleService/FindUser":
   170  		s.serveFindUser(ctx, w, r)
   171  		return
   172  	default:
   173  		err := Errorf(ErrBadRoute, "no handler for path %q", r.URL.Path)
   174  		RespondWithError(w, err)
   175  		return
   176  	}
   177  }
   178  
   179  func (s *exampleServiceServer) servePing(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   180  	header := r.Header.Get("Content-Type")
   181  	i := strings.Index(header, ";")
   182  	if i == -1 {
   183  		i = len(header)
   184  	}
   185  
   186  	switch strings.TrimSpace(strings.ToLower(header[:i])) {
   187  	case "application/json":
   188  		s.servePingJSON(ctx, w, r)
   189  	default:
   190  		err := Errorf(ErrBadRoute, "unexpected Content-Type: %q", r.Header.Get("Content-Type"))
   191  		RespondWithError(w, err)
   192  	}
   193  }
   194  
   195  func (s *exampleServiceServer) servePingJSON(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   196  	var err error
   197  	ctx = context.WithValue(ctx, MethodNameCtxKey, "Ping")
   198  
   199  	// Call service method
   200  	func() {
   201  		defer func() {
   202  			// In case of a panic, serve a 500 error and then panic.
   203  			if rr := recover(); rr != nil {
   204  				RespondWithError(w, ErrorInternal("internal service panic"))
   205  				panic(rr)
   206  			}
   207  		}()
   208  		err = s.ExampleService.Ping(ctx)
   209  	}()
   210  
   211  	if err != nil {
   212  		RespondWithError(w, err)
   213  		return
   214  	}
   215  
   216  	w.Header().Set("Content-Type", "application/json")
   217  	w.WriteHeader(http.StatusOK)
   218  }
   219  
   220  func (s *exampleServiceServer) serveStatus(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   221  	header := r.Header.Get("Content-Type")
   222  	i := strings.Index(header, ";")
   223  	if i == -1 {
   224  		i = len(header)
   225  	}
   226  
   227  	switch strings.TrimSpace(strings.ToLower(header[:i])) {
   228  	case "application/json":
   229  		s.serveStatusJSON(ctx, w, r)
   230  	default:
   231  		err := Errorf(ErrBadRoute, "unexpected Content-Type: %q", r.Header.Get("Content-Type"))
   232  		RespondWithError(w, err)
   233  	}
   234  }
   235  
   236  func (s *exampleServiceServer) serveStatusJSON(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   237  	var err error
   238  	ctx = context.WithValue(ctx, MethodNameCtxKey, "Status")
   239  
   240  	// Call service method
   241  	var ret0 bool
   242  	func() {
   243  		defer func() {
   244  			// In case of a panic, serve a 500 error and then panic.
   245  			if rr := recover(); rr != nil {
   246  				RespondWithError(w, ErrorInternal("internal service panic"))
   247  				panic(rr)
   248  			}
   249  		}()
   250  		ret0, err = s.ExampleService.Status(ctx)
   251  	}()
   252  	respContent := struct {
   253  		Ret0 bool `json:"status"`
   254  	}{ret0}
   255  
   256  	if err != nil {
   257  		RespondWithError(w, err)
   258  		return
   259  	}
   260  	respBody, err := json.Marshal(respContent)
   261  	if err != nil {
   262  		err = WrapError(ErrInternal, err, "failed to marshal json response")
   263  		RespondWithError(w, err)
   264  		return
   265  	}
   266  
   267  	w.Header().Set("Content-Type", "application/json")
   268  	w.WriteHeader(http.StatusOK)
   269  	w.Write(respBody)
   270  }
   271  
   272  func (s *exampleServiceServer) serveVersion(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   273  	header := r.Header.Get("Content-Type")
   274  	i := strings.Index(header, ";")
   275  	if i == -1 {
   276  		i = len(header)
   277  	}
   278  
   279  	switch strings.TrimSpace(strings.ToLower(header[:i])) {
   280  	case "application/json":
   281  		s.serveVersionJSON(ctx, w, r)
   282  	default:
   283  		err := Errorf(ErrBadRoute, "unexpected Content-Type: %q", r.Header.Get("Content-Type"))
   284  		RespondWithError(w, err)
   285  	}
   286  }
   287  
   288  func (s *exampleServiceServer) serveVersionJSON(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   289  	var err error
   290  	ctx = context.WithValue(ctx, MethodNameCtxKey, "Version")
   291  
   292  	// Call service method
   293  	var ret0 *Version
   294  	func() {
   295  		defer func() {
   296  			// In case of a panic, serve a 500 error and then panic.
   297  			if rr := recover(); rr != nil {
   298  				RespondWithError(w, ErrorInternal("internal service panic"))
   299  				panic(rr)
   300  			}
   301  		}()
   302  		ret0, err = s.ExampleService.Version(ctx)
   303  	}()
   304  	respContent := struct {
   305  		Ret0 *Version `json:"version"`
   306  	}{ret0}
   307  
   308  	if err != nil {
   309  		RespondWithError(w, err)
   310  		return
   311  	}
   312  	respBody, err := json.Marshal(respContent)
   313  	if err != nil {
   314  		err = WrapError(ErrInternal, err, "failed to marshal json response")
   315  		RespondWithError(w, err)
   316  		return
   317  	}
   318  
   319  	w.Header().Set("Content-Type", "application/json")
   320  	w.WriteHeader(http.StatusOK)
   321  	w.Write(respBody)
   322  }
   323  
   324  func (s *exampleServiceServer) serveGetUser(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   325  	header := r.Header.Get("Content-Type")
   326  	i := strings.Index(header, ";")
   327  	if i == -1 {
   328  		i = len(header)
   329  	}
   330  
   331  	switch strings.TrimSpace(strings.ToLower(header[:i])) {
   332  	case "application/json":
   333  		s.serveGetUserJSON(ctx, w, r)
   334  	default:
   335  		err := Errorf(ErrBadRoute, "unexpected Content-Type: %q", r.Header.Get("Content-Type"))
   336  		RespondWithError(w, err)
   337  	}
   338  }
   339  
   340  func (s *exampleServiceServer) serveGetUserJSON(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   341  	var err error
   342  	ctx = context.WithValue(ctx, MethodNameCtxKey, "GetUser")
   343  	reqContent := struct {
   344  		Arg0 map[string]string `json:"header"`
   345  		Arg1 uint64            `json:"userID"`
   346  	}{}
   347  
   348  	reqBody, err := ioutil.ReadAll(r.Body)
   349  	if err != nil {
   350  		err = WrapError(ErrInternal, err, "failed to read request data")
   351  		RespondWithError(w, err)
   352  		return
   353  	}
   354  	defer r.Body.Close()
   355  
   356  	err = json.Unmarshal(reqBody, &reqContent)
   357  	if err != nil {
   358  		err = WrapError(ErrInvalidArgument, err, "failed to unmarshal request data")
   359  		RespondWithError(w, err)
   360  		return
   361  	}
   362  
   363  	// Call service method
   364  	var ret0 uint32
   365  	var ret1 *User
   366  	func() {
   367  		defer func() {
   368  			// In case of a panic, serve a 500 error and then panic.
   369  			if rr := recover(); rr != nil {
   370  				RespondWithError(w, ErrorInternal("internal service panic"))
   371  				panic(rr)
   372  			}
   373  		}()
   374  		ret0, ret1, err = s.ExampleService.GetUser(ctx, reqContent.Arg0, reqContent.Arg1)
   375  	}()
   376  	respContent := struct {
   377  		Ret0 uint32 `json:"code"`
   378  		Ret1 *User  `json:"user"`
   379  	}{ret0, ret1}
   380  
   381  	if err != nil {
   382  		RespondWithError(w, err)
   383  		return
   384  	}
   385  	respBody, err := json.Marshal(respContent)
   386  	if err != nil {
   387  		err = WrapError(ErrInternal, err, "failed to marshal json response")
   388  		RespondWithError(w, err)
   389  		return
   390  	}
   391  
   392  	w.Header().Set("Content-Type", "application/json")
   393  	w.WriteHeader(http.StatusOK)
   394  	w.Write(respBody)
   395  }
   396  
   397  func (s *exampleServiceServer) serveFindUser(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   398  	header := r.Header.Get("Content-Type")
   399  	i := strings.Index(header, ";")
   400  	if i == -1 {
   401  		i = len(header)
   402  	}
   403  
   404  	switch strings.TrimSpace(strings.ToLower(header[:i])) {
   405  	case "application/json":
   406  		s.serveFindUserJSON(ctx, w, r)
   407  	default:
   408  		err := Errorf(ErrBadRoute, "unexpected Content-Type: %q", r.Header.Get("Content-Type"))
   409  		RespondWithError(w, err)
   410  	}
   411  }
   412  
   413  func (s *exampleServiceServer) serveFindUserJSON(ctx context.Context, w http.ResponseWriter, r *http.Request) {
   414  	var err error
   415  	ctx = context.WithValue(ctx, MethodNameCtxKey, "FindUser")
   416  	reqContent := struct {
   417  		Arg0 *SearchFilter `json:"s"`
   418  	}{}
   419  
   420  	reqBody, err := ioutil.ReadAll(r.Body)
   421  	if err != nil {
   422  		err = WrapError(ErrInternal, err, "failed to read request data")
   423  		RespondWithError(w, err)
   424  		return
   425  	}
   426  	defer r.Body.Close()
   427  
   428  	err = json.Unmarshal(reqBody, &reqContent)
   429  	if err != nil {
   430  		err = WrapError(ErrInvalidArgument, err, "failed to unmarshal request data")
   431  		RespondWithError(w, err)
   432  		return
   433  	}
   434  
   435  	// Call service method
   436  	var ret0 string
   437  	var ret1 *User
   438  	func() {
   439  		defer func() {
   440  			// In case of a panic, serve a 500 error and then panic.
   441  			if rr := recover(); rr != nil {
   442  				RespondWithError(w, ErrorInternal("internal service panic"))
   443  				panic(rr)
   444  			}
   445  		}()
   446  		ret0, ret1, err = s.ExampleService.FindUser(ctx, reqContent.Arg0)
   447  	}()
   448  	respContent := struct {
   449  		Ret0 string `json:"name"`
   450  		Ret1 *User  `json:"user"`
   451  	}{ret0, ret1}
   452  
   453  	if err != nil {
   454  		RespondWithError(w, err)
   455  		return
   456  	}
   457  	respBody, err := json.Marshal(respContent)
   458  	if err != nil {
   459  		err = WrapError(ErrInternal, err, "failed to marshal json response")
   460  		RespondWithError(w, err)
   461  		return
   462  	}
   463  
   464  	w.Header().Set("Content-Type", "application/json")
   465  	w.WriteHeader(http.StatusOK)
   466  	w.Write(respBody)
   467  }
   468  
   469  func RespondWithError(w http.ResponseWriter, err error) {
   470  	rpcErr, ok := err.(Error)
   471  	if !ok {
   472  		rpcErr = WrapError(ErrInternal, err, "webrpc error")
   473  	}
   474  
   475  	statusCode := HTTPStatusFromErrorCode(rpcErr.Code())
   476  
   477  	w.Header().Set("Content-Type", "application/json")
   478  	w.WriteHeader(statusCode)
   479  
   480  	respBody, _ := json.Marshal(rpcErr.Payload())
   481  	w.Write(respBody)
   482  }
   483  
   484  //
   485  // Client
   486  //
   487  
   488  const ExampleServicePathPrefix = "/rpc/ExampleService/"
   489  
   490  type exampleServiceClient struct {
   491  	client HTTPClient
   492  	urls   [5]string
   493  }
   494  
   495  func NewExampleServiceClient(addr string, client HTTPClient) ExampleService {
   496  	prefix := urlBase(addr) + ExampleServicePathPrefix
   497  	urls := [5]string{
   498  		prefix + "Ping",
   499  		prefix + "Status",
   500  		prefix + "Version",
   501  		prefix + "GetUser",
   502  		prefix + "FindUser",
   503  	}
   504  	return &exampleServiceClient{
   505  		client: client,
   506  		urls:   urls,
   507  	}
   508  }
   509  
   510  func (c *exampleServiceClient) Ping(ctx context.Context) error {
   511  
   512  	err := doJSONRequest(ctx, c.client, c.urls[0], nil, nil)
   513  	return err
   514  }
   515  
   516  func (c *exampleServiceClient) Status(ctx context.Context) (bool, error) {
   517  	out := struct {
   518  		Ret0 bool `json:"status"`
   519  	}{}
   520  
   521  	err := doJSONRequest(ctx, c.client, c.urls[1], nil, &out)
   522  	return out.Ret0, err
   523  }
   524  
   525  func (c *exampleServiceClient) Version(ctx context.Context) (*Version, error) {
   526  	out := struct {
   527  		Ret0 *Version `json:"version"`
   528  	}{}
   529  
   530  	err := doJSONRequest(ctx, c.client, c.urls[2], nil, &out)
   531  	return out.Ret0, err
   532  }
   533  
   534  func (c *exampleServiceClient) GetUser(ctx context.Context, header map[string]string, userID uint64) (uint32, *User, error) {
   535  	in := struct {
   536  		Arg0 map[string]string `json:"header"`
   537  		Arg1 uint64            `json:"userID"`
   538  	}{header, userID}
   539  	out := struct {
   540  		Ret0 uint32 `json:"code"`
   541  		Ret1 *User  `json:"user"`
   542  	}{}
   543  
   544  	err := doJSONRequest(ctx, c.client, c.urls[3], in, &out)
   545  	return out.Ret0, out.Ret1, err
   546  }
   547  
   548  func (c *exampleServiceClient) FindUser(ctx context.Context, s *SearchFilter) (string, *User, error) {
   549  	in := struct {
   550  		Arg0 *SearchFilter `json:"s"`
   551  	}{s}
   552  	out := struct {
   553  		Ret0 string `json:"name"`
   554  		Ret1 *User  `json:"user"`
   555  	}{}
   556  
   557  	err := doJSONRequest(ctx, c.client, c.urls[4], in, &out)
   558  	return out.Ret0, out.Ret1, err
   559  }
   560  
   561  // HTTPClient is the interface used by generated clients to send HTTP requests.
   562  // It is fulfilled by *(net/http).Client, which is sufficient for most users.
   563  // Users can provide their own implementation for special retry policies.
   564  type HTTPClient interface {
   565  	Do(req *http.Request) (*http.Response, error)
   566  }
   567  
   568  // urlBase helps ensure that addr specifies a scheme. If it is unparsable
   569  // as a URL, it returns addr unchanged.
   570  func urlBase(addr string) string {
   571  	// If the addr specifies a scheme, use it. If not, default to
   572  	// http. If url.Parse fails on it, return it unchanged.
   573  	url, err := url.Parse(addr)
   574  	if err != nil {
   575  		return addr
   576  	}
   577  	if url.Scheme == "" {
   578  		url.Scheme = "http"
   579  	}
   580  	return url.String()
   581  }
   582  
   583  // newRequest makes an http.Request from a client, adding common headers.
   584  func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType string) (*http.Request, error) {
   585  	req, err := http.NewRequest("POST", url, reqBody)
   586  	if err != nil {
   587  		return nil, err
   588  	}
   589  	req.Header.Set("Accept", contentType)
   590  	req.Header.Set("Content-Type", contentType)
   591  	if headers, ok := HTTPRequestHeaders(ctx); ok {
   592  		for k := range headers {
   593  			for _, v := range headers[k] {
   594  				req.Header.Add(k, v)
   595  			}
   596  		}
   597  	}
   598  	return req, nil
   599  }
   600  
   601  // doJSONRequest is common code to make a request to the remote service.
   602  func doJSONRequest(ctx context.Context, client HTTPClient, url string, in, out interface{}) error {
   603  	reqBody, err := json.Marshal(in)
   604  	if err != nil {
   605  		return clientError("failed to marshal json request", err)
   606  	}
   607  	if err = ctx.Err(); err != nil {
   608  		return clientError("aborted because context was done", err)
   609  	}
   610  
   611  	req, err := newRequest(ctx, url, bytes.NewBuffer(reqBody), "application/json")
   612  	if err != nil {
   613  		return clientError("could not build request", err)
   614  	}
   615  	resp, err := client.Do(req)
   616  	if err != nil {
   617  		return clientError("request failed", err)
   618  	}
   619  
   620  	defer func() {
   621  		cerr := resp.Body.Close()
   622  		if err == nil && cerr != nil {
   623  			err = clientError("failed to close response body", cerr)
   624  		}
   625  	}()
   626  
   627  	if err = ctx.Err(); err != nil {
   628  		return clientError("aborted because context was done", err)
   629  	}
   630  
   631  	if resp.StatusCode != 200 {
   632  		return errorFromResponse(resp)
   633  	}
   634  
   635  	if out != nil {
   636  		respBody, err := ioutil.ReadAll(resp.Body)
   637  		if err != nil {
   638  			return clientError("failed to read response body", err)
   639  		}
   640  
   641  		err = json.Unmarshal(respBody, &out)
   642  		if err != nil {
   643  			return clientError("failed to unmarshal json response body", err)
   644  		}
   645  		if err = ctx.Err(); err != nil {
   646  			return clientError("aborted because context was done", err)
   647  		}
   648  	}
   649  
   650  	return nil
   651  }
   652  
   653  // errorFromResponse builds a webrpc Error from a non-200 HTTP response.
   654  func errorFromResponse(resp *http.Response) Error {
   655  	respBody, err := ioutil.ReadAll(resp.Body)
   656  	if err != nil {
   657  		return clientError("failed to read server error response body", err)
   658  	}
   659  
   660  	var respErr ErrorPayload
   661  	if err := json.Unmarshal(respBody, &respErr); err != nil {
   662  		return clientError("failed unmarshal error response", err)
   663  	}
   664  
   665  	errCode := ErrorCode(respErr.Code)
   666  
   667  	if HTTPStatusFromErrorCode(errCode) == 0 {
   668  		return ErrorInternal("invalid code returned from server error response: %s", respErr.Code)
   669  	}
   670  
   671  	return &rpcErr{
   672  		code:  errCode,
   673  		msg:   respErr.Msg,
   674  		cause: errors.New(respErr.Cause),
   675  	}
   676  }
   677  
   678  func clientError(desc string, err error) Error {
   679  	return WrapError(ErrInternal, err, desc)
   680  }
   681  
   682  func WithHTTPRequestHeaders(ctx context.Context, h http.Header) (context.Context, error) {
   683  	if _, ok := h["Accept"]; ok {
   684  		return nil, errors.New("provided header cannot set Accept")
   685  	}
   686  	if _, ok := h["Content-Type"]; ok {
   687  		return nil, errors.New("provided header cannot set Content-Type")
   688  	}
   689  
   690  	copied := make(http.Header, len(h))
   691  	for k, vv := range h {
   692  		if vv == nil {
   693  			copied[k] = nil
   694  			continue
   695  		}
   696  		copied[k] = make([]string, len(vv))
   697  		copy(copied[k], vv)
   698  	}
   699  
   700  	return context.WithValue(ctx, HTTPClientRequestHeadersCtxKey, copied), nil
   701  }
   702  
   703  func HTTPRequestHeaders(ctx context.Context) (http.Header, bool) {
   704  	h, ok := ctx.Value(HTTPClientRequestHeadersCtxKey).(http.Header)
   705  	return h, ok
   706  }
   707  
   708  //
   709  // Helpers
   710  //
   711  
   712  type ErrorPayload struct {
   713  	Status int    `json:"status"`
   714  	Code   string `json:"code"`
   715  	Cause  string `json:"cause,omitempty"`
   716  	Msg    string `json:"msg"`
   717  	Error  string `json:"error"`
   718  }
   719  
   720  type Error interface {
   721  	// Code is of the valid error codes
   722  	Code() ErrorCode
   723  
   724  	// Msg returns a human-readable, unstructured messages describing the error
   725  	Msg() string
   726  
   727  	// Cause is reason for the error
   728  	Cause() error
   729  
   730  	// Error returns a string of the form "webrpc error <Code>: <Msg>"
   731  	Error() string
   732  
   733  	// Error response payload
   734  	Payload() ErrorPayload
   735  }
   736  
   737  func Errorf(code ErrorCode, msgf string, args ...interface{}) Error {
   738  	msg := fmt.Sprintf(msgf, args...)
   739  	if IsValidErrorCode(code) {
   740  		return &rpcErr{code: code, msg: msg}
   741  	}
   742  	return &rpcErr{code: ErrInternal, msg: "invalid error type " + string(code)}
   743  }
   744  
   745  func WrapError(code ErrorCode, cause error, format string, args ...interface{}) Error {
   746  	msg := fmt.Sprintf(format, args...)
   747  	if IsValidErrorCode(code) {
   748  		return &rpcErr{code: code, msg: msg, cause: cause}
   749  	}
   750  	return &rpcErr{code: ErrInternal, msg: "invalid error type " + string(code), cause: cause}
   751  }
   752  
   753  func ErrorNotFound(format string, args ...interface{}) Error {
   754  	return Errorf(ErrNotFound, format, args...)
   755  }
   756  
   757  func ErrorInvalidArgument(argument string, validationMsg string) Error {
   758  	return Errorf(ErrInvalidArgument, argument+" "+validationMsg)
   759  }
   760  
   761  func ErrorRequiredArgument(argument string) Error {
   762  	return ErrorInvalidArgument(argument, "is required")
   763  }
   764  
   765  func ErrorInternal(format string, args ...interface{}) Error {
   766  	return Errorf(ErrInternal, format, args...)
   767  }
   768  
   769  type ErrorCode string
   770  
   771  const (
   772  	// Canceled indicates the operation was cancelled (typically by the caller).
   773  	ErrCanceled ErrorCode = "canceled"
   774  
   775  	// Unknown error. For example when handling errors raised by APIs that do not
   776  	// return enough error information.
   777  	ErrUnknown ErrorCode = "unknown"
   778  
   779  	// InvalidArgument indicates client specified an invalid argument. It
   780  	// indicates arguments that are problematic regardless of the state of the
   781  	// system (i.e. a malformed file name, required argument, number out of range,
   782  	// etc.).
   783  	ErrInvalidArgument ErrorCode = "invalid argument"
   784  
   785  	// DeadlineExceeded means operation expired before completion. For operations
   786  	// that change the state of the system, this error may be returned even if the
   787  	// operation has completed successfully (timeout).
   788  	ErrDeadlineExceeded ErrorCode = "deadline exceeded"
   789  
   790  	// NotFound means some requested entity was not found.
   791  	ErrNotFound ErrorCode = "not found"
   792  
   793  	// BadRoute means that the requested URL path wasn't routable to a webrpc
   794  	// service and method. This is returned by the generated server, and usually
   795  	// shouldn't be returned by applications. Instead, applications should use
   796  	// NotFound or Unimplemented.
   797  	ErrBadRoute ErrorCode = "bad route"
   798  
   799  	// AlreadyExists means an attempt to create an entity failed because one
   800  	// already exists.
   801  	ErrAlreadyExists ErrorCode = "already exists"
   802  
   803  	// PermissionDenied indicates the caller does not have permission to execute
   804  	// the specified operation. It must not be used if the caller cannot be
   805  	// identified (Unauthenticated).
   806  	ErrPermissionDenied ErrorCode = "permission denied"
   807  
   808  	// Unauthenticated indicates the request does not have valid authentication
   809  	// credentials for the operation.
   810  	ErrUnauthenticated ErrorCode = "unauthenticated"
   811  
   812  	// ResourceExhausted indicates some resource has been exhausted, perhaps a
   813  	// per-user quota, or perhaps the entire file system is out of space.
   814  	ErrResourceExhausted ErrorCode = "resource exhausted"
   815  
   816  	// FailedPrecondition indicates operation was rejected because the system is
   817  	// not in a state required for the operation's execution. For example, doing
   818  	// an rmdir operation on a directory that is non-empty, or on a non-directory
   819  	// object, or when having conflicting read-modify-write on the same resource.
   820  	ErrFailedPrecondition ErrorCode = "failed precondition"
   821  
   822  	// Aborted indicates the operation was aborted, typically due to a concurrency
   823  	// issue like sequencer check failures, transaction aborts, etc.
   824  	ErrAborted ErrorCode = "aborted"
   825  
   826  	// OutOfRange means operation was attempted past the valid range. For example,
   827  	// seeking or reading past end of a paginated collection.
   828  	//
   829  	// Unlike InvalidArgument, this error indicates a problem that may be fixed if
   830  	// the system state changes (i.e. adding more items to the collection).
   831  	//
   832  	// There is a fair bit of overlap between FailedPrecondition and OutOfRange.
   833  	// We recommend using OutOfRange (the more specific error) when it applies so
   834  	// that callers who are iterating through a space can easily look for an
   835  	// OutOfRange error to detect when they are done.
   836  	ErrOutOfRange ErrorCode = "out of range"
   837  
   838  	// Unimplemented indicates operation is not implemented or not
   839  	// supported/enabled in this service.
   840  	ErrUnimplemented ErrorCode = "unimplemented"
   841  
   842  	// Internal errors. When some invariants expected by the underlying system
   843  	// have been broken. In other words, something bad happened in the library or
   844  	// backend service. Do not confuse with HTTP Internal Server Error; an
   845  	// Internal error could also happen on the client code, i.e. when parsing a
   846  	// server response.
   847  	ErrInternal ErrorCode = "internal"
   848  
   849  	// Unavailable indicates the service is currently unavailable. This is a most
   850  	// likely a transient condition and may be corrected by retrying with a
   851  	// backoff.
   852  	ErrUnavailable ErrorCode = "unavailable"
   853  
   854  	// DataLoss indicates unrecoverable data loss or corruption.
   855  	ErrDataLoss ErrorCode = "data loss"
   856  
   857  	// ErrNone is the zero-value, is considered an empty error and should not be
   858  	// used.
   859  	ErrNone ErrorCode = ""
   860  )
   861  
   862  func HTTPStatusFromErrorCode(code ErrorCode) int {
   863  	switch code {
   864  	case ErrCanceled:
   865  		return 408 // RequestTimeout
   866  	case ErrUnknown:
   867  		return 500 // Internal Server Error
   868  	case ErrInvalidArgument:
   869  		return 400 // BadRequest
   870  	case ErrDeadlineExceeded:
   871  		return 408 // RequestTimeout
   872  	case ErrNotFound:
   873  		return 404 // Not Found
   874  	case ErrBadRoute:
   875  		return 404 // Not Found
   876  	case ErrAlreadyExists:
   877  		return 409 // Conflict
   878  	case ErrPermissionDenied:
   879  		return 403 // Forbidden
   880  	case ErrUnauthenticated:
   881  		return 401 // Unauthorized
   882  	case ErrResourceExhausted:
   883  		return 403 // Forbidden
   884  	case ErrFailedPrecondition:
   885  		return 412 // Precondition Failed
   886  	case ErrAborted:
   887  		return 409 // Conflict
   888  	case ErrOutOfRange:
   889  		return 400 // Bad Request
   890  	case ErrUnimplemented:
   891  		return 501 // Not Implemented
   892  	case ErrInternal:
   893  		return 500 // Internal Server Error
   894  	case ErrUnavailable:
   895  		return 503 // Service Unavailable
   896  	case ErrDataLoss:
   897  		return 500 // Internal Server Error
   898  	case ErrNone:
   899  		return 200 // OK
   900  	default:
   901  		return 0 // Invalid!
   902  	}
   903  }
   904  
   905  func IsErrorCode(err error, code ErrorCode) bool {
   906  	if rpcErr, ok := err.(Error); ok {
   907  		if rpcErr.Code() == code {
   908  			return true
   909  		}
   910  	}
   911  	return false
   912  }
   913  
   914  func IsValidErrorCode(code ErrorCode) bool {
   915  	return HTTPStatusFromErrorCode(code) != 0
   916  }
   917  
   918  type rpcErr struct {
   919  	code  ErrorCode
   920  	msg   string
   921  	cause error
   922  }
   923  
   924  func (e *rpcErr) Code() ErrorCode {
   925  	return e.code
   926  }
   927  
   928  func (e *rpcErr) Msg() string {
   929  	return e.msg
   930  }
   931  
   932  func (e *rpcErr) Cause() error {
   933  	return e.cause
   934  }
   935  
   936  func (e *rpcErr) Error() string {
   937  	if e.cause != nil && e.cause.Error() != "" {
   938  		if e.msg != "" {
   939  			return fmt.Sprintf("webrpc %s error: %s -- %s", e.code, e.cause.Error(), e.msg)
   940  		} else {
   941  			return fmt.Sprintf("webrpc %s error: %s", e.code, e.cause.Error())
   942  		}
   943  	} else {
   944  		return fmt.Sprintf("webrpc %s error: %s", e.code, e.msg)
   945  	}
   946  }
   947  
   948  func (e *rpcErr) Payload() ErrorPayload {
   949  	statusCode := HTTPStatusFromErrorCode(e.Code())
   950  	errPayload := ErrorPayload{
   951  		Status: statusCode,
   952  		Code:   string(e.Code()),
   953  		Msg:    e.Msg(),
   954  		Error:  e.Error(),
   955  	}
   956  	if e.Cause() != nil {
   957  		errPayload.Cause = e.Cause().Error()
   958  	}
   959  	return errPayload
   960  }
   961  
   962  type contextKey struct {
   963  	name string
   964  }
   965  
   966  func (k *contextKey) String() string {
   967  	return "webrpc context value " + k.name
   968  }
   969  
   970  var (
   971  	// For Client
   972  	HTTPClientRequestHeadersCtxKey = &contextKey{"HTTPClientRequestHeaders"}
   973  
   974  	// For Server
   975  	HTTPResponseWriterCtxKey = &contextKey{"HTTPResponseWriter"}
   976  
   977  	HTTPRequestCtxKey = &contextKey{"HTTPRequest"}
   978  
   979  	ServiceNameCtxKey = &contextKey{"ServiceName"}
   980  
   981  	MethodNameCtxKey = &contextKey{"MethodName"}
   982  )