-
Notifications
You must be signed in to change notification settings - Fork 3
/
private-channel.go
149 lines (130 loc) · 4.46 KB
/
private-channel.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
package channels
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
_http "github.com/larisgo/laravel-echo-server/http"
"github.com/larisgo/laravel-echo-server/options"
"github.com/larisgo/laravel-echo-server/types"
"github.com/zishang520/engine.io/utils"
"github.com/zishang520/socket.io/socket"
)
type PrivateChannel struct {
// Request client.
client *_http.Client
// Configurable server options.
options *options.Config
}
// Create a new private channel instance.
func NewPrivateChannel(_options *options.Config) *PrivateChannel {
pch := &PrivateChannel{}
pch.options = _options
pch.client = _http.NewClient()
return pch
}
// Send authentication request to application server.
func (pch *PrivateChannel) Authenticate(_socket *socket.Socket, data *types.Data) (any, int, error) {
body, err := json.Marshal(map[string]string{
"channel_name": data.Channel,
})
if err != nil {
return nil, http.StatusInternalServerError, err
}
data.Auth.Headers["Content-Type"] = "application/json; charset=UTF-8"
options := &_http.Options{
Method: http.MethodPost,
Headers: data.Auth.Headers,
Url: pch.authHost(_socket) + pch.options.AuthEndpoint,
Body: bytes.NewReader(body),
}
if pch.options.DevMode {
utils.Log().Warning(`Sending auth request to: %s`, options.Url)
}
return pch.serverRequest(_socket, options, data.Channel)
}
// Get the auth host based on the Socket.
func (pch *PrivateChannel) authHost(_socket *socket.Socket) string {
_authHosts := pch.options.AuthHost
if _authHosts == nil {
_authHosts = pch.options.Host
}
authHosts := options.Hosts{}
switch hosts := _authHosts.(type) {
case string:
authHosts = options.Hosts{hosts}
case options.Hosts:
authHosts = hosts
}
authHostSelected := "http://localhost"
if len(authHosts) > 0 {
authHostSelected = authHosts[0]
}
if r := _socket.Request().Headers().Peek("Referer"); r != "" {
if referer, err := url.Parse(r); err != nil {
for _, authHost := range authHosts {
authHostSelected = authHost
if pch.hasMatchingHost(referer, authHost) {
authHostSelected = referer.Scheme + "//" + referer.Host
break
}
}
}
}
if pch.options.DevMode {
utils.Log().Warning(`Preparing authentication request to: %s`, authHostSelected)
}
return authHostSelected
}
// Check if there is a matching auth host.
func (pch *PrivateChannel) hasMatchingHost(referer *url.URL, host string) bool {
hostname := referer.Hostname()
return (hostname != "" && hostname[strings.Index(hostname, `.`):] == host) || (referer.Scheme+"//"+referer.Host) == host || referer.Host == host
}
// Send a request to the server.
func (pch *PrivateChannel) serverRequest(_socket *socket.Socket, options *_http.Options, channel_name string) (any, int, error) {
options.Headers = pch.prepareHeaders(_socket, options)
response, err := pch.client.Request(options)
if err != nil {
if pch.options.DevMode {
utils.Log().Error(`Error authenticating %s for %s`, _socket.Id(), channel_name)
utils.Log().Error("%v", err)
}
return nil, http.StatusBadGateway, errors.New("Error sending authentication request.")
}
if response.StatusCode != http.StatusOK {
if pch.options.DevMode {
utils.Log().Warning(`%s could not be authenticated to %s`, _socket.Id(), channel_name)
utils.Log().Error("%s", response.BodyBuffer.String())
}
return nil, response.StatusCode, errors.New(fmt.Sprintf(`Client can not be authenticated, got HTTP status %d`, response.StatusCode))
}
if pch.options.DevMode {
utils.Log().Info(`%s authenticated for: %s`, _socket.Id(), channel_name)
}
if response.BodyBuffer == nil {
return nil, http.StatusBadGateway, errors.New("Error sending authentication request.")
}
var res_channel_data *types.AuthenticateData = nil
if err := json.Unmarshal(response.BodyBuffer.Bytes(), &res_channel_data); err != nil {
var res_bool bool
if err := json.Unmarshal(response.BodyBuffer.Bytes(), &res_bool); err != nil {
return nil, http.StatusInternalServerError, err
}
return res_bool, response.StatusCode, nil
}
return res_channel_data, response.StatusCode, nil
}
// Prepare headers for request to app server.
func (pch *PrivateChannel) prepareHeaders(_socket *socket.Socket, options *_http.Options) map[string]string {
if cookie, HasCookie := options.Headers[`Cookie`]; !HasCookie || cookie == "" {
if c := _socket.Request().Headers().Peek("Cookie"); c != "" {
options.Headers[`Cookie`] = c
}
}
options.Headers[`X-Requested-With`] = `XMLHttpRequest`
return options.Headers
}