mirror of
https://github.com/mmatczuk/go-http-tunnel.git
synced 2026-05-15 14:16:17 -06:00
Merge c93916810b into 9da0263137
This commit is contained in:
commit
a09aa0c9ee
158 changed files with 1906 additions and 45918 deletions
225
.gitignore
vendored
225
.gitignore
vendored
|
|
@ -2,26 +2,219 @@
|
|||
.tunnel/
|
||||
build/
|
||||
|
||||
### Go
|
||||
*.cov
|
||||
*.prof
|
||||
*.mprof
|
||||
|
||||
### IntelliJ
|
||||
.idea/
|
||||
*.iml
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/go,vim,intellij,visualstudiocode,macos,windows
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=go,vim,intellij,visualstudiocode,macos,windows
|
||||
|
||||
### Vim
|
||||
# swap
|
||||
[._]*.s[a-w][a-z]
|
||||
[._]s[a-w][a-z]
|
||||
# session
|
||||
### Go ###
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
### Go Patch ###
|
||||
/vendor/
|
||||
/Godeps/
|
||||
|
||||
### Intellij ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Intellij Patch ###
|
||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||
|
||||
# *.iml
|
||||
# modules.xml
|
||||
# .idea/misc.xml
|
||||
# *.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
# https://plugins.jetbrains.com/plugin/7973-sonarlint
|
||||
.idea/**/sonarlint/
|
||||
|
||||
# SonarQube Plugin
|
||||
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
|
||||
.idea/**/sonarIssues.xml
|
||||
|
||||
# Markdown Navigator plugin
|
||||
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
|
||||
.idea/**/markdown-navigator.xml
|
||||
.idea/**/markdown-navigator-enh.xml
|
||||
.idea/**/markdown-navigator/
|
||||
|
||||
# Cache file creation bug
|
||||
# See https://youtrack.jetbrains.com/issue/JBR-2257
|
||||
.idea/$CACHE_FILE$
|
||||
|
||||
# CodeStream plugin
|
||||
# https://plugins.jetbrains.com/plugin/12206-codestream
|
||||
.idea/codestream.xml
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Vim ###
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
!*.svg # comment out if you don't need vector files
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-rt-v][a-z]
|
||||
[._]ss[a-gi-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Session
|
||||
Session.vim
|
||||
# temporary
|
||||
Sessionx.vim
|
||||
|
||||
# Temporary
|
||||
.netrwhist
|
||||
*~
|
||||
# auto-generated tag files
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
# Persistent undo
|
||||
[._]*.un~
|
||||
|
||||
### Mac
|
||||
.DS_Store
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
*.code-workspace
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
.ionide
|
||||
|
||||
### Windows ###
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/go,vim,intellij,visualstudiocode,macos,windows
|
||||
|
|
|
|||
18
Makefile
18
Makefile
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
GO_FILES := $(shell \
|
||||
find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
|
||||
find . '(' -path '*/.*' -o -path './vendor' -o -path './src' ')' -prune \
|
||||
-o -name '*.go' -print | cut -b3-)
|
||||
|
||||
LINT_IGNORE := "/id/\|/tunnelmock/\|/vendor/"
|
||||
|
|
@ -51,7 +51,7 @@ check: .check-fmt .check-vet .check-lint .check-ineffassign .check-static .check
|
|||
|
||||
.PHONY: .check-vendor
|
||||
.check-vendor:
|
||||
@dep ensure -no-vendor -dry-run
|
||||
@go mod vendor
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
|
|
@ -61,12 +61,11 @@ test:
|
|||
.PHONY: get-deps
|
||||
get-deps:
|
||||
@echo "==> Installing dependencies..."
|
||||
@dep ensure
|
||||
@go mod init
|
||||
|
||||
.PHONY: get-tools
|
||||
get-tools:
|
||||
@echo "==> Installing tools..."
|
||||
@go get -u github.com/golang/dep/cmd/dep
|
||||
@go get -u golang.org/x/lint/golint
|
||||
@go get -u github.com/golang/mock/gomock
|
||||
|
||||
|
|
@ -82,22 +81,25 @@ ARCH = "386 amd64 arm"
|
|||
OSARCH = "!darwin/386 !darwin/arm !windows/arm"
|
||||
GIT_COMMIT = $(shell git describe --always)
|
||||
|
||||
.PHONY: release_fast
|
||||
release_fast: check clean build package
|
||||
|
||||
.PHONY: release
|
||||
release: check test clean build package
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
mkdir ${OUTPUT_DIR}
|
||||
mkdir -p ${OUTPUT_DIR}
|
||||
CGO_ENABLED=0 GOARM=5 gox -ldflags "-w -X main.version=$(GIT_COMMIT)" \
|
||||
-os=${OS} -arch=${ARCH} -osarch=${OSARCH} -output "${OUTPUT_DIR}/pkg/{{.OS}}_{{.Arch}}/{{.Dir}}" \
|
||||
-mod=vendor -os=${OS} -arch=${ARCH} -osarch=${OSARCH} -output "${OUTPUT_DIR}/pkg/{{.OS}}_{{.Arch}}/{{.Dir}}" \
|
||||
./cmd/tunnel ./cmd/tunneld
|
||||
|
||||
.PHONY: package
|
||||
package:
|
||||
mkdir ${OUTPUT_DIR}/dist
|
||||
mkdir -p ${OUTPUT_DIR}/dist
|
||||
cd ${OUTPUT_DIR}/pkg/; for osarch in *; do (cd $$osarch; tar zcvf ../../dist/tunnel_$$osarch.tar.gz ./*); done;
|
||||
cd ${OUTPUT_DIR}/dist; sha256sum * > ./SHA256SUMS
|
||||
|
||||
.PHONY: publish
|
||||
publish:
|
||||
ghr -recreate -u mmatczuk -t ${GITHUB_TOKEN} -r go-http-tunnel pre-release ${OUTPUT_DIR}/dist
|
||||
ghr -recreate -u hons82 -t ${GITHUB_TOKEN} -r go-http-tunnel pre-release ${OUTPUT_DIR}/dist
|
||||
|
|
|
|||
63
README.md
63
README.md
|
|
@ -1,4 +1,4 @@
|
|||
# Go HTTP tunnel [](http://godoc.org/github.com/mmatczuk/go-http-tunnel) [](https://goreportcard.com/report/github.com/mmatczuk/go-http-tunnel) [](https://travis-ci.org/mmatczuk/go-http-tunnel) [](https://github.com/mmatczuk/go-http-tunnel/releases)
|
||||
# Go HTTP tunnel [](http://godoc.org/github.com/hons82/go-http-tunnel) [](https://goreportcard.com/report/github.com/hons82/go-http-tunnel) [](https://travis-ci.org/hons82/go-http-tunnel) [](https://github.com/hons82/go-http-tunnel/releases)
|
||||
|
||||
Go HTTP tunnel is a reverse tunnel based on HTTP/2. It enables you to share your localhost when you don't have a public IP.
|
||||
|
||||
|
|
@ -26,10 +26,10 @@ IF YOU WOULD LIKE TO SEE THIS PROJECT MODERNIZED PLEASE [UPVOTE THE ISSUE](https
|
|||
Build the latest version.
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/mmatczuk/go-http-tunnel/cmd/...
|
||||
$ go get -u github.com/hons82/go-http-tunnel/cmd/...
|
||||
```
|
||||
|
||||
Alternatively [download the latest release](https://github.com/mmatczuk/go-http-tunnel/releases/latest).
|
||||
Alternatively [download the latest release](https://github.com/hons82/go-http-tunnel/releases/latest).
|
||||
|
||||
## Running
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ $ openssl req -x509 -nodes -newkey rsa:2048 -sha256 -keyout client.key -out clie
|
|||
$ openssl req -x509 -nodes -newkey rsa:2048 -sha256 -keyout server.key -out server.crt
|
||||
```
|
||||
|
||||
Run client:
|
||||
### Run client:
|
||||
|
||||
* Install `tunnel` binary
|
||||
* Make `.tunnel` directory in your project directory
|
||||
|
|
@ -59,7 +59,7 @@ Run client:
|
|||
$ tunnel -config ./tunnel/tunnel.yml start-all
|
||||
```
|
||||
|
||||
Run server:
|
||||
### Run server:
|
||||
|
||||
* Install `tunneld` binary
|
||||
* Make `.tunneld` directory
|
||||
|
|
@ -131,7 +131,7 @@ $ sudo systemctl enable tunneld.service
|
|||
|
||||
There are many more options for systemd services, and this is by not means an exhaustive configuration file.
|
||||
|
||||
## Configuration
|
||||
## Configuration - Client
|
||||
|
||||
The tunnel client `tunnel` requires configuration file, by default it will try reading `tunnel.yml` in your current working directory. If you want to specify other file use `-config` flag.
|
||||
|
||||
|
|
@ -177,6 +177,53 @@ Configuration options:
|
|||
* `multiplier`: interval multiplier if reconnect failed, *default:* `1.5`
|
||||
* `max_interval`: maximal time client would wait before redialing the server, *default:* `1m`
|
||||
* `max_time`: maximal time client would try to reconnect to the server if connection was lost, set `0` to never stop trying, *default:* `15m`
|
||||
* `keep_alive`
|
||||
* `interval`: the amount of time to wait between sending keepalive packets, *default:* `25s`
|
||||
|
||||
## Configuration - Server
|
||||
|
||||
* `httpAddr`: Public address for HTTP connections, empty string to disable, *default:* `:80`
|
||||
* `httpsAddr`: Public address listening for HTTPS connections, emptry string to disable, *default:* `:443`
|
||||
* `tunnelAddr`: Public address listening for tunnel client, *default:* `:5223`
|
||||
* `apiAddr`: Public address for HTTP API to get info about the tunnels, *default:* `:5091`
|
||||
* `sniAddr`: Public address listening for TLS SNI connections, empty string to disable
|
||||
* `tlsCrt`: Path to a TLS certificate file, *default:* `server.crt`
|
||||
* `tlsKey`: Path to a TLS key file, *default:* `server.key`
|
||||
* `rootCA`: Path to the trusted certificate chian used for client certificate authentication, if empty any client certificate is accepted
|
||||
* `clients`: Path to a properties file that contains a list of 'host=tunnelClientId's, if empty accept all clients
|
||||
* `keepAlive`: the amount of time to wait between sending keepalive packets *default:* `45s`
|
||||
* `logLevel`: Level of messages to log, 0-3, *default:* 1
|
||||
|
||||
If both `httpAddr` and `httpsAddr` are configured, an automatic redirect to the secure channel will be established using an `http.StatusMovedPermanently` (301)
|
||||
|
||||
### Custom error pages
|
||||
|
||||
Just copy the `html` folder from this repository into the folder of the tunnel-server to have a starting point. In the `html/errors` folder you'll find a sample page for each error that is currently customisable which you'll be able to change according to your needs.
|
||||
|
||||
## Server API
|
||||
|
||||
If the `apiAddr` is properly set, the tunnel server offers a simple API to query its state.
|
||||
|
||||
### /api/clients/list
|
||||
|
||||
Returns a list of `clients` together with a list of open tunnels in JSON format.
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"Id": "BHXWUUT-A6IYDWI-2BSIC5A-...",
|
||||
"Listeners": [
|
||||
{
|
||||
"Network": "tcp",
|
||||
"Addr": "192.0.2.1:25"
|
||||
}
|
||||
],
|
||||
"Hosts": [
|
||||
"tunnel1.my-tunnel-host.com"
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
|
|
@ -188,7 +235,7 @@ The tunnel is based HTTP/2 for speed and security. There is a single TCP connect
|
|||
|
||||
If this project help you reduce time to develop, you can give me a cup of coffee.
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=RMM46NAEY7YZ6&lc=US&item_name=go%2dhttp%2dtunnel¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted)
|
||||
[](https://www.paypal.com/donate?hosted_button_id=E74HP49TAAUQ2)
|
||||
|
||||
A GitHub star is always appreciated!
|
||||
|
||||
|
|
@ -196,4 +243,4 @@ A GitHub star is always appreciated!
|
|||
|
||||
Copyright (C) 2017 Michał Matczuk
|
||||
|
||||
This project is distributed under the AGPL-3 license. See the [LICENSE](https://github.com/mmatczuk/go-http-tunnel/blob/master/LICENSE) file for details. If you need an enterprice license contact me directly.
|
||||
This project is distributed under the AGPL-3 license. See the [LICENSE](https://github.com/hons82/go-http-tunnel/blob/master/LICENSE) file for details. If you need an enterprice license contact me directly.
|
||||
|
|
|
|||
19
backoff.go
19
backoff.go
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright (C) 2017 Michał Matczuk
|
||||
// Use of this source code is governed by an AGPL-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tunnel
|
||||
|
||||
import "time"
|
||||
|
||||
// Backoff defines behavior of staggering reconnection retries.
|
||||
type Backoff interface {
|
||||
// Next returns the duration to sleep before retrying to reconnect.
|
||||
// If the returned value is negative, the retry is aborted.
|
||||
NextBackOff() time.Duration
|
||||
|
||||
// Reset is used to signal a reconnection was successful and next
|
||||
// call to Next should return desired time duration for 1st reconnection
|
||||
// attempt.
|
||||
Reset()
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# Benchmark report
|
||||
|
||||
The benchmark compares [tunnel](https://github.com/mmatczuk/go-http-tunnel) to [koding tunnel](https://github.com/koding/tunnel) on serving 184 midsized files that were gathered by saving `amazon.com` for offline view. The data set consists of images and text data (js, css, html). On start client loads the files into memory and act as a file server.
|
||||
The benchmark compares [tunnel](https://github.com/hons82/go-http-tunnel) to [koding tunnel](https://github.com/koding/tunnel) on serving 184 midsized files that were gathered by saving `amazon.com` for offline view. The data set consists of images and text data (js, css, html). On start client loads the files into memory and act as a file server.
|
||||
|
||||
The diagrams were rendered using [hdrhistogram](http://hdrhistogram.github.io/HdrHistogram/plotFiles.html) and the input files were generated with help of [github.com/codahale/hdrhistogram](https://github.com/codahale/hdrhistogram) library. The vegeta raw results were corrected for stalls using [hdr correction method](https://godoc.org/github.com/codahale/hdrhistogram#Histogram.RecordCorrectedValue).
|
||||
|
||||
|
|
|
|||
46
client.go
46
client.go
|
|
@ -14,10 +14,12 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff"
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
"github.com/mmatczuk/go-http-tunnel/log"
|
||||
"github.com/mmatczuk/go-http-tunnel/proto"
|
||||
"github.com/hons82/go-http-tunnel/connection"
|
||||
"github.com/hons82/go-http-tunnel/log"
|
||||
"github.com/hons82/go-http-tunnel/proto"
|
||||
)
|
||||
|
||||
// ClientConfig is configuration of the Client.
|
||||
|
|
@ -32,7 +34,7 @@ type ClientConfig struct {
|
|||
DialTLS func(network, addr string, config *tls.Config) (net.Conn, error)
|
||||
// Backoff specifies backoff policy on server connection retry. If nil
|
||||
// when dial fails it will not be retried.
|
||||
Backoff Backoff
|
||||
Backoff connection.Backoff
|
||||
// Tunnels specifies the tunnels client requests to be opened on server.
|
||||
Tunnels map[string]*proto.Tunnel
|
||||
// Proxy is ProxyFunc responsible for transferring data between server
|
||||
|
|
@ -40,6 +42,8 @@ type ClientConfig struct {
|
|||
Proxy ProxyFunc
|
||||
// Logger is optional logger. If nil logging is disabled.
|
||||
Logger log.Logger
|
||||
// Used to configure the tcp keepalive for the client -> server tcp connection
|
||||
KeepAlive connection.KeepAliveConfig
|
||||
}
|
||||
|
||||
// Client is responsible for creating connection to the server, handling control
|
||||
|
|
@ -95,7 +99,7 @@ func (c *Client) Start() error {
|
|||
"level", 1,
|
||||
"action", "start",
|
||||
)
|
||||
|
||||
b := backoff.NewExponentialBackOff()
|
||||
for {
|
||||
conn, err := c.connect()
|
||||
if err != nil {
|
||||
|
|
@ -112,17 +116,36 @@ func (c *Client) Start() error {
|
|||
)
|
||||
|
||||
c.connMu.Lock()
|
||||
now := time.Now()
|
||||
err = c.serverErr
|
||||
|
||||
// detect disconnect hiccup
|
||||
if err == nil && now.Sub(c.lastDisconnect).Seconds() < 5 {
|
||||
if err == nil && time.Since(c.lastDisconnect).Seconds() < 5 {
|
||||
err = fmt.Errorf("connection is being cut")
|
||||
}
|
||||
|
||||
// Backoff
|
||||
if b != nil {
|
||||
if err != nil {
|
||||
d := b.NextBackOff()
|
||||
if d > 0 {
|
||||
// backoff
|
||||
c.logger.Log(
|
||||
"level", 1,
|
||||
"action", "backoff",
|
||||
"sleep", d,
|
||||
"err", err,
|
||||
)
|
||||
time.Sleep(d)
|
||||
err = nil
|
||||
}
|
||||
} else {
|
||||
b.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
c.conn = nil
|
||||
c.serverErr = nil
|
||||
c.lastDisconnect = now
|
||||
c.lastDisconnect = time.Now()
|
||||
c.connMu.Unlock()
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -172,7 +195,11 @@ func (c *Client) dial() (net.Conn, error) {
|
|||
conn, err = d.Dial(network, addr)
|
||||
|
||||
if err == nil {
|
||||
err = keepAlive(conn)
|
||||
c.logger.Log(
|
||||
"level", 1,
|
||||
"msg", fmt.Sprintf("setting up keep alive using config: %v", c.config.KeepAlive.String()),
|
||||
)
|
||||
err = c.config.KeepAlive.Set(conn)
|
||||
}
|
||||
if err == nil {
|
||||
conn = tls.Client(conn, tlsConfig)
|
||||
|
|
@ -204,13 +231,12 @@ func (c *Client) dial() (net.Conn, error) {
|
|||
if b == nil {
|
||||
return doDial()
|
||||
}
|
||||
|
||||
b.Reset()
|
||||
for {
|
||||
conn, err := doDial()
|
||||
|
||||
// success
|
||||
if err == nil {
|
||||
b.Reset()
|
||||
return conn, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,15 +6,11 @@ package tunnel
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/mmatczuk/go-http-tunnel/proto"
|
||||
"github.com/mmatczuk/go-http-tunnel/tunnelmock"
|
||||
"github.com/hons82/go-http-tunnel/connection"
|
||||
"github.com/hons82/go-http-tunnel/proto"
|
||||
)
|
||||
|
||||
func TestClient_Dial(t *testing.T) {
|
||||
|
|
@ -30,6 +26,9 @@ func TestClient_Dial(t *testing.T) {
|
|||
},
|
||||
Tunnels: map[string]*proto.Tunnel{"test": {}},
|
||||
Proxy: Proxy(ProxyFuncs{}),
|
||||
KeepAlive: connection.KeepAliveConfig{
|
||||
KeepAliveInterval: connection.DefaultKeepAliveInterval,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -45,42 +44,45 @@ func TestClient_Dial(t *testing.T) {
|
|||
conn.Close()
|
||||
}
|
||||
|
||||
func TestClient_DialBackoff(t *testing.T) {
|
||||
t.Parallel()
|
||||
// func TestClient_DialBackoff(t *testing.T) {
|
||||
// t.Parallel()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
// ctrl := gomock.NewController(t)
|
||||
// defer ctrl.Finish()
|
||||
|
||||
b := tunnelmock.NewMockBackoff(ctrl)
|
||||
gomock.InOrder(
|
||||
b.EXPECT().NextBackOff().Return(50*time.Millisecond).Times(2),
|
||||
b.EXPECT().NextBackOff().Return(-time.Millisecond),
|
||||
)
|
||||
// b := tunnelmock.NewMockBackoff(ctrl)
|
||||
// gomock.InOrder(
|
||||
// b.EXPECT().NextBackOff().Return(50*time.Millisecond).Times(2),
|
||||
// b.EXPECT().NextBackOff().Return(-time.Millisecond),
|
||||
// )
|
||||
|
||||
d := func(network, addr string, config *tls.Config) (net.Conn, error) {
|
||||
return nil, errors.New("foobar")
|
||||
}
|
||||
// d := func(network, addr string, config *tls.Config) (net.Conn, error) {
|
||||
// return nil, errors.New("foobar")
|
||||
// }
|
||||
|
||||
c, err := NewClient(&ClientConfig{
|
||||
ServerAddr: "8.8.8.8",
|
||||
TLSClientConfig: &tls.Config{},
|
||||
DialTLS: d,
|
||||
Backoff: b,
|
||||
Tunnels: map[string]*proto.Tunnel{"test": {}},
|
||||
Proxy: Proxy(ProxyFuncs{}),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// c, err := NewClient(&ClientConfig{
|
||||
// ServerAddr: "8.8.8.8",
|
||||
// TLSClientConfig: &tls.Config{},
|
||||
// DialTLS: d,
|
||||
// Backoff: b,
|
||||
// Tunnels: map[string]*proto.Tunnel{"test": {}},
|
||||
// Proxy: Proxy(ProxyFuncs{}),
|
||||
// KeepAlive: connection.KeepAliveConfig{
|
||||
// KeepAliveInterval: connection.DefaultKeepAliveInterval,
|
||||
// },
|
||||
// })
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
start := time.Now()
|
||||
_, err = c.dial()
|
||||
// start := time.Now()
|
||||
// _, err = c.dial()
|
||||
|
||||
if time.Since(start) < 100*time.Millisecond {
|
||||
t.Fatal("Wait mismatch", err)
|
||||
}
|
||||
// if time.Since(start) < 100*time.Millisecond {
|
||||
// t.Fatal("Wait mismatch", err)
|
||||
// }
|
||||
|
||||
if err.Error() != "backoff limit exeded: foobar" {
|
||||
t.Fatal("Error mismatch", err)
|
||||
}
|
||||
}
|
||||
// if err.Error() != "backoff limit exeded: foobar" {
|
||||
// t.Fatal("Error mismatch", err)
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -8,29 +8,13 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/mmatczuk/go-http-tunnel/proto"
|
||||
"github.com/hons82/go-http-tunnel/connection"
|
||||
"github.com/hons82/go-http-tunnel/proto"
|
||||
)
|
||||
|
||||
// Default backoff configuration.
|
||||
const (
|
||||
DefaultBackoffInterval = 500 * time.Millisecond
|
||||
DefaultBackoffMultiplier = 1.5
|
||||
DefaultBackoffMaxInterval = 60 * time.Second
|
||||
DefaultBackoffMaxTime = 15 * time.Minute
|
||||
)
|
||||
|
||||
// BackoffConfig defines behavior of staggering reconnection retries.
|
||||
type BackoffConfig struct {
|
||||
Interval time.Duration `yaml:"interval"`
|
||||
Multiplier float64 `yaml:"multiplier"`
|
||||
MaxInterval time.Duration `yaml:"max_interval"`
|
||||
MaxTime time.Duration `yaml:"max_time"`
|
||||
}
|
||||
|
||||
// Tunnel defines a tunnel.
|
||||
type Tunnel struct {
|
||||
Protocol string `yaml:"proto,omitempty"`
|
||||
|
|
@ -42,12 +26,13 @@ type Tunnel struct {
|
|||
|
||||
// ClientConfig is a tunnel client configuration.
|
||||
type ClientConfig struct {
|
||||
ServerAddr string `yaml:"server_addr"`
|
||||
TLSCrt string `yaml:"tls_crt"`
|
||||
TLSKey string `yaml:"tls_key"`
|
||||
RootCA string `yaml:"root_ca"`
|
||||
Backoff BackoffConfig `yaml:"backoff"`
|
||||
Tunnels map[string]*Tunnel `yaml:"tunnels"`
|
||||
ServerAddr string `yaml:"server_addr"`
|
||||
TLSCrt string `yaml:"tls_crt"`
|
||||
TLSKey string `yaml:"tls_key"`
|
||||
RootCA string `yaml:"root_ca"`
|
||||
Tunnels map[string]*Tunnel `yaml:"tunnels"`
|
||||
Backoff connection.BackoffConfig `yaml:"backoff"`
|
||||
KeepAlive connection.KeepAliveConfig `yaml:"keep_alive"`
|
||||
}
|
||||
|
||||
func loadClientConfigFromFile(file string) (*ClientConfig, error) {
|
||||
|
|
@ -57,14 +42,10 @@ func loadClientConfigFromFile(file string) (*ClientConfig, error) {
|
|||
}
|
||||
|
||||
c := ClientConfig{
|
||||
TLSCrt: filepath.Join(filepath.Dir(file), "client.crt"),
|
||||
TLSKey: filepath.Join(filepath.Dir(file), "client.key"),
|
||||
Backoff: BackoffConfig{
|
||||
Interval: DefaultBackoffInterval,
|
||||
Multiplier: DefaultBackoffMultiplier,
|
||||
MaxInterval: DefaultBackoffMaxInterval,
|
||||
MaxTime: DefaultBackoffMaxTime,
|
||||
},
|
||||
TLSCrt: filepath.Join(filepath.Dir(file), "client.crt"),
|
||||
TLSKey: filepath.Join(filepath.Dir(file), "client.key"),
|
||||
Backoff: *connection.NewDefaultBackoffConfig(),
|
||||
KeepAlive: *connection.NewDefaultKeepAliveConfig(),
|
||||
}
|
||||
|
||||
if err = yaml.Unmarshal(buf, &c); err != nil {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ Commands:
|
|||
|
||||
Examples:
|
||||
tunnel start www ssh
|
||||
tunnel -config config.yaml -log-level 2 start ssh
|
||||
tunnel -config config.yaml -logLevel 2 start ssh
|
||||
tunnel start-all
|
||||
|
||||
config.yaml:
|
||||
|
|
@ -45,9 +45,11 @@ config.yaml:
|
|||
|
||||
Author:
|
||||
Written by M. Matczuk (mmatczuk@gmail.com)
|
||||
Forked by H. Tribus (hons82@gmail.com)
|
||||
|
||||
|
||||
Bugs:
|
||||
Submit bugs to https://github.com/mmatczuk/go-http-tunnel/issues
|
||||
Submit bugs to https://github.com/hons82/go-http-tunnel/issues
|
||||
`
|
||||
|
||||
func init() {
|
||||
|
|
@ -68,7 +70,7 @@ type options struct {
|
|||
|
||||
func parseArgs() (*options, error) {
|
||||
config := flag.String("config", "tunnel.yml", "Path to tunnel configuration file")
|
||||
logLevel := flag.Int("log-level", 1, "Level of messages to log, 0-3")
|
||||
logLevel := flag.Int("logLevel", 1, "Level of messages to log, 0-3")
|
||||
version := flag.Bool("version", false, "Prints tunnel version")
|
||||
flag.Parse()
|
||||
|
||||
|
|
|
|||
|
|
@ -13,14 +13,16 @@ import (
|
|||
"net/url"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/cenkalti/backoff"
|
||||
"github.com/mmatczuk/go-http-tunnel"
|
||||
"github.com/mmatczuk/go-http-tunnel/id"
|
||||
"github.com/mmatczuk/go-http-tunnel/log"
|
||||
"github.com/mmatczuk/go-http-tunnel/proto"
|
||||
tunnel "github.com/hons82/go-http-tunnel"
|
||||
"github.com/hons82/go-http-tunnel/connection"
|
||||
"github.com/hons82/go-http-tunnel/id"
|
||||
"github.com/hons82/go-http-tunnel/log"
|
||||
"github.com/hons82/go-http-tunnel/proto"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
@ -93,7 +95,12 @@ func main() {
|
|||
if err != nil {
|
||||
fatal("failed to dump config: %s", err)
|
||||
}
|
||||
logger.Log("config", string(b))
|
||||
for _, value := range strings.Split(string(b), "\n") {
|
||||
logger.Log(
|
||||
"level", 1,
|
||||
"config", value,
|
||||
)
|
||||
}
|
||||
|
||||
client, err := tunnel.NewClient(&tunnel.ClientConfig{
|
||||
ServerAddr: config.ServerAddr,
|
||||
|
|
@ -102,6 +109,7 @@ func main() {
|
|||
Tunnels: tunnels(config.Tunnels),
|
||||
Proxy: proxy(config.Tunnels, logger),
|
||||
Logger: logger,
|
||||
KeepAlive: config.KeepAlive,
|
||||
})
|
||||
if err != nil {
|
||||
fatal("failed to create client: %s", err)
|
||||
|
|
@ -143,7 +151,7 @@ func tlsConfig(config *ClientConfig) (*tls.Config, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func expBackoff(c BackoffConfig) *backoff.ExponentialBackOff {
|
||||
func expBackoff(c connection.BackoffConfig) *backoff.ExponentialBackOff {
|
||||
b := backoff.NewExponentialBackOff()
|
||||
b.InitialInterval = c.Interval
|
||||
b.Multiplier = c.Multiplier
|
||||
|
|
|
|||
61
cmd/tunneld/api.go
Normal file
61
cmd/tunneld/api.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright (C) 2021 Tribus Hannes
|
||||
// Use of this source code is governed by an AGPL-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
tunnel "github.com/hons82/go-http-tunnel"
|
||||
"github.com/hons82/go-http-tunnel/log"
|
||||
)
|
||||
|
||||
// ApiConfig defines configuration for the API.
|
||||
type ApiConfig struct {
|
||||
// Addr is TCP address to listen for client connections. If empty ":0" is used.
|
||||
Addr string
|
||||
//
|
||||
Server *tunnel.Server
|
||||
// Logger is optional logger. If nil logging is disabled.
|
||||
Logger log.Logger
|
||||
}
|
||||
|
||||
func initAPIServer(config *ApiConfig) {
|
||||
|
||||
logger := config.Logger
|
||||
if logger == nil {
|
||||
logger = log.NewNopLogger()
|
||||
}
|
||||
|
||||
http.HandleFunc("/api/clients/list", http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
logger.Log(
|
||||
"level", 2,
|
||||
"action", "start client list",
|
||||
)
|
||||
info := config.Server.GetClientInfo()
|
||||
data, err := json.Marshal(info)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
e := fmt.Sprintf("Error on unmarshall item %s", err)
|
||||
w.Write([]byte(e))
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(data)
|
||||
|
||||
logger.Log(
|
||||
"level", 3,
|
||||
"action", "transferred",
|
||||
"bytes", len(data),
|
||||
)
|
||||
},
|
||||
))
|
||||
|
||||
// Wrap our server with our gzip handler to gzip compress all responses.
|
||||
fatal("can not listen on: %s", http.ListenAndServe(config.Addr, nil))
|
||||
}
|
||||
|
|
@ -10,5 +10,4 @@ const banner = `
|
|||
/ / __/ __ \ / /_/ / / / / / / /_/ / / __/ / / / __ \/ __ \/ _ \/ /
|
||||
/ /_/ / /_/ / / __ / / / / / / ____/ / /_/ /_/ / / / / / / / __/ /
|
||||
\____/\____/ /_/ /_/ /_/ /_/ /_/ \__/\__,_/_/ /_/_/ /_/\___/_/
|
||||
github.com/mmatczuk/go-http-tunnel
|
||||
`
|
||||
github.com/hons82/go-http-tunnel`
|
||||
|
|
|
|||
|
|
@ -23,9 +23,10 @@ Example:
|
|||
|
||||
Author:
|
||||
Written by M. Matczuk (mmatczuk@gmail.com)
|
||||
Forked by H. Tribus (hons82@gmail.com)
|
||||
|
||||
Bugs:
|
||||
Submit bugs to https://github.com/mmatczuk/go-http-tunnel/issues
|
||||
Submit bugs to https://github.com/hons82/go-http-tunnel/issues
|
||||
`
|
||||
|
||||
func init() {
|
||||
|
|
@ -38,41 +39,50 @@ func init() {
|
|||
|
||||
// options specify arguments read command line arguments.
|
||||
type options struct {
|
||||
httpAddr string
|
||||
httpsAddr string
|
||||
tunnelAddr string
|
||||
sniAddr string
|
||||
tlsCrt string
|
||||
tlsKey string
|
||||
rootCA string
|
||||
clients string
|
||||
logLevel int
|
||||
version bool
|
||||
httpAddr string
|
||||
httpsAddr string
|
||||
tunnelAddr string
|
||||
apiAddr string
|
||||
sniAddr string
|
||||
tlsCrt string
|
||||
tlsKey string
|
||||
rootCA string
|
||||
clients string
|
||||
keepAlive string
|
||||
debounceLog string
|
||||
logLevel int
|
||||
version bool
|
||||
}
|
||||
|
||||
func parseArgs() *options {
|
||||
httpAddr := flag.String("httpAddr", ":80", "Public address for HTTP connections, empty string to disable")
|
||||
httpsAddr := flag.String("httpsAddr", ":443", "Public address listening for HTTPS connections, emptry string to disable")
|
||||
tunnelAddr := flag.String("tunnelAddr", ":5223", "Public address listening for tunnel client")
|
||||
apiAddr := flag.String("apiAddr", ":5091", "Public address for HTTP API to get tunnels info")
|
||||
sniAddr := flag.String("sniAddr", "", "Public address listening for TLS SNI connections, empty string to disable")
|
||||
tlsCrt := flag.String("tlsCrt", "server.crt", "Path to a TLS certificate file")
|
||||
tlsKey := flag.String("tlsKey", "server.key", "Path to a TLS key file")
|
||||
rootCA := flag.String("rootCA", "", "Path to the trusted certificate chian used for client certificate authentication, if empty any client certificate is accepted")
|
||||
clients := flag.String("clients", "", "Comma-separated list of tunnel client ids, if empty accept all clients")
|
||||
logLevel := flag.Int("log-level", 1, "Level of messages to log, 0-3")
|
||||
clients := flag.String("clients", "", "Path to a properties file that contains a list of 'host=tunnelClientId's, if empty accept all clients")
|
||||
keepAlive := flag.String("keepAlive", "45s", "TCP keep alive configuration")
|
||||
debounceLog := flag.String("debounceLog", "2s", "How long to keep disconnected log message before actually writing it to the log")
|
||||
logLevel := flag.Int("logLevel", 1, "Level of messages to log, 0-3")
|
||||
version := flag.Bool("version", false, "Prints tunneld version")
|
||||
flag.Parse()
|
||||
|
||||
return &options{
|
||||
httpAddr: *httpAddr,
|
||||
httpsAddr: *httpsAddr,
|
||||
tunnelAddr: *tunnelAddr,
|
||||
sniAddr: *sniAddr,
|
||||
tlsCrt: *tlsCrt,
|
||||
tlsKey: *tlsKey,
|
||||
rootCA: *rootCA,
|
||||
clients: *clients,
|
||||
logLevel: *logLevel,
|
||||
version: *version,
|
||||
httpAddr: *httpAddr,
|
||||
httpsAddr: *httpsAddr,
|
||||
tunnelAddr: *tunnelAddr,
|
||||
apiAddr: *apiAddr,
|
||||
sniAddr: *sniAddr,
|
||||
tlsCrt: *tlsCrt,
|
||||
tlsKey: *tlsKey,
|
||||
rootCA: *rootCA,
|
||||
clients: *clients,
|
||||
keepAlive: *keepAlive,
|
||||
debounceLog: *debounceLog,
|
||||
logLevel: *logLevel,
|
||||
version: *version,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,15 +9,18 @@ import (
|
|||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
"github.com/mmatczuk/go-http-tunnel"
|
||||
"github.com/mmatczuk/go-http-tunnel/id"
|
||||
"github.com/mmatczuk/go-http-tunnel/log"
|
||||
"github.com/bep/debounce"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
tunnel "github.com/hons82/go-http-tunnel"
|
||||
"github.com/hons82/go-http-tunnel/connection"
|
||||
"github.com/hons82/go-http-tunnel/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
@ -39,6 +42,19 @@ func main() {
|
|||
|
||||
autoSubscribe := opts.clients == ""
|
||||
|
||||
keepAlive, err := connection.Parse(opts.keepAlive)
|
||||
if err != nil {
|
||||
fatal("failed to parse KeepaliveConfig: %s", err)
|
||||
}
|
||||
|
||||
debounceLog, err := time.ParseDuration(opts.debounceLog)
|
||||
if err != nil {
|
||||
fatal("failed to parse keepalive interval [%s], [%v]", opts.debounceLog, err)
|
||||
}
|
||||
debounced := &tunnel.Debounced{
|
||||
Execute: debounce.New(debounceLog),
|
||||
}
|
||||
|
||||
// setup server
|
||||
server, err := tunnel.NewServer(&tunnel.ServerConfig{
|
||||
Addr: opts.tunnelAddr,
|
||||
|
|
@ -46,35 +62,126 @@ func main() {
|
|||
AutoSubscribe: autoSubscribe,
|
||||
TLSConfig: tlsconf,
|
||||
Logger: logger,
|
||||
KeepAlive: *keepAlive,
|
||||
Debounce: *debounced,
|
||||
})
|
||||
if err != nil {
|
||||
fatal("failed to create server: %s", err)
|
||||
}
|
||||
|
||||
if !autoSubscribe {
|
||||
for _, c := range strings.Split(opts.clients, ",") {
|
||||
if c == "" {
|
||||
fatal("empty client id")
|
||||
}
|
||||
identifier := id.ID{}
|
||||
err := identifier.UnmarshalText([]byte(c))
|
||||
// First load immediatly
|
||||
server.ReloadTunnels(opts.clients)
|
||||
|
||||
// Watch for the file to change
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
logger.Log(
|
||||
"level", 1,
|
||||
"action", "could not create file watcher",
|
||||
"err", err,
|
||||
)
|
||||
} else {
|
||||
defer watcher.Close()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
logger.Log(
|
||||
"level", 3,
|
||||
"action", "watched file changed",
|
||||
"file", event.Name,
|
||||
"action", event.Op.String(),
|
||||
)
|
||||
if event.Op&fsnotify.Write == fsnotify.Write ||
|
||||
event.Op&fsnotify.Create == fsnotify.Create ||
|
||||
event.Op&fsnotify.Remove == fsnotify.Remove {
|
||||
server.Clear()
|
||||
server.ReloadTunnels(opts.clients)
|
||||
}
|
||||
case err, ok := <-watcher.Errors:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
logger.Log(
|
||||
"level", 2,
|
||||
"action", "error watching file",
|
||||
"err", err,
|
||||
)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
err = watcher.Add(opts.clients)
|
||||
if err != nil {
|
||||
fatal("invalid identifier %q: %s", c, err)
|
||||
logger.Log(
|
||||
"level", 1,
|
||||
"action", "add watch failed",
|
||||
"file", opts.clients,
|
||||
"err", err,
|
||||
)
|
||||
}
|
||||
server.Subscribe(identifier)
|
||||
}
|
||||
}
|
||||
|
||||
// start API
|
||||
if opts.apiAddr != "" {
|
||||
go func() {
|
||||
logger.Log(
|
||||
"level", 1,
|
||||
"action", "start api",
|
||||
"addr", opts.apiAddr,
|
||||
)
|
||||
go initAPIServer(&ApiConfig{
|
||||
Addr: opts.apiAddr,
|
||||
Server: server,
|
||||
Logger: logger,
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
// start HTTP
|
||||
if opts.httpAddr != "" {
|
||||
go func() {
|
||||
logger.Log(
|
||||
"level", 1,
|
||||
"action", "start http",
|
||||
"addr", opts.httpAddr,
|
||||
)
|
||||
s := &http.Server{
|
||||
Addr: opts.httpAddr,
|
||||
}
|
||||
if opts.httpsAddr != "" {
|
||||
logger.Log(
|
||||
"level", 1,
|
||||
"action", "start http redirect",
|
||||
"addr", opts.httpAddr,
|
||||
)
|
||||
|
||||
fatal("failed to start HTTP: %s", http.ListenAndServe(opts.httpAddr, server))
|
||||
_, tlsPort, err := net.SplitHostPort(opts.httpsAddr)
|
||||
if err != nil {
|
||||
fatal("failed to get https port: %s", err)
|
||||
}
|
||||
s.Handler = http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
host, _, err := net.SplitHostPort(r.Host)
|
||||
if err != nil {
|
||||
host = r.Host
|
||||
}
|
||||
u := r.URL
|
||||
u.Host = net.JoinHostPort(host, tlsPort)
|
||||
u.Scheme = "https"
|
||||
http.Redirect(w, r, u.String(), http.StatusMovedPermanently)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
logger.Log(
|
||||
"level", 1,
|
||||
"action", "start http",
|
||||
"addr", opts.httpAddr,
|
||||
)
|
||||
s.Handler = server
|
||||
}
|
||||
fatal("failed to start HTTP: %s", s.ListenAndServe())
|
||||
}()
|
||||
}
|
||||
|
||||
|
|
@ -90,6 +197,9 @@ func main() {
|
|||
s := &http.Server{
|
||||
Addr: opts.httpsAddr,
|
||||
Handler: server,
|
||||
TLSConfig: &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
},
|
||||
}
|
||||
http2.ConfigureServer(s, nil)
|
||||
|
||||
|
|
@ -128,11 +238,7 @@ func tlsConfig(opts *options) (*tls.Config, error) {
|
|||
ClientCAs: roots,
|
||||
SessionTicketsDisabled: true,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||
PreferServerCipherSuites: true,
|
||||
NextProtos: []string{"h2"},
|
||||
NextProtos: []string{"h2"},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
44
connection/backoff.go
Normal file
44
connection/backoff.go
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (C) 2017 Michał Matczuk
|
||||
// Use of this source code is governed by an AGPL-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package connection
|
||||
|
||||
import "time"
|
||||
|
||||
// Backoff defines behavior of staggering reconnection retries.
|
||||
type Backoff interface {
|
||||
// Next returns the duration to sleep before retrying to reconnect.
|
||||
// If the returned value is negative, the retry is aborted.
|
||||
NextBackOff() time.Duration
|
||||
|
||||
// Reset is used to signal a reconnection was successful and next
|
||||
// call to Next should return desired time duration for 1st reconnection
|
||||
// attempt.
|
||||
Reset()
|
||||
}
|
||||
|
||||
func NewDefaultBackoffConfig() *BackoffConfig {
|
||||
return &BackoffConfig{
|
||||
Interval: DefaultBackoffInterval,
|
||||
Multiplier: DefaultBackoffMultiplier,
|
||||
MaxInterval: DefaultBackoffMaxInterval,
|
||||
MaxTime: DefaultBackoffMaxTime,
|
||||
}
|
||||
}
|
||||
|
||||
// Default backoff configuration.
|
||||
const (
|
||||
DefaultBackoffInterval = 500 * time.Millisecond
|
||||
DefaultBackoffMultiplier = 1.5
|
||||
DefaultBackoffMaxInterval = 60 * time.Second
|
||||
DefaultBackoffMaxTime = 15 * time.Minute
|
||||
)
|
||||
|
||||
// BackoffConfig defines behavior of staggering reconnection retries.
|
||||
type BackoffConfig struct {
|
||||
Interval time.Duration `yaml:"interval"`
|
||||
Multiplier float64 `yaml:"multiplier"`
|
||||
MaxInterval time.Duration `yaml:"max_interval"`
|
||||
MaxTime time.Duration `yaml:"max_time"`
|
||||
}
|
||||
68
connection/keepalive.go
Normal file
68
connection/keepalive.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (C) 2017 Michał Matczuk
|
||||
// Use of this source code is governed by an AGPL-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package connection
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Default keepAlive configuration.
|
||||
const (
|
||||
DefaultKeepAliveInterval = 25 * time.Second
|
||||
)
|
||||
|
||||
// KeepAliveConfig defines if and how the keepAlive package is sent.
|
||||
type KeepAliveConfig struct {
|
||||
KeepAliveInterval time.Duration `yaml:"interval"`
|
||||
}
|
||||
|
||||
func (k *KeepAliveConfig) Set(conn net.Conn) error {
|
||||
return keepAlive(conn, k.KeepAliveInterval)
|
||||
}
|
||||
|
||||
func (k *KeepAliveConfig) String() string {
|
||||
return fmt.Sprintf("KeepAlive { interval: %v }", (*k).KeepAliveInterval)
|
||||
}
|
||||
|
||||
func Parse(interval string) (*KeepAliveConfig, error) {
|
||||
_interval, err := time.ParseDuration(interval)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse keepalive interval [%s], [%v]", interval, err)
|
||||
}
|
||||
return &KeepAliveConfig{
|
||||
KeepAliveInterval: _interval,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewDefaultKeepAliveConfig() *KeepAliveConfig {
|
||||
return &KeepAliveConfig{
|
||||
KeepAliveInterval: DefaultKeepAliveInterval,
|
||||
}
|
||||
}
|
||||
|
||||
func SetDefaultKeepAlive(conn net.Conn) error {
|
||||
return keepAlive(conn, DefaultKeepAliveInterval)
|
||||
}
|
||||
|
||||
func keepAlive(conn net.Conn, interval time.Duration) error {
|
||||
c, ok := conn.(*net.TCPConn)
|
||||
if !ok {
|
||||
return fmt.Errorf("bad connection type: %T", c)
|
||||
}
|
||||
|
||||
if err := c.SetKeepAlive(interval > 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if interval > 0 {
|
||||
if err := c.SetKeepAlivePeriod(interval); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
22
errors.go
22
errors.go
|
|
@ -4,12 +4,24 @@
|
|||
|
||||
package tunnel
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
var (
|
||||
errClientNotSubscribed = errors.New("client not subscribed")
|
||||
errClientNotConnected = errors.New("client not connected")
|
||||
errClientAlreadyConnected = errors.New("client already connected")
|
||||
errClientNotSubscribed = newError("clientNotSubscribed.html", "client not subscribed")
|
||||
errClientNotConnected = newError("clientNotConnected.html", "client not connected")
|
||||
errClientAlreadyConnected = newError("clientAlreadyConnected.html", "client already connected")
|
||||
|
||||
errUnauthorised = errors.New("unauthorised")
|
||||
errUnauthorised = newError("unauthorised.html", "unauthorised")
|
||||
)
|
||||
|
||||
func newError(fileName string, defaultMsg string) error {
|
||||
content, err := ioutil.ReadFile("html/errors/" + fileName)
|
||||
if err != nil {
|
||||
// handle the case where the file doesn't exist
|
||||
return errors.New(defaultMsg)
|
||||
}
|
||||
return errors.New(string(content))
|
||||
}
|
||||
|
|
|
|||
51
fileutil/file.go
Normal file
51
fileutil/file.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package fileutil
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AppConfigProperties map[string]string
|
||||
|
||||
func ReadPropertiesFile(filename string) (AppConfigProperties, error) {
|
||||
config := AppConfigProperties{}
|
||||
|
||||
if len(filename) == 0 {
|
||||
return config, nil
|
||||
}
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if equal := strings.Index(line, ";"); equal >= 0 {
|
||||
if key := strings.TrimSpace(line[:equal]); len(key) > 0 {
|
||||
value := ""
|
||||
if len(line) > equal {
|
||||
value = strings.TrimSpace(line[equal+1:])
|
||||
}
|
||||
config[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// IsDirectory determines if a file represented by `path` is a directory or not
|
||||
func IsDirectory(path string) (bool, error) {
|
||||
fileInfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return fileInfo.IsDir(), err
|
||||
}
|
||||
5
go.mod
5
go.mod
|
|
@ -1,11 +1,12 @@
|
|||
module github.com/mmatczuk/go-http-tunnel
|
||||
module github.com/hons82/go-http-tunnel
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/bep/debounce v1.2.0
|
||||
github.com/calmh/luhn v2.0.0+incompatible
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible
|
||||
github.com/felixge/tcpkeepalive v0.0.0-20160804073959-5bb0b2dea91e
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/golang/mock v1.2.0
|
||||
github.com/inconshreveable/go-vhost v0.0.0-20160627193104-06d84117953b
|
||||
golang.org/x/net v0.0.0-20171123081856-c7086645de24
|
||||
|
|
|
|||
8
go.sum
8
go.sum
|
|
@ -1,15 +1,19 @@
|
|||
github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo=
|
||||
github.com/bep/debounce v1.2.0/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
|
||||
github.com/calmh/luhn v2.0.0+incompatible h1:xHkbAc8FBgMiGUaKsiYcwtf8xhSXVtRKA2NhY7hFCAc=
|
||||
github.com/calmh/luhn v2.0.0+incompatible/go.mod h1:70IGmMi0GKRs073gl/oH5/yiJnTt61h35YQhvo/k3Cc=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/felixge/tcpkeepalive v0.0.0-20160804073959-5bb0b2dea91e h1:mVIjvOd7NckIwf9J4hLB2YWXBYjhREF4vBeZXZ8mrWM=
|
||||
github.com/felixge/tcpkeepalive v0.0.0-20160804073959-5bb0b2dea91e/go.mod h1:z0yk3Pix6k848RFizhkU4uY36ts5pB1t3toBwudGbBo=
|
||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/inconshreveable/go-vhost v0.0.0-20160627193104-06d84117953b h1:IpLPmn6Re21F0MaV6Zsc5RdSE6KuoFpWmHiUSEs3PrE=
|
||||
github.com/inconshreveable/go-vhost v0.0.0-20160627193104-06d84117953b/go.mod h1:aA6DnFhALT3zH0y+A39we+zbrdMC2N0X/q21e6FI0LU=
|
||||
golang.org/x/net v0.0.0-20171123081856-c7086645de24 h1:z0cmn+BVQSCN8exp26jnHqHXHIvTlqIYhjHln4k/UAU=
|
||||
golang.org/x/net v0.0.0-20171123081856-c7086645de24/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.1.1-0.20171102192421-88f656faf3f3 h1:OxMYHd6bm+jH+TI7NBCb/CaYk6pMJnBC8GIzIi68Hk4=
|
||||
golang.org/x/text v0.1.1-0.20171102192421-88f656faf3f3/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
|
|
|||
183
html/errors/clientAlreadyConnected.html
Normal file
183
html/errors/clientAlreadyConnected.html
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Client Already Connected</title>
|
||||
<style>
|
||||
* {
|
||||
margin:0px auto;
|
||||
padding: 0px;
|
||||
text-align:center;
|
||||
}
|
||||
body {
|
||||
background-color: #D4D9ED;
|
||||
}
|
||||
.cont_principal {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.cont_error {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
top: 50%;
|
||||
margin-top:-150px;
|
||||
}
|
||||
.cont_error > h1 {
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-weight: 400;
|
||||
font-size:150px;
|
||||
color:#fff;
|
||||
position: relative;
|
||||
left:-100%;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.cont_error > p {
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-weight: 300;
|
||||
font-size:24px;
|
||||
letter-spacing: 5px;
|
||||
color:#9294AE;
|
||||
position: relative;
|
||||
left:100%;
|
||||
transition: all 0.5s;
|
||||
transition-delay: 0.5s;
|
||||
-webkit-transition: all 0.5s;
|
||||
-webkit-transition-delay: 0.5s;
|
||||
}
|
||||
.cont_aura_1 {
|
||||
position:absolute;
|
||||
width:300px;
|
||||
height: 120%;
|
||||
top:25px;
|
||||
right: -340px;
|
||||
background-color: #8A65DF;
|
||||
box-shadow: 0px 0px 60px 20px rgba(137,100,222,0.5);
|
||||
-webkit-transition: all 0.5s;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.cont_aura_2 {
|
||||
position:absolute;
|
||||
width:100%;
|
||||
height: 300px;
|
||||
right:-10%;
|
||||
bottom:-301px;
|
||||
background-color: #8B65E4;
|
||||
box-shadow: 0px 0px 60px 10px rgba(131, 95, 214, 0.5),0px 0px 20px 0px rgba(0,0,0,0.1);
|
||||
z-index:5;
|
||||
transition: all 0.5s;
|
||||
-webkit-transition: all 0.5s;
|
||||
}
|
||||
.cont_error_active > .cont_error > h1 {
|
||||
left:0%;
|
||||
}
|
||||
.cont_error_active > .cont_error > p {
|
||||
left:0%;
|
||||
}
|
||||
.cont_error_active > .cont_aura_2 {
|
||||
animation-name: animation_error_2;
|
||||
animation-duration: 4s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
.cont_error_active > .cont_aura_1 {
|
||||
transform: rotate(20deg);
|
||||
right:-170px;
|
||||
animation-name: animation_error_1;
|
||||
animation-duration: 4s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
}
|
||||
@-webkit-keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@-o-keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@-moz-keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
@-o-keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@-moz-keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
@keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
window.onload = function(){
|
||||
document.querySelector('.cont_principal').className= "cont_principal cont_error_active";
|
||||
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="cont_principal">
|
||||
<div class="cont_error">
|
||||
<h1>Oops</h1>
|
||||
<p>Somebody else already claimed this adress.</p>
|
||||
</div>
|
||||
<div class="cont_aura_1"></div>
|
||||
<div class="cont_aura_2"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
183
html/errors/clientNotConnected.html
Normal file
183
html/errors/clientNotConnected.html
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Client Not Connected</title>
|
||||
<style>
|
||||
* {
|
||||
margin:0px auto;
|
||||
padding: 0px;
|
||||
text-align:center;
|
||||
}
|
||||
body {
|
||||
background-color: #D4D9ED;
|
||||
}
|
||||
.cont_principal {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.cont_error {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
top: 50%;
|
||||
margin-top:-150px;
|
||||
}
|
||||
.cont_error > h1 {
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-weight: 400;
|
||||
font-size:150px;
|
||||
color:#fff;
|
||||
position: relative;
|
||||
left:-100%;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.cont_error > p {
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-weight: 300;
|
||||
font-size:24px;
|
||||
letter-spacing: 5px;
|
||||
color:#9294AE;
|
||||
position: relative;
|
||||
left:100%;
|
||||
transition: all 0.5s;
|
||||
transition-delay: 0.5s;
|
||||
-webkit-transition: all 0.5s;
|
||||
-webkit-transition-delay: 0.5s;
|
||||
}
|
||||
.cont_aura_1 {
|
||||
position:absolute;
|
||||
width:300px;
|
||||
height: 120%;
|
||||
top:25px;
|
||||
right: -340px;
|
||||
background-color: #8A65DF;
|
||||
box-shadow: 0px 0px 60px 20px rgba(137,100,222,0.5);
|
||||
-webkit-transition: all 0.5s;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.cont_aura_2 {
|
||||
position:absolute;
|
||||
width:100%;
|
||||
height: 300px;
|
||||
right:-10%;
|
||||
bottom:-301px;
|
||||
background-color: #8B65E4;
|
||||
box-shadow: 0px 0px 60px 10px rgba(131, 95, 214, 0.5),0px 0px 20px 0px rgba(0,0,0,0.1);
|
||||
z-index:5;
|
||||
transition: all 0.5s;
|
||||
-webkit-transition: all 0.5s;
|
||||
}
|
||||
.cont_error_active > .cont_error > h1 {
|
||||
left:0%;
|
||||
}
|
||||
.cont_error_active > .cont_error > p {
|
||||
left:0%;
|
||||
}
|
||||
.cont_error_active > .cont_aura_2 {
|
||||
animation-name: animation_error_2;
|
||||
animation-duration: 4s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
.cont_error_active > .cont_aura_1 {
|
||||
transform: rotate(20deg);
|
||||
right:-170px;
|
||||
animation-name: animation_error_1;
|
||||
animation-duration: 4s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
}
|
||||
@-webkit-keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@-o-keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@-moz-keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
@-o-keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@-moz-keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
@keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
window.onload = function(){
|
||||
document.querySelector('.cont_principal').className= "cont_principal cont_error_active";
|
||||
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="cont_principal">
|
||||
<div class="cont_error">
|
||||
<h1>Oops</h1>
|
||||
<p>This client is currently not connected.</p>
|
||||
</div>
|
||||
<div class="cont_aura_1"></div>
|
||||
<div class="cont_aura_2"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
183
html/errors/clientNotSubscribed.html
Normal file
183
html/errors/clientNotSubscribed.html
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Client Not Subscribed</title>
|
||||
<style>
|
||||
* {
|
||||
margin:0px auto;
|
||||
padding: 0px;
|
||||
text-align:center;
|
||||
}
|
||||
body {
|
||||
background-color: #D4D9ED;
|
||||
}
|
||||
.cont_principal {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.cont_error {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
top: 50%;
|
||||
margin-top:-150px;
|
||||
}
|
||||
.cont_error > h1 {
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-weight: 400;
|
||||
font-size:150px;
|
||||
color:#fff;
|
||||
position: relative;
|
||||
left:-100%;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.cont_error > p {
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-weight: 300;
|
||||
font-size:24px;
|
||||
letter-spacing: 5px;
|
||||
color:#9294AE;
|
||||
position: relative;
|
||||
left:100%;
|
||||
transition: all 0.5s;
|
||||
transition-delay: 0.5s;
|
||||
-webkit-transition: all 0.5s;
|
||||
-webkit-transition-delay: 0.5s;
|
||||
}
|
||||
.cont_aura_1 {
|
||||
position:absolute;
|
||||
width:300px;
|
||||
height: 120%;
|
||||
top:25px;
|
||||
right: -340px;
|
||||
background-color: #8A65DF;
|
||||
box-shadow: 0px 0px 60px 20px rgba(137,100,222,0.5);
|
||||
-webkit-transition: all 0.5s;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.cont_aura_2 {
|
||||
position:absolute;
|
||||
width:100%;
|
||||
height: 300px;
|
||||
right:-10%;
|
||||
bottom:-301px;
|
||||
background-color: #8B65E4;
|
||||
box-shadow: 0px 0px 60px 10px rgba(131, 95, 214, 0.5),0px 0px 20px 0px rgba(0,0,0,0.1);
|
||||
z-index:5;
|
||||
transition: all 0.5s;
|
||||
-webkit-transition: all 0.5s;
|
||||
}
|
||||
.cont_error_active > .cont_error > h1 {
|
||||
left:0%;
|
||||
}
|
||||
.cont_error_active > .cont_error > p {
|
||||
left:0%;
|
||||
}
|
||||
.cont_error_active > .cont_aura_2 {
|
||||
animation-name: animation_error_2;
|
||||
animation-duration: 4s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
.cont_error_active > .cont_aura_1 {
|
||||
transform: rotate(20deg);
|
||||
right:-170px;
|
||||
animation-name: animation_error_1;
|
||||
animation-duration: 4s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
}
|
||||
@-webkit-keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@-o-keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@-moz-keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
@-o-keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@-moz-keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
@keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
window.onload = function(){
|
||||
document.querySelector('.cont_principal').className= "cont_principal cont_error_active";
|
||||
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="cont_principal">
|
||||
<div class="cont_error">
|
||||
<h1>Oops</h1>
|
||||
<p>This client is currently unreachable.</p>
|
||||
</div>
|
||||
<div class="cont_aura_1"></div>
|
||||
<div class="cont_aura_2"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
183
html/errors/unauthorised.html
Normal file
183
html/errors/unauthorised.html
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Unauthorised</title>
|
||||
<style>
|
||||
* {
|
||||
margin:0px auto;
|
||||
padding: 0px;
|
||||
text-align:center;
|
||||
}
|
||||
body {
|
||||
background-color: #D4D9ED;
|
||||
}
|
||||
.cont_principal {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.cont_error {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
top: 50%;
|
||||
margin-top:-150px;
|
||||
}
|
||||
.cont_error > h1 {
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-weight: 400;
|
||||
font-size:150px;
|
||||
color:#fff;
|
||||
position: relative;
|
||||
left:-100%;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.cont_error > p {
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-weight: 300;
|
||||
font-size:24px;
|
||||
letter-spacing: 5px;
|
||||
color:#9294AE;
|
||||
position: relative;
|
||||
left:100%;
|
||||
transition: all 0.5s;
|
||||
transition-delay: 0.5s;
|
||||
-webkit-transition: all 0.5s;
|
||||
-webkit-transition-delay: 0.5s;
|
||||
}
|
||||
.cont_aura_1 {
|
||||
position:absolute;
|
||||
width:300px;
|
||||
height: 120%;
|
||||
top:25px;
|
||||
right: -340px;
|
||||
background-color: #8A65DF;
|
||||
box-shadow: 0px 0px 60px 20px rgba(137,100,222,0.5);
|
||||
-webkit-transition: all 0.5s;
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.cont_aura_2 {
|
||||
position:absolute;
|
||||
width:100%;
|
||||
height: 300px;
|
||||
right:-10%;
|
||||
bottom:-301px;
|
||||
background-color: #8B65E4;
|
||||
box-shadow: 0px 0px 60px 10px rgba(131, 95, 214, 0.5),0px 0px 20px 0px rgba(0,0,0,0.1);
|
||||
z-index:5;
|
||||
transition: all 0.5s;
|
||||
-webkit-transition: all 0.5s;
|
||||
}
|
||||
.cont_error_active > .cont_error > h1 {
|
||||
left:0%;
|
||||
}
|
||||
.cont_error_active > .cont_error > p {
|
||||
left:0%;
|
||||
}
|
||||
.cont_error_active > .cont_aura_2 {
|
||||
animation-name: animation_error_2;
|
||||
animation-duration: 4s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
.cont_error_active > .cont_aura_1 {
|
||||
transform: rotate(20deg);
|
||||
right:-170px;
|
||||
animation-name: animation_error_1;
|
||||
animation-duration: 4s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
}
|
||||
@-webkit-keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@-o-keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@-moz-keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@keyframes animation_error_1 {
|
||||
from {
|
||||
-webkit-transform: rotate(20deg);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(25deg);
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
@-o-keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@-moz-keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
@keyframes animation_error_2 {
|
||||
from { -webkit-transform: rotate(-15deg);
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
to { -webkit-transform: rotate(-20deg);
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
window.onload = function(){
|
||||
document.querySelector('.cont_principal').className= "cont_principal cont_error_active";
|
||||
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="cont_principal">
|
||||
<div class="cont_error">
|
||||
<h1>Unauthorised</h1>
|
||||
<p>This content is not for your eyes.</p>
|
||||
</div>
|
||||
<div class="cont_aura_1"></div>
|
||||
<div class="cont_aura_2"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -14,8 +14,8 @@ import (
|
|||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/mmatczuk/go-http-tunnel/log"
|
||||
"github.com/mmatczuk/go-http-tunnel/proto"
|
||||
"github.com/hons82/go-http-tunnel/log"
|
||||
"github.com/hons82/go-http-tunnel/proto"
|
||||
)
|
||||
|
||||
// HTTPProxy forwards HTTP traffic.
|
||||
|
|
|
|||
20
id/id.go
20
id/id.go
|
|
@ -173,3 +173,23 @@ func untypeoify(s string) string {
|
|||
s = strings.Replace(s, "8", "B", -1)
|
||||
return s
|
||||
}
|
||||
|
||||
// https://play.golang.org/p/Qg_uv_inCek
|
||||
// contains checks if a string is present in a slice
|
||||
func Contains(s []ID, item ID) bool {
|
||||
for _, v := range s {
|
||||
if v.Equals(item) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Remove(s []ID, item ID) ([]ID, bool) {
|
||||
for i, v := range s {
|
||||
if v.Equals(item) {
|
||||
return append(s[:i], s[i+1:]...), true
|
||||
}
|
||||
}
|
||||
return s, false
|
||||
}
|
||||
|
|
|
|||
25
id/ptls.go
25
id/ptls.go
|
|
@ -6,19 +6,25 @@ package id
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type IDInfo struct {
|
||||
Client string
|
||||
}
|
||||
|
||||
var emptyID [32]byte
|
||||
var emptyIDInfo = &IDInfo{}
|
||||
|
||||
// PeerID is modified https://github.com/andrew-d/ptls/blob/b89c7dcc94630a77f225a48befd3710144c7c10e/ptls.go#L81
|
||||
func PeerID(conn *tls.Conn) (ID, error) {
|
||||
func PeerID(conn *tls.Conn) (ID, IDInfo, error) {
|
||||
// Try a TLS connection over the given connection. We explicitly perform
|
||||
// the handshake, since we want to maintain the invariant that, if this
|
||||
// function returns successfully, then the connection should be valid
|
||||
// and verified.
|
||||
if err := conn.Handshake(); err != nil {
|
||||
return emptyID, err
|
||||
return emptyID, *emptyIDInfo, err
|
||||
}
|
||||
|
||||
cs := conn.ConnectionState()
|
||||
|
|
@ -26,14 +32,23 @@ func PeerID(conn *tls.Conn) (ID, error) {
|
|||
// We should have exactly one peer certificate.
|
||||
certs := cs.PeerCertificates
|
||||
if cl := len(certs); cl != 1 {
|
||||
return emptyID, ImproperCertsNumberError{cl}
|
||||
return emptyID, *emptyIDInfo, ImproperCertsNumberError{cl}
|
||||
}
|
||||
|
||||
// Get remote cert's ID.
|
||||
remoteCert := certs[0]
|
||||
remoteID := New(remoteCert.Raw)
|
||||
remoteID := New(remoteID(*remoteCert))
|
||||
remoteIDInfo := &IDInfo{
|
||||
Client: remoteCert.Issuer.SerialNumber,
|
||||
}
|
||||
return remoteID, *remoteIDInfo, nil
|
||||
}
|
||||
|
||||
return remoteID, nil
|
||||
func remoteID(c x509.Certificate) []byte {
|
||||
if c.Issuer.SerialNumber != "" {
|
||||
return []byte(c.Issuer.SerialNumber)
|
||||
}
|
||||
return c.Raw
|
||||
}
|
||||
|
||||
// ImproperCertsNumberError is returned from Server/Client whenever the remote
|
||||
|
|
|
|||
|
|
@ -20,9 +20,10 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mmatczuk/go-http-tunnel"
|
||||
"github.com/mmatczuk/go-http-tunnel/log"
|
||||
"github.com/mmatczuk/go-http-tunnel/proto"
|
||||
tunnel "github.com/hons82/go-http-tunnel"
|
||||
"github.com/hons82/go-http-tunnel/connection"
|
||||
"github.com/hons82/go-http-tunnel/log"
|
||||
"github.com/hons82/go-http-tunnel/proto"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -95,6 +96,9 @@ func makeTunnelServer(t testing.TB) *tunnel.Server {
|
|||
AutoSubscribe: true,
|
||||
TLSConfig: tlsConfig(),
|
||||
Logger: log.NewStdLogger(),
|
||||
KeepAlive: connection.KeepAliveConfig{
|
||||
KeepAliveInterval: connection.DefaultKeepAliveInterval,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -137,6 +141,9 @@ func makeTunnelClient(t testing.TB, serverAddr string, httpLocalAddr, httpAddr,
|
|||
TCP: tcpProxy.Proxy,
|
||||
}),
|
||||
Logger: log.NewStdLogger(),
|
||||
KeepAlive: connection.KeepAliveConfig{
|
||||
KeepAliveInterval: connection.DefaultKeepAliveInterval,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -322,14 +329,12 @@ func tlsConfig() *tls.Config {
|
|||
}
|
||||
|
||||
c := &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
ClientAuth: tls.RequireAnyClientCert,
|
||||
SessionTicketsDisabled: true,
|
||||
InsecureSkipVerify: true,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||
PreferServerCipherSuites: true,
|
||||
NextProtos: []string{"h2"},
|
||||
Certificates: []tls.Certificate{cert},
|
||||
ClientAuth: tls.RequireAnyClientCert,
|
||||
SessionTicketsDisabled: true,
|
||||
InsecureSkipVerify: true,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
NextProtos: []string{"h2"},
|
||||
}
|
||||
c.BuildNameToCertificate()
|
||||
return c
|
||||
|
|
|
|||
30
keepalive.go
30
keepalive.go
|
|
@ -1,30 +0,0 @@
|
|||
// Copyright (C) 2017 Michał Matczuk
|
||||
// Use of this source code is governed by an AGPL-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/felixge/tcpkeepalive"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultKeepAliveIdleTime specifies how long connection can be idle
|
||||
// before sending keepalive message.
|
||||
DefaultKeepAliveIdleTime = 15 * time.Minute
|
||||
// DefaultKeepAliveCount specifies maximal number of keepalive messages
|
||||
// sent before marking connection as dead.
|
||||
DefaultKeepAliveCount = 8
|
||||
// DefaultKeepAliveInterval specifies how often retry sending keepalive
|
||||
// messages when no response is received.
|
||||
DefaultKeepAliveInterval = 5 * time.Second
|
||||
)
|
||||
|
||||
func keepAlive(conn net.Conn) error {
|
||||
return tcpkeepalive.SetKeepAlive(conn, DefaultKeepAliveIdleTime, DefaultKeepAliveCount, DefaultKeepAliveInterval)
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright (C) 2017 Michał Matczuk
|
||||
// Use of this source code is governed by an AGPL-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
func keepAlive(conn net.Conn) error {
|
||||
c, ok := conn.(*net.TCPConn)
|
||||
if !ok {
|
||||
return fmt.Errorf("Bad connection type: %T", c)
|
||||
}
|
||||
|
||||
if err := c.SetKeepAlive(true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/mmatczuk/go-http-tunnel/tunnelmock"
|
||||
"github.com/hons82/go-http-tunnel/tunnelmock"
|
||||
)
|
||||
|
||||
func TestFilterLogger_Log(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/mmatczuk/go-http-tunnel/tunnelmock"
|
||||
"github.com/hons82/go-http-tunnel/tunnelmock"
|
||||
)
|
||||
|
||||
func TestContext_Log(t *testing.T) {
|
||||
|
|
|
|||
2
pool.go
2
pool.go
|
|
@ -14,7 +14,7 @@ import (
|
|||
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
"github.com/mmatczuk/go-http-tunnel/id"
|
||||
"github.com/hons82/go-http-tunnel/id"
|
||||
)
|
||||
|
||||
type connPair struct {
|
||||
|
|
|
|||
2
proxy.go
2
proxy.go
|
|
@ -7,7 +7,7 @@ package tunnel
|
|||
import (
|
||||
"io"
|
||||
|
||||
"github.com/mmatczuk/go-http-tunnel/proto"
|
||||
"github.com/hons82/go-http-tunnel/proto"
|
||||
)
|
||||
|
||||
// ProxyFunc is responsible for forwarding a remote connection to local server
|
||||
|
|
|
|||
89
registry.go
89
registry.go
|
|
@ -9,13 +9,14 @@ import (
|
|||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/mmatczuk/go-http-tunnel/id"
|
||||
"github.com/mmatczuk/go-http-tunnel/log"
|
||||
"github.com/hons82/go-http-tunnel/id"
|
||||
"github.com/hons82/go-http-tunnel/log"
|
||||
)
|
||||
|
||||
// RegistryItem holds information about hosts and listeners associated with a
|
||||
// client.
|
||||
type RegistryItem struct {
|
||||
*id.IDInfo
|
||||
Hosts []*HostAuth
|
||||
Listeners []net.Listener
|
||||
}
|
||||
|
|
@ -27,6 +28,7 @@ type HostAuth struct {
|
|||
}
|
||||
|
||||
type hostInfo struct {
|
||||
*id.IDInfo
|
||||
identifier id.ID
|
||||
auth *Auth
|
||||
}
|
||||
|
|
@ -62,7 +64,7 @@ func (r *registry) Subscribe(identifier id.ID) {
|
|||
}
|
||||
|
||||
r.logger.Log(
|
||||
"level", 1,
|
||||
"level", 2,
|
||||
"action", "subscribe",
|
||||
"identifier", identifier,
|
||||
)
|
||||
|
|
@ -91,30 +93,42 @@ func (r *registry) Subscriber(hostPort string) (id.ID, *Auth, bool) {
|
|||
return h.identifier, h.auth, ok
|
||||
}
|
||||
|
||||
func (r *registry) HasTunnel(hostPort string, identifier id.ID) bool {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
h, ok := r.hosts[trimPort(hostPort)]
|
||||
|
||||
return ok && h.identifier.Equals(identifier)
|
||||
}
|
||||
|
||||
// Unsubscribe removes client from registry and returns it's RegistryItem.
|
||||
func (r *registry) Unsubscribe(identifier id.ID) *RegistryItem {
|
||||
func (r *registry) Unsubscribe(identifier id.ID, autoSubscribe bool) *RegistryItem {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
i, ok := r.items[identifier]
|
||||
if !ok {
|
||||
if !ok || (autoSubscribe && i == voidRegistryItem) {
|
||||
return nil
|
||||
}
|
||||
|
||||
r.logger.Log(
|
||||
"level", 1,
|
||||
"level", 2,
|
||||
"action", "unsubscribe",
|
||||
"identifier", identifier,
|
||||
)
|
||||
|
||||
if i.Hosts != nil {
|
||||
for _, h := range i.Hosts {
|
||||
delete(r.hosts, h.Host)
|
||||
if autoSubscribe {
|
||||
if i.Hosts != nil {
|
||||
for _, h := range i.Hosts {
|
||||
delete(r.hosts, h.Host)
|
||||
}
|
||||
}
|
||||
delete(r.items, identifier)
|
||||
} else {
|
||||
r.items[identifier] = voidRegistryItem
|
||||
}
|
||||
|
||||
delete(r.items, identifier)
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +155,7 @@ func (r *registry) set(i *RegistryItem, identifier id.ID) error {
|
|||
if h.Auth != nil && h.Auth.User == "" {
|
||||
return fmt.Errorf("missing auth user")
|
||||
}
|
||||
if _, ok := r.hosts[trimPort(h.Host)]; ok {
|
||||
if hi, ok := r.hosts[trimPort(h.Host)]; ok && !hi.identifier.Equals(identifier) {
|
||||
return fmt.Errorf("host %q is occupied", h.Host)
|
||||
}
|
||||
}
|
||||
|
|
@ -159,30 +173,53 @@ func (r *registry) set(i *RegistryItem, identifier id.ID) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *registry) clear(identifier id.ID) *RegistryItem {
|
||||
func (r *registry) registerTunnel(host string, client string) error {
|
||||
identifier := id.New([]byte(client))
|
||||
|
||||
r.logger.Log(
|
||||
"level", 2,
|
||||
"action", "clear registry item",
|
||||
"level", 3,
|
||||
"action", "register tunnel",
|
||||
"host", host,
|
||||
"client", client,
|
||||
"identifier", identifier,
|
||||
)
|
||||
|
||||
r.Subscribe(identifier)
|
||||
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
if _, ok := r.hosts[trimPort(host)]; ok {
|
||||
return fmt.Errorf("host %q is occupied", host)
|
||||
}
|
||||
|
||||
r.hosts[trimPort(host)] = &hostInfo{
|
||||
identifier: identifier,
|
||||
IDInfo: &id.IDInfo{
|
||||
Client: client,
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clear removes all items from the registry
|
||||
func (r *registry) Clear() {
|
||||
r.logger.Log(
|
||||
"level", 3,
|
||||
"action", "clear registry ",
|
||||
)
|
||||
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
i, ok := r.items[identifier]
|
||||
if !ok || i == voidRegistryItem {
|
||||
return nil
|
||||
for k := range r.hosts {
|
||||
delete(r.hosts, k)
|
||||
}
|
||||
|
||||
if i.Hosts != nil {
|
||||
for _, h := range i.Hosts {
|
||||
delete(r.hosts, trimPort(h.Host))
|
||||
}
|
||||
for i := range r.items {
|
||||
delete(r.items, i)
|
||||
}
|
||||
|
||||
r.items[identifier] = voidRegistryItem
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
func trimPort(hostPort string) (host string) {
|
||||
|
|
|
|||
294
server.go
294
server.go
|
|
@ -14,18 +14,22 @@ import (
|
|||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
"github.com/hons82/go-http-tunnel/connection"
|
||||
"github.com/hons82/go-http-tunnel/fileutil"
|
||||
"github.com/hons82/go-http-tunnel/id"
|
||||
"github.com/hons82/go-http-tunnel/log"
|
||||
"github.com/hons82/go-http-tunnel/proto"
|
||||
"github.com/inconshreveable/go-vhost"
|
||||
"github.com/mmatczuk/go-http-tunnel/id"
|
||||
"github.com/mmatczuk/go-http-tunnel/log"
|
||||
"github.com/mmatczuk/go-http-tunnel/proto"
|
||||
)
|
||||
|
||||
// A set of listeners to manage subscribers
|
||||
// SubscriptionListener A set of listeners to manage subscribers
|
||||
type SubscriptionListener interface {
|
||||
// Invoked if AutoSubscribe is false and must return true if the client is allowed to subscribe or not.
|
||||
// If the tlsConfig is configured to require client certificate validation, chain will contain the first
|
||||
|
|
@ -41,21 +45,22 @@ type SubscriptionListener interface {
|
|||
|
||||
// ServerConfig defines configuration for the Server.
|
||||
type ServerConfig struct {
|
||||
// Addr is TCP address to listen for client connections. If empty ":0"
|
||||
// is used.
|
||||
// Addr is TCP address to listen for client connections. If empty ":0" is used.
|
||||
Addr string
|
||||
// AutoSubscribe if enabled will automatically subscribe new clients on
|
||||
// first call.
|
||||
// AutoSubscribe if enabled will automatically subscribe new clients on first call.
|
||||
AutoSubscribe bool
|
||||
// TLSConfig specifies the tls configuration to use with tls.Listener.
|
||||
TLSConfig *tls.Config
|
||||
// Listener specifies optional listener for client connections. If nil
|
||||
// tls.Listen("tcp", Addr, TLSConfig) is used.
|
||||
// Listener specifies optional listener for client connections. If nil tls.Listen("tcp", Addr, TLSConfig) is used.
|
||||
Listener net.Listener
|
||||
// Logger is optional logger. If nil logging is disabled.
|
||||
Logger log.Logger
|
||||
// Addr is TCP address to listen for TLS SNI connections
|
||||
SNIAddr string
|
||||
// Used to configure the keepalive for the server -> client tcp connection
|
||||
KeepAlive connection.KeepAliveConfig
|
||||
// How long should a disconnected message been hold before sending it to the log
|
||||
Debounce Debounced
|
||||
// Optional listener to manage subscribers
|
||||
SubscriptionListener SubscriptionListener
|
||||
}
|
||||
|
|
@ -64,15 +69,21 @@ type ServerConfig struct {
|
|||
// tunnel connection.
|
||||
type Server struct {
|
||||
*registry
|
||||
config *ServerConfig
|
||||
|
||||
config *ServerConfig
|
||||
listener net.Listener
|
||||
connPool *connPool
|
||||
httpClient *http.Client
|
||||
logger log.Logger
|
||||
debounce Debounced
|
||||
vhostMuxer *vhost.TLSMuxer
|
||||
}
|
||||
|
||||
// Debounced Hold IDs that are disconnected for a short time before executing the function.
|
||||
type Debounced struct {
|
||||
Execute func(f func())
|
||||
disconnectedIDs []id.ID
|
||||
}
|
||||
|
||||
// NewServer creates a new Server.
|
||||
func NewServer(config *ServerConfig) (*Server, error) {
|
||||
listener, err := listener(config)
|
||||
|
|
@ -90,6 +101,7 @@ func NewServer(config *ServerConfig) (*Server, error) {
|
|||
config: config,
|
||||
listener: listener,
|
||||
logger: logger,
|
||||
debounce: config.Debounce,
|
||||
}
|
||||
|
||||
t := &http2.Transport{}
|
||||
|
|
@ -169,16 +181,30 @@ func listener(config *ServerConfig) (net.Listener, error) {
|
|||
return net.Listen("tcp", config.Addr)
|
||||
}
|
||||
|
||||
// disconnected clears resources used by client, it's invoked by connection pool
|
||||
// when client goes away.
|
||||
// disconnected clears resources used by client, it's invoked by connection pool when client goes away.
|
||||
func (s *Server) disconnected(identifier id.ID) {
|
||||
s.logger.Log(
|
||||
"level", 1,
|
||||
"action", "disconnected",
|
||||
"identifier", identifier,
|
||||
)
|
||||
if s.debounce.Execute != nil {
|
||||
s.debounce.disconnectedIDs = append(s.debounce.disconnectedIDs, identifier)
|
||||
|
||||
i := s.registry.clear(identifier)
|
||||
s.debounce.Execute(func() {
|
||||
for _, id := range s.debounce.disconnectedIDs {
|
||||
s.logger.Log(
|
||||
"level", 1,
|
||||
"action", "disconnected",
|
||||
"identifier", id,
|
||||
)
|
||||
}
|
||||
s.debounce.disconnectedIDs = nil
|
||||
})
|
||||
} else {
|
||||
s.logger.Log(
|
||||
"level", 1,
|
||||
"action", "disconnected",
|
||||
"identifier", identifier,
|
||||
)
|
||||
}
|
||||
|
||||
i := s.registry.Unsubscribe(identifier, s.config.AutoSubscribe)
|
||||
if i == nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -225,7 +251,12 @@ func (s *Server) Start() {
|
|||
continue
|
||||
}
|
||||
|
||||
if err := keepAlive(conn); err != nil {
|
||||
s.logger.Log(
|
||||
"level", 2,
|
||||
"msg", fmt.Sprintf("setting up keep alive using config: %v", s.config.KeepAlive.String()),
|
||||
)
|
||||
|
||||
if err := s.config.KeepAlive.Set(conn); err != nil {
|
||||
s.logger.Log(
|
||||
"level", 0,
|
||||
"msg", "TCP keepalive for control connection failed",
|
||||
|
|
@ -239,15 +270,16 @@ func (s *Server) Start() {
|
|||
}
|
||||
|
||||
func (s *Server) handleClient(conn net.Conn) {
|
||||
logger := log.NewContext(s.logger).With("addr", conn.RemoteAddr())
|
||||
logger := log.NewContext(s.logger).With("remote addr", conn.RemoteAddr())
|
||||
|
||||
logger.Log(
|
||||
"level", 1,
|
||||
"level", 2,
|
||||
"action", "try connect",
|
||||
)
|
||||
|
||||
var (
|
||||
identifier id.ID
|
||||
IDInfo id.IDInfo
|
||||
req *http.Request
|
||||
resp *http.Response
|
||||
tunnels map[string]*proto.Tunnel
|
||||
|
|
@ -256,6 +288,9 @@ func (s *Server) handleClient(conn net.Conn) {
|
|||
|
||||
inConnPool bool
|
||||
certs []*x509.Certificate
|
||||
|
||||
remainingIDs []id.ID
|
||||
found bool
|
||||
)
|
||||
|
||||
tlsConn, ok := conn.(*tls.Conn)
|
||||
|
|
@ -268,7 +303,7 @@ func (s *Server) handleClient(conn net.Conn) {
|
|||
goto reject
|
||||
}
|
||||
|
||||
identifier, err = id.PeerID(tlsConn)
|
||||
identifier, IDInfo, err = id.PeerID(tlsConn)
|
||||
if err != nil {
|
||||
logger.Log(
|
||||
"level", 2,
|
||||
|
|
@ -348,7 +383,7 @@ func (s *Server) handleClient(conn net.Conn) {
|
|||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
err = fmt.Errorf("Status %s", resp.Status)
|
||||
err = fmt.Errorf("status %s", resp.Status)
|
||||
logger.Log(
|
||||
"level", 2,
|
||||
"msg", "handshake failed",
|
||||
|
|
@ -358,7 +393,7 @@ func (s *Server) handleClient(conn net.Conn) {
|
|||
}
|
||||
|
||||
if resp.ContentLength == 0 {
|
||||
err = fmt.Errorf("Tunnels Content-Legth: 0")
|
||||
err = fmt.Errorf("tunnels content-length: 0")
|
||||
logger.Log(
|
||||
"level", 2,
|
||||
"msg", "handshake failed",
|
||||
|
|
@ -377,28 +412,45 @@ func (s *Server) handleClient(conn net.Conn) {
|
|||
}
|
||||
|
||||
if len(tunnels) == 0 {
|
||||
err = fmt.Errorf("No tunnels")
|
||||
logger.Log(
|
||||
"level", 1,
|
||||
"msg", "configuration error",
|
||||
"err", fmt.Errorf("no tunnels"),
|
||||
)
|
||||
goto reject
|
||||
}
|
||||
|
||||
if err = s.hasTunnels(tunnels, identifier); err != nil {
|
||||
logger.Log(
|
||||
"level", 2,
|
||||
"msg", "handshake failed",
|
||||
"msg", "tunnel check failed",
|
||||
"err", err,
|
||||
)
|
||||
goto reject
|
||||
}
|
||||
|
||||
if err = s.addTunnels(tunnels, identifier); err != nil {
|
||||
if err = s.addTunnels(tunnels, identifier, IDInfo); err != nil {
|
||||
logger.Log(
|
||||
"level", 2,
|
||||
"msg", "handshake failed",
|
||||
"msg", "add tunnel failed",
|
||||
"err", err,
|
||||
)
|
||||
goto reject
|
||||
}
|
||||
|
||||
logger.Log(
|
||||
"level", 1,
|
||||
"action", "connected",
|
||||
)
|
||||
remainingIDs, found = id.Remove(s.debounce.disconnectedIDs, identifier)
|
||||
if found {
|
||||
s.debounce.disconnectedIDs = remainingIDs
|
||||
logger.Log(
|
||||
"level", 2,
|
||||
"action", "reconnected",
|
||||
)
|
||||
} else {
|
||||
logger.Log(
|
||||
"level", 1,
|
||||
"action", "connected",
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
|
|
@ -416,6 +468,65 @@ reject:
|
|||
conn.Close()
|
||||
}
|
||||
|
||||
// loadAllowedTunnels registers allowed tunnels from a file
|
||||
func (s *Server) loadAllowedTunnels(propertiesFile string) {
|
||||
clients, err := fileutil.ReadPropertiesFile(propertiesFile)
|
||||
if err != nil {
|
||||
s.logger.Log(
|
||||
"level", 1,
|
||||
"action", "failed to load clients",
|
||||
"err", err,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
for host, value := range clients {
|
||||
if err := s.registerTunnel(host, value); err != nil {
|
||||
s.logger.Log(
|
||||
"level", 2,
|
||||
"action", "failed to load tunnel",
|
||||
"host", host,
|
||||
"err", err,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ReloadTunnels registers allowed tunnels from a file
|
||||
func (s *Server) ReloadTunnels(path string) {
|
||||
directory, err := fileutil.IsDirectory(path)
|
||||
if err != nil {
|
||||
s.logger.Log(
|
||||
"level", 3,
|
||||
"action", "could not determine if path is a directory",
|
||||
"err", err,
|
||||
)
|
||||
}
|
||||
if directory {
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
s.logger.Log(
|
||||
"level", 2,
|
||||
"action", "could not read directory",
|
||||
"err", err,
|
||||
)
|
||||
}
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
s.logger.Log(
|
||||
"level", 3,
|
||||
"action", "skip directory",
|
||||
"file", file.Name(),
|
||||
)
|
||||
} else {
|
||||
s.loadAllowedTunnels(filepath.Join(path, file.Name()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.loadAllowedTunnels(path)
|
||||
}
|
||||
}
|
||||
|
||||
// notifyError tries to send error to client.
|
||||
func (s *Server) notifyError(serverError error, identifier id.ID) {
|
||||
if serverError == nil {
|
||||
|
|
@ -441,10 +552,25 @@ func (s *Server) notifyError(serverError error, identifier id.ID) {
|
|||
s.httpClient.Do(req.WithContext(ctx))
|
||||
}
|
||||
|
||||
func (s *Server) hasTunnels(tunnels map[string]*proto.Tunnel, identifier id.ID) error {
|
||||
var err error
|
||||
for name, t := range tunnels {
|
||||
// Check the current tunnel
|
||||
// AutoSubscribe --> Tunnel not yet registered (means that it isn't already opened)
|
||||
// !AutoSubscribe -> Tunnel has to be already registered, and therefore allowed to be opened
|
||||
if s.config.AutoSubscribe == s.HasTunnel(t.Host, identifier) {
|
||||
err = fmt.Errorf("tunnel %s (%s) not allowed for %s", name, t.Host, identifier)
|
||||
break
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// addTunnels invokes addHost or addListener based on data from proto.Tunnel. If
|
||||
// a tunnel cannot be added whole batch is reverted.
|
||||
func (s *Server) addTunnels(tunnels map[string]*proto.Tunnel, identifier id.ID) error {
|
||||
func (s *Server) addTunnels(tunnels map[string]*proto.Tunnel, identifier id.ID, IDInfo id.IDInfo) error {
|
||||
i := &RegistryItem{
|
||||
IDInfo: &IDInfo,
|
||||
Hosts: []*HostAuth{},
|
||||
Listeners: []net.Listener{},
|
||||
}
|
||||
|
|
@ -520,7 +646,7 @@ func (s *Server) Unsubscribe(identifier id.ID) *RegistryItem {
|
|||
s.config.SubscriptionListener.Unsubscribed(identifier)
|
||||
}
|
||||
s.connPool.DeleteConn(identifier)
|
||||
return s.registry.Unsubscribe(identifier)
|
||||
return s.registry.Unsubscribe(identifier, s.config.AutoSubscribe)
|
||||
}
|
||||
|
||||
// Ping measures the RTT response time.
|
||||
|
|
@ -561,13 +687,19 @@ func (s *Server) listen(l net.Listener, identifier id.ID) {
|
|||
}
|
||||
|
||||
tlsConn, ok := conn.(*vhost.TLSConn)
|
||||
|
||||
s.logger.Log(
|
||||
"level", 1,
|
||||
"msg", fmt.Sprintf("setting up keep alive using config: %v", s.config.KeepAlive.String()),
|
||||
)
|
||||
|
||||
if ok {
|
||||
msg.ForwardedHost = tlsConn.Host()
|
||||
err = keepAlive(tlsConn.Conn)
|
||||
err = s.config.KeepAlive.Set(tlsConn.Conn)
|
||||
|
||||
} else {
|
||||
msg.ForwardedHost = l.Addr().String()
|
||||
err = keepAlive(conn)
|
||||
err = s.config.KeepAlive.Set(conn)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -594,6 +726,7 @@ func (s *Server) listen(l net.Listener, identifier id.ID) {
|
|||
}
|
||||
}
|
||||
|
||||
// Upgrade the connection
|
||||
func (s *Server) Upgrade(identifier id.ID, conn net.Conn, requestBytes []byte) error {
|
||||
|
||||
var err error
|
||||
|
|
@ -606,11 +739,11 @@ func (s *Server) Upgrade(identifier id.ID, conn net.Conn, requestBytes []byte) e
|
|||
tlsConn, ok := conn.(*tls.Conn)
|
||||
if ok {
|
||||
msg.ForwardedHost = tlsConn.ConnectionState().ServerName
|
||||
err = keepAlive(tlsConn.NetConn())
|
||||
err = s.config.KeepAlive.Set(tlsConn.NetConn())
|
||||
|
||||
} else {
|
||||
msg.ForwardedHost = conn.RemoteAddr().String()
|
||||
err = keepAlive(conn)
|
||||
err = s.config.KeepAlive.Set(conn)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
|
@ -640,23 +773,44 @@ func (s *Server) Upgrade(identifier id.ID, conn net.Conn, requestBytes []byte) e
|
|||
|
||||
// ServeHTTP proxies http connection to the client.
|
||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
resp, err := s.RoundTrip(r)
|
||||
if err == errUnauthorised {
|
||||
w.Header().Set("WWW-Authenticate", "Basic realm=\"User Visible Realm\"")
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
if strings.EqualFold(r.Method, "TRACE") {
|
||||
s.logger.Log(
|
||||
"level", 2,
|
||||
"action", "method not allowed",
|
||||
"method", r.Method,
|
||||
"addr", r.RemoteAddr,
|
||||
"host", r.Host,
|
||||
"url", r.URL,
|
||||
)
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
resp, err := s.RoundTrip(r)
|
||||
if err != nil {
|
||||
level := 0
|
||||
code := http.StatusBadGateway
|
||||
if err == errUnauthorised {
|
||||
w.Header().Set("WWW-Authenticate", "Basic realm=\"User Visible Realm\"")
|
||||
level = 1
|
||||
code = http.StatusUnauthorized
|
||||
} else if err == errClientNotSubscribed {
|
||||
level = 2
|
||||
code = http.StatusNotFound
|
||||
}
|
||||
s.logger.Log(
|
||||
"level", 0,
|
||||
"level", level,
|
||||
"action", "round trip failed",
|
||||
"addr", r.RemoteAddr,
|
||||
"host", r.Host,
|
||||
"url", r.URL,
|
||||
"err", err,
|
||||
"code", code,
|
||||
)
|
||||
|
||||
http.Error(w, err.Error(), http.StatusBadGateway)
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
w.WriteHeader(code)
|
||||
fmt.Fprintln(w, err.Error())
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
|
@ -937,3 +1091,47 @@ func (s *Server) Stop() {
|
|||
s.listener.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// ListenerInfo info about the listener
|
||||
type ListenerInfo struct {
|
||||
Network string
|
||||
Addr string
|
||||
}
|
||||
|
||||
// ClientInfo info about the client
|
||||
type ClientInfo struct {
|
||||
ID string
|
||||
IDInfo id.IDInfo
|
||||
Listeners []*ListenerInfo
|
||||
Hosts []string
|
||||
}
|
||||
|
||||
// GetClientInfo prepare and get client info
|
||||
func (s *Server) GetClientInfo() []*ClientInfo {
|
||||
s.registry.mu.Lock()
|
||||
defer s.registry.mu.Unlock()
|
||||
ret := []*ClientInfo{}
|
||||
for k, v := range s.registry.items {
|
||||
c := &ClientInfo{
|
||||
ID: k.String(),
|
||||
}
|
||||
ret = append(ret, c)
|
||||
if v == voidRegistryItem {
|
||||
s.logger.Log(
|
||||
"level", 3,
|
||||
"identifier", k.String(),
|
||||
"msg", "void registry item",
|
||||
)
|
||||
} else {
|
||||
c.IDInfo = *v.IDInfo
|
||||
for _, l := range v.Hosts {
|
||||
c.Hosts = append(c.Hosts, l.Host)
|
||||
}
|
||||
for _, l := range v.Listeners {
|
||||
p := &ListenerInfo{Network: l.Addr().Network(), Addr: l.Addr().String()}
|
||||
c.Listeners = append(c.Listeners, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@ import (
|
|||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/mmatczuk/go-http-tunnel/log"
|
||||
"github.com/mmatczuk/go-http-tunnel/proto"
|
||||
"github.com/hons82/go-http-tunnel/connection"
|
||||
"github.com/hons82/go-http-tunnel/log"
|
||||
"github.com/hons82/go-http-tunnel/proto"
|
||||
)
|
||||
|
||||
// TCPProxy forwards TCP streams.
|
||||
|
|
@ -91,7 +92,7 @@ func (p *TCPProxy) Proxy(w io.Writer, r io.ReadCloser, msg *proto.ControlMessage
|
|||
}
|
||||
defer local.Close()
|
||||
|
||||
if err := keepAlive(local); err != nil {
|
||||
if err := connection.SetDefaultKeepAlive(local); err != nil {
|
||||
p.logger.Log(
|
||||
"level", 1,
|
||||
"msg", "TCP keepalive for tunneled connection failed",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Automatically generated by MockGen. DO NOT EDIT!
|
||||
// Source: github.com/mmatczuk/go-http-tunnel (interfaces: Backoff)
|
||||
// Source: github.com/hons82/go-http-tunnel (interfaces: Backoff)
|
||||
|
||||
package tunnelmock
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Automatically generated by MockGen. DO NOT EDIT!
|
||||
// Source: github.com/mmatczuk/go-http-tunnel/log (interfaces: Logger)
|
||||
// Source: github.com/hons82/go-http-tunnel/log (interfaces: Logger)
|
||||
|
||||
package tunnelmock
|
||||
|
||||
|
|
|
|||
2
utils.go
2
utils.go
|
|
@ -10,7 +10,7 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/mmatczuk/go-http-tunnel/log"
|
||||
"github.com/hons82/go-http-tunnel/log"
|
||||
)
|
||||
|
||||
func transfer(dst io.Writer, src io.Reader, logger log.Logger) {
|
||||
|
|
|
|||
19
vendor/github.com/calmh/luhn/LICENSE
generated
vendored
19
vendor/github.com/calmh/luhn/LICENSE
generated
vendored
|
|
@ -1,19 +0,0 @@
|
|||
Copyright (C) 2014 Jakob Borg
|
||||
|
||||
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.
|
||||
70
vendor/github.com/calmh/luhn/luhn.go
generated
vendored
70
vendor/github.com/calmh/luhn/luhn.go
generated
vendored
|
|
@ -1,70 +0,0 @@
|
|||
// Copyright (C) 2014 Jakob Borg
|
||||
|
||||
// Package luhn generates and validates Luhn mod N check digits.
|
||||
package luhn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// An alphabet is a string of N characters, representing the digits of a given
|
||||
// base N.
|
||||
type Alphabet string
|
||||
|
||||
var (
|
||||
Base32 Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
|
||||
)
|
||||
|
||||
// Generate returns a check digit for the string s, which should be composed
|
||||
// of characters from the Alphabet a.
|
||||
func (a Alphabet) Generate(s string) (rune, error) {
|
||||
factor := 1
|
||||
if len(s)%2 == 1 {
|
||||
factor = 2
|
||||
}
|
||||
sum := 0
|
||||
n := len(a)
|
||||
|
||||
for i := range s {
|
||||
codepoint := strings.IndexByte(string(a), s[i])
|
||||
if codepoint == -1 {
|
||||
return 0, fmt.Errorf("Digit %q not valid in alphabet %q", s[i], a)
|
||||
}
|
||||
addend := factor * codepoint
|
||||
if factor == 2 {
|
||||
factor = 1
|
||||
} else {
|
||||
factor = 2
|
||||
}
|
||||
addend = (addend / n) + (addend % n)
|
||||
sum += addend
|
||||
}
|
||||
remainder := sum % n
|
||||
checkCodepoint := (n - remainder) % n
|
||||
return rune(a[checkCodepoint]), nil
|
||||
}
|
||||
|
||||
// Validate returns true if the last character of the string s is correct, for
|
||||
// a string s composed of characters in the alphabet a.
|
||||
func (a Alphabet) Validate(s string) bool {
|
||||
t := s[:len(s)-1]
|
||||
c, err := a.Generate(t)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return rune(s[len(s)-1]) == c
|
||||
}
|
||||
|
||||
// NewAlphabet converts the given string an an Alphabet, verifying that it
|
||||
// is correct.
|
||||
func NewAlphabet(s string) (Alphabet, error) {
|
||||
cm := make(map[byte]bool, len(s))
|
||||
for i := range s {
|
||||
if cm[s[i]] {
|
||||
return "", fmt.Errorf("Digit %q non-unique in alphabet %q", s[i], s)
|
||||
}
|
||||
cm[s[i]] = true
|
||||
}
|
||||
return Alphabet(s), nil
|
||||
}
|
||||
22
vendor/github.com/cenkalti/backoff/.gitignore
generated
vendored
22
vendor/github.com/cenkalti/backoff/.gitignore
generated
vendored
|
|
@ -1,22 +0,0 @@
|
|||
# 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
|
||||
10
vendor/github.com/cenkalti/backoff/.travis.yml
generated
vendored
10
vendor/github.com/cenkalti/backoff/.travis.yml
generated
vendored
|
|
@ -1,10 +0,0 @@
|
|||
language: go
|
||||
go:
|
||||
- 1.7
|
||||
- 1.x
|
||||
- tip
|
||||
before_install:
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
script:
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
||||
20
vendor/github.com/cenkalti/backoff/LICENSE
generated
vendored
20
vendor/github.com/cenkalti/backoff/LICENSE
generated
vendored
|
|
@ -1,20 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Cenk Altı
|
||||
|
||||
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.
|
||||
30
vendor/github.com/cenkalti/backoff/README.md
generated
vendored
30
vendor/github.com/cenkalti/backoff/README.md
generated
vendored
|
|
@ -1,30 +0,0 @@
|
|||
# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Build Status][travis image]][travis] [![Coverage Status][coveralls image]][coveralls]
|
||||
|
||||
This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client].
|
||||
|
||||
[Exponential backoff][exponential backoff wiki]
|
||||
is an algorithm that uses feedback to multiplicatively decrease the rate of some process,
|
||||
in order to gradually find an acceptable rate.
|
||||
The retries exponentially increase and stop increasing when a certain threshold is met.
|
||||
|
||||
## Usage
|
||||
|
||||
See https://godoc.org/github.com/cenkalti/backoff#pkg-examples
|
||||
|
||||
## Contributing
|
||||
|
||||
* I would like to keep this library as small as possible.
|
||||
* Please don't send a PR without opening an issue and discussing it first.
|
||||
* If proposed change is not a common use case, I will probably not accept it.
|
||||
|
||||
[godoc]: https://godoc.org/github.com/cenkalti/backoff
|
||||
[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png
|
||||
[travis]: https://travis-ci.org/cenkalti/backoff
|
||||
[travis image]: https://travis-ci.org/cenkalti/backoff.png?branch=master
|
||||
[coveralls]: https://coveralls.io/github/cenkalti/backoff?branch=master
|
||||
[coveralls image]: https://coveralls.io/repos/github/cenkalti/backoff/badge.svg?branch=master
|
||||
|
||||
[google-http-java-client]: https://github.com/google/google-http-java-client/blob/da1aa993e90285ec18579f1553339b00e19b3ab5/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java
|
||||
[exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff
|
||||
|
||||
[advanced example]: https://godoc.org/github.com/cenkalti/backoff#example_
|
||||
66
vendor/github.com/cenkalti/backoff/backoff.go
generated
vendored
66
vendor/github.com/cenkalti/backoff/backoff.go
generated
vendored
|
|
@ -1,66 +0,0 @@
|
|||
// Package backoff implements backoff algorithms for retrying operations.
|
||||
//
|
||||
// Use Retry function for retrying operations that may fail.
|
||||
// If Retry does not meet your needs,
|
||||
// copy/paste the function into your project and modify as you wish.
|
||||
//
|
||||
// There is also Ticker type similar to time.Ticker.
|
||||
// You can use it if you need to work with channels.
|
||||
//
|
||||
// See Examples section below for usage examples.
|
||||
package backoff
|
||||
|
||||
import "time"
|
||||
|
||||
// BackOff is a backoff policy for retrying an operation.
|
||||
type BackOff interface {
|
||||
// NextBackOff returns the duration to wait before retrying the operation,
|
||||
// or backoff. Stop to indicate that no more retries should be made.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// duration := backoff.NextBackOff();
|
||||
// if (duration == backoff.Stop) {
|
||||
// // Do not retry operation.
|
||||
// } else {
|
||||
// // Sleep for duration and retry operation.
|
||||
// }
|
||||
//
|
||||
NextBackOff() time.Duration
|
||||
|
||||
// Reset to initial state.
|
||||
Reset()
|
||||
}
|
||||
|
||||
// Stop indicates that no more retries should be made for use in NextBackOff().
|
||||
const Stop time.Duration = -1
|
||||
|
||||
// ZeroBackOff is a fixed backoff policy whose backoff time is always zero,
|
||||
// meaning that the operation is retried immediately without waiting, indefinitely.
|
||||
type ZeroBackOff struct{}
|
||||
|
||||
func (b *ZeroBackOff) Reset() {}
|
||||
|
||||
func (b *ZeroBackOff) NextBackOff() time.Duration { return 0 }
|
||||
|
||||
// StopBackOff is a fixed backoff policy that always returns backoff.Stop for
|
||||
// NextBackOff(), meaning that the operation should never be retried.
|
||||
type StopBackOff struct{}
|
||||
|
||||
func (b *StopBackOff) Reset() {}
|
||||
|
||||
func (b *StopBackOff) NextBackOff() time.Duration { return Stop }
|
||||
|
||||
// ConstantBackOff is a backoff policy that always returns the same backoff delay.
|
||||
// This is in contrast to an exponential backoff policy,
|
||||
// which returns a delay that grows longer as you call NextBackOff() over and over again.
|
||||
type ConstantBackOff struct {
|
||||
Interval time.Duration
|
||||
}
|
||||
|
||||
func (b *ConstantBackOff) Reset() {}
|
||||
func (b *ConstantBackOff) NextBackOff() time.Duration { return b.Interval }
|
||||
|
||||
func NewConstantBackOff(d time.Duration) *ConstantBackOff {
|
||||
return &ConstantBackOff{Interval: d}
|
||||
}
|
||||
63
vendor/github.com/cenkalti/backoff/context.go
generated
vendored
63
vendor/github.com/cenkalti/backoff/context.go
generated
vendored
|
|
@ -1,63 +0,0 @@
|
|||
package backoff
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BackOffContext is a backoff policy that stops retrying after the context
|
||||
// is canceled.
|
||||
type BackOffContext interface {
|
||||
BackOff
|
||||
Context() context.Context
|
||||
}
|
||||
|
||||
type backOffContext struct {
|
||||
BackOff
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// WithContext returns a BackOffContext with context ctx
|
||||
//
|
||||
// ctx must not be nil
|
||||
func WithContext(b BackOff, ctx context.Context) BackOffContext {
|
||||
if ctx == nil {
|
||||
panic("nil context")
|
||||
}
|
||||
|
||||
if b, ok := b.(*backOffContext); ok {
|
||||
return &backOffContext{
|
||||
BackOff: b.BackOff,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
return &backOffContext{
|
||||
BackOff: b,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func ensureContext(b BackOff) BackOffContext {
|
||||
if cb, ok := b.(BackOffContext); ok {
|
||||
return cb
|
||||
}
|
||||
return WithContext(b, context.Background())
|
||||
}
|
||||
|
||||
func (b *backOffContext) Context() context.Context {
|
||||
return b.ctx
|
||||
}
|
||||
|
||||
func (b *backOffContext) NextBackOff() time.Duration {
|
||||
select {
|
||||
case <-b.ctx.Done():
|
||||
return Stop
|
||||
default:
|
||||
}
|
||||
next := b.BackOff.NextBackOff()
|
||||
if deadline, ok := b.ctx.Deadline(); ok && deadline.Sub(time.Now()) < next {
|
||||
return Stop
|
||||
}
|
||||
return next
|
||||
}
|
||||
153
vendor/github.com/cenkalti/backoff/exponential.go
generated
vendored
153
vendor/github.com/cenkalti/backoff/exponential.go
generated
vendored
|
|
@ -1,153 +0,0 @@
|
|||
package backoff
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
ExponentialBackOff is a backoff implementation that increases the backoff
|
||||
period for each retry attempt using a randomization function that grows exponentially.
|
||||
|
||||
NextBackOff() is calculated using the following formula:
|
||||
|
||||
randomized interval =
|
||||
RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor])
|
||||
|
||||
In other words NextBackOff() will range between the randomization factor
|
||||
percentage below and above the retry interval.
|
||||
|
||||
For example, given the following parameters:
|
||||
|
||||
RetryInterval = 2
|
||||
RandomizationFactor = 0.5
|
||||
Multiplier = 2
|
||||
|
||||
the actual backoff period used in the next retry attempt will range between 1 and 3 seconds,
|
||||
multiplied by the exponential, that is, between 2 and 6 seconds.
|
||||
|
||||
Note: MaxInterval caps the RetryInterval and not the randomized interval.
|
||||
|
||||
If the time elapsed since an ExponentialBackOff instance is created goes past the
|
||||
MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop.
|
||||
|
||||
The elapsed time can be reset by calling Reset().
|
||||
|
||||
Example: Given the following default arguments, for 10 tries the sequence will be,
|
||||
and assuming we go over the MaxElapsedTime on the 10th try:
|
||||
|
||||
Request # RetryInterval (seconds) Randomized Interval (seconds)
|
||||
|
||||
1 0.5 [0.25, 0.75]
|
||||
2 0.75 [0.375, 1.125]
|
||||
3 1.125 [0.562, 1.687]
|
||||
4 1.687 [0.8435, 2.53]
|
||||
5 2.53 [1.265, 3.795]
|
||||
6 3.795 [1.897, 5.692]
|
||||
7 5.692 [2.846, 8.538]
|
||||
8 8.538 [4.269, 12.807]
|
||||
9 12.807 [6.403, 19.210]
|
||||
10 19.210 backoff.Stop
|
||||
|
||||
Note: Implementation is not thread-safe.
|
||||
*/
|
||||
type ExponentialBackOff struct {
|
||||
InitialInterval time.Duration
|
||||
RandomizationFactor float64
|
||||
Multiplier float64
|
||||
MaxInterval time.Duration
|
||||
// After MaxElapsedTime the ExponentialBackOff stops.
|
||||
// It never stops if MaxElapsedTime == 0.
|
||||
MaxElapsedTime time.Duration
|
||||
Clock Clock
|
||||
|
||||
currentInterval time.Duration
|
||||
startTime time.Time
|
||||
}
|
||||
|
||||
// Clock is an interface that returns current time for BackOff.
|
||||
type Clock interface {
|
||||
Now() time.Time
|
||||
}
|
||||
|
||||
// Default values for ExponentialBackOff.
|
||||
const (
|
||||
DefaultInitialInterval = 500 * time.Millisecond
|
||||
DefaultRandomizationFactor = 0.5
|
||||
DefaultMultiplier = 1.5
|
||||
DefaultMaxInterval = 60 * time.Second
|
||||
DefaultMaxElapsedTime = 15 * time.Minute
|
||||
)
|
||||
|
||||
// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
|
||||
func NewExponentialBackOff() *ExponentialBackOff {
|
||||
b := &ExponentialBackOff{
|
||||
InitialInterval: DefaultInitialInterval,
|
||||
RandomizationFactor: DefaultRandomizationFactor,
|
||||
Multiplier: DefaultMultiplier,
|
||||
MaxInterval: DefaultMaxInterval,
|
||||
MaxElapsedTime: DefaultMaxElapsedTime,
|
||||
Clock: SystemClock,
|
||||
}
|
||||
b.Reset()
|
||||
return b
|
||||
}
|
||||
|
||||
type systemClock struct{}
|
||||
|
||||
func (t systemClock) Now() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// SystemClock implements Clock interface that uses time.Now().
|
||||
var SystemClock = systemClock{}
|
||||
|
||||
// Reset the interval back to the initial retry interval and restarts the timer.
|
||||
func (b *ExponentialBackOff) Reset() {
|
||||
b.currentInterval = b.InitialInterval
|
||||
b.startTime = b.Clock.Now()
|
||||
}
|
||||
|
||||
// NextBackOff calculates the next backoff interval using the formula:
|
||||
// Randomized interval = RetryInterval +/- (RandomizationFactor * RetryInterval)
|
||||
func (b *ExponentialBackOff) NextBackOff() time.Duration {
|
||||
// Make sure we have not gone over the maximum elapsed time.
|
||||
if b.MaxElapsedTime != 0 && b.GetElapsedTime() > b.MaxElapsedTime {
|
||||
return Stop
|
||||
}
|
||||
defer b.incrementCurrentInterval()
|
||||
return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
|
||||
}
|
||||
|
||||
// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
|
||||
// is created and is reset when Reset() is called.
|
||||
//
|
||||
// The elapsed time is computed using time.Now().UnixNano(). It is
|
||||
// safe to call even while the backoff policy is used by a running
|
||||
// ticker.
|
||||
func (b *ExponentialBackOff) GetElapsedTime() time.Duration {
|
||||
return b.Clock.Now().Sub(b.startTime)
|
||||
}
|
||||
|
||||
// Increments the current interval by multiplying it with the multiplier.
|
||||
func (b *ExponentialBackOff) incrementCurrentInterval() {
|
||||
// Check for overflow, if overflow is detected set the current interval to the max interval.
|
||||
if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
|
||||
b.currentInterval = b.MaxInterval
|
||||
} else {
|
||||
b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a random value from the following interval:
|
||||
// [randomizationFactor * currentInterval, randomizationFactor * currentInterval].
|
||||
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
|
||||
var delta = randomizationFactor * float64(currentInterval)
|
||||
var minInterval = float64(currentInterval) - delta
|
||||
var maxInterval = float64(currentInterval) + delta
|
||||
|
||||
// Get a random value from the range [minInterval, maxInterval].
|
||||
// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
|
||||
// we want a 33% chance for selecting either 1, 2 or 3.
|
||||
return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
|
||||
}
|
||||
82
vendor/github.com/cenkalti/backoff/retry.go
generated
vendored
82
vendor/github.com/cenkalti/backoff/retry.go
generated
vendored
|
|
@ -1,82 +0,0 @@
|
|||
package backoff
|
||||
|
||||
import "time"
|
||||
|
||||
// An Operation is executing by Retry() or RetryNotify().
|
||||
// The operation will be retried using a backoff policy if it returns an error.
|
||||
type Operation func() error
|
||||
|
||||
// Notify is a notify-on-error function. It receives an operation error and
|
||||
// backoff delay if the operation failed (with an error).
|
||||
//
|
||||
// NOTE that if the backoff policy stated to stop retrying,
|
||||
// the notify function isn't called.
|
||||
type Notify func(error, time.Duration)
|
||||
|
||||
// Retry the operation o until it does not return error or BackOff stops.
|
||||
// o is guaranteed to be run at least once.
|
||||
//
|
||||
// If o returns a *PermanentError, the operation is not retried, and the
|
||||
// wrapped error is returned.
|
||||
//
|
||||
// Retry sleeps the goroutine for the duration returned by BackOff after a
|
||||
// failed operation returns.
|
||||
func Retry(o Operation, b BackOff) error { return RetryNotify(o, b, nil) }
|
||||
|
||||
// RetryNotify calls notify function with the error and wait duration
|
||||
// for each failed attempt before sleep.
|
||||
func RetryNotify(operation Operation, b BackOff, notify Notify) error {
|
||||
var err error
|
||||
var next time.Duration
|
||||
var t *time.Timer
|
||||
|
||||
cb := ensureContext(b)
|
||||
|
||||
b.Reset()
|
||||
for {
|
||||
if err = operation(); err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if permanent, ok := err.(*PermanentError); ok {
|
||||
return permanent.Err
|
||||
}
|
||||
|
||||
if next = cb.NextBackOff(); next == Stop {
|
||||
return err
|
||||
}
|
||||
|
||||
if notify != nil {
|
||||
notify(err, next)
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
t = time.NewTimer(next)
|
||||
defer t.Stop()
|
||||
} else {
|
||||
t.Reset(next)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-cb.Context().Done():
|
||||
return err
|
||||
case <-t.C:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PermanentError signals that the operation should not be retried.
|
||||
type PermanentError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *PermanentError) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
// Permanent wraps the given err in a *PermanentError.
|
||||
func Permanent(err error) *PermanentError {
|
||||
return &PermanentError{
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
82
vendor/github.com/cenkalti/backoff/ticker.go
generated
vendored
82
vendor/github.com/cenkalti/backoff/ticker.go
generated
vendored
|
|
@ -1,82 +0,0 @@
|
|||
package backoff
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff.
|
||||
//
|
||||
// Ticks will continue to arrive when the previous operation is still running,
|
||||
// so operations that take a while to fail could run in quick succession.
|
||||
type Ticker struct {
|
||||
C <-chan time.Time
|
||||
c chan time.Time
|
||||
b BackOffContext
|
||||
stop chan struct{}
|
||||
stopOnce sync.Once
|
||||
}
|
||||
|
||||
// NewTicker returns a new Ticker containing a channel that will send
|
||||
// the time at times specified by the BackOff argument. Ticker is
|
||||
// guaranteed to tick at least once. The channel is closed when Stop
|
||||
// method is called or BackOff stops. It is not safe to manipulate the
|
||||
// provided backoff policy (notably calling NextBackOff or Reset)
|
||||
// while the ticker is running.
|
||||
func NewTicker(b BackOff) *Ticker {
|
||||
c := make(chan time.Time)
|
||||
t := &Ticker{
|
||||
C: c,
|
||||
c: c,
|
||||
b: ensureContext(b),
|
||||
stop: make(chan struct{}),
|
||||
}
|
||||
t.b.Reset()
|
||||
go t.run()
|
||||
return t
|
||||
}
|
||||
|
||||
// Stop turns off a ticker. After Stop, no more ticks will be sent.
|
||||
func (t *Ticker) Stop() {
|
||||
t.stopOnce.Do(func() { close(t.stop) })
|
||||
}
|
||||
|
||||
func (t *Ticker) run() {
|
||||
c := t.c
|
||||
defer close(c)
|
||||
|
||||
// Ticker is guaranteed to tick at least once.
|
||||
afterC := t.send(time.Now())
|
||||
|
||||
for {
|
||||
if afterC == nil {
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case tick := <-afterC:
|
||||
afterC = t.send(tick)
|
||||
case <-t.stop:
|
||||
t.c = nil // Prevent future ticks from being sent to the channel.
|
||||
return
|
||||
case <-t.b.Context().Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Ticker) send(tick time.Time) <-chan time.Time {
|
||||
select {
|
||||
case t.c <- tick:
|
||||
case <-t.stop:
|
||||
return nil
|
||||
}
|
||||
|
||||
next := t.b.NextBackOff()
|
||||
if next == Stop {
|
||||
t.Stop()
|
||||
return nil
|
||||
}
|
||||
|
||||
return time.After(next)
|
||||
}
|
||||
35
vendor/github.com/cenkalti/backoff/tries.go
generated
vendored
35
vendor/github.com/cenkalti/backoff/tries.go
generated
vendored
|
|
@ -1,35 +0,0 @@
|
|||
package backoff
|
||||
|
||||
import "time"
|
||||
|
||||
/*
|
||||
WithMaxRetries creates a wrapper around another BackOff, which will
|
||||
return Stop if NextBackOff() has been called too many times since
|
||||
the last time Reset() was called
|
||||
|
||||
Note: Implementation is not thread-safe.
|
||||
*/
|
||||
func WithMaxRetries(b BackOff, max uint64) BackOff {
|
||||
return &backOffTries{delegate: b, maxTries: max}
|
||||
}
|
||||
|
||||
type backOffTries struct {
|
||||
delegate BackOff
|
||||
maxTries uint64
|
||||
numTries uint64
|
||||
}
|
||||
|
||||
func (b *backOffTries) NextBackOff() time.Duration {
|
||||
if b.maxTries > 0 {
|
||||
if b.maxTries <= b.numTries {
|
||||
return Stop
|
||||
}
|
||||
b.numTries++
|
||||
}
|
||||
return b.delegate.NextBackOff()
|
||||
}
|
||||
|
||||
func (b *backOffTries) Reset() {
|
||||
b.numTries = 0
|
||||
b.delegate.Reset()
|
||||
}
|
||||
19
vendor/github.com/felixge/tcpkeepalive/LICENSE
generated
vendored
19
vendor/github.com/felixge/tcpkeepalive/LICENSE
generated
vendored
|
|
@ -1,19 +0,0 @@
|
|||
Copyright (c) 2014 Felix Geisendörfer (felix@debuggable.com) and contributors
|
||||
|
||||
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.
|
||||
15
vendor/github.com/felixge/tcpkeepalive/README.md
generated
vendored
15
vendor/github.com/felixge/tcpkeepalive/README.md
generated
vendored
|
|
@ -1,15 +0,0 @@
|
|||
# tcpkeepalive
|
||||
|
||||
**Known Issues:** Some problems with the implementation were [reported](https://groups.google.com/d/msg/golang-nuts/rRu6ibLNdeI/TIzShZCmbzwJ), I'll try to fix them when I get a chance, or if somebody sends a PR.
|
||||
|
||||
Package tcpkeepalive implements additional TCP keepalive control beyond what is
|
||||
currently offered by the net pkg.
|
||||
|
||||
Only Linux \>= 2.4, DragonFly, FreeBSD, NetBSD and OS X \>= 10.8 are supported
|
||||
at this point, but patches for additional platforms are welcome.
|
||||
|
||||
See also: http://felixge.de/2014/08/26/tcp-keepalive-with-golang.html
|
||||
|
||||
**License:** MIT
|
||||
|
||||
**Docs:** http://godoc.org/github.com/felixge/tcpkeepalive
|
||||
111
vendor/github.com/felixge/tcpkeepalive/keepalive.go
generated
vendored
111
vendor/github.com/felixge/tcpkeepalive/keepalive.go
generated
vendored
|
|
@ -1,111 +0,0 @@
|
|||
// Package tcpkeepalive implements additional TCP keepalive control beyond what
|
||||
// is currently offered by the net pkg.
|
||||
//
|
||||
// Only Linux >= 2.4, DragonFly, FreeBSD, NetBSD and OS X >= 10.8 are supported
|
||||
// at this point, but patches for additional platforms are welcome.
|
||||
//
|
||||
// See also: http://felixge.de/2014/08/26/tcp-keepalive-with-golang.html
|
||||
package tcpkeepalive
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"time"
|
||||
)
|
||||
|
||||
// EnableKeepAlive enables TCP keepalive for the given conn, which must be a
|
||||
// *tcp.TCPConn. The returned Conn allows overwriting the default keepalive
|
||||
// parameters used by the operating system.
|
||||
func EnableKeepAlive(conn net.Conn) (*Conn, error) {
|
||||
tcp, ok := conn.(*net.TCPConn)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Bad conn type: %T", conn)
|
||||
}
|
||||
if err := tcp.SetKeepAlive(true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file, err := tcp.File()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fd := int(file.Fd())
|
||||
return &Conn{TCPConn: tcp, fd: fd}, nil
|
||||
}
|
||||
|
||||
// Conn adds additional TCP keepalive control to a *net.TCPConn.
|
||||
type Conn struct {
|
||||
*net.TCPConn
|
||||
fd int
|
||||
}
|
||||
|
||||
// SetKeepAliveIdle sets the time (in seconds) the connection needs to remain
|
||||
// idle before TCP starts sending keepalive probes.
|
||||
func (c *Conn) SetKeepAliveIdle(d time.Duration) error {
|
||||
return setIdle(c.fd, secs(d))
|
||||
}
|
||||
|
||||
// SetKeepAliveCount sets the maximum number of keepalive probes TCP should
|
||||
// send before dropping the connection.
|
||||
func (c *Conn) SetKeepAliveCount(n int) error {
|
||||
return setCount(c.fd, n)
|
||||
}
|
||||
|
||||
// SetKeepAliveInterval sets the time (in seconds) between individual keepalive
|
||||
// probes.
|
||||
func (c *Conn) SetKeepAliveInterval(d time.Duration) error {
|
||||
return setInterval(c.fd, secs(d))
|
||||
}
|
||||
|
||||
func secs(d time.Duration) int {
|
||||
d += (time.Second - time.Nanosecond)
|
||||
return int(d.Seconds())
|
||||
}
|
||||
|
||||
// Enable TCP keepalive in non-blocking mode with given settings for
|
||||
// the connection, which must be a *tcp.TCPConn.
|
||||
func SetKeepAlive(c net.Conn, idleTime time.Duration, count int, interval time.Duration) (err error) {
|
||||
|
||||
conn, ok := c.(*net.TCPConn)
|
||||
if !ok {
|
||||
return fmt.Errorf("Bad connection type: %T", c)
|
||||
}
|
||||
|
||||
if err := conn.SetKeepAlive(true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var f *os.File
|
||||
|
||||
if f, err = conn.File(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fd := int(f.Fd())
|
||||
|
||||
if err = setIdle(fd, secs(idleTime)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = setCount(fd, count); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = setInterval(fd, secs(interval)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = setNonblock(fd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setNonblock(fd int) error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetNonblock(fd, true))
|
||||
|
||||
}
|
||||
20
vendor/github.com/felixge/tcpkeepalive/keepalive_bsd.go
generated
vendored
20
vendor/github.com/felixge/tcpkeepalive/keepalive_bsd.go
generated
vendored
|
|
@ -1,20 +0,0 @@
|
|||
// +build dragonfly freebsd netbsd
|
||||
|
||||
package tcpkeepalive
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func setIdle(fd int, secs int) error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs))
|
||||
}
|
||||
|
||||
func setCount(fd int, n int) error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT, n))
|
||||
}
|
||||
|
||||
func setInterval(fd int, secs int) error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs))
|
||||
}
|
||||
24
vendor/github.com/felixge/tcpkeepalive/keepalive_darwin.go
generated
vendored
24
vendor/github.com/felixge/tcpkeepalive/keepalive_darwin.go
generated
vendored
|
|
@ -1,24 +0,0 @@
|
|||
package tcpkeepalive
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// from netinet/tcp.h (OS X 10.9.4)
|
||||
const (
|
||||
_TCP_KEEPINTVL = 0x101 /* interval between keepalives */
|
||||
_TCP_KEEPCNT = 0x102 /* number of keepalives before close */
|
||||
)
|
||||
|
||||
func setIdle(fd int, secs int) error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPALIVE, secs))
|
||||
}
|
||||
|
||||
func setCount(fd int, n int) error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, _TCP_KEEPCNT, n))
|
||||
}
|
||||
|
||||
func setInterval(fd int, secs int) error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, _TCP_KEEPINTVL, secs))
|
||||
}
|
||||
18
vendor/github.com/felixge/tcpkeepalive/keepalive_linux.go
generated
vendored
18
vendor/github.com/felixge/tcpkeepalive/keepalive_linux.go
generated
vendored
|
|
@ -1,18 +0,0 @@
|
|||
package tcpkeepalive
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func setIdle(fd int, secs int) error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs))
|
||||
}
|
||||
|
||||
func setCount(fd int, n int) error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT, n))
|
||||
}
|
||||
|
||||
func setInterval(fd int, secs int) error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs))
|
||||
}
|
||||
18
vendor/github.com/felixge/tcpkeepalive/keepalive_solaris.go
generated
vendored
18
vendor/github.com/felixge/tcpkeepalive/keepalive_solaris.go
generated
vendored
|
|
@ -1,18 +0,0 @@
|
|||
package tcpkeepalive
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func setIdle(fd int, secs int) error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs))
|
||||
}
|
||||
|
||||
func setCount(fd int, n int) error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPCNT, n))
|
||||
}
|
||||
|
||||
func setInterval(fd int, secs int) error {
|
||||
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs))
|
||||
}
|
||||
12
vendor/github.com/golang/mock/AUTHORS
generated
vendored
12
vendor/github.com/golang/mock/AUTHORS
generated
vendored
|
|
@ -1,12 +0,0 @@
|
|||
# This is the official list of GoMock authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
|
||||
# Names should be added to this file as
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Alex Reece <awreece@gmail.com>
|
||||
Google Inc.
|
||||
37
vendor/github.com/golang/mock/CONTRIBUTORS
generated
vendored
37
vendor/github.com/golang/mock/CONTRIBUTORS
generated
vendored
|
|
@ -1,37 +0,0 @@
|
|||
# This is the official list of people who can contribute (and typically
|
||||
# have contributed) code to the gomock repository.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# The submission process automatically checks to make sure
|
||||
# that people submitting code are listed in this file (by email address).
|
||||
#
|
||||
# Names should be added to this file only after verifying that
|
||||
# the individual or the individual's organization has agreed to
|
||||
# the appropriate Contributor License Agreement, found here:
|
||||
#
|
||||
# http://code.google.com/legal/individual-cla-v1.0.html
|
||||
# http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
#
|
||||
# The agreement for individuals can be filled out on the web.
|
||||
#
|
||||
# When adding J Random Contributor's name to this file,
|
||||
# either J's name or J's organization's name should be
|
||||
# added to the AUTHORS file, depending on whether the
|
||||
# individual or corporate CLA was used.
|
||||
|
||||
# Names should be added to this file like so:
|
||||
# Name <email address>
|
||||
#
|
||||
# An entry with two email addresses specifies that the
|
||||
# first address should be used in the submit logs and
|
||||
# that the second address should be recognized as the
|
||||
# same person when interacting with Rietveld.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Aaron Jacobs <jacobsa@google.com> <aaronjjacobs@gmail.com>
|
||||
Alex Reece <awreece@gmail.com>
|
||||
David Symonds <dsymonds@golang.org>
|
||||
Ryan Barrett <ryanb@google.com>
|
||||
202
vendor/github.com/golang/mock/LICENSE
generated
vendored
202
vendor/github.com/golang/mock/LICENSE
generated
vendored
|
|
@ -1,202 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
420
vendor/github.com/golang/mock/gomock/call.go
generated
vendored
420
vendor/github.com/golang/mock/gomock/call.go
generated
vendored
|
|
@ -1,420 +0,0 @@
|
|||
// Copyright 2010 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gomock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Call represents an expected call to a mock.
|
||||
type Call struct {
|
||||
t TestHelper // for triggering test failures on invalid call setup
|
||||
|
||||
receiver interface{} // the receiver of the method call
|
||||
method string // the name of the method
|
||||
methodType reflect.Type // the type of the method
|
||||
args []Matcher // the args
|
||||
origin string // file and line number of call setup
|
||||
|
||||
preReqs []*Call // prerequisite calls
|
||||
|
||||
// Expectations
|
||||
minCalls, maxCalls int
|
||||
|
||||
numCalls int // actual number made
|
||||
|
||||
// actions are called when this Call is called. Each action gets the args and
|
||||
// can set the return values by returning a non-nil slice. Actions run in the
|
||||
// order they are created.
|
||||
actions []func([]interface{}) []interface{}
|
||||
}
|
||||
|
||||
// newCall creates a *Call. It requires the method type in order to support
|
||||
// unexported methods.
|
||||
func newCall(t TestHelper, receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call {
|
||||
t.Helper()
|
||||
|
||||
// TODO: check arity, types.
|
||||
margs := make([]Matcher, len(args))
|
||||
for i, arg := range args {
|
||||
if m, ok := arg.(Matcher); ok {
|
||||
margs[i] = m
|
||||
} else if arg == nil {
|
||||
// Handle nil specially so that passing a nil interface value
|
||||
// will match the typed nils of concrete args.
|
||||
margs[i] = Nil()
|
||||
} else {
|
||||
margs[i] = Eq(arg)
|
||||
}
|
||||
}
|
||||
|
||||
origin := callerInfo(3)
|
||||
actions := []func([]interface{}) []interface{}{func([]interface{}) []interface{} {
|
||||
// Synthesize the zero value for each of the return args' types.
|
||||
rets := make([]interface{}, methodType.NumOut())
|
||||
for i := 0; i < methodType.NumOut(); i++ {
|
||||
rets[i] = reflect.Zero(methodType.Out(i)).Interface()
|
||||
}
|
||||
return rets
|
||||
}}
|
||||
return &Call{t: t, receiver: receiver, method: method, methodType: methodType,
|
||||
args: margs, origin: origin, minCalls: 1, maxCalls: 1, actions: actions}
|
||||
}
|
||||
|
||||
// AnyTimes allows the expectation to be called 0 or more times
|
||||
func (c *Call) AnyTimes() *Call {
|
||||
c.minCalls, c.maxCalls = 0, 1e8 // close enough to infinity
|
||||
return c
|
||||
}
|
||||
|
||||
// MinTimes requires the call to occur at least n times. If AnyTimes or MaxTimes have not been called, MinTimes also
|
||||
// sets the maximum number of calls to infinity.
|
||||
func (c *Call) MinTimes(n int) *Call {
|
||||
c.minCalls = n
|
||||
if c.maxCalls == 1 {
|
||||
c.maxCalls = 1e8
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// MaxTimes limits the number of calls to n times. If AnyTimes or MinTimes have not been called, MaxTimes also
|
||||
// sets the minimum number of calls to 0.
|
||||
func (c *Call) MaxTimes(n int) *Call {
|
||||
c.maxCalls = n
|
||||
if c.minCalls == 1 {
|
||||
c.minCalls = 0
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// DoAndReturn declares the action to run when the call is matched.
|
||||
// The return values from this function are returned by the mocked function.
|
||||
// It takes an interface{} argument to support n-arity functions.
|
||||
func (c *Call) DoAndReturn(f interface{}) *Call {
|
||||
// TODO: Check arity and types here, rather than dying badly elsewhere.
|
||||
v := reflect.ValueOf(f)
|
||||
|
||||
c.addAction(func(args []interface{}) []interface{} {
|
||||
vargs := make([]reflect.Value, len(args))
|
||||
ft := v.Type()
|
||||
for i := 0; i < len(args); i++ {
|
||||
if args[i] != nil {
|
||||
vargs[i] = reflect.ValueOf(args[i])
|
||||
} else {
|
||||
// Use the zero value for the arg.
|
||||
vargs[i] = reflect.Zero(ft.In(i))
|
||||
}
|
||||
}
|
||||
vrets := v.Call(vargs)
|
||||
rets := make([]interface{}, len(vrets))
|
||||
for i, ret := range vrets {
|
||||
rets[i] = ret.Interface()
|
||||
}
|
||||
return rets
|
||||
})
|
||||
return c
|
||||
}
|
||||
|
||||
// Do declares the action to run when the call is matched. The function's
|
||||
// return values are ignored to retain backward compatibility. To use the
|
||||
// return values call DoAndReturn.
|
||||
// It takes an interface{} argument to support n-arity functions.
|
||||
func (c *Call) Do(f interface{}) *Call {
|
||||
// TODO: Check arity and types here, rather than dying badly elsewhere.
|
||||
v := reflect.ValueOf(f)
|
||||
|
||||
c.addAction(func(args []interface{}) []interface{} {
|
||||
vargs := make([]reflect.Value, len(args))
|
||||
ft := v.Type()
|
||||
for i := 0; i < len(args); i++ {
|
||||
if args[i] != nil {
|
||||
vargs[i] = reflect.ValueOf(args[i])
|
||||
} else {
|
||||
// Use the zero value for the arg.
|
||||
vargs[i] = reflect.Zero(ft.In(i))
|
||||
}
|
||||
}
|
||||
v.Call(vargs)
|
||||
return nil
|
||||
})
|
||||
return c
|
||||
}
|
||||
|
||||
// Return declares the values to be returned by the mocked function call.
|
||||
func (c *Call) Return(rets ...interface{}) *Call {
|
||||
c.t.Helper()
|
||||
|
||||
mt := c.methodType
|
||||
if len(rets) != mt.NumOut() {
|
||||
c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]",
|
||||
c.receiver, c.method, len(rets), mt.NumOut(), c.origin)
|
||||
}
|
||||
for i, ret := range rets {
|
||||
if got, want := reflect.TypeOf(ret), mt.Out(i); got == want {
|
||||
// Identical types; nothing to do.
|
||||
} else if got == nil {
|
||||
// Nil needs special handling.
|
||||
switch want.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||
// ok
|
||||
default:
|
||||
c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]",
|
||||
i, c.receiver, c.method, want, c.origin)
|
||||
}
|
||||
} else if got.AssignableTo(want) {
|
||||
// Assignable type relation. Make the assignment now so that the generated code
|
||||
// can return the values with a type assertion.
|
||||
v := reflect.New(want).Elem()
|
||||
v.Set(reflect.ValueOf(ret))
|
||||
rets[i] = v.Interface()
|
||||
} else {
|
||||
c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]",
|
||||
i, c.receiver, c.method, got, want, c.origin)
|
||||
}
|
||||
}
|
||||
|
||||
c.addAction(func([]interface{}) []interface{} {
|
||||
return rets
|
||||
})
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Times declares the exact number of times a function call is expected to be executed.
|
||||
func (c *Call) Times(n int) *Call {
|
||||
c.minCalls, c.maxCalls = n, n
|
||||
return c
|
||||
}
|
||||
|
||||
// SetArg declares an action that will set the nth argument's value,
|
||||
// indirected through a pointer. Or, in the case of a slice, SetArg
|
||||
// will copy value's elements into the nth argument.
|
||||
func (c *Call) SetArg(n int, value interface{}) *Call {
|
||||
c.t.Helper()
|
||||
|
||||
mt := c.methodType
|
||||
// TODO: This will break on variadic methods.
|
||||
// We will need to check those at invocation time.
|
||||
if n < 0 || n >= mt.NumIn() {
|
||||
c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]",
|
||||
n, mt.NumIn(), c.origin)
|
||||
}
|
||||
// Permit setting argument through an interface.
|
||||
// In the interface case, we don't (nay, can't) check the type here.
|
||||
at := mt.In(n)
|
||||
switch at.Kind() {
|
||||
case reflect.Ptr:
|
||||
dt := at.Elem()
|
||||
if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) {
|
||||
c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]",
|
||||
n, vt, dt, c.origin)
|
||||
}
|
||||
case reflect.Interface:
|
||||
// nothing to do
|
||||
case reflect.Slice:
|
||||
// nothing to do
|
||||
default:
|
||||
c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface non-slice type %v [%s]",
|
||||
n, at, c.origin)
|
||||
}
|
||||
|
||||
c.addAction(func(args []interface{}) []interface{} {
|
||||
v := reflect.ValueOf(value)
|
||||
switch reflect.TypeOf(args[n]).Kind() {
|
||||
case reflect.Slice:
|
||||
setSlice(args[n], v)
|
||||
default:
|
||||
reflect.ValueOf(args[n]).Elem().Set(v)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return c
|
||||
}
|
||||
|
||||
// isPreReq returns true if other is a direct or indirect prerequisite to c.
|
||||
func (c *Call) isPreReq(other *Call) bool {
|
||||
for _, preReq := range c.preReqs {
|
||||
if other == preReq || preReq.isPreReq(other) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// After declares that the call may only match after preReq has been exhausted.
|
||||
func (c *Call) After(preReq *Call) *Call {
|
||||
c.t.Helper()
|
||||
|
||||
if c == preReq {
|
||||
c.t.Fatalf("A call isn't allowed to be its own prerequisite")
|
||||
}
|
||||
if preReq.isPreReq(c) {
|
||||
c.t.Fatalf("Loop in call order: %v is a prerequisite to %v (possibly indirectly).", c, preReq)
|
||||
}
|
||||
|
||||
c.preReqs = append(c.preReqs, preReq)
|
||||
return c
|
||||
}
|
||||
|
||||
// Returns true if the minimum number of calls have been made.
|
||||
func (c *Call) satisfied() bool {
|
||||
return c.numCalls >= c.minCalls
|
||||
}
|
||||
|
||||
// Returns true iff the maximum number of calls have been made.
|
||||
func (c *Call) exhausted() bool {
|
||||
return c.numCalls >= c.maxCalls
|
||||
}
|
||||
|
||||
func (c *Call) String() string {
|
||||
args := make([]string, len(c.args))
|
||||
for i, arg := range c.args {
|
||||
args[i] = arg.String()
|
||||
}
|
||||
arguments := strings.Join(args, ", ")
|
||||
return fmt.Sprintf("%T.%v(%s) %s", c.receiver, c.method, arguments, c.origin)
|
||||
}
|
||||
|
||||
// Tests if the given call matches the expected call.
|
||||
// If yes, returns nil. If no, returns error with message explaining why it does not match.
|
||||
func (c *Call) matches(args []interface{}) error {
|
||||
if !c.methodType.IsVariadic() {
|
||||
if len(args) != len(c.args) {
|
||||
return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: %d",
|
||||
c.origin, len(args), len(c.args))
|
||||
}
|
||||
|
||||
for i, m := range c.args {
|
||||
if !m.Matches(args[i]) {
|
||||
return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v",
|
||||
c.origin, strconv.Itoa(i), args[i], m)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if len(c.args) < c.methodType.NumIn()-1 {
|
||||
return fmt.Errorf("Expected call at %s has the wrong number of matchers. Got: %d, want: %d",
|
||||
c.origin, len(c.args), c.methodType.NumIn()-1)
|
||||
}
|
||||
if len(c.args) != c.methodType.NumIn() && len(args) != len(c.args) {
|
||||
return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: %d",
|
||||
c.origin, len(args), len(c.args))
|
||||
}
|
||||
if len(args) < len(c.args)-1 {
|
||||
return fmt.Errorf("Expected call at %s has the wrong number of arguments. Got: %d, want: greater than or equal to %d",
|
||||
c.origin, len(args), len(c.args)-1)
|
||||
}
|
||||
|
||||
for i, m := range c.args {
|
||||
if i < c.methodType.NumIn()-1 {
|
||||
// Non-variadic args
|
||||
if !m.Matches(args[i]) {
|
||||
return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v",
|
||||
c.origin, strconv.Itoa(i), args[i], m)
|
||||
}
|
||||
continue
|
||||
}
|
||||
// The last arg has a possibility of a variadic argument, so let it branch
|
||||
|
||||
// sample: Foo(a int, b int, c ...int)
|
||||
if i < len(c.args) && i < len(args) {
|
||||
if m.Matches(args[i]) {
|
||||
// Got Foo(a, b, c) want Foo(matcherA, matcherB, gomock.Any())
|
||||
// Got Foo(a, b, c) want Foo(matcherA, matcherB, someSliceMatcher)
|
||||
// Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC)
|
||||
// Got Foo(a, b) want Foo(matcherA, matcherB)
|
||||
// Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// The number of actual args don't match the number of matchers,
|
||||
// or the last matcher is a slice and the last arg is not.
|
||||
// If this function still matches it is because the last matcher
|
||||
// matches all the remaining arguments or the lack of any.
|
||||
// Convert the remaining arguments, if any, into a slice of the
|
||||
// expected type.
|
||||
vargsType := c.methodType.In(c.methodType.NumIn() - 1)
|
||||
vargs := reflect.MakeSlice(vargsType, 0, len(args)-i)
|
||||
for _, arg := range args[i:] {
|
||||
vargs = reflect.Append(vargs, reflect.ValueOf(arg))
|
||||
}
|
||||
if m.Matches(vargs.Interface()) {
|
||||
// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, gomock.Any())
|
||||
// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, someSliceMatcher)
|
||||
// Got Foo(a, b) want Foo(matcherA, matcherB, gomock.Any())
|
||||
// Got Foo(a, b) want Foo(matcherA, matcherB, someEmptySliceMatcher)
|
||||
break
|
||||
}
|
||||
// Wrong number of matchers or not match. Fail.
|
||||
// Got Foo(a, b) want Foo(matcherA, matcherB, matcherC, matcherD)
|
||||
// Got Foo(a, b, c) want Foo(matcherA, matcherB, matcherC, matcherD)
|
||||
// Got Foo(a, b, c, d) want Foo(matcherA, matcherB, matcherC, matcherD, matcherE)
|
||||
// Got Foo(a, b, c, d, e) want Foo(matcherA, matcherB, matcherC, matcherD)
|
||||
// Got Foo(a, b, c) want Foo(matcherA, matcherB)
|
||||
return fmt.Errorf("Expected call at %s doesn't match the argument at index %s.\nGot: %v\nWant: %v",
|
||||
c.origin, strconv.Itoa(i), args[i:], c.args[i])
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all prerequisite calls have been satisfied.
|
||||
for _, preReqCall := range c.preReqs {
|
||||
if !preReqCall.satisfied() {
|
||||
return fmt.Errorf("Expected call at %s doesn't have a prerequisite call satisfied:\n%v\nshould be called before:\n%v",
|
||||
c.origin, preReqCall, c)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the call is not exhausted.
|
||||
if c.exhausted() {
|
||||
return fmt.Errorf("Expected call at %s has already been called the max number of times.", c.origin)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// dropPrereqs tells the expected Call to not re-check prerequisite calls any
|
||||
// longer, and to return its current set.
|
||||
func (c *Call) dropPrereqs() (preReqs []*Call) {
|
||||
preReqs = c.preReqs
|
||||
c.preReqs = nil
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Call) call(args []interface{}) []func([]interface{}) []interface{} {
|
||||
c.numCalls++
|
||||
return c.actions
|
||||
}
|
||||
|
||||
// InOrder declares that the given calls should occur in order.
|
||||
func InOrder(calls ...*Call) {
|
||||
for i := 1; i < len(calls); i++ {
|
||||
calls[i].After(calls[i-1])
|
||||
}
|
||||
}
|
||||
|
||||
func setSlice(arg interface{}, v reflect.Value) {
|
||||
va := reflect.ValueOf(arg)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
va.Index(i).Set(v.Index(i))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Call) addAction(action func([]interface{}) []interface{}) {
|
||||
c.actions = append(c.actions, action)
|
||||
}
|
||||
108
vendor/github.com/golang/mock/gomock/callset.go
generated
vendored
108
vendor/github.com/golang/mock/gomock/callset.go
generated
vendored
|
|
@ -1,108 +0,0 @@
|
|||
// Copyright 2011 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gomock
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// callSet represents a set of expected calls, indexed by receiver and method
|
||||
// name.
|
||||
type callSet struct {
|
||||
// Calls that are still expected.
|
||||
expected map[callSetKey][]*Call
|
||||
// Calls that have been exhausted.
|
||||
exhausted map[callSetKey][]*Call
|
||||
}
|
||||
|
||||
// callSetKey is the key in the maps in callSet
|
||||
type callSetKey struct {
|
||||
receiver interface{}
|
||||
fname string
|
||||
}
|
||||
|
||||
func newCallSet() *callSet {
|
||||
return &callSet{make(map[callSetKey][]*Call), make(map[callSetKey][]*Call)}
|
||||
}
|
||||
|
||||
// Add adds a new expected call.
|
||||
func (cs callSet) Add(call *Call) {
|
||||
key := callSetKey{call.receiver, call.method}
|
||||
m := cs.expected
|
||||
if call.exhausted() {
|
||||
m = cs.exhausted
|
||||
}
|
||||
m[key] = append(m[key], call)
|
||||
}
|
||||
|
||||
// Remove removes an expected call.
|
||||
func (cs callSet) Remove(call *Call) {
|
||||
key := callSetKey{call.receiver, call.method}
|
||||
calls := cs.expected[key]
|
||||
for i, c := range calls {
|
||||
if c == call {
|
||||
// maintain order for remaining calls
|
||||
cs.expected[key] = append(calls[:i], calls[i+1:]...)
|
||||
cs.exhausted[key] = append(cs.exhausted[key], call)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FindMatch searches for a matching call. Returns error with explanation message if no call matched.
|
||||
func (cs callSet) FindMatch(receiver interface{}, method string, args []interface{}) (*Call, error) {
|
||||
key := callSetKey{receiver, method}
|
||||
|
||||
// Search through the expected calls.
|
||||
expected := cs.expected[key]
|
||||
var callsErrors bytes.Buffer
|
||||
for _, call := range expected {
|
||||
err := call.matches(args)
|
||||
if err != nil {
|
||||
fmt.Fprintf(&callsErrors, "\n%v", err)
|
||||
} else {
|
||||
return call, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't found a match then search through the exhausted calls so we
|
||||
// get useful error messages.
|
||||
exhausted := cs.exhausted[key]
|
||||
for _, call := range exhausted {
|
||||
if err := call.matches(args); err != nil {
|
||||
fmt.Fprintf(&callsErrors, "\n%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(expected)+len(exhausted) == 0 {
|
||||
fmt.Fprintf(&callsErrors, "there are no expected calls of the method %q for that receiver", method)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf(callsErrors.String())
|
||||
}
|
||||
|
||||
// Failures returns the calls that are not satisfied.
|
||||
func (cs callSet) Failures() []*Call {
|
||||
failures := make([]*Call, 0, len(cs.expected))
|
||||
for _, calls := range cs.expected {
|
||||
for _, call := range calls {
|
||||
if !call.satisfied() {
|
||||
failures = append(failures, call)
|
||||
}
|
||||
}
|
||||
}
|
||||
return failures
|
||||
}
|
||||
235
vendor/github.com/golang/mock/gomock/controller.go
generated
vendored
235
vendor/github.com/golang/mock/gomock/controller.go
generated
vendored
|
|
@ -1,235 +0,0 @@
|
|||
// Copyright 2010 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// GoMock - a mock framework for Go.
|
||||
//
|
||||
// Standard usage:
|
||||
// (1) Define an interface that you wish to mock.
|
||||
// type MyInterface interface {
|
||||
// SomeMethod(x int64, y string)
|
||||
// }
|
||||
// (2) Use mockgen to generate a mock from the interface.
|
||||
// (3) Use the mock in a test:
|
||||
// func TestMyThing(t *testing.T) {
|
||||
// mockCtrl := gomock.NewController(t)
|
||||
// defer mockCtrl.Finish()
|
||||
//
|
||||
// mockObj := something.NewMockMyInterface(mockCtrl)
|
||||
// mockObj.EXPECT().SomeMethod(4, "blah")
|
||||
// // pass mockObj to a real object and play with it.
|
||||
// }
|
||||
//
|
||||
// By default, expected calls are not enforced to run in any particular order.
|
||||
// Call order dependency can be enforced by use of InOrder and/or Call.After.
|
||||
// Call.After can create more varied call order dependencies, but InOrder is
|
||||
// often more convenient.
|
||||
//
|
||||
// The following examples create equivalent call order dependencies.
|
||||
//
|
||||
// Example of using Call.After to chain expected call order:
|
||||
//
|
||||
// firstCall := mockObj.EXPECT().SomeMethod(1, "first")
|
||||
// secondCall := mockObj.EXPECT().SomeMethod(2, "second").After(firstCall)
|
||||
// mockObj.EXPECT().SomeMethod(3, "third").After(secondCall)
|
||||
//
|
||||
// Example of using InOrder to declare expected call order:
|
||||
//
|
||||
// gomock.InOrder(
|
||||
// mockObj.EXPECT().SomeMethod(1, "first"),
|
||||
// mockObj.EXPECT().SomeMethod(2, "second"),
|
||||
// mockObj.EXPECT().SomeMethod(3, "third"),
|
||||
// )
|
||||
//
|
||||
// TODO:
|
||||
// - Handle different argument/return types (e.g. ..., chan, map, interface).
|
||||
package gomock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A TestReporter is something that can be used to report test failures.
|
||||
// It is satisfied by the standard library's *testing.T.
|
||||
type TestReporter interface {
|
||||
Errorf(format string, args ...interface{})
|
||||
Fatalf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
// TestHelper is a TestReporter that has the Helper method. It is satisfied
|
||||
// by the standard library's *testing.T.
|
||||
type TestHelper interface {
|
||||
TestReporter
|
||||
Helper()
|
||||
}
|
||||
|
||||
// A Controller represents the top-level control of a mock ecosystem.
|
||||
// It defines the scope and lifetime of mock objects, as well as their expectations.
|
||||
// It is safe to call Controller's methods from multiple goroutines.
|
||||
type Controller struct {
|
||||
// T should only be called within a generated mock. It is not intended to
|
||||
// be used in user code and may be changed in future versions. T is the
|
||||
// TestReporter passed in when creating the Controller via NewController.
|
||||
// If the TestReporter does not implment a TestHelper it will be wrapped
|
||||
// with a nopTestHelper.
|
||||
T TestHelper
|
||||
mu sync.Mutex
|
||||
expectedCalls *callSet
|
||||
finished bool
|
||||
}
|
||||
|
||||
func NewController(t TestReporter) *Controller {
|
||||
h, ok := t.(TestHelper)
|
||||
if !ok {
|
||||
h = nopTestHelper{t}
|
||||
}
|
||||
|
||||
return &Controller{
|
||||
T: h,
|
||||
expectedCalls: newCallSet(),
|
||||
}
|
||||
}
|
||||
|
||||
type cancelReporter struct {
|
||||
TestHelper
|
||||
cancel func()
|
||||
}
|
||||
|
||||
func (r *cancelReporter) Errorf(format string, args ...interface{}) {
|
||||
r.TestHelper.Errorf(format, args...)
|
||||
}
|
||||
func (r *cancelReporter) Fatalf(format string, args ...interface{}) {
|
||||
defer r.cancel()
|
||||
r.TestHelper.Fatalf(format, args...)
|
||||
}
|
||||
|
||||
// WithContext returns a new Controller and a Context, which is cancelled on any
|
||||
// fatal failure.
|
||||
func WithContext(ctx context.Context, t TestReporter) (*Controller, context.Context) {
|
||||
h, ok := t.(TestHelper)
|
||||
if !ok {
|
||||
h = nopTestHelper{t}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
return NewController(&cancelReporter{h, cancel}), ctx
|
||||
}
|
||||
|
||||
type nopTestHelper struct {
|
||||
TestReporter
|
||||
}
|
||||
|
||||
func (h nopTestHelper) Helper() {}
|
||||
|
||||
func (ctrl *Controller) RecordCall(receiver interface{}, method string, args ...interface{}) *Call {
|
||||
ctrl.T.Helper()
|
||||
|
||||
recv := reflect.ValueOf(receiver)
|
||||
for i := 0; i < recv.Type().NumMethod(); i++ {
|
||||
if recv.Type().Method(i).Name == method {
|
||||
return ctrl.RecordCallWithMethodType(receiver, method, recv.Method(i).Type(), args...)
|
||||
}
|
||||
}
|
||||
ctrl.T.Fatalf("gomock: failed finding method %s on %T", method, receiver)
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func (ctrl *Controller) RecordCallWithMethodType(receiver interface{}, method string, methodType reflect.Type, args ...interface{}) *Call {
|
||||
ctrl.T.Helper()
|
||||
|
||||
call := newCall(ctrl.T, receiver, method, methodType, args...)
|
||||
|
||||
ctrl.mu.Lock()
|
||||
defer ctrl.mu.Unlock()
|
||||
ctrl.expectedCalls.Add(call)
|
||||
|
||||
return call
|
||||
}
|
||||
|
||||
func (ctrl *Controller) Call(receiver interface{}, method string, args ...interface{}) []interface{} {
|
||||
ctrl.T.Helper()
|
||||
|
||||
// Nest this code so we can use defer to make sure the lock is released.
|
||||
actions := func() []func([]interface{}) []interface{} {
|
||||
ctrl.T.Helper()
|
||||
ctrl.mu.Lock()
|
||||
defer ctrl.mu.Unlock()
|
||||
|
||||
expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args)
|
||||
if err != nil {
|
||||
origin := callerInfo(2)
|
||||
ctrl.T.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s", receiver, method, args, origin, err)
|
||||
}
|
||||
|
||||
// Two things happen here:
|
||||
// * the matching call no longer needs to check prerequite calls,
|
||||
// * and the prerequite calls are no longer expected, so remove them.
|
||||
preReqCalls := expected.dropPrereqs()
|
||||
for _, preReqCall := range preReqCalls {
|
||||
ctrl.expectedCalls.Remove(preReqCall)
|
||||
}
|
||||
|
||||
actions := expected.call(args)
|
||||
if expected.exhausted() {
|
||||
ctrl.expectedCalls.Remove(expected)
|
||||
}
|
||||
return actions
|
||||
}()
|
||||
|
||||
var rets []interface{}
|
||||
for _, action := range actions {
|
||||
if r := action(args); r != nil {
|
||||
rets = r
|
||||
}
|
||||
}
|
||||
|
||||
return rets
|
||||
}
|
||||
|
||||
func (ctrl *Controller) Finish() {
|
||||
ctrl.T.Helper()
|
||||
|
||||
ctrl.mu.Lock()
|
||||
defer ctrl.mu.Unlock()
|
||||
|
||||
if ctrl.finished {
|
||||
ctrl.T.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.")
|
||||
}
|
||||
ctrl.finished = true
|
||||
|
||||
// If we're currently panicking, probably because this is a deferred call,
|
||||
// pass through the panic.
|
||||
if err := recover(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Check that all remaining expected calls are satisfied.
|
||||
failures := ctrl.expectedCalls.Failures()
|
||||
for _, call := range failures {
|
||||
ctrl.T.Errorf("missing call(s) to %v", call)
|
||||
}
|
||||
if len(failures) != 0 {
|
||||
ctrl.T.Fatalf("aborting test due to missing call(s)")
|
||||
}
|
||||
}
|
||||
|
||||
func callerInfo(skip int) string {
|
||||
if _, file, line, ok := runtime.Caller(skip + 1); ok {
|
||||
return fmt.Sprintf("%s:%d", file, line)
|
||||
}
|
||||
return "unknown file"
|
||||
}
|
||||
122
vendor/github.com/golang/mock/gomock/matchers.go
generated
vendored
122
vendor/github.com/golang/mock/gomock/matchers.go
generated
vendored
|
|
@ -1,122 +0,0 @@
|
|||
// Copyright 2010 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gomock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// A Matcher is a representation of a class of values.
|
||||
// It is used to represent the valid or expected arguments to a mocked method.
|
||||
type Matcher interface {
|
||||
// Matches returns whether x is a match.
|
||||
Matches(x interface{}) bool
|
||||
|
||||
// String describes what the matcher matches.
|
||||
String() string
|
||||
}
|
||||
|
||||
type anyMatcher struct{}
|
||||
|
||||
func (anyMatcher) Matches(x interface{}) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (anyMatcher) String() string {
|
||||
return "is anything"
|
||||
}
|
||||
|
||||
type eqMatcher struct {
|
||||
x interface{}
|
||||
}
|
||||
|
||||
func (e eqMatcher) Matches(x interface{}) bool {
|
||||
return reflect.DeepEqual(e.x, x)
|
||||
}
|
||||
|
||||
func (e eqMatcher) String() string {
|
||||
return fmt.Sprintf("is equal to %v", e.x)
|
||||
}
|
||||
|
||||
type nilMatcher struct{}
|
||||
|
||||
func (nilMatcher) Matches(x interface{}) bool {
|
||||
if x == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(x)
|
||||
switch v.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map,
|
||||
reflect.Ptr, reflect.Slice:
|
||||
return v.IsNil()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (nilMatcher) String() string {
|
||||
return "is nil"
|
||||
}
|
||||
|
||||
type notMatcher struct {
|
||||
m Matcher
|
||||
}
|
||||
|
||||
func (n notMatcher) Matches(x interface{}) bool {
|
||||
return !n.m.Matches(x)
|
||||
}
|
||||
|
||||
func (n notMatcher) String() string {
|
||||
// TODO: Improve this if we add a NotString method to the Matcher interface.
|
||||
return "not(" + n.m.String() + ")"
|
||||
}
|
||||
|
||||
type assignableToTypeOfMatcher struct {
|
||||
targetType reflect.Type
|
||||
}
|
||||
|
||||
func (m assignableToTypeOfMatcher) Matches(x interface{}) bool {
|
||||
return reflect.TypeOf(x).AssignableTo(m.targetType)
|
||||
}
|
||||
|
||||
func (m assignableToTypeOfMatcher) String() string {
|
||||
return "is assignable to " + m.targetType.Name()
|
||||
}
|
||||
|
||||
// Constructors
|
||||
func Any() Matcher { return anyMatcher{} }
|
||||
func Eq(x interface{}) Matcher { return eqMatcher{x} }
|
||||
func Nil() Matcher { return nilMatcher{} }
|
||||
func Not(x interface{}) Matcher {
|
||||
if m, ok := x.(Matcher); ok {
|
||||
return notMatcher{m}
|
||||
}
|
||||
return notMatcher{Eq(x)}
|
||||
}
|
||||
|
||||
// AssignableToTypeOf is a Matcher that matches if the parameter to the mock
|
||||
// function is assignable to the type of the parameter to this function.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// dbMock.EXPECT().
|
||||
// Insert(gomock.AssignableToTypeOf(&EmployeeRecord{})).
|
||||
// Return(errors.New("DB error"))
|
||||
//
|
||||
func AssignableToTypeOf(x interface{}) Matcher {
|
||||
return assignableToTypeOfMatcher{reflect.TypeOf(x)}
|
||||
}
|
||||
13
vendor/github.com/inconshreveable/go-vhost/LICENSE
generated
vendored
13
vendor/github.com/inconshreveable/go-vhost/LICENSE
generated
vendored
|
|
@ -1,13 +0,0 @@
|
|||
Copyright 2014 Alan Shreve
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
110
vendor/github.com/inconshreveable/go-vhost/README.md
generated
vendored
110
vendor/github.com/inconshreveable/go-vhost/README.md
generated
vendored
|
|
@ -1,110 +0,0 @@
|
|||
# go-vhost
|
||||
go-vhost is a simple library that lets you implement virtual hosting functionality for different protocols (HTTP and TLS so far). go-vhost has a high-level and a low-level interface. The high-level interface lets you wrap existing net.Listeners with "muxer" objects. You can then Listen() on a muxer for a particular virtual host name of interest which will return to you a net.Listener for just connections with the virtual hostname of interest.
|
||||
|
||||
The lower-level go-vhost interface are just functions which extract the name/routing information for the given protocol and return an object implementing net.Conn which works as if no bytes had been consumed.
|
||||
|
||||
### [API Documentation](https://godoc.org/github.com/inconshreveable/go-vhost)
|
||||
|
||||
### Usage
|
||||
```go
|
||||
l, _ := net.Listen("tcp", *listen)
|
||||
|
||||
// start multiplexing on it
|
||||
mux, _ := vhost.NewHTTPMuxer(l, muxTimeout)
|
||||
|
||||
// listen for connections to different domains
|
||||
for _, v := range virtualHosts {
|
||||
vhost := v
|
||||
|
||||
// vhost.Name is a virtual hostname like "foo.example.com"
|
||||
muxListener, _ := mux.Listen(vhost.Name())
|
||||
|
||||
go func(vh virtualHost, ml net.Listener) {
|
||||
for {
|
||||
conn, _ := ml.Accept()
|
||||
go vh.Handle(conn)
|
||||
}
|
||||
}(vhost, muxListener)
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := mux.NextError()
|
||||
|
||||
switch err.(type) {
|
||||
case vhost.BadRequest:
|
||||
log.Printf("got a bad request!")
|
||||
conn.Write([]byte("bad request"))
|
||||
case vhost.NotFound:
|
||||
log.Printf("got a connection for an unknown vhost")
|
||||
conn.Write([]byte("vhost not found"))
|
||||
case vhost.Closed:
|
||||
log.Printf("closed conn: %s", err)
|
||||
default:
|
||||
if conn != nil {
|
||||
conn.Write([]byte("server error"))
|
||||
}
|
||||
}
|
||||
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
```
|
||||
### Low-level API usage
|
||||
```go
|
||||
// accept a new connection
|
||||
conn, _ := listener.Accept()
|
||||
|
||||
// parse out the HTTP request and the Host header
|
||||
if vhostConn, err = vhost.HTTP(conn); err != nil {
|
||||
panic("Not a valid http connection!")
|
||||
}
|
||||
|
||||
fmt.Printf("Target Host: ", vhostConn.Host())
|
||||
// Target Host: example.com
|
||||
|
||||
// vhostConn contains the entire request as if no bytes had been consumed
|
||||
bytes, _ := ioutil.ReadAll(vhostConn)
|
||||
fmt.Printf("%s", bytes)
|
||||
// GET / HTTP/1.1
|
||||
// Host: example.com
|
||||
// User-Agent: ...
|
||||
// ...
|
||||
```
|
||||
|
||||
### Advanced introspection
|
||||
The entire HTTP request headers are available for inspection in case you want to mux on something besides the Host header:
|
||||
```go
|
||||
// parse out the HTTP request and the Host header
|
||||
if vhostConn, err = vhost.HTTP(conn); err != nil {
|
||||
panic("Not a valid http connection!")
|
||||
}
|
||||
|
||||
httpVersion := vhost.Request.MinorVersion
|
||||
customRouting := vhost.Request.Header["X-Custom-Routing-Header"]
|
||||
```
|
||||
|
||||
Likewise for TLS, you can look at detailed information about the ClientHello message:
|
||||
```go
|
||||
if vhostConn, err = vhost.TLS(conn); err != nil {
|
||||
panic("Not a valid TLS connection!")
|
||||
}
|
||||
|
||||
cipherSuites := vhost.ClientHelloMsg.CipherSuites
|
||||
sessionId := vhost.ClientHelloMsg.SessionId
|
||||
```
|
||||
|
||||
##### Memory reduction with Free
|
||||
After you're done muxing, you probably don't need to inspect the header data anymore, so you can make it available for garbage collection:
|
||||
|
||||
```go
|
||||
// look up the upstream host
|
||||
upstreamHost := hostMapping[vhostConn.Host()]
|
||||
|
||||
// free up the muxing data
|
||||
vhostConn.Free()
|
||||
|
||||
// vhostConn.Host() == ""
|
||||
// vhostConn.Request == nil (HTTP)
|
||||
// vhostConn.ClientHelloMsg == nil (TLS)
|
||||
```
|
||||
42
vendor/github.com/inconshreveable/go-vhost/http.go
generated
vendored
42
vendor/github.com/inconshreveable/go-vhost/http.go
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
package vhost
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type HTTPConn struct {
|
||||
*sharedConn
|
||||
Request *http.Request
|
||||
}
|
||||
|
||||
// HTTP parses the head of the first HTTP request on conn and returns
|
||||
// a new, unread connection with metadata for virtual host muxing
|
||||
func HTTP(conn net.Conn) (httpConn *HTTPConn, err error) {
|
||||
c, rd := newShared(conn)
|
||||
|
||||
httpConn = &HTTPConn{sharedConn: c}
|
||||
if httpConn.Request, err = http.ReadRequest(bufio.NewReader(rd)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// You probably don't need access to the request body and this makes the API
|
||||
// simpler by allowing you to call Free() optionally
|
||||
httpConn.Request.Body.Close()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Free sets Request to nil so that it can be garbage collected
|
||||
func (c *HTTPConn) Free() {
|
||||
c.Request = nil
|
||||
}
|
||||
|
||||
func (c *HTTPConn) Host() string {
|
||||
if c.Request == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return c.Request.Host
|
||||
}
|
||||
11
vendor/github.com/inconshreveable/go-vhost/interface.go
generated
vendored
11
vendor/github.com/inconshreveable/go-vhost/interface.go
generated
vendored
|
|
@ -1,11 +0,0 @@
|
|||
package vhost
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
type Conn interface {
|
||||
net.Conn
|
||||
Host() string
|
||||
Free()
|
||||
}
|
||||
337
vendor/github.com/inconshreveable/go-vhost/mux.go
generated
vendored
337
vendor/github.com/inconshreveable/go-vhost/mux.go
generated
vendored
|
|
@ -1,337 +0,0 @@
|
|||
package vhost
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
normalize = strings.ToLower
|
||||
isClosed = func(err error) bool {
|
||||
netErr, ok := err.(net.Error)
|
||||
if ok {
|
||||
return netErr.Temporary()
|
||||
}
|
||||
return false
|
||||
}
|
||||
)
|
||||
|
||||
// NotFound is returned when a vhost is not found
|
||||
type NotFound struct {
|
||||
error
|
||||
}
|
||||
|
||||
// BadRequest is returned when extraction of the vhost name fails
|
||||
type BadRequest struct {
|
||||
error
|
||||
}
|
||||
|
||||
// Closed is returned when the underlying connection is closed
|
||||
type Closed struct {
|
||||
error
|
||||
}
|
||||
|
||||
type (
|
||||
// this is the function you apply to a net.Conn to get
|
||||
// a new virtual-host multiplexed connection
|
||||
muxFn func(net.Conn) (Conn, error)
|
||||
|
||||
// an error encountered when multiplexing a connection
|
||||
muxErr struct {
|
||||
err error
|
||||
conn net.Conn
|
||||
}
|
||||
)
|
||||
|
||||
type VhostMuxer struct {
|
||||
listener net.Listener // listener on which we mux connections
|
||||
muxTimeout time.Duration // a connection fails if it doesn't send enough data to mux after this timeout
|
||||
vhostFn muxFn // new connections are multiplexed by applying this function
|
||||
muxErrors chan muxErr // all muxing errors are sent over this channel
|
||||
registry map[string]*Listener // registry of name -> listener
|
||||
sync.RWMutex // protects the registry
|
||||
}
|
||||
|
||||
func NewVhostMuxer(listener net.Listener, vhostFn muxFn, muxTimeout time.Duration) (*VhostMuxer, error) {
|
||||
mux := &VhostMuxer{
|
||||
listener: listener,
|
||||
muxTimeout: muxTimeout,
|
||||
vhostFn: vhostFn,
|
||||
muxErrors: make(chan muxErr),
|
||||
registry: make(map[string]*Listener),
|
||||
}
|
||||
|
||||
go mux.run()
|
||||
return mux, nil
|
||||
}
|
||||
|
||||
// Listen begins multiplexing the underlying connection to send new
|
||||
// connections for the given name over the returned listener.
|
||||
func (m *VhostMuxer) Listen(name string) (net.Listener, error) {
|
||||
name = normalize(name)
|
||||
|
||||
vhost := &Listener{
|
||||
name: name,
|
||||
mux: m,
|
||||
accept: make(chan Conn),
|
||||
}
|
||||
|
||||
if err := m.set(name, vhost); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return vhost, nil
|
||||
}
|
||||
|
||||
// NextError returns the next error encountered while mux'ing a connection.
|
||||
// The net.Conn may be nil if the wrapped listener returned an error from Accept()
|
||||
func (m *VhostMuxer) NextError() (net.Conn, error) {
|
||||
muxErr := <-m.muxErrors
|
||||
return muxErr.conn, muxErr.err
|
||||
}
|
||||
|
||||
// Close closes the underlying listener
|
||||
func (m *VhostMuxer) Close() {
|
||||
m.listener.Close()
|
||||
}
|
||||
|
||||
// run is the VhostMuxer's main loop for accepting new connections from the wrapped listener
|
||||
func (m *VhostMuxer) run() {
|
||||
for {
|
||||
conn, err := m.listener.Accept()
|
||||
if err != nil {
|
||||
if isClosed(err) {
|
||||
m.sendError(nil, Closed{err})
|
||||
return
|
||||
} else {
|
||||
m.sendError(nil, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
go m.handle(conn)
|
||||
}
|
||||
}
|
||||
|
||||
// handle muxes a connection accepted from the listener
|
||||
func (m *VhostMuxer) handle(conn net.Conn) {
|
||||
defer func() {
|
||||
// recover from failures
|
||||
if r := recover(); r != nil {
|
||||
m.sendError(conn, fmt.Errorf("NameMux.handle failed with error %v", r))
|
||||
}
|
||||
}()
|
||||
|
||||
// Make sure we detect dead connections while we decide how to multiplex
|
||||
if err := conn.SetDeadline(time.Now().Add(m.muxTimeout)); err != nil {
|
||||
m.sendError(conn, fmt.Errorf("Failed to set deadline: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// extract the name
|
||||
vconn, err := m.vhostFn(conn)
|
||||
if err != nil {
|
||||
m.sendError(conn, BadRequest{fmt.Errorf("Failed to extract vhost name: %v", err)})
|
||||
return
|
||||
}
|
||||
|
||||
// normalize the name
|
||||
host := normalize(vconn.Host())
|
||||
|
||||
// look up the correct listener
|
||||
l, ok := m.get(host)
|
||||
if !ok {
|
||||
m.sendError(vconn, NotFound{fmt.Errorf("Host not found: %v", host)})
|
||||
return
|
||||
}
|
||||
|
||||
if err = vconn.SetDeadline(time.Time{}); err != nil {
|
||||
m.sendError(vconn, fmt.Errorf("Failed unset connection deadline: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
l.accept <- vconn
|
||||
}
|
||||
|
||||
func (m *VhostMuxer) sendError(conn net.Conn, err error) {
|
||||
m.muxErrors <- muxErr{conn: conn, err: err}
|
||||
}
|
||||
|
||||
func (m *VhostMuxer) get(name string) (l *Listener, ok bool) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
l, ok = m.registry[name]
|
||||
if !ok {
|
||||
// look for a matching wildcard
|
||||
parts := strings.Split(name, ".")
|
||||
for i := 0; i < len(parts)-1; i++ {
|
||||
parts[i] = "*"
|
||||
name = strings.Join(parts[i:], ".")
|
||||
l, ok = m.registry[name]
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m *VhostMuxer) set(name string, l *Listener) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if _, exists := m.registry[name]; exists {
|
||||
return fmt.Errorf("name %s is already bound", name)
|
||||
}
|
||||
m.registry[name] = l
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *VhostMuxer) del(name string) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
delete(m.registry, name)
|
||||
}
|
||||
|
||||
const (
|
||||
serverError = `HTTP/1.0 500 Internal Server Error
|
||||
Content-Length: 22
|
||||
|
||||
Internal Server Error
|
||||
`
|
||||
|
||||
notFound = `HTTP/1.0 404 Not Found
|
||||
Content-Length: 14
|
||||
|
||||
404 not found
|
||||
`
|
||||
|
||||
badRequest = `HTTP/1.0 400 Bad Request
|
||||
Content-Length: 12
|
||||
|
||||
Bad Request
|
||||
`
|
||||
)
|
||||
|
||||
type HTTPMuxer struct {
|
||||
*VhostMuxer
|
||||
}
|
||||
|
||||
// HandleErrors handles muxing errors by calling .NextError(). You must
|
||||
// invoke this function if you do not want to handle the errors yourself.
|
||||
func (m *HTTPMuxer) HandleErrors() {
|
||||
for {
|
||||
m.HandleError(m.NextError())
|
||||
}
|
||||
}
|
||||
|
||||
func (m *HTTPMuxer) HandleError(conn net.Conn, err error) {
|
||||
switch err.(type) {
|
||||
case Closed:
|
||||
return
|
||||
case NotFound:
|
||||
conn.Write([]byte(notFound))
|
||||
case BadRequest:
|
||||
conn.Write([]byte(badRequest))
|
||||
default:
|
||||
if conn != nil {
|
||||
conn.Write([]byte(serverError))
|
||||
}
|
||||
}
|
||||
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// NewHTTPMuxer begins muxing HTTP connections on the given listener by inspecting
|
||||
// the HTTP Host header in new connections.
|
||||
func NewHTTPMuxer(listener net.Listener, muxTimeout time.Duration) (*HTTPMuxer, error) {
|
||||
fn := func(c net.Conn) (Conn, error) { return HTTP(c) }
|
||||
mux, err := NewVhostMuxer(listener, fn, muxTimeout)
|
||||
return &HTTPMuxer{mux}, err
|
||||
}
|
||||
|
||||
type TLSMuxer struct {
|
||||
*VhostMuxer
|
||||
}
|
||||
|
||||
// HandleErrors is the default error handler for TLS muxers. At the moment, it simply
|
||||
// closes connections which are invalid or destined for virtual host names that it is
|
||||
// not listening for.
|
||||
// You must invoke this function if you do not want to handle the errors yourself.
|
||||
func (m *TLSMuxer) HandleErrors() {
|
||||
for {
|
||||
conn, err := m.NextError()
|
||||
|
||||
if conn == nil {
|
||||
if _, ok := err.(Closed); ok {
|
||||
return
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// XXX: respond with valid TLS close messages
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TLSMuxer) Listen(name string) (net.Listener, error) {
|
||||
// TLS SNI never includes the port
|
||||
host, _, err := net.SplitHostPort(name)
|
||||
if err != nil {
|
||||
host = name
|
||||
}
|
||||
return m.VhostMuxer.Listen(host)
|
||||
}
|
||||
|
||||
// NewTLSMuxer begins muxing TLS connections by inspecting the SNI extension.
|
||||
func NewTLSMuxer(listener net.Listener, muxTimeout time.Duration) (*TLSMuxer, error) {
|
||||
fn := func(c net.Conn) (Conn, error) { return TLS(c) }
|
||||
mux, err := NewVhostMuxer(listener, fn, muxTimeout)
|
||||
return &TLSMuxer{mux}, err
|
||||
}
|
||||
|
||||
// Listener is returned by a call to Listen() on a muxer. A Listener
|
||||
// only receives connections that were made to the name passed into the muxer's
|
||||
// Listen call.
|
||||
//
|
||||
// Listener implements the net.Listener interface, so you can Accept() new
|
||||
// connections and Close() it when finished. When you Close() a Listener,
|
||||
// the parent muxer will stop listening for connections to the Listener's name.
|
||||
type Listener struct {
|
||||
name string
|
||||
mux *VhostMuxer
|
||||
accept chan Conn
|
||||
}
|
||||
|
||||
// Accept returns the next mux'd connection for this listener and blocks
|
||||
// until one is available.
|
||||
func (l *Listener) Accept() (net.Conn, error) {
|
||||
conn, ok := <-l.accept
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Listener closed")
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Close stops the parent muxer from listening for connections to the mux'd
|
||||
// virtual host name.
|
||||
func (l *Listener) Close() error {
|
||||
l.mux.del(l.name)
|
||||
close(l.accept)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Addr returns the address of the bound listener used by the parent muxer.
|
||||
func (l *Listener) Addr() net.Addr {
|
||||
// XXX: include name in address?
|
||||
return l.mux.listener.Addr()
|
||||
}
|
||||
|
||||
// Name returns the name of the virtual host this listener receives connections on.
|
||||
func (l *Listener) Name() string {
|
||||
return l.name
|
||||
}
|
||||
52
vendor/github.com/inconshreveable/go-vhost/shared.go
generated
vendored
52
vendor/github.com/inconshreveable/go-vhost/shared.go
generated
vendored
|
|
@ -1,52 +0,0 @@
|
|||
package vhost
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
initVhostBufSize = 1024 // allocate 1 KB up front to try to avoid resizing
|
||||
)
|
||||
|
||||
type sharedConn struct {
|
||||
sync.Mutex
|
||||
net.Conn // the raw connection
|
||||
vhostBuf *bytes.Buffer // all of the initial data that has to be read in order to vhost a connection is saved here
|
||||
}
|
||||
|
||||
func newShared(conn net.Conn) (*sharedConn, io.Reader) {
|
||||
c := &sharedConn{
|
||||
Conn: conn,
|
||||
vhostBuf: bytes.NewBuffer(make([]byte, 0, initVhostBufSize)),
|
||||
}
|
||||
|
||||
return c, io.TeeReader(conn, c.vhostBuf)
|
||||
}
|
||||
|
||||
func (c *sharedConn) Read(p []byte) (n int, err error) {
|
||||
c.Lock()
|
||||
if c.vhostBuf == nil {
|
||||
c.Unlock()
|
||||
return c.Conn.Read(p)
|
||||
}
|
||||
n, err = c.vhostBuf.Read(p)
|
||||
|
||||
// end of the request buffer
|
||||
if err == io.EOF {
|
||||
// let the request buffer get garbage collected
|
||||
// and make sure we don't read from it again
|
||||
c.vhostBuf = nil
|
||||
|
||||
// continue reading from the connection
|
||||
var n2 int
|
||||
n2, err = c.Conn.Read(p[n:])
|
||||
|
||||
// update total read
|
||||
n += n2
|
||||
}
|
||||
c.Unlock()
|
||||
return
|
||||
}
|
||||
434
vendor/github.com/inconshreveable/go-vhost/tls.go
generated
vendored
434
vendor/github.com/inconshreveable/go-vhost/tls.go
generated
vendored
|
|
@ -1,434 +0,0 @@
|
|||
// Portions of the TLS code are:
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// TLS virtual hosting
|
||||
|
||||
package vhost
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
maxPlaintext = 16384 // maximum plaintext payload length
|
||||
maxCiphertext = 16384 + 2048 // maximum ciphertext payload length
|
||||
recordHeaderLen = 5 // record header length
|
||||
maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
|
||||
)
|
||||
|
||||
type alert uint8
|
||||
|
||||
const (
|
||||
alertUnexpectedMessage alert = 10
|
||||
alertRecordOverflow alert = 22
|
||||
alertInternalError alert = 80
|
||||
)
|
||||
|
||||
var alertText = map[alert]string{
|
||||
alertUnexpectedMessage: "unexpected message",
|
||||
alertRecordOverflow: "record overflow",
|
||||
alertInternalError: "internal error",
|
||||
}
|
||||
|
||||
func (e alert) String() string {
|
||||
s, ok := alertText[e]
|
||||
if ok {
|
||||
return s
|
||||
}
|
||||
return "alert(" + strconv.Itoa(int(e)) + ")"
|
||||
}
|
||||
|
||||
func (e alert) Error() string {
|
||||
return e.String()
|
||||
}
|
||||
|
||||
// TLS record types.
|
||||
type recordType uint8
|
||||
|
||||
const (
|
||||
recordTypeHandshake recordType = 22
|
||||
)
|
||||
|
||||
// TLS handshake message types.
|
||||
const (
|
||||
typeClientHello uint8 = 1
|
||||
)
|
||||
|
||||
// TLS extension numbers
|
||||
var (
|
||||
extensionServerName uint16 = 0
|
||||
extensionStatusRequest uint16 = 5
|
||||
extensionSupportedCurves uint16 = 10
|
||||
extensionSupportedPoints uint16 = 11
|
||||
extensionSessionTicket uint16 = 35
|
||||
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
|
||||
)
|
||||
|
||||
// TLS CertificateStatusType (RFC 3546)
|
||||
const (
|
||||
statusTypeOCSP uint8 = 1
|
||||
)
|
||||
|
||||
// A Conn represents a secured connection.
|
||||
// It implements the net.Conn interface.
|
||||
type TLSConn struct {
|
||||
*sharedConn
|
||||
ClientHelloMsg *ClientHelloMsg
|
||||
}
|
||||
|
||||
// TLS parses the ClientHello message on conn and returns
|
||||
// a new, unread connection with metadata for virtual host muxing
|
||||
func TLS(conn net.Conn) (tlsConn *TLSConn, err error) {
|
||||
c, rd := newShared(conn)
|
||||
|
||||
tlsConn = &TLSConn{sharedConn: c}
|
||||
if tlsConn.ClientHelloMsg, err = readClientHello(rd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *TLSConn) Host() string {
|
||||
if c.ClientHelloMsg == nil {
|
||||
return ""
|
||||
}
|
||||
return c.ClientHelloMsg.ServerName
|
||||
}
|
||||
|
||||
func (c *TLSConn) Free() {
|
||||
c.ClientHelloMsg = nil
|
||||
}
|
||||
|
||||
// A block is a simple data buffer.
|
||||
type block struct {
|
||||
data []byte
|
||||
off int // index for Read
|
||||
}
|
||||
|
||||
// resize resizes block to be n bytes, growing if necessary.
|
||||
func (b *block) resize(n int) {
|
||||
if n > cap(b.data) {
|
||||
b.reserve(n)
|
||||
}
|
||||
b.data = b.data[0:n]
|
||||
}
|
||||
|
||||
// reserve makes sure that block contains a capacity of at least n bytes.
|
||||
func (b *block) reserve(n int) {
|
||||
if cap(b.data) >= n {
|
||||
return
|
||||
}
|
||||
m := cap(b.data)
|
||||
if m == 0 {
|
||||
m = 1024
|
||||
}
|
||||
for m < n {
|
||||
m *= 2
|
||||
}
|
||||
data := make([]byte, len(b.data), m)
|
||||
copy(data, b.data)
|
||||
b.data = data
|
||||
}
|
||||
|
||||
// readFromUntil reads from r into b until b contains at least n bytes
|
||||
// or else returns an error.
|
||||
func (b *block) readFromUntil(r io.Reader, n int) error {
|
||||
// quick case
|
||||
if len(b.data) >= n {
|
||||
return nil
|
||||
}
|
||||
|
||||
// read until have enough.
|
||||
b.reserve(n)
|
||||
for {
|
||||
m, err := r.Read(b.data[len(b.data):cap(b.data)])
|
||||
b.data = b.data[0 : len(b.data)+m]
|
||||
if len(b.data) >= n {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *block) Read(p []byte) (n int, err error) {
|
||||
n = copy(p, b.data[b.off:])
|
||||
b.off += n
|
||||
return
|
||||
}
|
||||
|
||||
// newBlock allocates a new block
|
||||
func newBlock() *block {
|
||||
return new(block)
|
||||
}
|
||||
|
||||
// splitBlock splits a block after the first n bytes,
|
||||
// returning a block with those n bytes and a
|
||||
// block with the remainder. the latter may be nil.
|
||||
func splitBlock(b *block, n int) (*block, *block) {
|
||||
if len(b.data) <= n {
|
||||
return b, nil
|
||||
}
|
||||
bb := newBlock()
|
||||
bb.resize(len(b.data) - n)
|
||||
copy(bb.data, b.data[n:])
|
||||
b.data = b.data[0:n]
|
||||
return b, bb
|
||||
}
|
||||
|
||||
// readHandshake reads the next handshake message from
|
||||
// the record layer.
|
||||
func readClientHello(rd io.Reader) (*ClientHelloMsg, error) {
|
||||
var nextBlock *block // raw input, right off the wire
|
||||
var hand bytes.Buffer // handshake data waiting to be read
|
||||
|
||||
// readRecord reads the next TLS record from the connection
|
||||
// and updates the record layer state.
|
||||
readRecord := func() error {
|
||||
// Caller must be in sync with connection:
|
||||
// handshake data if handshake not yet completed,
|
||||
// else application data. (We don't support renegotiation.)
|
||||
if nextBlock == nil {
|
||||
nextBlock = newBlock()
|
||||
}
|
||||
b := nextBlock
|
||||
|
||||
// Read header, payload.
|
||||
if err := b.readFromUntil(rd, recordHeaderLen); err != nil {
|
||||
return err
|
||||
}
|
||||
typ := recordType(b.data[0])
|
||||
|
||||
// No valid TLS record has a type of 0x80, however SSLv2 handshakes
|
||||
// start with a uint16 length where the MSB is set and the first record
|
||||
// is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
|
||||
// an SSLv2 client.
|
||||
if typ == 0x80 {
|
||||
return errors.New("tls: unsupported SSLv2 handshake received")
|
||||
}
|
||||
|
||||
vers := uint16(b.data[1])<<8 | uint16(b.data[2])
|
||||
n := int(b.data[3])<<8 | int(b.data[4])
|
||||
if n > maxCiphertext {
|
||||
return alertRecordOverflow
|
||||
}
|
||||
|
||||
// First message, be extra suspicious:
|
||||
// this might not be a TLS client.
|
||||
// Bail out before reading a full 'body', if possible.
|
||||
// The current max version is 3.1.
|
||||
// If the version is >= 16.0, it's probably not real.
|
||||
// Similarly, a clientHello message encodes in
|
||||
// well under a kilobyte. If the length is >= 12 kB,
|
||||
// it's probably not real.
|
||||
if (typ != recordTypeHandshake) || vers >= 0x1000 || n >= 0x3000 {
|
||||
return alertUnexpectedMessage
|
||||
}
|
||||
|
||||
if err := b.readFromUntil(rd, recordHeaderLen+n); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Process message.
|
||||
b, nextBlock = splitBlock(b, recordHeaderLen+n)
|
||||
b.off = recordHeaderLen
|
||||
data := b.data[b.off:]
|
||||
if len(data) > maxPlaintext {
|
||||
return alertRecordOverflow
|
||||
}
|
||||
|
||||
hand.Write(data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := readRecord(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := hand.Bytes()
|
||||
n := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
|
||||
if n > maxHandshake {
|
||||
return nil, alertInternalError
|
||||
}
|
||||
for hand.Len() < 4+n {
|
||||
if err := readRecord(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
data = hand.Next(4 + n)
|
||||
if data[0] != typeClientHello {
|
||||
return nil, alertUnexpectedMessage
|
||||
}
|
||||
|
||||
msg := new(ClientHelloMsg)
|
||||
if !msg.unmarshal(data) {
|
||||
return nil, alertUnexpectedMessage
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
type ClientHelloMsg struct {
|
||||
Raw []byte
|
||||
Vers uint16
|
||||
Random []byte
|
||||
SessionId []byte
|
||||
CipherSuites []uint16
|
||||
CompressionMethods []uint8
|
||||
NextProtoNeg bool
|
||||
ServerName string
|
||||
OcspStapling bool
|
||||
SupportedCurves []uint16
|
||||
SupportedPoints []uint8
|
||||
TicketSupported bool
|
||||
SessionTicket []uint8
|
||||
}
|
||||
|
||||
func (m *ClientHelloMsg) unmarshal(data []byte) bool {
|
||||
if len(data) < 42 {
|
||||
return false
|
||||
}
|
||||
m.Raw = data
|
||||
m.Vers = uint16(data[4])<<8 | uint16(data[5])
|
||||
m.Random = data[6:38]
|
||||
sessionIdLen := int(data[38])
|
||||
if sessionIdLen > 32 || len(data) < 39+sessionIdLen {
|
||||
return false
|
||||
}
|
||||
m.SessionId = data[39 : 39+sessionIdLen]
|
||||
data = data[39+sessionIdLen:]
|
||||
if len(data) < 2 {
|
||||
return false
|
||||
}
|
||||
// cipherSuiteLen is the number of bytes of cipher suite numbers. Since
|
||||
// they are uint16s, the number must be even.
|
||||
cipherSuiteLen := int(data[0])<<8 | int(data[1])
|
||||
if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
|
||||
return false
|
||||
}
|
||||
numCipherSuites := cipherSuiteLen / 2
|
||||
m.CipherSuites = make([]uint16, numCipherSuites)
|
||||
for i := 0; i < numCipherSuites; i++ {
|
||||
m.CipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i])
|
||||
}
|
||||
data = data[2+cipherSuiteLen:]
|
||||
if len(data) < 1 {
|
||||
return false
|
||||
}
|
||||
compressionMethodsLen := int(data[0])
|
||||
if len(data) < 1+compressionMethodsLen {
|
||||
return false
|
||||
}
|
||||
m.CompressionMethods = data[1 : 1+compressionMethodsLen]
|
||||
|
||||
data = data[1+compressionMethodsLen:]
|
||||
|
||||
m.NextProtoNeg = false
|
||||
m.ServerName = ""
|
||||
m.OcspStapling = false
|
||||
m.TicketSupported = false
|
||||
m.SessionTicket = nil
|
||||
|
||||
if len(data) == 0 {
|
||||
// ClientHello is optionally followed by extension data
|
||||
return true
|
||||
}
|
||||
if len(data) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
extensionsLength := int(data[0])<<8 | int(data[1])
|
||||
data = data[2:]
|
||||
if extensionsLength != len(data) {
|
||||
return false
|
||||
}
|
||||
|
||||
for len(data) != 0 {
|
||||
if len(data) < 4 {
|
||||
return false
|
||||
}
|
||||
extension := uint16(data[0])<<8 | uint16(data[1])
|
||||
length := int(data[2])<<8 | int(data[3])
|
||||
data = data[4:]
|
||||
if len(data) < length {
|
||||
return false
|
||||
}
|
||||
|
||||
switch extension {
|
||||
case extensionServerName:
|
||||
if length < 2 {
|
||||
return false
|
||||
}
|
||||
numNames := int(data[0])<<8 | int(data[1])
|
||||
d := data[2:]
|
||||
for i := 0; i < numNames; i++ {
|
||||
if len(d) < 3 {
|
||||
return false
|
||||
}
|
||||
nameType := d[0]
|
||||
nameLen := int(d[1])<<8 | int(d[2])
|
||||
d = d[3:]
|
||||
if len(d) < nameLen {
|
||||
return false
|
||||
}
|
||||
if nameType == 0 {
|
||||
m.ServerName = string(d[0:nameLen])
|
||||
break
|
||||
}
|
||||
d = d[nameLen:]
|
||||
}
|
||||
case extensionNextProtoNeg:
|
||||
if length > 0 {
|
||||
return false
|
||||
}
|
||||
m.NextProtoNeg = true
|
||||
case extensionStatusRequest:
|
||||
m.OcspStapling = length > 0 && data[0] == statusTypeOCSP
|
||||
case extensionSupportedCurves:
|
||||
// http://tools.ietf.org/html/rfc4492#section-5.5.1
|
||||
if length < 2 {
|
||||
return false
|
||||
}
|
||||
l := int(data[0])<<8 | int(data[1])
|
||||
if l%2 == 1 || length != l+2 {
|
||||
return false
|
||||
}
|
||||
numCurves := l / 2
|
||||
m.SupportedCurves = make([]uint16, numCurves)
|
||||
d := data[2:]
|
||||
for i := 0; i < numCurves; i++ {
|
||||
m.SupportedCurves[i] = uint16(d[0])<<8 | uint16(d[1])
|
||||
d = d[2:]
|
||||
}
|
||||
case extensionSupportedPoints:
|
||||
// http://tools.ietf.org/html/rfc4492#section-5.5.2
|
||||
if length < 1 {
|
||||
return false
|
||||
}
|
||||
l := int(data[0])
|
||||
if length != l+1 {
|
||||
return false
|
||||
}
|
||||
m.SupportedPoints = make([]uint8, l)
|
||||
copy(m.SupportedPoints, data[1:])
|
||||
case extensionSessionTicket:
|
||||
// http://tools.ietf.org/html/rfc5077#section-3.2
|
||||
m.TicketSupported = true
|
||||
m.SessionTicket = data[:length]
|
||||
}
|
||||
data = data[length:]
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
3
vendor/golang.org/x/net/AUTHORS
generated
vendored
3
vendor/golang.org/x/net/AUTHORS
generated
vendored
|
|
@ -1,3 +0,0 @@
|
|||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/AUTHORS.
|
||||
3
vendor/golang.org/x/net/CONTRIBUTORS
generated
vendored
3
vendor/golang.org/x/net/CONTRIBUTORS
generated
vendored
|
|
@ -1,3 +0,0 @@
|
|||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
||||
27
vendor/golang.org/x/net/LICENSE
generated
vendored
27
vendor/golang.org/x/net/LICENSE
generated
vendored
|
|
@ -1,27 +0,0 @@
|
|||
Copyright (c) 2009 The Go 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.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
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
|
||||
OWNER 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.
|
||||
22
vendor/golang.org/x/net/PATENTS
generated
vendored
22
vendor/golang.org/x/net/PATENTS
generated
vendored
|
|
@ -1,22 +0,0 @@
|
|||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
2
vendor/golang.org/x/net/http2/.gitignore
generated
vendored
2
vendor/golang.org/x/net/http2/.gitignore
generated
vendored
|
|
@ -1,2 +0,0 @@
|
|||
*~
|
||||
h2i/h2i
|
||||
51
vendor/golang.org/x/net/http2/Dockerfile
generated
vendored
51
vendor/golang.org/x/net/http2/Dockerfile
generated
vendored
|
|
@ -1,51 +0,0 @@
|
|||
#
|
||||
# This Dockerfile builds a recent curl with HTTP/2 client support, using
|
||||
# a recent nghttp2 build.
|
||||
#
|
||||
# See the Makefile for how to tag it. If Docker and that image is found, the
|
||||
# Go tests use this curl binary for integration tests.
|
||||
#
|
||||
|
||||
FROM ubuntu:trusty
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get upgrade -y && \
|
||||
apt-get install -y git-core build-essential wget
|
||||
|
||||
RUN apt-get install -y --no-install-recommends \
|
||||
autotools-dev libtool pkg-config zlib1g-dev \
|
||||
libcunit1-dev libssl-dev libxml2-dev libevent-dev \
|
||||
automake autoconf
|
||||
|
||||
# The list of packages nghttp2 recommends for h2load:
|
||||
RUN apt-get install -y --no-install-recommends make binutils \
|
||||
autoconf automake autotools-dev \
|
||||
libtool pkg-config zlib1g-dev libcunit1-dev libssl-dev libxml2-dev \
|
||||
libev-dev libevent-dev libjansson-dev libjemalloc-dev \
|
||||
cython python3.4-dev python-setuptools
|
||||
|
||||
# Note: setting NGHTTP2_VER before the git clone, so an old git clone isn't cached:
|
||||
ENV NGHTTP2_VER 895da9a
|
||||
RUN cd /root && git clone https://github.com/tatsuhiro-t/nghttp2.git
|
||||
|
||||
WORKDIR /root/nghttp2
|
||||
RUN git reset --hard $NGHTTP2_VER
|
||||
RUN autoreconf -i
|
||||
RUN automake
|
||||
RUN autoconf
|
||||
RUN ./configure
|
||||
RUN make
|
||||
RUN make install
|
||||
|
||||
WORKDIR /root
|
||||
RUN wget http://curl.haxx.se/download/curl-7.45.0.tar.gz
|
||||
RUN tar -zxvf curl-7.45.0.tar.gz
|
||||
WORKDIR /root/curl-7.45.0
|
||||
RUN ./configure --with-ssl --with-nghttp2=/usr/local
|
||||
RUN make
|
||||
RUN make install
|
||||
RUN ldconfig
|
||||
|
||||
CMD ["-h"]
|
||||
ENTRYPOINT ["/usr/local/bin/curl"]
|
||||
|
||||
3
vendor/golang.org/x/net/http2/Makefile
generated
vendored
3
vendor/golang.org/x/net/http2/Makefile
generated
vendored
|
|
@ -1,3 +0,0 @@
|
|||
curlimage:
|
||||
docker build -t gohttp2/curl .
|
||||
|
||||
20
vendor/golang.org/x/net/http2/README
generated
vendored
20
vendor/golang.org/x/net/http2/README
generated
vendored
|
|
@ -1,20 +0,0 @@
|
|||
This is a work-in-progress HTTP/2 implementation for Go.
|
||||
|
||||
It will eventually live in the Go standard library and won't require
|
||||
any changes to your code to use. It will just be automatic.
|
||||
|
||||
Status:
|
||||
|
||||
* The server support is pretty good. A few things are missing
|
||||
but are being worked on.
|
||||
* The client work has just started but shares a lot of code
|
||||
is coming along much quicker.
|
||||
|
||||
Docs are at https://godoc.org/golang.org/x/net/http2
|
||||
|
||||
Demo test server at https://http2.golang.org/
|
||||
|
||||
Help & bug reports welcome!
|
||||
|
||||
Contributing: https://golang.org/doc/contribute.html
|
||||
Bugs: https://golang.org/issue/new?title=x/net/http2:+
|
||||
641
vendor/golang.org/x/net/http2/ciphers.go
generated
vendored
641
vendor/golang.org/x/net/http2/ciphers.go
generated
vendored
|
|
@ -1,641 +0,0 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
// A list of the possible cipher suite ids. Taken from
|
||||
// http://www.iana.org/assignments/tls-parameters/tls-parameters.txt
|
||||
|
||||
const (
|
||||
cipher_TLS_NULL_WITH_NULL_NULL uint16 = 0x0000
|
||||
cipher_TLS_RSA_WITH_NULL_MD5 uint16 = 0x0001
|
||||
cipher_TLS_RSA_WITH_NULL_SHA uint16 = 0x0002
|
||||
cipher_TLS_RSA_EXPORT_WITH_RC4_40_MD5 uint16 = 0x0003
|
||||
cipher_TLS_RSA_WITH_RC4_128_MD5 uint16 = 0x0004
|
||||
cipher_TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
|
||||
cipher_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 uint16 = 0x0006
|
||||
cipher_TLS_RSA_WITH_IDEA_CBC_SHA uint16 = 0x0007
|
||||
cipher_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0008
|
||||
cipher_TLS_RSA_WITH_DES_CBC_SHA uint16 = 0x0009
|
||||
cipher_TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000A
|
||||
cipher_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x000B
|
||||
cipher_TLS_DH_DSS_WITH_DES_CBC_SHA uint16 = 0x000C
|
||||
cipher_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0x000D
|
||||
cipher_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x000E
|
||||
cipher_TLS_DH_RSA_WITH_DES_CBC_SHA uint16 = 0x000F
|
||||
cipher_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x0010
|
||||
cipher_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0011
|
||||
cipher_TLS_DHE_DSS_WITH_DES_CBC_SHA uint16 = 0x0012
|
||||
cipher_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0x0013
|
||||
cipher_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0014
|
||||
cipher_TLS_DHE_RSA_WITH_DES_CBC_SHA uint16 = 0x0015
|
||||
cipher_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x0016
|
||||
cipher_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 uint16 = 0x0017
|
||||
cipher_TLS_DH_anon_WITH_RC4_128_MD5 uint16 = 0x0018
|
||||
cipher_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA uint16 = 0x0019
|
||||
cipher_TLS_DH_anon_WITH_DES_CBC_SHA uint16 = 0x001A
|
||||
cipher_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA uint16 = 0x001B
|
||||
// Reserved uint16 = 0x001C-1D
|
||||
cipher_TLS_KRB5_WITH_DES_CBC_SHA uint16 = 0x001E
|
||||
cipher_TLS_KRB5_WITH_3DES_EDE_CBC_SHA uint16 = 0x001F
|
||||
cipher_TLS_KRB5_WITH_RC4_128_SHA uint16 = 0x0020
|
||||
cipher_TLS_KRB5_WITH_IDEA_CBC_SHA uint16 = 0x0021
|
||||
cipher_TLS_KRB5_WITH_DES_CBC_MD5 uint16 = 0x0022
|
||||
cipher_TLS_KRB5_WITH_3DES_EDE_CBC_MD5 uint16 = 0x0023
|
||||
cipher_TLS_KRB5_WITH_RC4_128_MD5 uint16 = 0x0024
|
||||
cipher_TLS_KRB5_WITH_IDEA_CBC_MD5 uint16 = 0x0025
|
||||
cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA uint16 = 0x0026
|
||||
cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA uint16 = 0x0027
|
||||
cipher_TLS_KRB5_EXPORT_WITH_RC4_40_SHA uint16 = 0x0028
|
||||
cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 uint16 = 0x0029
|
||||
cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 uint16 = 0x002A
|
||||
cipher_TLS_KRB5_EXPORT_WITH_RC4_40_MD5 uint16 = 0x002B
|
||||
cipher_TLS_PSK_WITH_NULL_SHA uint16 = 0x002C
|
||||
cipher_TLS_DHE_PSK_WITH_NULL_SHA uint16 = 0x002D
|
||||
cipher_TLS_RSA_PSK_WITH_NULL_SHA uint16 = 0x002E
|
||||
cipher_TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002F
|
||||
cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA uint16 = 0x0030
|
||||
cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA uint16 = 0x0031
|
||||
cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA uint16 = 0x0032
|
||||
cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0x0033
|
||||
cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA uint16 = 0x0034
|
||||
cipher_TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
|
||||
cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA uint16 = 0x0036
|
||||
cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0037
|
||||
cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA uint16 = 0x0038
|
||||
cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0039
|
||||
cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA uint16 = 0x003A
|
||||
cipher_TLS_RSA_WITH_NULL_SHA256 uint16 = 0x003B
|
||||
cipher_TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003C
|
||||
cipher_TLS_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x003D
|
||||
cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA256 uint16 = 0x003E
|
||||
cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003F
|
||||
cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 uint16 = 0x0040
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0041
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0042
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0043
|
||||
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0044
|
||||
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0045
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA uint16 = 0x0046
|
||||
// Reserved uint16 = 0x0047-4F
|
||||
// Reserved uint16 = 0x0050-58
|
||||
// Reserved uint16 = 0x0059-5C
|
||||
// Unassigned uint16 = 0x005D-5F
|
||||
// Reserved uint16 = 0x0060-66
|
||||
cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x0067
|
||||
cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA256 uint16 = 0x0068
|
||||
cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x0069
|
||||
cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 uint16 = 0x006A
|
||||
cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x006B
|
||||
cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA256 uint16 = 0x006C
|
||||
cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA256 uint16 = 0x006D
|
||||
// Unassigned uint16 = 0x006E-83
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0084
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0085
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0086
|
||||
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0087
|
||||
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0088
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA uint16 = 0x0089
|
||||
cipher_TLS_PSK_WITH_RC4_128_SHA uint16 = 0x008A
|
||||
cipher_TLS_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x008B
|
||||
cipher_TLS_PSK_WITH_AES_128_CBC_SHA uint16 = 0x008C
|
||||
cipher_TLS_PSK_WITH_AES_256_CBC_SHA uint16 = 0x008D
|
||||
cipher_TLS_DHE_PSK_WITH_RC4_128_SHA uint16 = 0x008E
|
||||
cipher_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x008F
|
||||
cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0x0090
|
||||
cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0x0091
|
||||
cipher_TLS_RSA_PSK_WITH_RC4_128_SHA uint16 = 0x0092
|
||||
cipher_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0x0093
|
||||
cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA uint16 = 0x0094
|
||||
cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA uint16 = 0x0095
|
||||
cipher_TLS_RSA_WITH_SEED_CBC_SHA uint16 = 0x0096
|
||||
cipher_TLS_DH_DSS_WITH_SEED_CBC_SHA uint16 = 0x0097
|
||||
cipher_TLS_DH_RSA_WITH_SEED_CBC_SHA uint16 = 0x0098
|
||||
cipher_TLS_DHE_DSS_WITH_SEED_CBC_SHA uint16 = 0x0099
|
||||
cipher_TLS_DHE_RSA_WITH_SEED_CBC_SHA uint16 = 0x009A
|
||||
cipher_TLS_DH_anon_WITH_SEED_CBC_SHA uint16 = 0x009B
|
||||
cipher_TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009C
|
||||
cipher_TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009D
|
||||
cipher_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009E
|
||||
cipher_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009F
|
||||
cipher_TLS_DH_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x00A0
|
||||
cipher_TLS_DH_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x00A1
|
||||
cipher_TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 uint16 = 0x00A2
|
||||
cipher_TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 uint16 = 0x00A3
|
||||
cipher_TLS_DH_DSS_WITH_AES_128_GCM_SHA256 uint16 = 0x00A4
|
||||
cipher_TLS_DH_DSS_WITH_AES_256_GCM_SHA384 uint16 = 0x00A5
|
||||
cipher_TLS_DH_anon_WITH_AES_128_GCM_SHA256 uint16 = 0x00A6
|
||||
cipher_TLS_DH_anon_WITH_AES_256_GCM_SHA384 uint16 = 0x00A7
|
||||
cipher_TLS_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00A8
|
||||
cipher_TLS_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00A9
|
||||
cipher_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00AA
|
||||
cipher_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00AB
|
||||
cipher_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0x00AC
|
||||
cipher_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 uint16 = 0x00AD
|
||||
cipher_TLS_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00AE
|
||||
cipher_TLS_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00AF
|
||||
cipher_TLS_PSK_WITH_NULL_SHA256 uint16 = 0x00B0
|
||||
cipher_TLS_PSK_WITH_NULL_SHA384 uint16 = 0x00B1
|
||||
cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00B2
|
||||
cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00B3
|
||||
cipher_TLS_DHE_PSK_WITH_NULL_SHA256 uint16 = 0x00B4
|
||||
cipher_TLS_DHE_PSK_WITH_NULL_SHA384 uint16 = 0x00B5
|
||||
cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0x00B6
|
||||
cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0x00B7
|
||||
cipher_TLS_RSA_PSK_WITH_NULL_SHA256 uint16 = 0x00B8
|
||||
cipher_TLS_RSA_PSK_WITH_NULL_SHA384 uint16 = 0x00B9
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BA
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BB
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BC
|
||||
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BD
|
||||
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BE
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0x00BF
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C0
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C1
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C2
|
||||
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C3
|
||||
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C4
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 uint16 = 0x00C5
|
||||
// Unassigned uint16 = 0x00C6-FE
|
||||
cipher_TLS_EMPTY_RENEGOTIATION_INFO_SCSV uint16 = 0x00FF
|
||||
// Unassigned uint16 = 0x01-55,*
|
||||
cipher_TLS_FALLBACK_SCSV uint16 = 0x5600
|
||||
// Unassigned uint16 = 0x5601 - 0xC000
|
||||
cipher_TLS_ECDH_ECDSA_WITH_NULL_SHA uint16 = 0xC001
|
||||
cipher_TLS_ECDH_ECDSA_WITH_RC4_128_SHA uint16 = 0xC002
|
||||
cipher_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC003
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xC004
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xC005
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_NULL_SHA uint16 = 0xC006
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xC007
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC008
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xC009
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xC00A
|
||||
cipher_TLS_ECDH_RSA_WITH_NULL_SHA uint16 = 0xC00B
|
||||
cipher_TLS_ECDH_RSA_WITH_RC4_128_SHA uint16 = 0xC00C
|
||||
cipher_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC00D
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC00E
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC00F
|
||||
cipher_TLS_ECDHE_RSA_WITH_NULL_SHA uint16 = 0xC010
|
||||
cipher_TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xC011
|
||||
cipher_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC012
|
||||
cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC013
|
||||
cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC014
|
||||
cipher_TLS_ECDH_anon_WITH_NULL_SHA uint16 = 0xC015
|
||||
cipher_TLS_ECDH_anon_WITH_RC4_128_SHA uint16 = 0xC016
|
||||
cipher_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA uint16 = 0xC017
|
||||
cipher_TLS_ECDH_anon_WITH_AES_128_CBC_SHA uint16 = 0xC018
|
||||
cipher_TLS_ECDH_anon_WITH_AES_256_CBC_SHA uint16 = 0xC019
|
||||
cipher_TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01A
|
||||
cipher_TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01B
|
||||
cipher_TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA uint16 = 0xC01C
|
||||
cipher_TLS_SRP_SHA_WITH_AES_128_CBC_SHA uint16 = 0xC01D
|
||||
cipher_TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA uint16 = 0xC01E
|
||||
cipher_TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA uint16 = 0xC01F
|
||||
cipher_TLS_SRP_SHA_WITH_AES_256_CBC_SHA uint16 = 0xC020
|
||||
cipher_TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA uint16 = 0xC021
|
||||
cipher_TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA uint16 = 0xC022
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC023
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC024
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC025
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC026
|
||||
cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC027
|
||||
cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC028
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xC029
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 uint16 = 0xC02A
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02B
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC02C
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02D
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC02E
|
||||
cipher_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC02F
|
||||
cipher_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC030
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xC031
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xC032
|
||||
cipher_TLS_ECDHE_PSK_WITH_RC4_128_SHA uint16 = 0xC033
|
||||
cipher_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA uint16 = 0xC034
|
||||
cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0xC035
|
||||
cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0xC036
|
||||
cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0xC037
|
||||
cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0xC038
|
||||
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA uint16 = 0xC039
|
||||
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA256 uint16 = 0xC03A
|
||||
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA384 uint16 = 0xC03B
|
||||
cipher_TLS_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC03C
|
||||
cipher_TLS_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC03D
|
||||
cipher_TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC03E
|
||||
cipher_TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC03F
|
||||
cipher_TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC040
|
||||
cipher_TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC041
|
||||
cipher_TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC042
|
||||
cipher_TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC043
|
||||
cipher_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC044
|
||||
cipher_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC045
|
||||
cipher_TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC046
|
||||
cipher_TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC047
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC048
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC049
|
||||
cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04A
|
||||
cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04B
|
||||
cipher_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04C
|
||||
cipher_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04D
|
||||
cipher_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC04E
|
||||
cipher_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC04F
|
||||
cipher_TLS_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC050
|
||||
cipher_TLS_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC051
|
||||
cipher_TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC052
|
||||
cipher_TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC053
|
||||
cipher_TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC054
|
||||
cipher_TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC055
|
||||
cipher_TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC056
|
||||
cipher_TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC057
|
||||
cipher_TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC058
|
||||
cipher_TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC059
|
||||
cipher_TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05A
|
||||
cipher_TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05B
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05C
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05D
|
||||
cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC05E
|
||||
cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC05F
|
||||
cipher_TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC060
|
||||
cipher_TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC061
|
||||
cipher_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC062
|
||||
cipher_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC063
|
||||
cipher_TLS_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC064
|
||||
cipher_TLS_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC065
|
||||
cipher_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC066
|
||||
cipher_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC067
|
||||
cipher_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC068
|
||||
cipher_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC069
|
||||
cipher_TLS_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06A
|
||||
cipher_TLS_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06B
|
||||
cipher_TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06C
|
||||
cipher_TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06D
|
||||
cipher_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 uint16 = 0xC06E
|
||||
cipher_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 uint16 = 0xC06F
|
||||
cipher_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 uint16 = 0xC070
|
||||
cipher_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 uint16 = 0xC071
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC072
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC073
|
||||
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC074
|
||||
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC075
|
||||
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC076
|
||||
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC077
|
||||
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC078
|
||||
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC079
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07A
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07B
|
||||
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07C
|
||||
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07D
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC07E
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC07F
|
||||
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC080
|
||||
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC081
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC082
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC083
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC084
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC085
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC086
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC087
|
||||
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC088
|
||||
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC089
|
||||
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08A
|
||||
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08B
|
||||
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08C
|
||||
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08D
|
||||
cipher_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC08E
|
||||
cipher_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC08F
|
||||
cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC090
|
||||
cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC091
|
||||
cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 uint16 = 0xC092
|
||||
cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 uint16 = 0xC093
|
||||
cipher_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC094
|
||||
cipher_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC095
|
||||
cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC096
|
||||
cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC097
|
||||
cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC098
|
||||
cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC099
|
||||
cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 uint16 = 0xC09A
|
||||
cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 uint16 = 0xC09B
|
||||
cipher_TLS_RSA_WITH_AES_128_CCM uint16 = 0xC09C
|
||||
cipher_TLS_RSA_WITH_AES_256_CCM uint16 = 0xC09D
|
||||
cipher_TLS_DHE_RSA_WITH_AES_128_CCM uint16 = 0xC09E
|
||||
cipher_TLS_DHE_RSA_WITH_AES_256_CCM uint16 = 0xC09F
|
||||
cipher_TLS_RSA_WITH_AES_128_CCM_8 uint16 = 0xC0A0
|
||||
cipher_TLS_RSA_WITH_AES_256_CCM_8 uint16 = 0xC0A1
|
||||
cipher_TLS_DHE_RSA_WITH_AES_128_CCM_8 uint16 = 0xC0A2
|
||||
cipher_TLS_DHE_RSA_WITH_AES_256_CCM_8 uint16 = 0xC0A3
|
||||
cipher_TLS_PSK_WITH_AES_128_CCM uint16 = 0xC0A4
|
||||
cipher_TLS_PSK_WITH_AES_256_CCM uint16 = 0xC0A5
|
||||
cipher_TLS_DHE_PSK_WITH_AES_128_CCM uint16 = 0xC0A6
|
||||
cipher_TLS_DHE_PSK_WITH_AES_256_CCM uint16 = 0xC0A7
|
||||
cipher_TLS_PSK_WITH_AES_128_CCM_8 uint16 = 0xC0A8
|
||||
cipher_TLS_PSK_WITH_AES_256_CCM_8 uint16 = 0xC0A9
|
||||
cipher_TLS_PSK_DHE_WITH_AES_128_CCM_8 uint16 = 0xC0AA
|
||||
cipher_TLS_PSK_DHE_WITH_AES_256_CCM_8 uint16 = 0xC0AB
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CCM uint16 = 0xC0AC
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CCM uint16 = 0xC0AD
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 uint16 = 0xC0AE
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 uint16 = 0xC0AF
|
||||
// Unassigned uint16 = 0xC0B0-FF
|
||||
// Unassigned uint16 = 0xC1-CB,*
|
||||
// Unassigned uint16 = 0xCC00-A7
|
||||
cipher_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCA8
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCA9
|
||||
cipher_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAA
|
||||
cipher_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAB
|
||||
cipher_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAC
|
||||
cipher_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAD
|
||||
cipher_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xCCAE
|
||||
)
|
||||
|
||||
// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
|
||||
// References:
|
||||
// https://tools.ietf.org/html/rfc7540#appendix-A
|
||||
// Reject cipher suites from Appendix A.
|
||||
// "This list includes those cipher suites that do not
|
||||
// offer an ephemeral key exchange and those that are
|
||||
// based on the TLS null, stream or block cipher type"
|
||||
func isBadCipher(cipher uint16) bool {
|
||||
switch cipher {
|
||||
case cipher_TLS_NULL_WITH_NULL_NULL,
|
||||
cipher_TLS_RSA_WITH_NULL_MD5,
|
||||
cipher_TLS_RSA_WITH_NULL_SHA,
|
||||
cipher_TLS_RSA_EXPORT_WITH_RC4_40_MD5,
|
||||
cipher_TLS_RSA_WITH_RC4_128_MD5,
|
||||
cipher_TLS_RSA_WITH_RC4_128_SHA,
|
||||
cipher_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
|
||||
cipher_TLS_RSA_WITH_IDEA_CBC_SHA,
|
||||
cipher_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA,
|
||||
cipher_TLS_RSA_WITH_DES_CBC_SHA,
|
||||
cipher_TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_WITH_DES_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_WITH_DES_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_WITH_DES_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_DES_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5,
|
||||
cipher_TLS_DH_anon_WITH_RC4_128_MD5,
|
||||
cipher_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA,
|
||||
cipher_TLS_DH_anon_WITH_DES_CBC_SHA,
|
||||
cipher_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_KRB5_WITH_DES_CBC_SHA,
|
||||
cipher_TLS_KRB5_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_KRB5_WITH_RC4_128_SHA,
|
||||
cipher_TLS_KRB5_WITH_IDEA_CBC_SHA,
|
||||
cipher_TLS_KRB5_WITH_DES_CBC_MD5,
|
||||
cipher_TLS_KRB5_WITH_3DES_EDE_CBC_MD5,
|
||||
cipher_TLS_KRB5_WITH_RC4_128_MD5,
|
||||
cipher_TLS_KRB5_WITH_IDEA_CBC_MD5,
|
||||
cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA,
|
||||
cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA,
|
||||
cipher_TLS_KRB5_EXPORT_WITH_RC4_40_SHA,
|
||||
cipher_TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5,
|
||||
cipher_TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5,
|
||||
cipher_TLS_KRB5_EXPORT_WITH_RC4_40_MD5,
|
||||
cipher_TLS_PSK_WITH_NULL_SHA,
|
||||
cipher_TLS_DHE_PSK_WITH_NULL_SHA,
|
||||
cipher_TLS_RSA_PSK_WITH_NULL_SHA,
|
||||
cipher_TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_RSA_WITH_NULL_SHA256,
|
||||
cipher_TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_RSA_WITH_AES_256_CBC_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_AES_256_CBC_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_AES_256_CBC_SHA256,
|
||||
cipher_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
|
||||
cipher_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_AES_256_CBC_SHA256,
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA,
|
||||
cipher_TLS_PSK_WITH_RC4_128_SHA,
|
||||
cipher_TLS_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_PSK_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_PSK_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_DHE_PSK_WITH_RC4_128_SHA,
|
||||
cipher_TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_RSA_PSK_WITH_RC4_128_SHA,
|
||||
cipher_TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_RSA_WITH_SEED_CBC_SHA,
|
||||
cipher_TLS_DH_DSS_WITH_SEED_CBC_SHA,
|
||||
cipher_TLS_DH_RSA_WITH_SEED_CBC_SHA,
|
||||
cipher_TLS_DHE_DSS_WITH_SEED_CBC_SHA,
|
||||
cipher_TLS_DHE_RSA_WITH_SEED_CBC_SHA,
|
||||
cipher_TLS_DH_anon_WITH_SEED_CBC_SHA,
|
||||
cipher_TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_DH_RSA_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_DH_DSS_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_DH_anon_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_PSK_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_PSK_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_PSK_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_PSK_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_PSK_WITH_NULL_SHA256,
|
||||
cipher_TLS_PSK_WITH_NULL_SHA384,
|
||||
cipher_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_DHE_PSK_WITH_NULL_SHA256,
|
||||
cipher_TLS_DHE_PSK_WITH_NULL_SHA384,
|
||||
cipher_TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_RSA_PSK_WITH_NULL_SHA256,
|
||||
cipher_TLS_RSA_PSK_WITH_NULL_SHA384,
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256,
|
||||
cipher_TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256,
|
||||
cipher_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256,
|
||||
cipher_TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_NULL_SHA,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_NULL_SHA,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_ECDH_RSA_WITH_NULL_SHA,
|
||||
cipher_TLS_ECDH_RSA_WITH_RC4_128_SHA,
|
||||
cipher_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_ECDHE_RSA_WITH_NULL_SHA,
|
||||
cipher_TLS_ECDHE_RSA_WITH_RC4_128_SHA,
|
||||
cipher_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_ECDH_anon_WITH_NULL_SHA,
|
||||
cipher_TLS_ECDH_anon_WITH_RC4_128_SHA,
|
||||
cipher_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
|
||||
cipher_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
|
||||
cipher_TLS_ECDHE_PSK_WITH_RC4_128_SHA,
|
||||
cipher_TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA,
|
||||
cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
|
||||
cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
|
||||
cipher_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
|
||||
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA,
|
||||
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA256,
|
||||
cipher_TLS_ECDHE_PSK_WITH_NULL_SHA384,
|
||||
cipher_TLS_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_DH_anon_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_RSA_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_RSA_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_DH_anon_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_PSK_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_PSK_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256,
|
||||
cipher_TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384,
|
||||
cipher_TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256,
|
||||
cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384,
|
||||
cipher_TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256,
|
||||
cipher_TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384,
|
||||
cipher_TLS_RSA_WITH_AES_128_CCM,
|
||||
cipher_TLS_RSA_WITH_AES_256_CCM,
|
||||
cipher_TLS_RSA_WITH_AES_128_CCM_8,
|
||||
cipher_TLS_RSA_WITH_AES_256_CCM_8,
|
||||
cipher_TLS_PSK_WITH_AES_128_CCM,
|
||||
cipher_TLS_PSK_WITH_AES_256_CCM,
|
||||
cipher_TLS_PSK_WITH_AES_128_CCM_8,
|
||||
cipher_TLS_PSK_WITH_AES_256_CCM_8:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
256
vendor/golang.org/x/net/http2/client_conn_pool.go
generated
vendored
256
vendor/golang.org/x/net/http2/client_conn_pool.go
generated
vendored
|
|
@ -1,256 +0,0 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Transport code's client connection pooling.
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ClientConnPool manages a pool of HTTP/2 client connections.
|
||||
type ClientConnPool interface {
|
||||
GetClientConn(req *http.Request, addr string) (*ClientConn, error)
|
||||
MarkDead(*ClientConn)
|
||||
}
|
||||
|
||||
// clientConnPoolIdleCloser is the interface implemented by ClientConnPool
|
||||
// implementations which can close their idle connections.
|
||||
type clientConnPoolIdleCloser interface {
|
||||
ClientConnPool
|
||||
closeIdleConnections()
|
||||
}
|
||||
|
||||
var (
|
||||
_ clientConnPoolIdleCloser = (*clientConnPool)(nil)
|
||||
_ clientConnPoolIdleCloser = noDialClientConnPool{}
|
||||
)
|
||||
|
||||
// TODO: use singleflight for dialing and addConnCalls?
|
||||
type clientConnPool struct {
|
||||
t *Transport
|
||||
|
||||
mu sync.Mutex // TODO: maybe switch to RWMutex
|
||||
// TODO: add support for sharing conns based on cert names
|
||||
// (e.g. share conn for googleapis.com and appspot.com)
|
||||
conns map[string][]*ClientConn // key is host:port
|
||||
dialing map[string]*dialCall // currently in-flight dials
|
||||
keys map[*ClientConn][]string
|
||||
addConnCalls map[string]*addConnCall // in-flight addConnIfNeede calls
|
||||
}
|
||||
|
||||
func (p *clientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) {
|
||||
return p.getClientConn(req, addr, dialOnMiss)
|
||||
}
|
||||
|
||||
const (
|
||||
dialOnMiss = true
|
||||
noDialOnMiss = false
|
||||
)
|
||||
|
||||
func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
|
||||
if isConnectionCloseRequest(req) && dialOnMiss {
|
||||
// It gets its own connection.
|
||||
const singleUse = true
|
||||
cc, err := p.t.dialClientConn(addr, singleUse)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cc, nil
|
||||
}
|
||||
p.mu.Lock()
|
||||
for _, cc := range p.conns[addr] {
|
||||
if cc.CanTakeNewRequest() {
|
||||
p.mu.Unlock()
|
||||
return cc, nil
|
||||
}
|
||||
}
|
||||
if !dialOnMiss {
|
||||
p.mu.Unlock()
|
||||
return nil, ErrNoCachedConn
|
||||
}
|
||||
call := p.getStartDialLocked(addr)
|
||||
p.mu.Unlock()
|
||||
<-call.done
|
||||
return call.res, call.err
|
||||
}
|
||||
|
||||
// dialCall is an in-flight Transport dial call to a host.
|
||||
type dialCall struct {
|
||||
p *clientConnPool
|
||||
done chan struct{} // closed when done
|
||||
res *ClientConn // valid after done is closed
|
||||
err error // valid after done is closed
|
||||
}
|
||||
|
||||
// requires p.mu is held.
|
||||
func (p *clientConnPool) getStartDialLocked(addr string) *dialCall {
|
||||
if call, ok := p.dialing[addr]; ok {
|
||||
// A dial is already in-flight. Don't start another.
|
||||
return call
|
||||
}
|
||||
call := &dialCall{p: p, done: make(chan struct{})}
|
||||
if p.dialing == nil {
|
||||
p.dialing = make(map[string]*dialCall)
|
||||
}
|
||||
p.dialing[addr] = call
|
||||
go call.dial(addr)
|
||||
return call
|
||||
}
|
||||
|
||||
// run in its own goroutine.
|
||||
func (c *dialCall) dial(addr string) {
|
||||
const singleUse = false // shared conn
|
||||
c.res, c.err = c.p.t.dialClientConn(addr, singleUse)
|
||||
close(c.done)
|
||||
|
||||
c.p.mu.Lock()
|
||||
delete(c.p.dialing, addr)
|
||||
if c.err == nil {
|
||||
c.p.addConnLocked(addr, c.res)
|
||||
}
|
||||
c.p.mu.Unlock()
|
||||
}
|
||||
|
||||
// addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't
|
||||
// already exist. It coalesces concurrent calls with the same key.
|
||||
// This is used by the http1 Transport code when it creates a new connection. Because
|
||||
// the http1 Transport doesn't de-dup TCP dials to outbound hosts (because it doesn't know
|
||||
// the protocol), it can get into a situation where it has multiple TLS connections.
|
||||
// This code decides which ones live or die.
|
||||
// The return value used is whether c was used.
|
||||
// c is never closed.
|
||||
func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c *tls.Conn) (used bool, err error) {
|
||||
p.mu.Lock()
|
||||
for _, cc := range p.conns[key] {
|
||||
if cc.CanTakeNewRequest() {
|
||||
p.mu.Unlock()
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
call, dup := p.addConnCalls[key]
|
||||
if !dup {
|
||||
if p.addConnCalls == nil {
|
||||
p.addConnCalls = make(map[string]*addConnCall)
|
||||
}
|
||||
call = &addConnCall{
|
||||
p: p,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
p.addConnCalls[key] = call
|
||||
go call.run(t, key, c)
|
||||
}
|
||||
p.mu.Unlock()
|
||||
|
||||
<-call.done
|
||||
if call.err != nil {
|
||||
return false, call.err
|
||||
}
|
||||
return !dup, nil
|
||||
}
|
||||
|
||||
type addConnCall struct {
|
||||
p *clientConnPool
|
||||
done chan struct{} // closed when done
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *addConnCall) run(t *Transport, key string, tc *tls.Conn) {
|
||||
cc, err := t.NewClientConn(tc)
|
||||
|
||||
p := c.p
|
||||
p.mu.Lock()
|
||||
if err != nil {
|
||||
c.err = err
|
||||
} else {
|
||||
p.addConnLocked(key, cc)
|
||||
}
|
||||
delete(p.addConnCalls, key)
|
||||
p.mu.Unlock()
|
||||
close(c.done)
|
||||
}
|
||||
|
||||
func (p *clientConnPool) addConn(key string, cc *ClientConn) {
|
||||
p.mu.Lock()
|
||||
p.addConnLocked(key, cc)
|
||||
p.mu.Unlock()
|
||||
}
|
||||
|
||||
// p.mu must be held
|
||||
func (p *clientConnPool) addConnLocked(key string, cc *ClientConn) {
|
||||
for _, v := range p.conns[key] {
|
||||
if v == cc {
|
||||
return
|
||||
}
|
||||
}
|
||||
if p.conns == nil {
|
||||
p.conns = make(map[string][]*ClientConn)
|
||||
}
|
||||
if p.keys == nil {
|
||||
p.keys = make(map[*ClientConn][]string)
|
||||
}
|
||||
p.conns[key] = append(p.conns[key], cc)
|
||||
p.keys[cc] = append(p.keys[cc], key)
|
||||
}
|
||||
|
||||
func (p *clientConnPool) MarkDead(cc *ClientConn) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
for _, key := range p.keys[cc] {
|
||||
vv, ok := p.conns[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
newList := filterOutClientConn(vv, cc)
|
||||
if len(newList) > 0 {
|
||||
p.conns[key] = newList
|
||||
} else {
|
||||
delete(p.conns, key)
|
||||
}
|
||||
}
|
||||
delete(p.keys, cc)
|
||||
}
|
||||
|
||||
func (p *clientConnPool) closeIdleConnections() {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
// TODO: don't close a cc if it was just added to the pool
|
||||
// milliseconds ago and has never been used. There's currently
|
||||
// a small race window with the HTTP/1 Transport's integration
|
||||
// where it can add an idle conn just before using it, and
|
||||
// somebody else can concurrently call CloseIdleConns and
|
||||
// break some caller's RoundTrip.
|
||||
for _, vv := range p.conns {
|
||||
for _, cc := range vv {
|
||||
cc.closeIfIdle()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func filterOutClientConn(in []*ClientConn, exclude *ClientConn) []*ClientConn {
|
||||
out := in[:0]
|
||||
for _, v := range in {
|
||||
if v != exclude {
|
||||
out = append(out, v)
|
||||
}
|
||||
}
|
||||
// If we filtered it out, zero out the last item to prevent
|
||||
// the GC from seeing it.
|
||||
if len(in) != len(out) {
|
||||
in[len(in)-1] = nil
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// noDialClientConnPool is an implementation of http2.ClientConnPool
|
||||
// which never dials. We let the HTTP/1.1 client dial and use its TLS
|
||||
// connection instead.
|
||||
type noDialClientConnPool struct{ *clientConnPool }
|
||||
|
||||
func (p noDialClientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) {
|
||||
return p.getClientConn(req, addr, noDialOnMiss)
|
||||
}
|
||||
80
vendor/golang.org/x/net/http2/configure_transport.go
generated
vendored
80
vendor/golang.org/x/net/http2/configure_transport.go
generated
vendored
|
|
@ -1,80 +0,0 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.6
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func configureTransport(t1 *http.Transport) (*Transport, error) {
|
||||
connPool := new(clientConnPool)
|
||||
t2 := &Transport{
|
||||
ConnPool: noDialClientConnPool{connPool},
|
||||
t1: t1,
|
||||
}
|
||||
connPool.t = t2
|
||||
if err := registerHTTPSProtocol(t1, noDialH2RoundTripper{t2}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t1.TLSClientConfig == nil {
|
||||
t1.TLSClientConfig = new(tls.Config)
|
||||
}
|
||||
if !strSliceContains(t1.TLSClientConfig.NextProtos, "h2") {
|
||||
t1.TLSClientConfig.NextProtos = append([]string{"h2"}, t1.TLSClientConfig.NextProtos...)
|
||||
}
|
||||
if !strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") {
|
||||
t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1")
|
||||
}
|
||||
upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper {
|
||||
addr := authorityAddr("https", authority)
|
||||
if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil {
|
||||
go c.Close()
|
||||
return erringRoundTripper{err}
|
||||
} else if !used {
|
||||
// Turns out we don't need this c.
|
||||
// For example, two goroutines made requests to the same host
|
||||
// at the same time, both kicking off TCP dials. (since protocol
|
||||
// was unknown)
|
||||
go c.Close()
|
||||
}
|
||||
return t2
|
||||
}
|
||||
if m := t1.TLSNextProto; len(m) == 0 {
|
||||
t1.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{
|
||||
"h2": upgradeFn,
|
||||
}
|
||||
} else {
|
||||
m["h2"] = upgradeFn
|
||||
}
|
||||
return t2, nil
|
||||
}
|
||||
|
||||
// registerHTTPSProtocol calls Transport.RegisterProtocol but
|
||||
// converting panics into errors.
|
||||
func registerHTTPSProtocol(t *http.Transport, rt http.RoundTripper) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("%v", e)
|
||||
}
|
||||
}()
|
||||
t.RegisterProtocol("https", rt)
|
||||
return nil
|
||||
}
|
||||
|
||||
// noDialH2RoundTripper is a RoundTripper which only tries to complete the request
|
||||
// if there's already has a cached connection to the host.
|
||||
type noDialH2RoundTripper struct{ t *Transport }
|
||||
|
||||
func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
res, err := rt.t.RoundTrip(req)
|
||||
if err == ErrNoCachedConn {
|
||||
return nil, http.ErrSkipAltProtocol
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
146
vendor/golang.org/x/net/http2/databuffer.go
generated
vendored
146
vendor/golang.org/x/net/http2/databuffer.go
generated
vendored
|
|
@ -1,146 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Buffer chunks are allocated from a pool to reduce pressure on GC.
|
||||
// The maximum wasted space per dataBuffer is 2x the largest size class,
|
||||
// which happens when the dataBuffer has multiple chunks and there is
|
||||
// one unread byte in both the first and last chunks. We use a few size
|
||||
// classes to minimize overheads for servers that typically receive very
|
||||
// small request bodies.
|
||||
//
|
||||
// TODO: Benchmark to determine if the pools are necessary. The GC may have
|
||||
// improved enough that we can instead allocate chunks like this:
|
||||
// make([]byte, max(16<<10, expectedBytesRemaining))
|
||||
var (
|
||||
dataChunkSizeClasses = []int{
|
||||
1 << 10,
|
||||
2 << 10,
|
||||
4 << 10,
|
||||
8 << 10,
|
||||
16 << 10,
|
||||
}
|
||||
dataChunkPools = [...]sync.Pool{
|
||||
{New: func() interface{} { return make([]byte, 1<<10) }},
|
||||
{New: func() interface{} { return make([]byte, 2<<10) }},
|
||||
{New: func() interface{} { return make([]byte, 4<<10) }},
|
||||
{New: func() interface{} { return make([]byte, 8<<10) }},
|
||||
{New: func() interface{} { return make([]byte, 16<<10) }},
|
||||
}
|
||||
)
|
||||
|
||||
func getDataBufferChunk(size int64) []byte {
|
||||
i := 0
|
||||
for ; i < len(dataChunkSizeClasses)-1; i++ {
|
||||
if size <= int64(dataChunkSizeClasses[i]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return dataChunkPools[i].Get().([]byte)
|
||||
}
|
||||
|
||||
func putDataBufferChunk(p []byte) {
|
||||
for i, n := range dataChunkSizeClasses {
|
||||
if len(p) == n {
|
||||
dataChunkPools[i].Put(p)
|
||||
return
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("unexpected buffer len=%v", len(p)))
|
||||
}
|
||||
|
||||
// dataBuffer is an io.ReadWriter backed by a list of data chunks.
|
||||
// Each dataBuffer is used to read DATA frames on a single stream.
|
||||
// The buffer is divided into chunks so the server can limit the
|
||||
// total memory used by a single connection without limiting the
|
||||
// request body size on any single stream.
|
||||
type dataBuffer struct {
|
||||
chunks [][]byte
|
||||
r int // next byte to read is chunks[0][r]
|
||||
w int // next byte to write is chunks[len(chunks)-1][w]
|
||||
size int // total buffered bytes
|
||||
expected int64 // we expect at least this many bytes in future Write calls (ignored if <= 0)
|
||||
}
|
||||
|
||||
var errReadEmpty = errors.New("read from empty dataBuffer")
|
||||
|
||||
// Read copies bytes from the buffer into p.
|
||||
// It is an error to read when no data is available.
|
||||
func (b *dataBuffer) Read(p []byte) (int, error) {
|
||||
if b.size == 0 {
|
||||
return 0, errReadEmpty
|
||||
}
|
||||
var ntotal int
|
||||
for len(p) > 0 && b.size > 0 {
|
||||
readFrom := b.bytesFromFirstChunk()
|
||||
n := copy(p, readFrom)
|
||||
p = p[n:]
|
||||
ntotal += n
|
||||
b.r += n
|
||||
b.size -= n
|
||||
// If the first chunk has been consumed, advance to the next chunk.
|
||||
if b.r == len(b.chunks[0]) {
|
||||
putDataBufferChunk(b.chunks[0])
|
||||
end := len(b.chunks) - 1
|
||||
copy(b.chunks[:end], b.chunks[1:])
|
||||
b.chunks[end] = nil
|
||||
b.chunks = b.chunks[:end]
|
||||
b.r = 0
|
||||
}
|
||||
}
|
||||
return ntotal, nil
|
||||
}
|
||||
|
||||
func (b *dataBuffer) bytesFromFirstChunk() []byte {
|
||||
if len(b.chunks) == 1 {
|
||||
return b.chunks[0][b.r:b.w]
|
||||
}
|
||||
return b.chunks[0][b.r:]
|
||||
}
|
||||
|
||||
// Len returns the number of bytes of the unread portion of the buffer.
|
||||
func (b *dataBuffer) Len() int {
|
||||
return b.size
|
||||
}
|
||||
|
||||
// Write appends p to the buffer.
|
||||
func (b *dataBuffer) Write(p []byte) (int, error) {
|
||||
ntotal := len(p)
|
||||
for len(p) > 0 {
|
||||
// If the last chunk is empty, allocate a new chunk. Try to allocate
|
||||
// enough to fully copy p plus any additional bytes we expect to
|
||||
// receive. However, this may allocate less than len(p).
|
||||
want := int64(len(p))
|
||||
if b.expected > want {
|
||||
want = b.expected
|
||||
}
|
||||
chunk := b.lastChunkOrAlloc(want)
|
||||
n := copy(chunk[b.w:], p)
|
||||
p = p[n:]
|
||||
b.w += n
|
||||
b.size += n
|
||||
b.expected -= int64(n)
|
||||
}
|
||||
return ntotal, nil
|
||||
}
|
||||
|
||||
func (b *dataBuffer) lastChunkOrAlloc(want int64) []byte {
|
||||
if len(b.chunks) != 0 {
|
||||
last := b.chunks[len(b.chunks)-1]
|
||||
if b.w < len(last) {
|
||||
return last
|
||||
}
|
||||
}
|
||||
chunk := getDataBufferChunk(want)
|
||||
b.chunks = append(b.chunks, chunk)
|
||||
b.w = 0
|
||||
return chunk
|
||||
}
|
||||
133
vendor/golang.org/x/net/http2/errors.go
generated
vendored
133
vendor/golang.org/x/net/http2/errors.go
generated
vendored
|
|
@ -1,133 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// An ErrCode is an unsigned 32-bit error code as defined in the HTTP/2 spec.
|
||||
type ErrCode uint32
|
||||
|
||||
const (
|
||||
ErrCodeNo ErrCode = 0x0
|
||||
ErrCodeProtocol ErrCode = 0x1
|
||||
ErrCodeInternal ErrCode = 0x2
|
||||
ErrCodeFlowControl ErrCode = 0x3
|
||||
ErrCodeSettingsTimeout ErrCode = 0x4
|
||||
ErrCodeStreamClosed ErrCode = 0x5
|
||||
ErrCodeFrameSize ErrCode = 0x6
|
||||
ErrCodeRefusedStream ErrCode = 0x7
|
||||
ErrCodeCancel ErrCode = 0x8
|
||||
ErrCodeCompression ErrCode = 0x9
|
||||
ErrCodeConnect ErrCode = 0xa
|
||||
ErrCodeEnhanceYourCalm ErrCode = 0xb
|
||||
ErrCodeInadequateSecurity ErrCode = 0xc
|
||||
ErrCodeHTTP11Required ErrCode = 0xd
|
||||
)
|
||||
|
||||
var errCodeName = map[ErrCode]string{
|
||||
ErrCodeNo: "NO_ERROR",
|
||||
ErrCodeProtocol: "PROTOCOL_ERROR",
|
||||
ErrCodeInternal: "INTERNAL_ERROR",
|
||||
ErrCodeFlowControl: "FLOW_CONTROL_ERROR",
|
||||
ErrCodeSettingsTimeout: "SETTINGS_TIMEOUT",
|
||||
ErrCodeStreamClosed: "STREAM_CLOSED",
|
||||
ErrCodeFrameSize: "FRAME_SIZE_ERROR",
|
||||
ErrCodeRefusedStream: "REFUSED_STREAM",
|
||||
ErrCodeCancel: "CANCEL",
|
||||
ErrCodeCompression: "COMPRESSION_ERROR",
|
||||
ErrCodeConnect: "CONNECT_ERROR",
|
||||
ErrCodeEnhanceYourCalm: "ENHANCE_YOUR_CALM",
|
||||
ErrCodeInadequateSecurity: "INADEQUATE_SECURITY",
|
||||
ErrCodeHTTP11Required: "HTTP_1_1_REQUIRED",
|
||||
}
|
||||
|
||||
func (e ErrCode) String() string {
|
||||
if s, ok := errCodeName[e]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("unknown error code 0x%x", uint32(e))
|
||||
}
|
||||
|
||||
// ConnectionError is an error that results in the termination of the
|
||||
// entire connection.
|
||||
type ConnectionError ErrCode
|
||||
|
||||
func (e ConnectionError) Error() string { return fmt.Sprintf("connection error: %s", ErrCode(e)) }
|
||||
|
||||
// StreamError is an error that only affects one stream within an
|
||||
// HTTP/2 connection.
|
||||
type StreamError struct {
|
||||
StreamID uint32
|
||||
Code ErrCode
|
||||
Cause error // optional additional detail
|
||||
}
|
||||
|
||||
func streamError(id uint32, code ErrCode) StreamError {
|
||||
return StreamError{StreamID: id, Code: code}
|
||||
}
|
||||
|
||||
func (e StreamError) Error() string {
|
||||
if e.Cause != nil {
|
||||
return fmt.Sprintf("stream error: stream ID %d; %v; %v", e.StreamID, e.Code, e.Cause)
|
||||
}
|
||||
return fmt.Sprintf("stream error: stream ID %d; %v", e.StreamID, e.Code)
|
||||
}
|
||||
|
||||
// 6.9.1 The Flow Control Window
|
||||
// "If a sender receives a WINDOW_UPDATE that causes a flow control
|
||||
// window to exceed this maximum it MUST terminate either the stream
|
||||
// or the connection, as appropriate. For streams, [...]; for the
|
||||
// connection, a GOAWAY frame with a FLOW_CONTROL_ERROR code."
|
||||
type goAwayFlowError struct{}
|
||||
|
||||
func (goAwayFlowError) Error() string { return "connection exceeded flow control window size" }
|
||||
|
||||
// connError represents an HTTP/2 ConnectionError error code, along
|
||||
// with a string (for debugging) explaining why.
|
||||
//
|
||||
// Errors of this type are only returned by the frame parser functions
|
||||
// and converted into ConnectionError(Code), after stashing away
|
||||
// the Reason into the Framer's errDetail field, accessible via
|
||||
// the (*Framer).ErrorDetail method.
|
||||
type connError struct {
|
||||
Code ErrCode // the ConnectionError error code
|
||||
Reason string // additional reason
|
||||
}
|
||||
|
||||
func (e connError) Error() string {
|
||||
return fmt.Sprintf("http2: connection error: %v: %v", e.Code, e.Reason)
|
||||
}
|
||||
|
||||
type pseudoHeaderError string
|
||||
|
||||
func (e pseudoHeaderError) Error() string {
|
||||
return fmt.Sprintf("invalid pseudo-header %q", string(e))
|
||||
}
|
||||
|
||||
type duplicatePseudoHeaderError string
|
||||
|
||||
func (e duplicatePseudoHeaderError) Error() string {
|
||||
return fmt.Sprintf("duplicate pseudo-header %q", string(e))
|
||||
}
|
||||
|
||||
type headerFieldNameError string
|
||||
|
||||
func (e headerFieldNameError) Error() string {
|
||||
return fmt.Sprintf("invalid header field name %q", string(e))
|
||||
}
|
||||
|
||||
type headerFieldValueError string
|
||||
|
||||
func (e headerFieldValueError) Error() string {
|
||||
return fmt.Sprintf("invalid header field value %q", string(e))
|
||||
}
|
||||
|
||||
var (
|
||||
errMixPseudoHeaderTypes = errors.New("mix of request and response pseudo headers")
|
||||
errPseudoAfterRegular = errors.New("pseudo header field after regular")
|
||||
)
|
||||
50
vendor/golang.org/x/net/http2/flow.go
generated
vendored
50
vendor/golang.org/x/net/http2/flow.go
generated
vendored
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Flow control
|
||||
|
||||
package http2
|
||||
|
||||
// flow is the flow control window's size.
|
||||
type flow struct {
|
||||
// n is the number of DATA bytes we're allowed to send.
|
||||
// A flow is kept both on a conn and a per-stream.
|
||||
n int32
|
||||
|
||||
// conn points to the shared connection-level flow that is
|
||||
// shared by all streams on that conn. It is nil for the flow
|
||||
// that's on the conn directly.
|
||||
conn *flow
|
||||
}
|
||||
|
||||
func (f *flow) setConnFlow(cf *flow) { f.conn = cf }
|
||||
|
||||
func (f *flow) available() int32 {
|
||||
n := f.n
|
||||
if f.conn != nil && f.conn.n < n {
|
||||
n = f.conn.n
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (f *flow) take(n int32) {
|
||||
if n > f.available() {
|
||||
panic("internal error: took too much")
|
||||
}
|
||||
f.n -= n
|
||||
if f.conn != nil {
|
||||
f.conn.n -= n
|
||||
}
|
||||
}
|
||||
|
||||
// add adds n bytes (positive or negative) to the flow control window.
|
||||
// It returns false if the sum would exceed 2^31-1.
|
||||
func (f *flow) add(n int32) bool {
|
||||
remain := (1<<31 - 1) - f.n
|
||||
if n > remain {
|
||||
return false
|
||||
}
|
||||
f.n += n
|
||||
return true
|
||||
}
|
||||
1579
vendor/golang.org/x/net/http2/frame.go
generated
vendored
1579
vendor/golang.org/x/net/http2/frame.go
generated
vendored
File diff suppressed because it is too large
Load diff
16
vendor/golang.org/x/net/http2/go16.go
generated
vendored
16
vendor/golang.org/x/net/http2/go16.go
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.6
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
|
||||
return t1.ExpectContinueTimeout
|
||||
}
|
||||
106
vendor/golang.org/x/net/http2/go17.go
generated
vendored
106
vendor/golang.org/x/net/http2/go17.go
generated
vendored
|
|
@ -1,106 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.7
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"time"
|
||||
)
|
||||
|
||||
type contextContext interface {
|
||||
context.Context
|
||||
}
|
||||
|
||||
func serverConnBaseContext(c net.Conn, opts *ServeConnOpts) (ctx contextContext, cancel func()) {
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
ctx = context.WithValue(ctx, http.LocalAddrContextKey, c.LocalAddr())
|
||||
if hs := opts.baseConfig(); hs != nil {
|
||||
ctx = context.WithValue(ctx, http.ServerContextKey, hs)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func contextWithCancel(ctx contextContext) (_ contextContext, cancel func()) {
|
||||
return context.WithCancel(ctx)
|
||||
}
|
||||
|
||||
func requestWithContext(req *http.Request, ctx contextContext) *http.Request {
|
||||
return req.WithContext(ctx)
|
||||
}
|
||||
|
||||
type clientTrace httptrace.ClientTrace
|
||||
|
||||
func reqContext(r *http.Request) context.Context { return r.Context() }
|
||||
|
||||
func (t *Transport) idleConnTimeout() time.Duration {
|
||||
if t.t1 != nil {
|
||||
return t.t1.IdleConnTimeout
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func setResponseUncompressed(res *http.Response) { res.Uncompressed = true }
|
||||
|
||||
func traceGotConn(req *http.Request, cc *ClientConn) {
|
||||
trace := httptrace.ContextClientTrace(req.Context())
|
||||
if trace == nil || trace.GotConn == nil {
|
||||
return
|
||||
}
|
||||
ci := httptrace.GotConnInfo{Conn: cc.tconn}
|
||||
cc.mu.Lock()
|
||||
ci.Reused = cc.nextStreamID > 1
|
||||
ci.WasIdle = len(cc.streams) == 0 && ci.Reused
|
||||
if ci.WasIdle && !cc.lastActive.IsZero() {
|
||||
ci.IdleTime = time.Now().Sub(cc.lastActive)
|
||||
}
|
||||
cc.mu.Unlock()
|
||||
|
||||
trace.GotConn(ci)
|
||||
}
|
||||
|
||||
func traceWroteHeaders(trace *clientTrace) {
|
||||
if trace != nil && trace.WroteHeaders != nil {
|
||||
trace.WroteHeaders()
|
||||
}
|
||||
}
|
||||
|
||||
func traceGot100Continue(trace *clientTrace) {
|
||||
if trace != nil && trace.Got100Continue != nil {
|
||||
trace.Got100Continue()
|
||||
}
|
||||
}
|
||||
|
||||
func traceWait100Continue(trace *clientTrace) {
|
||||
if trace != nil && trace.Wait100Continue != nil {
|
||||
trace.Wait100Continue()
|
||||
}
|
||||
}
|
||||
|
||||
func traceWroteRequest(trace *clientTrace, err error) {
|
||||
if trace != nil && trace.WroteRequest != nil {
|
||||
trace.WroteRequest(httptrace.WroteRequestInfo{Err: err})
|
||||
}
|
||||
}
|
||||
|
||||
func traceFirstResponseByte(trace *clientTrace) {
|
||||
if trace != nil && trace.GotFirstResponseByte != nil {
|
||||
trace.GotFirstResponseByte()
|
||||
}
|
||||
}
|
||||
|
||||
func requestTrace(req *http.Request) *clientTrace {
|
||||
trace := httptrace.ContextClientTrace(req.Context())
|
||||
return (*clientTrace)(trace)
|
||||
}
|
||||
|
||||
// Ping sends a PING frame to the server and waits for the ack.
|
||||
func (cc *ClientConn) Ping(ctx context.Context) error {
|
||||
return cc.ping(ctx)
|
||||
}
|
||||
36
vendor/golang.org/x/net/http2/go17_not18.go
generated
vendored
36
vendor/golang.org/x/net/http2/go17_not18.go
generated
vendored
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.7,!go1.8
|
||||
|
||||
package http2
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
// temporary copy of Go 1.7's private tls.Config.clone:
|
||||
func cloneTLSConfig(c *tls.Config) *tls.Config {
|
||||
return &tls.Config{
|
||||
Rand: c.Rand,
|
||||
Time: c.Time,
|
||||
Certificates: c.Certificates,
|
||||
NameToCertificate: c.NameToCertificate,
|
||||
GetCertificate: c.GetCertificate,
|
||||
RootCAs: c.RootCAs,
|
||||
NextProtos: c.NextProtos,
|
||||
ServerName: c.ServerName,
|
||||
ClientAuth: c.ClientAuth,
|
||||
ClientCAs: c.ClientCAs,
|
||||
InsecureSkipVerify: c.InsecureSkipVerify,
|
||||
CipherSuites: c.CipherSuites,
|
||||
PreferServerCipherSuites: c.PreferServerCipherSuites,
|
||||
SessionTicketsDisabled: c.SessionTicketsDisabled,
|
||||
SessionTicketKey: c.SessionTicketKey,
|
||||
ClientSessionCache: c.ClientSessionCache,
|
||||
MinVersion: c.MinVersion,
|
||||
MaxVersion: c.MaxVersion,
|
||||
CurvePreferences: c.CurvePreferences,
|
||||
DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
|
||||
Renegotiation: c.Renegotiation,
|
||||
}
|
||||
}
|
||||
56
vendor/golang.org/x/net/http2/go18.go
generated
vendored
56
vendor/golang.org/x/net/http2/go18.go
generated
vendored
|
|
@ -1,56 +0,0 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func cloneTLSConfig(c *tls.Config) *tls.Config {
|
||||
c2 := c.Clone()
|
||||
c2.GetClientCertificate = c.GetClientCertificate // golang.org/issue/19264
|
||||
return c2
|
||||
}
|
||||
|
||||
var _ http.Pusher = (*responseWriter)(nil)
|
||||
|
||||
// Push implements http.Pusher.
|
||||
func (w *responseWriter) Push(target string, opts *http.PushOptions) error {
|
||||
internalOpts := pushOptions{}
|
||||
if opts != nil {
|
||||
internalOpts.Method = opts.Method
|
||||
internalOpts.Header = opts.Header
|
||||
}
|
||||
return w.push(target, internalOpts)
|
||||
}
|
||||
|
||||
func configureServer18(h1 *http.Server, h2 *Server) error {
|
||||
if h2.IdleTimeout == 0 {
|
||||
if h1.IdleTimeout != 0 {
|
||||
h2.IdleTimeout = h1.IdleTimeout
|
||||
} else {
|
||||
h2.IdleTimeout = h1.ReadTimeout
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func shouldLogPanic(panicValue interface{}) bool {
|
||||
return panicValue != nil && panicValue != http.ErrAbortHandler
|
||||
}
|
||||
|
||||
func reqGetBody(req *http.Request) func() (io.ReadCloser, error) {
|
||||
return req.GetBody
|
||||
}
|
||||
|
||||
func reqBodyIsNoBody(body io.ReadCloser) bool {
|
||||
return body == http.NoBody
|
||||
}
|
||||
|
||||
func go18httpNoBody() io.ReadCloser { return http.NoBody } // for tests only
|
||||
16
vendor/golang.org/x/net/http2/go19.go
generated
vendored
16
vendor/golang.org/x/net/http2/go19.go
generated
vendored
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.9
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func configureServer19(s *http.Server, conf *Server) error {
|
||||
s.RegisterOnShutdown(conf.state.startGracefulShutdown)
|
||||
return nil
|
||||
}
|
||||
170
vendor/golang.org/x/net/http2/gotrack.go
generated
vendored
170
vendor/golang.org/x/net/http2/gotrack.go
generated
vendored
|
|
@ -1,170 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Defensive debug-only utility to track that functions run on the
|
||||
// goroutine that they're supposed to.
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
|
||||
|
||||
type goroutineLock uint64
|
||||
|
||||
func newGoroutineLock() goroutineLock {
|
||||
if !DebugGoroutines {
|
||||
return 0
|
||||
}
|
||||
return goroutineLock(curGoroutineID())
|
||||
}
|
||||
|
||||
func (g goroutineLock) check() {
|
||||
if !DebugGoroutines {
|
||||
return
|
||||
}
|
||||
if curGoroutineID() != uint64(g) {
|
||||
panic("running on the wrong goroutine")
|
||||
}
|
||||
}
|
||||
|
||||
func (g goroutineLock) checkNotOn() {
|
||||
if !DebugGoroutines {
|
||||
return
|
||||
}
|
||||
if curGoroutineID() == uint64(g) {
|
||||
panic("running on the wrong goroutine")
|
||||
}
|
||||
}
|
||||
|
||||
var goroutineSpace = []byte("goroutine ")
|
||||
|
||||
func curGoroutineID() uint64 {
|
||||
bp := littleBuf.Get().(*[]byte)
|
||||
defer littleBuf.Put(bp)
|
||||
b := *bp
|
||||
b = b[:runtime.Stack(b, false)]
|
||||
// Parse the 4707 out of "goroutine 4707 ["
|
||||
b = bytes.TrimPrefix(b, goroutineSpace)
|
||||
i := bytes.IndexByte(b, ' ')
|
||||
if i < 0 {
|
||||
panic(fmt.Sprintf("No space found in %q", b))
|
||||
}
|
||||
b = b[:i]
|
||||
n, err := parseUintBytes(b, 10, 64)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
var littleBuf = sync.Pool{
|
||||
New: func() interface{} {
|
||||
buf := make([]byte, 64)
|
||||
return &buf
|
||||
},
|
||||
}
|
||||
|
||||
// parseUintBytes is like strconv.ParseUint, but using a []byte.
|
||||
func parseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) {
|
||||
var cutoff, maxVal uint64
|
||||
|
||||
if bitSize == 0 {
|
||||
bitSize = int(strconv.IntSize)
|
||||
}
|
||||
|
||||
s0 := s
|
||||
switch {
|
||||
case len(s) < 1:
|
||||
err = strconv.ErrSyntax
|
||||
goto Error
|
||||
|
||||
case 2 <= base && base <= 36:
|
||||
// valid base; nothing to do
|
||||
|
||||
case base == 0:
|
||||
// Look for octal, hex prefix.
|
||||
switch {
|
||||
case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
|
||||
base = 16
|
||||
s = s[2:]
|
||||
if len(s) < 1 {
|
||||
err = strconv.ErrSyntax
|
||||
goto Error
|
||||
}
|
||||
case s[0] == '0':
|
||||
base = 8
|
||||
default:
|
||||
base = 10
|
||||
}
|
||||
|
||||
default:
|
||||
err = errors.New("invalid base " + strconv.Itoa(base))
|
||||
goto Error
|
||||
}
|
||||
|
||||
n = 0
|
||||
cutoff = cutoff64(base)
|
||||
maxVal = 1<<uint(bitSize) - 1
|
||||
|
||||
for i := 0; i < len(s); i++ {
|
||||
var v byte
|
||||
d := s[i]
|
||||
switch {
|
||||
case '0' <= d && d <= '9':
|
||||
v = d - '0'
|
||||
case 'a' <= d && d <= 'z':
|
||||
v = d - 'a' + 10
|
||||
case 'A' <= d && d <= 'Z':
|
||||
v = d - 'A' + 10
|
||||
default:
|
||||
n = 0
|
||||
err = strconv.ErrSyntax
|
||||
goto Error
|
||||
}
|
||||
if int(v) >= base {
|
||||
n = 0
|
||||
err = strconv.ErrSyntax
|
||||
goto Error
|
||||
}
|
||||
|
||||
if n >= cutoff {
|
||||
// n*base overflows
|
||||
n = 1<<64 - 1
|
||||
err = strconv.ErrRange
|
||||
goto Error
|
||||
}
|
||||
n *= uint64(base)
|
||||
|
||||
n1 := n + uint64(v)
|
||||
if n1 < n || n1 > maxVal {
|
||||
// n+v overflows
|
||||
n = 1<<64 - 1
|
||||
err = strconv.ErrRange
|
||||
goto Error
|
||||
}
|
||||
n = n1
|
||||
}
|
||||
|
||||
return n, nil
|
||||
|
||||
Error:
|
||||
return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err}
|
||||
}
|
||||
|
||||
// Return the first number n such that n*base >= 1<<64.
|
||||
func cutoff64(base int) uint64 {
|
||||
if base < 2 {
|
||||
return 0
|
||||
}
|
||||
return (1<<64-1)/uint64(base) + 1
|
||||
}
|
||||
78
vendor/golang.org/x/net/http2/headermap.go
generated
vendored
78
vendor/golang.org/x/net/http2/headermap.go
generated
vendored
|
|
@ -1,78 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
commonLowerHeader = map[string]string{} // Go-Canonical-Case -> lower-case
|
||||
commonCanonHeader = map[string]string{} // lower-case -> Go-Canonical-Case
|
||||
)
|
||||
|
||||
func init() {
|
||||
for _, v := range []string{
|
||||
"accept",
|
||||
"accept-charset",
|
||||
"accept-encoding",
|
||||
"accept-language",
|
||||
"accept-ranges",
|
||||
"age",
|
||||
"access-control-allow-origin",
|
||||
"allow",
|
||||
"authorization",
|
||||
"cache-control",
|
||||
"content-disposition",
|
||||
"content-encoding",
|
||||
"content-language",
|
||||
"content-length",
|
||||
"content-location",
|
||||
"content-range",
|
||||
"content-type",
|
||||
"cookie",
|
||||
"date",
|
||||
"etag",
|
||||
"expect",
|
||||
"expires",
|
||||
"from",
|
||||
"host",
|
||||
"if-match",
|
||||
"if-modified-since",
|
||||
"if-none-match",
|
||||
"if-unmodified-since",
|
||||
"last-modified",
|
||||
"link",
|
||||
"location",
|
||||
"max-forwards",
|
||||
"proxy-authenticate",
|
||||
"proxy-authorization",
|
||||
"range",
|
||||
"referer",
|
||||
"refresh",
|
||||
"retry-after",
|
||||
"server",
|
||||
"set-cookie",
|
||||
"strict-transport-security",
|
||||
"trailer",
|
||||
"transfer-encoding",
|
||||
"user-agent",
|
||||
"vary",
|
||||
"via",
|
||||
"www-authenticate",
|
||||
} {
|
||||
chk := http.CanonicalHeaderKey(v)
|
||||
commonLowerHeader[chk] = v
|
||||
commonCanonHeader[v] = chk
|
||||
}
|
||||
}
|
||||
|
||||
func lowerHeader(v string) string {
|
||||
if s, ok := commonLowerHeader[v]; ok {
|
||||
return s
|
||||
}
|
||||
return strings.ToLower(v)
|
||||
}
|
||||
240
vendor/golang.org/x/net/http2/hpack/encode.go
generated
vendored
240
vendor/golang.org/x/net/http2/hpack/encode.go
generated
vendored
|
|
@ -1,240 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package hpack
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
uint32Max = ^uint32(0)
|
||||
initialHeaderTableSize = 4096
|
||||
)
|
||||
|
||||
type Encoder struct {
|
||||
dynTab dynamicTable
|
||||
// minSize is the minimum table size set by
|
||||
// SetMaxDynamicTableSize after the previous Header Table Size
|
||||
// Update.
|
||||
minSize uint32
|
||||
// maxSizeLimit is the maximum table size this encoder
|
||||
// supports. This will protect the encoder from too large
|
||||
// size.
|
||||
maxSizeLimit uint32
|
||||
// tableSizeUpdate indicates whether "Header Table Size
|
||||
// Update" is required.
|
||||
tableSizeUpdate bool
|
||||
w io.Writer
|
||||
buf []byte
|
||||
}
|
||||
|
||||
// NewEncoder returns a new Encoder which performs HPACK encoding. An
|
||||
// encoded data is written to w.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
e := &Encoder{
|
||||
minSize: uint32Max,
|
||||
maxSizeLimit: initialHeaderTableSize,
|
||||
tableSizeUpdate: false,
|
||||
w: w,
|
||||
}
|
||||
e.dynTab.table.init()
|
||||
e.dynTab.setMaxSize(initialHeaderTableSize)
|
||||
return e
|
||||
}
|
||||
|
||||
// WriteField encodes f into a single Write to e's underlying Writer.
|
||||
// This function may also produce bytes for "Header Table Size Update"
|
||||
// if necessary. If produced, it is done before encoding f.
|
||||
func (e *Encoder) WriteField(f HeaderField) error {
|
||||
e.buf = e.buf[:0]
|
||||
|
||||
if e.tableSizeUpdate {
|
||||
e.tableSizeUpdate = false
|
||||
if e.minSize < e.dynTab.maxSize {
|
||||
e.buf = appendTableSize(e.buf, e.minSize)
|
||||
}
|
||||
e.minSize = uint32Max
|
||||
e.buf = appendTableSize(e.buf, e.dynTab.maxSize)
|
||||
}
|
||||
|
||||
idx, nameValueMatch := e.searchTable(f)
|
||||
if nameValueMatch {
|
||||
e.buf = appendIndexed(e.buf, idx)
|
||||
} else {
|
||||
indexing := e.shouldIndex(f)
|
||||
if indexing {
|
||||
e.dynTab.add(f)
|
||||
}
|
||||
|
||||
if idx == 0 {
|
||||
e.buf = appendNewName(e.buf, f, indexing)
|
||||
} else {
|
||||
e.buf = appendIndexedName(e.buf, f, idx, indexing)
|
||||
}
|
||||
}
|
||||
n, err := e.w.Write(e.buf)
|
||||
if err == nil && n != len(e.buf) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// searchTable searches f in both stable and dynamic header tables.
|
||||
// The static header table is searched first. Only when there is no
|
||||
// exact match for both name and value, the dynamic header table is
|
||||
// then searched. If there is no match, i is 0. If both name and value
|
||||
// match, i is the matched index and nameValueMatch becomes true. If
|
||||
// only name matches, i points to that index and nameValueMatch
|
||||
// becomes false.
|
||||
func (e *Encoder) searchTable(f HeaderField) (i uint64, nameValueMatch bool) {
|
||||
i, nameValueMatch = staticTable.search(f)
|
||||
if nameValueMatch {
|
||||
return i, true
|
||||
}
|
||||
|
||||
j, nameValueMatch := e.dynTab.table.search(f)
|
||||
if nameValueMatch || (i == 0 && j != 0) {
|
||||
return j + uint64(staticTable.len()), nameValueMatch
|
||||
}
|
||||
|
||||
return i, false
|
||||
}
|
||||
|
||||
// SetMaxDynamicTableSize changes the dynamic header table size to v.
|
||||
// The actual size is bounded by the value passed to
|
||||
// SetMaxDynamicTableSizeLimit.
|
||||
func (e *Encoder) SetMaxDynamicTableSize(v uint32) {
|
||||
if v > e.maxSizeLimit {
|
||||
v = e.maxSizeLimit
|
||||
}
|
||||
if v < e.minSize {
|
||||
e.minSize = v
|
||||
}
|
||||
e.tableSizeUpdate = true
|
||||
e.dynTab.setMaxSize(v)
|
||||
}
|
||||
|
||||
// SetMaxDynamicTableSizeLimit changes the maximum value that can be
|
||||
// specified in SetMaxDynamicTableSize to v. By default, it is set to
|
||||
// 4096, which is the same size of the default dynamic header table
|
||||
// size described in HPACK specification. If the current maximum
|
||||
// dynamic header table size is strictly greater than v, "Header Table
|
||||
// Size Update" will be done in the next WriteField call and the
|
||||
// maximum dynamic header table size is truncated to v.
|
||||
func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) {
|
||||
e.maxSizeLimit = v
|
||||
if e.dynTab.maxSize > v {
|
||||
e.tableSizeUpdate = true
|
||||
e.dynTab.setMaxSize(v)
|
||||
}
|
||||
}
|
||||
|
||||
// shouldIndex reports whether f should be indexed.
|
||||
func (e *Encoder) shouldIndex(f HeaderField) bool {
|
||||
return !f.Sensitive && f.Size() <= e.dynTab.maxSize
|
||||
}
|
||||
|
||||
// appendIndexed appends index i, as encoded in "Indexed Header Field"
|
||||
// representation, to dst and returns the extended buffer.
|
||||
func appendIndexed(dst []byte, i uint64) []byte {
|
||||
first := len(dst)
|
||||
dst = appendVarInt(dst, 7, i)
|
||||
dst[first] |= 0x80
|
||||
return dst
|
||||
}
|
||||
|
||||
// appendNewName appends f, as encoded in one of "Literal Header field
|
||||
// - New Name" representation variants, to dst and returns the
|
||||
// extended buffer.
|
||||
//
|
||||
// If f.Sensitive is true, "Never Indexed" representation is used. If
|
||||
// f.Sensitive is false and indexing is true, "Inremental Indexing"
|
||||
// representation is used.
|
||||
func appendNewName(dst []byte, f HeaderField, indexing bool) []byte {
|
||||
dst = append(dst, encodeTypeByte(indexing, f.Sensitive))
|
||||
dst = appendHpackString(dst, f.Name)
|
||||
return appendHpackString(dst, f.Value)
|
||||
}
|
||||
|
||||
// appendIndexedName appends f and index i referring indexed name
|
||||
// entry, as encoded in one of "Literal Header field - Indexed Name"
|
||||
// representation variants, to dst and returns the extended buffer.
|
||||
//
|
||||
// If f.Sensitive is true, "Never Indexed" representation is used. If
|
||||
// f.Sensitive is false and indexing is true, "Incremental Indexing"
|
||||
// representation is used.
|
||||
func appendIndexedName(dst []byte, f HeaderField, i uint64, indexing bool) []byte {
|
||||
first := len(dst)
|
||||
var n byte
|
||||
if indexing {
|
||||
n = 6
|
||||
} else {
|
||||
n = 4
|
||||
}
|
||||
dst = appendVarInt(dst, n, i)
|
||||
dst[first] |= encodeTypeByte(indexing, f.Sensitive)
|
||||
return appendHpackString(dst, f.Value)
|
||||
}
|
||||
|
||||
// appendTableSize appends v, as encoded in "Header Table Size Update"
|
||||
// representation, to dst and returns the extended buffer.
|
||||
func appendTableSize(dst []byte, v uint32) []byte {
|
||||
first := len(dst)
|
||||
dst = appendVarInt(dst, 5, uint64(v))
|
||||
dst[first] |= 0x20
|
||||
return dst
|
||||
}
|
||||
|
||||
// appendVarInt appends i, as encoded in variable integer form using n
|
||||
// bit prefix, to dst and returns the extended buffer.
|
||||
//
|
||||
// See
|
||||
// http://http2.github.io/http2-spec/compression.html#integer.representation
|
||||
func appendVarInt(dst []byte, n byte, i uint64) []byte {
|
||||
k := uint64((1 << n) - 1)
|
||||
if i < k {
|
||||
return append(dst, byte(i))
|
||||
}
|
||||
dst = append(dst, byte(k))
|
||||
i -= k
|
||||
for ; i >= 128; i >>= 7 {
|
||||
dst = append(dst, byte(0x80|(i&0x7f)))
|
||||
}
|
||||
return append(dst, byte(i))
|
||||
}
|
||||
|
||||
// appendHpackString appends s, as encoded in "String Literal"
|
||||
// representation, to dst and returns the the extended buffer.
|
||||
//
|
||||
// s will be encoded in Huffman codes only when it produces strictly
|
||||
// shorter byte string.
|
||||
func appendHpackString(dst []byte, s string) []byte {
|
||||
huffmanLength := HuffmanEncodeLength(s)
|
||||
if huffmanLength < uint64(len(s)) {
|
||||
first := len(dst)
|
||||
dst = appendVarInt(dst, 7, huffmanLength)
|
||||
dst = AppendHuffmanString(dst, s)
|
||||
dst[first] |= 0x80
|
||||
} else {
|
||||
dst = appendVarInt(dst, 7, uint64(len(s)))
|
||||
dst = append(dst, s...)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// encodeTypeByte returns type byte. If sensitive is true, type byte
|
||||
// for "Never Indexed" representation is returned. If sensitive is
|
||||
// false and indexing is true, type byte for "Incremental Indexing"
|
||||
// representation is returned. Otherwise, type byte for "Without
|
||||
// Indexing" is returned.
|
||||
func encodeTypeByte(indexing, sensitive bool) byte {
|
||||
if sensitive {
|
||||
return 0x10
|
||||
}
|
||||
if indexing {
|
||||
return 0x40
|
||||
}
|
||||
return 0
|
||||
}
|
||||
490
vendor/golang.org/x/net/http2/hpack/hpack.go
generated
vendored
490
vendor/golang.org/x/net/http2/hpack/hpack.go
generated
vendored
|
|
@ -1,490 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package hpack implements HPACK, a compression format for
|
||||
// efficiently representing HTTP header fields in the context of HTTP/2.
|
||||
//
|
||||
// See http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09
|
||||
package hpack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// A DecodingError is something the spec defines as a decoding error.
|
||||
type DecodingError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (de DecodingError) Error() string {
|
||||
return fmt.Sprintf("decoding error: %v", de.Err)
|
||||
}
|
||||
|
||||
// An InvalidIndexError is returned when an encoder references a table
|
||||
// entry before the static table or after the end of the dynamic table.
|
||||
type InvalidIndexError int
|
||||
|
||||
func (e InvalidIndexError) Error() string {
|
||||
return fmt.Sprintf("invalid indexed representation index %d", int(e))
|
||||
}
|
||||
|
||||
// A HeaderField is a name-value pair. Both the name and value are
|
||||
// treated as opaque sequences of octets.
|
||||
type HeaderField struct {
|
||||
Name, Value string
|
||||
|
||||
// Sensitive means that this header field should never be
|
||||
// indexed.
|
||||
Sensitive bool
|
||||
}
|
||||
|
||||
// IsPseudo reports whether the header field is an http2 pseudo header.
|
||||
// That is, it reports whether it starts with a colon.
|
||||
// It is not otherwise guaranteed to be a valid pseudo header field,
|
||||
// though.
|
||||
func (hf HeaderField) IsPseudo() bool {
|
||||
return len(hf.Name) != 0 && hf.Name[0] == ':'
|
||||
}
|
||||
|
||||
func (hf HeaderField) String() string {
|
||||
var suffix string
|
||||
if hf.Sensitive {
|
||||
suffix = " (sensitive)"
|
||||
}
|
||||
return fmt.Sprintf("header field %q = %q%s", hf.Name, hf.Value, suffix)
|
||||
}
|
||||
|
||||
// Size returns the size of an entry per RFC 7541 section 4.1.
|
||||
func (hf HeaderField) Size() uint32 {
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.4.1
|
||||
// "The size of the dynamic table is the sum of the size of
|
||||
// its entries. The size of an entry is the sum of its name's
|
||||
// length in octets (as defined in Section 5.2), its value's
|
||||
// length in octets (see Section 5.2), plus 32. The size of
|
||||
// an entry is calculated using the length of the name and
|
||||
// value without any Huffman encoding applied."
|
||||
|
||||
// This can overflow if somebody makes a large HeaderField
|
||||
// Name and/or Value by hand, but we don't care, because that
|
||||
// won't happen on the wire because the encoding doesn't allow
|
||||
// it.
|
||||
return uint32(len(hf.Name) + len(hf.Value) + 32)
|
||||
}
|
||||
|
||||
// A Decoder is the decoding context for incremental processing of
|
||||
// header blocks.
|
||||
type Decoder struct {
|
||||
dynTab dynamicTable
|
||||
emit func(f HeaderField)
|
||||
|
||||
emitEnabled bool // whether calls to emit are enabled
|
||||
maxStrLen int // 0 means unlimited
|
||||
|
||||
// buf is the unparsed buffer. It's only written to
|
||||
// saveBuf if it was truncated in the middle of a header
|
||||
// block. Because it's usually not owned, we can only
|
||||
// process it under Write.
|
||||
buf []byte // not owned; only valid during Write
|
||||
|
||||
// saveBuf is previous data passed to Write which we weren't able
|
||||
// to fully parse before. Unlike buf, we own this data.
|
||||
saveBuf bytes.Buffer
|
||||
}
|
||||
|
||||
// NewDecoder returns a new decoder with the provided maximum dynamic
|
||||
// table size. The emitFunc will be called for each valid field
|
||||
// parsed, in the same goroutine as calls to Write, before Write returns.
|
||||
func NewDecoder(maxDynamicTableSize uint32, emitFunc func(f HeaderField)) *Decoder {
|
||||
d := &Decoder{
|
||||
emit: emitFunc,
|
||||
emitEnabled: true,
|
||||
}
|
||||
d.dynTab.table.init()
|
||||
d.dynTab.allowedMaxSize = maxDynamicTableSize
|
||||
d.dynTab.setMaxSize(maxDynamicTableSize)
|
||||
return d
|
||||
}
|
||||
|
||||
// ErrStringLength is returned by Decoder.Write when the max string length
|
||||
// (as configured by Decoder.SetMaxStringLength) would be violated.
|
||||
var ErrStringLength = errors.New("hpack: string too long")
|
||||
|
||||
// SetMaxStringLength sets the maximum size of a HeaderField name or
|
||||
// value string. If a string exceeds this length (even after any
|
||||
// decompression), Write will return ErrStringLength.
|
||||
// A value of 0 means unlimited and is the default from NewDecoder.
|
||||
func (d *Decoder) SetMaxStringLength(n int) {
|
||||
d.maxStrLen = n
|
||||
}
|
||||
|
||||
// SetEmitFunc changes the callback used when new header fields
|
||||
// are decoded.
|
||||
// It must be non-nil. It does not affect EmitEnabled.
|
||||
func (d *Decoder) SetEmitFunc(emitFunc func(f HeaderField)) {
|
||||
d.emit = emitFunc
|
||||
}
|
||||
|
||||
// SetEmitEnabled controls whether the emitFunc provided to NewDecoder
|
||||
// should be called. The default is true.
|
||||
//
|
||||
// This facility exists to let servers enforce MAX_HEADER_LIST_SIZE
|
||||
// while still decoding and keeping in-sync with decoder state, but
|
||||
// without doing unnecessary decompression or generating unnecessary
|
||||
// garbage for header fields past the limit.
|
||||
func (d *Decoder) SetEmitEnabled(v bool) { d.emitEnabled = v }
|
||||
|
||||
// EmitEnabled reports whether calls to the emitFunc provided to NewDecoder
|
||||
// are currently enabled. The default is true.
|
||||
func (d *Decoder) EmitEnabled() bool { return d.emitEnabled }
|
||||
|
||||
// TODO: add method *Decoder.Reset(maxSize, emitFunc) to let callers re-use Decoders and their
|
||||
// underlying buffers for garbage reasons.
|
||||
|
||||
func (d *Decoder) SetMaxDynamicTableSize(v uint32) {
|
||||
d.dynTab.setMaxSize(v)
|
||||
}
|
||||
|
||||
// SetAllowedMaxDynamicTableSize sets the upper bound that the encoded
|
||||
// stream (via dynamic table size updates) may set the maximum size
|
||||
// to.
|
||||
func (d *Decoder) SetAllowedMaxDynamicTableSize(v uint32) {
|
||||
d.dynTab.allowedMaxSize = v
|
||||
}
|
||||
|
||||
type dynamicTable struct {
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.2.3.2
|
||||
table headerFieldTable
|
||||
size uint32 // in bytes
|
||||
maxSize uint32 // current maxSize
|
||||
allowedMaxSize uint32 // maxSize may go up to this, inclusive
|
||||
}
|
||||
|
||||
func (dt *dynamicTable) setMaxSize(v uint32) {
|
||||
dt.maxSize = v
|
||||
dt.evict()
|
||||
}
|
||||
|
||||
func (dt *dynamicTable) add(f HeaderField) {
|
||||
dt.table.addEntry(f)
|
||||
dt.size += f.Size()
|
||||
dt.evict()
|
||||
}
|
||||
|
||||
// If we're too big, evict old stuff.
|
||||
func (dt *dynamicTable) evict() {
|
||||
var n int
|
||||
for dt.size > dt.maxSize && n < dt.table.len() {
|
||||
dt.size -= dt.table.ents[n].Size()
|
||||
n++
|
||||
}
|
||||
dt.table.evictOldest(n)
|
||||
}
|
||||
|
||||
func (d *Decoder) maxTableIndex() int {
|
||||
// This should never overflow. RFC 7540 Section 6.5.2 limits the size of
|
||||
// the dynamic table to 2^32 bytes, where each entry will occupy more than
|
||||
// one byte. Further, the staticTable has a fixed, small length.
|
||||
return d.dynTab.table.len() + staticTable.len()
|
||||
}
|
||||
|
||||
func (d *Decoder) at(i uint64) (hf HeaderField, ok bool) {
|
||||
// See Section 2.3.3.
|
||||
if i == 0 {
|
||||
return
|
||||
}
|
||||
if i <= uint64(staticTable.len()) {
|
||||
return staticTable.ents[i-1], true
|
||||
}
|
||||
if i > uint64(d.maxTableIndex()) {
|
||||
return
|
||||
}
|
||||
// In the dynamic table, newer entries have lower indices.
|
||||
// However, dt.ents[0] is the oldest entry. Hence, dt.ents is
|
||||
// the reversed dynamic table.
|
||||
dt := d.dynTab.table
|
||||
return dt.ents[dt.len()-(int(i)-staticTable.len())], true
|
||||
}
|
||||
|
||||
// Decode decodes an entire block.
|
||||
//
|
||||
// TODO: remove this method and make it incremental later? This is
|
||||
// easier for debugging now.
|
||||
func (d *Decoder) DecodeFull(p []byte) ([]HeaderField, error) {
|
||||
var hf []HeaderField
|
||||
saveFunc := d.emit
|
||||
defer func() { d.emit = saveFunc }()
|
||||
d.emit = func(f HeaderField) { hf = append(hf, f) }
|
||||
if _, err := d.Write(p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := d.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hf, nil
|
||||
}
|
||||
|
||||
func (d *Decoder) Close() error {
|
||||
if d.saveBuf.Len() > 0 {
|
||||
d.saveBuf.Reset()
|
||||
return DecodingError{errors.New("truncated headers")}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) Write(p []byte) (n int, err error) {
|
||||
if len(p) == 0 {
|
||||
// Prevent state machine CPU attacks (making us redo
|
||||
// work up to the point of finding out we don't have
|
||||
// enough data)
|
||||
return
|
||||
}
|
||||
// Only copy the data if we have to. Optimistically assume
|
||||
// that p will contain a complete header block.
|
||||
if d.saveBuf.Len() == 0 {
|
||||
d.buf = p
|
||||
} else {
|
||||
d.saveBuf.Write(p)
|
||||
d.buf = d.saveBuf.Bytes()
|
||||
d.saveBuf.Reset()
|
||||
}
|
||||
|
||||
for len(d.buf) > 0 {
|
||||
err = d.parseHeaderFieldRepr()
|
||||
if err == errNeedMore {
|
||||
// Extra paranoia, making sure saveBuf won't
|
||||
// get too large. All the varint and string
|
||||
// reading code earlier should already catch
|
||||
// overlong things and return ErrStringLength,
|
||||
// but keep this as a last resort.
|
||||
const varIntOverhead = 8 // conservative
|
||||
if d.maxStrLen != 0 && int64(len(d.buf)) > 2*(int64(d.maxStrLen)+varIntOverhead) {
|
||||
return 0, ErrStringLength
|
||||
}
|
||||
d.saveBuf.Write(d.buf)
|
||||
return len(p), nil
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return len(p), err
|
||||
}
|
||||
|
||||
// errNeedMore is an internal sentinel error value that means the
|
||||
// buffer is truncated and we need to read more data before we can
|
||||
// continue parsing.
|
||||
var errNeedMore = errors.New("need more data")
|
||||
|
||||
type indexType int
|
||||
|
||||
const (
|
||||
indexedTrue indexType = iota
|
||||
indexedFalse
|
||||
indexedNever
|
||||
)
|
||||
|
||||
func (v indexType) indexed() bool { return v == indexedTrue }
|
||||
func (v indexType) sensitive() bool { return v == indexedNever }
|
||||
|
||||
// returns errNeedMore if there isn't enough data available.
|
||||
// any other error is fatal.
|
||||
// consumes d.buf iff it returns nil.
|
||||
// precondition: must be called with len(d.buf) > 0
|
||||
func (d *Decoder) parseHeaderFieldRepr() error {
|
||||
b := d.buf[0]
|
||||
switch {
|
||||
case b&128 != 0:
|
||||
// Indexed representation.
|
||||
// High bit set?
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.1
|
||||
return d.parseFieldIndexed()
|
||||
case b&192 == 64:
|
||||
// 6.2.1 Literal Header Field with Incremental Indexing
|
||||
// 0b10xxxxxx: top two bits are 10
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.1
|
||||
return d.parseFieldLiteral(6, indexedTrue)
|
||||
case b&240 == 0:
|
||||
// 6.2.2 Literal Header Field without Indexing
|
||||
// 0b0000xxxx: top four bits are 0000
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.2
|
||||
return d.parseFieldLiteral(4, indexedFalse)
|
||||
case b&240 == 16:
|
||||
// 6.2.3 Literal Header Field never Indexed
|
||||
// 0b0001xxxx: top four bits are 0001
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.3
|
||||
return d.parseFieldLiteral(4, indexedNever)
|
||||
case b&224 == 32:
|
||||
// 6.3 Dynamic Table Size Update
|
||||
// Top three bits are '001'.
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.6.3
|
||||
return d.parseDynamicTableSizeUpdate()
|
||||
}
|
||||
|
||||
return DecodingError{errors.New("invalid encoding")}
|
||||
}
|
||||
|
||||
// (same invariants and behavior as parseHeaderFieldRepr)
|
||||
func (d *Decoder) parseFieldIndexed() error {
|
||||
buf := d.buf
|
||||
idx, buf, err := readVarInt(7, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
hf, ok := d.at(idx)
|
||||
if !ok {
|
||||
return DecodingError{InvalidIndexError(idx)}
|
||||
}
|
||||
d.buf = buf
|
||||
return d.callEmit(HeaderField{Name: hf.Name, Value: hf.Value})
|
||||
}
|
||||
|
||||
// (same invariants and behavior as parseHeaderFieldRepr)
|
||||
func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error {
|
||||
buf := d.buf
|
||||
nameIdx, buf, err := readVarInt(n, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var hf HeaderField
|
||||
wantStr := d.emitEnabled || it.indexed()
|
||||
if nameIdx > 0 {
|
||||
ihf, ok := d.at(nameIdx)
|
||||
if !ok {
|
||||
return DecodingError{InvalidIndexError(nameIdx)}
|
||||
}
|
||||
hf.Name = ihf.Name
|
||||
} else {
|
||||
hf.Name, buf, err = d.readString(buf, wantStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
hf.Value, buf, err = d.readString(buf, wantStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.buf = buf
|
||||
if it.indexed() {
|
||||
d.dynTab.add(hf)
|
||||
}
|
||||
hf.Sensitive = it.sensitive()
|
||||
return d.callEmit(hf)
|
||||
}
|
||||
|
||||
func (d *Decoder) callEmit(hf HeaderField) error {
|
||||
if d.maxStrLen != 0 {
|
||||
if len(hf.Name) > d.maxStrLen || len(hf.Value) > d.maxStrLen {
|
||||
return ErrStringLength
|
||||
}
|
||||
}
|
||||
if d.emitEnabled {
|
||||
d.emit(hf)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// (same invariants and behavior as parseHeaderFieldRepr)
|
||||
func (d *Decoder) parseDynamicTableSizeUpdate() error {
|
||||
buf := d.buf
|
||||
size, buf, err := readVarInt(5, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if size > uint64(d.dynTab.allowedMaxSize) {
|
||||
return DecodingError{errors.New("dynamic table size update too large")}
|
||||
}
|
||||
d.dynTab.setMaxSize(uint32(size))
|
||||
d.buf = buf
|
||||
return nil
|
||||
}
|
||||
|
||||
var errVarintOverflow = DecodingError{errors.New("varint integer overflow")}
|
||||
|
||||
// readVarInt reads an unsigned variable length integer off the
|
||||
// beginning of p. n is the parameter as described in
|
||||
// http://http2.github.io/http2-spec/compression.html#rfc.section.5.1.
|
||||
//
|
||||
// n must always be between 1 and 8.
|
||||
//
|
||||
// The returned remain buffer is either a smaller suffix of p, or err != nil.
|
||||
// The error is errNeedMore if p doesn't contain a complete integer.
|
||||
func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) {
|
||||
if n < 1 || n > 8 {
|
||||
panic("bad n")
|
||||
}
|
||||
if len(p) == 0 {
|
||||
return 0, p, errNeedMore
|
||||
}
|
||||
i = uint64(p[0])
|
||||
if n < 8 {
|
||||
i &= (1 << uint64(n)) - 1
|
||||
}
|
||||
if i < (1<<uint64(n))-1 {
|
||||
return i, p[1:], nil
|
||||
}
|
||||
|
||||
origP := p
|
||||
p = p[1:]
|
||||
var m uint64
|
||||
for len(p) > 0 {
|
||||
b := p[0]
|
||||
p = p[1:]
|
||||
i += uint64(b&127) << m
|
||||
if b&128 == 0 {
|
||||
return i, p, nil
|
||||
}
|
||||
m += 7
|
||||
if m >= 63 { // TODO: proper overflow check. making this up.
|
||||
return 0, origP, errVarintOverflow
|
||||
}
|
||||
}
|
||||
return 0, origP, errNeedMore
|
||||
}
|
||||
|
||||
// readString decodes an hpack string from p.
|
||||
//
|
||||
// wantStr is whether s will be used. If false, decompression and
|
||||
// []byte->string garbage are skipped if s will be ignored
|
||||
// anyway. This does mean that huffman decoding errors for non-indexed
|
||||
// strings past the MAX_HEADER_LIST_SIZE are ignored, but the server
|
||||
// is returning an error anyway, and because they're not indexed, the error
|
||||
// won't affect the decoding state.
|
||||
func (d *Decoder) readString(p []byte, wantStr bool) (s string, remain []byte, err error) {
|
||||
if len(p) == 0 {
|
||||
return "", p, errNeedMore
|
||||
}
|
||||
isHuff := p[0]&128 != 0
|
||||
strLen, p, err := readVarInt(7, p)
|
||||
if err != nil {
|
||||
return "", p, err
|
||||
}
|
||||
if d.maxStrLen != 0 && strLen > uint64(d.maxStrLen) {
|
||||
return "", nil, ErrStringLength
|
||||
}
|
||||
if uint64(len(p)) < strLen {
|
||||
return "", p, errNeedMore
|
||||
}
|
||||
if !isHuff {
|
||||
if wantStr {
|
||||
s = string(p[:strLen])
|
||||
}
|
||||
return s, p[strLen:], nil
|
||||
}
|
||||
|
||||
if wantStr {
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
buf.Reset() // don't trust others
|
||||
defer bufPool.Put(buf)
|
||||
if err := huffmanDecode(buf, d.maxStrLen, p[:strLen]); err != nil {
|
||||
buf.Reset()
|
||||
return "", nil, err
|
||||
}
|
||||
s = buf.String()
|
||||
buf.Reset() // be nice to GC
|
||||
}
|
||||
return s, p[strLen:], nil
|
||||
}
|
||||
212
vendor/golang.org/x/net/http2/hpack/huffman.go
generated
vendored
212
vendor/golang.org/x/net/http2/hpack/huffman.go
generated
vendored
|
|
@ -1,212 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package hpack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} { return new(bytes.Buffer) },
|
||||
}
|
||||
|
||||
// HuffmanDecode decodes the string in v and writes the expanded
|
||||
// result to w, returning the number of bytes written to w and the
|
||||
// Write call's return value. At most one Write call is made.
|
||||
func HuffmanDecode(w io.Writer, v []byte) (int, error) {
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
buf.Reset()
|
||||
defer bufPool.Put(buf)
|
||||
if err := huffmanDecode(buf, 0, v); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return w.Write(buf.Bytes())
|
||||
}
|
||||
|
||||
// HuffmanDecodeToString decodes the string in v.
|
||||
func HuffmanDecodeToString(v []byte) (string, error) {
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
buf.Reset()
|
||||
defer bufPool.Put(buf)
|
||||
if err := huffmanDecode(buf, 0, v); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
// ErrInvalidHuffman is returned for errors found decoding
|
||||
// Huffman-encoded strings.
|
||||
var ErrInvalidHuffman = errors.New("hpack: invalid Huffman-encoded data")
|
||||
|
||||
// huffmanDecode decodes v to buf.
|
||||
// If maxLen is greater than 0, attempts to write more to buf than
|
||||
// maxLen bytes will return ErrStringLength.
|
||||
func huffmanDecode(buf *bytes.Buffer, maxLen int, v []byte) error {
|
||||
n := rootHuffmanNode
|
||||
// cur is the bit buffer that has not been fed into n.
|
||||
// cbits is the number of low order bits in cur that are valid.
|
||||
// sbits is the number of bits of the symbol prefix being decoded.
|
||||
cur, cbits, sbits := uint(0), uint8(0), uint8(0)
|
||||
for _, b := range v {
|
||||
cur = cur<<8 | uint(b)
|
||||
cbits += 8
|
||||
sbits += 8
|
||||
for cbits >= 8 {
|
||||
idx := byte(cur >> (cbits - 8))
|
||||
n = n.children[idx]
|
||||
if n == nil {
|
||||
return ErrInvalidHuffman
|
||||
}
|
||||
if n.children == nil {
|
||||
if maxLen != 0 && buf.Len() == maxLen {
|
||||
return ErrStringLength
|
||||
}
|
||||
buf.WriteByte(n.sym)
|
||||
cbits -= n.codeLen
|
||||
n = rootHuffmanNode
|
||||
sbits = cbits
|
||||
} else {
|
||||
cbits -= 8
|
||||
}
|
||||
}
|
||||
}
|
||||
for cbits > 0 {
|
||||
n = n.children[byte(cur<<(8-cbits))]
|
||||
if n == nil {
|
||||
return ErrInvalidHuffman
|
||||
}
|
||||
if n.children != nil || n.codeLen > cbits {
|
||||
break
|
||||
}
|
||||
if maxLen != 0 && buf.Len() == maxLen {
|
||||
return ErrStringLength
|
||||
}
|
||||
buf.WriteByte(n.sym)
|
||||
cbits -= n.codeLen
|
||||
n = rootHuffmanNode
|
||||
sbits = cbits
|
||||
}
|
||||
if sbits > 7 {
|
||||
// Either there was an incomplete symbol, or overlong padding.
|
||||
// Both are decoding errors per RFC 7541 section 5.2.
|
||||
return ErrInvalidHuffman
|
||||
}
|
||||
if mask := uint(1<<cbits - 1); cur&mask != mask {
|
||||
// Trailing bits must be a prefix of EOS per RFC 7541 section 5.2.
|
||||
return ErrInvalidHuffman
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type node struct {
|
||||
// children is non-nil for internal nodes
|
||||
children []*node
|
||||
|
||||
// The following are only valid if children is nil:
|
||||
codeLen uint8 // number of bits that led to the output of sym
|
||||
sym byte // output symbol
|
||||
}
|
||||
|
||||
func newInternalNode() *node {
|
||||
return &node{children: make([]*node, 256)}
|
||||
}
|
||||
|
||||
var rootHuffmanNode = newInternalNode()
|
||||
|
||||
func init() {
|
||||
if len(huffmanCodes) != 256 {
|
||||
panic("unexpected size")
|
||||
}
|
||||
for i, code := range huffmanCodes {
|
||||
addDecoderNode(byte(i), code, huffmanCodeLen[i])
|
||||
}
|
||||
}
|
||||
|
||||
func addDecoderNode(sym byte, code uint32, codeLen uint8) {
|
||||
cur := rootHuffmanNode
|
||||
for codeLen > 8 {
|
||||
codeLen -= 8
|
||||
i := uint8(code >> codeLen)
|
||||
if cur.children[i] == nil {
|
||||
cur.children[i] = newInternalNode()
|
||||
}
|
||||
cur = cur.children[i]
|
||||
}
|
||||
shift := 8 - codeLen
|
||||
start, end := int(uint8(code<<shift)), int(1<<shift)
|
||||
for i := start; i < start+end; i++ {
|
||||
cur.children[i] = &node{sym: sym, codeLen: codeLen}
|
||||
}
|
||||
}
|
||||
|
||||
// AppendHuffmanString appends s, as encoded in Huffman codes, to dst
|
||||
// and returns the extended buffer.
|
||||
func AppendHuffmanString(dst []byte, s string) []byte {
|
||||
rembits := uint8(8)
|
||||
|
||||
for i := 0; i < len(s); i++ {
|
||||
if rembits == 8 {
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
dst, rembits = appendByteToHuffmanCode(dst, rembits, s[i])
|
||||
}
|
||||
|
||||
if rembits < 8 {
|
||||
// special EOS symbol
|
||||
code := uint32(0x3fffffff)
|
||||
nbits := uint8(30)
|
||||
|
||||
t := uint8(code >> (nbits - rembits))
|
||||
dst[len(dst)-1] |= t
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// HuffmanEncodeLength returns the number of bytes required to encode
|
||||
// s in Huffman codes. The result is round up to byte boundary.
|
||||
func HuffmanEncodeLength(s string) uint64 {
|
||||
n := uint64(0)
|
||||
for i := 0; i < len(s); i++ {
|
||||
n += uint64(huffmanCodeLen[s[i]])
|
||||
}
|
||||
return (n + 7) / 8
|
||||
}
|
||||
|
||||
// appendByteToHuffmanCode appends Huffman code for c to dst and
|
||||
// returns the extended buffer and the remaining bits in the last
|
||||
// element. The appending is not byte aligned and the remaining bits
|
||||
// in the last element of dst is given in rembits.
|
||||
func appendByteToHuffmanCode(dst []byte, rembits uint8, c byte) ([]byte, uint8) {
|
||||
code := huffmanCodes[c]
|
||||
nbits := huffmanCodeLen[c]
|
||||
|
||||
for {
|
||||
if rembits > nbits {
|
||||
t := uint8(code << (rembits - nbits))
|
||||
dst[len(dst)-1] |= t
|
||||
rembits -= nbits
|
||||
break
|
||||
}
|
||||
|
||||
t := uint8(code >> (nbits - rembits))
|
||||
dst[len(dst)-1] |= t
|
||||
|
||||
nbits -= rembits
|
||||
rembits = 8
|
||||
|
||||
if nbits == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
|
||||
return dst, rembits
|
||||
}
|
||||
479
vendor/golang.org/x/net/http2/hpack/tables.go
generated
vendored
479
vendor/golang.org/x/net/http2/hpack/tables.go
generated
vendored
|
|
@ -1,479 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package hpack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// headerFieldTable implements a list of HeaderFields.
|
||||
// This is used to implement the static and dynamic tables.
|
||||
type headerFieldTable struct {
|
||||
// For static tables, entries are never evicted.
|
||||
//
|
||||
// For dynamic tables, entries are evicted from ents[0] and added to the end.
|
||||
// Each entry has a unique id that starts at one and increments for each
|
||||
// entry that is added. This unique id is stable across evictions, meaning
|
||||
// it can be used as a pointer to a specific entry. As in hpack, unique ids
|
||||
// are 1-based. The unique id for ents[k] is k + evictCount + 1.
|
||||
//
|
||||
// Zero is not a valid unique id.
|
||||
//
|
||||
// evictCount should not overflow in any remotely practical situation. In
|
||||
// practice, we will have one dynamic table per HTTP/2 connection. If we
|
||||
// assume a very powerful server that handles 1M QPS per connection and each
|
||||
// request adds (then evicts) 100 entries from the table, it would still take
|
||||
// 2M years for evictCount to overflow.
|
||||
ents []HeaderField
|
||||
evictCount uint64
|
||||
|
||||
// byName maps a HeaderField name to the unique id of the newest entry with
|
||||
// the same name. See above for a definition of "unique id".
|
||||
byName map[string]uint64
|
||||
|
||||
// byNameValue maps a HeaderField name/value pair to the unique id of the newest
|
||||
// entry with the same name and value. See above for a definition of "unique id".
|
||||
byNameValue map[pairNameValue]uint64
|
||||
}
|
||||
|
||||
type pairNameValue struct {
|
||||
name, value string
|
||||
}
|
||||
|
||||
func (t *headerFieldTable) init() {
|
||||
t.byName = make(map[string]uint64)
|
||||
t.byNameValue = make(map[pairNameValue]uint64)
|
||||
}
|
||||
|
||||
// len reports the number of entries in the table.
|
||||
func (t *headerFieldTable) len() int {
|
||||
return len(t.ents)
|
||||
}
|
||||
|
||||
// addEntry adds a new entry.
|
||||
func (t *headerFieldTable) addEntry(f HeaderField) {
|
||||
id := uint64(t.len()) + t.evictCount + 1
|
||||
t.byName[f.Name] = id
|
||||
t.byNameValue[pairNameValue{f.Name, f.Value}] = id
|
||||
t.ents = append(t.ents, f)
|
||||
}
|
||||
|
||||
// evictOldest evicts the n oldest entries in the table.
|
||||
func (t *headerFieldTable) evictOldest(n int) {
|
||||
if n > t.len() {
|
||||
panic(fmt.Sprintf("evictOldest(%v) on table with %v entries", n, t.len()))
|
||||
}
|
||||
for k := 0; k < n; k++ {
|
||||
f := t.ents[k]
|
||||
id := t.evictCount + uint64(k) + 1
|
||||
if t.byName[f.Name] == id {
|
||||
delete(t.byName, f.Name)
|
||||
}
|
||||
if p := (pairNameValue{f.Name, f.Value}); t.byNameValue[p] == id {
|
||||
delete(t.byNameValue, p)
|
||||
}
|
||||
}
|
||||
copy(t.ents, t.ents[n:])
|
||||
for k := t.len() - n; k < t.len(); k++ {
|
||||
t.ents[k] = HeaderField{} // so strings can be garbage collected
|
||||
}
|
||||
t.ents = t.ents[:t.len()-n]
|
||||
if t.evictCount+uint64(n) < t.evictCount {
|
||||
panic("evictCount overflow")
|
||||
}
|
||||
t.evictCount += uint64(n)
|
||||
}
|
||||
|
||||
// search finds f in the table. If there is no match, i is 0.
|
||||
// If both name and value match, i is the matched index and nameValueMatch
|
||||
// becomes true. If only name matches, i points to that index and
|
||||
// nameValueMatch becomes false.
|
||||
//
|
||||
// The returned index is a 1-based HPACK index. For dynamic tables, HPACK says
|
||||
// that index 1 should be the newest entry, but t.ents[0] is the oldest entry,
|
||||
// meaning t.ents is reversed for dynamic tables. Hence, when t is a dynamic
|
||||
// table, the return value i actually refers to the entry t.ents[t.len()-i].
|
||||
//
|
||||
// All tables are assumed to be a dynamic tables except for the global
|
||||
// staticTable pointer.
|
||||
//
|
||||
// See Section 2.3.3.
|
||||
func (t *headerFieldTable) search(f HeaderField) (i uint64, nameValueMatch bool) {
|
||||
if !f.Sensitive {
|
||||
if id := t.byNameValue[pairNameValue{f.Name, f.Value}]; id != 0 {
|
||||
return t.idToIndex(id), true
|
||||
}
|
||||
}
|
||||
if id := t.byName[f.Name]; id != 0 {
|
||||
return t.idToIndex(id), false
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// idToIndex converts a unique id to an HPACK index.
|
||||
// See Section 2.3.3.
|
||||
func (t *headerFieldTable) idToIndex(id uint64) uint64 {
|
||||
if id <= t.evictCount {
|
||||
panic(fmt.Sprintf("id (%v) <= evictCount (%v)", id, t.evictCount))
|
||||
}
|
||||
k := id - t.evictCount - 1 // convert id to an index t.ents[k]
|
||||
if t != staticTable {
|
||||
return uint64(t.len()) - k // dynamic table
|
||||
}
|
||||
return k + 1
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B
|
||||
var staticTable = newStaticTable()
|
||||
var staticTableEntries = [...]HeaderField{
|
||||
{Name: ":authority"},
|
||||
{Name: ":method", Value: "GET"},
|
||||
{Name: ":method", Value: "POST"},
|
||||
{Name: ":path", Value: "/"},
|
||||
{Name: ":path", Value: "/index.html"},
|
||||
{Name: ":scheme", Value: "http"},
|
||||
{Name: ":scheme", Value: "https"},
|
||||
{Name: ":status", Value: "200"},
|
||||
{Name: ":status", Value: "204"},
|
||||
{Name: ":status", Value: "206"},
|
||||
{Name: ":status", Value: "304"},
|
||||
{Name: ":status", Value: "400"},
|
||||
{Name: ":status", Value: "404"},
|
||||
{Name: ":status", Value: "500"},
|
||||
{Name: "accept-charset"},
|
||||
{Name: "accept-encoding", Value: "gzip, deflate"},
|
||||
{Name: "accept-language"},
|
||||
{Name: "accept-ranges"},
|
||||
{Name: "accept"},
|
||||
{Name: "access-control-allow-origin"},
|
||||
{Name: "age"},
|
||||
{Name: "allow"},
|
||||
{Name: "authorization"},
|
||||
{Name: "cache-control"},
|
||||
{Name: "content-disposition"},
|
||||
{Name: "content-encoding"},
|
||||
{Name: "content-language"},
|
||||
{Name: "content-length"},
|
||||
{Name: "content-location"},
|
||||
{Name: "content-range"},
|
||||
{Name: "content-type"},
|
||||
{Name: "cookie"},
|
||||
{Name: "date"},
|
||||
{Name: "etag"},
|
||||
{Name: "expect"},
|
||||
{Name: "expires"},
|
||||
{Name: "from"},
|
||||
{Name: "host"},
|
||||
{Name: "if-match"},
|
||||
{Name: "if-modified-since"},
|
||||
{Name: "if-none-match"},
|
||||
{Name: "if-range"},
|
||||
{Name: "if-unmodified-since"},
|
||||
{Name: "last-modified"},
|
||||
{Name: "link"},
|
||||
{Name: "location"},
|
||||
{Name: "max-forwards"},
|
||||
{Name: "proxy-authenticate"},
|
||||
{Name: "proxy-authorization"},
|
||||
{Name: "range"},
|
||||
{Name: "referer"},
|
||||
{Name: "refresh"},
|
||||
{Name: "retry-after"},
|
||||
{Name: "server"},
|
||||
{Name: "set-cookie"},
|
||||
{Name: "strict-transport-security"},
|
||||
{Name: "transfer-encoding"},
|
||||
{Name: "user-agent"},
|
||||
{Name: "vary"},
|
||||
{Name: "via"},
|
||||
{Name: "www-authenticate"},
|
||||
}
|
||||
|
||||
func newStaticTable() *headerFieldTable {
|
||||
t := &headerFieldTable{}
|
||||
t.init()
|
||||
for _, e := range staticTableEntries[:] {
|
||||
t.addEntry(e)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
var huffmanCodes = [256]uint32{
|
||||
0x1ff8,
|
||||
0x7fffd8,
|
||||
0xfffffe2,
|
||||
0xfffffe3,
|
||||
0xfffffe4,
|
||||
0xfffffe5,
|
||||
0xfffffe6,
|
||||
0xfffffe7,
|
||||
0xfffffe8,
|
||||
0xffffea,
|
||||
0x3ffffffc,
|
||||
0xfffffe9,
|
||||
0xfffffea,
|
||||
0x3ffffffd,
|
||||
0xfffffeb,
|
||||
0xfffffec,
|
||||
0xfffffed,
|
||||
0xfffffee,
|
||||
0xfffffef,
|
||||
0xffffff0,
|
||||
0xffffff1,
|
||||
0xffffff2,
|
||||
0x3ffffffe,
|
||||
0xffffff3,
|
||||
0xffffff4,
|
||||
0xffffff5,
|
||||
0xffffff6,
|
||||
0xffffff7,
|
||||
0xffffff8,
|
||||
0xffffff9,
|
||||
0xffffffa,
|
||||
0xffffffb,
|
||||
0x14,
|
||||
0x3f8,
|
||||
0x3f9,
|
||||
0xffa,
|
||||
0x1ff9,
|
||||
0x15,
|
||||
0xf8,
|
||||
0x7fa,
|
||||
0x3fa,
|
||||
0x3fb,
|
||||
0xf9,
|
||||
0x7fb,
|
||||
0xfa,
|
||||
0x16,
|
||||
0x17,
|
||||
0x18,
|
||||
0x0,
|
||||
0x1,
|
||||
0x2,
|
||||
0x19,
|
||||
0x1a,
|
||||
0x1b,
|
||||
0x1c,
|
||||
0x1d,
|
||||
0x1e,
|
||||
0x1f,
|
||||
0x5c,
|
||||
0xfb,
|
||||
0x7ffc,
|
||||
0x20,
|
||||
0xffb,
|
||||
0x3fc,
|
||||
0x1ffa,
|
||||
0x21,
|
||||
0x5d,
|
||||
0x5e,
|
||||
0x5f,
|
||||
0x60,
|
||||
0x61,
|
||||
0x62,
|
||||
0x63,
|
||||
0x64,
|
||||
0x65,
|
||||
0x66,
|
||||
0x67,
|
||||
0x68,
|
||||
0x69,
|
||||
0x6a,
|
||||
0x6b,
|
||||
0x6c,
|
||||
0x6d,
|
||||
0x6e,
|
||||
0x6f,
|
||||
0x70,
|
||||
0x71,
|
||||
0x72,
|
||||
0xfc,
|
||||
0x73,
|
||||
0xfd,
|
||||
0x1ffb,
|
||||
0x7fff0,
|
||||
0x1ffc,
|
||||
0x3ffc,
|
||||
0x22,
|
||||
0x7ffd,
|
||||
0x3,
|
||||
0x23,
|
||||
0x4,
|
||||
0x24,
|
||||
0x5,
|
||||
0x25,
|
||||
0x26,
|
||||
0x27,
|
||||
0x6,
|
||||
0x74,
|
||||
0x75,
|
||||
0x28,
|
||||
0x29,
|
||||
0x2a,
|
||||
0x7,
|
||||
0x2b,
|
||||
0x76,
|
||||
0x2c,
|
||||
0x8,
|
||||
0x9,
|
||||
0x2d,
|
||||
0x77,
|
||||
0x78,
|
||||
0x79,
|
||||
0x7a,
|
||||
0x7b,
|
||||
0x7ffe,
|
||||
0x7fc,
|
||||
0x3ffd,
|
||||
0x1ffd,
|
||||
0xffffffc,
|
||||
0xfffe6,
|
||||
0x3fffd2,
|
||||
0xfffe7,
|
||||
0xfffe8,
|
||||
0x3fffd3,
|
||||
0x3fffd4,
|
||||
0x3fffd5,
|
||||
0x7fffd9,
|
||||
0x3fffd6,
|
||||
0x7fffda,
|
||||
0x7fffdb,
|
||||
0x7fffdc,
|
||||
0x7fffdd,
|
||||
0x7fffde,
|
||||
0xffffeb,
|
||||
0x7fffdf,
|
||||
0xffffec,
|
||||
0xffffed,
|
||||
0x3fffd7,
|
||||
0x7fffe0,
|
||||
0xffffee,
|
||||
0x7fffe1,
|
||||
0x7fffe2,
|
||||
0x7fffe3,
|
||||
0x7fffe4,
|
||||
0x1fffdc,
|
||||
0x3fffd8,
|
||||
0x7fffe5,
|
||||
0x3fffd9,
|
||||
0x7fffe6,
|
||||
0x7fffe7,
|
||||
0xffffef,
|
||||
0x3fffda,
|
||||
0x1fffdd,
|
||||
0xfffe9,
|
||||
0x3fffdb,
|
||||
0x3fffdc,
|
||||
0x7fffe8,
|
||||
0x7fffe9,
|
||||
0x1fffde,
|
||||
0x7fffea,
|
||||
0x3fffdd,
|
||||
0x3fffde,
|
||||
0xfffff0,
|
||||
0x1fffdf,
|
||||
0x3fffdf,
|
||||
0x7fffeb,
|
||||
0x7fffec,
|
||||
0x1fffe0,
|
||||
0x1fffe1,
|
||||
0x3fffe0,
|
||||
0x1fffe2,
|
||||
0x7fffed,
|
||||
0x3fffe1,
|
||||
0x7fffee,
|
||||
0x7fffef,
|
||||
0xfffea,
|
||||
0x3fffe2,
|
||||
0x3fffe3,
|
||||
0x3fffe4,
|
||||
0x7ffff0,
|
||||
0x3fffe5,
|
||||
0x3fffe6,
|
||||
0x7ffff1,
|
||||
0x3ffffe0,
|
||||
0x3ffffe1,
|
||||
0xfffeb,
|
||||
0x7fff1,
|
||||
0x3fffe7,
|
||||
0x7ffff2,
|
||||
0x3fffe8,
|
||||
0x1ffffec,
|
||||
0x3ffffe2,
|
||||
0x3ffffe3,
|
||||
0x3ffffe4,
|
||||
0x7ffffde,
|
||||
0x7ffffdf,
|
||||
0x3ffffe5,
|
||||
0xfffff1,
|
||||
0x1ffffed,
|
||||
0x7fff2,
|
||||
0x1fffe3,
|
||||
0x3ffffe6,
|
||||
0x7ffffe0,
|
||||
0x7ffffe1,
|
||||
0x3ffffe7,
|
||||
0x7ffffe2,
|
||||
0xfffff2,
|
||||
0x1fffe4,
|
||||
0x1fffe5,
|
||||
0x3ffffe8,
|
||||
0x3ffffe9,
|
||||
0xffffffd,
|
||||
0x7ffffe3,
|
||||
0x7ffffe4,
|
||||
0x7ffffe5,
|
||||
0xfffec,
|
||||
0xfffff3,
|
||||
0xfffed,
|
||||
0x1fffe6,
|
||||
0x3fffe9,
|
||||
0x1fffe7,
|
||||
0x1fffe8,
|
||||
0x7ffff3,
|
||||
0x3fffea,
|
||||
0x3fffeb,
|
||||
0x1ffffee,
|
||||
0x1ffffef,
|
||||
0xfffff4,
|
||||
0xfffff5,
|
||||
0x3ffffea,
|
||||
0x7ffff4,
|
||||
0x3ffffeb,
|
||||
0x7ffffe6,
|
||||
0x3ffffec,
|
||||
0x3ffffed,
|
||||
0x7ffffe7,
|
||||
0x7ffffe8,
|
||||
0x7ffffe9,
|
||||
0x7ffffea,
|
||||
0x7ffffeb,
|
||||
0xffffffe,
|
||||
0x7ffffec,
|
||||
0x7ffffed,
|
||||
0x7ffffee,
|
||||
0x7ffffef,
|
||||
0x7fffff0,
|
||||
0x3ffffee,
|
||||
}
|
||||
|
||||
var huffmanCodeLen = [256]uint8{
|
||||
13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28,
|
||||
28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||
6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6,
|
||||
5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10,
|
||||
13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6,
|
||||
15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5,
|
||||
6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28,
|
||||
20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23,
|
||||
24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24,
|
||||
22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23,
|
||||
21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23,
|
||||
26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25,
|
||||
19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27,
|
||||
20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23,
|
||||
26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26,
|
||||
}
|
||||
391
vendor/golang.org/x/net/http2/http2.go
generated
vendored
391
vendor/golang.org/x/net/http2/http2.go
generated
vendored
|
|
@ -1,391 +0,0 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package http2 implements the HTTP/2 protocol.
|
||||
//
|
||||
// This package is low-level and intended to be used directly by very
|
||||
// few people. Most users will use it indirectly through the automatic
|
||||
// use by the net/http package (from Go 1.6 and later).
|
||||
// For use in earlier Go versions see ConfigureServer. (Transport support
|
||||
// requires Go 1.6 or later)
|
||||
//
|
||||
// See https://http2.github.io/ for more information on HTTP/2.
|
||||
//
|
||||
// See https://http2.golang.org/ for a test server running this code.
|
||||
//
|
||||
package http2 // import "golang.org/x/net/http2"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/lex/httplex"
|
||||
)
|
||||
|
||||
var (
|
||||
VerboseLogs bool
|
||||
logFrameWrites bool
|
||||
logFrameReads bool
|
||||
inTests bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
e := os.Getenv("GODEBUG")
|
||||
if strings.Contains(e, "http2debug=1") {
|
||||
VerboseLogs = true
|
||||
}
|
||||
if strings.Contains(e, "http2debug=2") {
|
||||
VerboseLogs = true
|
||||
logFrameWrites = true
|
||||
logFrameReads = true
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// ClientPreface is the string that must be sent by new
|
||||
// connections from clients.
|
||||
ClientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
||||
|
||||
// SETTINGS_MAX_FRAME_SIZE default
|
||||
// http://http2.github.io/http2-spec/#rfc.section.6.5.2
|
||||
initialMaxFrameSize = 16384
|
||||
|
||||
// NextProtoTLS is the NPN/ALPN protocol negotiated during
|
||||
// HTTP/2's TLS setup.
|
||||
NextProtoTLS = "h2"
|
||||
|
||||
// http://http2.github.io/http2-spec/#SettingValues
|
||||
initialHeaderTableSize = 4096
|
||||
|
||||
initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size
|
||||
|
||||
defaultMaxReadFrameSize = 1 << 20
|
||||
)
|
||||
|
||||
var (
|
||||
clientPreface = []byte(ClientPreface)
|
||||
)
|
||||
|
||||
type streamState int
|
||||
|
||||
// HTTP/2 stream states.
|
||||
//
|
||||
// See http://tools.ietf.org/html/rfc7540#section-5.1.
|
||||
//
|
||||
// For simplicity, the server code merges "reserved (local)" into
|
||||
// "half-closed (remote)". This is one less state transition to track.
|
||||
// The only downside is that we send PUSH_PROMISEs slightly less
|
||||
// liberally than allowable. More discussion here:
|
||||
// https://lists.w3.org/Archives/Public/ietf-http-wg/2016JulSep/0599.html
|
||||
//
|
||||
// "reserved (remote)" is omitted since the client code does not
|
||||
// support server push.
|
||||
const (
|
||||
stateIdle streamState = iota
|
||||
stateOpen
|
||||
stateHalfClosedLocal
|
||||
stateHalfClosedRemote
|
||||
stateClosed
|
||||
)
|
||||
|
||||
var stateName = [...]string{
|
||||
stateIdle: "Idle",
|
||||
stateOpen: "Open",
|
||||
stateHalfClosedLocal: "HalfClosedLocal",
|
||||
stateHalfClosedRemote: "HalfClosedRemote",
|
||||
stateClosed: "Closed",
|
||||
}
|
||||
|
||||
func (st streamState) String() string {
|
||||
return stateName[st]
|
||||
}
|
||||
|
||||
// Setting is a setting parameter: which setting it is, and its value.
|
||||
type Setting struct {
|
||||
// ID is which setting is being set.
|
||||
// See http://http2.github.io/http2-spec/#SettingValues
|
||||
ID SettingID
|
||||
|
||||
// Val is the value.
|
||||
Val uint32
|
||||
}
|
||||
|
||||
func (s Setting) String() string {
|
||||
return fmt.Sprintf("[%v = %d]", s.ID, s.Val)
|
||||
}
|
||||
|
||||
// Valid reports whether the setting is valid.
|
||||
func (s Setting) Valid() error {
|
||||
// Limits and error codes from 6.5.2 Defined SETTINGS Parameters
|
||||
switch s.ID {
|
||||
case SettingEnablePush:
|
||||
if s.Val != 1 && s.Val != 0 {
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
case SettingInitialWindowSize:
|
||||
if s.Val > 1<<31-1 {
|
||||
return ConnectionError(ErrCodeFlowControl)
|
||||
}
|
||||
case SettingMaxFrameSize:
|
||||
if s.Val < 16384 || s.Val > 1<<24-1 {
|
||||
return ConnectionError(ErrCodeProtocol)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A SettingID is an HTTP/2 setting as defined in
|
||||
// http://http2.github.io/http2-spec/#iana-settings
|
||||
type SettingID uint16
|
||||
|
||||
const (
|
||||
SettingHeaderTableSize SettingID = 0x1
|
||||
SettingEnablePush SettingID = 0x2
|
||||
SettingMaxConcurrentStreams SettingID = 0x3
|
||||
SettingInitialWindowSize SettingID = 0x4
|
||||
SettingMaxFrameSize SettingID = 0x5
|
||||
SettingMaxHeaderListSize SettingID = 0x6
|
||||
)
|
||||
|
||||
var settingName = map[SettingID]string{
|
||||
SettingHeaderTableSize: "HEADER_TABLE_SIZE",
|
||||
SettingEnablePush: "ENABLE_PUSH",
|
||||
SettingMaxConcurrentStreams: "MAX_CONCURRENT_STREAMS",
|
||||
SettingInitialWindowSize: "INITIAL_WINDOW_SIZE",
|
||||
SettingMaxFrameSize: "MAX_FRAME_SIZE",
|
||||
SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE",
|
||||
}
|
||||
|
||||
func (s SettingID) String() string {
|
||||
if v, ok := settingName[s]; ok {
|
||||
return v
|
||||
}
|
||||
return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s))
|
||||
}
|
||||
|
||||
var (
|
||||
errInvalidHeaderFieldName = errors.New("http2: invalid header field name")
|
||||
errInvalidHeaderFieldValue = errors.New("http2: invalid header field value")
|
||||
)
|
||||
|
||||
// validWireHeaderFieldName reports whether v is a valid header field
|
||||
// name (key). See httplex.ValidHeaderName for the base rules.
|
||||
//
|
||||
// Further, http2 says:
|
||||
// "Just as in HTTP/1.x, header field names are strings of ASCII
|
||||
// characters that are compared in a case-insensitive
|
||||
// fashion. However, header field names MUST be converted to
|
||||
// lowercase prior to their encoding in HTTP/2. "
|
||||
func validWireHeaderFieldName(v string) bool {
|
||||
if len(v) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, r := range v {
|
||||
if !httplex.IsTokenRune(r) {
|
||||
return false
|
||||
}
|
||||
if 'A' <= r && r <= 'Z' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var httpCodeStringCommon = map[int]string{} // n -> strconv.Itoa(n)
|
||||
|
||||
func init() {
|
||||
for i := 100; i <= 999; i++ {
|
||||
if v := http.StatusText(i); v != "" {
|
||||
httpCodeStringCommon[i] = strconv.Itoa(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func httpCodeString(code int) string {
|
||||
if s, ok := httpCodeStringCommon[code]; ok {
|
||||
return s
|
||||
}
|
||||
return strconv.Itoa(code)
|
||||
}
|
||||
|
||||
// from pkg io
|
||||
type stringWriter interface {
|
||||
WriteString(s string) (n int, err error)
|
||||
}
|
||||
|
||||
// A gate lets two goroutines coordinate their activities.
|
||||
type gate chan struct{}
|
||||
|
||||
func (g gate) Done() { g <- struct{}{} }
|
||||
func (g gate) Wait() { <-g }
|
||||
|
||||
// A closeWaiter is like a sync.WaitGroup but only goes 1 to 0 (open to closed).
|
||||
type closeWaiter chan struct{}
|
||||
|
||||
// Init makes a closeWaiter usable.
|
||||
// It exists because so a closeWaiter value can be placed inside a
|
||||
// larger struct and have the Mutex and Cond's memory in the same
|
||||
// allocation.
|
||||
func (cw *closeWaiter) Init() {
|
||||
*cw = make(chan struct{})
|
||||
}
|
||||
|
||||
// Close marks the closeWaiter as closed and unblocks any waiters.
|
||||
func (cw closeWaiter) Close() {
|
||||
close(cw)
|
||||
}
|
||||
|
||||
// Wait waits for the closeWaiter to become closed.
|
||||
func (cw closeWaiter) Wait() {
|
||||
<-cw
|
||||
}
|
||||
|
||||
// bufferedWriter is a buffered writer that writes to w.
|
||||
// Its buffered writer is lazily allocated as needed, to minimize
|
||||
// idle memory usage with many connections.
|
||||
type bufferedWriter struct {
|
||||
w io.Writer // immutable
|
||||
bw *bufio.Writer // non-nil when data is buffered
|
||||
}
|
||||
|
||||
func newBufferedWriter(w io.Writer) *bufferedWriter {
|
||||
return &bufferedWriter{w: w}
|
||||
}
|
||||
|
||||
// bufWriterPoolBufferSize is the size of bufio.Writer's
|
||||
// buffers created using bufWriterPool.
|
||||
//
|
||||
// TODO: pick a less arbitrary value? this is a bit under
|
||||
// (3 x typical 1500 byte MTU) at least. Other than that,
|
||||
// not much thought went into it.
|
||||
const bufWriterPoolBufferSize = 4 << 10
|
||||
|
||||
var bufWriterPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return bufio.NewWriterSize(nil, bufWriterPoolBufferSize)
|
||||
},
|
||||
}
|
||||
|
||||
func (w *bufferedWriter) Available() int {
|
||||
if w.bw == nil {
|
||||
return bufWriterPoolBufferSize
|
||||
}
|
||||
return w.bw.Available()
|
||||
}
|
||||
|
||||
func (w *bufferedWriter) Write(p []byte) (n int, err error) {
|
||||
if w.bw == nil {
|
||||
bw := bufWriterPool.Get().(*bufio.Writer)
|
||||
bw.Reset(w.w)
|
||||
w.bw = bw
|
||||
}
|
||||
return w.bw.Write(p)
|
||||
}
|
||||
|
||||
func (w *bufferedWriter) Flush() error {
|
||||
bw := w.bw
|
||||
if bw == nil {
|
||||
return nil
|
||||
}
|
||||
err := bw.Flush()
|
||||
bw.Reset(nil)
|
||||
bufWriterPool.Put(bw)
|
||||
w.bw = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func mustUint31(v int32) uint32 {
|
||||
if v < 0 || v > 2147483647 {
|
||||
panic("out of range")
|
||||
}
|
||||
return uint32(v)
|
||||
}
|
||||
|
||||
// bodyAllowedForStatus reports whether a given response status code
|
||||
// permits a body. See RFC 2616, section 4.4.
|
||||
func bodyAllowedForStatus(status int) bool {
|
||||
switch {
|
||||
case status >= 100 && status <= 199:
|
||||
return false
|
||||
case status == 204:
|
||||
return false
|
||||
case status == 304:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type httpError struct {
|
||||
msg string
|
||||
timeout bool
|
||||
}
|
||||
|
||||
func (e *httpError) Error() string { return e.msg }
|
||||
func (e *httpError) Timeout() bool { return e.timeout }
|
||||
func (e *httpError) Temporary() bool { return true }
|
||||
|
||||
var errTimeout error = &httpError{msg: "http2: timeout awaiting response headers", timeout: true}
|
||||
|
||||
type connectionStater interface {
|
||||
ConnectionState() tls.ConnectionState
|
||||
}
|
||||
|
||||
var sorterPool = sync.Pool{New: func() interface{} { return new(sorter) }}
|
||||
|
||||
type sorter struct {
|
||||
v []string // owned by sorter
|
||||
}
|
||||
|
||||
func (s *sorter) Len() int { return len(s.v) }
|
||||
func (s *sorter) Swap(i, j int) { s.v[i], s.v[j] = s.v[j], s.v[i] }
|
||||
func (s *sorter) Less(i, j int) bool { return s.v[i] < s.v[j] }
|
||||
|
||||
// Keys returns the sorted keys of h.
|
||||
//
|
||||
// The returned slice is only valid until s used again or returned to
|
||||
// its pool.
|
||||
func (s *sorter) Keys(h http.Header) []string {
|
||||
keys := s.v[:0]
|
||||
for k := range h {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
s.v = keys
|
||||
sort.Sort(s)
|
||||
return keys
|
||||
}
|
||||
|
||||
func (s *sorter) SortStrings(ss []string) {
|
||||
// Our sorter works on s.v, which sorter owns, so
|
||||
// stash it away while we sort the user's buffer.
|
||||
save := s.v
|
||||
s.v = ss
|
||||
sort.Sort(s)
|
||||
s.v = save
|
||||
}
|
||||
|
||||
// validPseudoPath reports whether v is a valid :path pseudo-header
|
||||
// value. It must be either:
|
||||
//
|
||||
// *) a non-empty string starting with '/'
|
||||
// *) the string '*', for OPTIONS requests.
|
||||
//
|
||||
// For now this is only used a quick check for deciding when to clean
|
||||
// up Opaque URLs before sending requests from the Transport.
|
||||
// See golang.org/issue/16847
|
||||
//
|
||||
// We used to enforce that the path also didn't start with "//", but
|
||||
// Google's GFE accepts such paths and Chrome sends them, so ignore
|
||||
// that part of the spec. See golang.org/issue/19103.
|
||||
func validPseudoPath(v string) bool {
|
||||
return (len(v) > 0 && v[0] == '/') || v == "*"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue