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 )