├── .gitignore ├── Godeps ├── Godeps.json └── Readme ├── Procfile ├── README.MD ├── chat.html ├── chat ├── message.go ├── server.go └── user.go ├── config ├── config.json └── configuration.go ├── images ├── add-new-user.png ├── connected-user.png └── golang_chat_flow-diagram.xml ├── main.go └── vendor └── github.com └── gorilla └── websocket ├── .gitignore ├── .travis.yml ├── AUTHORS ├── LICENSE ├── README.md ├── client.go ├── client_clone.go ├── client_clone_legacy.go ├── compression.go ├── conn.go ├── conn_read.go ├── conn_read_legacy.go ├── doc.go ├── json.go ├── mask.go ├── mask_safe.go ├── prepared.go ├── server.go └── util.go /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.log 3 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/gaurigshankar/golang-chat", 3 | "GoVersion": "go1.7", 4 | "GodepVersion": "v79", 5 | "Deps": [ 6 | { 7 | "ImportPath": "github.com/gorilla/websocket", 8 | "Rev": "" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: golang-chat 2 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | ### Summary 2 | This golang chat application is built with help of websocket. 3 | #### Websocket 4 | Websocket protocol is bidirectional and extensively matches for building a chat room. In chatroom apps, we need to open an interactive communication session between the user's browser and a server for bidirectional communication of messages. These days most of the popular browsers support websocket , so its best to use websocket. 5 | #### Gorilla Websocket 6 | In this example i am using [gorilla websocket](https://github.com/gorilla/websocket) lib Instead of using native golang.org/x/net. 7 | When i was searching about websockets in golang, this seems to be popular. 8 | #### golang Channels 9 | Channels are popular in golang for sharing data. In the golang training i took, the trainer was quoting ```Don't communicate by sharing memory, share memory by communicating.``` from https://blog.golang.org/share-memory-by-communicating . So Channels are used to share data between different go routines in this example. 10 | 11 | ### Organisation of files 12 | | File name | Description | 13 | | -----| ---| 14 | | main.go | This is startup file. The `init()` , which by defualt gets called first before `main()` initializes the necessary configuration such as the server host,port and log files . The `main()` creates a `chat.NewServer` type and starts a `go routine` to listen on the server. Apart from it, the `main()` also create a webserver and route handler to load the inital page of the application | 15 | | server.go | The server file has `Server` struct defined with essential fields. The `Server` glues everything together. `server.go` has different functions defined for perfoming activities in a chat room such as The `Listen()` is triggerd as a `go routine` from `main()`. The `Listen()` handles a separate http route `/chat` which is used by `ws` protocol for communicating with the server. The `Listen()` has an infinite `for` loop that has a `select` to match case for different channel operations | 16 | | user.go | The `user.go` is representation of a user. I have a struct defined called `User` which has the props for the user. `NewUser` gets created whenever a new websocket connection is established. There are two main functions here `listenRead()` and `listenWrite()` that helps in listening for new incoming and outgoing messages. `listenWrite()` is triggered as a separate `go routine` | 17 | | message.go | This is a simple struct that defines how a message look like | 18 | | configuration.go | This file helps in setting up the initial configuration of the system by reading the config.json | 19 | |config.json| All the configurable properties are set here | 20 | | chat.html | This is the only html file that defines how the chat app looks like and has JS code to send and recieve websocket frames and display them accordingly | 21 | 22 | 23 | ### Flow diagrams (made with https://www.draw.io and data in images/) 24 | ![Adding new User to chat room](images/add-new-user.png) 25 | ![Connected user message flow](images/connected-user.png) 26 | 27 | 28 | ### Heroku 29 | This app is deployed to Heroku and can be accessed via https://gauri-golang-chat.herokuapp.com/ 30 | 31 | ### Dependencies 32 | * Using https://github.com/tools/godep for managing Dependencies. 33 | * Whenever new Dependencies are added / used, run `godep save ./...` . This will update Godeps/Godeps.json with new dependencies and copy the dependencies to vendor directory. 34 | 35 | ### Rest Services 36 | * Get all the current messages from `/getAllMessages` 37 | -------------------------------------------------------------------------------- /chat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Welcome to golang chat 6 | 7 | 8 | 29 | 30 | 31 |
32 |

Welcome to chat powered by Golang

