├── .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
Adding a user to chatroom Removing a user from chat room Brodcasting message to all users who are connected to the chat room has the history of all the messages so far created 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 | 
25 | 
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 |
49 |
55 |
56 |
Send
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 | [](https://travis-ci.org/gorilla/websocket)
7 | [](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 |
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 |
--------------------------------------------------------------------------------