From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- libgo/go/websocket/client.go | 321 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 libgo/go/websocket/client.go (limited to 'libgo/go/websocket/client.go') diff --git a/libgo/go/websocket/client.go b/libgo/go/websocket/client.go new file mode 100644 index 000000000..091345944 --- /dev/null +++ b/libgo/go/websocket/client.go @@ -0,0 +1,321 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package websocket + +import ( + "bufio" + "bytes" + "container/vector" + "crypto/tls" + "fmt" + "http" + "io" + "net" + "os" + "rand" + "strings" +) + +type ProtocolError struct { + os.ErrorString +} + +var ( + ErrBadScheme = os.ErrorString("bad scheme") + ErrBadStatus = &ProtocolError{"bad status"} + ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"} + ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"} + ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"} + ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"} + ErrChallengeResponse = &ProtocolError{"mismatch challange/response"} + secKeyRandomChars [0x30 - 0x21 + 0x7F - 0x3A]byte +) + +type DialError struct { + URL string + Protocol string + Origin string + Error os.Error +} + +func (e *DialError) String() string { + return "websocket.Dial " + e.URL + ": " + e.Error.String() +} + +func init() { + i := 0 + for ch := byte(0x21); ch < 0x30; ch++ { + secKeyRandomChars[i] = ch + i++ + } + for ch := byte(0x3a); ch < 0x7F; ch++ { + secKeyRandomChars[i] = ch + i++ + } +} + +type handshaker func(resourceName, host, origin, location, protocol string, br *bufio.Reader, bw *bufio.Writer) os.Error + +// newClient creates a new Web Socket client connection. +func newClient(resourceName, host, origin, location, protocol string, rwc io.ReadWriteCloser, handshake handshaker) (ws *Conn, err os.Error) { + br := bufio.NewReader(rwc) + bw := bufio.NewWriter(rwc) + err = handshake(resourceName, host, origin, location, protocol, br, bw) + if err != nil { + return + } + buf := bufio.NewReadWriter(br, bw) + ws = newConn(origin, location, protocol, buf, rwc) + return +} + +/* +Dial opens a new client connection to a Web Socket. + +A trivial example client: + + package main + + import ( + "websocket" + "strings" + ) + + func main() { + ws, err := websocket.Dial("ws://localhost/ws", "", "http://localhost/"); + if err != nil { + panic("Dial: " + err.String()) + } + if _, err := ws.Write([]byte("hello, world!\n")); err != nil { + panic("Write: " + err.String()) + } + var msg = make([]byte, 512); + if n, err := ws.Read(msg); err != nil { + panic("Read: " + err.String()) + } + // use msg[0:n] + } +*/ +func Dial(url, protocol, origin string) (ws *Conn, err os.Error) { + var client net.Conn + + parsedUrl, err := http.ParseURL(url) + if err != nil { + goto Error + } + + switch parsedUrl.Scheme { + case "ws": + client, err = net.Dial("tcp", "", parsedUrl.Host) + + case "wss": + client, err = tls.Dial("tcp", "", parsedUrl.Host, nil) + + default: + err = ErrBadScheme + } + if err != nil { + goto Error + } + + ws, err = newClient(parsedUrl.RawPath, parsedUrl.Host, origin, url, protocol, client, handshake) + if err != nil { + goto Error + } + return + +Error: + return nil, &DialError{url, protocol, origin, err} +} + +/* +Generates handshake key as described in 4.1 Opening handshake step 16 to 22. +cf. http://www.whatwg.org/specs/web-socket-protocol/ +*/ +func generateKeyNumber() (key string, number uint32) { + // 16. Let /spaces_n/ be a random integer from 1 to 12 inclusive. + spaces := rand.Intn(12) + 1 + + // 17. Let /max_n/ be the largest integer not greater than + // 4,294,967,295 divided by /spaces_n/ + max := int(4294967295 / uint32(spaces)) + + // 18. Let /number_n/ be a random integer from 0 to /max_n/ inclusive. + number = uint32(rand.Intn(max + 1)) + + // 19. Let /product_n/ be the result of multiplying /number_n/ and + // /spaces_n/ together. + product := number * uint32(spaces) + + // 20. Let /key_n/ be a string consisting of /product_n/, expressed + // in base ten using the numerals in the range U+0030 DIGIT ZERO (0) + // to U+0039 DIGIT NINE (9). + key = fmt.Sprintf("%d", product) + + // 21. Insert between one and twelve random characters from the ranges + // U+0021 to U+002F and U+003A to U+007E into /key_n/ at random + // positions. + n := rand.Intn(12) + 1 + for i := 0; i < n; i++ { + pos := rand.Intn(len(key)) + 1 + ch := secKeyRandomChars[rand.Intn(len(secKeyRandomChars))] + key = key[0:pos] + string(ch) + key[pos:] + } + + // 22. Insert /spaces_n/ U+0020 SPACE characters into /key_n/ at random + // positions other than the start or end of the string. + for i := 0; i < spaces; i++ { + pos := rand.Intn(len(key)-1) + 1 + key = key[0:pos] + " " + key[pos:] + } + + return +} + +/* +Generates handshake key_3 as described in 4.1 Opening handshake step 26. +cf. http://www.whatwg.org/specs/web-socket-protocol/ +*/ +func generateKey3() (key []byte) { + // 26. Let /key3/ be a string consisting of eight random bytes (or + // equivalently, a random 64 bit integer encoded in big-endian order). + key = make([]byte, 8) + for i := 0; i < 8; i++ { + key[i] = byte(rand.Intn(256)) + } + return +} + +/* +Web Socket protocol handshake based on +http://www.whatwg.org/specs/web-socket-protocol/ +(draft of http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol) +*/ +func handshake(resourceName, host, origin, location, protocol string, br *bufio.Reader, bw *bufio.Writer) (err os.Error) { + // 4.1. Opening handshake. + // Step 5. send a request line. + bw.WriteString("GET " + resourceName + " HTTP/1.1\r\n") + + // Step 6-14. push request headers in fields. + var fields vector.StringVector + fields.Push("Upgrade: WebSocket\r\n") + fields.Push("Connection: Upgrade\r\n") + fields.Push("Host: " + host + "\r\n") + fields.Push("Origin: " + origin + "\r\n") + if protocol != "" { + fields.Push("Sec-WebSocket-Protocol: " + protocol + "\r\n") + } + // TODO(ukai): Step 15. send cookie if any. + + // Step 16-23. generate keys and push Sec-WebSocket-Key in fields. + key1, number1 := generateKeyNumber() + key2, number2 := generateKeyNumber() + fields.Push("Sec-WebSocket-Key1: " + key1 + "\r\n") + fields.Push("Sec-WebSocket-Key2: " + key2 + "\r\n") + + // Step 24. shuffle fields and send them out. + for i := 1; i < len(fields); i++ { + j := rand.Intn(i) + fields[i], fields[j] = fields[j], fields[i] + } + for i := 0; i < len(fields); i++ { + bw.WriteString(fields[i]) + } + // Step 25. send CRLF. + bw.WriteString("\r\n") + + // Step 26. genearte 8 bytes random key. + key3 := generateKey3() + // Step 27. send it out. + bw.Write(key3) + if err = bw.Flush(); err != nil { + return + } + + // Step 28-29, 32-40. read response from server. + resp, err := http.ReadResponse(br, "GET") + if err != nil { + return err + } + // Step 30. check response code is 101. + if resp.StatusCode != 101 { + return ErrBadStatus + } + + // Step 41. check websocket headers. + if resp.Header["Upgrade"] != "WebSocket" || + strings.ToLower(resp.Header["Connection"]) != "upgrade" { + return ErrBadUpgrade + } + + if resp.Header["Sec-Websocket-Origin"] != origin { + return ErrBadWebSocketOrigin + } + + if resp.Header["Sec-Websocket-Location"] != location { + return ErrBadWebSocketLocation + } + + if protocol != "" && resp.Header["Sec-Websocket-Protocol"] != protocol { + return ErrBadWebSocketProtocol + } + + // Step 42-43. get expected data from challange data. + expected, err := getChallengeResponse(number1, number2, key3) + if err != nil { + return err + } + + // Step 44. read 16 bytes from server. + reply := make([]byte, 16) + if _, err = io.ReadFull(br, reply); err != nil { + return err + } + + // Step 45. check the reply equals to expected data. + if !bytes.Equal(expected, reply) { + return ErrChallengeResponse + } + // WebSocket connection is established. + return +} + +/* +Handhake described in (soon obsolete) +draft-hixie-thewebsocket-protocol-75. +*/ +func draft75handshake(resourceName, host, origin, location, protocol string, br *bufio.Reader, bw *bufio.Writer) (err os.Error) { + bw.WriteString("GET " + resourceName + " HTTP/1.1\r\n") + bw.WriteString("Upgrade: WebSocket\r\n") + bw.WriteString("Connection: Upgrade\r\n") + bw.WriteString("Host: " + host + "\r\n") + bw.WriteString("Origin: " + origin + "\r\n") + if protocol != "" { + bw.WriteString("WebSocket-Protocol: " + protocol + "\r\n") + } + bw.WriteString("\r\n") + bw.Flush() + resp, err := http.ReadResponse(br, "GET") + if err != nil { + return + } + if resp.Status != "101 Web Socket Protocol Handshake" { + return ErrBadStatus + } + if resp.Header["Upgrade"] != "WebSocket" || + resp.Header["Connection"] != "Upgrade" { + return ErrBadUpgrade + } + if resp.Header["Websocket-Origin"] != origin { + return ErrBadWebSocketOrigin + } + if resp.Header["Websocket-Location"] != location { + return ErrBadWebSocketLocation + } + if protocol != "" && resp.Header["Websocket-Protocol"] != protocol { + return ErrBadWebSocketProtocol + } + return +} -- cgit v1.2.3