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}}