2021-05-14 15:19:02 +00:00
|
|
|
package upgrade
|
2018-04-25 16:58:48 +00:00
|
|
|
|
|
|
|
import (
|
2018-09-29 12:39:12 +00:00
|
|
|
"crypto/sha1"
|
|
|
|
"encoding/base64"
|
2018-04-25 16:58:48 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
)
|
|
|
|
|
2021-05-14 15:19:02 +00:00
|
|
|
// HTTPVersion constant
|
|
|
|
const HTTPVersion = "1.1"
|
|
|
|
|
|
|
|
// UsedWSVersion constant websocket version
|
|
|
|
const UsedWSVersion = 13
|
|
|
|
|
|
|
|
// WSSalt constant websocket salt
|
|
|
|
const WSSalt = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
|
|
|
|
|
|
|
// Response represents an HTTP Upgrade Response
|
|
|
|
type Response struct {
|
|
|
|
code StatusCode // status code
|
|
|
|
accept []byte // processed from Sec-WebSocket-Key
|
|
|
|
protocol []byte // set from Sec-WebSocket-Protocol or none if not received
|
|
|
|
}
|
|
|
|
|
2018-04-25 16:58:48 +00:00
|
|
|
// SetStatusCode sets the status code
|
2021-05-14 15:19:02 +00:00
|
|
|
func (r *Response) SetStatusCode(sc StatusCode) {
|
2018-04-25 16:58:48 +00:00
|
|
|
r.code = sc
|
|
|
|
}
|
|
|
|
|
2021-05-14 14:47:02 +00:00
|
|
|
// SetProtocol sets the protocols
|
2021-05-14 15:19:02 +00:00
|
|
|
func (r *Response) SetProtocol(p []byte) {
|
2018-04-25 16:58:48 +00:00
|
|
|
r.protocol = p
|
|
|
|
}
|
|
|
|
|
|
|
|
// ProcessKey processes the accept token according
|
|
|
|
// to the rfc from the Sec-WebSocket-Key
|
2021-05-14 15:19:02 +00:00
|
|
|
func (r *Response) ProcessKey(k []byte) {
|
2018-04-25 16:58:48 +00:00
|
|
|
|
2018-04-28 14:23:07 +00:00
|
|
|
// do nothing for empty key
|
|
|
|
if k == nil || len(k) == 0 {
|
|
|
|
r.accept = nil
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-05-14 15:19:02 +00:00
|
|
|
// 1. Concat with constant salt
|
|
|
|
mix := append(k, []byte(WSSalt)...)
|
2018-04-25 16:58:48 +00:00
|
|
|
|
2021-05-14 15:19:02 +00:00
|
|
|
// 2. Hash with sha1 algorithm
|
2018-04-25 16:58:48 +00:00
|
|
|
digest := sha1.Sum(mix)
|
|
|
|
|
2021-05-14 15:19:02 +00:00
|
|
|
// 3. Base64 encode it
|
2018-09-29 12:39:12 +00:00
|
|
|
r.accept = []byte(base64.StdEncoding.EncodeToString(digest[:sha1.Size]))
|
2018-04-25 16:58:48 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send sends the response through an io.Writer
|
|
|
|
// typically a socket
|
2021-05-14 15:19:02 +00:00
|
|
|
func (r Response) Send(w io.Writer) (int, error) {
|
2018-04-25 16:58:48 +00:00
|
|
|
|
2021-05-14 15:19:02 +00:00
|
|
|
// 1. Build response line
|
|
|
|
responseLine := fmt.Sprintf("HTTP/%s %d %s\r\n", HTTPVersion, r.code, r.code)
|
2018-04-25 16:58:48 +00:00
|
|
|
|
2021-05-14 15:19:02 +00:00
|
|
|
// 2. Build headers
|
2018-04-25 16:58:48 +00:00
|
|
|
optionalProtocol := ""
|
|
|
|
if len(r.protocol) > 0 {
|
|
|
|
optionalProtocol = fmt.Sprintf("Sec-WebSocket-Protocol: %s\r\n", r.protocol)
|
|
|
|
}
|
|
|
|
|
2021-05-14 15:19:02 +00:00
|
|
|
headers := fmt.Sprintf("Upgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Version: %d\r\n%s", UsedWSVersion, optionalProtocol)
|
2018-04-28 14:23:07 +00:00
|
|
|
if r.accept != nil {
|
|
|
|
headers = fmt.Sprintf("%sSec-WebSocket-Accept: %s\r\n", headers, r.accept)
|
|
|
|
}
|
|
|
|
headers = fmt.Sprintf("%s\r\n", headers)
|
2018-04-25 16:58:48 +00:00
|
|
|
|
2021-05-14 15:19:02 +00:00
|
|
|
// 3. Build all
|
2018-05-02 20:36:59 +00:00
|
|
|
raw := []byte(fmt.Sprintf("%s%s", responseLine, headers))
|
2018-04-25 16:58:48 +00:00
|
|
|
|
2021-05-14 15:19:02 +00:00
|
|
|
// 4. Write
|
2018-04-25 16:58:48 +00:00
|
|
|
written, err := w.Write(raw)
|
|
|
|
|
|
|
|
return written, err
|
|
|
|
|
2018-04-26 21:26:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetProtocol returns the choosen protocol if set, else nil
|
2021-05-14 15:19:02 +00:00
|
|
|
func (r Response) GetProtocol() []byte {
|
2018-04-26 21:26:20 +00:00
|
|
|
return r.protocol
|
2018-04-28 14:23:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetStatusCode returns the response status code
|
2021-05-14 15:19:02 +00:00
|
|
|
func (r Response) GetStatusCode() StatusCode {
|
2018-04-28 14:23:07 +00:00
|
|
|
return r.code
|
2018-09-29 12:39:12 +00:00
|
|
|
}
|