-
Notifications
You must be signed in to change notification settings - Fork 100
/
options.go
133 lines (122 loc) · 5.43 KB
/
options.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
package sockjs
import (
"encoding/json"
"fmt"
"math/rand"
"net/http"
"sync"
"time"
"github.com/gorilla/websocket"
)
var (
entropy *rand.Rand
entropyMutex sync.Mutex
)
func init() {
entropy = rand.New(rand.NewSource(time.Now().UnixNano()))
}
// Options type is used for defining various sockjs options
type Options struct {
// Transports which don't support cross-domain communication natively ('eventsource' to name one) use an iframe trick.
// A simple page is served from the SockJS server (using its foreign domain) and is placed in an invisible iframe.
// Code run from this iframe doesn't need to worry about cross-domain issues, as it's being run from domain local to the SockJS server.
// This iframe also does need to load SockJS javascript client library, and this option lets you specify its url (if you're unsure,
// point it to the latest minified SockJS client release, this is the default). You must explicitly specify this url on the server
// side for security reasons - we don't want the possibility of running any foreign javascript within the SockJS domain (aka cross site scripting attack).
// Also, sockjs javascript library is probably already cached by the browser - it makes sense to reuse the sockjs url you're using in normally.
SockJSURL string
// Most streaming transports save responses on the client side and don't free memory used by delivered messages.
// Such transports need to be garbage-collected once in a while. `response_limit` sets a minimum number of bytes that can be send
// over a single http streaming request before it will be closed. After that client needs to open new request.
// Setting this value to one effectively disables streaming and will make streaming transports to behave like polling transports.
// The default value is 128K.
ResponseLimit uint32
// Some load balancers don't support websockets. This option can be used to disable websockets support by the server. By default websockets are enabled.
Websocket bool
// This option can be used to enable raw websockets support by the server. By default raw websockets are disabled.
RawWebsocket bool
// Provide a custom Upgrader for Websocket connections to enable features like compression.
// See https://godoc.org/github.com/gorilla/websocket#Upgrader for more details.
WebsocketUpgrader websocket.Upgrader
// In order to keep proxies and load balancers from closing long running http requests we need to pretend that the connection is active
// and send a heartbeat packet once in a while. This setting controls how often this is done.
// By default a heartbeat packet is sent every 25 seconds.
HeartbeatDelay time.Duration
// The server closes a session when a client receiving connection have not been seen for a while.
// This delay is configured by this setting.
// By default the session is closed when a receiving connection wasn't seen for 5 seconds.
DisconnectDelay time.Duration
// Some hosting providers enable sticky sessions only to requests that have JSessionID cookie set.
// This setting controls if the server should set this cookie to a dummy value.
// By default setting JSessionID cookie is disabled. More sophisticated behaviour can be achieved by supplying a function.
JSessionID func(http.ResponseWriter, *http.Request)
}
// DefaultOptions is a convenient set of options to be used for sockjs
var DefaultOptions = Options{
Websocket: true,
RawWebsocket: false,
JSessionID: nil,
SockJSURL: "//cdnjs.cloudflare.com/ajax/libs/sockjs-client/0.3.4/sockjs.min.js",
HeartbeatDelay: 25 * time.Second,
DisconnectDelay: 5 * time.Second,
ResponseLimit: 128 * 1024,
WebsocketUpgrader: websocket.Upgrader{
ReadBufferSize: WebSocketReadBufSize,
WriteBufferSize: WebSocketWriteBufSize,
CheckOrigin: func(_ *http.Request) bool {
// Allow all connections by default.
return true
},
Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) {
// Don't return errors to maintain backwards compatibility.
},
},
}
type info struct {
Websocket bool `json:"websocket"`
CookieNeeded bool `json:"cookie_needed"`
Origins []string `json:"origins"`
Entropy int32 `json:"entropy"`
}
func (options *Options) info(rw http.ResponseWriter, req *http.Request) {
switch req.Method {
case "GET":
rw.Header().Set("Content-Type", "application/json; charset=UTF-8")
json.NewEncoder(rw).Encode(info{
Websocket: options.Websocket,
CookieNeeded: options.JSessionID != nil,
Origins: []string{"*:*"},
Entropy: generateEntropy(),
})
case "OPTIONS":
rw.Header().Set("Access-Control-Allow-Methods", "OPTIONS, GET")
rw.Header().Set("Access-Control-Max-Age", fmt.Sprintf("%d", 365*24*60*60))
rw.WriteHeader(http.StatusNoContent) // 204
default:
http.NotFound(rw, req)
}
}
// DefaultJSessionID is a default behaviour function to be used in options for JSessionID if JSESSIONID is needed
func DefaultJSessionID(rw http.ResponseWriter, req *http.Request) {
cookie, err := req.Cookie("JSESSIONID")
if err == http.ErrNoCookie {
cookie = &http.Cookie{
Name: "JSESSIONID",
Value: "dummy",
}
}
cookie.Path = "/"
header := rw.Header()
header.Add("Set-Cookie", cookie.String())
}
func (options *Options) cookie(rw http.ResponseWriter, req *http.Request) {
if options.JSessionID != nil { // cookie is needed
options.JSessionID(rw, req)
}
}
func generateEntropy() int32 {
entropyMutex.Lock()
entropy := entropy.Int31()
entropyMutex.Unlock()
return entropy
}