OSDN Git Service

Update to current version of Go library.
[pf3gnuchains/gcc-fork.git] / libgo / go / websocket / server.go
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.
4
5 package websocket
6
7 import (
8         "http"
9         "io"
10         "strings"
11 )
12
13 /*
14 Handler is an interface to a WebSocket.
15
16 A trivial example server:
17
18         package main
19
20         import (
21                 "http"
22                 "io"
23                 "websocket"
24         )
25
26         // Echo the data received on the Web Socket.
27         func EchoServer(ws *websocket.Conn) {
28                 io.Copy(ws, ws);
29         }
30
31         func main() {
32                 http.Handle("/echo", websocket.Handler(EchoServer));
33                 err := http.ListenAndServe(":12345", nil);
34                 if err != nil {
35                         panic("ListenAndServe: " + err.String())
36                 }
37         }
38 */
39 type Handler func(*Conn)
40
41 /*
42 Gets key number from Sec-WebSocket-Key<n>: field as described
43 in 5.2 Sending the server's opening handshake, 4.
44 */
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
49         // in /key_n/.
50         r = 0
51         for i := 0; i < len(s); i++ {
52                 if s[i] >= '0' && s[i] <= '9' {
53                         r = r*10 + uint32(s[i]) - '0'
54                 }
55         }
56         return
57 }
58
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.(http.Hijacker).Hijack()
62         if err != nil {
63                 panic("Hijack failed: " + err.String())
64                 return
65         }
66         // The server should abort the WebSocket connection if it finds
67         // the client did not send a handshake that matches with protocol
68         // specification.
69         defer rwc.Close()
70
71         if req.Method != "GET" {
72                 return
73         }
74         // HTTP version can be safely ignored.
75
76         if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
77                 strings.ToLower(req.Header.Get("Connection")) != "upgrade" {
78                 return
79         }
80
81         // TODO(ukai): check Host
82         origin := req.Header.Get("Origin")
83         if origin == "" {
84                 return
85         }
86
87         key1 := req.Header.Get("Sec-Websocket-Key1")
88         if key1 == "" {
89                 return
90         }
91         key2 := req.Header.Get("Sec-Websocket-Key2")
92         if key2 == "" {
93                 return
94         }
95         key3 := make([]byte, 8)
96         if _, err := io.ReadFull(buf, key3); err != nil {
97                 return
98         }
99
100         var location string
101         if req.TLS != nil {
102                 location = "wss://" + req.Host + req.URL.RawPath
103         } else {
104                 location = "ws://" + req.Host + req.URL.RawPath
105         }
106
107         // Step 4. get key number in Sec-WebSocket-Key<n> fields.
108         keyNumber1 := getKeyNumber(key1)
109         keyNumber2 := getKeyNumber(key2)
110
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 {
115                 return
116         }
117
118         // Step 6. key number must be an integral multiple of spaces.
119         if keyNumber1%space1 != 0 || keyNumber2%space2 != 0 {
120                 return
121         }
122
123         // Step 7. let part be key number divided by spaces.
124         part1 := keyNumber1 / space1
125         part2 := keyNumber2 / space2
126
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)
130         if err != nil {
131                 return
132         }
133
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 := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
142         if protocol != "" {
143                 buf.WriteString("Sec-WebSocket-Protocol: " + protocol + "\r\n")
144         }
145         // Step 12. send CRLF.
146         buf.WriteString("\r\n")
147         // Step 13. send response data.
148         buf.Write(response)
149         if err := buf.Flush(); err != nil {
150                 return
151         }
152         ws := newConn(origin, location, protocol, buf, rwc)
153         ws.Request = req
154         f(ws)
155 }
156
157
158 /*
159 Draft75Handler is an interface to a WebSocket based on the
160 (soon obsolete) draft-hixie-thewebsocketprotocol-75.
161 */
162 type Draft75Handler func(*Conn)
163
164 // ServeHTTP implements the http.Handler interface for a Web Socket.
165 func (f Draft75Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
166         if req.Method != "GET" || req.Proto != "HTTP/1.1" {
167                 w.WriteHeader(http.StatusBadRequest)
168                 io.WriteString(w, "Unexpected request")
169                 return
170         }
171         if req.Header.Get("Upgrade") != "WebSocket" {
172                 w.WriteHeader(http.StatusBadRequest)
173                 io.WriteString(w, "missing Upgrade: WebSocket header")
174                 return
175         }
176         if req.Header.Get("Connection") != "Upgrade" {
177                 w.WriteHeader(http.StatusBadRequest)
178                 io.WriteString(w, "missing Connection: Upgrade header")
179                 return
180         }
181         origin := strings.TrimSpace(req.Header.Get("Origin"))
182         if origin == "" {
183                 w.WriteHeader(http.StatusBadRequest)
184                 io.WriteString(w, "missing Origin header")
185                 return
186         }
187
188         rwc, buf, err := w.(http.Hijacker).Hijack()
189         if err != nil {
190                 panic("Hijack failed: " + err.String())
191                 return
192         }
193         defer rwc.Close()
194
195         var location string
196         if req.TLS != nil {
197                 location = "wss://" + req.Host + req.URL.RawPath
198         } else {
199                 location = "ws://" + req.Host + req.URL.RawPath
200         }
201
202         // TODO(ukai): verify origin,location,protocol.
203
204         buf.WriteString("HTTP/1.1 101 Web Socket Protocol Handshake\r\n")
205         buf.WriteString("Upgrade: WebSocket\r\n")
206         buf.WriteString("Connection: Upgrade\r\n")
207         buf.WriteString("WebSocket-Origin: " + origin + "\r\n")
208         buf.WriteString("WebSocket-Location: " + location + "\r\n")
209         protocol := strings.TrimSpace(req.Header.Get("Websocket-Protocol"))
210         // canonical header key of WebSocket-Protocol.
211         if protocol != "" {
212                 buf.WriteString("WebSocket-Protocol: " + protocol + "\r\n")
213         }
214         buf.WriteString("\r\n")
215         if err := buf.Flush(); err != nil {
216                 return
217         }
218         ws := newConn(origin, location, protocol, buf, rwc)
219         f(ws)
220 }