github.com/vcilabs/webrpc@v0.5.2-0.20201116131534-162e27b1b33b/gen/golang/templates/server.go.tmpl (about)

     1  {{define "server"}}
     2  {{if .Services}}
     3  //
     4  // Server
     5  //
     6  
     7  type WebRPCServer interface {
     8    http.Handler
     9  }
    10  
    11  {{- range .Services}}
    12    {{$name := .Name}}
    13    {{$serviceName := .Name | serverServiceName}}
    14  
    15    type {{$serviceName}} struct {
    16      {{.Name}}
    17    }
    18  
    19    func {{ .Name | newServerServiceName }}(svc {{.Name}}) WebRPCServer {
    20      return &{{$serviceName}}{
    21        {{.Name}}: svc,
    22      }
    23    }
    24  
    25    func (s *{{$serviceName}}) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    26      ctx := r.Context()
    27      ctx = context.WithValue(ctx, HTTPResponseWriterCtxKey, w)
    28      ctx = context.WithValue(ctx, HTTPRequestCtxKey, r)
    29      ctx = context.WithValue(ctx, ServiceNameCtxKey, "{{.Name}}")
    30  
    31      if r.Method != "POST" {
    32        err := Errorf(ErrBadRoute, "unsupported method %q (only POST is allowed)", r.Method)
    33        RespondWithError(w, err)
    34        return
    35      }
    36  
    37      switch r.URL.Path {
    38      {{- range .Methods}}
    39      case "/rpc/{{$name}}/{{.Name}}":
    40        s.{{.Name | serviceMethodName}}(ctx, w, r)
    41        return
    42      {{- end}}
    43      default:
    44        err := Errorf(ErrBadRoute, "no handler for path %q", r.URL.Path)
    45        RespondWithError(w, err)
    46        return
    47      }
    48    }
    49  
    50    {{range .Methods}}
    51      func (s *{{$serviceName}}) {{.Name | serviceMethodName}}(ctx context.Context, w http.ResponseWriter, r *http.Request) {
    52        header := r.Header.Get("Content-Type")
    53        i := strings.Index(header, ";")
    54        if i == -1 {
    55          i = len(header)
    56        }
    57  
    58        switch strings.TrimSpace(strings.ToLower(header[:i])) {
    59        case "application/json":
    60          s.{{ .Name | serviceMethodJSONName }}(ctx, w, r)
    61        default:
    62          err := Errorf(ErrBadRoute, "unexpected Content-Type: %q", r.Header.Get("Content-Type"))
    63          RespondWithError(w, err)
    64        }
    65      }
    66  
    67      func (s *{{$serviceName}}) {{.Name | serviceMethodJSONName}}(ctx context.Context, w http.ResponseWriter, r *http.Request) {
    68        var err error
    69        ctx = context.WithValue(ctx, MethodNameCtxKey, "{{.Name}}")
    70  
    71        {{- if .Inputs|len}}
    72        reqContent := struct {
    73        {{- range $i, $input := .Inputs}}
    74          Arg{{$i}} {{. | methodArgType}} `json:"{{$input.Name | downcaseName}}"`
    75        {{- end}}
    76        }{}
    77  
    78        reqBody, err := ioutil.ReadAll(r.Body)
    79        if err != nil {
    80          err = WrapError(ErrInternal, err, "failed to read request data")
    81          RespondWithError(w, err)
    82          return
    83        }
    84        defer r.Body.Close()
    85  
    86        err = json.Unmarshal(reqBody, &reqContent)
    87        if err != nil {
    88          err = WrapError(ErrInvalidArgument, err, "failed to unmarshal request data")
    89          RespondWithError(w, err)
    90          return
    91        }
    92        {{- end}}
    93  
    94        // Call service method
    95        {{- range $i, $output := .Outputs}}
    96        var ret{{$i}} {{$output | methodArgType}}
    97        {{- end}}
    98        func() {
    99          defer func() {
   100            // In case of a panic, serve a 500 error and then panic.
   101            if rr := recover(); rr != nil {
   102              RespondWithError(w, ErrorInternal("internal service panic"))
   103              panic(rr)
   104            }
   105          }()
   106          {{argsList .Outputs "ret"}}{{.Outputs | commaIfLen}} err = s.{{$name}}.{{.Name}}(ctx{{.Inputs | commaIfLen}}{{argsList .Inputs "reqContent.Arg"}})
   107        }()
   108        {{- if .Outputs | len}}
   109        respContent := struct {
   110        {{- range $i, $output := .Outputs}}
   111          Ret{{$i}} {{$output | methodArgType}} `json:"{{$output.Name | downcaseName}}"`
   112        {{- end}}         
   113        }{ {{argsList .Outputs "ret"}} }
   114        {{- end}}
   115  
   116        if err != nil {
   117          RespondWithError(w, err)
   118          return
   119        }
   120  
   121        {{- if .Outputs | len}}
   122        respBody, err := json.Marshal(respContent)
   123        if err != nil {
   124          err = WrapError(ErrInternal, err, "failed to marshal json response")
   125          RespondWithError(w, err)
   126          return
   127        }
   128        {{- end}}
   129  
   130        w.Header().Set("Content-Type", "application/json")
   131        w.WriteHeader(http.StatusOK)
   132  
   133        {{- if .Outputs | len}}
   134        w.Write(respBody)
   135        {{- end}}
   136      }
   137    {{end}}
   138  {{- end}}
   139  
   140  func RespondWithError(w http.ResponseWriter, err error) {
   141  	rpcErr, ok := err.(Error)
   142  	if !ok {
   143  		rpcErr = WrapError(ErrInternal, err, "webrpc error")
   144  	}
   145  
   146  	statusCode := HTTPStatusFromErrorCode(rpcErr.Code())
   147  
   148  	w.Header().Set("Content-Type", "application/json")
   149  	w.WriteHeader(statusCode)
   150  
   151  	respBody, _ := json.Marshal(rpcErr.Payload())
   152  	w.Write(respBody)
   153  }
   154  {{end}}
   155  {{end}}