1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
14 Handler is an interface to a WebSocket.
16 A trivial example server:
26 // Echo the data received on the Web Socket.
27 func EchoServer(ws *websocket.Conn) {
32 http.Handle("/echo", websocket.Handler(EchoServer));
33 err := http.ListenAndServe(":12345", nil);
35 panic("ListenAndServe: " + err.String())
39 type Handler
func(*Conn
)
42 Gets key number from Sec-WebSocket-Key<n>: field as described
43 in 5.2 Sending the server's opening handshake, 4.
45 func getKeyNumber(s
string) (r
uint32) {
46 // 4. Let /key-number_n/ be the digits (characters in the range
47 // U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9)) in /key_1/,
48 // interpreted as a base ten integer, ignoring all other characters
51 for i
:= 0; i
< len(s
); i
++ {
52 if s
[i
] >= '0' && s
[i
] <= '9' {
53 r
= r
*10 + uint32(s
[i
]) - '0'
59 // ServeHTTP implements the http.Handler interface for a Web Socket
60 func (f Handler
) ServeHTTP(w http
.ResponseWriter
, req
*http
.Request
) {
61 rwc
, buf
, err
:= w
.Hijack()
63 panic("Hijack failed: " + err
.String())
66 // The server should abort the WebSocket connection if it finds
67 // the client did not send a handshake that matches with protocol
71 if req
.Method
!= "GET" {
74 // HTTP version can be safely ignored.
76 if strings
.ToLower(req
.Header
["Upgrade"]) != "websocket" ||
77 strings
.ToLower(req
.Header
["Connection"]) != "upgrade" {
81 // TODO(ukai): check Host
82 origin
, found
:= req
.Header
["Origin"]
87 key1
, found
:= req
.Header
["Sec-Websocket-Key1"]
91 key2
, found
:= req
.Header
["Sec-Websocket-Key2"]
95 key3
:= make([]byte, 8)
96 if _
, err
:= io
.ReadFull(buf
, key3
); err
!= nil {
102 location
= "wss://" + req
.Host
+ req
.URL
.RawPath
104 location
= "ws://" + req
.Host
+ req
.URL
.RawPath
107 // Step 4. get key number in Sec-WebSocket-Key<n> fields.
108 keyNumber1
:= getKeyNumber(key1
)
109 keyNumber2
:= getKeyNumber(key2
)
111 // Step 5. get number of spaces in Sec-WebSocket-Key<n> fields.
112 space1
:= uint32(strings
.Count(key1
, " "))
113 space2
:= uint32(strings
.Count(key2
, " "))
114 if space1
== 0 || space2
== 0 {
118 // Step 6. key number must be an integral multiple of spaces.
119 if keyNumber1%space
1 != 0 || keyNumber2%space
2 != 0 {
123 // Step 7. let part be key number divided by spaces.
124 part1
:= keyNumber1
/ space1
125 part2
:= keyNumber2
/ space2
127 // Step 8. let challenge to be concatination of part1, part2 and key3.
128 // Step 9. get MD5 fingerprint of challenge.
129 response
, err
:= getChallengeResponse(part1
, part2
, key3
)
134 // Step 10. send response status line.
135 buf
.WriteString("HTTP/1.1 101 WebSocket Protocol Handshake\r\n")
136 // Step 11. send response headers.
137 buf
.WriteString("Upgrade: WebSocket\r\n")
138 buf
.WriteString("Connection: Upgrade\r\n")
139 buf
.WriteString("Sec-WebSocket-Location: " + location
+ "\r\n")
140 buf
.WriteString("Sec-WebSocket-Origin: " + origin
+ "\r\n")
141 protocol
, found
:= req
.Header
["Sec-Websocket-Protocol"]
143 buf
.WriteString("Sec-WebSocket-Protocol: " + protocol
+ "\r\n")
145 // Step 12. send CRLF.
146 buf
.WriteString("\r\n")
147 // Step 13. send response data.
149 if err
:= buf
.Flush(); err
!= nil {
152 ws
:= newConn(origin
, location
, protocol
, buf
, rwc
)
158 Draft75Handler is an interface to a WebSocket based on the
159 (soon obsolete) draft-hixie-thewebsocketprotocol-75.
161 type Draft75Handler
func(*Conn
)
163 // ServeHTTP implements the http.Handler interface for a Web Socket.
164 func (f Draft75Handler
) ServeHTTP(w http
.ResponseWriter
, req
*http
.Request
) {
165 if req
.Method
!= "GET" || req
.Proto
!= "HTTP/1.1" {
166 w
.WriteHeader(http
.StatusBadRequest
)
167 io
.WriteString(w
, "Unexpected request")
170 if req
.Header
["Upgrade"] != "WebSocket" {
171 w
.WriteHeader(http
.StatusBadRequest
)
172 io
.WriteString(w
, "missing Upgrade: WebSocket header")
175 if req
.Header
["Connection"] != "Upgrade" {
176 w
.WriteHeader(http
.StatusBadRequest
)
177 io
.WriteString(w
, "missing Connection: Upgrade header")
180 origin
, found
:= req
.Header
["Origin"]
182 w
.WriteHeader(http
.StatusBadRequest
)
183 io
.WriteString(w
, "missing Origin header")
187 rwc
, buf
, err
:= w
.Hijack()
189 panic("Hijack failed: " + err
.String())
196 location
= "wss://" + req
.Host
+ req
.URL
.RawPath
198 location
= "ws://" + req
.Host
+ req
.URL
.RawPath
201 // TODO(ukai): verify origin,location,protocol.
203 buf
.WriteString("HTTP/1.1 101 Web Socket Protocol Handshake\r\n")
204 buf
.WriteString("Upgrade: WebSocket\r\n")
205 buf
.WriteString("Connection: Upgrade\r\n")
206 buf
.WriteString("WebSocket-Origin: " + origin
+ "\r\n")
207 buf
.WriteString("WebSocket-Location: " + location
+ "\r\n")
208 protocol
, found
:= req
.Header
["Websocket-Protocol"]
209 // canonical header key of WebSocket-Protocol.
211 buf
.WriteString("WebSocket-Protocol: " + protocol
+ "\r\n")
213 buf
.WriteString("\r\n")
214 if err
:= buf
.Flush(); err
!= nil {
217 ws
:= newConn(origin
, location
, protocol
, buf
, rwc
)