This repository has been archived by the owner on Jul 30, 2019. It is now read-only.
/
gateway.go
126 lines (108 loc) · 3.52 KB
/
gateway.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// Copyright (C) 2018 MediBloc
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>
package rpc
import (
"net/http"
"encoding/json"
"io"
"strings"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
pb "github.com/medibloc/go-medibloc/rpc/pb"
rpcpb "github.com/medibloc/go-medibloc/rpc/pb"
"github.com/medibloc/go-medibloc/util/logging"
"github.com/rs/cors"
"github.com/sirupsen/logrus"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// HTTPServer is a rest gateway wrapping the grpc server.
type HTTPServer struct {
handler http.Handler
httpAddr string
grpcAddr string
cancel context.CancelFunc
}
// NewHTTPServer creates HTTPServer.
func NewHTTPServer(httpAddr string, grpcAddr string) (*HTTPServer, error) {
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
mux := runtime.NewServeMux(runtime.WithMarshalerOption(runtime.MIMEWildcard,
&runtime.JSONPb{OrigName: true, EmitDefaults: true}),
runtime.WithProtoErrorHandler(errorHandler))
opts := []grpc.DialOption{grpc.WithInsecure()}
err := rpcpb.RegisterApiServiceHandlerFromEndpoint(ctx, mux, grpcAddr, opts)
if err != nil {
return nil, err
}
httpMux := http.NewServeMux()
httpMux.HandleFunc("/swagger.json", func(w http.ResponseWriter, req *http.Request) {
io.Copy(w, strings.NewReader(pb.Swagger))
})
httpMux.Handle("/swagger/", http.StripPrefix("/swagger/", http.FileServer(http.Dir("./rpc/swaggerui"))))
httpMux.Handle("/", mux)
return &HTTPServer{
handler: cors.Default().Handler(httpMux),
httpAddr: httpAddr,
grpcAddr: grpcAddr,
cancel: cancel,
}, nil
}
// Run starts the server.
func (srv *HTTPServer) Run() error {
defer srv.cancel()
return http.ListenAndServe(srv.httpAddr, srv.handler)
}
type errorBody struct {
Error string `json:"error,omitempty"`
}
func errorHandler(ctx context.Context, _ *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, _ *http.Request, err error) {
const fallback = "failed to marshal error message"
s, ok := status.FromError(err)
if !ok {
s = status.New(codes.Unknown, err.Error())
}
if s.Code() == codes.Unimplemented {
s = status.New(codes.NotFound, "Page not found")
}
body := &errorBody{
Error: s.Message(),
}
w.Header().Set("Content-type", marshaler.ContentType())
if s.Code() == codes.Unknown {
w.WriteHeader(runtime.HTTPStatusFromCode(codes.OutOfRange))
} else {
w.WriteHeader(runtime.HTTPStatusFromCode(s.Code()))
}
jErr := json.NewEncoder(w).Encode(errorBody{
Error: body.Error,
})
if jErr != nil {
jsonFallback, tmpErr := json.Marshal(errorBody{Error: fallback})
if tmpErr != nil {
logging.WithFields(logrus.Fields{
"error": tmpErr,
"jsonFallback": jsonFallback,
}).Debug("Failed to marshal fallback msg")
}
_, tmpErr = w.Write(jsonFallback)
if tmpErr != nil {
logging.WithFields(logrus.Fields{
"error": tmpErr,
}).Debug("Failed to write fallback msg")
}
}
}