-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbind.go
113 lines (98 loc) · 2.8 KB
/
bind.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
// Copyright (c) 2014 SameGoal LLC. All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package wc
import (
"fmt"
"net/http"
)
// Special cases to think about:
// * duplicate back channel
// * noop message on back channel
// * buffered proxy mode (ensure flushing all messages)
// * 'Unknown SID' error message with HTTP status 400
// * other ChannelRequest.Error error values (special cases?)
// * XSS escaping messages for client
// * backchannel handoff
// * messages delivered when no back channel exists for a session
// * client side session reconnects after server crash
// * chunk compression
// Tasks for application level:
// * sharding comet server? (moving sessions across servers?)
// * restart server without dropping back channels
// * expvar stats: # sessions, # backchannels, # pending messages, etc
type reqRegister struct {
w http.ResponseWriter
r *http.Request
done chan struct{}
}
func newReqRegister(w http.ResponseWriter, r *http.Request) *reqRegister {
return &reqRegister{w, r, make(chan struct{})}
}
func init() {
sessionWrapperMap = make(map[string]*sessionWrapper)
}
func debug(format string, a ...interface{}) {
sm.Debug(fmt.Sprintf(format, a...))
}
func newSession(r *http.Request) (*sessionWrapper, error) {
mutex.Lock()
defer mutex.Unlock()
session, err := sm.NewSession(r)
if err != nil {
return nil, err
}
sw := newSessionWrapper(session)
launchSession(sw)
sessionWrapperMap[session.SID()] = sw
return sw, nil
}
func getSession(r *http.Request) (*sessionWrapper, error) {
mutex.Lock()
defer mutex.Unlock()
sid := r.FormValue("SID")
if sw, hasSession := sessionWrapperMap[sid]; hasSession {
return sw, nil
}
session, si, err := sm.LookupSession(r, sid)
if err != nil {
return nil, err
}
sw := newSessionWrapper(session)
sw.si = si
launchSession(sw)
sessionWrapperMap[sid] = sw
return sw, nil
}
// BindHandler handles forward and backward channel HTTP requests. When using
// the defaults this handler should be installed at "/channel" (WebChannel) or
// "/channel/bind" (BrowserChannel).
func BindHandler(w http.ResponseWriter, r *http.Request) {
if sm == nil {
panic("No SessionManager provided")
}
var sw *sessionWrapper
var err error
switch {
case r.FormValue("SID") == "":
sw, err = newSession(r)
default:
sw, err = getSession(r)
}
if err != nil {
sm.Error(r, err)
switch {
case err == ErrUnknownSID:
// Special case 'Unknown SID' to be compatible with JS impl. See
// goog.labs.net.webChannel.ChannelRequest#onXmlHttpReadyStateChanged_
// for more details.
http.Error(w, ErrUnknownSID.Error(), 400)
default:
http.Error(w, "Unable to locate SID", http.StatusInternalServerError)
}
return
}
rr := newReqRegister(w, r)
sw.reqNotifier <- rr
<-rr.done
}