trpc.group/trpc-go/trpc-go@v1.0.3/restful/README.md (about)

     1  English | [中文](README.zh_CN.md)
     2  
     3  # Introduction
     4  
     5  The tRPC framework uses PB to define services, but it is still a common requirement to provide REST-style APIs based on
     6  the HTTP protocol. Unifying RPC and REST is not an easy task, and the tRPC-Go framework's HTTP RPC protocol aims to 
     7  define the same set of PB files that can be called through RPC (through the client's NewXXXClientProxy provided by 
     8  the stub code) or through native HTTP requests. However, such HTTP calls do not comply with the RESTful specification,
     9  for example, custom routes cannot be defined, wildcards are not supported, and the response body is empty when an 
    10  error occurs (the error message can only be placed in the response header). Therefore, trpc additionally support the
    11  RESTful protocol and no longer attempt to force RPC and REST together. If the service is specified as RESTful 
    12  protocol, it does not support the use of stub code calls and only supports HTTP client calls. However, the benefit 
    13  of this approach is that it can provide APIs that comply with RESTful specification through the protobuf annotation
    14  in the same set of PB files and can use various tRPC framework plugins or filters.
    15  
    16  # Principles
    17  ## Transcoder
    18  
    19  Unlike other protocol plugins in the tRPC-Go framework, the RESTful protocol plugin implements a tRPC and HTTP/JSON 
    20  transcoder based on the tRPC HttpRule at the Transport layer. This eliminates the need for Codec encoding and decoding 
    21  processes as PB is directly obtained after transcoding and processed in the REST Stub generated by the trpc tool.
    22  
    23  ![restful-overall-design](/.resources/user_guide/server/restful/restful-overall-design.png)
    24  
    25  ## Transcoder Core: HttpRule
    26  For a service defined using the same set of PB files, support for both RPC and REST calls requires a set of rules to 
    27  indicate the mapping between RPC and REST, or more precisely, the transcoding between PB and HTTP/JSON. In the industry, 
    28  Google has defined such rules, namely HttpRule, which tRPC's implementation also references. tRPC's HttpRule needs to 
    29  be specified in the PB file as an option: option (trpc.api.http), which means that the same set of PB-defined services 
    30  support both RPC and REST calls. 
    31  Now, let's take an example of how to bind HttpRule to the `SayHello` method in a Greeter service:
    32  
    33  ```protobuf
    34  service Greeter {
    35    rpc SayHello(HelloRequest) returns (HelloReply) {
    36      option (trpc.api.http) = {
    37        post: "/v1/foobar/{name}"
    38        body: "*"
    39        additional_bindings: {
    40          post: "/v1/foo/{name=/x/y/**}"
    41          body: "single_nested"
    42          response_body: "message"
    43        }
    44      };
    45    }
    46  }
    47  message HelloRequest {
    48    string name = 1;
    49    Nested single_nested = 2;
    50    oneof oneof_value {
    51      google.protobuf.Empty oneof_empty = 3;
    52      string oneof_string = 4;
    53    }
    54  }
    55  message Nested {
    56    string name = 1;
    57  }
    58  message HelloReply {
    59    string message = 1;
    60  }
    61  ```
    62  
    63  Through the above example, it can be seen that HttpRule has the following fields: 
    64  
    65  > - The "body" field indicates which field of the PB request message is carried in the HTTP request body. 
    66  > - The "response_body" field indicates which field of the PB response message is carried in the HTTP response body. 
    67  > - The "additional_bindings" field represents additional HttpRule, meaning that an RPC method can be bound to multiple HttpRules.
    68  
    69  
    70  Combining the specific rules of HttpRule, let's take a look at how the HTTP request/response are mapped to HelloRequest and 
    71  HelloReply in the above example:
    72  
    73  > When mapping, the "leaf fields" of the RPC request Proto Message (which refers to the fields that cannot be nested and 
    74  > traversed further, in the above example HelloRequest.Name is a leaf field, while HelloRequest.SingleNested is not, 
    75  > and only HelloRequest.SingleNested.Name is) are mapped in three ways:
    76  >
    77  > * The leaf fields are referenced by the URL Path of the HttpRule: If the URL Path of the HttpRule references one or 
    78  > more fields in the RPC request message, then these fields are passed through the HTTP request URL Path. However, these
    79  > fields must be non-array fields of native basic types, and do not support fields of message types or array fields. 
    80  > In the above example, if the HttpRule selector field is defined as post: "/v1/foobar/{name}", then the value of the 
    81  > HelloRequest.Name field is mapped to "xyz" when the HTTP request POST /v1/foobar/xyz is made.
    82  > * The leaf fields are referenced by the Body of the HttpRule: If the field to be mapped is specified in the Body of 
    83  > the HttpRule, then this field in the RPC request message is passed through the HTTP request Body. In the above example, 
    84  > if the HttpRule body field is defined as body: "name", then the value of the HelloRequest.Name field is mapped to 
    85  > "xyz" when the HTTP request Body is "xyz".
    86  > * Other leaf fields: Other leaf fields are automatically turned into URL query parameters, and if they are repeated 
    87  > fields, multiple queries of the same URL query parameter are supported. In the above example, if the selector in 
    88  > the additional_bindings specifies post: "/v1/foo/{name=/x/y/*}", and the body is not specified as body: "", then 
    89  > all fields in HelloRequest except HelloRequest.Name are passed through URL query parameters. For example, if the 
    90  > HTTP request POST /v1/foo/x/y/z/xyz?single_nested.name=abc is made, the value of the HelloRequest.Name field is 
    91  > mapped to "/x/y/z/xyz", and the value of the HelloRequest.SingleNested.Name field is mapped to "abc".
    92  >
    93  > Supplement:
    94  > * If the field is not specified in the Body of the HttpRule and is defined as "", then each field of the request 
    95  > message that is not bound by the URL path is passed through the Body of the HTTP request. That is, the URL query 
    96  > parameters are invalid.
    97  > * If the Body of the HttpRule is empty, then every field of the request message that is not bound by the URL path 
    98  > becomes a URL query parameter. That is, the Body is invalid.
    99  > * If the response_body of the HttpRule is empty, then the entire PB response message will be serialized into the 
   100  > HTTP response Body. In the above example, if response_body is "", then the serialized HelloReply is the HTTP response Body.
   101  > * HttpRule body and response_body fields can reference fields of the PB Message, which may or may not be leaf fields, 
   102  > but must be first-level fields in the PB Message. For example, for HelloRequest, HttpRule body can be defined as 
   103  > "name" or "single_nested", but not as "single_nested.name".
   104  
   105  Now let's take a look at a few more examples to better understand how to use HttpRule.
   106  
   107  
   108  **1. Take the content that matches "messages/\*" inside the URL Path as the value of the "name" field:**
   109  
   110  ```protobuf
   111  service Messaging {
   112    rpc GetMessage(GetMessageRequest) returns (Message) {
   113      option (trpc.api.http) = {
   114          get: "/v1/{name=messages/*}"
   115      };
   116    }
   117  }
   118  message GetMessageRequest {
   119    string name = 1; // Mapped to URL path.
   120  }
   121  message Message {
   122    string text = 1; // The resource content.
   123  }
   124  ```
   125  
   126  The HttpRule above results in the following mapping:
   127  
   128  | HTTP                    | tRPC                                |
   129  | ----------------------- | ----------------------------------- |
   130  | GET /v1/messages/123456 | GetMessage(name: "messages/123456") |
   131  
   132  
   133  
   134  **2. A more complex nested message construction, using "123456" in the URL Path as the value of "message_id", and the 
   135  value of "sub.subfield" in the URL Path as the value of the "subfield" field in the nested message:**
   136  
   137  ```protobuf
   138  service Messaging {
   139    rpc GetMessage(GetMessageRequest) returns (Message) {
   140      option (trpc.api.http) = {
   141          get:"/v1/messages/{message_id}"
   142      };
   143    }
   144  }
   145  message GetMessageRequest {
   146    message SubMessage {
   147      string subfield = 1;
   148    }
   149    string message_id = 1; // Mapped to URL path.
   150    int64 revision = 2;    // Mapped to URL query parameter `revision`.
   151    SubMessage sub = 3;    // Mapped to URL query parameter `sub.subfield`.
   152  }
   153  ```
   154  
   155  The HttpRule above results in the following mapping:
   156  
   157  | HTTP                                                | tRPC                                                                          |
   158  | --------------------------------------------------- | ----------------------------------------------------------------------------- |
   159  | GET /v1/messages/123456?revision=2&sub.subfield=foo | GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo")) |
   160  
   161  
   162  
   163  **3. Parse the entire HTTP Body as a Message type, i.e. use "Hi!" as the value of "message.text":**
   164  
   165  
   166  ```protobuf
   167  service Messaging {
   168    rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
   169      option (trpc.api.http) = {
   170        post: "/v1/messages/{message_id}"
   171        body: "message"
   172      };
   173    }
   174  }
   175  message UpdateMessageRequest {
   176    string message_id = 1; // mapped to the URL
   177    Message message = 2;   // mapped to the body
   178  }
   179  ```
   180  
   181  The HttpRule above results in the following mapping:
   182  
   183  | HTTP                                       | tRPC                                                        |
   184  | ------------------------------------------ | ----------------------------------------------------------- |
   185  | POST /v1/messages/123456 { "text": "Hi!" } | UpdateMessage(message_id: "123456" message { text: "Hi!" }) |
   186  
   187  
   188  
   189  **4. Parse the field in the HTTP Body as the "text" field of the Message:**
   190  
   191  ```protobuf
   192  service Messaging {
   193    rpc UpdateMessage(Message) returns (Message) {
   194      option (trpc.api.http) = {
   195        post: "/v1/messages/{message_id}"
   196        body: "*"
   197      };
   198    }
   199  }
   200  message Message {
   201    string message_id = 1;
   202    string text = 2;
   203  }
   204  ```
   205  
   206  The HttpRule above results in the following mapping:
   207  
   208  | HTTP                                      | tRPC                                            |
   209  | ----------------------------------------- | ----------------------------------------------- |
   210  | POST/v1/messages/123456 { "text": "Hi!" } | UpdateMessage(message_id: "123456" text: "Hi!") |
   211  
   212  **5. Using additional_bindings to indicate APIs with additional bindings:**
   213  
   214  ```protobuf
   215  service Messaging {
   216    rpc GetMessage(GetMessageRequest) returns (Message) {
   217      option (trpc.api.http) = {
   218        get: "/v1/messages/{message_id}"
   219        additional_bindings {
   220          get: "/v1/users/{user_id}/messages/{message_id}"
   221        }
   222      };
   223    }
   224  }
   225  message GetMessageRequest {
   226    string message_id = 1;
   227    string user_id = 2;
   228  }
   229  ```
   230  
   231  The HttpRule above results in the following mapping:
   232  
   233  | HTTP                             | tRPC                                           |
   234  | -------------------------------- | ---------------------------------------------- |
   235  | GET /v1/messages/123456          | GetMessage(message_id: "123456")               |
   236  | GET /v1/users/me/messages/123456 | GetMessage(user_id: "me" message_id: "123456") |
   237  
   238  # Implementation
   239  
   240  Please refer to the [trpc-go/restful](/restful).
   241  
   242  # Examples
   243  
   244  After understanding HttpRule, let's take a look at how to enable tRPC-Go's RESTful service.
   245  
   246  **1. PB Definition**
   247  
   248  First, update the `trpc-cmdline` tool to the latest version. To use the **trpc.api.http** annotation, you need 
   249  to import a proto file:
   250  
   251  ```protobuf
   252  import "trpc/api/annotations.proto";
   253  ```
   254  
   255  Let's define a PB for a Greeter service:
   256  
   257  ```protobuf
   258  ...
   259  import "trpc/api/annotations.proto";
   260  service Greeter {
   261    rpc SayHello(HelloRequest) returns (HelloReply) {
   262      option (trpc.api.http) = {
   263        post: "/v1/foobar"
   264        body: "*"
   265        additional_bindings: {
   266          post: "/v1/foo/{name}"
   267        }
   268      };
   269    }
   270  }
   271  message HelloRequest {
   272    string name = 1;
   273    ...
   274  }  
   275  ...
   276  ```
   277  
   278  **2. Generating Stub Code**
   279  
   280  Use the `trpc create` command to generate stub code directly.
   281  
   282  **3. Configuration**
   283  
   284  Just like configuring other protocols, set the protocol configuration of the service in `trpc_go.yaml` to `restful`.
   285  
   286  ```yaml
   287  server: 
   288    ...
   289    service:                                         
   290      - name: trpc.test.helloworld.Greeter      
   291        ip: 127.0.0.1                            
   292        # nic: eth0
   293        port: 8080                
   294        network: tcp                             
   295        protocol: restful              
   296        timeout: 1000
   297  ```
   298  
   299  A more common scenario is to configure a tRPC protocol service and add a RESTful protocol service, so that one set of 
   300  PB files can simultaneously support providing both RPC services and RESTful services.
   301  
   302  ```yaml
   303  server: 
   304    ...
   305    service:                                         
   306      - name: trpc.test.helloworld.Greeter1      
   307        ip: 127.0.0.1                            
   308        # nic: eth0
   309        port: 12345                
   310        network: tcp                             
   311        protocol: trpc              
   312        timeout: 1000
   313      - name: trpc.test.helloworld.Greeter2      
   314        ip: 127.0.0.1                            
   315        # nic: eth0
   316        port: 54321                
   317        network: tcp                             
   318        protocol: restful              
   319        timeout: 1000
   320  ```
   321  
   322  **Note: Each service in tRPC must be configured with a different port number.**
   323  
   324  **4. starting the Service**
   325  
   326  Starting the service is the same as other protocols:
   327  
   328  ```go
   329  package main
   330  import (
   331      ...
   332      pb "trpc.group/trpc-go/trpc-go/examples/restful/helloworld"
   333  )
   334  func main() {
   335      s := trpc.NewServer()
   336      pb.RegisterGreeterService(s, &greeterServerImpl{})
   337      // Start
   338      if err := s.Serve(); err != nil {
   339          ...
   340      }
   341  }
   342  ```
   343  
   344  **5. Calling**
   345  
   346  Since you are building a RESTful service, please use any REST client to make calls. It is not supported to use the RPC 
   347  method of calling using NewXXXClientProxy.
   348  
   349  ```go
   350  package main
   351  import "net/http"
   352  func main() {
   353      ...
   354      // native HTTP invocation
   355      req, err := http.NewRequest("POST", "http://127.0.0.1:8080/v1/foobar", bytes.Newbuffer([]byte(`{"name": "xyz"}`)))
   356      if err != nil {
   357          ...
   358      }
   359      cli := http.Client{}
   360      resp, err := cli.Do(req)
   361      if err != nil {
   362          ...
   363      }
   364      ...
   365  }
   366  ```
   367  Of course, if you have configured a tRPC protocol service in step 3 [Configuration], you can still call the tRPC 
   368  protocol service using the RPC method of NewXXXClientProxy, but be sure to distinguish the port.
   369  
   370  
   371  **6. Mapping Custom HTTP Headers to RPC Context**
   372  
   373  HttpRule resolves the transcoding between tRPC message body and HTTP/JSON, but how can HTTP requests pass the RPC call 
   374  context? This requires defining the mapping of HTTP headers to RPC context.
   375  
   376  The HeaderMatcher for RESTful service is defined as follows:
   377  
   378  ```go
   379  type HeaderMatcher func(
   380      ctx context.Context,
   381      w http.ResponseWriter,
   382      r *http.Request,
   383      serviceName, methodName string,
   384  ) (context.Context, error)
   385  ```
   386  
   387  The default handling of HeaderMatcher is as follows:
   388  
   389  ```go
   390  var defaultHeaderMatcher = func(
   391      ctx context.Context,
   392      w http.ResponseWriter,
   393      req *http.Request,
   394      serviceName, methodName string,
   395  ) (context.Context, error) {
   396      // It is recommended to customize and pass the codec.Msg in the ctx, and specify the target service and method name.
   397      ctx, msg := codec.WithNewMessage(ctx)
   398      msg.WithCalleeServiceName(service)
   399      msg.WithServerRPCName(method)
   400      msg.WithSerializationType(codec.SerializationTypePB)
   401      return ctx, nil
   402  }
   403  ```
   404  
   405  You can set the HeaderMatcher through the `WithOptions` method:
   406  
   407  ```go
   408  service := server.New(server.WithRESTOptions(restful.WithHeaderMatcher(xxx)))
   409  ```
   410  
   411  **7. Customize the Response Handling [Set the Return Code for Successful Request Handling]**
   412  
   413  The "response_body" field in HttpRule specifies the RPC response, for example, in the above example, the "HelloReply" 
   414  needs to be serialized into the HTTP Response Body as a whole or for a specific field. However, users may want to 
   415  perform additional custom operations, such as setting the response code for successful requests.
   416  
   417  The custom response handling function for RESTful services is defined as follows:
   418  
   419  ```go
   420  type CustomResponseHandler func(
   421      ctx context.Context,
   422      w http.ResponseWriter,
   423      r *http.Request,
   424      resp proto.Message,
   425      body []byte,
   426  ) error
   427  ```
   428  
   429  The "trpc-go/restful" package provides a function that allows users to set the response code for successful request handling:
   430  
   431  ```go
   432  func SetStatusCodeOnSucceed(ctx context.Context, code int) {}
   433  ```
   434  
   435  The default custom response handling function is as follows:
   436  
   437  ```go
   438  var defaultResponseHandler = func(
   439      ctx context.Context,
   440      w http.ResponseWriter,
   441      r *http.Request,
   442      resp proto.Message,
   443      body []byte,
   444  ) error {
   445      // compress
   446      var writer io.Writer = w
   447      _, compressor := compressorForRequest(r)
   448      if compressor != nil {
   449          writeCloser, err := compressor.Compress(w)
   450          if err != nil {
   451              return fmt.Errorf("failed to compress resp body: %w", err)
   452          }
   453          defer writeCloser.Close()
   454          w.Header().Set(headerContentEncoding, compressor.ContentEncoding())
   455          writer = writeCloser
   456      }
   457      // Set StatusCode
   458      statusCode := GetStatusCodeOnSucceed(ctx)
   459      w.WriteHeader(statusCode)
   460      // Set body
   461      if statusCode != http.StatusNoContent && statusCode != http.StatusNotModified {
   462          writer.Write(body)
   463      }
   464      return nil
   465  }
   466  ```
   467  
   468  If using the default custom response handling function, users can set the return code in their own RPC handling functions
   469   (if not set, it will return 200 for success):
   470  
   471  ```go
   472  func (s *greeterServerImpl) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {   
   473      ...
   474      restful.SetStatusCodeOnSucceed(ctx, 200) // Set the return code for success.
   475      return rsp, nil
   476  }
   477  ```
   478  
   479  You can set the HeaderMatcher through the `WithOptions` method:
   480  
   481  ```go
   482  var xxxResponseHandler = func(
   483      ctx context.Context,
   484      w http.ResponseWriter,
   485      r *http.Request,
   486      resp proto.Message,
   487      body []byte,
   488  ) error {
   489      reply, ok := resp.(*pb.HelloReply)
   490      if !ok {
   491          return errors.New("xxx")
   492      }
   493      ...
   494      w.Header().Set("x", "y")
   495      expiration := time.Now()
   496      expiration := expiration.AddDate(1, 0, 0)
   497      cookie := http.Cookie{Name: "abc", Value: "def", Expires: expiration}
   498      http.SetCookie(w, &cookie)
   499      w.Write(body)
   500      return nil
   501  }
   502  ...
   503  service := server.New(server.WithRESTOptions(restful.WithResponseHandler(xxxResponseHandler)))
   504  ```
   505  
   506  **8. Custom Error Handling [Error Code]**
   507  
   508  The definition of the error handling function for RESTful services is as follows:
   509  
   510  ```go
   511  type ErrorHandler func(context.Context, http.ResponseWriter, *http.Request, error)
   512  ```
   513  
   514  You can set the HeaderMatcher through the `WithOptions` method:
   515  
   516  ```go
   517  var xxxErrorHandler = func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
   518      if err == errors.New("say hello failed") {
   519          w.WriteHeader(500)
   520      }
   521      ...
   522  }
   523  service := server.New(server.WithRESTOptions(restful.WithErrorHandler(xxxErrorHandler)))
   524  ```
   525  
   526  
   527  **Recommend using the default error handling function of the trpc-go/restful package or referring to the implementation
   528   to create your own error handling function.**
   529  
   530  Regarding **error codes:**
   531  
   532  If an error of the type defined in the "trpc-go/errs" package is returned during RPC processing, the default error 
   533  handling function of "trpc-go/restful" will map tRPC's error codes to HTTP error codes. If users want to decide 
   534  what error code is used for a specific error, they can use `WithStatusCode` defined in the "trpc-go/restful" package.
   535  
   536  
   537  ```go
   538  type WithStatusCode struct {
   539      StatusCode int
   540      Err        error
   541  }
   542  ```
   543  
   544  Wrap your own error in a function and return it, such as:
   545  
   546  ```go
   547  func (s *greeterServerImpl) SayHello(ctx context.Context, req *hpb.HelloRequest, rsp *hpb.HelloReply) (err error) {
   548      if req.Name != "xyz" {
   549          return &restful.WithStatusCode{
   550              StatusCode: 400,
   551              Err:        errors.New("test error"),
   552          }
   553      }
   554      return nil
   555  }
   556  ```
   557  
   558  If the error type is not the `Error` type defined by "trpc-go/errs" and is not wrapped with `WithStatusCode` defined 
   559  in the "trpc-go/restful" package, the default error code 500 will be returned.
   560  
   561  **9. Body Serialization and Compression**
   562  
   563  Like normal REST requests, it's specified through HTTP headers and supports several popular formats.
   564  
   565  > **Supported Content-Type (or Accept) for serialization: application/json, application/x-www-form-urlencoded, 
   566  > application/octet-stream. By default it is application/json.**
   567  
   568  Serialization interface is defined as follows:
   569  
   570  ```go
   571  type Serializer interface {
   572      // Marshal serializes the tRPC message or one of its fields into the HTTP body. 
   573      Marshal(v interface{}) ([]byte, error)
   574      // Unmarshal deserializes the HTTP body into the tRPC message or one of its fields. 
   575      Unmarshal(data []byte, v interface{}) error
   576      // Name Serializer Name
   577      Name() string
   578      // ContentType  is set when returning the HTTP response.
   579      ContentType() string
   580  }
   581  ```
   582  
   583  **Users can implement their own serializer and register it using the `restful.RegisterSerializer()` function.**
   584  
   585  > **Compression is supported through Content-Encoding (or Accept-Encoding): gzip. By default, there is no compression.**
   586  
   587  Compression interface is defined as follows:
   588  
   589  ```go
   590  type Compressor interface {
   591      // Compress 
   592      Compress(w io.Writer) (io.WriteCloser, error)
   593      // Decompress 
   594      Decompress(r io.Reader) (io.Reader, error)
   595      // Name represents the name of the compressor.
   596      Name() string
   597      // ContentEncoding represents the Content-Encoding that is set when returning the HTTP response.
   598      ContentEncoding() string
   599  }
   600  ```
   601  
   602  **Users can implement their own serializer and register it using the `restful.RegisterSerializer()` function.**
   603  
   604  **10. Cross-Origin Requests**
   605  
   606  RESTful also supports [trpc-filter/cors](https://github.com/trpc-ecosystem/go-filter/tree/main/cors) cross-origin requests 
   607  plugin. To use it, you need to add the HTTP OPTIONS method in pb by using `custom`, for example:
   608  
   609  ```protobuf
   610  service HelloTrpcGo {
   611    rpc Hello(HelloReq) returns (HelloRsp) {
   612      option (trpc.api.http) = {
   613        post: "/hello"
   614        body: "*"
   615        additional_bindings: {
   616          get: "/hello/{name}"
   617        }
   618        additional_bindings: {
   619          custom: { // use custom verb
   620            kind: "OPTIONS"
   621            path: "/hello"
   622          }
   623        }
   624      };
   625    }
   626  }
   627  ```
   628  
   629  Next, regenerate the stub code using the trpc-cmdline command-line tool.
   630  Finally, add the CORS plugin to the service interceptors.
   631  
   632  If you do not want to modify the protobuf file, RESTful also provides a code-based custom method for cross-origin requests.
   633  
   634  The RESTful protocol plugin will generate a corresponding http.Handler for each Service. You can retrieve it before 
   635  starting to listen, and replace it with you own custom http.Handler:
   636  
   637  
   638  ```go
   639  func allowCORS(h http.Handler) http.Handler {
   640      return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   641          if origin := r.Header.Get("Origin"); origin != "" {
   642              w.Header().Set("Access-Control-Allow-Origin", origin)
   643              if r.Method == "OPTIONS" && r.Header.Get("Access-Control-Request-Method") != "" {
   644                  preflightHandler(w, r)
   645                  return
   646              }
   647          }
   648          h.ServeHTTP(w, r)
   649      })
   650  }
   651  func main() {
   652      // set custom header matcher
   653      s := trpc.NewServer()
   654      //  register service implementation
   655      pb.RegisterPingService(s, &pingServiceImpl{})
   656      // retrieve restful.Router
   657      router := restful.GetRouter(pb.PingServer_ServiceDesc.ServiceName)
   658      // wrap it up and re-register it again
   659      restful.RegisterRouter(pb.PingServer_ServiceDesc.ServiceName, allowCORS(router))
   660      // start
   661      if err := s.Serve(); err != nil {
   662          log.Fatal(err)
   663      }
   664  }
   665  ```
   666  
   667  # Performance
   668  
   669  To improve performance, the RESTful protocol plugin also supports handling HTTP packets based on [fasthttp](https://github.com/valyala/fasthttp). 
   670  The performance of the RESTful protocol plugin is related to the complexity of the registered URL path and the method of 
   671  passing PB Message fields. Here is a comparison between the two modes in the simplest echo test scenario: 
   672  
   673  Test PB:
   674  
   675  ```protobuf
   676  service Greeter {
   677    rpc SayHello(HelloRequest) returns (HelloReply) {
   678      option (trpc.api.http) = {
   679        get: "/v1/foobar/{name}"
   680      };
   681    }
   682  }
   683  message HelloRequest {
   684    string name = 1;
   685  }
   686  message HelloReply {
   687    string message = 1;
   688  }
   689  ```
   690  
   691  Greeter implementation
   692  
   693  ```go
   694  type greeterServiceImpl struct{}
   695  func (s *greeterServiceImpl) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
   696      return &pb.HelloReply{Message: Name}, nil
   697  }
   698  ```
   699  
   700  Test machine: 8 cores 
   701  
   702  | mode              | QPS when P99 < 10ms |
   703  | ----------------- | ------------------- |
   704  | based on net/http | 16w                 |
   705  | base on fasthttp  | 25w                 |
   706  
   707  - To enable fasthttp, add one line of code before `trpc.NewServer()` as follows:
   708  
   709  ```go
   710  package main
   711  import (
   712      "trpc.group/trpc-go/trpc-go/transport"
   713      thttp "trpc.group/trpc-go/trpc-go/http"
   714  )
   715  func main() {
   716      transport.RegisterServerTransport("restful", thttp.NewRESTServerTransport(true))
   717      s := trpc.NewServer()
   718      ...
   719  }
   720  ```