/
rpc.go
159 lines (131 loc) · 3.36 KB
/
rpc.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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package handler
import (
"encoding/json"
"net/http"
"strconv"
"strings"
"time"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/config/cmd"
"github.com/micro/go-micro/errors"
"github.com/micro-in-cn/x-gateway/internal/helper"
)
type rpcRequest struct {
Service string
Endpoint string
Method string
Address string
Request interface{}
}
// RPC Handler passes on a JSON or form encoded RPC request to
// a service.
func RPC(w http.ResponseWriter, r *http.Request) {
if r.Method == "OPTIONS" {
helper.ServeCORS(w, r)
return
}
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
defer r.Body.Close()
badRequest := func(description string) {
e := errors.BadRequest("go.micro.rpc", description)
w.WriteHeader(400)
w.Write([]byte(e.Error()))
}
var service, endpoint, address string
var request interface{}
// response content type
w.Header().Set("Content-Type", "application/json")
ct := r.Header.Get("Content-Type")
// Strip charset from Content-Type (like `application/json; charset=UTF-8`)
if idx := strings.IndexRune(ct, ';'); idx >= 0 {
ct = ct[:idx]
}
switch ct {
case "application/json":
var rpcReq rpcRequest
d := json.NewDecoder(r.Body)
d.UseNumber()
if err := d.Decode(&rpcReq); err != nil {
badRequest(err.Error())
return
}
service = rpcReq.Service
endpoint = rpcReq.Endpoint
address = rpcReq.Address
request = rpcReq.Request
if len(endpoint) == 0 {
endpoint = rpcReq.Method
}
// JSON as string
if req, ok := rpcReq.Request.(string); ok {
d := json.NewDecoder(strings.NewReader(req))
d.UseNumber()
if err := d.Decode(&request); err != nil {
badRequest("error decoding request string: " + err.Error())
return
}
}
default:
r.ParseForm()
service = r.Form.Get("service")
endpoint = r.Form.Get("endpoint")
address = r.Form.Get("address")
if len(endpoint) == 0 {
endpoint = r.Form.Get("method")
}
d := json.NewDecoder(strings.NewReader(r.Form.Get("request")))
d.UseNumber()
if err := d.Decode(&request); err != nil {
badRequest("error decoding request string: " + err.Error())
return
}
}
if len(service) == 0 {
badRequest("invalid service")
return
}
if len(endpoint) == 0 {
badRequest("invalid endpoint")
return
}
// create request/response
var response json.RawMessage
var err error
req := (*cmd.DefaultOptions().Client).NewRequest(service, endpoint, request, client.WithContentType("application/json"))
// create context
ctx := helper.RequestToContext(r)
var opts []client.CallOption
timeout, _ := strconv.Atoi(r.Header.Get("Timeout"))
// set timeout
if timeout > 0 {
opts = append(opts, client.WithRequestTimeout(time.Duration(timeout)*time.Second))
}
// remote call
if len(address) > 0 {
opts = append(opts, client.WithAddress(address))
}
// remote call
err = (*cmd.DefaultOptions().Client).Call(ctx, req, &response, opts...)
if err != nil {
ce := errors.Parse(err.Error())
switch ce.Code {
case 0:
// assuming it's totally screwed
ce.Code = 500
ce.Id = "go.micro.rpc"
ce.Status = http.StatusText(500)
ce.Detail = "error during request: " + ce.Detail
w.WriteHeader(500)
default:
w.WriteHeader(int(ce.Code))
}
w.Write([]byte(ce.Error()))
return
}
b, _ := response.MarshalJSON()
w.Header().Set("Content-Length", strconv.Itoa(len(b)))
w.Write(b)
}