-
Notifications
You must be signed in to change notification settings - Fork 39
/
Copy pathhandlers.go
140 lines (121 loc) · 3.98 KB
/
handlers.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
package zenrpc
import (
"encoding/json"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/gorilla/websocket"
)
type Printer interface {
Printf(string, ...interface{})
}
// ServeHTTP process JSON-RPC 2.0 requests via HTTP.
// http://www.simple-is-better.org/json-rpc/transport_http.html
func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// check for CORS GET & POST requests
if s.options.AllowCORS {
w.Header().Set("Access-Control-Allow-Origin", "*")
}
// check for smd parameter and server settings and write schema if all conditions met,
if _, ok := r.URL.Query()["smd"]; ok && s.options.ExposeSMD && r.Method == http.MethodGet {
b, _ := json.Marshal(s.SMD())
w.Write(b)
return
}
// check for CORS OPTIONS pre-requests for POST https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
if s.options.AllowCORS && r.Method == http.MethodOptions {
w.Header().Set("Allow", "OPTIONS, GET, POST")
w.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET, POST")
w.Header().Set("Access-Control-Allow-Headers", "X-PINGOTHER, Content-Type")
w.Header().Set("Access-Control-Max-Age", "86400")
w.WriteHeader(http.StatusOK)
return
}
// check for content-type and POST method.
if !s.options.DisableTransportChecks {
if !strings.HasPrefix(r.Header.Get("Content-Type"), contentTypeJSON) {
w.WriteHeader(http.StatusUnsupportedMediaType)
return
} else if r.Method == http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
return
} else if r.Method != http.MethodPost {
// skip rpc calls
return
}
}
// ok, method is POST and content-type is application/json, process body
b, err := ioutil.ReadAll(r.Body)
var data interface{}
if err != nil {
s.printf("read request body failed with err=%v", err)
data = NewResponseError(nil, ParseError, "", nil)
} else {
data = s.process(newRequestContext(r.Context(), r), b)
}
// if responses is empty -> all requests are notifications -> exit immediately
if data == nil {
return
}
// set headers
w.Header().Set("Content-Type", contentTypeJSON)
// marshals data and write it to client.
if resp, err := json.Marshal(data); err != nil {
s.printf("marshal json response failed with err=%v", err)
w.WriteHeader(http.StatusInternalServerError)
} else if _, err := w.Write(resp); err != nil {
s.printf("write response failed with err=%v", err)
w.WriteHeader(http.StatusInternalServerError)
}
return
}
// ServeWS processes JSON-RPC 2.0 requests via Gorilla WebSocket.
// https://github.com/gorilla/websocket/blob/master/examples/echo/
func (s Server) ServeWS(w http.ResponseWriter, r *http.Request) {
c, err := s.options.Upgrader.Upgrade(w, r, nil)
if err != nil {
s.printf("upgrade connection failed with err=%v", err)
return
}
defer c.Close()
for {
mt, message, err := c.ReadMessage()
// normal closure
if websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {
break
}
// abnormal closure
if err != nil {
s.printf("read message failed with err=%v", err)
break
}
data, err := s.Do(newRequestContext(r.Context(), r), message)
if err != nil {
s.printf("marshal json response failed with err=%v", err)
c.WriteControl(websocket.CloseInternalServerErr, nil, time.Time{})
break
}
if err = c.WriteMessage(mt, data); err != nil {
s.printf("write response failed with err=%v", err)
c.WriteControl(websocket.CloseInternalServerErr, nil, time.Time{})
break
}
}
}
// SMDBoxHandler is a handler for SMDBox web app.
func SMDBoxHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SMD Box</title>
<link rel="stylesheet" href="https://bootswatch.com/3/paper/bootstrap.min.css">
<link href="https://cdn.jsdelivr.net/gh/semrush/smdbox@latest/dist/app.css" rel="stylesheet"></head>
<body>
<div id="json-rpc-root"></div>
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/semrush/smdbox@latest/dist/app.js"></script></body>
</html>
`))
}