fix build time
All checks were successful
Build and Deploy Backend / build-and-deploy (push) Successful in 3m34s
All checks were successful
Build and Deploy Backend / build-and-deploy (push) Successful in 3m34s
This commit is contained in:
parent
c3a171163a
commit
01b2ef298c
@ -1,33 +1,24 @@
|
||||
# ============================================================
|
||||
# hw-ws-service Dockerfile — 多阶段构建
|
||||
# 构建阶段:Go 编译(含 CGO for libopus)
|
||||
# 运行阶段:Alpine + libopus + ffmpeg(最终镜像 ~60-80MB)
|
||||
# hw-ws-service Dockerfile — 多阶段构建(国内 CI 优化版)
|
||||
# 优化:go mod vendor 跳过网络下载,Alpine 阿里云源加速
|
||||
# ============================================================
|
||||
|
||||
# ---- 构建阶段 ----
|
||||
FROM golang:1.23-alpine AS builder
|
||||
|
||||
# 安装 CGO 编译所需的 C 工具链和 libopus 开发头文件
|
||||
RUN apk add --no-cache gcc musl-dev opus-dev opusfile-dev
|
||||
# Alpine 换国内源 + 安装编译依赖
|
||||
RUN sed -i 's#dl-cdn.alpinelinux.org#mirrors.aliyun.com#g' /etc/apk/repositories && \
|
||||
apk add --no-cache gcc musl-dev opus-dev opusfile-dev
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# 使用国内镜像加速,避免 proxy.golang.org 超时
|
||||
ENV GOPROXY=https://goproxy.cn,direct
|
||||
|
||||
# 先拷贝 go.mod/go.sum,利用 cache mount 持久化模块缓存
|
||||
COPY go.mod go.sum ./
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
# 使用 cache mount 持久化模块缓存和编译缓存,CI 重复构建大幅加速
|
||||
# vendor 模式:依赖已随代码提交,无需联网下载
|
||||
# CGO_ENABLED=1 必须开启(hraban/opus 是 CGO 库)
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
--mount=type=cache,target=/root/.cache/go-build \
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
CGO_ENABLED=1 GOOS=linux \
|
||||
go build \
|
||||
-mod=vendor \
|
||||
-trimpath \
|
||||
-ldflags="-s -w" \
|
||||
-o hw-ws-service \
|
||||
@ -36,19 +27,12 @@ RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
# ---- 运行阶段 ----
|
||||
FROM alpine:3.20
|
||||
|
||||
# 运行时依赖:
|
||||
# opus — libopus 动态库(hraban/opus CGO 绑定需要)
|
||||
# ffmpeg — MP3/AAC 解码为 PCM
|
||||
# ca-certificates — HTTPS 请求 OSS 需要根证书
|
||||
RUN apk add --no-cache opus ffmpeg ca-certificates && \
|
||||
# 创建非 root 运行用户(安全最佳实践)
|
||||
RUN sed -i 's#dl-cdn.alpinelinux.org#mirrors.aliyun.com#g' /etc/apk/repositories && \
|
||||
apk add --no-cache opus ffmpeg ca-certificates && \
|
||||
addgroup -S hwws && adduser -S hwws -G hwws
|
||||
|
||||
COPY --from=builder /app/hw-ws-service /hw-ws-service
|
||||
|
||||
# 以非 root 用户运行
|
||||
USER hwws
|
||||
|
||||
EXPOSE 8888
|
||||
|
||||
ENTRYPOINT ["/hw-ws-service"]
|
||||
|
||||
25
hw_service_go/vendor/github.com/gorilla/websocket/.gitignore
generated
vendored
Normal file
25
hw_service_go/vendor/github.com/gorilla/websocket/.gitignore
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
|
||||
.idea/
|
||||
*.iml
|
||||
9
hw_service_go/vendor/github.com/gorilla/websocket/AUTHORS
generated
vendored
Normal file
9
hw_service_go/vendor/github.com/gorilla/websocket/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
# This is the official list of Gorilla WebSocket authors for copyright
|
||||
# purposes.
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Gary Burd <gary@beagledreams.com>
|
||||
Google LLC (https://opensource.google.com/)
|
||||
Joachim Bauch <mail@joachim-bauch.de>
|
||||
|
||||
22
hw_service_go/vendor/github.com/gorilla/websocket/LICENSE
generated
vendored
Normal file
22
hw_service_go/vendor/github.com/gorilla/websocket/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
33
hw_service_go/vendor/github.com/gorilla/websocket/README.md
generated
vendored
Normal file
33
hw_service_go/vendor/github.com/gorilla/websocket/README.md
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
# Gorilla WebSocket
|
||||
|
||||
[](https://godoc.org/github.com/gorilla/websocket)
|
||||
[](https://circleci.com/gh/gorilla/websocket)
|
||||
|
||||
Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
|
||||
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
|
||||
|
||||
|
||||
### Documentation
|
||||
|
||||
* [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc)
|
||||
* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)
|
||||
* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
|
||||
* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
|
||||
* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)
|
||||
|
||||
### Status
|
||||
|
||||
The Gorilla WebSocket package provides a complete and tested implementation of
|
||||
the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The
|
||||
package API is stable.
|
||||
|
||||
### Installation
|
||||
|
||||
go get github.com/gorilla/websocket
|
||||
|
||||
### Protocol Compliance
|
||||
|
||||
The Gorilla WebSocket package passes the server tests in the [Autobahn Test
|
||||
Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn
|
||||
subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).
|
||||
|
||||
434
hw_service_go/vendor/github.com/gorilla/websocket/client.go
generated
vendored
Normal file
434
hw_service_go/vendor/github.com/gorilla/websocket/client.go
generated
vendored
Normal file
@ -0,0 +1,434 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrBadHandshake is returned when the server response to opening handshake is
|
||||
// invalid.
|
||||
var ErrBadHandshake = errors.New("websocket: bad handshake")
|
||||
|
||||
var errInvalidCompression = errors.New("websocket: invalid compression negotiation")
|
||||
|
||||
// NewClient creates a new client connection using the given net connection.
|
||||
// The URL u specifies the host and request URI. Use requestHeader to specify
|
||||
// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
|
||||
// (Cookie). Use the response.Header to get the selected subprotocol
|
||||
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
|
||||
//
|
||||
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
|
||||
// non-nil *http.Response so that callers can handle redirects, authentication,
|
||||
// etc.
|
||||
//
|
||||
// Deprecated: Use Dialer instead.
|
||||
func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) {
|
||||
d := Dialer{
|
||||
ReadBufferSize: readBufSize,
|
||||
WriteBufferSize: writeBufSize,
|
||||
NetDial: func(net, addr string) (net.Conn, error) {
|
||||
return netConn, nil
|
||||
},
|
||||
}
|
||||
return d.Dial(u.String(), requestHeader)
|
||||
}
|
||||
|
||||
// A Dialer contains options for connecting to WebSocket server.
|
||||
//
|
||||
// It is safe to call Dialer's methods concurrently.
|
||||
type Dialer struct {
|
||||
// NetDial specifies the dial function for creating TCP connections. If
|
||||
// NetDial is nil, net.Dial is used.
|
||||
NetDial func(network, addr string) (net.Conn, error)
|
||||
|
||||
// NetDialContext specifies the dial function for creating TCP connections. If
|
||||
// NetDialContext is nil, NetDial is used.
|
||||
NetDialContext func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
|
||||
// NetDialTLSContext specifies the dial function for creating TLS/TCP connections. If
|
||||
// NetDialTLSContext is nil, NetDialContext is used.
|
||||
// If NetDialTLSContext is set, Dial assumes the TLS handshake is done there and
|
||||
// TLSClientConfig is ignored.
|
||||
NetDialTLSContext func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
|
||||
// Proxy specifies a function to return a proxy for a given
|
||||
// Request. If the function returns a non-nil error, the
|
||||
// request is aborted with the provided error.
|
||||
// If Proxy is nil or returns a nil *URL, no proxy is used.
|
||||
Proxy func(*http.Request) (*url.URL, error)
|
||||
|
||||
// TLSClientConfig specifies the TLS configuration to use with tls.Client.
|
||||
// If nil, the default configuration is used.
|
||||
// If either NetDialTLS or NetDialTLSContext are set, Dial assumes the TLS handshake
|
||||
// is done there and TLSClientConfig is ignored.
|
||||
TLSClientConfig *tls.Config
|
||||
|
||||
// HandshakeTimeout specifies the duration for the handshake to complete.
|
||||
HandshakeTimeout time.Duration
|
||||
|
||||
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer
|
||||
// size is zero, then a useful default size is used. The I/O buffer sizes
|
||||
// do not limit the size of the messages that can be sent or received.
|
||||
ReadBufferSize, WriteBufferSize int
|
||||
|
||||
// WriteBufferPool is a pool of buffers for write operations. If the value
|
||||
// is not set, then write buffers are allocated to the connection for the
|
||||
// lifetime of the connection.
|
||||
//
|
||||
// A pool is most useful when the application has a modest volume of writes
|
||||
// across a large number of connections.
|
||||
//
|
||||
// Applications should use a single pool for each unique value of
|
||||
// WriteBufferSize.
|
||||
WriteBufferPool BufferPool
|
||||
|
||||
// Subprotocols specifies the client's requested subprotocols.
|
||||
Subprotocols []string
|
||||
|
||||
// EnableCompression specifies if the client should attempt to negotiate
|
||||
// per message compression (RFC 7692). Setting this value to true does not
|
||||
// guarantee that compression will be supported. Currently only "no context
|
||||
// takeover" modes are supported.
|
||||
EnableCompression bool
|
||||
|
||||
// Jar specifies the cookie jar.
|
||||
// If Jar is nil, cookies are not sent in requests and ignored
|
||||
// in responses.
|
||||
Jar http.CookieJar
|
||||
}
|
||||
|
||||
// Dial creates a new client connection by calling DialContext with a background context.
|
||||
func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
|
||||
return d.DialContext(context.Background(), urlStr, requestHeader)
|
||||
}
|
||||
|
||||
var errMalformedURL = errors.New("malformed ws or wss URL")
|
||||
|
||||
func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
|
||||
hostPort = u.Host
|
||||
hostNoPort = u.Host
|
||||
if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") {
|
||||
hostNoPort = hostNoPort[:i]
|
||||
} else {
|
||||
switch u.Scheme {
|
||||
case "wss":
|
||||
hostPort += ":443"
|
||||
case "https":
|
||||
hostPort += ":443"
|
||||
default:
|
||||
hostPort += ":80"
|
||||
}
|
||||
}
|
||||
return hostPort, hostNoPort
|
||||
}
|
||||
|
||||
// DefaultDialer is a dialer with all fields set to the default values.
|
||||
var DefaultDialer = &Dialer{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
HandshakeTimeout: 45 * time.Second,
|
||||
}
|
||||
|
||||
// nilDialer is dialer to use when receiver is nil.
|
||||
var nilDialer = *DefaultDialer
|
||||
|
||||
// DialContext creates a new client connection. Use requestHeader to specify the
|
||||
// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie).
|
||||
// Use the response.Header to get the selected subprotocol
|
||||
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
|
||||
//
|
||||
// The context will be used in the request and in the Dialer.
|
||||
//
|
||||
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
|
||||
// non-nil *http.Response so that callers can handle redirects, authentication,
|
||||
// etcetera. The response body may not contain the entire response and does not
|
||||
// need to be closed by the application.
|
||||
func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
|
||||
if d == nil {
|
||||
d = &nilDialer
|
||||
}
|
||||
|
||||
challengeKey, err := generateChallengeKey()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
u, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "ws":
|
||||
u.Scheme = "http"
|
||||
case "wss":
|
||||
u.Scheme = "https"
|
||||
default:
|
||||
return nil, nil, errMalformedURL
|
||||
}
|
||||
|
||||
if u.User != nil {
|
||||
// User name and password are not allowed in websocket URIs.
|
||||
return nil, nil, errMalformedURL
|
||||
}
|
||||
|
||||
req := &http.Request{
|
||||
Method: http.MethodGet,
|
||||
URL: u,
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: make(http.Header),
|
||||
Host: u.Host,
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
// Set the cookies present in the cookie jar of the dialer
|
||||
if d.Jar != nil {
|
||||
for _, cookie := range d.Jar.Cookies(u) {
|
||||
req.AddCookie(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
// Set the request headers using the capitalization for names and values in
|
||||
// RFC examples. Although the capitalization shouldn't matter, there are
|
||||
// servers that depend on it. The Header.Set method is not used because the
|
||||
// method canonicalizes the header names.
|
||||
req.Header["Upgrade"] = []string{"websocket"}
|
||||
req.Header["Connection"] = []string{"Upgrade"}
|
||||
req.Header["Sec-WebSocket-Key"] = []string{challengeKey}
|
||||
req.Header["Sec-WebSocket-Version"] = []string{"13"}
|
||||
if len(d.Subprotocols) > 0 {
|
||||
req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")}
|
||||
}
|
||||
for k, vs := range requestHeader {
|
||||
switch {
|
||||
case k == "Host":
|
||||
if len(vs) > 0 {
|
||||
req.Host = vs[0]
|
||||
}
|
||||
case k == "Upgrade" ||
|
||||
k == "Connection" ||
|
||||
k == "Sec-Websocket-Key" ||
|
||||
k == "Sec-Websocket-Version" ||
|
||||
k == "Sec-Websocket-Extensions" ||
|
||||
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
||||
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
||||
case k == "Sec-Websocket-Protocol":
|
||||
req.Header["Sec-WebSocket-Protocol"] = vs
|
||||
default:
|
||||
req.Header[k] = vs
|
||||
}
|
||||
}
|
||||
|
||||
if d.EnableCompression {
|
||||
req.Header["Sec-WebSocket-Extensions"] = []string{"permessage-deflate; server_no_context_takeover; client_no_context_takeover"}
|
||||
}
|
||||
|
||||
if d.HandshakeTimeout != 0 {
|
||||
var cancel func()
|
||||
ctx, cancel = context.WithTimeout(ctx, d.HandshakeTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
// Get network dial function.
|
||||
var netDial func(network, add string) (net.Conn, error)
|
||||
|
||||
switch u.Scheme {
|
||||
case "http":
|
||||
if d.NetDialContext != nil {
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return d.NetDialContext(ctx, network, addr)
|
||||
}
|
||||
} else if d.NetDial != nil {
|
||||
netDial = d.NetDial
|
||||
}
|
||||
case "https":
|
||||
if d.NetDialTLSContext != nil {
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return d.NetDialTLSContext(ctx, network, addr)
|
||||
}
|
||||
} else if d.NetDialContext != nil {
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return d.NetDialContext(ctx, network, addr)
|
||||
}
|
||||
} else if d.NetDial != nil {
|
||||
netDial = d.NetDial
|
||||
}
|
||||
default:
|
||||
return nil, nil, errMalformedURL
|
||||
}
|
||||
|
||||
if netDial == nil {
|
||||
netDialer := &net.Dialer{}
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return netDialer.DialContext(ctx, network, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// If needed, wrap the dial function to set the connection deadline.
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
forwardDial := netDial
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
c, err := forwardDial(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = c.SetDeadline(deadline)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If needed, wrap the dial function to connect through a proxy.
|
||||
if d.Proxy != nil {
|
||||
proxyURL, err := d.Proxy(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if proxyURL != nil {
|
||||
dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
netDial = dialer.Dial
|
||||
}
|
||||
}
|
||||
|
||||
hostPort, hostNoPort := hostPortNoPort(u)
|
||||
trace := httptrace.ContextClientTrace(ctx)
|
||||
if trace != nil && trace.GetConn != nil {
|
||||
trace.GetConn(hostPort)
|
||||
}
|
||||
|
||||
netConn, err := netDial("tcp", hostPort)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if trace != nil && trace.GotConn != nil {
|
||||
trace.GotConn(httptrace.GotConnInfo{
|
||||
Conn: netConn,
|
||||
})
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if netConn != nil {
|
||||
netConn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if u.Scheme == "https" && d.NetDialTLSContext == nil {
|
||||
// If NetDialTLSContext is set, assume that the TLS handshake has already been done
|
||||
|
||||
cfg := cloneTLSConfig(d.TLSClientConfig)
|
||||
if cfg.ServerName == "" {
|
||||
cfg.ServerName = hostNoPort
|
||||
}
|
||||
tlsConn := tls.Client(netConn, cfg)
|
||||
netConn = tlsConn
|
||||
|
||||
if trace != nil && trace.TLSHandshakeStart != nil {
|
||||
trace.TLSHandshakeStart()
|
||||
}
|
||||
err := doHandshake(ctx, tlsConn, cfg)
|
||||
if trace != nil && trace.TLSHandshakeDone != nil {
|
||||
trace.TLSHandshakeDone(tlsConn.ConnectionState(), err)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize, d.WriteBufferPool, nil, nil)
|
||||
|
||||
if err := req.Write(netConn); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if trace != nil && trace.GotFirstResponseByte != nil {
|
||||
if peek, err := conn.br.Peek(1); err == nil && len(peek) == 1 {
|
||||
trace.GotFirstResponseByte()
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(conn.br, req)
|
||||
if err != nil {
|
||||
if d.TLSClientConfig != nil {
|
||||
for _, proto := range d.TLSClientConfig.NextProtos {
|
||||
if proto != "http/1.1" {
|
||||
return nil, nil, fmt.Errorf(
|
||||
"websocket: protocol %q was given but is not supported;"+
|
||||
"sharing tls.Config with net/http Transport can cause this error: %w",
|
||||
proto, err,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if d.Jar != nil {
|
||||
if rc := resp.Cookies(); len(rc) > 0 {
|
||||
d.Jar.SetCookies(u, rc)
|
||||
}
|
||||
}
|
||||
|
||||
if resp.StatusCode != 101 ||
|
||||
!tokenListContainsValue(resp.Header, "Upgrade", "websocket") ||
|
||||
!tokenListContainsValue(resp.Header, "Connection", "upgrade") ||
|
||||
resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) {
|
||||
// Before closing the network connection on return from this
|
||||
// function, slurp up some of the response to aid application
|
||||
// debugging.
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := io.ReadFull(resp.Body, buf)
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n]))
|
||||
return nil, resp, ErrBadHandshake
|
||||
}
|
||||
|
||||
for _, ext := range parseExtensions(resp.Header) {
|
||||
if ext[""] != "permessage-deflate" {
|
||||
continue
|
||||
}
|
||||
_, snct := ext["server_no_context_takeover"]
|
||||
_, cnct := ext["client_no_context_takeover"]
|
||||
if !snct || !cnct {
|
||||
return nil, resp, errInvalidCompression
|
||||
}
|
||||
conn.newCompressionWriter = compressNoContextTakeover
|
||||
conn.newDecompressionReader = decompressNoContextTakeover
|
||||
break
|
||||
}
|
||||
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
||||
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
||||
|
||||
netConn.SetDeadline(time.Time{})
|
||||
netConn = nil // to avoid close in defer.
|
||||
return conn, resp, nil
|
||||
}
|
||||
|
||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||
if cfg == nil {
|
||||
return &tls.Config{}
|
||||
}
|
||||
return cfg.Clone()
|
||||
}
|
||||
148
hw_service_go/vendor/github.com/gorilla/websocket/compression.go
generated
vendored
Normal file
148
hw_service_go/vendor/github.com/gorilla/websocket/compression.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"compress/flate"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
minCompressionLevel = -2 // flate.HuffmanOnly not defined in Go < 1.6
|
||||
maxCompressionLevel = flate.BestCompression
|
||||
defaultCompressionLevel = 1
|
||||
)
|
||||
|
||||
var (
|
||||
flateWriterPools [maxCompressionLevel - minCompressionLevel + 1]sync.Pool
|
||||
flateReaderPool = sync.Pool{New: func() interface{} {
|
||||
return flate.NewReader(nil)
|
||||
}}
|
||||
)
|
||||
|
||||
func decompressNoContextTakeover(r io.Reader) io.ReadCloser {
|
||||
const tail =
|
||||
// Add four bytes as specified in RFC
|
||||
"\x00\x00\xff\xff" +
|
||||
// Add final block to squelch unexpected EOF error from flate reader.
|
||||
"\x01\x00\x00\xff\xff"
|
||||
|
||||
fr, _ := flateReaderPool.Get().(io.ReadCloser)
|
||||
fr.(flate.Resetter).Reset(io.MultiReader(r, strings.NewReader(tail)), nil)
|
||||
return &flateReadWrapper{fr}
|
||||
}
|
||||
|
||||
func isValidCompressionLevel(level int) bool {
|
||||
return minCompressionLevel <= level && level <= maxCompressionLevel
|
||||
}
|
||||
|
||||
func compressNoContextTakeover(w io.WriteCloser, level int) io.WriteCloser {
|
||||
p := &flateWriterPools[level-minCompressionLevel]
|
||||
tw := &truncWriter{w: w}
|
||||
fw, _ := p.Get().(*flate.Writer)
|
||||
if fw == nil {
|
||||
fw, _ = flate.NewWriter(tw, level)
|
||||
} else {
|
||||
fw.Reset(tw)
|
||||
}
|
||||
return &flateWriteWrapper{fw: fw, tw: tw, p: p}
|
||||
}
|
||||
|
||||
// truncWriter is an io.Writer that writes all but the last four bytes of the
|
||||
// stream to another io.Writer.
|
||||
type truncWriter struct {
|
||||
w io.WriteCloser
|
||||
n int
|
||||
p [4]byte
|
||||
}
|
||||
|
||||
func (w *truncWriter) Write(p []byte) (int, error) {
|
||||
n := 0
|
||||
|
||||
// fill buffer first for simplicity.
|
||||
if w.n < len(w.p) {
|
||||
n = copy(w.p[w.n:], p)
|
||||
p = p[n:]
|
||||
w.n += n
|
||||
if len(p) == 0 {
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
|
||||
m := len(p)
|
||||
if m > len(w.p) {
|
||||
m = len(w.p)
|
||||
}
|
||||
|
||||
if nn, err := w.w.Write(w.p[:m]); err != nil {
|
||||
return n + nn, err
|
||||
}
|
||||
|
||||
copy(w.p[:], w.p[m:])
|
||||
copy(w.p[len(w.p)-m:], p[len(p)-m:])
|
||||
nn, err := w.w.Write(p[:len(p)-m])
|
||||
return n + nn, err
|
||||
}
|
||||
|
||||
type flateWriteWrapper struct {
|
||||
fw *flate.Writer
|
||||
tw *truncWriter
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
func (w *flateWriteWrapper) Write(p []byte) (int, error) {
|
||||
if w.fw == nil {
|
||||
return 0, errWriteClosed
|
||||
}
|
||||
return w.fw.Write(p)
|
||||
}
|
||||
|
||||
func (w *flateWriteWrapper) Close() error {
|
||||
if w.fw == nil {
|
||||
return errWriteClosed
|
||||
}
|
||||
err1 := w.fw.Flush()
|
||||
w.p.Put(w.fw)
|
||||
w.fw = nil
|
||||
if w.tw.p != [4]byte{0, 0, 0xff, 0xff} {
|
||||
return errors.New("websocket: internal error, unexpected bytes at end of flate stream")
|
||||
}
|
||||
err2 := w.tw.w.Close()
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
|
||||
type flateReadWrapper struct {
|
||||
fr io.ReadCloser
|
||||
}
|
||||
|
||||
func (r *flateReadWrapper) Read(p []byte) (int, error) {
|
||||
if r.fr == nil {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
n, err := r.fr.Read(p)
|
||||
if err == io.EOF {
|
||||
// Preemptively place the reader back in the pool. This helps with
|
||||
// scenarios where the application does not call NextReader() soon after
|
||||
// this final read.
|
||||
r.Close()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *flateReadWrapper) Close() error {
|
||||
if r.fr == nil {
|
||||
return io.ErrClosedPipe
|
||||
}
|
||||
err := r.fr.Close()
|
||||
flateReaderPool.Put(r.fr)
|
||||
r.fr = nil
|
||||
return err
|
||||
}
|
||||
1238
hw_service_go/vendor/github.com/gorilla/websocket/conn.go
generated
vendored
Normal file
1238
hw_service_go/vendor/github.com/gorilla/websocket/conn.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
227
hw_service_go/vendor/github.com/gorilla/websocket/doc.go
generated
vendored
Normal file
227
hw_service_go/vendor/github.com/gorilla/websocket/doc.go
generated
vendored
Normal file
@ -0,0 +1,227 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package websocket implements the WebSocket protocol defined in RFC 6455.
|
||||
//
|
||||
// Overview
|
||||
//
|
||||
// The Conn type represents a WebSocket connection. A server application calls
|
||||
// the Upgrader.Upgrade method from an HTTP request handler to get a *Conn:
|
||||
//
|
||||
// var upgrader = websocket.Upgrader{
|
||||
// ReadBufferSize: 1024,
|
||||
// WriteBufferSize: 1024,
|
||||
// }
|
||||
//
|
||||
// func handler(w http.ResponseWriter, r *http.Request) {
|
||||
// conn, err := upgrader.Upgrade(w, r, nil)
|
||||
// if err != nil {
|
||||
// log.Println(err)
|
||||
// return
|
||||
// }
|
||||
// ... Use conn to send and receive messages.
|
||||
// }
|
||||
//
|
||||
// Call the connection's WriteMessage and ReadMessage methods to send and
|
||||
// receive messages as a slice of bytes. This snippet of code shows how to echo
|
||||
// messages using these methods:
|
||||
//
|
||||
// for {
|
||||
// messageType, p, err := conn.ReadMessage()
|
||||
// if err != nil {
|
||||
// log.Println(err)
|
||||
// return
|
||||
// }
|
||||
// if err := conn.WriteMessage(messageType, p); err != nil {
|
||||
// log.Println(err)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// In above snippet of code, p is a []byte and messageType is an int with value
|
||||
// websocket.BinaryMessage or websocket.TextMessage.
|
||||
//
|
||||
// An application can also send and receive messages using the io.WriteCloser
|
||||
// and io.Reader interfaces. To send a message, call the connection NextWriter
|
||||
// method to get an io.WriteCloser, write the message to the writer and close
|
||||
// the writer when done. To receive a message, call the connection NextReader
|
||||
// method to get an io.Reader and read until io.EOF is returned. This snippet
|
||||
// shows how to echo messages using the NextWriter and NextReader methods:
|
||||
//
|
||||
// for {
|
||||
// messageType, r, err := conn.NextReader()
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// w, err := conn.NextWriter(messageType)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if _, err := io.Copy(w, r); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err := w.Close(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Data Messages
|
||||
//
|
||||
// The WebSocket protocol distinguishes between text and binary data messages.
|
||||
// Text messages are interpreted as UTF-8 encoded text. The interpretation of
|
||||
// binary messages is left to the application.
|
||||
//
|
||||
// This package uses the TextMessage and BinaryMessage integer constants to
|
||||
// identify the two data message types. The ReadMessage and NextReader methods
|
||||
// return the type of the received message. The messageType argument to the
|
||||
// WriteMessage and NextWriter methods specifies the type of a sent message.
|
||||
//
|
||||
// It is the application's responsibility to ensure that text messages are
|
||||
// valid UTF-8 encoded text.
|
||||
//
|
||||
// Control Messages
|
||||
//
|
||||
// The WebSocket protocol defines three types of control messages: close, ping
|
||||
// and pong. Call the connection WriteControl, WriteMessage or NextWriter
|
||||
// methods to send a control message to the peer.
|
||||
//
|
||||
// Connections handle received close messages by calling the handler function
|
||||
// set with the SetCloseHandler method and by returning a *CloseError from the
|
||||
// NextReader, ReadMessage or the message Read method. The default close
|
||||
// handler sends a close message to the peer.
|
||||
//
|
||||
// Connections handle received ping messages by calling the handler function
|
||||
// set with the SetPingHandler method. The default ping handler sends a pong
|
||||
// message to the peer.
|
||||
//
|
||||
// Connections handle received pong messages by calling the handler function
|
||||
// set with the SetPongHandler method. The default pong handler does nothing.
|
||||
// If an application sends ping messages, then the application should set a
|
||||
// pong handler to receive the corresponding pong.
|
||||
//
|
||||
// The control message handler functions are called from the NextReader,
|
||||
// ReadMessage and message reader Read methods. The default close and ping
|
||||
// handlers can block these methods for a short time when the handler writes to
|
||||
// the connection.
|
||||
//
|
||||
// The application must read the connection to process close, ping and pong
|
||||
// messages sent from the peer. If the application is not otherwise interested
|
||||
// in messages from the peer, then the application should start a goroutine to
|
||||
// read and discard messages from the peer. A simple example is:
|
||||
//
|
||||
// func readLoop(c *websocket.Conn) {
|
||||
// for {
|
||||
// if _, _, err := c.NextReader(); err != nil {
|
||||
// c.Close()
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Concurrency
|
||||
//
|
||||
// Connections support one concurrent reader and one concurrent writer.
|
||||
//
|
||||
// Applications are responsible for ensuring that no more than one goroutine
|
||||
// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
|
||||
// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and
|
||||
// that no more than one goroutine calls the read methods (NextReader,
|
||||
// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler)
|
||||
// concurrently.
|
||||
//
|
||||
// The Close and WriteControl methods can be called concurrently with all other
|
||||
// methods.
|
||||
//
|
||||
// Origin Considerations
|
||||
//
|
||||
// Web browsers allow Javascript applications to open a WebSocket connection to
|
||||
// any host. It's up to the server to enforce an origin policy using the Origin
|
||||
// request header sent by the browser.
|
||||
//
|
||||
// The Upgrader calls the function specified in the CheckOrigin field to check
|
||||
// the origin. If the CheckOrigin function returns false, then the Upgrade
|
||||
// method fails the WebSocket handshake with HTTP status 403.
|
||||
//
|
||||
// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail
|
||||
// the handshake if the Origin request header is present and the Origin host is
|
||||
// not equal to the Host request header.
|
||||
//
|
||||
// The deprecated package-level Upgrade function does not perform origin
|
||||
// checking. The application is responsible for checking the Origin header
|
||||
// before calling the Upgrade function.
|
||||
//
|
||||
// Buffers
|
||||
//
|
||||
// Connections buffer network input and output to reduce the number
|
||||
// of system calls when reading or writing messages.
|
||||
//
|
||||
// Write buffers are also used for constructing WebSocket frames. See RFC 6455,
|
||||
// Section 5 for a discussion of message framing. A WebSocket frame header is
|
||||
// written to the network each time a write buffer is flushed to the network.
|
||||
// Decreasing the size of the write buffer can increase the amount of framing
|
||||
// overhead on the connection.
|
||||
//
|
||||
// The buffer sizes in bytes are specified by the ReadBufferSize and
|
||||
// WriteBufferSize fields in the Dialer and Upgrader. The Dialer uses a default
|
||||
// size of 4096 when a buffer size field is set to zero. The Upgrader reuses
|
||||
// buffers created by the HTTP server when a buffer size field is set to zero.
|
||||
// The HTTP server buffers have a size of 4096 at the time of this writing.
|
||||
//
|
||||
// The buffer sizes do not limit the size of a message that can be read or
|
||||
// written by a connection.
|
||||
//
|
||||
// Buffers are held for the lifetime of the connection by default. If the
|
||||
// Dialer or Upgrader WriteBufferPool field is set, then a connection holds the
|
||||
// write buffer only when writing a message.
|
||||
//
|
||||
// Applications should tune the buffer sizes to balance memory use and
|
||||
// performance. Increasing the buffer size uses more memory, but can reduce the
|
||||
// number of system calls to read or write the network. In the case of writing,
|
||||
// increasing the buffer size can reduce the number of frame headers written to
|
||||
// the network.
|
||||
//
|
||||
// Some guidelines for setting buffer parameters are:
|
||||
//
|
||||
// Limit the buffer sizes to the maximum expected message size. Buffers larger
|
||||
// than the largest message do not provide any benefit.
|
||||
//
|
||||
// Depending on the distribution of message sizes, setting the buffer size to
|
||||
// a value less than the maximum expected message size can greatly reduce memory
|
||||
// use with a small impact on performance. Here's an example: If 99% of the
|
||||
// messages are smaller than 256 bytes and the maximum message size is 512
|
||||
// bytes, then a buffer size of 256 bytes will result in 1.01 more system calls
|
||||
// than a buffer size of 512 bytes. The memory savings is 50%.
|
||||
//
|
||||
// A write buffer pool is useful when the application has a modest number
|
||||
// writes over a large number of connections. when buffers are pooled, a larger
|
||||
// buffer size has a reduced impact on total memory use and has the benefit of
|
||||
// reducing system calls and frame overhead.
|
||||
//
|
||||
// Compression EXPERIMENTAL
|
||||
//
|
||||
// Per message compression extensions (RFC 7692) are experimentally supported
|
||||
// by this package in a limited capacity. Setting the EnableCompression option
|
||||
// to true in Dialer or Upgrader will attempt to negotiate per message deflate
|
||||
// support.
|
||||
//
|
||||
// var upgrader = websocket.Upgrader{
|
||||
// EnableCompression: true,
|
||||
// }
|
||||
//
|
||||
// If compression was successfully negotiated with the connection's peer, any
|
||||
// message received in compressed form will be automatically decompressed.
|
||||
// All Read methods will return uncompressed bytes.
|
||||
//
|
||||
// Per message compression of messages written to a connection can be enabled
|
||||
// or disabled by calling the corresponding Conn method:
|
||||
//
|
||||
// conn.EnableWriteCompression(false)
|
||||
//
|
||||
// Currently this package does not support compression with "context takeover".
|
||||
// This means that messages must be compressed and decompressed in isolation,
|
||||
// without retaining sliding window or dictionary state across messages. For
|
||||
// more details refer to RFC 7692.
|
||||
//
|
||||
// Use of compression is experimental and may result in decreased performance.
|
||||
package websocket
|
||||
42
hw_service_go/vendor/github.com/gorilla/websocket/join.go
generated
vendored
Normal file
42
hw_service_go/vendor/github.com/gorilla/websocket/join.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2019 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// JoinMessages concatenates received messages to create a single io.Reader.
|
||||
// The string term is appended to each message. The returned reader does not
|
||||
// support concurrent calls to the Read method.
|
||||
func JoinMessages(c *Conn, term string) io.Reader {
|
||||
return &joinReader{c: c, term: term}
|
||||
}
|
||||
|
||||
type joinReader struct {
|
||||
c *Conn
|
||||
term string
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (r *joinReader) Read(p []byte) (int, error) {
|
||||
if r.r == nil {
|
||||
var err error
|
||||
_, r.r, err = r.c.NextReader()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if r.term != "" {
|
||||
r.r = io.MultiReader(r.r, strings.NewReader(r.term))
|
||||
}
|
||||
}
|
||||
n, err := r.r.Read(p)
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
r.r = nil
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
60
hw_service_go/vendor/github.com/gorilla/websocket/json.go
generated
vendored
Normal file
60
hw_service_go/vendor/github.com/gorilla/websocket/json.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
// WriteJSON writes the JSON encoding of v as a message.
|
||||
//
|
||||
// Deprecated: Use c.WriteJSON instead.
|
||||
func WriteJSON(c *Conn, v interface{}) error {
|
||||
return c.WriteJSON(v)
|
||||
}
|
||||
|
||||
// WriteJSON writes the JSON encoding of v as a message.
|
||||
//
|
||||
// See the documentation for encoding/json Marshal for details about the
|
||||
// conversion of Go values to JSON.
|
||||
func (c *Conn) WriteJSON(v interface{}) error {
|
||||
w, err := c.NextWriter(TextMessage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err1 := json.NewEncoder(w).Encode(v)
|
||||
err2 := w.Close()
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
|
||||
// ReadJSON reads the next JSON-encoded message from the connection and stores
|
||||
// it in the value pointed to by v.
|
||||
//
|
||||
// Deprecated: Use c.ReadJSON instead.
|
||||
func ReadJSON(c *Conn, v interface{}) error {
|
||||
return c.ReadJSON(v)
|
||||
}
|
||||
|
||||
// ReadJSON reads the next JSON-encoded message from the connection and stores
|
||||
// it in the value pointed to by v.
|
||||
//
|
||||
// See the documentation for the encoding/json Unmarshal function for details
|
||||
// about the conversion of JSON to a Go value.
|
||||
func (c *Conn) ReadJSON(v interface{}) error {
|
||||
_, r, err := c.NextReader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = json.NewDecoder(r).Decode(v)
|
||||
if err == io.EOF {
|
||||
// One value is expected in the message.
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return err
|
||||
}
|
||||
55
hw_service_go/vendor/github.com/gorilla/websocket/mask.go
generated
vendored
Normal file
55
hw_service_go/vendor/github.com/gorilla/websocket/mask.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in the
|
||||
// LICENSE file.
|
||||
|
||||
//go:build !appengine
|
||||
// +build !appengine
|
||||
|
||||
package websocket
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||
|
||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||
// Mask one byte at a time for small buffers.
|
||||
if len(b) < 2*wordSize {
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
return pos & 3
|
||||
}
|
||||
|
||||
// Mask one byte at a time to word boundary.
|
||||
if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {
|
||||
n = wordSize - n
|
||||
for i := range b[:n] {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
b = b[n:]
|
||||
}
|
||||
|
||||
// Create aligned word size key.
|
||||
var k [wordSize]byte
|
||||
for i := range k {
|
||||
k[i] = key[(pos+i)&3]
|
||||
}
|
||||
kw := *(*uintptr)(unsafe.Pointer(&k))
|
||||
|
||||
// Mask one word at a time.
|
||||
n := (len(b) / wordSize) * wordSize
|
||||
for i := 0; i < n; i += wordSize {
|
||||
*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw
|
||||
}
|
||||
|
||||
// Mask one byte at a time for remaining bytes.
|
||||
b = b[n:]
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
|
||||
return pos & 3
|
||||
}
|
||||
16
hw_service_go/vendor/github.com/gorilla/websocket/mask_safe.go
generated
vendored
Normal file
16
hw_service_go/vendor/github.com/gorilla/websocket/mask_safe.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in the
|
||||
// LICENSE file.
|
||||
|
||||
//go:build appengine
|
||||
// +build appengine
|
||||
|
||||
package websocket
|
||||
|
||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
return pos & 3
|
||||
}
|
||||
102
hw_service_go/vendor/github.com/gorilla/websocket/prepared.go
generated
vendored
Normal file
102
hw_service_go/vendor/github.com/gorilla/websocket/prepared.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PreparedMessage caches on the wire representations of a message payload.
|
||||
// Use PreparedMessage to efficiently send a message payload to multiple
|
||||
// connections. PreparedMessage is especially useful when compression is used
|
||||
// because the CPU and memory expensive compression operation can be executed
|
||||
// once for a given set of compression options.
|
||||
type PreparedMessage struct {
|
||||
messageType int
|
||||
data []byte
|
||||
mu sync.Mutex
|
||||
frames map[prepareKey]*preparedFrame
|
||||
}
|
||||
|
||||
// prepareKey defines a unique set of options to cache prepared frames in PreparedMessage.
|
||||
type prepareKey struct {
|
||||
isServer bool
|
||||
compress bool
|
||||
compressionLevel int
|
||||
}
|
||||
|
||||
// preparedFrame contains data in wire representation.
|
||||
type preparedFrame struct {
|
||||
once sync.Once
|
||||
data []byte
|
||||
}
|
||||
|
||||
// NewPreparedMessage returns an initialized PreparedMessage. You can then send
|
||||
// it to connection using WritePreparedMessage method. Valid wire
|
||||
// representation will be calculated lazily only once for a set of current
|
||||
// connection options.
|
||||
func NewPreparedMessage(messageType int, data []byte) (*PreparedMessage, error) {
|
||||
pm := &PreparedMessage{
|
||||
messageType: messageType,
|
||||
frames: make(map[prepareKey]*preparedFrame),
|
||||
data: data,
|
||||
}
|
||||
|
||||
// Prepare a plain server frame.
|
||||
_, frameData, err := pm.frame(prepareKey{isServer: true, compress: false})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// To protect against caller modifying the data argument, remember the data
|
||||
// copied to the plain server frame.
|
||||
pm.data = frameData[len(frameData)-len(data):]
|
||||
return pm, nil
|
||||
}
|
||||
|
||||
func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) {
|
||||
pm.mu.Lock()
|
||||
frame, ok := pm.frames[key]
|
||||
if !ok {
|
||||
frame = &preparedFrame{}
|
||||
pm.frames[key] = frame
|
||||
}
|
||||
pm.mu.Unlock()
|
||||
|
||||
var err error
|
||||
frame.once.Do(func() {
|
||||
// Prepare a frame using a 'fake' connection.
|
||||
// TODO: Refactor code in conn.go to allow more direct construction of
|
||||
// the frame.
|
||||
mu := make(chan struct{}, 1)
|
||||
mu <- struct{}{}
|
||||
var nc prepareConn
|
||||
c := &Conn{
|
||||
conn: &nc,
|
||||
mu: mu,
|
||||
isServer: key.isServer,
|
||||
compressionLevel: key.compressionLevel,
|
||||
enableWriteCompression: true,
|
||||
writeBuf: make([]byte, defaultWriteBufferSize+maxFrameHeaderSize),
|
||||
}
|
||||
if key.compress {
|
||||
c.newCompressionWriter = compressNoContextTakeover
|
||||
}
|
||||
err = c.WriteMessage(pm.messageType, pm.data)
|
||||
frame.data = nc.buf.Bytes()
|
||||
})
|
||||
return pm.messageType, frame.data, err
|
||||
}
|
||||
|
||||
type prepareConn struct {
|
||||
buf bytes.Buffer
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (pc *prepareConn) Write(p []byte) (int, error) { return pc.buf.Write(p) }
|
||||
func (pc *prepareConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
77
hw_service_go/vendor/github.com/gorilla/websocket/proxy.go
generated
vendored
Normal file
77
hw_service_go/vendor/github.com/gorilla/websocket/proxy.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright 2017 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type netDialerFunc func(network, addr string) (net.Conn, error)
|
||||
|
||||
func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) {
|
||||
return fn(network, addr)
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) {
|
||||
return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil
|
||||
})
|
||||
}
|
||||
|
||||
type httpProxyDialer struct {
|
||||
proxyURL *url.URL
|
||||
forwardDial func(network, addr string) (net.Conn, error)
|
||||
}
|
||||
|
||||
func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) {
|
||||
hostPort, _ := hostPortNoPort(hpd.proxyURL)
|
||||
conn, err := hpd.forwardDial(network, hostPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
connectHeader := make(http.Header)
|
||||
if user := hpd.proxyURL.User; user != nil {
|
||||
proxyUser := user.Username()
|
||||
if proxyPassword, passwordSet := user.Password(); passwordSet {
|
||||
credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
|
||||
connectHeader.Set("Proxy-Authorization", "Basic "+credential)
|
||||
}
|
||||
}
|
||||
|
||||
connectReq := &http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Opaque: addr},
|
||||
Host: addr,
|
||||
Header: connectHeader,
|
||||
}
|
||||
|
||||
if err := connectReq.Write(conn); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read response. It's OK to use and discard buffered reader here becaue
|
||||
// the remote server does not speak until spoken to.
|
||||
br := bufio.NewReader(conn)
|
||||
resp, err := http.ReadResponse(br, connectReq)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
conn.Close()
|
||||
f := strings.SplitN(resp.Status, " ", 2)
|
||||
return nil, errors.New(f[1])
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
365
hw_service_go/vendor/github.com/gorilla/websocket/server.go
generated
vendored
Normal file
365
hw_service_go/vendor/github.com/gorilla/websocket/server.go
generated
vendored
Normal file
@ -0,0 +1,365 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HandshakeError describes an error with the handshake from the peer.
|
||||
type HandshakeError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func (e HandshakeError) Error() string { return e.message }
|
||||
|
||||
// Upgrader specifies parameters for upgrading an HTTP connection to a
|
||||
// WebSocket connection.
|
||||
//
|
||||
// It is safe to call Upgrader's methods concurrently.
|
||||
type Upgrader struct {
|
||||
// HandshakeTimeout specifies the duration for the handshake to complete.
|
||||
HandshakeTimeout time.Duration
|
||||
|
||||
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer
|
||||
// size is zero, then buffers allocated by the HTTP server are used. The
|
||||
// I/O buffer sizes do not limit the size of the messages that can be sent
|
||||
// or received.
|
||||
ReadBufferSize, WriteBufferSize int
|
||||
|
||||
// WriteBufferPool is a pool of buffers for write operations. If the value
|
||||
// is not set, then write buffers are allocated to the connection for the
|
||||
// lifetime of the connection.
|
||||
//
|
||||
// A pool is most useful when the application has a modest volume of writes
|
||||
// across a large number of connections.
|
||||
//
|
||||
// Applications should use a single pool for each unique value of
|
||||
// WriteBufferSize.
|
||||
WriteBufferPool BufferPool
|
||||
|
||||
// Subprotocols specifies the server's supported protocols in order of
|
||||
// preference. If this field is not nil, then the Upgrade method negotiates a
|
||||
// subprotocol by selecting the first match in this list with a protocol
|
||||
// requested by the client. If there's no match, then no protocol is
|
||||
// negotiated (the Sec-Websocket-Protocol header is not included in the
|
||||
// handshake response).
|
||||
Subprotocols []string
|
||||
|
||||
// Error specifies the function for generating HTTP error responses. If Error
|
||||
// is nil, then http.Error is used to generate the HTTP response.
|
||||
Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
|
||||
|
||||
// CheckOrigin returns true if the request Origin header is acceptable. If
|
||||
// CheckOrigin is nil, then a safe default is used: return false if the
|
||||
// Origin request header is present and the origin host is not equal to
|
||||
// request Host header.
|
||||
//
|
||||
// A CheckOrigin function should carefully validate the request origin to
|
||||
// prevent cross-site request forgery.
|
||||
CheckOrigin func(r *http.Request) bool
|
||||
|
||||
// EnableCompression specify if the server should attempt to negotiate per
|
||||
// message compression (RFC 7692). Setting this value to true does not
|
||||
// guarantee that compression will be supported. Currently only "no context
|
||||
// takeover" modes are supported.
|
||||
EnableCompression bool
|
||||
}
|
||||
|
||||
func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {
|
||||
err := HandshakeError{reason}
|
||||
if u.Error != nil {
|
||||
u.Error(w, r, status, err)
|
||||
} else {
|
||||
w.Header().Set("Sec-Websocket-Version", "13")
|
||||
http.Error(w, http.StatusText(status), status)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// checkSameOrigin returns true if the origin is not set or is equal to the request host.
|
||||
func checkSameOrigin(r *http.Request) bool {
|
||||
origin := r.Header["Origin"]
|
||||
if len(origin) == 0 {
|
||||
return true
|
||||
}
|
||||
u, err := url.Parse(origin[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return equalASCIIFold(u.Host, r.Host)
|
||||
}
|
||||
|
||||
func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {
|
||||
if u.Subprotocols != nil {
|
||||
clientProtocols := Subprotocols(r)
|
||||
for _, serverProtocol := range u.Subprotocols {
|
||||
for _, clientProtocol := range clientProtocols {
|
||||
if clientProtocol == serverProtocol {
|
||||
return clientProtocol
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if responseHeader != nil {
|
||||
return responseHeader.Get("Sec-Websocket-Protocol")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
|
||||
//
|
||||
// The responseHeader is included in the response to the client's upgrade
|
||||
// request. Use the responseHeader to specify cookies (Set-Cookie). To specify
|
||||
// subprotocols supported by the server, set Upgrader.Subprotocols directly.
|
||||
//
|
||||
// If the upgrade fails, then Upgrade replies to the client with an HTTP error
|
||||
// response.
|
||||
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
|
||||
const badHandshake = "websocket: the client is not using the websocket protocol: "
|
||||
|
||||
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
|
||||
return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'upgrade' token not found in 'Connection' header")
|
||||
}
|
||||
|
||||
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
|
||||
return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header")
|
||||
}
|
||||
|
||||
if r.Method != http.MethodGet {
|
||||
return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"request method is not GET")
|
||||
}
|
||||
|
||||
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
|
||||
}
|
||||
|
||||
if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
|
||||
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported")
|
||||
}
|
||||
|
||||
checkOrigin := u.CheckOrigin
|
||||
if checkOrigin == nil {
|
||||
checkOrigin = checkSameOrigin
|
||||
}
|
||||
if !checkOrigin(r) {
|
||||
return u.returnError(w, r, http.StatusForbidden, "websocket: request origin not allowed by Upgrader.CheckOrigin")
|
||||
}
|
||||
|
||||
challengeKey := r.Header.Get("Sec-Websocket-Key")
|
||||
if !isValidChallengeKey(challengeKey) {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header must be Base64 encoded value of 16-byte in length")
|
||||
}
|
||||
|
||||
subprotocol := u.selectSubprotocol(r, responseHeader)
|
||||
|
||||
// Negotiate PMCE
|
||||
var compress bool
|
||||
if u.EnableCompression {
|
||||
for _, ext := range parseExtensions(r.Header) {
|
||||
if ext[""] != "permessage-deflate" {
|
||||
continue
|
||||
}
|
||||
compress = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
h, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
|
||||
}
|
||||
var brw *bufio.ReadWriter
|
||||
netConn, brw, err := h.Hijack()
|
||||
if err != nil {
|
||||
return u.returnError(w, r, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if brw.Reader.Buffered() > 0 {
|
||||
netConn.Close()
|
||||
return nil, errors.New("websocket: client sent data before handshake is complete")
|
||||
}
|
||||
|
||||
var br *bufio.Reader
|
||||
if u.ReadBufferSize == 0 && bufioReaderSize(netConn, brw.Reader) > 256 {
|
||||
// Reuse hijacked buffered reader as connection reader.
|
||||
br = brw.Reader
|
||||
}
|
||||
|
||||
buf := bufioWriterBuffer(netConn, brw.Writer)
|
||||
|
||||
var writeBuf []byte
|
||||
if u.WriteBufferPool == nil && u.WriteBufferSize == 0 && len(buf) >= maxFrameHeaderSize+256 {
|
||||
// Reuse hijacked write buffer as connection buffer.
|
||||
writeBuf = buf
|
||||
}
|
||||
|
||||
c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize, u.WriteBufferPool, br, writeBuf)
|
||||
c.subprotocol = subprotocol
|
||||
|
||||
if compress {
|
||||
c.newCompressionWriter = compressNoContextTakeover
|
||||
c.newDecompressionReader = decompressNoContextTakeover
|
||||
}
|
||||
|
||||
// Use larger of hijacked buffer and connection write buffer for header.
|
||||
p := buf
|
||||
if len(c.writeBuf) > len(p) {
|
||||
p = c.writeBuf
|
||||
}
|
||||
p = p[:0]
|
||||
|
||||
p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...)
|
||||
p = append(p, computeAcceptKey(challengeKey)...)
|
||||
p = append(p, "\r\n"...)
|
||||
if c.subprotocol != "" {
|
||||
p = append(p, "Sec-WebSocket-Protocol: "...)
|
||||
p = append(p, c.subprotocol...)
|
||||
p = append(p, "\r\n"...)
|
||||
}
|
||||
if compress {
|
||||
p = append(p, "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...)
|
||||
}
|
||||
for k, vs := range responseHeader {
|
||||
if k == "Sec-Websocket-Protocol" {
|
||||
continue
|
||||
}
|
||||
for _, v := range vs {
|
||||
p = append(p, k...)
|
||||
p = append(p, ": "...)
|
||||
for i := 0; i < len(v); i++ {
|
||||
b := v[i]
|
||||
if b <= 31 {
|
||||
// prevent response splitting.
|
||||
b = ' '
|
||||
}
|
||||
p = append(p, b)
|
||||
}
|
||||
p = append(p, "\r\n"...)
|
||||
}
|
||||
}
|
||||
p = append(p, "\r\n"...)
|
||||
|
||||
// Clear deadlines set by HTTP server.
|
||||
netConn.SetDeadline(time.Time{})
|
||||
|
||||
if u.HandshakeTimeout > 0 {
|
||||
netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout))
|
||||
}
|
||||
if _, err = netConn.Write(p); err != nil {
|
||||
netConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
if u.HandshakeTimeout > 0 {
|
||||
netConn.SetWriteDeadline(time.Time{})
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
|
||||
//
|
||||
// Deprecated: Use websocket.Upgrader instead.
|
||||
//
|
||||
// Upgrade does not perform origin checking. The application is responsible for
|
||||
// checking the Origin header before calling Upgrade. An example implementation
|
||||
// of the same origin policy check is:
|
||||
//
|
||||
// if req.Header.Get("Origin") != "http://"+req.Host {
|
||||
// http.Error(w, "Origin not allowed", http.StatusForbidden)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// If the endpoint supports subprotocols, then the application is responsible
|
||||
// for negotiating the protocol used on the connection. Use the Subprotocols()
|
||||
// function to get the subprotocols requested by the client. Use the
|
||||
// Sec-Websocket-Protocol response header to specify the subprotocol selected
|
||||
// by the application.
|
||||
//
|
||||
// The responseHeader is included in the response to the client's upgrade
|
||||
// request. Use the responseHeader to specify cookies (Set-Cookie) and the
|
||||
// negotiated subprotocol (Sec-Websocket-Protocol).
|
||||
//
|
||||
// The connection buffers IO to the underlying network connection. The
|
||||
// readBufSize and writeBufSize parameters specify the size of the buffers to
|
||||
// use. Messages can be larger than the buffers.
|
||||
//
|
||||
// If the request is not a valid WebSocket handshake, then Upgrade returns an
|
||||
// error of type HandshakeError. Applications should handle this error by
|
||||
// replying to the client with an HTTP error response.
|
||||
func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) {
|
||||
u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize}
|
||||
u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) {
|
||||
// don't return errors to maintain backwards compatibility
|
||||
}
|
||||
u.CheckOrigin = func(r *http.Request) bool {
|
||||
// allow all connections by default
|
||||
return true
|
||||
}
|
||||
return u.Upgrade(w, r, responseHeader)
|
||||
}
|
||||
|
||||
// Subprotocols returns the subprotocols requested by the client in the
|
||||
// Sec-Websocket-Protocol header.
|
||||
func Subprotocols(r *http.Request) []string {
|
||||
h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol"))
|
||||
if h == "" {
|
||||
return nil
|
||||
}
|
||||
protocols := strings.Split(h, ",")
|
||||
for i := range protocols {
|
||||
protocols[i] = strings.TrimSpace(protocols[i])
|
||||
}
|
||||
return protocols
|
||||
}
|
||||
|
||||
// IsWebSocketUpgrade returns true if the client requested upgrade to the
|
||||
// WebSocket protocol.
|
||||
func IsWebSocketUpgrade(r *http.Request) bool {
|
||||
return tokenListContainsValue(r.Header, "Connection", "upgrade") &&
|
||||
tokenListContainsValue(r.Header, "Upgrade", "websocket")
|
||||
}
|
||||
|
||||
// bufioReaderSize size returns the size of a bufio.Reader.
|
||||
func bufioReaderSize(originalReader io.Reader, br *bufio.Reader) int {
|
||||
// This code assumes that peek on a reset reader returns
|
||||
// bufio.Reader.buf[:0].
|
||||
// TODO: Use bufio.Reader.Size() after Go 1.10
|
||||
br.Reset(originalReader)
|
||||
if p, err := br.Peek(0); err == nil {
|
||||
return cap(p)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// writeHook is an io.Writer that records the last slice passed to it vio
|
||||
// io.Writer.Write.
|
||||
type writeHook struct {
|
||||
p []byte
|
||||
}
|
||||
|
||||
func (wh *writeHook) Write(p []byte) (int, error) {
|
||||
wh.p = p
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// bufioWriterBuffer grabs the buffer from a bufio.Writer.
|
||||
func bufioWriterBuffer(originalWriter io.Writer, bw *bufio.Writer) []byte {
|
||||
// This code assumes that bufio.Writer.buf[:1] is passed to the
|
||||
// bufio.Writer's underlying writer.
|
||||
var wh writeHook
|
||||
bw.Reset(&wh)
|
||||
bw.WriteByte(0)
|
||||
bw.Flush()
|
||||
|
||||
bw.Reset(originalWriter)
|
||||
|
||||
return wh.p[:cap(wh.p)]
|
||||
}
|
||||
21
hw_service_go/vendor/github.com/gorilla/websocket/tls_handshake.go
generated
vendored
Normal file
21
hw_service_go/vendor/github.com/gorilla/websocket/tls_handshake.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
//go:build go1.17
|
||||
// +build go1.17
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
func doHandshake(ctx context.Context, tlsConn *tls.Conn, cfg *tls.Config) error {
|
||||
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
if !cfg.InsecureSkipVerify {
|
||||
if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
21
hw_service_go/vendor/github.com/gorilla/websocket/tls_handshake_116.go
generated
vendored
Normal file
21
hw_service_go/vendor/github.com/gorilla/websocket/tls_handshake_116.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
//go:build !go1.17
|
||||
// +build !go1.17
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
)
|
||||
|
||||
func doHandshake(ctx context.Context, tlsConn *tls.Conn, cfg *tls.Config) error {
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
if !cfg.InsecureSkipVerify {
|
||||
if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
298
hw_service_go/vendor/github.com/gorilla/websocket/util.go
generated
vendored
Normal file
298
hw_service_go/vendor/github.com/gorilla/websocket/util.go
generated
vendored
Normal file
@ -0,0 +1,298 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||
|
||||
func computeAcceptKey(challengeKey string) string {
|
||||
h := sha1.New()
|
||||
h.Write([]byte(challengeKey))
|
||||
h.Write(keyGUID)
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func generateChallengeKey() (string, error) {
|
||||
p := make([]byte, 16)
|
||||
if _, err := io.ReadFull(rand.Reader, p); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(p), nil
|
||||
}
|
||||
|
||||
// Token octets per RFC 2616.
|
||||
var isTokenOctet = [256]bool{
|
||||
'!': true,
|
||||
'#': true,
|
||||
'$': true,
|
||||
'%': true,
|
||||
'&': true,
|
||||
'\'': true,
|
||||
'*': true,
|
||||
'+': true,
|
||||
'-': true,
|
||||
'.': true,
|
||||
'0': true,
|
||||
'1': true,
|
||||
'2': true,
|
||||
'3': true,
|
||||
'4': true,
|
||||
'5': true,
|
||||
'6': true,
|
||||
'7': true,
|
||||
'8': true,
|
||||
'9': true,
|
||||
'A': true,
|
||||
'B': true,
|
||||
'C': true,
|
||||
'D': true,
|
||||
'E': true,
|
||||
'F': true,
|
||||
'G': true,
|
||||
'H': true,
|
||||
'I': true,
|
||||
'J': true,
|
||||
'K': true,
|
||||
'L': true,
|
||||
'M': true,
|
||||
'N': true,
|
||||
'O': true,
|
||||
'P': true,
|
||||
'Q': true,
|
||||
'R': true,
|
||||
'S': true,
|
||||
'T': true,
|
||||
'U': true,
|
||||
'W': true,
|
||||
'V': true,
|
||||
'X': true,
|
||||
'Y': true,
|
||||
'Z': true,
|
||||
'^': true,
|
||||
'_': true,
|
||||
'`': true,
|
||||
'a': true,
|
||||
'b': true,
|
||||
'c': true,
|
||||
'd': true,
|
||||
'e': true,
|
||||
'f': true,
|
||||
'g': true,
|
||||
'h': true,
|
||||
'i': true,
|
||||
'j': true,
|
||||
'k': true,
|
||||
'l': true,
|
||||
'm': true,
|
||||
'n': true,
|
||||
'o': true,
|
||||
'p': true,
|
||||
'q': true,
|
||||
'r': true,
|
||||
's': true,
|
||||
't': true,
|
||||
'u': true,
|
||||
'v': true,
|
||||
'w': true,
|
||||
'x': true,
|
||||
'y': true,
|
||||
'z': true,
|
||||
'|': true,
|
||||
'~': true,
|
||||
}
|
||||
|
||||
// skipSpace returns a slice of the string s with all leading RFC 2616 linear
|
||||
// whitespace removed.
|
||||
func skipSpace(s string) (rest string) {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
if b := s[i]; b != ' ' && b != '\t' {
|
||||
break
|
||||
}
|
||||
}
|
||||
return s[i:]
|
||||
}
|
||||
|
||||
// nextToken returns the leading RFC 2616 token of s and the string following
|
||||
// the token.
|
||||
func nextToken(s string) (token, rest string) {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
if !isTokenOctet[s[i]] {
|
||||
break
|
||||
}
|
||||
}
|
||||
return s[:i], s[i:]
|
||||
}
|
||||
|
||||
// nextTokenOrQuoted returns the leading token or quoted string per RFC 2616
|
||||
// and the string following the token or quoted string.
|
||||
func nextTokenOrQuoted(s string) (value string, rest string) {
|
||||
if !strings.HasPrefix(s, "\"") {
|
||||
return nextToken(s)
|
||||
}
|
||||
s = s[1:]
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch s[i] {
|
||||
case '"':
|
||||
return s[:i], s[i+1:]
|
||||
case '\\':
|
||||
p := make([]byte, len(s)-1)
|
||||
j := copy(p, s[:i])
|
||||
escape := true
|
||||
for i = i + 1; i < len(s); i++ {
|
||||
b := s[i]
|
||||
switch {
|
||||
case escape:
|
||||
escape = false
|
||||
p[j] = b
|
||||
j++
|
||||
case b == '\\':
|
||||
escape = true
|
||||
case b == '"':
|
||||
return string(p[:j]), s[i+1:]
|
||||
default:
|
||||
p[j] = b
|
||||
j++
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// equalASCIIFold returns true if s is equal to t with ASCII case folding as
|
||||
// defined in RFC 4790.
|
||||
func equalASCIIFold(s, t string) bool {
|
||||
for s != "" && t != "" {
|
||||
sr, size := utf8.DecodeRuneInString(s)
|
||||
s = s[size:]
|
||||
tr, size := utf8.DecodeRuneInString(t)
|
||||
t = t[size:]
|
||||
if sr == tr {
|
||||
continue
|
||||
}
|
||||
if 'A' <= sr && sr <= 'Z' {
|
||||
sr = sr + 'a' - 'A'
|
||||
}
|
||||
if 'A' <= tr && tr <= 'Z' {
|
||||
tr = tr + 'a' - 'A'
|
||||
}
|
||||
if sr != tr {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return s == t
|
||||
}
|
||||
|
||||
// tokenListContainsValue returns true if the 1#token header with the given
|
||||
// name contains a token equal to value with ASCII case folding.
|
||||
func tokenListContainsValue(header http.Header, name string, value string) bool {
|
||||
headers:
|
||||
for _, s := range header[name] {
|
||||
for {
|
||||
var t string
|
||||
t, s = nextToken(skipSpace(s))
|
||||
if t == "" {
|
||||
continue headers
|
||||
}
|
||||
s = skipSpace(s)
|
||||
if s != "" && s[0] != ',' {
|
||||
continue headers
|
||||
}
|
||||
if equalASCIIFold(t, value) {
|
||||
return true
|
||||
}
|
||||
if s == "" {
|
||||
continue headers
|
||||
}
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// parseExtensions parses WebSocket extensions from a header.
|
||||
func parseExtensions(header http.Header) []map[string]string {
|
||||
// From RFC 6455:
|
||||
//
|
||||
// Sec-WebSocket-Extensions = extension-list
|
||||
// extension-list = 1#extension
|
||||
// extension = extension-token *( ";" extension-param )
|
||||
// extension-token = registered-token
|
||||
// registered-token = token
|
||||
// extension-param = token [ "=" (token | quoted-string) ]
|
||||
// ;When using the quoted-string syntax variant, the value
|
||||
// ;after quoted-string unescaping MUST conform to the
|
||||
// ;'token' ABNF.
|
||||
|
||||
var result []map[string]string
|
||||
headers:
|
||||
for _, s := range header["Sec-Websocket-Extensions"] {
|
||||
for {
|
||||
var t string
|
||||
t, s = nextToken(skipSpace(s))
|
||||
if t == "" {
|
||||
continue headers
|
||||
}
|
||||
ext := map[string]string{"": t}
|
||||
for {
|
||||
s = skipSpace(s)
|
||||
if !strings.HasPrefix(s, ";") {
|
||||
break
|
||||
}
|
||||
var k string
|
||||
k, s = nextToken(skipSpace(s[1:]))
|
||||
if k == "" {
|
||||
continue headers
|
||||
}
|
||||
s = skipSpace(s)
|
||||
var v string
|
||||
if strings.HasPrefix(s, "=") {
|
||||
v, s = nextTokenOrQuoted(skipSpace(s[1:]))
|
||||
s = skipSpace(s)
|
||||
}
|
||||
if s != "" && s[0] != ',' && s[0] != ';' {
|
||||
continue headers
|
||||
}
|
||||
ext[k] = v
|
||||
}
|
||||
if s != "" && s[0] != ',' {
|
||||
continue headers
|
||||
}
|
||||
result = append(result, ext)
|
||||
if s == "" {
|
||||
continue headers
|
||||
}
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// isValidChallengeKey checks if the argument meets RFC6455 specification.
|
||||
func isValidChallengeKey(s string) bool {
|
||||
// From RFC6455:
|
||||
//
|
||||
// A |Sec-WebSocket-Key| header field with a base64-encoded (see
|
||||
// Section 4 of [RFC4648]) value that, when decoded, is 16 bytes in
|
||||
// length.
|
||||
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
decoded, err := base64.StdEncoding.DecodeString(s)
|
||||
return err == nil && len(decoded) == 16
|
||||
}
|
||||
473
hw_service_go/vendor/github.com/gorilla/websocket/x_net_proxy.go
generated
vendored
Normal file
473
hw_service_go/vendor/github.com/gorilla/websocket/x_net_proxy.go
generated
vendored
Normal file
@ -0,0 +1,473 @@
|
||||
// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.
|
||||
//go:generate bundle -o x_net_proxy.go golang.org/x/net/proxy
|
||||
|
||||
// Package proxy provides support for a variety of protocols to proxy network
|
||||
// data.
|
||||
//
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type proxy_direct struct{}
|
||||
|
||||
// Direct is a direct proxy: one that makes network connections directly.
|
||||
var proxy_Direct = proxy_direct{}
|
||||
|
||||
func (proxy_direct) Dial(network, addr string) (net.Conn, error) {
|
||||
return net.Dial(network, addr)
|
||||
}
|
||||
|
||||
// A PerHost directs connections to a default Dialer unless the host name
|
||||
// requested matches one of a number of exceptions.
|
||||
type proxy_PerHost struct {
|
||||
def, bypass proxy_Dialer
|
||||
|
||||
bypassNetworks []*net.IPNet
|
||||
bypassIPs []net.IP
|
||||
bypassZones []string
|
||||
bypassHosts []string
|
||||
}
|
||||
|
||||
// NewPerHost returns a PerHost Dialer that directs connections to either
|
||||
// defaultDialer or bypass, depending on whether the connection matches one of
|
||||
// the configured rules.
|
||||
func proxy_NewPerHost(defaultDialer, bypass proxy_Dialer) *proxy_PerHost {
|
||||
return &proxy_PerHost{
|
||||
def: defaultDialer,
|
||||
bypass: bypass,
|
||||
}
|
||||
}
|
||||
|
||||
// Dial connects to the address addr on the given network through either
|
||||
// defaultDialer or bypass.
|
||||
func (p *proxy_PerHost) Dial(network, addr string) (c net.Conn, err error) {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.dialerForRequest(host).Dial(network, addr)
|
||||
}
|
||||
|
||||
func (p *proxy_PerHost) dialerForRequest(host string) proxy_Dialer {
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
for _, net := range p.bypassNetworks {
|
||||
if net.Contains(ip) {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
for _, bypassIP := range p.bypassIPs {
|
||||
if bypassIP.Equal(ip) {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
return p.def
|
||||
}
|
||||
|
||||
for _, zone := range p.bypassZones {
|
||||
if strings.HasSuffix(host, zone) {
|
||||
return p.bypass
|
||||
}
|
||||
if host == zone[1:] {
|
||||
// For a zone ".example.com", we match "example.com"
|
||||
// too.
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
for _, bypassHost := range p.bypassHosts {
|
||||
if bypassHost == host {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
return p.def
|
||||
}
|
||||
|
||||
// AddFromString parses a string that contains comma-separated values
|
||||
// specifying hosts that should use the bypass proxy. Each value is either an
|
||||
// IP address, a CIDR range, a zone (*.example.com) or a host name
|
||||
// (localhost). A best effort is made to parse the string and errors are
|
||||
// ignored.
|
||||
func (p *proxy_PerHost) AddFromString(s string) {
|
||||
hosts := strings.Split(s, ",")
|
||||
for _, host := range hosts {
|
||||
host = strings.TrimSpace(host)
|
||||
if len(host) == 0 {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(host, "/") {
|
||||
// We assume that it's a CIDR address like 127.0.0.0/8
|
||||
if _, net, err := net.ParseCIDR(host); err == nil {
|
||||
p.AddNetwork(net)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
p.AddIP(ip)
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(host, "*.") {
|
||||
p.AddZone(host[1:])
|
||||
continue
|
||||
}
|
||||
p.AddHost(host)
|
||||
}
|
||||
}
|
||||
|
||||
// AddIP specifies an IP address that will use the bypass proxy. Note that
|
||||
// this will only take effect if a literal IP address is dialed. A connection
|
||||
// to a named host will never match an IP.
|
||||
func (p *proxy_PerHost) AddIP(ip net.IP) {
|
||||
p.bypassIPs = append(p.bypassIPs, ip)
|
||||
}
|
||||
|
||||
// AddNetwork specifies an IP range that will use the bypass proxy. Note that
|
||||
// this will only take effect if a literal IP address is dialed. A connection
|
||||
// to a named host will never match.
|
||||
func (p *proxy_PerHost) AddNetwork(net *net.IPNet) {
|
||||
p.bypassNetworks = append(p.bypassNetworks, net)
|
||||
}
|
||||
|
||||
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
|
||||
// "example.com" matches "example.com" and all of its subdomains.
|
||||
func (p *proxy_PerHost) AddZone(zone string) {
|
||||
if strings.HasSuffix(zone, ".") {
|
||||
zone = zone[:len(zone)-1]
|
||||
}
|
||||
if !strings.HasPrefix(zone, ".") {
|
||||
zone = "." + zone
|
||||
}
|
||||
p.bypassZones = append(p.bypassZones, zone)
|
||||
}
|
||||
|
||||
// AddHost specifies a host name that will use the bypass proxy.
|
||||
func (p *proxy_PerHost) AddHost(host string) {
|
||||
if strings.HasSuffix(host, ".") {
|
||||
host = host[:len(host)-1]
|
||||
}
|
||||
p.bypassHosts = append(p.bypassHosts, host)
|
||||
}
|
||||
|
||||
// A Dialer is a means to establish a connection.
|
||||
type proxy_Dialer interface {
|
||||
// Dial connects to the given address via the proxy.
|
||||
Dial(network, addr string) (c net.Conn, err error)
|
||||
}
|
||||
|
||||
// Auth contains authentication parameters that specific Dialers may require.
|
||||
type proxy_Auth struct {
|
||||
User, Password string
|
||||
}
|
||||
|
||||
// FromEnvironment returns the dialer specified by the proxy related variables in
|
||||
// the environment.
|
||||
func proxy_FromEnvironment() proxy_Dialer {
|
||||
allProxy := proxy_allProxyEnv.Get()
|
||||
if len(allProxy) == 0 {
|
||||
return proxy_Direct
|
||||
}
|
||||
|
||||
proxyURL, err := url.Parse(allProxy)
|
||||
if err != nil {
|
||||
return proxy_Direct
|
||||
}
|
||||
proxy, err := proxy_FromURL(proxyURL, proxy_Direct)
|
||||
if err != nil {
|
||||
return proxy_Direct
|
||||
}
|
||||
|
||||
noProxy := proxy_noProxyEnv.Get()
|
||||
if len(noProxy) == 0 {
|
||||
return proxy
|
||||
}
|
||||
|
||||
perHost := proxy_NewPerHost(proxy, proxy_Direct)
|
||||
perHost.AddFromString(noProxy)
|
||||
return perHost
|
||||
}
|
||||
|
||||
// proxySchemes is a map from URL schemes to a function that creates a Dialer
|
||||
// from a URL with such a scheme.
|
||||
var proxy_proxySchemes map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error)
|
||||
|
||||
// RegisterDialerType takes a URL scheme and a function to generate Dialers from
|
||||
// a URL with that scheme and a forwarding Dialer. Registered schemes are used
|
||||
// by FromURL.
|
||||
func proxy_RegisterDialerType(scheme string, f func(*url.URL, proxy_Dialer) (proxy_Dialer, error)) {
|
||||
if proxy_proxySchemes == nil {
|
||||
proxy_proxySchemes = make(map[string]func(*url.URL, proxy_Dialer) (proxy_Dialer, error))
|
||||
}
|
||||
proxy_proxySchemes[scheme] = f
|
||||
}
|
||||
|
||||
// FromURL returns a Dialer given a URL specification and an underlying
|
||||
// Dialer for it to make network requests.
|
||||
func proxy_FromURL(u *url.URL, forward proxy_Dialer) (proxy_Dialer, error) {
|
||||
var auth *proxy_Auth
|
||||
if u.User != nil {
|
||||
auth = new(proxy_Auth)
|
||||
auth.User = u.User.Username()
|
||||
if p, ok := u.User.Password(); ok {
|
||||
auth.Password = p
|
||||
}
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "socks5":
|
||||
return proxy_SOCKS5("tcp", u.Host, auth, forward)
|
||||
}
|
||||
|
||||
// If the scheme doesn't match any of the built-in schemes, see if it
|
||||
// was registered by another package.
|
||||
if proxy_proxySchemes != nil {
|
||||
if f, ok := proxy_proxySchemes[u.Scheme]; ok {
|
||||
return f(u, forward)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
|
||||
}
|
||||
|
||||
var (
|
||||
proxy_allProxyEnv = &proxy_envOnce{
|
||||
names: []string{"ALL_PROXY", "all_proxy"},
|
||||
}
|
||||
proxy_noProxyEnv = &proxy_envOnce{
|
||||
names: []string{"NO_PROXY", "no_proxy"},
|
||||
}
|
||||
)
|
||||
|
||||
// envOnce looks up an environment variable (optionally by multiple
|
||||
// names) once. It mitigates expensive lookups on some platforms
|
||||
// (e.g. Windows).
|
||||
// (Borrowed from net/http/transport.go)
|
||||
type proxy_envOnce struct {
|
||||
names []string
|
||||
once sync.Once
|
||||
val string
|
||||
}
|
||||
|
||||
func (e *proxy_envOnce) Get() string {
|
||||
e.once.Do(e.init)
|
||||
return e.val
|
||||
}
|
||||
|
||||
func (e *proxy_envOnce) init() {
|
||||
for _, n := range e.names {
|
||||
e.val = os.Getenv(n)
|
||||
if e.val != "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address
|
||||
// with an optional username and password. See RFC 1928 and RFC 1929.
|
||||
func proxy_SOCKS5(network, addr string, auth *proxy_Auth, forward proxy_Dialer) (proxy_Dialer, error) {
|
||||
s := &proxy_socks5{
|
||||
network: network,
|
||||
addr: addr,
|
||||
forward: forward,
|
||||
}
|
||||
if auth != nil {
|
||||
s.user = auth.User
|
||||
s.password = auth.Password
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type proxy_socks5 struct {
|
||||
user, password string
|
||||
network, addr string
|
||||
forward proxy_Dialer
|
||||
}
|
||||
|
||||
const proxy_socks5Version = 5
|
||||
|
||||
const (
|
||||
proxy_socks5AuthNone = 0
|
||||
proxy_socks5AuthPassword = 2
|
||||
)
|
||||
|
||||
const proxy_socks5Connect = 1
|
||||
|
||||
const (
|
||||
proxy_socks5IP4 = 1
|
||||
proxy_socks5Domain = 3
|
||||
proxy_socks5IP6 = 4
|
||||
)
|
||||
|
||||
var proxy_socks5Errors = []string{
|
||||
"",
|
||||
"general failure",
|
||||
"connection forbidden",
|
||||
"network unreachable",
|
||||
"host unreachable",
|
||||
"connection refused",
|
||||
"TTL expired",
|
||||
"command not supported",
|
||||
"address type not supported",
|
||||
}
|
||||
|
||||
// Dial connects to the address addr on the given network via the SOCKS5 proxy.
|
||||
func (s *proxy_socks5) Dial(network, addr string) (net.Conn, error) {
|
||||
switch network {
|
||||
case "tcp", "tcp6", "tcp4":
|
||||
default:
|
||||
return nil, errors.New("proxy: no support for SOCKS5 proxy connections of type " + network)
|
||||
}
|
||||
|
||||
conn, err := s.forward.Dial(s.network, s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.connect(conn, addr); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// connect takes an existing connection to a socks5 proxy server,
|
||||
// and commands the server to extend that connection to target,
|
||||
// which must be a canonical address with a host and port.
|
||||
func (s *proxy_socks5) connect(conn net.Conn, target string) error {
|
||||
host, portStr, err := net.SplitHostPort(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to parse port number: " + portStr)
|
||||
}
|
||||
if port < 1 || port > 0xffff {
|
||||
return errors.New("proxy: port number out of range: " + portStr)
|
||||
}
|
||||
|
||||
// the size here is just an estimate
|
||||
buf := make([]byte, 0, 6+len(host))
|
||||
|
||||
buf = append(buf, proxy_socks5Version)
|
||||
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
|
||||
buf = append(buf, 2 /* num auth methods */, proxy_socks5AuthNone, proxy_socks5AuthPassword)
|
||||
} else {
|
||||
buf = append(buf, 1 /* num auth methods */, proxy_socks5AuthNone)
|
||||
}
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
if buf[0] != 5 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
if buf[1] == 0xff {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
||||
}
|
||||
|
||||
// See RFC 1929
|
||||
if buf[1] == proxy_socks5AuthPassword {
|
||||
buf = buf[:0]
|
||||
buf = append(buf, 1 /* password protocol version */)
|
||||
buf = append(buf, uint8(len(s.user)))
|
||||
buf = append(buf, s.user...)
|
||||
buf = append(buf, uint8(len(s.password)))
|
||||
buf = append(buf, s.password...)
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if buf[1] != 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
||||
}
|
||||
}
|
||||
|
||||
buf = buf[:0]
|
||||
buf = append(buf, proxy_socks5Version, proxy_socks5Connect, 0 /* reserved */)
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
buf = append(buf, proxy_socks5IP4)
|
||||
ip = ip4
|
||||
} else {
|
||||
buf = append(buf, proxy_socks5IP6)
|
||||
}
|
||||
buf = append(buf, ip...)
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return errors.New("proxy: destination host name too long: " + host)
|
||||
}
|
||||
buf = append(buf, proxy_socks5Domain)
|
||||
buf = append(buf, byte(len(host)))
|
||||
buf = append(buf, host...)
|
||||
}
|
||||
buf = append(buf, byte(port>>8), byte(port))
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
|
||||
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
failure := "unknown error"
|
||||
if int(buf[1]) < len(proxy_socks5Errors) {
|
||||
failure = proxy_socks5Errors[buf[1]]
|
||||
}
|
||||
|
||||
if len(failure) > 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
||||
}
|
||||
|
||||
bytesToDiscard := 0
|
||||
switch buf[3] {
|
||||
case proxy_socks5IP4:
|
||||
bytesToDiscard = net.IPv4len
|
||||
case proxy_socks5IP6:
|
||||
bytesToDiscard = net.IPv6len
|
||||
case proxy_socks5Domain:
|
||||
_, err := io.ReadFull(conn, buf[:1])
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
bytesToDiscard = int(buf[0])
|
||||
default:
|
||||
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
||||
}
|
||||
|
||||
if cap(buf) < bytesToDiscard {
|
||||
buf = make([]byte, bytesToDiscard)
|
||||
} else {
|
||||
buf = buf[:bytesToDiscard]
|
||||
}
|
||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
// Also need to discard the port number
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
20
hw_service_go/vendor/github.com/hraban/opus/.gitignore
generated
vendored
Normal file
20
hw_service_go/vendor/github.com/hraban/opus/.gitignore
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
# go noise
|
||||
*.6
|
||||
*.8
|
||||
*.o
|
||||
*.so
|
||||
*.out
|
||||
*.go~
|
||||
*.cgo?.*
|
||||
_cgo_*
|
||||
_obj
|
||||
_test
|
||||
_testmain.go
|
||||
*.test
|
||||
|
||||
# Vim noise
|
||||
*.swp
|
||||
|
||||
# Just noise
|
||||
*~
|
||||
*.orig
|
||||
12
hw_service_go/vendor/github.com/hraban/opus/AUTHORS
generated
vendored
Normal file
12
hw_service_go/vendor/github.com/hraban/opus/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
All code and content in this project is Copyright © 2015-2022 Go Opus Authors
|
||||
|
||||
Go Opus Authors and copyright holders of this package are listed below, in no
|
||||
particular order. By adding yourself to this list you agree to license your
|
||||
contributions under the relevant license (see the LICENSE file).
|
||||
|
||||
Hraban Luyat <hraban@0brg.net>
|
||||
Dejian Xu <xudejian2008@gmail.com>
|
||||
Tobias Wellnitz <tobias.wellnitz@gmail.com>
|
||||
Elinor Natanzon <stop.start.dev@gmail.com>
|
||||
Victor Gaydov <victor@enise.org>
|
||||
Randy Reddig <ydnar@shaderlab.com>
|
||||
19
hw_service_go/vendor/github.com/hraban/opus/LICENSE
generated
vendored
Normal file
19
hw_service_go/vendor/github.com/hraban/opus/LICENSE
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright © 2015-2022 Go Opus Authors (see AUTHORS file)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
302
hw_service_go/vendor/github.com/hraban/opus/README.md
generated
vendored
Normal file
302
hw_service_go/vendor/github.com/hraban/opus/README.md
generated
vendored
Normal file
@ -0,0 +1,302 @@
|
||||
[](https://github.com/hraban/opus/actions?query=workflow%3ATest)
|
||||
|
||||
## Go wrapper for Opus
|
||||
|
||||
This package provides Go bindings for the xiph.org C libraries libopus and
|
||||
libopusfile.
|
||||
|
||||
The C libraries and docs are hosted at https://opus-codec.org/. This package
|
||||
just handles the wrapping in Go, and is unaffiliated with xiph.org.
|
||||
|
||||
Features:
|
||||
|
||||
- ✅ encode and decode raw PCM data to raw Opus data
|
||||
- ✅ useful when you control the recording device, _and_ the playback
|
||||
- ✅ decode .opus and .ogg files into raw audio data ("PCM")
|
||||
- ✅ reuse the system libraries for opus decoding (libopus)
|
||||
- ✅ works easily on Linux, Mac and Docker; needs libs on Windows
|
||||
- ❌ does not _create_ .opus or .ogg files (but feel free to send a PR)
|
||||
- ❌ does not work with .wav files (you need a separate .wav library for that)
|
||||
- ❌ no self-contained binary (you need the xiph.org libopus lib, e.g. through a package manager)
|
||||
- ❌ no cross compiling (because it uses CGo)
|
||||
|
||||
Good use cases:
|
||||
|
||||
- 👍 you are writing a music player app in Go, and you want to play back .opus files
|
||||
- 👍 you record raw wav in a web app or mobile app, you encode it as Opus on the client, you send the opus to a remote webserver written in Go, and you want to decode it back to raw audio data on that server
|
||||
|
||||
## Details
|
||||
|
||||
This wrapper provides a Go translation layer for three elements from the
|
||||
xiph.org opus libs:
|
||||
|
||||
* encoders
|
||||
* decoders
|
||||
* files & streams
|
||||
|
||||
### Import
|
||||
|
||||
```go
|
||||
import "gopkg.in/hraban/opus.v2"
|
||||
```
|
||||
|
||||
### Encoding
|
||||
|
||||
To encode raw audio to the Opus format, create an encoder first:
|
||||
|
||||
```go
|
||||
const sampleRate = 48000
|
||||
const channels = 1 // mono; 2 for stereo
|
||||
|
||||
enc, err := opus.NewEncoder(sampleRate, channels, opus.AppVoIP)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Then pass it some raw PCM data to encode.
|
||||
|
||||
Make sure that the raw PCM data you want to encode has a legal Opus frame size.
|
||||
This means it must be exactly 2.5, 5, 10, 20, 40 or 60 ms long. The number of
|
||||
bytes this corresponds to depends on the sample rate (see the [libopus
|
||||
documentation](https://www.opus-codec.org/docs/opus_api-1.1.3/group__opus__encoder.html)).
|
||||
|
||||
```go
|
||||
var pcm []int16 = ... // obtain your raw PCM data somewhere
|
||||
const bufferSize = 1000 // choose any buffer size you like. 1k is plenty.
|
||||
|
||||
// Check the frame size. You don't need to do this if you trust your input.
|
||||
frameSize := len(pcm) // must be interleaved if stereo
|
||||
frameSizeMs := float32(frameSize) / channels * 1000 / sampleRate
|
||||
switch frameSizeMs {
|
||||
case 2.5, 5, 10, 20, 40, 60:
|
||||
// Good.
|
||||
default:
|
||||
return fmt.Errorf("Illegal frame size: %d bytes (%f ms)", frameSize, frameSizeMs)
|
||||
}
|
||||
|
||||
data := make([]byte, bufferSize)
|
||||
n, err := enc.Encode(pcm, data)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
data = data[:n] // only the first N bytes are opus data. Just like io.Reader.
|
||||
```
|
||||
|
||||
Note that you must choose a target buffer size, and this buffer size will affect
|
||||
the encoding process:
|
||||
|
||||
> Size of the allocated memory for the output payload. This may be used to
|
||||
> impose an upper limit on the instant bitrate, but should not be used as the
|
||||
> only bitrate control. Use `OPUS_SET_BITRATE` to control the bitrate.
|
||||
|
||||
-- https://opus-codec.org/docs/opus_api-1.1.3/group__opus__encoder.html
|
||||
|
||||
### Decoding
|
||||
|
||||
To decode opus data to raw PCM format, first create a decoder:
|
||||
|
||||
```go
|
||||
dec, err := opus.NewDecoder(sampleRate, channels)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Now pass it the opus bytes, and a buffer to store the PCM sound in:
|
||||
|
||||
```go
|
||||
var frameSizeMs float32 = ... // if you don't know, go with 60 ms.
|
||||
frameSize := channels * frameSizeMs * sampleRate / 1000
|
||||
pcm := make([]int16, int(frameSize))
|
||||
n, err := dec.Decode(data, pcm)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
// To get all samples (interleaved if multiple channels):
|
||||
pcm = pcm[:n*channels] // only necessary if you didn't know the right frame size
|
||||
|
||||
// or access sample per sample, directly:
|
||||
for i := 0; i < n; i++ {
|
||||
ch1 := pcm[i*channels+0]
|
||||
// For stereo output: copy ch1 into ch2 in mono mode, or deinterleave stereo
|
||||
ch2 := pcm[(i*channels)+(channels-1)]
|
||||
}
|
||||
```
|
||||
|
||||
To handle packet loss from an unreliable network, see the
|
||||
[DecodePLC](https://godoc.org/gopkg.in/hraban/opus.v2#Decoder.DecodePLC) and
|
||||
[DecodeFEC](https://godoc.org/gopkg.in/hraban/opus.v2#Decoder.DecodeFEC)
|
||||
options.
|
||||
|
||||
### Streams (and Files)
|
||||
|
||||
To decode a .opus file (or .ogg with Opus data), or to decode a "Opus stream"
|
||||
(which is a Ogg stream with Opus data), use the `Stream` interface. It wraps an
|
||||
io.Reader providing the raw stream bytes and returns the decoded Opus data.
|
||||
|
||||
A crude example for reading from a .opus file:
|
||||
|
||||
```go
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
s, err := opus.NewStream(f)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
defer s.Close()
|
||||
pcmbuf := make([]int16, 16384)
|
||||
for {
|
||||
n, err = s.Read(pcmbuf)
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
...
|
||||
}
|
||||
pcm := pcmbuf[:n*channels]
|
||||
|
||||
// send pcm to audio device here, or write to a .wav file
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
See https://godoc.org/gopkg.in/hraban/opus.v2#Stream for further info.
|
||||
|
||||
### "My .ogg/.opus file doesn't play!" or "How do I play Opus in VLC / mplayer / ...?"
|
||||
|
||||
Note: this package only does _encoding_ of your audio, to _raw opus data_. You can't just dump those all in one big file and play it back. You need extra info. First of all, you need to know how big each individual block is. Remember: opus data is a stream of encoded separate blocks, not one big stream of bytes. Second, you need meta-data: how many channels? What's the sampling rate? Frame size? Etc.
|
||||
|
||||
Look closely at the decoding sample code (not stream), above: we're passing all that meta-data in, hard-coded. If you just put all your encoded bytes in one big file and gave that to a media player, it wouldn't know what to do with it. It wouldn't even know that it's Opus data. It would just look like `/dev/random`.
|
||||
|
||||
What you need is a [container format](https://en.wikipedia.org/wiki/Container_format_(computing)).
|
||||
|
||||
Compare it to video:
|
||||
|
||||
* Encodings: MPEG[1234], VP9, H26[45], AV1
|
||||
* Container formats: .mkv, .avi, .mov, .ogv
|
||||
|
||||
For Opus audio, the most common container format is OGG, aka .ogg or .opus. You'll know OGG from OGG/Vorbis: that's [Vorbis](https://xiph.org/vorbis/) encoded audio in an OGG container. So for Opus, you'd call it OGG/Opus. But technically you could stick opus data in any container format that supports it, including e.g. Matroska (.mka for audio, you probably know it from .mkv for video).
|
||||
|
||||
Note: libopus, the C library that this wraps, technically comes with libopusfile, which can help with the creation of OGG/Opus streams from raw audio data. I just never needed it myself, so I haven't added the necessary code for it. If you find yourself adding it: send me a PR and we'll get it merged.
|
||||
|
||||
This libopus wrapper _does_ come with code for _decoding_ an OGG/Opus stream. Just not for writing one.
|
||||
|
||||
### API Docs
|
||||
|
||||
Go wrapper API reference:
|
||||
https://godoc.org/gopkg.in/hraban/opus.v2
|
||||
|
||||
Full libopus C API reference:
|
||||
https://www.opus-codec.org/docs/opus_api-1.1.3/
|
||||
|
||||
For more examples, see the `_test.go` files.
|
||||
|
||||
## Build & Installation
|
||||
|
||||
This package requires libopus and libopusfile development packages to be
|
||||
installed on your system. These are available on Debian based systems from
|
||||
aptitude as `libopus-dev` and `libopusfile-dev`, and on Mac OS X from homebrew.
|
||||
|
||||
They are linked into the app using pkg-config.
|
||||
|
||||
Debian, Ubuntu, ...:
|
||||
```sh
|
||||
sudo apt-get install pkg-config libopus-dev libopusfile-dev
|
||||
```
|
||||
|
||||
Mac:
|
||||
```sh
|
||||
brew install pkg-config opus opusfile
|
||||
```
|
||||
|
||||
### Building Without `libopusfile`
|
||||
|
||||
This package can be built without `libopusfile` by using the build tag `nolibopusfile`.
|
||||
This enables the compilation of statically-linked binaries with no external
|
||||
dependencies on operating systems without a static `libopusfile`, such as
|
||||
[Alpine Linux](https://pkgs.alpinelinux.org/contents?branch=edge&name=opusfile-dev&arch=x86_64&repo=main).
|
||||
|
||||
**Note:** this will disable all file and `Stream` APIs.
|
||||
|
||||
To enable this feature, add `-tags nolibopusfile` to your `go build` or `go test` commands:
|
||||
|
||||
```sh
|
||||
# Build
|
||||
go build -tags nolibopusfile ...
|
||||
|
||||
# Test
|
||||
go test -tags nolibopusfile ./...
|
||||
```
|
||||
|
||||
### Using in Docker
|
||||
|
||||
If your Dockerized app has this library as a dependency (directly or
|
||||
indirectly), it will need to install the aforementioned packages, too.
|
||||
|
||||
This means you can't use the standard `golang:*-onbuild` images, because those
|
||||
will try to build the app from source before allowing you to install extra
|
||||
dependencies. Instead, try this as a Dockerfile:
|
||||
|
||||
```Dockerfile
|
||||
# Choose any golang image, just make sure it doesn't have -onbuild
|
||||
FROM golang:1
|
||||
|
||||
RUN apt-get update && apt-get -y install libopus-dev libopusfile-dev
|
||||
|
||||
# Everything below is copied manually from the official -onbuild image,
|
||||
# with the ONBUILD keywords removed.
|
||||
|
||||
RUN mkdir -p /go/src/app
|
||||
WORKDIR /go/src/app
|
||||
|
||||
CMD ["go-wrapper", "run"]
|
||||
COPY . /go/src/app
|
||||
RUN go-wrapper download
|
||||
RUN go-wrapper install
|
||||
```
|
||||
|
||||
For more information, see <https://hub.docker.com/_/golang/>.
|
||||
|
||||
### Linking libopus and libopusfile
|
||||
|
||||
The opus and opusfile libraries will be linked into your application
|
||||
dynamically. This means everyone who uses the resulting binary will need those
|
||||
libraries available on their system. E.g. if you use this wrapper to write a
|
||||
music app in Go, everyone using that music app will need libopus and libopusfile
|
||||
on their system. On Debian systems the packages are called `libopus0` and
|
||||
`libopusfile0`.
|
||||
|
||||
The "cleanest" way to do this is to publish your software through a package
|
||||
manager and specify libopus and libopusfile as dependencies of your program. If
|
||||
that is not an option, you can compile the dynamic libraries yourself and ship
|
||||
them with your software as seperate (.dll or .so) files.
|
||||
|
||||
On Linux, for example, you would need the libopus.so.0 and libopusfile.so.0
|
||||
files in the same directory as the binary. Set your ELF binary's rpath to
|
||||
`$ORIGIN` (this is not a shell variable but elf magic):
|
||||
|
||||
```sh
|
||||
patchelf --set-origin '$ORIGIN' your-app-binary
|
||||
```
|
||||
|
||||
Now you can run the binary and it will automatically pick up shared library
|
||||
files from its own directory.
|
||||
|
||||
Wrap it all in a .zip, and ship.
|
||||
|
||||
I know there is a similar trick for Mac (involving prefixing the shared library
|
||||
names with `./`, which is, arguably, better). And Windows... probably just picks
|
||||
up .dll files from the same dir by default? I don't know. But there are ways.
|
||||
|
||||
## License
|
||||
|
||||
The licensing terms for the Go bindings are found in the LICENSE file. The
|
||||
authors and copyright holders are listed in the AUTHORS file.
|
||||
|
||||
The copyright notice uses range notation to indicate all years in between are
|
||||
subject to copyright, as well. This statement is necessary, apparently. For all
|
||||
those nefarious actors ready to abuse a copyright notice with incorrect
|
||||
notation, but thwarted by a mention in the README. Pfew!
|
||||
29
hw_service_go/vendor/github.com/hraban/opus/callbacks.c
generated
vendored
Normal file
29
hw_service_go/vendor/github.com/hraban/opus/callbacks.c
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
// +build !nolibopusfile
|
||||
|
||||
// Copyright © Go Opus Authors (see AUTHORS file)
|
||||
//
|
||||
// License for use of this code is detailed in the LICENSE file
|
||||
|
||||
// Allocate callback struct in C to ensure it's not managed by the Go GC. This
|
||||
// plays nice with the CGo rules and avoids any confusion.
|
||||
|
||||
#include <opusfile.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Defined in Go. Uses the same signature as Go, no need for proxy function.
|
||||
int go_readcallback(void *p, unsigned char *buf, int nbytes);
|
||||
|
||||
static struct OpusFileCallbacks callbacks = {
|
||||
.read = go_readcallback,
|
||||
};
|
||||
|
||||
// Proxy function for op_open_callbacks, because it takes a void * context but
|
||||
// we want to pass it non-pointer data, namely an arbitrary uintptr_t
|
||||
// value. This is legal C, but go test -race (-d=checkptr) complains anyway. So
|
||||
// we have this wrapper function to shush it.
|
||||
// https://groups.google.com/g/golang-nuts/c/995uZyRPKlU
|
||||
OggOpusFile *
|
||||
my_open_callbacks(uintptr_t p, int *error)
|
||||
{
|
||||
return op_open_callbacks((void *)p, &callbacks, NULL, 0, error);
|
||||
}
|
||||
262
hw_service_go/vendor/github.com/hraban/opus/decoder.go
generated
vendored
Normal file
262
hw_service_go/vendor/github.com/hraban/opus/decoder.go
generated
vendored
Normal file
@ -0,0 +1,262 @@
|
||||
// Copyright © Go Opus Authors (see AUTHORS file)
|
||||
//
|
||||
// License for use of this code is detailed in the LICENSE file
|
||||
|
||||
package opus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo pkg-config: opus
|
||||
#include <opus.h>
|
||||
|
||||
int
|
||||
bridge_decoder_get_last_packet_duration(OpusDecoder *st, opus_int32 *samples)
|
||||
{
|
||||
return opus_decoder_ctl(st, OPUS_GET_LAST_PACKET_DURATION(samples));
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
var errDecUninitialized = fmt.Errorf("opus decoder uninitialized")
|
||||
|
||||
type Decoder struct {
|
||||
p *C.struct_OpusDecoder
|
||||
// Same purpose as encoder struct
|
||||
mem []byte
|
||||
sample_rate int
|
||||
channels int
|
||||
}
|
||||
|
||||
// NewDecoder allocates a new Opus decoder and initializes it with the
|
||||
// appropriate parameters. All related memory is managed by the Go GC.
|
||||
func NewDecoder(sample_rate int, channels int) (*Decoder, error) {
|
||||
var dec Decoder
|
||||
err := dec.Init(sample_rate, channels)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dec, nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) Init(sample_rate int, channels int) error {
|
||||
if dec.p != nil {
|
||||
return fmt.Errorf("opus decoder already initialized")
|
||||
}
|
||||
if channels != 1 && channels != 2 {
|
||||
return fmt.Errorf("Number of channels must be 1 or 2: %d", channels)
|
||||
}
|
||||
size := C.opus_decoder_get_size(C.int(channels))
|
||||
dec.sample_rate = sample_rate
|
||||
dec.channels = channels
|
||||
dec.mem = make([]byte, size)
|
||||
dec.p = (*C.OpusDecoder)(unsafe.Pointer(&dec.mem[0]))
|
||||
errno := C.opus_decoder_init(
|
||||
dec.p,
|
||||
C.opus_int32(sample_rate),
|
||||
C.int(channels))
|
||||
if errno != 0 {
|
||||
return Error(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decode encoded Opus data into the supplied buffer. On success, returns the
|
||||
// number of samples correctly written to the target buffer.
|
||||
func (dec *Decoder) Decode(data []byte, pcm []int16) (int, error) {
|
||||
if dec.p == nil {
|
||||
return 0, errDecUninitialized
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return 0, fmt.Errorf("opus: no data supplied")
|
||||
}
|
||||
if len(pcm) == 0 {
|
||||
return 0, fmt.Errorf("opus: target buffer empty")
|
||||
}
|
||||
if cap(pcm)%dec.channels != 0 {
|
||||
return 0, fmt.Errorf("opus: target buffer capacity must be multiple of channels")
|
||||
}
|
||||
n := int(C.opus_decode(
|
||||
dec.p,
|
||||
(*C.uchar)(&data[0]),
|
||||
C.opus_int32(len(data)),
|
||||
(*C.opus_int16)(&pcm[0]),
|
||||
C.int(cap(pcm)/dec.channels),
|
||||
0))
|
||||
if n < 0 {
|
||||
return 0, Error(n)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Decode encoded Opus data into the supplied buffer. On success, returns the
|
||||
// number of samples correctly written to the target buffer.
|
||||
func (dec *Decoder) DecodeFloat32(data []byte, pcm []float32) (int, error) {
|
||||
if dec.p == nil {
|
||||
return 0, errDecUninitialized
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return 0, fmt.Errorf("opus: no data supplied")
|
||||
}
|
||||
if len(pcm) == 0 {
|
||||
return 0, fmt.Errorf("opus: target buffer empty")
|
||||
}
|
||||
if cap(pcm)%dec.channels != 0 {
|
||||
return 0, fmt.Errorf("opus: target buffer capacity must be multiple of channels")
|
||||
}
|
||||
n := int(C.opus_decode_float(
|
||||
dec.p,
|
||||
(*C.uchar)(&data[0]),
|
||||
C.opus_int32(len(data)),
|
||||
(*C.float)(&pcm[0]),
|
||||
C.int(cap(pcm)/dec.channels),
|
||||
0))
|
||||
if n < 0 {
|
||||
return 0, Error(n)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// DecodeFEC encoded Opus data into the supplied buffer with forward error
|
||||
// correction.
|
||||
//
|
||||
// It is to be used on the packet directly following the lost one. The supplied
|
||||
// buffer needs to be exactly the duration of audio that is missing
|
||||
//
|
||||
// When a packet is considered "lost", DecodeFEC can be called on the next
|
||||
// packet in order to try and recover some of the lost data. The PCM needs to be
|
||||
// exactly the duration of audio that is missing. `LastPacketDuration()` can be
|
||||
// used on the decoder to get the length of the last packet. Note also that in
|
||||
// order to use this feature the encoder needs to be configured with
|
||||
// SetInBandFEC(true) and SetPacketLossPerc(x) options.
|
||||
//
|
||||
// Note that DecodeFEC automatically falls back to PLC when no FEC data is
|
||||
// available in the provided packet.
|
||||
func (dec *Decoder) DecodeFEC(data []byte, pcm []int16) error {
|
||||
if dec.p == nil {
|
||||
return errDecUninitialized
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return fmt.Errorf("opus: no data supplied")
|
||||
}
|
||||
if len(pcm) == 0 {
|
||||
return fmt.Errorf("opus: target buffer empty")
|
||||
}
|
||||
if cap(pcm)%dec.channels != 0 {
|
||||
return fmt.Errorf("opus: target buffer capacity must be multiple of channels")
|
||||
}
|
||||
n := int(C.opus_decode(
|
||||
dec.p,
|
||||
(*C.uchar)(&data[0]),
|
||||
C.opus_int32(len(data)),
|
||||
(*C.opus_int16)(&pcm[0]),
|
||||
C.int(cap(pcm)/dec.channels),
|
||||
1))
|
||||
if n < 0 {
|
||||
return Error(n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeFECFloat32 encoded Opus data into the supplied buffer with forward error
|
||||
// correction. It is to be used on the packet directly following the lost one.
|
||||
// The supplied buffer needs to be exactly the duration of audio that is missing
|
||||
func (dec *Decoder) DecodeFECFloat32(data []byte, pcm []float32) error {
|
||||
if dec.p == nil {
|
||||
return errDecUninitialized
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return fmt.Errorf("opus: no data supplied")
|
||||
}
|
||||
if len(pcm) == 0 {
|
||||
return fmt.Errorf("opus: target buffer empty")
|
||||
}
|
||||
if cap(pcm)%dec.channels != 0 {
|
||||
return fmt.Errorf("opus: target buffer capacity must be multiple of channels")
|
||||
}
|
||||
n := int(C.opus_decode_float(
|
||||
dec.p,
|
||||
(*C.uchar)(&data[0]),
|
||||
C.opus_int32(len(data)),
|
||||
(*C.float)(&pcm[0]),
|
||||
C.int(cap(pcm)/dec.channels),
|
||||
1))
|
||||
if n < 0 {
|
||||
return Error(n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodePLC recovers a lost packet using Opus Packet Loss Concealment feature.
|
||||
//
|
||||
// The supplied buffer needs to be exactly the duration of audio that is missing.
|
||||
// When a packet is considered "lost", `DecodePLC` and `DecodePLCFloat32` methods
|
||||
// can be called in order to obtain something better sounding than just silence.
|
||||
// The PCM needs to be exactly the duration of audio that is missing.
|
||||
// `LastPacketDuration()` can be used on the decoder to get the length of the
|
||||
// last packet.
|
||||
//
|
||||
// This option does not require any additional encoder options. Unlike FEC,
|
||||
// PLC does not introduce additional latency. It is calculated from the previous
|
||||
// packet, not from the next one.
|
||||
func (dec *Decoder) DecodePLC(pcm []int16) error {
|
||||
if dec.p == nil {
|
||||
return errDecUninitialized
|
||||
}
|
||||
if len(pcm) == 0 {
|
||||
return fmt.Errorf("opus: target buffer empty")
|
||||
}
|
||||
if cap(pcm)%dec.channels != 0 {
|
||||
return fmt.Errorf("opus: output buffer capacity must be multiple of channels")
|
||||
}
|
||||
n := int(C.opus_decode(
|
||||
dec.p,
|
||||
nil,
|
||||
0,
|
||||
(*C.opus_int16)(&pcm[0]),
|
||||
C.int(cap(pcm)/dec.channels),
|
||||
0))
|
||||
if n < 0 {
|
||||
return Error(n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodePLCFloat32 recovers a lost packet using Opus Packet Loss Concealment feature.
|
||||
// The supplied buffer needs to be exactly the duration of audio that is missing.
|
||||
func (dec *Decoder) DecodePLCFloat32(pcm []float32) error {
|
||||
if dec.p == nil {
|
||||
return errDecUninitialized
|
||||
}
|
||||
if len(pcm) == 0 {
|
||||
return fmt.Errorf("opus: target buffer empty")
|
||||
}
|
||||
if cap(pcm)%dec.channels != 0 {
|
||||
return fmt.Errorf("opus: output buffer capacity must be multiple of channels")
|
||||
}
|
||||
n := int(C.opus_decode_float(
|
||||
dec.p,
|
||||
nil,
|
||||
0,
|
||||
(*C.float)(&pcm[0]),
|
||||
C.int(cap(pcm)/dec.channels),
|
||||
0))
|
||||
if n < 0 {
|
||||
return Error(n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LastPacketDuration gets the duration (in samples)
|
||||
// of the last packet successfully decoded or concealed.
|
||||
func (dec *Decoder) LastPacketDuration() (int, error) {
|
||||
var samples C.opus_int32
|
||||
res := C.bridge_decoder_get_last_packet_duration(dec.p, &samples)
|
||||
if res != C.OPUS_OK {
|
||||
return 0, Error(res)
|
||||
}
|
||||
return int(samples), nil
|
||||
}
|
||||
402
hw_service_go/vendor/github.com/hraban/opus/encoder.go
generated
vendored
Normal file
402
hw_service_go/vendor/github.com/hraban/opus/encoder.go
generated
vendored
Normal file
@ -0,0 +1,402 @@
|
||||
// Copyright © Go Opus Authors (see AUTHORS file)
|
||||
//
|
||||
// License for use of this code is detailed in the LICENSE file
|
||||
|
||||
package opus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo pkg-config: opus
|
||||
#include <opus.h>
|
||||
|
||||
int
|
||||
bridge_encoder_set_dtx(OpusEncoder *st, opus_int32 use_dtx)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_SET_DTX(use_dtx));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_dtx(OpusEncoder *st, opus_int32 *dtx)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_DTX(dtx));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_in_dtx(OpusEncoder *st, opus_int32 *in_dtx)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_IN_DTX(in_dtx));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_sample_rate(OpusEncoder *st, opus_int32 *sample_rate)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_SAMPLE_RATE(sample_rate));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
bridge_encoder_set_bitrate(OpusEncoder *st, opus_int32 bitrate)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_SET_BITRATE(bitrate));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_bitrate(OpusEncoder *st, opus_int32 *bitrate)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_BITRATE(bitrate));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_set_complexity(OpusEncoder *st, opus_int32 complexity)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_SET_COMPLEXITY(complexity));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_complexity(OpusEncoder *st, opus_int32 *complexity)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_COMPLEXITY(complexity));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_set_max_bandwidth(OpusEncoder *st, opus_int32 max_bw)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_SET_MAX_BANDWIDTH(max_bw));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_max_bandwidth(OpusEncoder *st, opus_int32 *max_bw)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_MAX_BANDWIDTH(max_bw));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_set_inband_fec(OpusEncoder *st, opus_int32 fec)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_SET_INBAND_FEC(fec));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_inband_fec(OpusEncoder *st, opus_int32 *fec)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_INBAND_FEC(fec));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_set_packet_loss_perc(OpusEncoder *st, opus_int32 loss_perc)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_SET_PACKET_LOSS_PERC(loss_perc));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_get_packet_loss_perc(OpusEncoder *st, opus_int32 *loss_perc)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_GET_PACKET_LOSS_PERC(loss_perc));
|
||||
}
|
||||
|
||||
int
|
||||
bridge_encoder_reset_state(OpusEncoder *st)
|
||||
{
|
||||
return opus_encoder_ctl(st, OPUS_RESET_STATE);
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type Bandwidth int
|
||||
|
||||
const (
|
||||
// 4 kHz passband
|
||||
Narrowband = Bandwidth(C.OPUS_BANDWIDTH_NARROWBAND)
|
||||
// 6 kHz passband
|
||||
Mediumband = Bandwidth(C.OPUS_BANDWIDTH_MEDIUMBAND)
|
||||
// 8 kHz passband
|
||||
Wideband = Bandwidth(C.OPUS_BANDWIDTH_WIDEBAND)
|
||||
// 12 kHz passband
|
||||
SuperWideband = Bandwidth(C.OPUS_BANDWIDTH_SUPERWIDEBAND)
|
||||
// 20 kHz passband
|
||||
Fullband = Bandwidth(C.OPUS_BANDWIDTH_FULLBAND)
|
||||
)
|
||||
|
||||
var errEncUninitialized = fmt.Errorf("opus encoder uninitialized")
|
||||
|
||||
// Encoder contains the state of an Opus encoder for libopus.
|
||||
type Encoder struct {
|
||||
p *C.struct_OpusEncoder
|
||||
channels int
|
||||
// Memory for the encoder struct allocated on the Go heap to allow Go GC to
|
||||
// manage it (and obviate need to free())
|
||||
mem []byte
|
||||
}
|
||||
|
||||
// NewEncoder allocates a new Opus encoder and initializes it with the
|
||||
// appropriate parameters. All related memory is managed by the Go GC.
|
||||
func NewEncoder(sample_rate int, channels int, application Application) (*Encoder, error) {
|
||||
var enc Encoder
|
||||
err := enc.Init(sample_rate, channels, application)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &enc, nil
|
||||
}
|
||||
|
||||
// Init initializes a pre-allocated opus encoder. Unless the encoder has been
|
||||
// created using NewEncoder, this method must be called exactly once in the
|
||||
// life-time of this object, before calling any other methods.
|
||||
func (enc *Encoder) Init(sample_rate int, channels int, application Application) error {
|
||||
if enc.p != nil {
|
||||
return fmt.Errorf("opus encoder already initialized")
|
||||
}
|
||||
if channels != 1 && channels != 2 {
|
||||
return fmt.Errorf("Number of channels must be 1 or 2: %d", channels)
|
||||
}
|
||||
size := C.opus_encoder_get_size(C.int(channels))
|
||||
enc.channels = channels
|
||||
enc.mem = make([]byte, size)
|
||||
enc.p = (*C.OpusEncoder)(unsafe.Pointer(&enc.mem[0]))
|
||||
errno := int(C.opus_encoder_init(
|
||||
enc.p,
|
||||
C.opus_int32(sample_rate),
|
||||
C.int(channels),
|
||||
C.int(application)))
|
||||
if errno != 0 {
|
||||
return Error(int(errno))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode raw PCM data and store the result in the supplied buffer. On success,
|
||||
// returns the number of bytes used up by the encoded data.
|
||||
func (enc *Encoder) Encode(pcm []int16, data []byte) (int, error) {
|
||||
if enc.p == nil {
|
||||
return 0, errEncUninitialized
|
||||
}
|
||||
if len(pcm) == 0 {
|
||||
return 0, fmt.Errorf("opus: no data supplied")
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return 0, fmt.Errorf("opus: no target buffer")
|
||||
}
|
||||
// libopus talks about samples as 1 sample containing multiple channels. So
|
||||
// e.g. 20 samples of 2-channel data is actually 40 raw data points.
|
||||
if len(pcm)%enc.channels != 0 {
|
||||
return 0, fmt.Errorf("opus: input buffer length must be multiple of channels")
|
||||
}
|
||||
samples := len(pcm) / enc.channels
|
||||
n := int(C.opus_encode(
|
||||
enc.p,
|
||||
(*C.opus_int16)(&pcm[0]),
|
||||
C.int(samples),
|
||||
(*C.uchar)(&data[0]),
|
||||
C.opus_int32(cap(data))))
|
||||
if n < 0 {
|
||||
return 0, Error(n)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Encode raw PCM data and store the result in the supplied buffer. On success,
|
||||
// returns the number of bytes used up by the encoded data.
|
||||
func (enc *Encoder) EncodeFloat32(pcm []float32, data []byte) (int, error) {
|
||||
if enc.p == nil {
|
||||
return 0, errEncUninitialized
|
||||
}
|
||||
if len(pcm) == 0 {
|
||||
return 0, fmt.Errorf("opus: no data supplied")
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return 0, fmt.Errorf("opus: no target buffer")
|
||||
}
|
||||
if len(pcm)%enc.channels != 0 {
|
||||
return 0, fmt.Errorf("opus: input buffer length must be multiple of channels")
|
||||
}
|
||||
samples := len(pcm) / enc.channels
|
||||
n := int(C.opus_encode_float(
|
||||
enc.p,
|
||||
(*C.float)(&pcm[0]),
|
||||
C.int(samples),
|
||||
(*C.uchar)(&data[0]),
|
||||
C.opus_int32(cap(data))))
|
||||
if n < 0 {
|
||||
return 0, Error(n)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// SetDTX configures the encoder's use of discontinuous transmission (DTX).
|
||||
func (enc *Encoder) SetDTX(dtx bool) error {
|
||||
i := 0
|
||||
if dtx {
|
||||
i = 1
|
||||
}
|
||||
res := C.bridge_encoder_set_dtx(enc.p, C.opus_int32(i))
|
||||
if res != C.OPUS_OK {
|
||||
return Error(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DTX reports whether this encoder is configured to use discontinuous
|
||||
// transmission (DTX).
|
||||
func (enc *Encoder) DTX() (bool, error) {
|
||||
var dtx C.opus_int32
|
||||
res := C.bridge_encoder_get_dtx(enc.p, &dtx)
|
||||
if res != C.OPUS_OK {
|
||||
return false, Error(res)
|
||||
}
|
||||
return dtx != 0, nil
|
||||
}
|
||||
|
||||
// InDTX returns whether the last encoded frame was either a comfort noise update
|
||||
// during DTX or not encoded because of DTX.
|
||||
func (enc *Encoder) InDTX() (bool, error) {
|
||||
var inDTX C.opus_int32
|
||||
res := C.bridge_encoder_get_in_dtx(enc.p, &inDTX)
|
||||
if res != C.OPUS_OK {
|
||||
return false, Error(res)
|
||||
}
|
||||
return inDTX != 0, nil
|
||||
}
|
||||
|
||||
// SampleRate returns the encoder sample rate in Hz.
|
||||
func (enc *Encoder) SampleRate() (int, error) {
|
||||
var sr C.opus_int32
|
||||
res := C.bridge_encoder_get_sample_rate(enc.p, &sr)
|
||||
if res != C.OPUS_OK {
|
||||
return 0, Error(res)
|
||||
}
|
||||
return int(sr), nil
|
||||
}
|
||||
|
||||
// SetBitrate sets the bitrate of the Encoder
|
||||
func (enc *Encoder) SetBitrate(bitrate int) error {
|
||||
res := C.bridge_encoder_set_bitrate(enc.p, C.opus_int32(bitrate))
|
||||
if res != C.OPUS_OK {
|
||||
return Error(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBitrateToAuto will allow the encoder to automatically set the bitrate
|
||||
func (enc *Encoder) SetBitrateToAuto() error {
|
||||
res := C.bridge_encoder_set_bitrate(enc.p, C.opus_int32(C.OPUS_AUTO))
|
||||
if res != C.OPUS_OK {
|
||||
return Error(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBitrateToMax causes the encoder to use as much rate as it can. This can be
|
||||
// useful for controlling the rate by adjusting the output buffer size.
|
||||
func (enc *Encoder) SetBitrateToMax() error {
|
||||
res := C.bridge_encoder_set_bitrate(enc.p, C.opus_int32(C.OPUS_BITRATE_MAX))
|
||||
if res != C.OPUS_OK {
|
||||
return Error(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bitrate returns the bitrate of the Encoder
|
||||
func (enc *Encoder) Bitrate() (int, error) {
|
||||
var bitrate C.opus_int32
|
||||
res := C.bridge_encoder_get_bitrate(enc.p, &bitrate)
|
||||
if res != C.OPUS_OK {
|
||||
return 0, Error(res)
|
||||
}
|
||||
return int(bitrate), nil
|
||||
}
|
||||
|
||||
// SetComplexity sets the encoder's computational complexity
|
||||
func (enc *Encoder) SetComplexity(complexity int) error {
|
||||
res := C.bridge_encoder_set_complexity(enc.p, C.opus_int32(complexity))
|
||||
if res != C.OPUS_OK {
|
||||
return Error(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Complexity returns the computational complexity used by the encoder
|
||||
func (enc *Encoder) Complexity() (int, error) {
|
||||
var complexity C.opus_int32
|
||||
res := C.bridge_encoder_get_complexity(enc.p, &complexity)
|
||||
if res != C.OPUS_OK {
|
||||
return 0, Error(res)
|
||||
}
|
||||
return int(complexity), nil
|
||||
}
|
||||
|
||||
// SetMaxBandwidth configures the maximum bandpass that the encoder will select
|
||||
// automatically
|
||||
func (enc *Encoder) SetMaxBandwidth(maxBw Bandwidth) error {
|
||||
res := C.bridge_encoder_set_max_bandwidth(enc.p, C.opus_int32(maxBw))
|
||||
if res != C.OPUS_OK {
|
||||
return Error(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MaxBandwidth gets the encoder's configured maximum allowed bandpass.
|
||||
func (enc *Encoder) MaxBandwidth() (Bandwidth, error) {
|
||||
var maxBw C.opus_int32
|
||||
res := C.bridge_encoder_get_max_bandwidth(enc.p, &maxBw)
|
||||
if res != C.OPUS_OK {
|
||||
return 0, Error(res)
|
||||
}
|
||||
return Bandwidth(maxBw), nil
|
||||
}
|
||||
|
||||
// SetInBandFEC configures the encoder's use of inband forward error
|
||||
// correction (FEC)
|
||||
func (enc *Encoder) SetInBandFEC(fec bool) error {
|
||||
i := 0
|
||||
if fec {
|
||||
i = 1
|
||||
}
|
||||
res := C.bridge_encoder_set_inband_fec(enc.p, C.opus_int32(i))
|
||||
if res != C.OPUS_OK {
|
||||
return Error(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InBandFEC gets the encoder's configured inband forward error correction (FEC)
|
||||
func (enc *Encoder) InBandFEC() (bool, error) {
|
||||
var fec C.opus_int32
|
||||
res := C.bridge_encoder_get_inband_fec(enc.p, &fec)
|
||||
if res != C.OPUS_OK {
|
||||
return false, Error(res)
|
||||
}
|
||||
return fec != 0, nil
|
||||
}
|
||||
|
||||
// SetPacketLossPerc configures the encoder's expected packet loss percentage.
|
||||
func (enc *Encoder) SetPacketLossPerc(lossPerc int) error {
|
||||
res := C.bridge_encoder_set_packet_loss_perc(enc.p, C.opus_int32(lossPerc))
|
||||
if res != C.OPUS_OK {
|
||||
return Error(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PacketLossPerc gets the encoder's configured packet loss percentage.
|
||||
func (enc *Encoder) PacketLossPerc() (int, error) {
|
||||
var lossPerc C.opus_int32
|
||||
res := C.bridge_encoder_get_packet_loss_perc(enc.p, &lossPerc)
|
||||
if res != C.OPUS_OK {
|
||||
return 0, Error(res)
|
||||
}
|
||||
return int(lossPerc), nil
|
||||
}
|
||||
|
||||
// Reset resets the codec state to be equivalent to a freshly initialized state.
|
||||
func (enc *Encoder) Reset() error {
|
||||
res := C.bridge_encoder_reset_state(enc.p)
|
||||
if res != C.OPUS_OK {
|
||||
return Error(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
36
hw_service_go/vendor/github.com/hraban/opus/errors.go
generated
vendored
Normal file
36
hw_service_go/vendor/github.com/hraban/opus/errors.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright © Go Opus Authors (see AUTHORS file)
|
||||
//
|
||||
// License for use of this code is detailed in the LICENSE file
|
||||
|
||||
package opus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo pkg-config: opus
|
||||
#include <opus.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type Error int
|
||||
|
||||
var _ error = Error(0)
|
||||
|
||||
// Libopus errors
|
||||
const (
|
||||
ErrOK = Error(C.OPUS_OK)
|
||||
ErrBadArg = Error(C.OPUS_BAD_ARG)
|
||||
ErrBufferTooSmall = Error(C.OPUS_BUFFER_TOO_SMALL)
|
||||
ErrInternalError = Error(C.OPUS_INTERNAL_ERROR)
|
||||
ErrInvalidPacket = Error(C.OPUS_INVALID_PACKET)
|
||||
ErrUnimplemented = Error(C.OPUS_UNIMPLEMENTED)
|
||||
ErrInvalidState = Error(C.OPUS_INVALID_STATE)
|
||||
ErrAllocFail = Error(C.OPUS_ALLOC_FAIL)
|
||||
)
|
||||
|
||||
// Error string (in human readable format) for libopus errors.
|
||||
func (e Error) Error() string {
|
||||
return fmt.Sprintf("opus: %s", C.GoString(C.opus_strerror(C.int(e))))
|
||||
}
|
||||
36
hw_service_go/vendor/github.com/hraban/opus/opus.go
generated
vendored
Normal file
36
hw_service_go/vendor/github.com/hraban/opus/opus.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright © Go Opus Authors (see AUTHORS file)
|
||||
//
|
||||
// License for use of this code is detailed in the LICENSE file
|
||||
|
||||
package opus
|
||||
|
||||
/*
|
||||
// Link opus using pkg-config.
|
||||
#cgo pkg-config: opus
|
||||
#include <opus.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type Application int
|
||||
|
||||
const (
|
||||
// Optimize encoding for VoIP
|
||||
AppVoIP = Application(C.OPUS_APPLICATION_VOIP)
|
||||
// Optimize encoding for non-voice signals like music
|
||||
AppAudio = Application(C.OPUS_APPLICATION_AUDIO)
|
||||
// Optimize encoding for low latency applications
|
||||
AppRestrictedLowdelay = Application(C.OPUS_APPLICATION_RESTRICTED_LOWDELAY)
|
||||
)
|
||||
|
||||
const (
|
||||
xMAX_BITRATE = 48000
|
||||
xMAX_FRAME_SIZE_MS = 60
|
||||
xMAX_FRAME_SIZE = xMAX_BITRATE * xMAX_FRAME_SIZE_MS / 1000
|
||||
// Maximum size of an encoded frame. I actually have no idea, but this
|
||||
// looks like it's big enough.
|
||||
maxEncodedFrameSize = 10000
|
||||
)
|
||||
|
||||
func Version() string {
|
||||
return C.GoString(C.opus_get_version_string())
|
||||
}
|
||||
183
hw_service_go/vendor/github.com/hraban/opus/stream.go
generated
vendored
Normal file
183
hw_service_go/vendor/github.com/hraban/opus/stream.go
generated
vendored
Normal file
@ -0,0 +1,183 @@
|
||||
// Copyright © Go Opus Authors (see AUTHORS file)
|
||||
//
|
||||
// License for use of this code is detailed in the LICENSE file
|
||||
|
||||
// +build !nolibopusfile
|
||||
|
||||
package opus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo pkg-config: opusfile
|
||||
#include <opusfile.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
OggOpusFile *my_open_callbacks(uintptr_t p, int *error);
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// Stream wraps a io.Reader in a decoding layer. It provides an API similar to
|
||||
// io.Reader, but it provides raw PCM data instead of the encoded Opus data.
|
||||
//
|
||||
// This is not the same as directly decoding the bytes on the io.Reader; opus
|
||||
// streams are Ogg Opus audio streams, which package raw Opus data.
|
||||
//
|
||||
// This wraps libopusfile. For more information, see the api docs on xiph.org:
|
||||
//
|
||||
// https://www.opus-codec.org/docs/opusfile_api-0.7/index.html
|
||||
type Stream struct {
|
||||
id uintptr
|
||||
oggfile *C.OggOpusFile
|
||||
read io.Reader
|
||||
// Preallocated buffer to pass to the reader
|
||||
buf []byte
|
||||
}
|
||||
|
||||
var streams = newStreamsMap()
|
||||
|
||||
//export go_readcallback
|
||||
func go_readcallback(p unsafe.Pointer, cbuf *C.uchar, cmaxbytes C.int) C.int {
|
||||
streamId := uintptr(p)
|
||||
stream := streams.Get(streamId)
|
||||
if stream == nil {
|
||||
// This is bad
|
||||
return -1
|
||||
}
|
||||
|
||||
maxbytes := int(cmaxbytes)
|
||||
if maxbytes > cap(stream.buf) {
|
||||
maxbytes = cap(stream.buf)
|
||||
}
|
||||
// Don't bother cleaning up old data because that's not required by the
|
||||
// io.Reader API.
|
||||
n, err := stream.read.Read(stream.buf[:maxbytes])
|
||||
// Go allows returning non-nil error (like EOF) and n>0, libopusfile doesn't
|
||||
// expect that. So return n first to indicate the valid bytes, let the
|
||||
// subsequent call (which will be n=0, same-error) handle the actual error.
|
||||
if n == 0 && err != nil {
|
||||
if err == io.EOF {
|
||||
return 0
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
C.memcpy(unsafe.Pointer(cbuf), unsafe.Pointer(&stream.buf[0]), C.size_t(n))
|
||||
return C.int(n)
|
||||
}
|
||||
|
||||
// NewStream creates and initializes a new stream. Don't call .Init() on this.
|
||||
func NewStream(read io.Reader) (*Stream, error) {
|
||||
var s Stream
|
||||
err := s.Init(read)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
// Init initializes a stream with an io.Reader to fetch opus encoded data from
|
||||
// on demand. Errors from the reader are all transformed to an EOF, any actual
|
||||
// error information is lost. The same happens when a read returns succesfully,
|
||||
// but with zero bytes.
|
||||
func (s *Stream) Init(read io.Reader) error {
|
||||
if s.oggfile != nil {
|
||||
return fmt.Errorf("opus stream is already initialized")
|
||||
}
|
||||
if read == nil {
|
||||
return fmt.Errorf("Reader must be non-nil")
|
||||
}
|
||||
|
||||
s.read = read
|
||||
s.buf = make([]byte, maxEncodedFrameSize)
|
||||
s.id = streams.NextId()
|
||||
var errno C.int
|
||||
|
||||
// Immediately delete the stream after .Init to avoid leaking if the
|
||||
// caller forgets to (/ doesn't want to) call .Close(). No need for that,
|
||||
// since the callback is only ever called during a .Read operation; just
|
||||
// Save and Delete from the map around that every time a reader function is
|
||||
// called.
|
||||
streams.Save(s)
|
||||
defer streams.Del(s)
|
||||
oggfile := C.my_open_callbacks(C.uintptr_t(s.id), &errno)
|
||||
if errno != 0 {
|
||||
return StreamError(errno)
|
||||
}
|
||||
s.oggfile = oggfile
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read a chunk of raw opus data from the stream and decode it. Returns the
|
||||
// number of decoded samples per channel. This means that a dual channel
|
||||
// (stereo) feed will have twice as many samples as the value returned.
|
||||
//
|
||||
// Read may successfully read less bytes than requested, but it will never read
|
||||
// exactly zero bytes succesfully if a non-zero buffer is supplied.
|
||||
//
|
||||
// The number of channels in the output data must be known in advance. It is
|
||||
// possible to extract this information from the stream itself, but I'm not
|
||||
// motivated to do that. Feel free to send a pull request.
|
||||
func (s *Stream) Read(pcm []int16) (int, error) {
|
||||
if s.oggfile == nil {
|
||||
return 0, fmt.Errorf("opus stream is uninitialized or already closed")
|
||||
}
|
||||
if len(pcm) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
streams.Save(s)
|
||||
defer streams.Del(s)
|
||||
n := C.op_read(
|
||||
s.oggfile,
|
||||
(*C.opus_int16)(&pcm[0]),
|
||||
C.int(len(pcm)),
|
||||
nil)
|
||||
if n < 0 {
|
||||
return 0, StreamError(n)
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
// ReadFloat32 is the same as Read, but decodes to float32 instead of int16.
|
||||
func (s *Stream) ReadFloat32(pcm []float32) (int, error) {
|
||||
if s.oggfile == nil {
|
||||
return 0, fmt.Errorf("opus stream is uninitialized or already closed")
|
||||
}
|
||||
if len(pcm) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
streams.Save(s)
|
||||
defer streams.Del(s)
|
||||
n := C.op_read_float(
|
||||
s.oggfile,
|
||||
(*C.float)(&pcm[0]),
|
||||
C.int(len(pcm)),
|
||||
nil)
|
||||
if n < 0 {
|
||||
return 0, StreamError(n)
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
func (s *Stream) Close() error {
|
||||
if s.oggfile == nil {
|
||||
return fmt.Errorf("opus stream is uninitialized or already closed")
|
||||
}
|
||||
C.op_free(s.oggfile)
|
||||
if closer, ok := s.read.(io.Closer); ok {
|
||||
return closer.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
75
hw_service_go/vendor/github.com/hraban/opus/stream_errors.go
generated
vendored
Normal file
75
hw_service_go/vendor/github.com/hraban/opus/stream_errors.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright © 2015-2017 Go Opus Authors (see AUTHORS file)
|
||||
//
|
||||
// License for use of this code is detailed in the LICENSE file
|
||||
|
||||
// +build !nolibopusfile
|
||||
|
||||
package opus
|
||||
|
||||
/*
|
||||
#cgo pkg-config: opusfile
|
||||
#include <opusfile.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// StreamError represents an error from libopusfile.
|
||||
type StreamError int
|
||||
|
||||
var _ error = StreamError(0)
|
||||
|
||||
// Libopusfile errors. The names are copied verbatim from the libopusfile
|
||||
// library.
|
||||
const (
|
||||
ErrStreamFalse = StreamError(C.OP_FALSE)
|
||||
ErrStreamEOF = StreamError(C.OP_EOF)
|
||||
ErrStreamHole = StreamError(C.OP_HOLE)
|
||||
ErrStreamRead = StreamError(C.OP_EREAD)
|
||||
ErrStreamFault = StreamError(C.OP_EFAULT)
|
||||
ErrStreamImpl = StreamError(C.OP_EIMPL)
|
||||
ErrStreamInval = StreamError(C.OP_EINVAL)
|
||||
ErrStreamNotFormat = StreamError(C.OP_ENOTFORMAT)
|
||||
ErrStreamBadHeader = StreamError(C.OP_EBADHEADER)
|
||||
ErrStreamVersion = StreamError(C.OP_EVERSION)
|
||||
ErrStreamNotAudio = StreamError(C.OP_ENOTAUDIO)
|
||||
ErrStreamBadPacked = StreamError(C.OP_EBADPACKET)
|
||||
ErrStreamBadLink = StreamError(C.OP_EBADLINK)
|
||||
ErrStreamNoSeek = StreamError(C.OP_ENOSEEK)
|
||||
ErrStreamBadTimestamp = StreamError(C.OP_EBADTIMESTAMP)
|
||||
)
|
||||
|
||||
func (i StreamError) Error() string {
|
||||
switch i {
|
||||
case ErrStreamFalse:
|
||||
return "OP_FALSE"
|
||||
case ErrStreamEOF:
|
||||
return "OP_EOF"
|
||||
case ErrStreamHole:
|
||||
return "OP_HOLE"
|
||||
case ErrStreamRead:
|
||||
return "OP_EREAD"
|
||||
case ErrStreamFault:
|
||||
return "OP_EFAULT"
|
||||
case ErrStreamImpl:
|
||||
return "OP_EIMPL"
|
||||
case ErrStreamInval:
|
||||
return "OP_EINVAL"
|
||||
case ErrStreamNotFormat:
|
||||
return "OP_ENOTFORMAT"
|
||||
case ErrStreamBadHeader:
|
||||
return "OP_EBADHEADER"
|
||||
case ErrStreamVersion:
|
||||
return "OP_EVERSION"
|
||||
case ErrStreamNotAudio:
|
||||
return "OP_ENOTAUDIO"
|
||||
case ErrStreamBadPacked:
|
||||
return "OP_EBADPACKET"
|
||||
case ErrStreamBadLink:
|
||||
return "OP_EBADLINK"
|
||||
case ErrStreamNoSeek:
|
||||
return "OP_ENOSEEK"
|
||||
case ErrStreamBadTimestamp:
|
||||
return "OP_EBADTIMESTAMP"
|
||||
default:
|
||||
return "libopusfile error: %d (unknown code)"
|
||||
}
|
||||
}
|
||||
64
hw_service_go/vendor/github.com/hraban/opus/streams_map.go
generated
vendored
Normal file
64
hw_service_go/vendor/github.com/hraban/opus/streams_map.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright © Go Opus Authors (see AUTHORS file)
|
||||
//
|
||||
// License for use of this code is detailed in the LICENSE file
|
||||
|
||||
// +build !nolibopusfile
|
||||
|
||||
package opus
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// A map of simple integers to the actual pointers to stream structs. Avoids
|
||||
// passing pointers into the Go heap to C.
|
||||
//
|
||||
// As per the CGo pointers design doc for go 1.6:
|
||||
//
|
||||
// A particular unsafe area is C code that wants to hold on to Go func and
|
||||
// pointer values for future callbacks from C to Go. This works today but is not
|
||||
// permitted by the invariant. It is hard to detect. One safe approach is: Go
|
||||
// code that wants to preserve funcs/pointers stores them into a map indexed by
|
||||
// an int. Go code calls the C code, passing the int, which the C code may store
|
||||
// freely. When the C code wants to call into Go, it passes the int to a Go
|
||||
// function that looks in the map and makes the call. An explicit call is
|
||||
// required to release the value from the map if it is no longer needed, but
|
||||
// that was already true before.
|
||||
//
|
||||
// - https://github.com/golang/proposal/blob/master/design/12416-cgo-pointers.md
|
||||
type streamsMap struct {
|
||||
sync.RWMutex
|
||||
m map[uintptr]*Stream
|
||||
counter uintptr
|
||||
}
|
||||
|
||||
func (sm *streamsMap) Get(id uintptr) *Stream {
|
||||
sm.RLock()
|
||||
defer sm.RUnlock()
|
||||
return sm.m[id]
|
||||
}
|
||||
|
||||
func (sm *streamsMap) Del(s *Stream) {
|
||||
sm.Lock()
|
||||
defer sm.Unlock()
|
||||
delete(sm.m, s.id)
|
||||
}
|
||||
|
||||
// NextId returns a unique ID for each call.
|
||||
func (sm *streamsMap) NextId() uintptr {
|
||||
return atomic.AddUintptr(&sm.counter, 1)
|
||||
}
|
||||
|
||||
func (sm *streamsMap) Save(s *Stream) {
|
||||
sm.Lock()
|
||||
defer sm.Unlock()
|
||||
sm.m[s.id] = s
|
||||
}
|
||||
|
||||
func newStreamsMap() *streamsMap {
|
||||
return &streamsMap{
|
||||
counter: 0,
|
||||
m: map[uintptr]*Stream{},
|
||||
}
|
||||
}
|
||||
6
hw_service_go/vendor/modules.txt
vendored
Normal file
6
hw_service_go/vendor/modules.txt
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
# github.com/gorilla/websocket v1.5.3
|
||||
## explicit; go 1.12
|
||||
github.com/gorilla/websocket
|
||||
# github.com/hraban/opus v0.0.0-20230925203106-0188a62cb302
|
||||
## explicit
|
||||
github.com/hraban/opus
|
||||
Loading…
x
Reference in New Issue
Block a user