OSDN Git Service

Add Go frontend, libgo library, and Go testsuite.
[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.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["Upgrade"]) != "websocket" ||
77                 strings.ToLower(req.Header["Connection"]) != "upgrade" {
78                 return
79         }
80
81         // TODO(ukai): check Host
82         origin, found := req.Header["Origin"]
83         if !found {
84                 return
85         }
86
87         key1, found := req.Header["Sec-Websocket-Key1"]
88         if !found {
89                 return
90         }
91         key2, found := req.Header["Sec-Websocket-Key2"]
92         if !found {
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 w.UsingTLS() {
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, found := req.Header["Sec-Websocket-Protocol"]
142         if found {
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         f(ws)
154 }
155
156
157 /*
158 Draft75Handler is an interface to a WebSocket based on the
159 (soon obsolete) draft-hixie-thewebsocketprotocol-75.
160 */
161 type Draft75Handler func(*Conn)
162
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")
168                 return
169         }
170         if req.Header["Upgrade"] != "WebSocket" {
171                 w.WriteHeader(http.StatusBadRequest)
172                 io.WriteString(w, "missing Upgrade: WebSocket header")
173                 return
174         }
175         if req.Header["Connection"] != "Upgrade" {
176                 w.WriteHeader(http.StatusBadRequest)
177                 io.WriteString(w, "missing Connection: Upgrade header")
178                 return
179         }
180         origin, found := req.Header["Origin"]
181         if !found {
182                 w.WriteHeader(http.StatusBadRequest)
183                 io.WriteString(w, "missing Origin header")
184                 return
185         }
186
187         rwc, buf, err := w.Hijack()
188         if err != nil {
189                 panic("Hijack failed: " + err.String())
190                 return
191         }
192         defer rwc.Close()
193
194         var location string
195         if w.UsingTLS() {
196                 location = "wss://" + req.Host + req.URL.RawPath
197         } else {
198                 location = "ws://" + req.Host + req.URL.RawPath
199         }
200
201         // TODO(ukai): verify origin,location,protocol.
202
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.
210         if found {
211                 buf.WriteString("WebSocket-Protocol: " + protocol + "\r\n")
212         }
213         buf.WriteString("\r\n")
214         if err := buf.Flush(); err != nil {
215                 return
216         }
217         ws := newConn(origin, location, protocol, buf, rwc)
218         f(ws)
219 }