33 |
34 | 35 |
36 |
37 |
38 | Please start typing something to start the chat session 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 | 119 | 120 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /chat/message.go: -------------------------------------------------------------------------------- 1 | package chat 2 | 3 | type Message struct { 4 | UserName string `json:"userName"` 5 | Body string `json:"body"` 6 | Timestamp string `json:"timestamp"` 7 | } 8 | 9 | func (self *Message) String() string { 10 | return self.UserName +" at "+ self.Timestamp + " says " + self.Body 11 | } 12 | -------------------------------------------------------------------------------- /chat/server.go: -------------------------------------------------------------------------------- 1 | package chat 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "encoding/json" 7 | "github.com/gorilla/websocket" 8 | ) 9 | 10 | var upgrader = websocket.Upgrader{ 11 | ReadBufferSize: 1024, 12 | WriteBufferSize: 1024, 13 | } 14 | 15 | type Server struct { 16 | connectedUsers map[int] *User 17 | Messages []*Message `json: messages` 18 | addUser chan *User 19 | removeUser chan *User 20 | newIncomingMessage chan *Message 21 | errorChannel chan error 22 | doneCh chan bool 23 | 24 | } 25 | 26 | func NewServer() *Server { 27 | Messages := []*Message{} 28 | connectedUsers := make(map[int]*User) 29 | addUser := make(chan *User) 30 | removeUser := make(chan *User) 31 | newIncomingMessage := make(chan *Message) 32 | errorChannel := make(chan error) 33 | doneCh := make(chan bool) 34 | 35 | return &Server { 36 | connectedUsers, 37 | Messages, 38 | addUser, 39 | removeUser, 40 | newIncomingMessage, 41 | errorChannel, 42 | doneCh, 43 | } 44 | } 45 | 46 | func (server *Server) AddUser(user *User) { 47 | log.Println("In AddUser") 48 | server.addUser <- user 49 | } 50 | 51 | func (server *Server) RemoveUser(user *User) { 52 | log.Println("Removing user") 53 | server.removeUser <- user 54 | } 55 | 56 | 57 | func (server *Server) ProcessNewIncomingMessage(message *Message) { 58 | // log.Println("In ProcessNewIncomingMessage ",message) 59 | server.newIncomingMessage <- message 60 | } 61 | 62 | func (server *Server) Done() { 63 | server.doneCh <- true 64 | } 65 | 66 | func (server *Server) sendPastMessages(user *User) { 67 | for _, msg := range server.Messages { 68 | // log.Println("In sendPastMessages writing ",msg) 69 | user.Write(msg) 70 | } 71 | } 72 | 73 | func (server *Server) Err(err error) { 74 | server.errorChannel <- err 75 | } 76 | 77 | func (server *Server) sendAll(msg *Message) { 78 | log.Println("In Sending to all Connected users") 79 | for _, user := range server.connectedUsers { 80 | user.Write(msg) 81 | } 82 | } 83 | 84 | func (server *Server) Listen() { 85 | log.Println("Server Listening .....") 86 | 87 | http.HandleFunc("/chat", server.handleChat) 88 | http.HandleFunc("/getAllMessages", server.handleGetAllMessages) 89 | 90 | for { 91 | select { 92 | // Adding a new user 93 | case user := <-server.addUser: 94 | log.Println("Added a new User") 95 | server.connectedUsers[user.id] = user 96 | log.Println("Now ", len(server.connectedUsers), " users are connected to chat room") 97 | server.sendPastMessages(user) 98 | 99 | case user := <-server.removeUser: 100 | log.Println("Removing user from chat room") 101 | delete(server.connectedUsers, user.id) 102 | 103 | case msg := <-server.newIncomingMessage: 104 | server.Messages = append(server.Messages, msg) 105 | server.sendAll(msg) 106 | case err := <-server.errorChannel: 107 | log.Println("Error : ",err) 108 | case <-server.doneCh: 109 | return 110 | } 111 | } 112 | 113 | } 114 | 115 | func (server *Server) handleChat(responseWriter http.ResponseWriter, request *http.Request) { 116 | log.Println("HAndling chat request ") 117 | var messageObject Message 118 | conn, _ := upgrader.Upgrade(responseWriter, request, nil) 119 | //msgType, msg, err := conn.ReadMessage() 120 | err := conn.ReadJSON(&messageObject) 121 | log.Println("MEssage retireved when add user recieved", &messageObject) 122 | 123 | if err != nil { 124 | log.Println("Error while reading JSON from websocket ",err.Error()) 125 | } 126 | user := NewUser(conn, server) 127 | 128 | log.Println("going to add user", user) 129 | server.AddUser(user) 130 | 131 | log.Println("user added successfully") 132 | server.ProcessNewIncomingMessage(&messageObject) 133 | user.Listen() 134 | 135 | 136 | } 137 | 138 | 139 | func (server *Server) handleGetAllMessages(responseWriter http.ResponseWriter, request *http.Request) { 140 | 141 | 142 | json.NewEncoder(responseWriter).Encode(server) 143 | } 144 | -------------------------------------------------------------------------------- /chat/user.go: -------------------------------------------------------------------------------- 1 | package chat 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "github.com/gorilla/websocket" 7 | ) 8 | 9 | const channelBufSize = 100 10 | 11 | var maxId int = 0 12 | 13 | type User struct { 14 | id int 15 | conn *websocket.Conn 16 | server *Server 17 | outgoingMessage chan *Message 18 | doneCh chan bool 19 | } 20 | 21 | func NewUser(conn *websocket.Conn, server *Server) *User { 22 | if conn == nil { 23 | panic("connection cannot be nil") 24 | } 25 | if server == nil{ 26 | panic(" Server cannot be nil") 27 | } 28 | 29 | maxId++ 30 | ch := make(chan *Message, channelBufSize) 31 | doneCh := make(chan bool) 32 | log.Println("Done creating new User") 33 | return &User{maxId, conn, server, ch, doneCh} 34 | } 35 | 36 | func (user *User) Conn() *websocket.Conn { 37 | return user.conn 38 | } 39 | 40 | func (user *User) Write(message *Message){ 41 | select { 42 | case user.outgoingMessage <- message: 43 | default: 44 | user.server.RemoveUser(user) 45 | err := fmt.Errorf("User %d is disconnected.", user.id) 46 | user.server.Err(err) 47 | 48 | } 49 | } 50 | 51 | func (user *User) Done(){ 52 | user.doneCh <- true 53 | } 54 | 55 | func (user *User) Listen() { 56 | go user.listenWrite() 57 | user.listenRead() 58 | } 59 | 60 | 61 | func (user *User) listenWrite(){ 62 | log.Println("Listening to write to client") 63 | 64 | for { 65 | select { 66 | 67 | //send message to user 68 | case msg:= <-user.outgoingMessage: 69 | // log.Println("send in listenWrite for user :",user.id, msg) 70 | user.conn.WriteJSON(&msg) 71 | 72 | // receive done request 73 | case <-user.doneCh: 74 | log.Println("Done Channel for user:") 75 | user.server.RemoveUser(user) 76 | user.doneCh <- true 77 | return 78 | 79 | } 80 | } 81 | } 82 | 83 | func (user *User) listenRead() { 84 | //log.Println("Listening to Read to client") 85 | for { 86 | select { 87 | //receive Done request 88 | case <- user.doneCh: 89 | user.server.RemoveUser(user) 90 | user.doneCh <- true 91 | return 92 | 93 | // read data from websocket connection 94 | default: 95 | var messageObject Message 96 | err := user.conn.ReadJSON(&messageObject) 97 | 98 | if err != nil { 99 | user.doneCh <- true 100 | log.Println("Error while reading JSON from websocket ",err.Error()) 101 | user.server.Err(err) 102 | } else { 103 | user.server.ProcessNewIncomingMessage(&messageObject) 104 | } 105 | 106 | 107 | 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": 9999, 3 | "hostName" : "dev.walmart.com", 4 | "logfilePath": "logs.log" 5 | } 6 | -------------------------------------------------------------------------------- /config/configuration.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "os" 5 | "fmt" 6 | "log" 7 | "encoding/json" 8 | ) 9 | 10 | type Configuration struct { 11 | Port int `json:"port"` 12 | Hostname string `json:"hostName"` 13 | LogfilePath string `json:"logfilePath"` 14 | } 15 | 16 | 17 | func loadConfig() Configuration { 18 | configuration := Configuration{} 19 | configFile, err := os.Open("./config/config.json") 20 | defer configFile.Close() 21 | if err != nil { 22 | fmt.Println("Error loading config.json",err.Error()) 23 | panic("Error loading config.json") 24 | } 25 | decoder := json.NewDecoder(configFile) 26 | err = decoder.Decode(&configuration) 27 | if err != nil { 28 | panic("Error loading config object from config.json") 29 | } 30 | return configuration 31 | } 32 | 33 | func setupLogging(configuration Configuration) { 34 | nf, err := os.Create(configuration.LogfilePath) 35 | if err != nil { 36 | fmt.Println(err) 37 | } 38 | log.SetOutput(nf) 39 | } 40 | 41 | func LoadConfigAndSetUpLogging() Configuration { 42 | configuration := loadConfig() 43 | setupLogging(configuration) 44 | 45 | return configuration 46 | } 47 | -------------------------------------------------------------------------------- /images/add-new-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaurigshankar/golang-chat/3a5618e6c7043885a98dec630e44ebc233795a6a/images/add-new-user.png -------------------------------------------------------------------------------- /images/connected-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaurigshankar/golang-chat/3a5618e6c7043885a98dec630e44ebc233795a6a/images/connected-user.png -------------------------------------------------------------------------------- /images/golang_chat_flow-diagram.xml: -------------------------------------------------------------------------------- 1 | 7Vtbb9s2GP01BrYBNXS3/JikXTcgHYKlRbdHSqIlLrKoUXRs79ePFEndSKdOSzVN2j7E0ieKog4Pz3ehuvCvtoe3BNTFO5zBcuE52WHhv1543jqI2F9uOApD6LvCkBOUCdPAcIv+g9LoSOsOZbAZNaQYlxTVY2OKqwqmdGQDhOD9uNkGl+On1iCHmuE2BaVu/YgyWghrHDq9/TeI8kI92XXklQSkdznBu0o+b+H5m/afuLwFqi/ZvilAhvcDk/9m4V8RjKk42h6uYMmhVbCJ+349cbUbN4EVPecGT9xwD8odVCOOSnbr5QazHtgA6VGCEv27w+rCq6adsgvWwPPrQ3+RHeX8V04MzFiLDw0kfBJK9qKyczYg0b9oLbHoHuVReOD2gm5LZnDZYUMJvoNXuMSEWSpcQT4UVJYTEyhRXrHTlAHAHutf3kNCEZvZC3lhi7KMP+ZyXyAKb2uQ8mfuGY27gQzhk4jyXuBhYJJwvoV4Cyk5sibyaje1kvqeXAr7nkd+FApbMeCQug1I6uZdz/30sQM5gyemf6UhCTNGbHmKCS1wjitQvumtly1bIe/BGUPOMCDHv6S9PfmbnyxDdvoPpPQo1y3YUcxMfe/XGNeyj5OANnhHUjlGX65wQHIoWwXCxEf/IOgEloCi+/Gy/RIIfW1BlKihsPoTguynnzV4x+CZKDUAdEhXJgwZgPEm1bjNrkRpDJONHT667piP7srRCNm1GRIyskDIQEPzhuAUNs0fcP97leItqvJ37JTpqW1wQxhngQnc2Ev8KLK02FdngBvPBK4b2VztVld0aHv5yltvMGq9kpqcYKK1jrNUz1a9CFWRN07A7UZyFt6hwVdO8FfO5hoksLzBDaIIc6eTYErxlrunphbxygYd+CwMZ2DqqSjH3Oz4WNhQ80duDzmPv5YpogQdlgmPfFqvZ0M4JtSOAo3awWpp8GQrbxmenslz2a2U+Jn5MjfUnZkiytf3Zq7O2evWnT1LtQ3iM9TWn0ltVXxmCJULtw9khSUhUwvrXm9WK8P7AjV8fFnGf2gB2d8K7nnGILxjmwB1l9ICsPC6bM8B18M94PelBAIRcqOqa8v04J7H3xHY8jmskqYejEBG+Wk3YX0g77upl/DEZRrbV5rvlkNiP790lhOBfmeuZwj+G8ZXNipmCPuz9+0CfuWdYjVm8LS5if+6YLkBrOYJDSIDV9cGrro2EoFuuPOQlU94ryNO0bIPtLzboIpB3ObcPOkruXx2NO0bNrDkfpDxBjRQJMZpoe6amaonuIlrSAD318vviKXBhKWhgaXhbCydKV1tvXrv5G27eOXORy7eesR7Noh6CaeBVXZRls/Sx0fhU/p4PV+1LZuDuWmPe38/8fSgHdlUDKX/H1TXdszB807wZhgecKUkmKUcnpMcW5ktS645ov3yI2lF+ruRuSmrvqrM+eEsMucuHdcbZzOBOr2BBLFx8lRwBv0zpDj+k6U4hrS8ZfezLNZFK2/EU2+tEzU2pNxWxC+aVfzaBEfUUpsuPBQih2SI1otg24K08rWHSYPTO8gVDVSZ1LLmVJTIdKZSD32w1tiNenjHdySJ8acVcWVQxLUNpsUGpk1A5pto9fmv2flEkKgenAdf3wvC0fv7hvzMVLiNLZS2TAvtqaWqm5NPE2iAkGdAyIbLtJsYHBDtSn/sWPjKVZckGMqCqy+rCyowh14z0p1m9FQ+M34Z8M6+vTDZXYjHHYjZ1XYWHtuNvQ2K9cy68jkJ4CN0RSmxpwnxXDLjmspkFhHbbGCUGpU4W60Tx7GEmPqc4Gsgplcb2oQyxxpw3843HI9AVJXFJktWwzeYC1/9+wNRwH/hCPt69GWE2LMAsV7k2QJUvXCADRoxF4d9k6raraLtRf1qvGGGdzTHozRyMdo124nv0Ez7aPZ2HNQgfuyDnSi9+Qb/PmPtLdBg/Va+kbFfMjOHn1NfFkxgtRd/qjfSPp57vgW56ScH3lr/nHOugpyvxwK2dXQyPd/wTq6uq8auf+zp8srWRHEDPbgyfpT4OYrLTvuv1YVm9P8jwH/zPw==7Vtdc6M2FP01nmk7sx6+jR/z1fYh28lMtpP2UYAM6mJEhRw7/fWVkGQQkrNJDfYmaWZiw5UQ4tyjI92LPPOv1rtfCKiLzziD5cxzst3Mv5553jKI2Cc3PAlD6LvCkBOUCVPPcI/+gdLoSOsGZbDRKlKMS4pq3ZjiqoIp1WyAELzVq61wqd+1Bjk0DPcpKE3rA8poIaxx6HT2XyHKC3Vn15ElCUi/5gRvKnm/meev2j9RvAaqLVm/KUCGtz2TfzPzrwjGVBytd1ew5NAq2MR1Px8o3febwIq+5AJPXPAIyg1UPW77RZ8UFvxxarNdeatHSCjc2ZwCEtVC92CMLxCvISVPrN5O97mkir+QzWx7wMsqRQ/zOJTulq7O9w13j8sO5BPbn97/9tO3voS8vjPzL7cFovC+Bikv3TLqM1tB16z9a5cdrlBZXuESk/ZaPwMwXqXM3lCCv8JeSZTGMFmxEhNX7wCuJn49gDwLQMp2DECBgQfM2ECRp5jQAue4AuVNZ73UEeuhA3eI/iHN/PhPfjxfhPy0Yh3bl/GTXuFfkNInqRFgQzEzdXe+xbiW7R/EssEbkkLN5RSQHFLNxJ/sWbwJLAFFj7pEHINu+C7QHQ83eekdRuwWh/TBi/UGhHPlNX2le10zghBGM60L98/yIq9GE4tKCOMssIlK7CV+FB0pKkqFPUOEp9KYxbR4rVYwSq0inC2WieOMg1cUngyv2MBr00Ayz7EBG+s71bHRMahwBQeASRMoUV6x05QhApn9kiOB2ProQhasUZa1emRzxjF4ytJgMFoNdIOJ0F0a6DJwH987vr7zMoC94wFWOtxDeA1Q9b7xtcjDVAR2XQPfmReVHMgVbifEDuHo7w1WBZ+adg1wwSp4Qb3rCtlRzr8vsgxVOe8d+6/gln3+zsaGapz1S7Qvan/HzjwUuRx0pheGmjcDc7R4i3Aad6rejrRIPLASPG4hqLrYW2a7csT019nu2RbaqjfPrTEUqW5BAss73CCKMCdXginFa07DphaphhXacWz7uA4ZSTladoKziL/mt1zvcp46maeIErSbJzxp0bJ7BMIO1rpuaIbUwWJuYezCm48QVlvQfROctcSGbnAuzqqVyHsAMTzbwDczPAWo2GRxVQD6w48GwicPzF4/tH19LnLDhTG2XVvoEY2xtggMOH+DW74KGBvL/5I5ezWWfqTr5NKUyemgDA0o2QJrCihPQsvQG045J6TlyIk0JZXu1FIZWaRycTapNPNWDayyO9DQz7BpQA6bN8nMaJj4O+UoV8nAt8bMhYWZ8dmYaWYIHwgj2hudc/T5+7R0dAzERllTzgNvQMllO3xbwx0kiPWUxzZtC+MyNbYw1TsbU83cbIkaCqu3y1c3/rZ8xpZIcpQ1kpmMVdmmwu0STcKSkKGFNW9Wq5XhSwFZ0wP3OAXrr8hwoWqFKmae8e0DhFfl1GRMY5GDVrGBJc8LeE4KGl57DWhaqKsisOZerJKm7nVCJuLSvcu6XJvvpl7CtwsM0294Q3OMqlxOxoeaHqThcA0J4JmMea9CPUGSrmG05elB/zrszr60g/mTd4jcmPFwVbb7HwqUZbAaibPD7LYl/+Ha9hS4o2TsbC+0xmXtlghigixrWkryE0UQTkHFEYfJqyrftMnaviVlsXDFN+5MyVJ7cvjDcHE43/uBJTAKJ+OiOSGNzUU9nyIFtdmrpnhRILcE8YyqZGDCr4UN3yWEmoJNgexBK/6ZEggoVOJqfc/w3jnjDoJpy/uG6eRLvQqckDLCqVKPhL+551EjJK096cmUeAHLOfPUzrJlKVRuny9hc5vzpVCX69QxVW9MuQOiC1JL2ddPz74S+zAMDt2B6llEbzoGe5NEOVMH3WqLrBbKROcKZVRvejJw20r7m0wCLRxdUa3pSX+iqNu3bTMdV1E713zHEcxeLP+PXA4mK5c6T0PLzD/ZYtE33+6MzVNbRrk1dmGMNXYZckbO6oMFQbsP7kHERh+GMuFyMNX60XScYafdbxPE3tju9x/+zb8= -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "os" 6 | "log" 7 | "fmt" 8 | "strconv" 9 | "github.com/gaurigshankar/golang-chat/chat" 10 | "github.com/gaurigshankar/golang-chat/config" 11 | ) 12 | 13 | var configuration config.Configuration 14 | var serverHostName string 15 | 16 | func init() { 17 | configuration = config.LoadConfigAndSetUpLogging() 18 | 19 | herokuConfigPort := os.Getenv("PORT") 20 | if herokuConfigPort == "" { 21 | serverHostName = fmt.Sprintf("%s:%s",configuration.Hostname,strconv.Itoa(configuration.Port)) 22 | } else { 23 | serverHostName = fmt.Sprintf(":%s",herokuConfigPort) 24 | } 25 | log.Println("The serverHost url", serverHostName) 26 | 27 | } 28 | 29 | func main() { 30 | 31 | // websocket server 32 | server := chat.NewServer() 33 | go server.Listen() 34 | http.HandleFunc("/messages", handleHomePage) 35 | http.HandleFunc("/", handleHomePage) 36 | 37 | http.ListenAndServe(serverHostName, nil) 38 | 39 | } 40 | 41 | func handleHomePage(responseWriter http.ResponseWriter, request *http.Request) { 42 | http.ServeFile(responseWriter, request, "chat.html") 43 | } 44 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | 24 | .idea/ 25 | *.iml -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | 4 | matrix: 5 | include: 6 | - go: 1.4 7 | - go: 1.5 8 | - go: 1.6 9 | - go: 1.7 10 | - go: 1.8 11 | - go: tip 12 | allow_failures: 13 | - go: tip 14 | 15 | script: 16 | - go get -t -v ./... 17 | - diff -u <(echo -n) <(gofmt -d .) 18 | - go vet $(go list ./... | grep -v /vendor/) 19 | - go test -v -race ./... 20 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of Gorilla WebSocket authors for copyright 2 | # purposes. 3 | # 4 | # Please keep the list sorted. 5 | 6 | Gary Burd 7 | Joachim Bauch 8 | 9 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/README.md: -------------------------------------------------------------------------------- 1 | # Gorilla WebSocket 2 | 3 | Gorilla WebSocket is a [Go](http://golang.org/) implementation of the 4 | [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. 5 | 6 | [![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket) 7 | [![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket) 8 | 9 | ### Documentation 10 | 11 | * [API Reference](http://godoc.org/github.com/gorilla/websocket) 12 | * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) 13 | * [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) 14 | * [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) 15 | * [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) 16 | 17 | ### Status 18 | 19 | The Gorilla WebSocket package provides a complete and tested implementation of 20 | the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The 21 | package API is stable. 22 | 23 | ### Installation 24 | 25 | go get github.com/gorilla/websocket 26 | 27 | ### Protocol Compliance 28 | 29 | The Gorilla WebSocket package passes the server tests in the [Autobahn Test 30 | Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn 31 | subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). 32 | 33 | ### Gorilla WebSocket compared with other packages 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
github.com/gorillagolang.org/x/net
RFC 6455 Features
Passes Autobahn Test SuiteYesNo
Receive fragmented messageYesNo, see note 1
Send close messageYesNo
Send pings and receive pongsYesNo
Get the type of a received data messageYesYes, see note 2
Other Features
Compression ExtensionsExperimentalNo
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
53 | 54 | Notes: 55 | 56 | 1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). 57 | 2. The application can get the type of a received data message by implementing 58 | a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) 59 | function. 60 | 3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. 61 | Read returns when the input buffer is full or a frame boundary is 62 | encountered. Each call to Write sends a single frame message. The Gorilla 63 | io.Reader and io.WriteCloser operate on a single WebSocket message. 64 | 65 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket 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 | "bufio" 9 | "bytes" 10 | "crypto/tls" 11 | "encoding/base64" 12 | "errors" 13 | "io" 14 | "io/ioutil" 15 | "net" 16 | "net/http" 17 | "net/url" 18 | "strings" 19 | "time" 20 | ) 21 | 22 | // ErrBadHandshake is returned when the server response to opening handshake is 23 | // invalid. 24 | var ErrBadHandshake = errors.New("websocket: bad handshake") 25 | 26 | var errInvalidCompression = errors.New("websocket: invalid compression negotiation") 27 | 28 | // NewClient creates a new client connection using the given net connection. 29 | // The URL u specifies the host and request URI. Use requestHeader to specify 30 | // the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies 31 | // (Cookie). Use the response.Header to get the selected subprotocol 32 | // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). 33 | // 34 | // If the WebSocket handshake fails, ErrBadHandshake is returned along with a 35 | // non-nil *http.Response so that callers can handle redirects, authentication, 36 | // etc. 37 | // 38 | // Deprecated: Use Dialer instead. 39 | func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) { 40 | d := Dialer{ 41 | ReadBufferSize: readBufSize, 42 | WriteBufferSize: writeBufSize, 43 | NetDial: func(net, addr string) (net.Conn, error) { 44 | return netConn, nil 45 | }, 46 | } 47 | return d.Dial(u.String(), requestHeader) 48 | } 49 | 50 | // A Dialer contains options for connecting to WebSocket server. 51 | type Dialer struct { 52 | // NetDial specifies the dial function for creating TCP connections. If 53 | // NetDial is nil, net.Dial is used. 54 | NetDial func(network, addr string) (net.Conn, error) 55 | 56 | // Proxy specifies a function to return a proxy for a given 57 | // Request. If the function returns a non-nil error, the 58 | // request is aborted with the provided error. 59 | // If Proxy is nil or returns a nil *URL, no proxy is used. 60 | Proxy func(*http.Request) (*url.URL, error) 61 | 62 | // TLSClientConfig specifies the TLS configuration to use with tls.Client. 63 | // If nil, the default configuration is used. 64 | TLSClientConfig *tls.Config 65 | 66 | // HandshakeTimeout specifies the duration for the handshake to complete. 67 | HandshakeTimeout time.Duration 68 | 69 | // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer 70 | // size is zero, then a useful default size is used. The I/O buffer sizes 71 | // do not limit the size of the messages that can be sent or received. 72 | ReadBufferSize, WriteBufferSize int 73 | 74 | // Subprotocols specifies the client's requested subprotocols. 75 | Subprotocols []string 76 | 77 | // EnableCompression specifies if the client should attempt to negotiate 78 | // per message compression (RFC 7692). Setting this value to true does not 79 | // guarantee that compression will be supported. Currently only "no context 80 | // takeover" modes are supported. 81 | EnableCompression bool 82 | 83 | // Jar specifies the cookie jar. 84 | // If Jar is nil, cookies are not sent in requests and ignored 85 | // in responses. 86 | Jar http.CookieJar 87 | } 88 | 89 | var errMalformedURL = errors.New("malformed ws or wss URL") 90 | 91 | // parseURL parses the URL. 92 | // 93 | // This function is a replacement for the standard library url.Parse function. 94 | // In Go 1.4 and earlier, url.Parse loses information from the path. 95 | func parseURL(s string) (*url.URL, error) { 96 | // From the RFC: 97 | // 98 | // ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ] 99 | // wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ] 100 | var u url.URL 101 | switch { 102 | case strings.HasPrefix(s, "ws://"): 103 | u.Scheme = "ws" 104 | s = s[len("ws://"):] 105 | case strings.HasPrefix(s, "wss://"): 106 | u.Scheme = "wss" 107 | s = s[len("wss://"):] 108 | default: 109 | return nil, errMalformedURL 110 | } 111 | 112 | if i := strings.Index(s, "?"); i >= 0 { 113 | u.RawQuery = s[i+1:] 114 | s = s[:i] 115 | } 116 | 117 | if i := strings.Index(s, "/"); i >= 0 { 118 | u.Opaque = s[i:] 119 | s = s[:i] 120 | } else { 121 | u.Opaque = "/" 122 | } 123 | 124 | u.Host = s 125 | 126 | if strings.Contains(u.Host, "@") { 127 | // Don't bother parsing user information because user information is 128 | // not allowed in websocket URIs. 129 | return nil, errMalformedURL 130 | } 131 | 132 | return &u, nil 133 | } 134 | 135 | func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { 136 | hostPort = u.Host 137 | hostNoPort = u.Host 138 | if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") { 139 | hostNoPort = hostNoPort[:i] 140 | } else { 141 | switch u.Scheme { 142 | case "wss": 143 | hostPort += ":443" 144 | case "https": 145 | hostPort += ":443" 146 | default: 147 | hostPort += ":80" 148 | } 149 | } 150 | return hostPort, hostNoPort 151 | } 152 | 153 | // DefaultDialer is a dialer with all fields set to the default zero values. 154 | var DefaultDialer = &Dialer{ 155 | Proxy: http.ProxyFromEnvironment, 156 | } 157 | 158 | // Dial creates a new client connection. Use requestHeader to specify the 159 | // origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie). 160 | // Use the response.Header to get the selected subprotocol 161 | // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). 162 | // 163 | // If the WebSocket handshake fails, ErrBadHandshake is returned along with a 164 | // non-nil *http.Response so that callers can handle redirects, authentication, 165 | // etcetera. The response body may not contain the entire response and does not 166 | // need to be closed by the application. 167 | func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { 168 | 169 | if d == nil { 170 | d = &Dialer{ 171 | Proxy: http.ProxyFromEnvironment, 172 | } 173 | } 174 | 175 | challengeKey, err := generateChallengeKey() 176 | if err != nil { 177 | return nil, nil, err 178 | } 179 | 180 | u, err := parseURL(urlStr) 181 | if err != nil { 182 | return nil, nil, err 183 | } 184 | 185 | switch u.Scheme { 186 | case "ws": 187 | u.Scheme = "http" 188 | case "wss": 189 | u.Scheme = "https" 190 | default: 191 | return nil, nil, errMalformedURL 192 | } 193 | 194 | if u.User != nil { 195 | // User name and password are not allowed in websocket URIs. 196 | return nil, nil, errMalformedURL 197 | } 198 | 199 | req := &http.Request{ 200 | Method: "GET", 201 | URL: u, 202 | Proto: "HTTP/1.1", 203 | ProtoMajor: 1, 204 | ProtoMinor: 1, 205 | Header: make(http.Header), 206 | Host: u.Host, 207 | } 208 | 209 | // Set the cookies present in the cookie jar of the dialer 210 | if d.Jar != nil { 211 | for _, cookie := range d.Jar.Cookies(u) { 212 | req.AddCookie(cookie) 213 | } 214 | } 215 | 216 | // Set the request headers using the capitalization for names and values in 217 | // RFC examples. Although the capitalization shouldn't matter, there are 218 | // servers that depend on it. The Header.Set method is not used because the 219 | // method canonicalizes the header names. 220 | req.Header["Upgrade"] = []string{"websocket"} 221 | req.Header["Connection"] = []string{"Upgrade"} 222 | req.Header["Sec-WebSocket-Key"] = []string{challengeKey} 223 | req.Header["Sec-WebSocket-Version"] = []string{"13"} 224 | if len(d.Subprotocols) > 0 { 225 | req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")} 226 | } 227 | for k, vs := range requestHeader { 228 | switch { 229 | case k == "Host": 230 | if len(vs) > 0 { 231 | req.Host = vs[0] 232 | } 233 | case k == "Upgrade" || 234 | k == "Connection" || 235 | k == "Sec-Websocket-Key" || 236 | k == "Sec-Websocket-Version" || 237 | k == "Sec-Websocket-Extensions" || 238 | (k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0): 239 | return nil, nil, errors.New("websocket: duplicate header not allowed: " + k) 240 | default: 241 | req.Header[k] = vs 242 | } 243 | } 244 | 245 | if d.EnableCompression { 246 | req.Header.Set("Sec-Websocket-Extensions", "permessage-deflate; server_no_context_takeover; client_no_context_takeover") 247 | } 248 | 249 | hostPort, hostNoPort := hostPortNoPort(u) 250 | 251 | var proxyURL *url.URL 252 | // Check wether the proxy method has been configured 253 | if d.Proxy != nil { 254 | proxyURL, err = d.Proxy(req) 255 | } 256 | if err != nil { 257 | return nil, nil, err 258 | } 259 | 260 | var targetHostPort string 261 | if proxyURL != nil { 262 | targetHostPort, _ = hostPortNoPort(proxyURL) 263 | } else { 264 | targetHostPort = hostPort 265 | } 266 | 267 | var deadline time.Time 268 | if d.HandshakeTimeout != 0 { 269 | deadline = time.Now().Add(d.HandshakeTimeout) 270 | } 271 | 272 | netDial := d.NetDial 273 | if netDial == nil { 274 | netDialer := &net.Dialer{Deadline: deadline} 275 | netDial = netDialer.Dial 276 | } 277 | 278 | netConn, err := netDial("tcp", targetHostPort) 279 | if err != nil { 280 | return nil, nil, err 281 | } 282 | 283 | defer func() { 284 | if netConn != nil { 285 | netConn.Close() 286 | } 287 | }() 288 | 289 | if err := netConn.SetDeadline(deadline); err != nil { 290 | return nil, nil, err 291 | } 292 | 293 | if proxyURL != nil { 294 | connectHeader := make(http.Header) 295 | if user := proxyURL.User; user != nil { 296 | proxyUser := user.Username() 297 | if proxyPassword, passwordSet := user.Password(); passwordSet { 298 | credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword)) 299 | connectHeader.Set("Proxy-Authorization", "Basic "+credential) 300 | } 301 | } 302 | connectReq := &http.Request{ 303 | Method: "CONNECT", 304 | URL: &url.URL{Opaque: hostPort}, 305 | Host: hostPort, 306 | Header: connectHeader, 307 | } 308 | 309 | connectReq.Write(netConn) 310 | 311 | // Read response. 312 | // Okay to use and discard buffered reader here, because 313 | // TLS server will not speak until spoken to. 314 | br := bufio.NewReader(netConn) 315 | resp, err := http.ReadResponse(br, connectReq) 316 | if err != nil { 317 | return nil, nil, err 318 | } 319 | if resp.StatusCode != 200 { 320 | f := strings.SplitN(resp.Status, " ", 2) 321 | return nil, nil, errors.New(f[1]) 322 | } 323 | } 324 | 325 | if u.Scheme == "https" { 326 | cfg := cloneTLSConfig(d.TLSClientConfig) 327 | if cfg.ServerName == "" { 328 | cfg.ServerName = hostNoPort 329 | } 330 | tlsConn := tls.Client(netConn, cfg) 331 | netConn = tlsConn 332 | if err := tlsConn.Handshake(); err != nil { 333 | return nil, nil, err 334 | } 335 | if !cfg.InsecureSkipVerify { 336 | if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { 337 | return nil, nil, err 338 | } 339 | } 340 | } 341 | 342 | conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize) 343 | 344 | if err := req.Write(netConn); err != nil { 345 | return nil, nil, err 346 | } 347 | 348 | resp, err := http.ReadResponse(conn.br, req) 349 | if err != nil { 350 | return nil, nil, err 351 | } 352 | 353 | if d.Jar != nil { 354 | if rc := resp.Cookies(); len(rc) > 0 { 355 | d.Jar.SetCookies(u, rc) 356 | } 357 | } 358 | 359 | if resp.StatusCode != 101 || 360 | !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || 361 | !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || 362 | resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) { 363 | // Before closing the network connection on return from this 364 | // function, slurp up some of the response to aid application 365 | // debugging. 366 | buf := make([]byte, 1024) 367 | n, _ := io.ReadFull(resp.Body, buf) 368 | resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n])) 369 | return nil, resp, ErrBadHandshake 370 | } 371 | 372 | for _, ext := range parseExtensions(resp.Header) { 373 | if ext[""] != "permessage-deflate" { 374 | continue 375 | } 376 | _, snct := ext["server_no_context_takeover"] 377 | _, cnct := ext["client_no_context_takeover"] 378 | if !snct || !cnct { 379 | return nil, resp, errInvalidCompression 380 | } 381 | conn.newCompressionWriter = compressNoContextTakeover 382 | conn.newDecompressionReader = decompressNoContextTakeover 383 | break 384 | } 385 | 386 | resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{})) 387 | conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") 388 | 389 | netConn.SetDeadline(time.Time{}) 390 | netConn = nil // to avoid close in defer. 391 | return conn, resp, nil 392 | } 393 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/client_clone.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket 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 | // +build go1.8 6 | 7 | package websocket 8 | 9 | import "crypto/tls" 10 | 11 | func cloneTLSConfig(cfg *tls.Config) *tls.Config { 12 | if cfg == nil { 13 | return &tls.Config{} 14 | } 15 | return cfg.Clone() 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/client_clone_legacy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket 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 | // +build !go1.8 6 | 7 | package websocket 8 | 9 | import "crypto/tls" 10 | 11 | // cloneTLSConfig clones all public fields except the fields 12 | // SessionTicketsDisabled and SessionTicketKey. This avoids copying the 13 | // sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a 14 | // config in active use. 15 | func cloneTLSConfig(cfg *tls.Config) *tls.Config { 16 | if cfg == nil { 17 | return &tls.Config{} 18 | } 19 | return &tls.Config{ 20 | Rand: cfg.Rand, 21 | Time: cfg.Time, 22 | Certificates: cfg.Certificates, 23 | NameToCertificate: cfg.NameToCertificate, 24 | GetCertificate: cfg.GetCertificate, 25 | RootCAs: cfg.RootCAs, 26 | NextProtos: cfg.NextProtos, 27 | ServerName: cfg.ServerName, 28 | ClientAuth: cfg.ClientAuth, 29 | ClientCAs: cfg.ClientCAs, 30 | InsecureSkipVerify: cfg.InsecureSkipVerify, 31 | CipherSuites: cfg.CipherSuites, 32 | PreferServerCipherSuites: cfg.PreferServerCipherSuites, 33 | ClientSessionCache: cfg.ClientSessionCache, 34 | MinVersion: cfg.MinVersion, 35 | MaxVersion: cfg.MaxVersion, 36 | CurvePreferences: cfg.CurvePreferences, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/compression.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Gorilla WebSocket 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 | "compress/flate" 9 | "errors" 10 | "io" 11 | "strings" 12 | "sync" 13 | ) 14 | 15 | const ( 16 | minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6 17 | maxCompressionLevel = flate.BestCompression 18 | defaultCompressionLevel = 1 19 | ) 20 | 21 | var ( 22 | flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool 23 | flateReaderPool = sync.Pool{New: func() interface{} { 24 | return flate.NewReader(nil) 25 | }} 26 | ) 27 | 28 | func decompressNoContextTakeover(r io.Reader) io.ReadCloser { 29 | const tail = 30 | // Add four bytes as specified in RFC 31 | "\x00\x00\xff\xff" + 32 | // Add final block to squelch unexpected EOF error from flate reader. 33 | "\x01\x00\x00\xff\xff" 34 | 35 | fr, _ := flateReaderPool.Get().(io.ReadCloser) 36 | fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil) 37 | return &flateReadWrapper{fr} 38 | } 39 | 40 | func isValidCompressionLevel(level int) bool { 41 | return minCompressionLevel <= level && level <= maxCompressionLevel 42 | } 43 | 44 | func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser { 45 | p := &flateWriterPools[level-minCompressionLevel] 46 | tw := &truncWriter{w: w} 47 | fw, _ := p.Get().(*flate.Writer) 48 | if fw == nil { 49 | fw, _ = flate.NewWriter(tw, level) 50 | } else { 51 | fw.Reset(tw) 52 | } 53 | return &flateWriteWrapper{fw: fw, tw: tw, p: p} 54 | } 55 | 56 | // truncWriter is an io.Writer that writes all but the last four bytes of the 57 | // stream to another io.Writer. 58 | type truncWriter struct { 59 | w io.WriteCloser 60 | n int 61 | p [4]byte 62 | } 63 | 64 | func (w *truncWriter) Write(p []byte) (int, error) { 65 | n := 0 66 | 67 | // fill buffer first for simplicity. 68 | if w.n < len(w.p) { 69 | n = copy(w.p[w.n:], p) 70 | p = p[n:] 71 | w.n += n 72 | if len(p) == 0 { 73 | return n, nil 74 | } 75 | } 76 | 77 | m := len(p) 78 | if m > len(w.p) { 79 | m = len(w.p) 80 | } 81 | 82 | if nn, err := w.w.Write(w.p[:m]); err != nil { 83 | return n + nn, err 84 | } 85 | 86 | copy(w.p[:], w.p[m:]) 87 | copy(w.p[len(w.p)-m:], p[len(p)-m:]) 88 | nn, err := w.w.Write(p[:len(p)-m]) 89 | return n + nn, err 90 | } 91 | 92 | type flateWriteWrapper struct { 93 | fw *flate.Writer 94 | tw *truncWriter 95 | p *sync.Pool 96 | } 97 | 98 | func (w *flateWriteWrapper) Write(p []byte) (int, error) { 99 | if w.fw == nil { 100 | return 0, errWriteClosed 101 | } 102 | return w.fw.Write(p) 103 | } 104 | 105 | func (w *flateWriteWrapper) Close() error { 106 | if w.fw == nil { 107 | return errWriteClosed 108 | } 109 | err1 := w.fw.Flush() 110 | w.p.Put(w.fw) 111 | w.fw = nil 112 | if w.tw.p != [4]byte{0, 0, 0xff, 0xff} { 113 | return errors.New("websocket: internal error, unexpected bytes at end of flate stream") 114 | } 115 | err2 := w.tw.w.Close() 116 | if err1 != nil { 117 | return err1 118 | } 119 | return err2 120 | } 121 | 122 | type flateReadWrapper struct { 123 | fr io.ReadCloser 124 | } 125 | 126 | func (r *flateReadWrapper) Read(p []byte) (int, error) { 127 | if r.fr == nil { 128 | return 0, io.ErrClosedPipe 129 | } 130 | n, err := r.fr.Read(p) 131 | if err == io.EOF { 132 | // Preemptively place the reader back in the pool. This helps with 133 | // scenarios where the application does not call NextReader() soon after 134 | // this final read. 135 | r.Close() 136 | } 137 | return n, err 138 | } 139 | 140 | func (r *flateReadWrapper) Close() error { 141 | if r.fr == nil { 142 | return io.ErrClosedPipe 143 | } 144 | err := r.fr.Close() 145 | flateReaderPool.Put(r.fr) 146 | r.fr = nil 147 | return err 148 | } 149 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/conn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket 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 | "bufio" 9 | "encoding/binary" 10 | "errors" 11 | "io" 12 | "io/ioutil" 13 | "math/rand" 14 | "net" 15 | "strconv" 16 | "sync" 17 | "time" 18 | "unicode/utf8" 19 | ) 20 | 21 | const ( 22 | // Frame header byte 0 bits from Section 5.2 of RFC 6455 23 | finalBit = 1 << 7 24 | rsv1Bit = 1 << 6 25 | rsv2Bit = 1 << 5 26 | rsv3Bit = 1 << 4 27 | 28 | // Frame header byte 1 bits from Section 5.2 of RFC 6455 29 | maskBit = 1 << 7 30 | 31 | maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask 32 | maxControlFramePayloadSize = 125 33 | 34 | writeWait = time.Second 35 | 36 | defaultReadBufferSize = 4096 37 | defaultWriteBufferSize = 4096 38 | 39 | continuationFrame = 0 40 | noFrame = -1 41 | ) 42 | 43 | // Close codes defined in RFC 6455, section 11.7. 44 | const ( 45 | CloseNormalClosure = 1000 46 | CloseGoingAway = 1001 47 | CloseProtocolError = 1002 48 | CloseUnsupportedData = 1003 49 | CloseNoStatusReceived = 1005 50 | CloseAbnormalClosure = 1006 51 | CloseInvalidFramePayloadData = 1007 52 | ClosePolicyViolation = 1008 53 | CloseMessageTooBig = 1009 54 | CloseMandatoryExtension = 1010 55 | CloseInternalServerErr = 1011 56 | CloseServiceRestart = 1012 57 | CloseTryAgainLater = 1013 58 | CloseTLSHandshake = 1015 59 | ) 60 | 61 | // The message types are defined in RFC 6455, section 11.8. 62 | const ( 63 | // TextMessage denotes a text data message. The text message payload is 64 | // interpreted as UTF-8 encoded text data. 65 | TextMessage = 1 66 | 67 | // BinaryMessage denotes a binary data message. 68 | BinaryMessage = 2 69 | 70 | // CloseMessage denotes a close control message. The optional message 71 | // payload contains a numeric code and text. Use the FormatCloseMessage 72 | // function to format a close message payload. 73 | CloseMessage = 8 74 | 75 | // PingMessage denotes a ping control message. The optional message payload 76 | // is UTF-8 encoded text. 77 | PingMessage = 9 78 | 79 | // PongMessage denotes a ping control message. The optional message payload 80 | // is UTF-8 encoded text. 81 | PongMessage = 10 82 | ) 83 | 84 | // ErrCloseSent is returned when the application writes a message to the 85 | // connection after sending a close message. 86 | var ErrCloseSent = errors.New("websocket: close sent") 87 | 88 | // ErrReadLimit is returned when reading a message that is larger than the 89 | // read limit set for the connection. 90 | var ErrReadLimit = errors.New("websocket: read limit exceeded") 91 | 92 | // netError satisfies the net Error interface. 93 | type netError struct { 94 | msg string 95 | temporary bool 96 | timeout bool 97 | } 98 | 99 | func (e *netError) Error() string { return e.msg } 100 | func (e *netError) Temporary() bool { return e.temporary } 101 | func (e *netError) Timeout() bool { return e.timeout } 102 | 103 | // CloseError represents close frame. 104 | type CloseError struct { 105 | 106 | // Code is defined in RFC 6455, section 11.7. 107 | Code int 108 | 109 | // Text is the optional text payload. 110 | Text string 111 | } 112 | 113 | func (e *CloseError) Error() string { 114 | s := []byte("websocket: close ") 115 | s = strconv.AppendInt(s, int64(e.Code), 10) 116 | switch e.Code { 117 | case CloseNormalClosure: 118 | s = append(s, " (normal)"...) 119 | case CloseGoingAway: 120 | s = append(s, " (going away)"...) 121 | case CloseProtocolError: 122 | s = append(s, " (protocol error)"...) 123 | case CloseUnsupportedData: 124 | s = append(s, " (unsupported data)"...) 125 | case CloseNoStatusReceived: 126 | s = append(s, " (no status)"...) 127 | case CloseAbnormalClosure: 128 | s = append(s, " (abnormal closure)"...) 129 | case CloseInvalidFramePayloadData: 130 | s = append(s, " (invalid payload data)"...) 131 | case ClosePolicyViolation: 132 | s = append(s, " (policy violation)"...) 133 | case CloseMessageTooBig: 134 | s = append(s, " (message too big)"...) 135 | case CloseMandatoryExtension: 136 | s = append(s, " (mandatory extension missing)"...) 137 | case CloseInternalServerErr: 138 | s = append(s, " (internal server error)"...) 139 | case CloseTLSHandshake: 140 | s = append(s, " (TLS handshake error)"...) 141 | } 142 | if e.Text != "" { 143 | s = append(s, ": "...) 144 | s = append(s, e.Text...) 145 | } 146 | return string(s) 147 | } 148 | 149 | // IsCloseError returns boolean indicating whether the error is a *CloseError 150 | // with one of the specified codes. 151 | func IsCloseError(err error, codes ...int) bool { 152 | if e, ok := err.(*CloseError); ok { 153 | for _, code := range codes { 154 | if e.Code == code { 155 | return true 156 | } 157 | } 158 | } 159 | return false 160 | } 161 | 162 | // IsUnexpectedCloseError returns boolean indicating whether the error is a 163 | // *CloseError with a code not in the list of expected codes. 164 | func IsUnexpectedCloseError(err error, expectedCodes ...int) bool { 165 | if e, ok := err.(*CloseError); ok { 166 | for _, code := range expectedCodes { 167 | if e.Code == code { 168 | return false 169 | } 170 | } 171 | return true 172 | } 173 | return false 174 | } 175 | 176 | var ( 177 | errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true, temporary: true} 178 | errUnexpectedEOF = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()} 179 | errBadWriteOpCode = errors.New("websocket: bad write message type") 180 | errWriteClosed = errors.New("websocket: write closed") 181 | errInvalidControlFrame = errors.New("websocket: invalid control frame") 182 | ) 183 | 184 | func newMaskKey() [4]byte { 185 | n := rand.Uint32() 186 | return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)} 187 | } 188 | 189 | func hideTempErr(err error) error { 190 | if e, ok := err.(net.Error); ok && e.Temporary() { 191 | err = &netError{msg: e.Error(), timeout: e.Timeout()} 192 | } 193 | return err 194 | } 195 | 196 | func isControl(frameType int) bool { 197 | return frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage 198 | } 199 | 200 | func isData(frameType int) bool { 201 | return frameType == TextMessage || frameType == BinaryMessage 202 | } 203 | 204 | var validReceivedCloseCodes = map[int]bool{ 205 | // see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number 206 | 207 | CloseNormalClosure: true, 208 | CloseGoingAway: true, 209 | CloseProtocolError: true, 210 | CloseUnsupportedData: true, 211 | CloseNoStatusReceived: false, 212 | CloseAbnormalClosure: false, 213 | CloseInvalidFramePayloadData: true, 214 | ClosePolicyViolation: true, 215 | CloseMessageTooBig: true, 216 | CloseMandatoryExtension: true, 217 | CloseInternalServerErr: true, 218 | CloseServiceRestart: true, 219 | CloseTryAgainLater: true, 220 | CloseTLSHandshake: false, 221 | } 222 | 223 | func isValidReceivedCloseCode(code int) bool { 224 | return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999) 225 | } 226 | 227 | // The Conn type represents a WebSocket connection. 228 | type Conn struct { 229 | conn net.Conn 230 | isServer bool 231 | subprotocol string 232 | 233 | // Write fields 234 | mu chan bool // used as mutex to protect write to conn 235 | writeBuf []byte // frame is constructed in this buffer. 236 | writeDeadline time.Time 237 | writer io.WriteCloser // the current writer returned to the application 238 | isWriting bool // for best-effort concurrent write detection 239 | 240 | writeErrMu sync.Mutex 241 | writeErr error 242 | 243 | enableWriteCompression bool 244 | compressionLevel int 245 | newCompressionWriter func(io.WriteCloser, int) io.WriteCloser 246 | 247 | // Read fields 248 | reader io.ReadCloser // the current reader returned to the application 249 | readErr error 250 | br *bufio.Reader 251 | readRemaining int64 // bytes remaining in current frame. 252 | readFinal bool // true the current message has more frames. 253 | readLength int64 // Message size. 254 | readLimit int64 // Maximum message size. 255 | readMaskPos int 256 | readMaskKey [4]byte 257 | handlePong func(string) error 258 | handlePing func(string) error 259 | handleClose func(int, string) error 260 | readErrCount int 261 | messageReader *messageReader // the current low-level reader 262 | 263 | readDecompress bool // whether last read frame had RSV1 set 264 | newDecompressionReader func(io.Reader) io.ReadCloser 265 | } 266 | 267 | func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn { 268 | return newConnBRW(conn, isServer, readBufferSize, writeBufferSize, nil) 269 | } 270 | 271 | type writeHook struct { 272 | p []byte 273 | } 274 | 275 | func (wh *writeHook) Write(p []byte) (int, error) { 276 | wh.p = p 277 | return len(p), nil 278 | } 279 | 280 | func newConnBRW(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, brw *bufio.ReadWriter) *Conn { 281 | mu := make(chan bool, 1) 282 | mu <- true 283 | 284 | var br *bufio.Reader 285 | if readBufferSize == 0 && brw != nil && brw.Reader != nil { 286 | // Reuse the supplied bufio.Reader if the buffer has a useful size. 287 | // This code assumes that peek on a reader returns 288 | // bufio.Reader.buf[:0]. 289 | brw.Reader.Reset(conn) 290 | if p, err := brw.Reader.Peek(0); err == nil && cap(p) >= 256 { 291 | br = brw.Reader 292 | } 293 | } 294 | if br == nil { 295 | if readBufferSize == 0 { 296 | readBufferSize = defaultReadBufferSize 297 | } 298 | if readBufferSize < maxControlFramePayloadSize { 299 | readBufferSize = maxControlFramePayloadSize 300 | } 301 | br = bufio.NewReaderSize(conn, readBufferSize) 302 | } 303 | 304 | var writeBuf []byte 305 | if writeBufferSize == 0 && brw != nil && brw.Writer != nil { 306 | // Use the bufio.Writer's buffer if the buffer has a useful size. This 307 | // code assumes that bufio.Writer.buf[:1] is passed to the 308 | // bufio.Writer's underlying writer. 309 | var wh writeHook 310 | brw.Writer.Reset(&wh) 311 | brw.Writer.WriteByte(0) 312 | brw.Flush() 313 | if cap(wh.p) >= maxFrameHeaderSize+256 { 314 | writeBuf = wh.p[:cap(wh.p)] 315 | } 316 | } 317 | 318 | if writeBuf == nil { 319 | if writeBufferSize == 0 { 320 | writeBufferSize = defaultWriteBufferSize 321 | } 322 | writeBuf = make([]byte, writeBufferSize+maxFrameHeaderSize) 323 | } 324 | 325 | c := &Conn{ 326 | isServer: isServer, 327 | br: br, 328 | conn: conn, 329 | mu: mu, 330 | readFinal: true, 331 | writeBuf: writeBuf, 332 | enableWriteCompression: true, 333 | compressionLevel: defaultCompressionLevel, 334 | } 335 | c.SetCloseHandler(nil) 336 | c.SetPingHandler(nil) 337 | c.SetPongHandler(nil) 338 | return c 339 | } 340 | 341 | // Subprotocol returns the negotiated protocol for the connection. 342 | func (c *Conn) Subprotocol() string { 343 | return c.subprotocol 344 | } 345 | 346 | // Close closes the underlying network connection without sending or waiting for a close frame. 347 | func (c *Conn) Close() error { 348 | return c.conn.Close() 349 | } 350 | 351 | // LocalAddr returns the local network address. 352 | func (c *Conn) LocalAddr() net.Addr { 353 | return c.conn.LocalAddr() 354 | } 355 | 356 | // RemoteAddr returns the remote network address. 357 | func (c *Conn) RemoteAddr() net.Addr { 358 | return c.conn.RemoteAddr() 359 | } 360 | 361 | // Write methods 362 | 363 | func (c *Conn) writeFatal(err error) error { 364 | err = hideTempErr(err) 365 | c.writeErrMu.Lock() 366 | if c.writeErr == nil { 367 | c.writeErr = err 368 | } 369 | c.writeErrMu.Unlock() 370 | return err 371 | } 372 | 373 | func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error { 374 | <-c.mu 375 | defer func() { c.mu <- true }() 376 | 377 | c.writeErrMu.Lock() 378 | err := c.writeErr 379 | c.writeErrMu.Unlock() 380 | if err != nil { 381 | return err 382 | } 383 | 384 | c.conn.SetWriteDeadline(deadline) 385 | for _, buf := range bufs { 386 | if len(buf) > 0 { 387 | _, err := c.conn.Write(buf) 388 | if err != nil { 389 | return c.writeFatal(err) 390 | } 391 | } 392 | } 393 | 394 | if frameType == CloseMessage { 395 | c.writeFatal(ErrCloseSent) 396 | } 397 | return nil 398 | } 399 | 400 | // WriteControl writes a control message with the given deadline. The allowed 401 | // message types are CloseMessage, PingMessage and PongMessage. 402 | func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error { 403 | if !isControl(messageType) { 404 | return errBadWriteOpCode 405 | } 406 | if len(data) > maxControlFramePayloadSize { 407 | return errInvalidControlFrame 408 | } 409 | 410 | b0 := byte(messageType) | finalBit 411 | b1 := byte(len(data)) 412 | if !c.isServer { 413 | b1 |= maskBit 414 | } 415 | 416 | buf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize) 417 | buf = append(buf, b0, b1) 418 | 419 | if c.isServer { 420 | buf = append(buf, data...) 421 | } else { 422 | key := newMaskKey() 423 | buf = append(buf, key[:]...) 424 | buf = append(buf, data...) 425 | maskBytes(key, 0, buf[6:]) 426 | } 427 | 428 | d := time.Hour * 1000 429 | if !deadline.IsZero() { 430 | d = deadline.Sub(time.Now()) 431 | if d < 0 { 432 | return errWriteTimeout 433 | } 434 | } 435 | 436 | timer := time.NewTimer(d) 437 | select { 438 | case <-c.mu: 439 | timer.Stop() 440 | case <-timer.C: 441 | return errWriteTimeout 442 | } 443 | defer func() { c.mu <- true }() 444 | 445 | c.writeErrMu.Lock() 446 | err := c.writeErr 447 | c.writeErrMu.Unlock() 448 | if err != nil { 449 | return err 450 | } 451 | 452 | c.conn.SetWriteDeadline(deadline) 453 | _, err = c.conn.Write(buf) 454 | if err != nil { 455 | return c.writeFatal(err) 456 | } 457 | if messageType == CloseMessage { 458 | c.writeFatal(ErrCloseSent) 459 | } 460 | return err 461 | } 462 | 463 | func (c *Conn) prepWrite(messageType int) error { 464 | // Close previous writer if not already closed by the application. It's 465 | // probably better to return an error in this situation, but we cannot 466 | // change this without breaking existing applications. 467 | if c.writer != nil { 468 | c.writer.Close() 469 | c.writer = nil 470 | } 471 | 472 | if !isControl(messageType) && !isData(messageType) { 473 | return errBadWriteOpCode 474 | } 475 | 476 | c.writeErrMu.Lock() 477 | err := c.writeErr 478 | c.writeErrMu.Unlock() 479 | return err 480 | } 481 | 482 | // NextWriter returns a writer for the next message to send. The writer's Close 483 | // method flushes the complete message to the network. 484 | // 485 | // There can be at most one open writer on a connection. NextWriter closes the 486 | // previous writer if the application has not already done so. 487 | func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { 488 | if err := c.prepWrite(messageType); err != nil { 489 | return nil, err 490 | } 491 | 492 | mw := &messageWriter{ 493 | c: c, 494 | frameType: messageType, 495 | pos: maxFrameHeaderSize, 496 | } 497 | c.writer = mw 498 | if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { 499 | w := c.newCompressionWriter(c.writer, c.compressionLevel) 500 | mw.compress = true 501 | c.writer = w 502 | } 503 | return c.writer, nil 504 | } 505 | 506 | type messageWriter struct { 507 | c *Conn 508 | compress bool // whether next call to flushFrame should set RSV1 509 | pos int // end of data in writeBuf. 510 | frameType int // type of the current frame. 511 | err error 512 | } 513 | 514 | func (w *messageWriter) fatal(err error) error { 515 | if w.err != nil { 516 | w.err = err 517 | w.c.writer = nil 518 | } 519 | return err 520 | } 521 | 522 | // flushFrame writes buffered data and extra as a frame to the network. The 523 | // final argument indicates that this is the last frame in the message. 524 | func (w *messageWriter) flushFrame(final bool, extra []byte) error { 525 | c := w.c 526 | length := w.pos - maxFrameHeaderSize + len(extra) 527 | 528 | // Check for invalid control frames. 529 | if isControl(w.frameType) && 530 | (!final || length > maxControlFramePayloadSize) { 531 | return w.fatal(errInvalidControlFrame) 532 | } 533 | 534 | b0 := byte(w.frameType) 535 | if final { 536 | b0 |= finalBit 537 | } 538 | if w.compress { 539 | b0 |= rsv1Bit 540 | } 541 | w.compress = false 542 | 543 | b1 := byte(0) 544 | if !c.isServer { 545 | b1 |= maskBit 546 | } 547 | 548 | // Assume that the frame starts at beginning of c.writeBuf. 549 | framePos := 0 550 | if c.isServer { 551 | // Adjust up if mask not included in the header. 552 | framePos = 4 553 | } 554 | 555 | switch { 556 | case length >= 65536: 557 | c.writeBuf[framePos] = b0 558 | c.writeBuf[framePos+1] = b1 | 127 559 | binary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length)) 560 | case length > 125: 561 | framePos += 6 562 | c.writeBuf[framePos] = b0 563 | c.writeBuf[framePos+1] = b1 | 126 564 | binary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length)) 565 | default: 566 | framePos += 8 567 | c.writeBuf[framePos] = b0 568 | c.writeBuf[framePos+1] = b1 | byte(length) 569 | } 570 | 571 | if !c.isServer { 572 | key := newMaskKey() 573 | copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) 574 | maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos]) 575 | if len(extra) > 0 { 576 | return c.writeFatal(errors.New("websocket: internal error, extra used in client mode")) 577 | } 578 | } 579 | 580 | // Write the buffers to the connection with best-effort detection of 581 | // concurrent writes. See the concurrency section in the package 582 | // documentation for more info. 583 | 584 | if c.isWriting { 585 | panic("concurrent write to websocket connection") 586 | } 587 | c.isWriting = true 588 | 589 | err := c.write(w.frameType, c.writeDeadline, c.writeBuf[framePos:w.pos], extra) 590 | 591 | if !c.isWriting { 592 | panic("concurrent write to websocket connection") 593 | } 594 | c.isWriting = false 595 | 596 | if err != nil { 597 | return w.fatal(err) 598 | } 599 | 600 | if final { 601 | c.writer = nil 602 | return nil 603 | } 604 | 605 | // Setup for next frame. 606 | w.pos = maxFrameHeaderSize 607 | w.frameType = continuationFrame 608 | return nil 609 | } 610 | 611 | func (w *messageWriter) ncopy(max int) (int, error) { 612 | n := len(w.c.writeBuf) - w.pos 613 | if n <= 0 { 614 | if err := w.flushFrame(false, nil); err != nil { 615 | return 0, err 616 | } 617 | n = len(w.c.writeBuf) - w.pos 618 | } 619 | if n > max { 620 | n = max 621 | } 622 | return n, nil 623 | } 624 | 625 | func (w *messageWriter) Write(p []byte) (int, error) { 626 | if w.err != nil { 627 | return 0, w.err 628 | } 629 | 630 | if len(p) > 2*len(w.c.writeBuf) && w.c.isServer { 631 | // Don't buffer large messages. 632 | err := w.flushFrame(false, p) 633 | if err != nil { 634 | return 0, err 635 | } 636 | return len(p), nil 637 | } 638 | 639 | nn := len(p) 640 | for len(p) > 0 { 641 | n, err := w.ncopy(len(p)) 642 | if err != nil { 643 | return 0, err 644 | } 645 | copy(w.c.writeBuf[w.pos:], p[:n]) 646 | w.pos += n 647 | p = p[n:] 648 | } 649 | return nn, nil 650 | } 651 | 652 | func (w *messageWriter) WriteString(p string) (int, error) { 653 | if w.err != nil { 654 | return 0, w.err 655 | } 656 | 657 | nn := len(p) 658 | for len(p) > 0 { 659 | n, err := w.ncopy(len(p)) 660 | if err != nil { 661 | return 0, err 662 | } 663 | copy(w.c.writeBuf[w.pos:], p[:n]) 664 | w.pos += n 665 | p = p[n:] 666 | } 667 | return nn, nil 668 | } 669 | 670 | func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) { 671 | if w.err != nil { 672 | return 0, w.err 673 | } 674 | for { 675 | if w.pos == len(w.c.writeBuf) { 676 | err = w.flushFrame(false, nil) 677 | if err != nil { 678 | break 679 | } 680 | } 681 | var n int 682 | n, err = r.Read(w.c.writeBuf[w.pos:]) 683 | w.pos += n 684 | nn += int64(n) 685 | if err != nil { 686 | if err == io.EOF { 687 | err = nil 688 | } 689 | break 690 | } 691 | } 692 | return nn, err 693 | } 694 | 695 | func (w *messageWriter) Close() error { 696 | if w.err != nil { 697 | return w.err 698 | } 699 | if err := w.flushFrame(true, nil); err != nil { 700 | return err 701 | } 702 | w.err = errWriteClosed 703 | return nil 704 | } 705 | 706 | // WritePreparedMessage writes prepared message into connection. 707 | func (c *Conn) WritePreparedMessage(pm *PreparedMessage) error { 708 | frameType, frameData, err := pm.frame(prepareKey{ 709 | isServer: c.isServer, 710 | compress: c.newCompressionWriter != nil && c.enableWriteCompression && isData(pm.messageType), 711 | compressionLevel: c.compressionLevel, 712 | }) 713 | if err != nil { 714 | return err 715 | } 716 | if c.isWriting { 717 | panic("concurrent write to websocket connection") 718 | } 719 | c.isWriting = true 720 | err = c.write(frameType, c.writeDeadline, frameData, nil) 721 | if !c.isWriting { 722 | panic("concurrent write to websocket connection") 723 | } 724 | c.isWriting = false 725 | return err 726 | } 727 | 728 | // WriteMessage is a helper method for getting a writer using NextWriter, 729 | // writing the message and closing the writer. 730 | func (c *Conn) WriteMessage(messageType int, data []byte) error { 731 | 732 | if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { 733 | // Fast path with no allocations and single frame. 734 | 735 | if err := c.prepWrite(messageType); err != nil { 736 | return err 737 | } 738 | mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize} 739 | n := copy(c.writeBuf[mw.pos:], data) 740 | mw.pos += n 741 | data = data[n:] 742 | return mw.flushFrame(true, data) 743 | } 744 | 745 | w, err := c.NextWriter(messageType) 746 | if err != nil { 747 | return err 748 | } 749 | if _, err = w.Write(data); err != nil { 750 | return err 751 | } 752 | return w.Close() 753 | } 754 | 755 | // SetWriteDeadline sets the write deadline on the underlying network 756 | // connection. After a write has timed out, the websocket state is corrupt and 757 | // all future writes will return an error. A zero value for t means writes will 758 | // not time out. 759 | func (c *Conn) SetWriteDeadline(t time.Time) error { 760 | c.writeDeadline = t 761 | return nil 762 | } 763 | 764 | // Read methods 765 | 766 | func (c *Conn) advanceFrame() (int, error) { 767 | 768 | // 1. Skip remainder of previous frame. 769 | 770 | if c.readRemaining > 0 { 771 | if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil { 772 | return noFrame, err 773 | } 774 | } 775 | 776 | // 2. Read and parse first two bytes of frame header. 777 | 778 | p, err := c.read(2) 779 | if err != nil { 780 | return noFrame, err 781 | } 782 | 783 | final := p[0]&finalBit != 0 784 | frameType := int(p[0] & 0xf) 785 | mask := p[1]&maskBit != 0 786 | c.readRemaining = int64(p[1] & 0x7f) 787 | 788 | c.readDecompress = false 789 | if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 { 790 | c.readDecompress = true 791 | p[0] &^= rsv1Bit 792 | } 793 | 794 | if rsv := p[0] & (rsv1Bit | rsv2Bit | rsv3Bit); rsv != 0 { 795 | return noFrame, c.handleProtocolError("unexpected reserved bits 0x" + strconv.FormatInt(int64(rsv), 16)) 796 | } 797 | 798 | switch frameType { 799 | case CloseMessage, PingMessage, PongMessage: 800 | if c.readRemaining > maxControlFramePayloadSize { 801 | return noFrame, c.handleProtocolError("control frame length > 125") 802 | } 803 | if !final { 804 | return noFrame, c.handleProtocolError("control frame not final") 805 | } 806 | case TextMessage, BinaryMessage: 807 | if !c.readFinal { 808 | return noFrame, c.handleProtocolError("message start before final message frame") 809 | } 810 | c.readFinal = final 811 | case continuationFrame: 812 | if c.readFinal { 813 | return noFrame, c.handleProtocolError("continuation after final message frame") 814 | } 815 | c.readFinal = final 816 | default: 817 | return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType)) 818 | } 819 | 820 | // 3. Read and parse frame length. 821 | 822 | switch c.readRemaining { 823 | case 126: 824 | p, err := c.read(2) 825 | if err != nil { 826 | return noFrame, err 827 | } 828 | c.readRemaining = int64(binary.BigEndian.Uint16(p)) 829 | case 127: 830 | p, err := c.read(8) 831 | if err != nil { 832 | return noFrame, err 833 | } 834 | c.readRemaining = int64(binary.BigEndian.Uint64(p)) 835 | } 836 | 837 | // 4. Handle frame masking. 838 | 839 | if mask != c.isServer { 840 | return noFrame, c.handleProtocolError("incorrect mask flag") 841 | } 842 | 843 | if mask { 844 | c.readMaskPos = 0 845 | p, err := c.read(len(c.readMaskKey)) 846 | if err != nil { 847 | return noFrame, err 848 | } 849 | copy(c.readMaskKey[:], p) 850 | } 851 | 852 | // 5. For text and binary messages, enforce read limit and return. 853 | 854 | if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage { 855 | 856 | c.readLength += c.readRemaining 857 | if c.readLimit > 0 && c.readLength > c.readLimit { 858 | c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)) 859 | return noFrame, ErrReadLimit 860 | } 861 | 862 | return frameType, nil 863 | } 864 | 865 | // 6. Read control frame payload. 866 | 867 | var payload []byte 868 | if c.readRemaining > 0 { 869 | payload, err = c.read(int(c.readRemaining)) 870 | c.readRemaining = 0 871 | if err != nil { 872 | return noFrame, err 873 | } 874 | if c.isServer { 875 | maskBytes(c.readMaskKey, 0, payload) 876 | } 877 | } 878 | 879 | // 7. Process control frame payload. 880 | 881 | switch frameType { 882 | case PongMessage: 883 | if err := c.handlePong(string(payload)); err != nil { 884 | return noFrame, err 885 | } 886 | case PingMessage: 887 | if err := c.handlePing(string(payload)); err != nil { 888 | return noFrame, err 889 | } 890 | case CloseMessage: 891 | closeCode := CloseNoStatusReceived 892 | closeText := "" 893 | if len(payload) >= 2 { 894 | closeCode = int(binary.BigEndian.Uint16(payload)) 895 | if !isValidReceivedCloseCode(closeCode) { 896 | return noFrame, c.handleProtocolError("invalid close code") 897 | } 898 | closeText = string(payload[2:]) 899 | if !utf8.ValidString(closeText) { 900 | return noFrame, c.handleProtocolError("invalid utf8 payload in close frame") 901 | } 902 | } 903 | if err := c.handleClose(closeCode, closeText); err != nil { 904 | return noFrame, err 905 | } 906 | return noFrame, &CloseError{Code: closeCode, Text: closeText} 907 | } 908 | 909 | return frameType, nil 910 | } 911 | 912 | func (c *Conn) handleProtocolError(message string) error { 913 | c.WriteControl(CloseMessage, FormatCloseMessage(CloseProtocolError, message), time.Now().Add(writeWait)) 914 | return errors.New("websocket: " + message) 915 | } 916 | 917 | // NextReader returns the next data message received from the peer. The 918 | // returned messageType is either TextMessage or BinaryMessage. 919 | // 920 | // There can be at most one open reader on a connection. NextReader discards 921 | // the previous message if the application has not already consumed it. 922 | // 923 | // Applications must break out of the application's read loop when this method 924 | // returns a non-nil error value. Errors returned from this method are 925 | // permanent. Once this method returns a non-nil error, all subsequent calls to 926 | // this method return the same error. 927 | func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { 928 | // Close previous reader, only relevant for decompression. 929 | if c.reader != nil { 930 | c.reader.Close() 931 | c.reader = nil 932 | } 933 | 934 | c.messageReader = nil 935 | c.readLength = 0 936 | 937 | for c.readErr == nil { 938 | frameType, err := c.advanceFrame() 939 | if err != nil { 940 | c.readErr = hideTempErr(err) 941 | break 942 | } 943 | if frameType == TextMessage || frameType == BinaryMessage { 944 | c.messageReader = &messageReader{c} 945 | c.reader = c.messageReader 946 | if c.readDecompress { 947 | c.reader = c.newDecompressionReader(c.reader) 948 | } 949 | return frameType, c.reader, nil 950 | } 951 | } 952 | 953 | // Applications that do handle the error returned from this method spin in 954 | // tight loop on connection failure. To help application developers detect 955 | // this error, panic on repeated reads to the failed connection. 956 | c.readErrCount++ 957 | if c.readErrCount >= 1000 { 958 | panic("repeated read on failed websocket connection") 959 | } 960 | 961 | return noFrame, nil, c.readErr 962 | } 963 | 964 | type messageReader struct{ c *Conn } 965 | 966 | func (r *messageReader) Read(b []byte) (int, error) { 967 | c := r.c 968 | if c.messageReader != r { 969 | return 0, io.EOF 970 | } 971 | 972 | for c.readErr == nil { 973 | 974 | if c.readRemaining > 0 { 975 | if int64(len(b)) > c.readRemaining { 976 | b = b[:c.readRemaining] 977 | } 978 | n, err := c.br.Read(b) 979 | c.readErr = hideTempErr(err) 980 | if c.isServer { 981 | c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n]) 982 | } 983 | c.readRemaining -= int64(n) 984 | if c.readRemaining > 0 && c.readErr == io.EOF { 985 | c.readErr = errUnexpectedEOF 986 | } 987 | return n, c.readErr 988 | } 989 | 990 | if c.readFinal { 991 | c.messageReader = nil 992 | return 0, io.EOF 993 | } 994 | 995 | frameType, err := c.advanceFrame() 996 | switch { 997 | case err != nil: 998 | c.readErr = hideTempErr(err) 999 | case frameType == TextMessage || frameType == BinaryMessage: 1000 | c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader") 1001 | } 1002 | } 1003 | 1004 | err := c.readErr 1005 | if err == io.EOF && c.messageReader == r { 1006 | err = errUnexpectedEOF 1007 | } 1008 | return 0, err 1009 | } 1010 | 1011 | func (r *messageReader) Close() error { 1012 | return nil 1013 | } 1014 | 1015 | // ReadMessage is a helper method for getting a reader using NextReader and 1016 | // reading from that reader to a buffer. 1017 | func (c *Conn) ReadMessage() (messageType int, p []byte, err error) { 1018 | var r io.Reader 1019 | messageType, r, err = c.NextReader() 1020 | if err != nil { 1021 | return messageType, nil, err 1022 | } 1023 | p, err = ioutil.ReadAll(r) 1024 | return messageType, p, err 1025 | } 1026 | 1027 | // SetReadDeadline sets the read deadline on the underlying network connection. 1028 | // After a read has timed out, the websocket connection state is corrupt and 1029 | // all future reads will return an error. A zero value for t means reads will 1030 | // not time out. 1031 | func (c *Conn) SetReadDeadline(t time.Time) error { 1032 | return c.conn.SetReadDeadline(t) 1033 | } 1034 | 1035 | // SetReadLimit sets the maximum size for a message read from the peer. If a 1036 | // message exceeds the limit, the connection sends a close frame to the peer 1037 | // and returns ErrReadLimit to the application. 1038 | func (c *Conn) SetReadLimit(limit int64) { 1039 | c.readLimit = limit 1040 | } 1041 | 1042 | // CloseHandler returns the current close handler 1043 | func (c *Conn) CloseHandler() func(code int, text string) error { 1044 | return c.handleClose 1045 | } 1046 | 1047 | // SetCloseHandler sets the handler for close messages received from the peer. 1048 | // The code argument to h is the received close code or CloseNoStatusReceived 1049 | // if the close message is empty. The default close handler sends a close frame 1050 | // back to the peer. 1051 | // 1052 | // The application must read the connection to process close messages as 1053 | // described in the section on Control Frames above. 1054 | // 1055 | // The connection read methods return a CloseError when a close frame is 1056 | // received. Most applications should handle close messages as part of their 1057 | // normal error handling. Applications should only set a close handler when the 1058 | // application must perform some action before sending a close frame back to 1059 | // the peer. 1060 | func (c *Conn) SetCloseHandler(h func(code int, text string) error) { 1061 | if h == nil { 1062 | h = func(code int, text string) error { 1063 | message := []byte{} 1064 | if code != CloseNoStatusReceived { 1065 | message = FormatCloseMessage(code, "") 1066 | } 1067 | c.WriteControl(CloseMessage, message, time.Now().Add(writeWait)) 1068 | return nil 1069 | } 1070 | } 1071 | c.handleClose = h 1072 | } 1073 | 1074 | // PingHandler returns the current ping handler 1075 | func (c *Conn) PingHandler() func(appData string) error { 1076 | return c.handlePing 1077 | } 1078 | 1079 | // SetPingHandler sets the handler for ping messages received from the peer. 1080 | // The appData argument to h is the PING frame application data. The default 1081 | // ping handler sends a pong to the peer. 1082 | // 1083 | // The application must read the connection to process ping messages as 1084 | // described in the section on Control Frames above. 1085 | func (c *Conn) SetPingHandler(h func(appData string) error) { 1086 | if h == nil { 1087 | h = func(message string) error { 1088 | err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait)) 1089 | if err == ErrCloseSent { 1090 | return nil 1091 | } else if e, ok := err.(net.Error); ok && e.Temporary() { 1092 | return nil 1093 | } 1094 | return err 1095 | } 1096 | } 1097 | c.handlePing = h 1098 | } 1099 | 1100 | // PongHandler returns the current pong handler 1101 | func (c *Conn) PongHandler() func(appData string) error { 1102 | return c.handlePong 1103 | } 1104 | 1105 | // SetPongHandler sets the handler for pong messages received from the peer. 1106 | // The appData argument to h is the PONG frame application data. The default 1107 | // pong handler does nothing. 1108 | // 1109 | // The application must read the connection to process ping messages as 1110 | // described in the section on Control Frames above. 1111 | func (c *Conn) SetPongHandler(h func(appData string) error) { 1112 | if h == nil { 1113 | h = func(string) error { return nil } 1114 | } 1115 | c.handlePong = h 1116 | } 1117 | 1118 | // UnderlyingConn returns the internal net.Conn. This can be used to further 1119 | // modifications to connection specific flags. 1120 | func (c *Conn) UnderlyingConn() net.Conn { 1121 | return c.conn 1122 | } 1123 | 1124 | // EnableWriteCompression enables and disables write compression of 1125 | // subsequent text and binary messages. This function is a noop if 1126 | // compression was not negotiated with the peer. 1127 | func (c *Conn) EnableWriteCompression(enable bool) { 1128 | c.enableWriteCompression = enable 1129 | } 1130 | 1131 | // SetCompressionLevel sets the flate compression level for subsequent text and 1132 | // binary messages. This function is a noop if compression was not negotiated 1133 | // with the peer. See the compress/flate package for a description of 1134 | // compression levels. 1135 | func (c *Conn) SetCompressionLevel(level int) error { 1136 | if !isValidCompressionLevel(level) { 1137 | return errors.New("websocket: invalid compression level") 1138 | } 1139 | c.compressionLevel = level 1140 | return nil 1141 | } 1142 | 1143 | // FormatCloseMessage formats closeCode and text as a WebSocket close message. 1144 | func FormatCloseMessage(closeCode int, text string) []byte { 1145 | buf := make([]byte, 2+len(text)) 1146 | binary.BigEndian.PutUint16(buf, uint16(closeCode)) 1147 | copy(buf[2:], text) 1148 | return buf 1149 | } 1150 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/conn_read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket 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 | // +build go1.5 6 | 7 | package websocket 8 | 9 | import "io" 10 | 11 | func (c *Conn) read(n int) ([]byte, error) { 12 | p, err := c.br.Peek(n) 13 | if err == io.EOF { 14 | err = errUnexpectedEOF 15 | } 16 | c.br.Discard(len(p)) 17 | return p, err 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/conn_read_legacy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket 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 | // +build !go1.5 6 | 7 | package websocket 8 | 9 | import "io" 10 | 11 | func (c *Conn) read(n int) ([]byte, error) { 12 | p, err := c.br.Peek(n) 13 | if err == io.EOF { 14 | err = errUnexpectedEOF 15 | } 16 | if len(p) > 0 { 17 | // advance over the bytes just read 18 | io.ReadFull(c.br, p) 19 | } 20 | return p, err 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket 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 implements the WebSocket protocol defined in RFC 6455. 6 | // 7 | // Overview 8 | // 9 | // The Conn type represents a WebSocket connection. A server application calls 10 | // the Upgrader.Upgrade method from an HTTP request handler to get a *Conn: 11 | // 12 | // var upgrader = websocket.Upgrader{ 13 | // ReadBufferSize: 1024, 14 | // WriteBufferSize: 1024, 15 | // } 16 | // 17 | // func handler(w http.ResponseWriter, r *http.Request) { 18 | // conn, err := upgrader.Upgrade(w, r, nil) 19 | // if err != nil { 20 | // log.Println(err) 21 | // return 22 | // } 23 | // ... Use conn to send and receive messages. 24 | // } 25 | // 26 | // Call the connection's WriteMessage and ReadMessage methods to send and 27 | // receive messages as a slice of bytes. This snippet of code shows how to echo 28 | // messages using these methods: 29 | // 30 | // for { 31 | // messageType, p, err := conn.ReadMessage() 32 | // if err != nil { 33 | // return 34 | // } 35 | // if err := conn.WriteMessage(messageType, p); err != nil { 36 | // return err 37 | // } 38 | // } 39 | // 40 | // In above snippet of code, p is a []byte and messageType is an int with value 41 | // websocket.BinaryMessage or websocket.TextMessage. 42 | // 43 | // An application can also send and receive messages using the io.WriteCloser 44 | // and io.Reader interfaces. To send a message, call the connection NextWriter 45 | // method to get an io.WriteCloser, write the message to the writer and close 46 | // the writer when done. To receive a message, call the connection NextReader 47 | // method to get an io.Reader and read until io.EOF is returned. This snippet 48 | // shows how to echo messages using the NextWriter and NextReader methods: 49 | // 50 | // for { 51 | // messageType, r, err := conn.NextReader() 52 | // if err != nil { 53 | // return 54 | // } 55 | // w, err := conn.NextWriter(messageType) 56 | // if err != nil { 57 | // return err 58 | // } 59 | // if _, err := io.Copy(w, r); err != nil { 60 | // return err 61 | // } 62 | // if err := w.Close(); err != nil { 63 | // return err 64 | // } 65 | // } 66 | // 67 | // Data Messages 68 | // 69 | // The WebSocket protocol distinguishes between text and binary data messages. 70 | // Text messages are interpreted as UTF-8 encoded text. The interpretation of 71 | // binary messages is left to the application. 72 | // 73 | // This package uses the TextMessage and BinaryMessage integer constants to 74 | // identify the two data message types. The ReadMessage and NextReader methods 75 | // return the type of the received message. The messageType argument to the 76 | // WriteMessage and NextWriter methods specifies the type of a sent message. 77 | // 78 | // It is the application's responsibility to ensure that text messages are 79 | // valid UTF-8 encoded text. 80 | // 81 | // Control Messages 82 | // 83 | // The WebSocket protocol defines three types of control messages: close, ping 84 | // and pong. Call the connection WriteControl, WriteMessage or NextWriter 85 | // methods to send a control message to the peer. 86 | // 87 | // Connections handle received close messages by sending a close message to the 88 | // peer and returning a *CloseError from the the NextReader, ReadMessage or the 89 | // message Read method. 90 | // 91 | // Connections handle received ping and pong messages by invoking callback 92 | // functions set with SetPingHandler and SetPongHandler methods. The callback 93 | // functions are called from the NextReader, ReadMessage and the message Read 94 | // methods. 95 | // 96 | // The default ping handler sends a pong to the peer. The application's reading 97 | // goroutine can block for a short time while the handler writes the pong data 98 | // to the connection. 99 | // 100 | // The application must read the connection to process ping, pong and close 101 | // messages sent from the peer. If the application is not otherwise interested 102 | // in messages from the peer, then the application should start a goroutine to 103 | // read and discard messages from the peer. A simple example is: 104 | // 105 | // func readLoop(c *websocket.Conn) { 106 | // for { 107 | // if _, _, err := c.NextReader(); err != nil { 108 | // c.Close() 109 | // break 110 | // } 111 | // } 112 | // } 113 | // 114 | // Concurrency 115 | // 116 | // Connections support one concurrent reader and one concurrent writer. 117 | // 118 | // Applications are responsible for ensuring that no more than one goroutine 119 | // calls the write methods (NextWriter, SetWriteDeadline, WriteMessage, 120 | // WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and 121 | // that no more than one goroutine calls the read methods (NextReader, 122 | // SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler) 123 | // concurrently. 124 | // 125 | // The Close and WriteControl methods can be called concurrently with all other 126 | // methods. 127 | // 128 | // Origin Considerations 129 | // 130 | // Web browsers allow Javascript applications to open a WebSocket connection to 131 | // any host. It's up to the server to enforce an origin policy using the Origin 132 | // request header sent by the browser. 133 | // 134 | // The Upgrader calls the function specified in the CheckOrigin field to check 135 | // the origin. If the CheckOrigin function returns false, then the Upgrade 136 | // method fails the WebSocket handshake with HTTP status 403. 137 | // 138 | // If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail 139 | // the handshake if the Origin request header is present and not equal to the 140 | // Host request header. 141 | // 142 | // An application can allow connections from any origin by specifying a 143 | // function that always returns true: 144 | // 145 | // var upgrader = websocket.Upgrader{ 146 | // CheckOrigin: func(r *http.Request) bool { return true }, 147 | // } 148 | // 149 | // The deprecated package-level Upgrade function does not perform origin 150 | // checking. The application is responsible for checking the Origin header 151 | // before calling the Upgrade function. 152 | // 153 | // Compression EXPERIMENTAL 154 | // 155 | // Per message compression extensions (RFC 7692) are experimentally supported 156 | // by this package in a limited capacity. Setting the EnableCompression option 157 | // to true in Dialer or Upgrader will attempt to negotiate per message deflate 158 | // support. 159 | // 160 | // var upgrader = websocket.Upgrader{ 161 | // EnableCompression: true, 162 | // } 163 | // 164 | // If compression was successfully negotiated with the connection's peer, any 165 | // message received in compressed form will be automatically decompressed. 166 | // All Read methods will return uncompressed bytes. 167 | // 168 | // Per message compression of messages written to a connection can be enabled 169 | // or disabled by calling the corresponding Conn method: 170 | // 171 | // conn.EnableWriteCompression(false) 172 | // 173 | // Currently this package does not support compression with "context takeover". 174 | // This means that messages must be compressed and decompressed in isolation, 175 | // without retaining sliding window or dictionary state across messages. For 176 | // more details refer to RFC 7692. 177 | // 178 | // Use of compression is experimental and may result in decreased performance. 179 | package websocket 180 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket 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 | "encoding/json" 9 | "io" 10 | ) 11 | 12 | // WriteJSON writes the JSON encoding of v as a message. 13 | // 14 | // Deprecated: Use c.WriteJSON instead. 15 | func WriteJSON(c *Conn, v interface{}) error { 16 | return c.WriteJSON(v) 17 | } 18 | 19 | // WriteJSON writes the JSON encoding of v as a message. 20 | // 21 | // See the documentation for encoding/json Marshal for details about the 22 | // conversion of Go values to JSON. 23 | func (c *Conn) WriteJSON(v interface{}) error { 24 | w, err := c.NextWriter(TextMessage) 25 | if err != nil { 26 | return err 27 | } 28 | err1 := json.NewEncoder(w).Encode(v) 29 | err2 := w.Close() 30 | if err1 != nil { 31 | return err1 32 | } 33 | return err2 34 | } 35 | 36 | // ReadJSON reads the next JSON-encoded message from the connection and stores 37 | // it in the value pointed to by v. 38 | // 39 | // Deprecated: Use c.ReadJSON instead. 40 | func ReadJSON(c *Conn, v interface{}) error { 41 | return c.ReadJSON(v) 42 | } 43 | 44 | // ReadJSON reads the next JSON-encoded message from the connection and stores 45 | // it in the value pointed to by v. 46 | // 47 | // See the documentation for the encoding/json Unmarshal function for details 48 | // about the conversion of JSON to a Go value. 49 | func (c *Conn) ReadJSON(v interface{}) error { 50 | _, r, err := c.NextReader() 51 | if err != nil { 52 | return err 53 | } 54 | err = json.NewDecoder(r).Decode(v) 55 | if err == io.EOF { 56 | // One value is expected in the message. 57 | err = io.ErrUnexpectedEOF 58 | } 59 | return err 60 | } 61 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/mask.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of 2 | // this source code is governed by a BSD-style license that can be found in the 3 | // LICENSE file. 4 | 5 | // +build !appengine 6 | 7 | package websocket 8 | 9 | import "unsafe" 10 | 11 | const wordSize = int(unsafe.Sizeof(uintptr(0))) 12 | 13 | func maskBytes(key [4]byte, pos int, b []byte) int { 14 | 15 | // Mask one byte at a time for small buffers. 16 | if len(b) < 2*wordSize { 17 | for i := range b { 18 | b[i] ^= key[pos&3] 19 | pos++ 20 | } 21 | return pos & 3 22 | } 23 | 24 | // Mask one byte at a time to word boundary. 25 | if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 { 26 | n = wordSize - n 27 | for i := range b[:n] { 28 | b[i] ^= key[pos&3] 29 | pos++ 30 | } 31 | b = b[n:] 32 | } 33 | 34 | // Create aligned word size key. 35 | var k [wordSize]byte 36 | for i := range k { 37 | k[i] = key[(pos+i)&3] 38 | } 39 | kw := *(*uintptr)(unsafe.Pointer(&k)) 40 | 41 | // Mask one word at a time. 42 | n := (len(b) / wordSize) * wordSize 43 | for i := 0; i < n; i += wordSize { 44 | *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw 45 | } 46 | 47 | // Mask one byte at a time for remaining bytes. 48 | b = b[n:] 49 | for i := range b { 50 | b[i] ^= key[pos&3] 51 | pos++ 52 | } 53 | 54 | return pos & 3 55 | } 56 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/mask_safe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of 2 | // this source code is governed by a BSD-style license that can be found in the 3 | // LICENSE file. 4 | 5 | // +build appengine 6 | 7 | package websocket 8 | 9 | func maskBytes(key [4]byte, pos int, b []byte) int { 10 | for i := range b { 11 | b[i] ^= key[pos&3] 12 | pos++ 13 | } 14 | return pos & 3 15 | } 16 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/prepared.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Gorilla WebSocket 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 | "bytes" 9 | "net" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | // PreparedMessage caches on the wire representations of a message payload. 15 | // Use PreparedMessage to efficiently send a message payload to multiple 16 | // connections. PreparedMessage is especially useful when compression is used 17 | // because the CPU and memory expensive compression operation can be executed 18 | // once for a given set of compression options. 19 | type PreparedMessage struct { 20 | messageType int 21 | data []byte 22 | err error 23 | mu sync.Mutex 24 | frames map[prepareKey]*preparedFrame 25 | } 26 | 27 | // prepareKey defines a unique set of options to cache prepared frames in PreparedMessage. 28 | type prepareKey struct { 29 | isServer bool 30 | compress bool 31 | compressionLevel int 32 | } 33 | 34 | // preparedFrame contains data in wire representation. 35 | type preparedFrame struct { 36 | once sync.Once 37 | data []byte 38 | } 39 | 40 | // NewPreparedMessage returns an initialized PreparedMessage. You can then send 41 | // it to connection using WritePreparedMessage method. Valid wire 42 | // representation will be calculated lazily only once for a set of current 43 | // connection options. 44 | func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) { 45 | pm := &PreparedMessage{ 46 | messageType: messageType, 47 | frames: make(map[prepareKey]*preparedFrame), 48 | data: data, 49 | } 50 | 51 | // Prepare a plain server frame. 52 | _, frameData, err := pm.frame(prepareKey{isServer: true, compress: false}) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | // To protect against caller modifying the data argument, remember the data 58 | // copied to the plain server frame. 59 | pm.data = frameData[len(frameData)-len(data):] 60 | return pm, nil 61 | } 62 | 63 | func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { 64 | pm.mu.Lock() 65 | frame, ok := pm.frames[key] 66 | if !ok { 67 | frame = &preparedFrame{} 68 | pm.frames[key] = frame 69 | } 70 | pm.mu.Unlock() 71 | 72 | var err error 73 | frame.once.Do(func() { 74 | // Prepare a frame using a 'fake' connection. 75 | // TODO: Refactor code in conn.go to allow more direct construction of 76 | // the frame. 77 | mu := make(chan bool, 1) 78 | mu <- true 79 | var nc prepareConn 80 | c := &Conn{ 81 | conn: &nc, 82 | mu: mu, 83 | isServer: key.isServer, 84 | compressionLevel: key.compressionLevel, 85 | enableWriteCompression: true, 86 | writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize), 87 | } 88 | if key.compress { 89 | c.newCompressionWriter = compressNoContextTakeover 90 | } 91 | err = c.WriteMessage(pm.messageType, pm.data) 92 | frame.data = nc.buf.Bytes() 93 | }) 94 | return pm.messageType, frame.data, err 95 | } 96 | 97 | type prepareConn struct { 98 | buf bytes.Buffer 99 | net.Conn 100 | } 101 | 102 | func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) } 103 | func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil } 104 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket 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 | "bufio" 9 | "errors" 10 | "net" 11 | "net/http" 12 | "net/url" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | // HandshakeError describes an error with the handshake from the peer. 18 | type HandshakeError struct { 19 | message string 20 | } 21 | 22 | func (e HandshakeError) Error() string { return e.message } 23 | 24 | // Upgrader specifies parameters for upgrading an HTTP connection to a 25 | // WebSocket connection. 26 | type Upgrader struct { 27 | // HandshakeTimeout specifies the duration for the handshake to complete. 28 | HandshakeTimeout time.Duration 29 | 30 | // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer 31 | // size is zero, then buffers allocated by the HTTP server are used. The 32 | // I/O buffer sizes do not limit the size of the messages that can be sent 33 | // or received. 34 | ReadBufferSize, WriteBufferSize int 35 | 36 | // Subprotocols specifies the server's supported protocols in order of 37 | // preference. If this field is set, then the Upgrade method negotiates a 38 | // subprotocol by selecting the first match in this list with a protocol 39 | // requested by the client. 40 | Subprotocols []string 41 | 42 | // Error specifies the function for generating HTTP error responses. If Error 43 | // is nil, then http.Error is used to generate the HTTP response. 44 | Error func(w http.ResponseWriter, r *http.Request, status int, reason error) 45 | 46 | // CheckOrigin returns true if the request Origin header is acceptable. If 47 | // CheckOrigin is nil, the host in the Origin header must not be set or 48 | // must match the host of the request. 49 | CheckOrigin func(r *http.Request) bool 50 | 51 | // EnableCompression specify if the server should attempt to negotiate per 52 | // message compression (RFC 7692). Setting this value to true does not 53 | // guarantee that compression will be supported. Currently only "no context 54 | // takeover" modes are supported. 55 | EnableCompression bool 56 | } 57 | 58 | func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) { 59 | err := HandshakeError{reason} 60 | if u.Error != nil { 61 | u.Error(w, r, status, err) 62 | } else { 63 | w.Header().Set("Sec-Websocket-Version", "13") 64 | http.Error(w, http.StatusText(status), status) 65 | } 66 | return nil, err 67 | } 68 | 69 | // checkSameOrigin returns true if the origin is not set or is equal to the request host. 70 | func checkSameOrigin(r *http.Request) bool { 71 | origin := r.Header["Origin"] 72 | if len(origin) == 0 { 73 | return true 74 | } 75 | u, err := url.Parse(origin[0]) 76 | if err != nil { 77 | return false 78 | } 79 | return u.Host == r.Host 80 | } 81 | 82 | func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string { 83 | if u.Subprotocols != nil { 84 | clientProtocols := Subprotocols(r) 85 | for _, serverProtocol := range u.Subprotocols { 86 | for _, clientProtocol := range clientProtocols { 87 | if clientProtocol == serverProtocol { 88 | return clientProtocol 89 | } 90 | } 91 | } 92 | } else if responseHeader != nil { 93 | return responseHeader.Get("Sec-Websocket-Protocol") 94 | } 95 | return "" 96 | } 97 | 98 | // Upgrade upgrades the HTTP server connection to the WebSocket protocol. 99 | // 100 | // The responseHeader is included in the response to the client's upgrade 101 | // request. Use the responseHeader to specify cookies (Set-Cookie) and the 102 | // application negotiated subprotocol (Sec-Websocket-Protocol). 103 | // 104 | // If the upgrade fails, then Upgrade replies to the client with an HTTP error 105 | // response. 106 | func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { 107 | if r.Method != "GET" { 108 | return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: not a websocket handshake: request method is not GET") 109 | } 110 | 111 | if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok { 112 | return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-Websocket-Extensions' headers are unsupported") 113 | } 114 | 115 | if !tokenListContainsValue(r.Header, "Connection", "upgrade") { 116 | return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'upgrade' token not found in 'Connection' header") 117 | } 118 | 119 | if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { 120 | return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'websocket' token not found in 'Upgrade' header") 121 | } 122 | 123 | if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") { 124 | return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header") 125 | } 126 | 127 | checkOrigin := u.CheckOrigin 128 | if checkOrigin == nil { 129 | checkOrigin = checkSameOrigin 130 | } 131 | if !checkOrigin(r) { 132 | return u.returnError(w, r, http.StatusForbidden, "websocket: 'Origin' header value not allowed") 133 | } 134 | 135 | challengeKey := r.Header.Get("Sec-Websocket-Key") 136 | if challengeKey == "" { 137 | return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-Websocket-Key' header is missing or blank") 138 | } 139 | 140 | subprotocol := u.selectSubprotocol(r, responseHeader) 141 | 142 | // Negotiate PMCE 143 | var compress bool 144 | if u.EnableCompression { 145 | for _, ext := range parseExtensions(r.Header) { 146 | if ext[""] != "permessage-deflate" { 147 | continue 148 | } 149 | compress = true 150 | break 151 | } 152 | } 153 | 154 | var ( 155 | netConn net.Conn 156 | err error 157 | ) 158 | 159 | h, ok := w.(http.Hijacker) 160 | if !ok { 161 | return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") 162 | } 163 | var brw *bufio.ReadWriter 164 | netConn, brw, err = h.Hijack() 165 | if err != nil { 166 | return u.returnError(w, r, http.StatusInternalServerError, err.Error()) 167 | } 168 | 169 | if brw.Reader.Buffered() > 0 { 170 | netConn.Close() 171 | return nil, errors.New("websocket: client sent data before handshake is complete") 172 | } 173 | 174 | c := newConnBRW(netConn, true, u.ReadBufferSize, u.WriteBufferSize, brw) 175 | c.subprotocol = subprotocol 176 | 177 | if compress { 178 | c.newCompressionWriter = compressNoContextTakeover 179 | c.newDecompressionReader = decompressNoContextTakeover 180 | } 181 | 182 | p := c.writeBuf[:0] 183 | p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...) 184 | p = append(p, computeAcceptKey(challengeKey)...) 185 | p = append(p, "\r\n"...) 186 | if c.subprotocol != "" { 187 | p = append(p, "Sec-Websocket-Protocol: "...) 188 | p = append(p, c.subprotocol...) 189 | p = append(p, "\r\n"...) 190 | } 191 | if compress { 192 | p = append(p, "Sec-Websocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...) 193 | } 194 | for k, vs := range responseHeader { 195 | if k == "Sec-Websocket-Protocol" { 196 | continue 197 | } 198 | for _, v := range vs { 199 | p = append(p, k...) 200 | p = append(p, ": "...) 201 | for i := 0; i < len(v); i++ { 202 | b := v[i] 203 | if b <= 31 { 204 | // prevent response splitting. 205 | b = ' ' 206 | } 207 | p = append(p, b) 208 | } 209 | p = append(p, "\r\n"...) 210 | } 211 | } 212 | p = append(p, "\r\n"...) 213 | 214 | // Clear deadlines set by HTTP server. 215 | netConn.SetDeadline(time.Time{}) 216 | 217 | if u.HandshakeTimeout > 0 { 218 | netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)) 219 | } 220 | if _, err = netConn.Write(p); err != nil { 221 | netConn.Close() 222 | return nil, err 223 | } 224 | if u.HandshakeTimeout > 0 { 225 | netConn.SetWriteDeadline(time.Time{}) 226 | } 227 | 228 | return c, nil 229 | } 230 | 231 | // Upgrade upgrades the HTTP server connection to the WebSocket protocol. 232 | // 233 | // Deprecated: Use websocket.Upgrader instead. 234 | // 235 | // Upgrade does not perform origin checking. The application is responsible for 236 | // checking the Origin header before calling Upgrade. An example implementation 237 | // of the same origin policy check is: 238 | // 239 | // if req.Header.Get("Origin") != "http://"+req.Host { 240 | // http.Error(w, "Origin not allowed", 403) 241 | // return 242 | // } 243 | // 244 | // If the endpoint supports subprotocols, then the application is responsible 245 | // for negotiating the protocol used on the connection. Use the Subprotocols() 246 | // function to get the subprotocols requested by the client. Use the 247 | // Sec-Websocket-Protocol response header to specify the subprotocol selected 248 | // by the application. 249 | // 250 | // The responseHeader is included in the response to the client's upgrade 251 | // request. Use the responseHeader to specify cookies (Set-Cookie) and the 252 | // negotiated subprotocol (Sec-Websocket-Protocol). 253 | // 254 | // The connection buffers IO to the underlying network connection. The 255 | // readBufSize and writeBufSize parameters specify the size of the buffers to 256 | // use. Messages can be larger than the buffers. 257 | // 258 | // If the request is not a valid WebSocket handshake, then Upgrade returns an 259 | // error of type HandshakeError. Applications should handle this error by 260 | // replying to the client with an HTTP error response. 261 | func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) { 262 | u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize} 263 | u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) { 264 | // don't return errors to maintain backwards compatibility 265 | } 266 | u.CheckOrigin = func(r *http.Request) bool { 267 | // allow all connections by default 268 | return true 269 | } 270 | return u.Upgrade(w, r, responseHeader) 271 | } 272 | 273 | // Subprotocols returns the subprotocols requested by the client in the 274 | // Sec-Websocket-Protocol header. 275 | func Subprotocols(r *http.Request) []string { 276 | h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol")) 277 | if h == "" { 278 | return nil 279 | } 280 | protocols := strings.Split(h, ",") 281 | for i := range protocols { 282 | protocols[i] = strings.TrimSpace(protocols[i]) 283 | } 284 | return protocols 285 | } 286 | 287 | // IsWebSocketUpgrade returns true if the client requested upgrade to the 288 | // WebSocket protocol. 289 | func IsWebSocketUpgrade(r *http.Request) bool { 290 | return tokenListContainsValue(r.Header, "Connection", "upgrade") && 291 | tokenListContainsValue(r.Header, "Upgrade", "websocket") 292 | } 293 | -------------------------------------------------------------------------------- /vendor/github.com/gorilla/websocket/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket 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 | "crypto/rand" 9 | "crypto/sha1" 10 | "encoding/base64" 11 | "io" 12 | "net/http" 13 | "strings" 14 | ) 15 | 16 | var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") 17 | 18 | func computeAcceptKey(challengeKey string) string { 19 | h := sha1.New() 20 | h.Write([]byte(challengeKey)) 21 | h.Write(keyGUID) 22 | return base64.StdEncoding.EncodeToString(h.Sum(nil)) 23 | } 24 | 25 | func generateChallengeKey() (string, error) { 26 | p := make([]byte, 16) 27 | if _, err := io.ReadFull(rand.Reader, p); err != nil { 28 | return "", err 29 | } 30 | return base64.StdEncoding.EncodeToString(p), nil 31 | } 32 | 33 | // Octet types from RFC 2616. 34 | var octetTypes [256]byte 35 | 36 | const ( 37 | isTokenOctet = 1 << iota 38 | isSpaceOctet 39 | ) 40 | 41 | func init() { 42 | // From RFC 2616 43 | // 44 | // OCTET = 45 | // CHAR = 46 | // CTL = 47 | // CR = 48 | // LF = 49 | // SP = 50 | // HT = 51 | // <"> = 52 | // CRLF = CR LF 53 | // LWS = [CRLF] 1*( SP | HT ) 54 | // TEXT = 55 | // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> 56 | // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT 57 | // token = 1* 58 | // qdtext = > 59 | 60 | for c := 0; c < 256; c++ { 61 | var t byte 62 | isCtl := c <= 31 || c == 127 63 | isChar := 0 <= c && c <= 127 64 | isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 65 | if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { 66 | t |= isSpaceOctet 67 | } 68 | if isChar && !isCtl && !isSeparator { 69 | t |= isTokenOctet 70 | } 71 | octetTypes[c] = t 72 | } 73 | } 74 | 75 | func skipSpace(s string) (rest string) { 76 | i := 0 77 | for ; i < len(s); i++ { 78 | if octetTypes[s[i]]&isSpaceOctet == 0 { 79 | break 80 | } 81 | } 82 | return s[i:] 83 | } 84 | 85 | func nextToken(s string) (token, rest string) { 86 | i := 0 87 | for ; i < len(s); i++ { 88 | if octetTypes[s[i]]&isTokenOctet == 0 { 89 | break 90 | } 91 | } 92 | return s[:i], s[i:] 93 | } 94 | 95 | func nextTokenOrQuoted(s string) (value string, rest string) { 96 | if !strings.HasPrefix(s, "\"") { 97 | return nextToken(s) 98 | } 99 | s = s[1:] 100 | for i := 0; i < len(s); i++ { 101 | switch s[i] { 102 | case '"': 103 | return s[:i], s[i+1:] 104 | case '\\': 105 | p := make([]byte, len(s)-1) 106 | j := copy(p, s[:i]) 107 | escape := true 108 | for i = i + 1; i < len(s); i++ { 109 | b := s[i] 110 | switch { 111 | case escape: 112 | escape = false 113 | p[j] = b 114 | j++ 115 | case b == '\\': 116 | escape = true 117 | case b == '"': 118 | return string(p[:j]), s[i+1:] 119 | default: 120 | p[j] = b 121 | j++ 122 | } 123 | } 124 | return "", "" 125 | } 126 | } 127 | return "", "" 128 | } 129 | 130 | // tokenListContainsValue returns true if the 1#token header with the given 131 | // name contains token. 132 | func tokenListContainsValue(header http.Header, name string, value string) bool { 133 | headers: 134 | for _, s := range header[name] { 135 | for { 136 | var t string 137 | t, s = nextToken(skipSpace(s)) 138 | if t == "" { 139 | continue headers 140 | } 141 | s = skipSpace(s) 142 | if s != "" && s[0] != ',' { 143 | continue headers 144 | } 145 | if strings.EqualFold(t, value) { 146 | return true 147 | } 148 | if s == "" { 149 | continue headers 150 | } 151 | s = s[1:] 152 | } 153 | } 154 | return false 155 | } 156 | 157 | // parseExtensiosn parses WebSocket extensions from a header. 158 | func parseExtensions(header http.Header) []map[string]string { 159 | 160 | // From RFC 6455: 161 | // 162 | // Sec-WebSocket-Extensions = extension-list 163 | // extension-list = 1#extension 164 | // extension = extension-token *( ";" extension-param ) 165 | // extension-token = registered-token 166 | // registered-token = token 167 | // extension-param = token [ "=" (token | quoted-string) ] 168 | // ;When using the quoted-string syntax variant, the value 169 | // ;after quoted-string unescaping MUST conform to the 170 | // ;'token' ABNF. 171 | 172 | var result []map[string]string 173 | headers: 174 | for _, s := range header["Sec-Websocket-Extensions"] { 175 | for { 176 | var t string 177 | t, s = nextToken(skipSpace(s)) 178 | if t == "" { 179 | continue headers 180 | } 181 | ext := map[string]string{"": t} 182 | for { 183 | s = skipSpace(s) 184 | if !strings.HasPrefix(s, ";") { 185 | break 186 | } 187 | var k string 188 | k, s = nextToken(skipSpace(s[1:])) 189 | if k == "" { 190 | continue headers 191 | } 192 | s = skipSpace(s) 193 | var v string 194 | if strings.HasPrefix(s, "=") { 195 | v, s = nextTokenOrQuoted(skipSpace(s[1:])) 196 | s = skipSpace(s) 197 | } 198 | if s != "" && s[0] != ',' && s[0] != ';' { 199 | continue headers 200 | } 201 | ext[k] = v 202 | } 203 | if s != "" && s[0] != ',' { 204 | continue headers 205 | } 206 | result = append(result, ext) 207 | if s == "" { 208 | continue headers 209 | } 210 | s = s[1:] 211 | } 212 | } 213 | return result 214 | } 215 | --------------------------------------------------------------------------------