1
0
mirror of https://github.com/beego/bee.git synced 2024-11-22 05:00:54 +00:00

Merge pull request #361 from beego/develop

bee 1.8.0
This commit is contained in:
astaxie 2017-03-06 21:59:08 +08:00 committed by GitHub
commit 932f16ba4e
29 changed files with 2784 additions and 177 deletions

1
.gitignore vendored
View File

@ -7,6 +7,7 @@
_obj _obj
_test _test
.idea .idea
.vscode
# Architecture specific extensions/prefixes # Architecture specific extensions/prefixes
*.[568vq] *.[568vq]

View File

@ -1,6 +1,5 @@
language: go language: go
go: go:
- 1.5.3 - 1.6.3
- 1.4.3 - 1.7.3
- 1.3.3

View File

@ -13,3 +13,4 @@ cmd_args: []
envs: [] envs: []
database: database:
driver: "mysql" driver: "mysql"
enable_reload: false

242
README.md
View File

@ -32,18 +32,21 @@ go get -u github.com/beego/bee
## Basic commands ## Basic commands
Bee provides a variety of commands which can be helpful at various stages of development. The top level commands include: Bee provides a variety of commands which can be helpful at various stages of development. The top level commands include:
``` ```
new Create a Beego application new Creates a Beego application
run Run the app and start a Web server for development run Run the application by starting a local development server
pack Compress a beego project into a single file pack Compresses a Beego application into a single file
api Create an API beego application api Creates a Beego API application
hprose Create an rpc application use hprose base on beego framework hprose Creates an RPC application based on Hprose and Beego frameworks
bale Packs non-Go files to Go source files bale Transforms non-Go files to Go source files
version Prints the current Bee version version Prints the current Bee version
generate Source code generator generate Source code generator
migrate Run database migrations migrate Runs database migrations
fix Fix the Beego application to make it compatible with Beego 1.6 fix Fixes your application by making it compatible with newer versions of Beego
dockerize Generates a Dockerfile for your Beego application
``` ```
### bee version ### bee version
To display the current version of `bee`, `beego` and `go` installed on your machine: To display the current version of `bee`, `beego` and `go` installed on your machine:
@ -55,19 +58,38 @@ ______
| |_/ / ___ ___ | |_/ / ___ ___
| ___ \ / _ \ / _ \ | ___ \ / _ \ / _ \
| |_/ /| __/| __/ | |_/ /| __/| __/
\____/ \___| \___| v1.5.0 \____/ \___| \___| v1.6.2
├── Beego : 1.7.0 ├── Beego : 1.7.2
├── GoVersion : go1.6.2 ├── GoVersion : go1.7.4
├── GOOS : windows ├── GOOS : linux
├── GOARCH : amd64 ├── GOARCH : amd64
├── NumCPU : 4 ├── NumCPU : 2
├── GOPATH : C:\Users\beeuser\go ├── GOPATH : /home/beeuser/.go
├── GOROOT : C:\go ├── GOROOT : /usr/lib/go
├── Compiler : gc ├── Compiler : gc
└── Date : Monday, 22 Aug 2016 └── Date : Monday, 26 Dec 2016
``` ```
You can also change the output format using `-o` flag:
```bash
$ bee version -o json
{
"GoVersion": "go1.7.4",
"GOOS": "linux",
"GOARCH": "amd64",
"NumCPU": 2,
"GOPATH": "/home/beeuser/.go",
"GOROOT": "/usr/lib/go",
"Compiler": "gc",
"BeeVersion": "1.6.2",
"BeegoVersion": "1.7.2"
}
```
For more information on the usage, run `bee help version`.
### bee new ### bee new
To create a new Beego web application: To create a new Beego web application:
@ -79,26 +101,26 @@ ______
| |_/ / ___ ___ | |_/ / ___ ___
| ___ \ / _ \ / _ \ | ___ \ / _ \ / _ \
| |_/ /| __/| __/ | |_/ /| __/| __/
\____/ \___| \___| v1.5.0 \____/ \___| \___| v1.6.2
2016/08/22 14:53:45 [INFO] Creating application... 2016/12/26 22:28:11 INFO ▶ 0001 Creating application...
create C:\Users\beeuser\go\src\github.com\user\my-web-app\ create /home/beeuser/.go/src/github.com/user/my-web-app/
create C:\Users\beeuser\go\src\github.com\user\my-web-app\conf\ create /home/beeuser/.go/src/github.com/user/my-web-app/conf/
create C:\Users\beeuser\go\src\github.com\user\my-web-app\controllers\ create /home/beeuser/.go/src/github.com/user/my-web-app/controllers/
create C:\Users\beeuser\go\src\github.com\user\my-web-app\models\ create /home/beeuser/.go/src/github.com/user/my-web-app/models/
create C:\Users\beeuser\go\src\github.com\user\my-web-app\routers\ create /home/beeuser/.go/src/github.com/user/my-web-app/routers/
create C:\Users\beeuser\go\src\github.com\user\my-web-app\tests\ create /home/beeuser/.go/src/github.com/user/my-web-app/tests/
create C:\Users\beeuser\go\src\github.com\user\my-web-app\static\ create /home/beeuser/.go/src/github.com/user/my-web-app/static/
create C:\Users\beeuser\go\src\github.com\user\my-web-app\static\js\ create /home/beeuser/.go/src/github.com/user/my-web-app/static/js/
create C:\Users\beeuser\go\src\github.com\user\my-web-app\static\css\ create /home/beeuser/.go/src/github.com/user/my-web-app/static/css/
create C:\Users\beeuser\go\src\github.com\user\my-web-app\static\img\ create /home/beeuser/.go/src/github.com/user/my-web-app/static/img/
create C:\Users\beeuser\go\src\github.com\user\my-web-app\views\ create /home/beeuser/.go/src/github.com/user/my-web-app/views/
create C:\Users\beeuser\go\src\github.com\user\my-web-app\conf\app.conf create /home/beeuser/.go/src/github.com/user/my-web-app/conf/app.conf
create C:\Users\beeuser\go\src\github.com\user\my-web-app\controllers\default.go create /home/beeuser/.go/src/github.com/user/my-web-app/controllers/default.go
create C:\Users\beeuser\go\src\github.com\user\my-web-app\views\index.tpl create /home/beeuser/.go/src/github.com/user/my-web-app/views/index.tpl
create C:\Users\beeuser\go\src\github.com\user\my-web-app\routers\router.go create /home/beeuser/.go/src/github.com/user/my-web-app/routers/router.go
create C:\Users\beeuser\go\src\github.com\user\my-web-app\tests\default_test.go create /home/beeuser/.go/src/github.com/user/my-web-app/tests/default_test.go
create C:\Users\beeuser\go\src\github.com\user\my-web-app\main.go create /home/beeuser/.go/src/github.com/user/my-web-app/main.go
2016/08/22 14:53:45 [SUCC] New application successfully created! 2016/12/26 22:28:11 SUCCESS ▶ 0002 New application successfully created!
``` ```
For more information on the usage, run `bee help new`. For more information on the usage, run `bee help new`.
@ -130,14 +152,15 @@ ______
| |_/ / ___ ___ | |_/ / ___ ___
| ___ \ / _ \ / _ \ | ___ \ / _ \ / _ \
| |_/ /| __/| __/ | |_/ /| __/| __/
\____/ \___| \___| v1.5.0 \____/ \___| \___| v1.6.2
2016/08/22 15:11:01 Packaging application: C:\Users\beeuser\go\src\github.com\user\my-web-app 2016/12/26 22:29:29 INFO ▶ 0001 Packaging application on '/home/beeuser/.go/src/github.com/user/my-web-app'...
2016/08/22 15:11:01 Building application... 2016/12/26 22:29:29 INFO ▶ 0002 Building application...
2016/08/22 15:11:01 Env: GOOS=windows GOARCH=amd64 2016/12/26 22:29:29 INFO ▶ 0003 Using: GOOS=linux GOARCH=amd64
2016/08/22 15:11:08 Build successful 2016/12/26 22:29:31 SUCCESS ▶ 0004 Build Successful!
2016/08/22 15:11:08 Excluding relpath prefix: . 2016/12/26 22:29:31 INFO ▶ 0005 Writing to output: /home/beeuser/.go/src/github.com/user/my-web-app/my-web-app.tar.gz
2016/08/22 15:11:08 Excluding relpath suffix: .go:.DS_Store:.tmp 2016/12/26 22:29:31 INFO ▶ 0006 Excluding relpath prefix: .
2016/08/22 15:11:10 Writing to output: `C:\Users\beeuser\go\src\github.com\user\my-web-app\my-web-app.tar.gz` 2016/12/26 22:29:31 INFO ▶ 0007 Excluding relpath suffix: .go:.DS_Store:.tmp
2016/12/26 22:29:32 SUCCESS ▶ 0008 Application packed!
``` ```
For more information on the usage, run `bee help pack`. For more information on the usage, run `bee help pack`.
@ -153,23 +176,23 @@ ______
| |_/ / ___ ___ | |_/ / ___ ___
| ___ \ / _ \ / _ \ | ___ \ / _ \ / _ \
| |_/ /| __/| __/ | |_/ /| __/| __/
\____/ \___| \___| v1.5.0 \____/ \___| \___| v1.6.2
2016/08/22 15:14:10 [INFO] Creating API... 2016/12/26 22:30:12 INFO ▶ 0001 Creating API...
create C:\Users\beeuser\go\src\github.com\user\my-api create /home/beeuser/.go/src/github.com/user/my-api
create C:\Users\beeuser\go\src\github.com\user\my-api\conf create /home/beeuser/.go/src/github.com/user/my-api/conf
create C:\Users\beeuser\go\src\github.com\user\my-api\controllers create /home/beeuser/.go/src/github.com/user/my-api/controllers
create C:\Users\beeuser\go\src\github.com\user\my-api\tests create /home/beeuser/.go/src/github.com/user/my-api/tests
create C:\Users\beeuser\go\src\github.com\user\my-api\conf\app.conf create /home/beeuser/.go/src/github.com/user/my-api/conf/app.conf
create C:\Users\beeuser\go\src\github.com\user\my-api\models create /home/beeuser/.go/src/github.com/user/my-api/models
create C:\Users\beeuser\go\src\github.com\user\my-api\routers\ create /home/beeuser/.go/src/github.com/user/my-api/routers/
create C:\Users\beeuser\go\src\github.com\user\my-api\controllers\object.go create /home/beeuser/.go/src/github.com/user/my-api/controllers/object.go
create C:\Users\beeuser\go\src\github.com\user\my-api\controllers\user.go create /home/beeuser/.go/src/github.com/user/my-api/controllers/user.go
create C:\Users\beeuser\go\src\github.com\user\my-api\tests\default_test.go create /home/beeuser/.go/src/github.com/user/my-api/tests/default_test.go
create C:\Users\beeuser\go\src\github.com\user\my-api\routers\router.go create /home/beeuser/.go/src/github.com/user/my-api/routers/router.go
create C:\Users\beeuser\go\src\github.com\user\my-api\models\object.go create /home/beeuser/.go/src/github.com/user/my-api/models/object.go
create C:\Users\beeuser\go\src\github.com\user\my-api\models\user.go create /home/beeuser/.go/src/github.com/user/my-api/models/user.go
create C:\Users\beeuser\go\src\github.com\user\my-api\main.go create /home/beeuser/.go/src/github.com/user/my-api/main.go
2016/08/22 15:14:10 [SUCC] New API successfully created! 2016/12/26 22:30:12 SUCCESS ▶ 0002 New API successfully created!
``` ```
For more information on the usage, run `bee help api`. For more information on the usage, run `bee help api`.
@ -185,16 +208,26 @@ ______
| |_/ / ___ ___ | |_/ / ___ ___
| ___ \ / _ \ / _ \ | ___ \ / _ \ / _ \
| |_/ /| __/| __/ | |_/ /| __/| __/
\____/ \___| \___| v1.5.0 \____/ \___| \___| v1.6.2
2016/08/22 16:09:13 [INFO] Creating Hprose application... 2016/12/26 22:30:58 INFO ▶ 0001 Creating application...
create C:\Users\beeuser\go\src\github.com\user\my-rpc-app create /home/beeuser/.go/src/github.com/user/my-rpc-app/
create C:\Users\beeuser\go\src\github.com\user\my-rpc-app\conf create /home/beeuser/.go/src/github.com/user/my-rpc-app/conf/
create C:\Users\beeuser\go\src\github.com\user\my-rpc-app\conf\app.conf create /home/beeuser/.go/src/github.com/user/my-rpc-app/controllers/
create C:\Users\beeuser\go\src\github.com\user\my-rpc-app\models create /home/beeuser/.go/src/github.com/user/my-rpc-app/models/
create C:\Users\beeuser\go\src\github.com\user\my-rpc-app\models\object.go create /home/beeuser/.go/src/github.com/user/my-rpc-app/routers/
create C:\Users\beeuser\go\src\github.com\user\my-rpc-app\models\user.go create /home/beeuser/.go/src/github.com/user/my-rpc-app/tests/
create C:\Users\beeuser\go\src\github.com\user\my-rpc-app\main.go create /home/beeuser/.go/src/github.com/user/my-rpc-app/static/
2016/08/22 16:09:13 [SUCC] New Hprose application successfully created! create /home/beeuser/.go/src/github.com/user/my-rpc-app/static/js/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/static/css/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/static/img/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/views/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/conf/app.conf
create /home/beeuser/.go/src/github.com/user/my-rpc-app/controllers/default.go
create /home/beeuser/.go/src/github.com/user/my-rpc-app/views/index.tpl
create /home/beeuser/.go/src/github.com/user/my-rpc-app/routers/router.go
create /home/beeuser/.go/src/github.com/user/my-rpc-app/tests/default_test.go
create /home/beeuser/.go/src/github.com/user/my-rpc-app/main.go
2016/12/26 22:30:58 SUCCESS ▶ 0002 New application successfully created!
``` ```
For more information on the usage, run `bee help hprose`. For more information on the usage, run `bee help hprose`.
@ -210,11 +243,9 @@ ______
| |_/ / ___ ___ | |_/ / ___ ___
| ___ \ / _ \ / _ \ | ___ \ / _ \ / _ \
| |_/ /| __/| __/ | |_/ /| __/| __/
\____/ \___| \___| v1.5.0 \____/ \___| \___| v1.6.2
2016/08/22 16:37:24 [INFO] Detected bee.json 2016/12/26 22:32:41 INFO ▶ 0001 Loading configuration from 'bee.json'...
2016/08/22 16:37:24 [INFO] Packaging directory(static/js) 2016/12/26 22:32:41 SUCCESS ▶ 0002 Baled resources successfully!
2016/08/22 16:37:24 [INFO] Packaging directory(static/css)
2016/08/22 16:37:24 [SUCC] Baled resources successfully!
``` ```
For more information on the usage, run `bee help bale`. For more information on the usage, run `bee help bale`.
@ -238,15 +269,35 @@ ______
| |_/ / ___ ___ | |_/ / ___ ___
| ___ \ / _ \ / _ \ | ___ \ / _ \ / _ \
| |_/ /| __/| __/ | |_/ /| __/| __/
\____/ \___| \___| v1.5.0 \____/ \___| \___| v1.6.2
2016/08/22 16:55:30 [INFO] Using 'Hello' as controller name 2016/12/26 22:33:58 INFO ▶ 0001 Using 'Hello' as controller name
2016/08/22 16:55:30 [INFO] Using 'controllers' as package name 2016/12/26 22:33:58 INFO ▶ 0002 Using 'controllers' as package name
create C:\Users\beeuser\go\src\github.com\user\my-web-app/controllers/hello.go create /home/beeuser/.go/src/github.com/user/my-web-app/controllers/hello.go
2016/08/22 16:55:30 [SUCC] Controller successfully generated! 2016/12/26 22:33:58 SUCCESS ▶ 0003 Controller successfully generated!
``` ```
For more information on the usage, run `bee help generate`. For more information on the usage, run `bee help generate`.
### bee dockerize
Bee also helps you dockerize your Beego application by generating a Dockerfile.
For example, to generate a Dockerfile with `Go version 1.6.4` and exposing port `9000`:
```bash
$ bee dockerize -image="library/golang:1.6.4" -expose=9000
______
| ___ \
| |_/ / ___ ___
| ___ \ / _ \ / _ \
| |_/ /| __/| __/
\____/ \___| \___| v1.6.2
2016/12/26 22:34:54 INFO ▶ 0001 Generating Dockerfile...
2016/12/26 22:34:54 SUCCESS ▶ 0002 Dockerfile generated.
```
For more information on the usage, run `bee help dockerize`.
## Shortcuts ## Shortcuts
Because you'll likely type these generator commands over and over, it makes sense to create aliases: Because you'll likely type these generator commands over and over, it makes sense to create aliases:
@ -270,10 +321,33 @@ For instance, to get more information about the `run` command:
```bash ```bash
$ bee help run $ bee help run
usage: bee run [appname] [watchall] [-main=*.go] [-downdoc=true] [-gendoc=true] [-vendor=true] [-e=folderToExclude] [-tags=goBuildTags] USAGE
bee run [appname] [watchall] [-main=*.go] [-downdoc=true] [-gendoc=true] [-vendor=true] [-e=folderToExclude] [-tags=goBuildTags] [-runmode=BEEGO_RUNMODE]
Run command will supervise the file system of the beego project using inotify, OPTIONS
it will recompile and restart the app after any modifications. -downdoc
Enable auto-download of the swagger file if it does not exist.
-e=[]
List of paths to exclude.
-gendoc
Enable auto-generate the docs.
-main=[]
Specify main go files.
-runmode
Set the Beego run mode.
-tags
Set the build tags. See: https://golang.org/pkg/go/build/
-vendor=false
Enable watch vendor folder.
DESCRIPTION
Run command will supervise the filesystem of the application for any changes, and recompile/restart it.
``` ```
## Contributing ## Contributing

View File

@ -8,7 +8,8 @@ import (
"text/template" "text/template"
) )
type vars struct { // RuntimeInfo holds information about the current runtime.
type RuntimeInfo struct {
GoVersion string GoVersion string
GOOS string GOOS string
GOARCH string GOARCH string
@ -45,7 +46,7 @@ func show(out io.Writer, content string) {
logger.Fatalf("Cannot parse the banner template: %s", err) logger.Fatalf("Cannot parse the banner template: %s", err)
} }
err = t.Execute(out, vars{ err = t.Execute(out, RuntimeInfo{
getGoVersion(), getGoVersion(),
runtime.GOOS, runtime.GOOS,
runtime.GOARCH, runtime.GOARCH,

14
bee.go
View File

@ -25,7 +25,7 @@ import (
"text/template" "text/template"
) )
const version = "1.6.2" const version = "1.8.0"
// Command is the unit of execution // Command is the unit of execution
type Command struct { type Command struct {
@ -99,12 +99,7 @@ func (c *Command) Options() map[string]string {
c.Flag.VisitAll(func(f *flag.Flag) { c.Flag.VisitAll(func(f *flag.Flag) {
defaultVal := f.DefValue defaultVal := f.DefValue
if len(defaultVal) > 0 { if len(defaultVal) > 0 {
if strings.Contains(defaultVal, ":") { options[f.Name+"="+defaultVal] = f.Usage
// Truncate the flag's default value by appending '...' at the end
options[f.Name+"="+strings.Split(defaultVal, ":")[0]+":..."] = f.Usage
} else {
options[f.Name+"="+defaultVal] = f.Usage
}
} else { } else {
options[f.Name] = f.Usage options[f.Name] = f.Usage
} }
@ -126,6 +121,7 @@ var availableCommands = []*Command{
//cmdRundocs, //cmdRundocs,
cmdMigrate, cmdMigrate,
cmdFix, cmdFix,
cmdDockerize,
} }
var logger = GetBeeLogger(os.Stdout) var logger = GetBeeLogger(os.Stdout)
@ -209,7 +205,9 @@ Use {{"bee help [topic]" | bold}} for more information about that topic.
var helpTemplate = `{{"USAGE" | headline}} var helpTemplate = `{{"USAGE" | headline}}
{{.UsageLine | printf "bee %s" | bold}} {{.UsageLine | printf "bee %s" | bold}}
{{if .Options}}{{endline}}{{"OPTIONS" | headline}}{{range $k,$v := .Options}} {{if .Options}}{{endline}}{{"OPTIONS" | headline}}{{range $k,$v := .Options}}
{{$k | printf "-%-12s" | bold}} {{$v}}{{end}}{{endline}}{{end}} {{$k | printf "-%s" | bold}}
{{$v}}
{{end}}{{end}}
{{"DESCRIPTION" | headline}} {{"DESCRIPTION" | headline}}
{{tmpltostr .Long . | trim}} {{tmpltostr .Long . | trim}}
` `

View File

@ -16,5 +16,6 @@
"envs": [], "envs": [],
"database": { "database": {
"driver": "mysql" "driver": "mysql"
} },
"enable_reload": false
} }

12
conf.go
View File

@ -45,7 +45,8 @@ var defaultConf = `{
"envs": [], "envs": [],
"database": { "database": {
"driver": "mysql" "driver": "mysql"
} },
"enable_reload": false
} }
` `
var conf struct { var conf struct {
@ -75,6 +76,7 @@ var conf struct {
Driver string Driver string
Conn string Conn string
} }
EnableReload bool `json:"enable_reload" yaml:"enable_reload"`
} }
// loadConfig loads customized configuration. // loadConfig loads customized configuration.
@ -90,7 +92,7 @@ func loadConfig() (err error) {
if fileInfo.Name() == "bee.json" { if fileInfo.Name() == "bee.json" {
logger.Info("Loading configuration from 'bee.json'...") logger.Info("Loading configuration from 'bee.json'...")
err = parseJSON(path, conf) err = parseJSON(path, &conf)
if err != nil { if err != nil {
logger.Errorf("Failed to parse JSON file: %s", err) logger.Errorf("Failed to parse JSON file: %s", err)
return err return err
@ -100,7 +102,7 @@ func loadConfig() (err error) {
if fileInfo.Name() == "Beefile" { if fileInfo.Name() == "Beefile" {
logger.Info("Loading configuration from 'Beefile'...") logger.Info("Loading configuration from 'Beefile'...")
err = parseYAML(path, conf) err = parseYAML(path, &conf)
if err != nil { if err != nil {
logger.Errorf("Failed to parse YAML file: %s", err) logger.Errorf("Failed to parse YAML file: %s", err)
return err return err
@ -151,7 +153,7 @@ func parseJSON(path string, v interface{}) error {
if err != nil { if err != nil {
return err return err
} }
err = json.Unmarshal(data, &v) err = json.Unmarshal(data, v)
if err != nil { if err != nil {
return err return err
} }
@ -167,7 +169,7 @@ func parseYAML(path string, v interface{}) error {
if err != nil { if err != nil {
return err return err
} }
err = yaml.Unmarshal(data, &v) err = yaml.Unmarshal(data, v)
if err != nil { if err != nil {
return err return err
} }

126
dockerize.go Normal file
View File

@ -0,0 +1,126 @@
// Copyright 2016 bee authors
//
// 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 main
import (
"flag"
"os"
"path"
"path/filepath"
"strings"
"text/template"
)
var cmdDockerize = &Command{
CustomFlags: true,
UsageLine: "dockerize",
Short: "Generates a Dockerfile for your Beego application",
Long: `Dockerize generates a Dockerfile for your Beego Web Application.
The Dockerfile will compile, get the dependencies with {{"godep"|bold}}, and set the entrypoint.
{{"Example:"|bold}}
$ bee dockerize -expose="3000,80,25"
`,
PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() },
Run: dockerizeApp,
}
const dockerBuildTemplate = `FROM {{.BaseImage}}
# Godep for vendoring
RUN go get github.com/tools/godep
# Recompile the standard library without CGO
RUN CGO_ENABLED=0 go install -a std
ENV APP_DIR $GOPATH{{.Appdir}}
RUN mkdir -p $APP_DIR
# Set the entrypoint
ENTRYPOINT $APP_DIR/{{.Entrypoint}}
ADD . $APP_DIR
# Compile the binary and statically link
RUN cd $APP_DIR
RUN CGO_ENABLED=0 godep go build -ldflags '-d -w -s'
EXPOSE {{.Expose}}
`
// Dockerfile holds the information about the Docker container.
type Dockerfile struct {
BaseImage string
Appdir string
Entrypoint string
Expose string
}
var (
expose string
baseImage string
)
func init() {
fs := flag.NewFlagSet("dockerize", flag.ContinueOnError)
fs.StringVar(&baseImage, "image", "library/golang", "Set the base image of the Docker container.")
fs.StringVar(&expose, "expose", "8080", "Port(s) to expose in the Docker container.")
cmdDockerize.Flag = *fs
}
func dockerizeApp(cmd *Command, args []string) int {
if err := cmd.Flag.Parse(args); err != nil {
logger.Fatalf("Error parsing flags: %v", err.Error())
}
logger.Info("Generating Dockerfile...")
gopath := os.Getenv("GOPATH")
dir, err := filepath.Abs(".")
MustCheck(err)
appdir := strings.Replace(dir, gopath, "", 1)
// In case of multiple ports to expose inside the container,
// replace all the commas with whitespaces.
// See the verb EXPOSE in the Docker documentation.
if strings.Contains(expose, ",") {
expose = strings.Replace(expose, ",", " ", -1)
}
_, entrypoint := path.Split(appdir)
dockerfile := Dockerfile{
BaseImage: baseImage,
Appdir: appdir,
Entrypoint: entrypoint,
Expose: expose,
}
generateDockerfile(dockerfile)
return 0
}
func generateDockerfile(df Dockerfile) {
t := template.Must(template.New("dockerBuildTemplate").Parse(dockerBuildTemplate)).Funcs(BeeFuncMap())
f, err := os.Create("Dockerfile")
if err != nil {
logger.Fatalf("Error writing Dockerfile: %v", err.Error())
}
defer CloseFile(f)
t.Execute(f, df)
logger.Success("Dockerfile generated.")
}

View File

@ -165,7 +165,7 @@ import (
"github.com/astaxie/beego" "github.com/astaxie/beego"
) )
// {{controllerName}}Controller oprations for {{controllerName}} // {{controllerName}}Controller operations for {{controllerName}}
type {{controllerName}}Controller struct { type {{controllerName}}Controller struct {
beego.Controller beego.Controller
} }

View File

@ -26,6 +26,7 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"regexp" "regexp"
"runtime"
"strconv" "strconv"
"strings" "strings"
"unicode" "unicode"
@ -53,25 +54,31 @@ var astPkgs map[string]*ast.Package
// refer to builtin.go // refer to builtin.go
var basicTypes = map[string]string{ var basicTypes = map[string]string{
"bool": "boolean:", "bool": "boolean:",
"uint": "integer:int32", "uint": "integer:int32",
"uint8": "integer:int32", "uint8": "integer:int32",
"uint16": "integer:int32", "uint16": "integer:int32",
"uint32": "integer:int32", "uint32": "integer:int32",
"uint64": "integer:int64", "uint64": "integer:int64",
"int": "integer:int64", "int": "integer:int64",
"int8": "integer:int32", "int8": "integer:int32",
"int16:int32": "integer:int32", "int16": "integer:int32",
"int32": "integer:int32", "int32": "integer:int32",
"int64": "integer:int64", "int64": "integer:int64",
"uintptr": "integer:int64", "uintptr": "integer:int64",
"float32": "number:float", "float32": "number:float",
"float64": "number:double", "float64": "number:double",
"string": "string:", "string": "string:",
"complex64": "number:float", "complex64": "number:float",
"complex128": "number:double", "complex128": "number:double",
"byte": "string:byte", "byte": "string:byte",
"rune": "string:byte", "rune": "string:byte",
// builtin golang objects
"time.Time": "string:string",
}
var stdlibObject = map[string]string{
"&{time Time}": "time.Time",
} }
func init() { func init() {
@ -402,6 +409,9 @@ func analyseControllerPkg(localName, pkgpath string) {
func isSystemPackage(pkgpath string) bool { func isSystemPackage(pkgpath string) bool {
goroot := os.Getenv("GOROOT") goroot := os.Getenv("GOROOT")
if goroot == "" {
goroot = runtime.GOROOT()
}
if goroot == "" { if goroot == "" {
logger.Fatalf("GOROOT environment variable is not set or empty") logger.Fatalf("GOROOT environment variable is not set or empty")
} }
@ -872,10 +882,14 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) {
} }
return false, val, "object" return false, val, "object"
} }
if k, ok := basicTypes[fmt.Sprint(f.Type)]; ok { basicType := fmt.Sprint(f.Type)
return false, fmt.Sprint(f.Type), k if object, isStdLibObject := stdlibObject[basicType]; isStdLibObject {
basicType = object
} }
return false, fmt.Sprint(f.Type), "object" if k, ok := basicTypes[basicType]; ok {
return false, basicType, k
}
return false, basicType, "object"
} }
func isBasicType(Type string) bool { func isBasicType(Type string) bool {

View File

@ -300,6 +300,10 @@ func init() {
func createhprose(cmd *Command, args []string) int { func createhprose(cmd *Command, args []string) int {
output := cmd.Out() output := cmd.Out()
if len(args) != 1 {
logger.Fatal("Argument [appname] is missing")
}
curpath, _ := os.Getwd() curpath, _ := os.Getwd()
if len(args) > 1 { if len(args) > 1 {
cmd.Flag.Parse(args[1:]) cmd.Flag.Parse(args[1:])

6
new.go
View File

@ -87,6 +87,7 @@ func createApp(cmd *Command, args []string) int {
os.Mkdir(path.Join(apppath, "static"), 0755) os.Mkdir(path.Join(apppath, "static"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static")+string(path.Separator), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "static", "js"), 0755) os.Mkdir(path.Join(apppath, "static", "js"), 0755)
WriteToFile(path.Join(apppath, "static", "js", "reload.min.js"), reloadJsClient)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "js")+string(path.Separator), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "js")+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(apppath, "static", "css"), 0755) os.Mkdir(path.Join(apppath, "static", "css"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "css")+string(path.Separator), "\x1b[0m") fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "static", "css")+string(path.Separator), "\x1b[0m")
@ -294,6 +295,11 @@ var indextpl = `<!DOCTYPE html>
</div> </div>
</footer> </footer>
<div class="backdrop"></div> <div class="backdrop"></div>
<script src="/static/js/reload.min.js"></script>
</body> </body>
</html> </html>
` `
var reloadJsClient = `function b(a){var c=new WebSocket(a);c.onclose=function(){setTimeout(function(){b(a)},2E3)};c.onmessage=function(){location.reload()}}try{if(window.WebSocket)try{b("ws://localhost:12450/reload")}catch(a){console.error(a)}else console.log("Your browser does not support WebSockets.")}catch(a){console.error("Exception during connecting to Reload:",a)};
`

191
reload.go Normal file
View File

@ -0,0 +1,191 @@
// Copyright 2017 bee authors
//
// 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 main
import (
"bytes"
"net/http"
"time"
"github.com/gorilla/websocket"
)
// wsBroker maintains the set of active clients and broadcasts messages to the clients.
type wsBroker struct {
clients map[*wsClient]bool // Registered clients.
broadcast chan []byte // Inbound messages from the clients.
register chan *wsClient // Register requests from the clients.
unregister chan *wsClient // Unregister requests from clients.
}
func (br *wsBroker) run() {
for {
select {
case client := <-br.register:
br.clients[client] = true
case client := <-br.unregister:
if _, ok := br.clients[client]; ok {
delete(br.clients, client)
close(client.send)
}
case message := <-br.broadcast:
for client := range br.clients {
select {
case client.send <- message:
default:
close(client.send)
delete(br.clients, client)
}
}
}
}
}
// wsClient represents the end-client.
type wsClient struct {
broker *wsBroker // The broker.
conn *websocket.Conn // The websocket connection.
send chan []byte // Buffered channel of outbound messages.
}
// readPump pumps messages from the websocket connection to the broker.
func (c *wsClient) readPump() {
defer func() {
c.broker.unregister <- c
c.conn.Close()
}()
for {
_, _, err := c.conn.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
logger.Errorf("An error happened when reading from the Websocket client: %v", err)
}
break
}
}
}
// write writes a message with the given message type and payload.
func (c *wsClient) write(mt int, payload []byte) error {
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
return c.conn.WriteMessage(mt, payload)
}
// writePump pumps messages from the broker to the websocket connection.
func (c *wsClient) writePump() {
ticker := time.NewTicker(pingPeriod)
defer func() {
ticker.Stop()
c.conn.Close()
}()
for {
select {
case message, ok := <-c.send:
if !ok {
// The broker closed the channel.
c.write(websocket.CloseMessage, []byte{})
return
}
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
w, err := c.conn.NextWriter(websocket.TextMessage)
if err != nil {
return
}
w.Write(message)
n := len(c.send)
for i := 0; i < n; i++ {
w.Write(newline)
w.Write(<-c.send)
}
if err := w.Close(); err != nil {
return
}
case <-ticker.C:
if err := c.write(websocket.PingMessage, []byte{}); err != nil {
return
}
}
}
}
var (
broker *wsBroker // The broker.
reloadAddress = ":12450" // The port on which the reload server will listen to.
upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool { return true },
}
)
const (
writeWait = 10 * time.Second // Time allowed to write a message to the peer.
pongWait = 60 * time.Second // Time allowed to read the next pong message from the peer.
pingPeriod = (pongWait * 9) / 10 // Send pings to peer with this period. Must be less than pongWait.
)
func startReloadServer() {
broker = &wsBroker{
broadcast: make(chan []byte),
register: make(chan *wsClient),
unregister: make(chan *wsClient),
clients: make(map[*wsClient]bool),
}
go broker.run()
http.HandleFunc("/reload", func(w http.ResponseWriter, r *http.Request) {
handleWsRequest(broker, w, r)
})
go startServer()
logger.Infof("Reload server listening at %s", reloadAddress)
}
func startServer() {
err := http.ListenAndServe(reloadAddress, nil)
if err != nil {
logger.Errorf("Failed to start up the Reload server: %v", err)
return
}
}
func sendReload(payload string) {
message := bytes.TrimSpace([]byte(payload))
broker.broadcast <- message
}
// handleWsRequest handles websocket requests from the peer.
func handleWsRequest(broker *wsBroker, w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
logger.Errorf("error while upgrading server connection: %v", err)
return
}
client := &wsClient{
broker: broker,
conn: conn,
send: make(chan []byte, 256),
}
client.broker.register <- client
go client.writePump()
client.readPump()
}

7
run.go
View File

@ -137,6 +137,11 @@ func runApp(cmd *Command, args []string) int {
} }
} }
} }
// Start the Reload server (if enabled)
if conf.EnableReload {
startReloadServer()
}
if gendoc == "true" { if gendoc == "true" {
NewWatcher(paths, files, true) NewWatcher(paths, files, true)
AutoBuild(files, true) AutoBuild(files, true)
@ -185,7 +190,7 @@ func readAppDirectories(directory string, paths *[]string) {
continue continue
} }
if path.Ext(fileInfo.Name()) == ".go" { if path.Ext(fileInfo.Name()) == ".go" || (ifStaticFile(fileInfo.Name()) && conf.EnableReload) {
*paths = append(*paths, directory) *paths = append(*paths, directory)
useDirectory = true useDirectory = true
} }

8
vendor/github.com/gorilla/websocket/AUTHORS generated vendored Normal file
View File

@ -0,0 +1,8 @@
# This is the official list of Gorilla WebSocket authors for copyright
# purposes.
#
# Please keep the list sorted.
Gary Burd <gary@beagledreams.com>
Joachim Bauch <mail@joachim-bauch.de>

22
vendor/github.com/gorilla/websocket/LICENSE generated vendored Normal file
View File

@ -0,0 +1,22 @@
Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

61
vendor/github.com/gorilla/websocket/README.md generated vendored Normal file
View File

@ -0,0 +1,61 @@
# Gorilla WebSocket
Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
### Documentation
* [API Reference](http://godoc.org/github.com/gorilla/websocket)
* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)
* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)
### Status
The Gorilla WebSocket package provides a complete and tested implementation of
the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The
package API is stable.
### Installation
go get github.com/gorilla/websocket
### Protocol Compliance
The Gorilla WebSocket package passes the server tests in the [Autobahn Test
Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn
subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).
### Gorilla WebSocket compared with other packages
<table>
<tr>
<th></th>
<th><a href="http://godoc.org/github.com/gorilla/websocket">github.com/gorilla</a></th>
<th><a href="http://godoc.org/golang.org/x/net/websocket">golang.org/x/net</a></th>
</tr>
<tr>
<tr><td colspan="3"><a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a> Features</td></tr>
<tr><td>Passes <a href="http://autobahn.ws/testsuite/">Autobahn Test Suite</a></td><td><a href="https://github.com/gorilla/websocket/tree/master/examples/autobahn">Yes</a></td><td>No</td></tr>
<tr><td>Receive <a href="https://tools.ietf.org/html/rfc6455#section-5.4">fragmented</a> message<td>Yes</td><td><a href="https://code.google.com/p/go/issues/detail?id=7632">No</a>, see note 1</td></tr>
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">close</a> message</td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td><a href="https://code.google.com/p/go/issues/detail?id=4588">No</a></td></tr>
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">pings</a> and receive <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pongs</a></td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td>No</td></tr>
<tr><td>Get the <a href="https://tools.ietf.org/html/rfc6455#section-5.6">type</a> of a received data message</td><td>Yes</td><td>Yes, see note 2</td></tr>
<tr><td colspan="3">Other Features</tr></td>
<tr><td>Limit size of received message</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.SetReadLimit">Yes</a></td><td><a href="https://code.google.com/p/go/issues/detail?id=5082">No</a></td></tr>
<tr><td>Read message using io.Reader</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextReader">Yes</a></td><td>No, see note 3</td></tr>
<tr><td>Write message using io.WriteCloser</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextWriter">Yes</a></td><td>No, see note 3</td></tr>
</table>
Notes:
1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html).
2. The application can get the type of a received data message by implementing
a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal)
function.
3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries.
Read returns when the input buffer is full or a frame boundary is
encountered. Each call to Write sends a single frame message. The Gorilla
io.Reader and io.WriteCloser operate on a single WebSocket message.

375
vendor/github.com/gorilla/websocket/client.go generated vendored Normal file
View File

@ -0,0 +1,375 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"bufio"
"bytes"
"crypto/tls"
"encoding/base64"
"errors"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"strings"
"time"
)
// ErrBadHandshake is returned when the server response to opening handshake is
// invalid.
var ErrBadHandshake = errors.New("websocket: bad handshake")
// NewClient creates a new client connection using the given net connection.
// The URL u specifies the host and request URI. Use requestHeader to specify
// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
// (Cookie). Use the response.Header to get the selected subprotocol
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
//
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
// non-nil *http.Response so that callers can handle redirects, authentication,
// etc.
//
// Deprecated: Use Dialer instead.
func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) {
d := Dialer{
ReadBufferSize: readBufSize,
WriteBufferSize: writeBufSize,
NetDial: func(net, addr string) (net.Conn, error) {
return netConn, nil
},
}
return d.Dial(u.String(), requestHeader)
}
// A Dialer contains options for connecting to WebSocket server.
type Dialer struct {
// NetDial specifies the dial function for creating TCP connections. If
// NetDial is nil, net.Dial is used.
NetDial func(network, addr string) (net.Conn, error)
// Proxy specifies a function to return a proxy for a given
// Request. If the function returns a non-nil error, the
// request is aborted with the provided error.
// If Proxy is nil or returns a nil *URL, no proxy is used.
Proxy func(*http.Request) (*url.URL, error)
// TLSClientConfig specifies the TLS configuration to use with tls.Client.
// If nil, the default configuration is used.
TLSClientConfig *tls.Config
// HandshakeTimeout specifies the duration for the handshake to complete.
HandshakeTimeout time.Duration
// Input and output buffer sizes. If the buffer size is zero, then a
// default value of 4096 is used.
ReadBufferSize, WriteBufferSize int
// Subprotocols specifies the client's requested subprotocols.
Subprotocols []string
}
var errMalformedURL = errors.New("malformed ws or wss URL")
// parseURL parses the URL.
//
// This function is a replacement for the standard library url.Parse function.
// In Go 1.4 and earlier, url.Parse loses information from the path.
func parseURL(s string) (*url.URL, error) {
// From the RFC:
//
// ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
// wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
var u url.URL
switch {
case strings.HasPrefix(s, "ws://"):
u.Scheme = "ws"
s = s[len("ws://"):]
case strings.HasPrefix(s, "wss://"):
u.Scheme = "wss"
s = s[len("wss://"):]
default:
return nil, errMalformedURL
}
if i := strings.Index(s, "?"); i >= 0 {
u.RawQuery = s[i+1:]
s = s[:i]
}
if i := strings.Index(s, "/"); i >= 0 {
u.Opaque = s[i:]
s = s[:i]
} else {
u.Opaque = "/"
}
u.Host = s
if strings.Contains(u.Host, "@") {
// Don't bother parsing user information because user information is
// not allowed in websocket URIs.
return nil, errMalformedURL
}
return &u, nil
}
func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
hostPort = u.Host
hostNoPort = u.Host
if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") {
hostNoPort = hostNoPort[:i]
} else {
switch u.Scheme {
case "wss":
hostPort += ":443"
case "https":
hostPort += ":443"
default:
hostPort += ":80"
}
}
return hostPort, hostNoPort
}
// DefaultDialer is a dialer with all fields set to the default zero values.
var DefaultDialer = &Dialer{
Proxy: http.ProxyFromEnvironment,
}
// Dial creates a new client connection. Use requestHeader to specify the
// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie).
// Use the response.Header to get the selected subprotocol
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
//
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
// non-nil *http.Response so that callers can handle redirects, authentication,
// etcetera. The response body may not contain the entire response and does not
// need to be closed by the application.
func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
if d == nil {
d = &Dialer{
Proxy: http.ProxyFromEnvironment,
}
}
challengeKey, err := generateChallengeKey()
if err != nil {
return nil, nil, err
}
u, err := parseURL(urlStr)
if err != nil {
return nil, nil, err
}
switch u.Scheme {
case "ws":
u.Scheme = "http"
case "wss":
u.Scheme = "https"
default:
return nil, nil, errMalformedURL
}
if u.User != nil {
// User name and password are not allowed in websocket URIs.
return nil, nil, errMalformedURL
}
req := &http.Request{
Method: "GET",
URL: u,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Header: make(http.Header),
Host: u.Host,
}
// Set the request headers using the capitalization for names and values in
// RFC examples. Although the capitalization shouldn't matter, there are
// servers that depend on it. The Header.Set method is not used because the
// method canonicalizes the header names.
req.Header["Upgrade"] = []string{"websocket"}
req.Header["Connection"] = []string{"Upgrade"}
req.Header["Sec-WebSocket-Key"] = []string{challengeKey}
req.Header["Sec-WebSocket-Version"] = []string{"13"}
if len(d.Subprotocols) > 0 {
req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")}
}
for k, vs := range requestHeader {
switch {
case k == "Host":
if len(vs) > 0 {
req.Host = vs[0]
}
case k == "Upgrade" ||
k == "Connection" ||
k == "Sec-Websocket-Key" ||
k == "Sec-Websocket-Version" ||
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
default:
req.Header[k] = vs
}
}
hostPort, hostNoPort := hostPortNoPort(u)
var proxyURL *url.URL
// Check wether the proxy method has been configured
if d.Proxy != nil {
proxyURL, err = d.Proxy(req)
}
if err != nil {
return nil, nil, err
}
var targetHostPort string
if proxyURL != nil {
targetHostPort, _ = hostPortNoPort(proxyURL)
} else {
targetHostPort = hostPort
}
var deadline time.Time
if d.HandshakeTimeout != 0 {
deadline = time.Now().Add(d.HandshakeTimeout)
}
netDial := d.NetDial
if netDial == nil {
netDialer := &net.Dialer{Deadline: deadline}
netDial = netDialer.Dial
}
netConn, err := netDial("tcp", targetHostPort)
if err != nil {
return nil, nil, err
}
defer func() {
if netConn != nil {
netConn.Close()
}
}()
if err := netConn.SetDeadline(deadline); err != nil {
return nil, nil, err
}
if proxyURL != nil {
connectHeader := make(http.Header)
if user := proxyURL.User; user != nil {
proxyUser := user.Username()
if proxyPassword, passwordSet := user.Password(); passwordSet {
credential := base64.StdEncoding.EncodeToString([]byte(proxyUser + ":" + proxyPassword))
connectHeader.Set("Proxy-Authorization", "Basic "+credential)
}
}
connectReq := &http.Request{
Method: "CONNECT",
URL: &url.URL{Opaque: hostPort},
Host: hostPort,
Header: connectHeader,
}
connectReq.Write(netConn)
// Read response.
// Okay to use and discard buffered reader here, because
// TLS server will not speak until spoken to.
br := bufio.NewReader(netConn)
resp, err := http.ReadResponse(br, connectReq)
if err != nil {
return nil, nil, err
}
if resp.StatusCode != 200 {
f := strings.SplitN(resp.Status, " ", 2)
return nil, nil, errors.New(f[1])
}
}
if u.Scheme == "https" {
cfg := cloneTLSConfig(d.TLSClientConfig)
if cfg.ServerName == "" {
cfg.ServerName = hostNoPort
}
tlsConn := tls.Client(netConn, cfg)
netConn = tlsConn
if err := tlsConn.Handshake(); err != nil {
return nil, nil, err
}
if !cfg.InsecureSkipVerify {
if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
return nil, nil, err
}
}
}
conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize)
if err := req.Write(netConn); err != nil {
return nil, nil, err
}
resp, err := http.ReadResponse(conn.br, req)
if err != nil {
return nil, nil, err
}
if resp.StatusCode != 101 ||
!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) {
// Before closing the network connection on return from this
// function, slurp up some of the response to aid application
// debugging.
buf := make([]byte, 1024)
n, _ := io.ReadFull(resp.Body, buf)
resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n]))
return nil, resp, ErrBadHandshake
}
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
netConn.SetDeadline(time.Time{})
netConn = nil // to avoid close in defer.
return conn, resp, nil
}
// cloneTLSConfig clones all public fields except the fields
// SessionTicketsDisabled and SessionTicketKey. This avoids copying the
// sync.Mutex in the sync.Once and makes it safe to call cloneTLSConfig on a
// config in active use.
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
if cfg == nil {
return &tls.Config{}
}
return &tls.Config{
Rand: cfg.Rand,
Time: cfg.Time,
Certificates: cfg.Certificates,
NameToCertificate: cfg.NameToCertificate,
GetCertificate: cfg.GetCertificate,
RootCAs: cfg.RootCAs,
NextProtos: cfg.NextProtos,
ServerName: cfg.ServerName,
ClientAuth: cfg.ClientAuth,
ClientCAs: cfg.ClientCAs,
InsecureSkipVerify: cfg.InsecureSkipVerify,
CipherSuites: cfg.CipherSuites,
PreferServerCipherSuites: cfg.PreferServerCipherSuites,
ClientSessionCache: cfg.ClientSessionCache,
MinVersion: cfg.MinVersion,
MaxVersion: cfg.MaxVersion,
CurvePreferences: cfg.CurvePreferences,
}
}

951
vendor/github.com/gorilla/websocket/conn.go generated vendored Normal file
View File

@ -0,0 +1,951 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"bufio"
"encoding/binary"
"errors"
"io"
"io/ioutil"
"math/rand"
"net"
"strconv"
"time"
"unicode/utf8"
)
const (
maxFrameHeaderSize = 2 + 8 + 4 // Fixed header + length + mask
maxControlFramePayloadSize = 125
finalBit = 1 << 7
maskBit = 1 << 7
writeWait = time.Second
defaultReadBufferSize = 4096
defaultWriteBufferSize = 4096
continuationFrame = 0
noFrame = -1
)
// Close codes defined in RFC 6455, section 11.7.
const (
CloseNormalClosure = 1000
CloseGoingAway = 1001
CloseProtocolError = 1002
CloseUnsupportedData = 1003
CloseNoStatusReceived = 1005
CloseAbnormalClosure = 1006
CloseInvalidFramePayloadData = 1007
ClosePolicyViolation = 1008
CloseMessageTooBig = 1009
CloseMandatoryExtension = 1010
CloseInternalServerErr = 1011
CloseServiceRestart = 1012
CloseTryAgainLater = 1013
CloseTLSHandshake = 1015
)
// The message types are defined in RFC 6455, section 11.8.
const (
// TextMessage denotes a text data message. The text message payload is
// interpreted as UTF-8 encoded text data.
TextMessage = 1
// BinaryMessage denotes a binary data message.
BinaryMessage = 2
// CloseMessage denotes a close control message. The optional message
// payload contains a numeric code and text. Use the FormatCloseMessage
// function to format a close message payload.
CloseMessage = 8
// PingMessage denotes a ping control message. The optional message payload
// is UTF-8 encoded text.
PingMessage = 9
// PongMessage denotes a ping control message. The optional message payload
// is UTF-8 encoded text.
PongMessage = 10
)
// ErrCloseSent is returned when the application writes a message to the
// connection after sending a close message.
var ErrCloseSent = errors.New("websocket: close sent")
// ErrReadLimit is returned when reading a message that is larger than the
// read limit set for the connection.
var ErrReadLimit = errors.New("websocket: read limit exceeded")
// netError satisfies the net Error interface.
type netError struct {
msg string
temporary bool
timeout bool
}
func (e *netError) Error() string { return e.msg }
func (e *netError) Temporary() bool { return e.temporary }
func (e *netError) Timeout() bool { return e.timeout }
// CloseError represents close frame.
type CloseError struct {
// Code is defined in RFC 6455, section 11.7.
Code int
// Text is the optional text payload.
Text string
}
func (e *CloseError) Error() string {
s := []byte("websocket: close ")
s = strconv.AppendInt(s, int64(e.Code), 10)
switch e.Code {
case CloseNormalClosure:
s = append(s, " (normal)"...)
case CloseGoingAway:
s = append(s, " (going away)"...)
case CloseProtocolError:
s = append(s, " (protocol error)"...)
case CloseUnsupportedData:
s = append(s, " (unsupported data)"...)
case CloseNoStatusReceived:
s = append(s, " (no status)"...)
case CloseAbnormalClosure:
s = append(s, " (abnormal closure)"...)
case CloseInvalidFramePayloadData:
s = append(s, " (invalid payload data)"...)
case ClosePolicyViolation:
s = append(s, " (policy violation)"...)
case CloseMessageTooBig:
s = append(s, " (message too big)"...)
case CloseMandatoryExtension:
s = append(s, " (mandatory extension missing)"...)
case CloseInternalServerErr:
s = append(s, " (internal server error)"...)
case CloseTLSHandshake:
s = append(s, " (TLS handshake error)"...)
}
if e.Text != "" {
s = append(s, ": "...)
s = append(s, e.Text...)
}
return string(s)
}
// IsCloseError returns boolean indicating whether the error is a *CloseError
// with one of the specified codes.
func IsCloseError(err error, codes ...int) bool {
if e, ok := err.(*CloseError); ok {
for _, code := range codes {
if e.Code == code {
return true
}
}
}
return false
}
// IsUnexpectedCloseError returns boolean indicating whether the error is a
// *CloseError with a code not in the list of expected codes.
func IsUnexpectedCloseError(err error, expectedCodes ...int) bool {
if e, ok := err.(*CloseError); ok {
for _, code := range expectedCodes {
if e.Code == code {
return false
}
}
return true
}
return false
}
var (
errWriteTimeout = &netError{msg: "websocket: write timeout", timeout: true, temporary: true}
errUnexpectedEOF = &CloseError{Code: CloseAbnormalClosure, Text: io.ErrUnexpectedEOF.Error()}
errBadWriteOpCode = errors.New("websocket: bad write message type")
errWriteClosed = errors.New("websocket: write closed")
errInvalidControlFrame = errors.New("websocket: invalid control frame")
)
func hideTempErr(err error) error {
if e, ok := err.(net.Error); ok && e.Temporary() {
err = &netError{msg: e.Error(), timeout: e.Timeout()}
}
return err
}
func isControl(frameType int) bool {
return frameType == CloseMessage || frameType == PingMessage || frameType == PongMessage
}
func isData(frameType int) bool {
return frameType == TextMessage || frameType == BinaryMessage
}
var validReceivedCloseCodes = map[int]bool{
// see http://www.iana.org/assignments/websocket/websocket.xhtml#close-code-number
CloseNormalClosure: true,
CloseGoingAway: true,
CloseProtocolError: true,
CloseUnsupportedData: true,
CloseNoStatusReceived: false,
CloseAbnormalClosure: false,
CloseInvalidFramePayloadData: true,
ClosePolicyViolation: true,
CloseMessageTooBig: true,
CloseMandatoryExtension: true,
CloseInternalServerErr: true,
CloseServiceRestart: true,
CloseTryAgainLater: true,
CloseTLSHandshake: false,
}
func isValidReceivedCloseCode(code int) bool {
return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)
}
func maskBytes(key [4]byte, pos int, b []byte) int {
for i := range b {
b[i] ^= key[pos&3]
pos++
}
return pos & 3
}
func newMaskKey() [4]byte {
n := rand.Uint32()
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
}
// Conn represents a WebSocket connection.
type Conn struct {
conn net.Conn
isServer bool
subprotocol string
// Write fields
mu chan bool // used as mutex to protect write to conn and closeSent
closeSent bool // true if close message was sent
// Message writer fields.
writeErr error
writeBuf []byte // frame is constructed in this buffer.
writePos int // end of data in writeBuf.
writeFrameType int // type of the current frame.
writeDeadline time.Time
isWriting bool // for best-effort concurrent write detection
messageWriter *messageWriter // the current writer
// Read fields
readErr error
br *bufio.Reader
readRemaining int64 // bytes remaining in current frame.
readFinal bool // true the current message has more frames.
readLength int64 // Message size.
readLimit int64 // Maximum message size.
readMaskPos int
readMaskKey [4]byte
handlePong func(string) error
handlePing func(string) error
readErrCount int
messageReader *messageReader // the current reader
}
func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn {
mu := make(chan bool, 1)
mu <- true
if readBufferSize == 0 {
readBufferSize = defaultReadBufferSize
}
if readBufferSize < maxControlFramePayloadSize {
readBufferSize = maxControlFramePayloadSize
}
if writeBufferSize == 0 {
writeBufferSize = defaultWriteBufferSize
}
c := &Conn{
isServer: isServer,
br: bufio.NewReaderSize(conn, readBufferSize),
conn: conn,
mu: mu,
readFinal: true,
writeBuf: make([]byte, writeBufferSize+maxFrameHeaderSize),
writeFrameType: noFrame,
writePos: maxFrameHeaderSize,
}
c.SetPingHandler(nil)
c.SetPongHandler(nil)
return c
}
// Subprotocol returns the negotiated protocol for the connection.
func (c *Conn) Subprotocol() string {
return c.subprotocol
}
// Close closes the underlying network connection without sending or waiting for a close frame.
func (c *Conn) Close() error {
return c.conn.Close()
}
// LocalAddr returns the local network address.
func (c *Conn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
// RemoteAddr returns the remote network address.
func (c *Conn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
// Write methods
func (c *Conn) write(frameType int, deadline time.Time, bufs ...[]byte) error {
<-c.mu
defer func() { c.mu <- true }()
if c.closeSent {
return ErrCloseSent
} else if frameType == CloseMessage {
c.closeSent = true
}
c.conn.SetWriteDeadline(deadline)
for _, buf := range bufs {
if len(buf) > 0 {
n, err := c.conn.Write(buf)
if n != len(buf) {
// Close on partial write.
c.conn.Close()
}
if err != nil {
return err
}
}
}
return nil
}
// WriteControl writes a control message with the given deadline. The allowed
// message types are CloseMessage, PingMessage and PongMessage.
func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) error {
if !isControl(messageType) {
return errBadWriteOpCode
}
if len(data) > maxControlFramePayloadSize {
return errInvalidControlFrame
}
b0 := byte(messageType) | finalBit
b1 := byte(len(data))
if !c.isServer {
b1 |= maskBit
}
buf := make([]byte, 0, maxFrameHeaderSize+maxControlFramePayloadSize)
buf = append(buf, b0, b1)
if c.isServer {
buf = append(buf, data...)
} else {
key := newMaskKey()
buf = append(buf, key[:]...)
buf = append(buf, data...)
maskBytes(key, 0, buf[6:])
}
d := time.Hour * 1000
if !deadline.IsZero() {
d = deadline.Sub(time.Now())
if d < 0 {
return errWriteTimeout
}
}
timer := time.NewTimer(d)
select {
case <-c.mu:
timer.Stop()
case <-timer.C:
return errWriteTimeout
}
defer func() { c.mu <- true }()
if c.closeSent {
return ErrCloseSent
} else if messageType == CloseMessage {
c.closeSent = true
}
c.conn.SetWriteDeadline(deadline)
n, err := c.conn.Write(buf)
if n != 0 && n != len(buf) {
c.conn.Close()
}
return hideTempErr(err)
}
// NextWriter returns a writer for the next message to send. The writer's Close
// method flushes the complete message to the network.
//
// There can be at most one open writer on a connection. NextWriter closes the
// previous writer if the application has not already done so.
func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) {
if c.writeErr != nil {
return nil, c.writeErr
}
if c.writeFrameType != noFrame {
if err := c.flushFrame(true, nil); err != nil {
return nil, err
}
}
if !isControl(messageType) && !isData(messageType) {
return nil, errBadWriteOpCode
}
c.writeFrameType = messageType
w := &messageWriter{c}
c.messageWriter = w
return w, nil
}
func (c *Conn) flushFrame(final bool, extra []byte) error {
length := c.writePos - maxFrameHeaderSize + len(extra)
// Check for invalid control frames.
if isControl(c.writeFrameType) &&
(!final || length > maxControlFramePayloadSize) {
c.messageWriter = nil
c.writeFrameType = noFrame
c.writePos = maxFrameHeaderSize
return errInvalidControlFrame
}
b0 := byte(c.writeFrameType)
if final {
b0 |= finalBit
}
b1 := byte(0)
if !c.isServer {
b1 |= maskBit
}
// Assume that the frame starts at beginning of c.writeBuf.
framePos := 0
if c.isServer {
// Adjust up if mask not included in the header.
framePos = 4
}
switch {
case length >= 65536:
c.writeBuf[framePos] = b0
c.writeBuf[framePos+1] = b1 | 127
binary.BigEndian.PutUint64(c.writeBuf[framePos+2:], uint64(length))
case length > 125:
framePos += 6
c.writeBuf[framePos] = b0
c.writeBuf[framePos+1] = b1 | 126
binary.BigEndian.PutUint16(c.writeBuf[framePos+2:], uint16(length))
default:
framePos += 8
c.writeBuf[framePos] = b0
c.writeBuf[framePos+1] = b1 | byte(length)
}
if !c.isServer {
key := newMaskKey()
copy(c.writeBuf[maxFrameHeaderSize-4:], key[:])
maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:c.writePos])
if len(extra) > 0 {
c.writeErr = errors.New("websocket: internal error, extra used in client mode")
return c.writeErr
}
}
// Write the buffers to the connection with best-effort detection of
// concurrent writes. See the concurrency section in the package
// documentation for more info.
if c.isWriting {
panic("concurrent write to websocket connection")
}
c.isWriting = true
c.writeErr = c.write(c.writeFrameType, c.writeDeadline, c.writeBuf[framePos:c.writePos], extra)
if !c.isWriting {
panic("concurrent write to websocket connection")
}
c.isWriting = false
// Setup for next frame.
c.writePos = maxFrameHeaderSize
c.writeFrameType = continuationFrame
if final {
c.messageWriter = nil
c.writeFrameType = noFrame
}
return c.writeErr
}
type messageWriter struct{ c *Conn }
func (w *messageWriter) err() error {
c := w.c
if c.messageWriter != w {
return errWriteClosed
}
if c.writeErr != nil {
return c.writeErr
}
return nil
}
func (w *messageWriter) ncopy(max int) (int, error) {
n := len(w.c.writeBuf) - w.c.writePos
if n <= 0 {
if err := w.c.flushFrame(false, nil); err != nil {
return 0, err
}
n = len(w.c.writeBuf) - w.c.writePos
}
if n > max {
n = max
}
return n, nil
}
func (w *messageWriter) write(final bool, p []byte) (int, error) {
if err := w.err(); err != nil {
return 0, err
}
if len(p) > 2*len(w.c.writeBuf) && w.c.isServer {
// Don't buffer large messages.
err := w.c.flushFrame(final, p)
if err != nil {
return 0, err
}
return len(p), nil
}
nn := len(p)
for len(p) > 0 {
n, err := w.ncopy(len(p))
if err != nil {
return 0, err
}
copy(w.c.writeBuf[w.c.writePos:], p[:n])
w.c.writePos += n
p = p[n:]
}
return nn, nil
}
func (w *messageWriter) Write(p []byte) (int, error) {
return w.write(false, p)
}
func (w *messageWriter) WriteString(p string) (int, error) {
if err := w.err(); err != nil {
return 0, err
}
nn := len(p)
for len(p) > 0 {
n, err := w.ncopy(len(p))
if err != nil {
return 0, err
}
copy(w.c.writeBuf[w.c.writePos:], p[:n])
w.c.writePos += n
p = p[n:]
}
return nn, nil
}
func (w *messageWriter) ReadFrom(r io.Reader) (nn int64, err error) {
if err := w.err(); err != nil {
return 0, err
}
for {
if w.c.writePos == len(w.c.writeBuf) {
err = w.c.flushFrame(false, nil)
if err != nil {
break
}
}
var n int
n, err = r.Read(w.c.writeBuf[w.c.writePos:])
w.c.writePos += n
nn += int64(n)
if err != nil {
if err == io.EOF {
err = nil
}
break
}
}
return nn, err
}
func (w *messageWriter) Close() error {
if err := w.err(); err != nil {
return err
}
return w.c.flushFrame(true, nil)
}
// WriteMessage is a helper method for getting a writer using NextWriter,
// writing the message and closing the writer.
func (c *Conn) WriteMessage(messageType int, data []byte) error {
w, err := c.NextWriter(messageType)
if err != nil {
return err
}
if _, ok := w.(*messageWriter); ok && c.isServer {
// Optimize write as a single frame.
n := copy(c.writeBuf[c.writePos:], data)
c.writePos += n
data = data[n:]
err = c.flushFrame(true, data)
return err
}
if _, err = w.Write(data); err != nil {
return err
}
return w.Close()
}
// SetWriteDeadline sets the write deadline on the underlying network
// connection. After a write has timed out, the websocket state is corrupt and
// all future writes will return an error. A zero value for t means writes will
// not time out.
func (c *Conn) SetWriteDeadline(t time.Time) error {
c.writeDeadline = t
return nil
}
// Read methods
func (c *Conn) advanceFrame() (int, error) {
// 1. Skip remainder of previous frame.
if c.readRemaining > 0 {
if _, err := io.CopyN(ioutil.Discard, c.br, c.readRemaining); err != nil {
return noFrame, err
}
}
// 2. Read and parse first two bytes of frame header.
p, err := c.read(2)
if err != nil {
return noFrame, err
}
final := p[0]&finalBit != 0
frameType := int(p[0] & 0xf)
reserved := int((p[0] >> 4) & 0x7)
mask := p[1]&maskBit != 0
c.readRemaining = int64(p[1] & 0x7f)
if reserved != 0 {
return noFrame, c.handleProtocolError("unexpected reserved bits " + strconv.Itoa(reserved))
}
switch frameType {
case CloseMessage, PingMessage, PongMessage:
if c.readRemaining > maxControlFramePayloadSize {
return noFrame, c.handleProtocolError("control frame length > 125")
}
if !final {
return noFrame, c.handleProtocolError("control frame not final")
}
case TextMessage, BinaryMessage:
if !c.readFinal {
return noFrame, c.handleProtocolError("message start before final message frame")
}
c.readFinal = final
case continuationFrame:
if c.readFinal {
return noFrame, c.handleProtocolError("continuation after final message frame")
}
c.readFinal = final
default:
return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType))
}
// 3. Read and parse frame length.
switch c.readRemaining {
case 126:
p, err := c.read(2)
if err != nil {
return noFrame, err
}
c.readRemaining = int64(binary.BigEndian.Uint16(p))
case 127:
p, err := c.read(8)
if err != nil {
return noFrame, err
}
c.readRemaining = int64(binary.BigEndian.Uint64(p))
}
// 4. Handle frame masking.
if mask != c.isServer {
return noFrame, c.handleProtocolError("incorrect mask flag")
}
if mask {
c.readMaskPos = 0
p, err := c.read(len(c.readMaskKey))
if err != nil {
return noFrame, err
}
copy(c.readMaskKey[:], p)
}
// 5. For text and binary messages, enforce read limit and return.
if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage {
c.readLength += c.readRemaining
if c.readLimit > 0 && c.readLength > c.readLimit {
c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait))
return noFrame, ErrReadLimit
}
return frameType, nil
}
// 6. Read control frame payload.
var payload []byte
if c.readRemaining > 0 {
payload, err = c.read(int(c.readRemaining))
c.readRemaining = 0
if err != nil {
return noFrame, err
}
if c.isServer {
maskBytes(c.readMaskKey, 0, payload)
}
}
// 7. Process control frame payload.
switch frameType {
case PongMessage:
if err := c.handlePong(string(payload)); err != nil {
return noFrame, err
}
case PingMessage:
if err := c.handlePing(string(payload)); err != nil {
return noFrame, err
}
case CloseMessage:
echoMessage := []byte{}
closeCode := CloseNoStatusReceived
closeText := ""
if len(payload) >= 2 {
echoMessage = payload[:2]
closeCode = int(binary.BigEndian.Uint16(payload))
if !isValidReceivedCloseCode(closeCode) {
return noFrame, c.handleProtocolError("invalid close code")
}
closeText = string(payload[2:])
if !utf8.ValidString(closeText) {
return noFrame, c.handleProtocolError("invalid utf8 payload in close frame")
}
}
c.WriteControl(CloseMessage, echoMessage, time.Now().Add(writeWait))
return noFrame, &CloseError{Code: closeCode, Text: closeText}
}
return frameType, nil
}
func (c *Conn) handleProtocolError(message string) error {
c.WriteControl(CloseMessage, FormatCloseMessage(CloseProtocolError, message), time.Now().Add(writeWait))
return errors.New("websocket: " + message)
}
// NextReader returns the next data message received from the peer. The
// returned messageType is either TextMessage or BinaryMessage.
//
// There can be at most one open reader on a connection. NextReader discards
// the previous message if the application has not already consumed it.
//
// Applications must break out of the application's read loop when this method
// returns a non-nil error value. Errors returned from this method are
// permanent. Once this method returns a non-nil error, all subsequent calls to
// this method return the same error.
func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
c.messageReader = nil
c.readLength = 0
for c.readErr == nil {
frameType, err := c.advanceFrame()
if err != nil {
c.readErr = hideTempErr(err)
break
}
if frameType == TextMessage || frameType == BinaryMessage {
r := &messageReader{c}
c.messageReader = r
return frameType, r, nil
}
}
// Applications that do handle the error returned from this method spin in
// tight loop on connection failure. To help application developers detect
// this error, panic on repeated reads to the failed connection.
c.readErrCount++
if c.readErrCount >= 1000 {
panic("repeated read on failed websocket connection")
}
return noFrame, nil, c.readErr
}
type messageReader struct{ c *Conn }
func (r *messageReader) Read(b []byte) (int, error) {
c := r.c
if c.messageReader != r {
return 0, io.EOF
}
for c.readErr == nil {
if c.readRemaining > 0 {
if int64(len(b)) > c.readRemaining {
b = b[:c.readRemaining]
}
n, err := c.br.Read(b)
c.readErr = hideTempErr(err)
if c.isServer {
c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n])
}
c.readRemaining -= int64(n)
if c.readRemaining > 0 && c.readErr == io.EOF {
c.readErr = errUnexpectedEOF
}
return n, c.readErr
}
if c.readFinal {
c.messageReader = nil
return 0, io.EOF
}
frameType, err := c.advanceFrame()
switch {
case err != nil:
c.readErr = hideTempErr(err)
case frameType == TextMessage || frameType == BinaryMessage:
c.readErr = errors.New("websocket: internal error, unexpected text or binary in Reader")
}
}
err := c.readErr
if err == io.EOF && c.messageReader == r {
err = errUnexpectedEOF
}
return 0, err
}
// ReadMessage is a helper method for getting a reader using NextReader and
// reading from that reader to a buffer.
func (c *Conn) ReadMessage() (messageType int, p []byte, err error) {
var r io.Reader
messageType, r, err = c.NextReader()
if err != nil {
return messageType, nil, err
}
p, err = ioutil.ReadAll(r)
return messageType, p, err
}
// SetReadDeadline sets the read deadline on the underlying network connection.
// After a read has timed out, the websocket connection state is corrupt and
// all future reads will return an error. A zero value for t means reads will
// not time out.
func (c *Conn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
// SetReadLimit sets the maximum size for a message read from the peer. If a
// message exceeds the limit, the connection sends a close frame to the peer
// and returns ErrReadLimit to the application.
func (c *Conn) SetReadLimit(limit int64) {
c.readLimit = limit
}
// PingHandler returns the current ping handler
func (c *Conn) PingHandler() func(appData string) error {
return c.handlePing
}
// SetPingHandler sets the handler for ping messages received from the peer.
// The appData argument to h is the PING frame application data. The default
// ping handler sends a pong to the peer.
func (c *Conn) SetPingHandler(h func(appData string) error) {
if h == nil {
h = func(message string) error {
err := c.WriteControl(PongMessage, []byte(message), time.Now().Add(writeWait))
if err == ErrCloseSent {
return nil
} else if e, ok := err.(net.Error); ok && e.Temporary() {
return nil
}
return err
}
}
c.handlePing = h
}
// PongHandler returns the current pong handler
func (c *Conn) PongHandler() func(appData string) error {
return c.handlePong
}
// SetPongHandler sets the handler for pong messages received from the peer.
// The appData argument to h is the PONG frame application data. The default
// pong handler does nothing.
func (c *Conn) SetPongHandler(h func(appData string) error) {
if h == nil {
h = func(string) error { return nil }
}
c.handlePong = h
}
// UnderlyingConn returns the internal net.Conn. This can be used to further
// modifications to connection specific flags.
func (c *Conn) UnderlyingConn() net.Conn {
return c.conn
}
// FormatCloseMessage formats closeCode and text as a WebSocket close message.
func FormatCloseMessage(closeCode int, text string) []byte {
buf := make([]byte, 2+len(text))
binary.BigEndian.PutUint16(buf, uint16(closeCode))
copy(buf[2:], text)
return buf
}

18
vendor/github.com/gorilla/websocket/conn_read.go generated vendored Normal file
View File

@ -0,0 +1,18 @@
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.5
package websocket
import "io"
func (c *Conn) read(n int) ([]byte, error) {
p, err := c.br.Peek(n)
if err == io.EOF {
err = errUnexpectedEOF
}
c.br.Discard(len(p))
return p, err
}

View File

@ -0,0 +1,21 @@
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.5
package websocket
import "io"
func (c *Conn) read(n int) ([]byte, error) {
p, err := c.br.Peek(n)
if err == io.EOF {
err = errUnexpectedEOF
}
if len(p) > 0 {
// advance over the bytes just read
io.ReadFull(c.br, p)
}
return p, err
}

152
vendor/github.com/gorilla/websocket/doc.go generated vendored Normal file
View File

@ -0,0 +1,152 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package websocket implements the WebSocket protocol defined in RFC 6455.
//
// Overview
//
// The Conn type represents a WebSocket connection. A server application uses
// the Upgrade function from an Upgrader object with a HTTP request handler
// to get a pointer to a Conn:
//
// var upgrader = websocket.Upgrader{
// ReadBufferSize: 1024,
// WriteBufferSize: 1024,
// }
//
// func handler(w http.ResponseWriter, r *http.Request) {
// conn, err := upgrader.Upgrade(w, r, nil)
// if err != nil {
// log.Println(err)
// return
// }
// ... Use conn to send and receive messages.
// }
//
// Call the connection's WriteMessage and ReadMessage methods to send and
// receive messages as a slice of bytes. This snippet of code shows how to echo
// messages using these methods:
//
// for {
// messageType, p, err := conn.ReadMessage()
// if err != nil {
// return
// }
// if err = conn.WriteMessage(messageType, p); err != nil {
// return err
// }
// }
//
// In above snippet of code, p is a []byte and messageType is an int with value
// websocket.BinaryMessage or websocket.TextMessage.
//
// An application can also send and receive messages using the io.WriteCloser
// and io.Reader interfaces. To send a message, call the connection NextWriter
// method to get an io.WriteCloser, write the message to the writer and close
// the writer when done. To receive a message, call the connection NextReader
// method to get an io.Reader and read until io.EOF is returned. This snippet
// shows how to echo messages using the NextWriter and NextReader methods:
//
// for {
// messageType, r, err := conn.NextReader()
// if err != nil {
// return
// }
// w, err := conn.NextWriter(messageType)
// if err != nil {
// return err
// }
// if _, err := io.Copy(w, r); err != nil {
// return err
// }
// if err := w.Close(); err != nil {
// return err
// }
// }
//
// Data Messages
//
// The WebSocket protocol distinguishes between text and binary data messages.
// Text messages are interpreted as UTF-8 encoded text. The interpretation of
// binary messages is left to the application.
//
// This package uses the TextMessage and BinaryMessage integer constants to
// identify the two data message types. The ReadMessage and NextReader methods
// return the type of the received message. The messageType argument to the
// WriteMessage and NextWriter methods specifies the type of a sent message.
//
// It is the application's responsibility to ensure that text messages are
// valid UTF-8 encoded text.
//
// Control Messages
//
// The WebSocket protocol defines three types of control messages: close, ping
// and pong. Call the connection WriteControl, WriteMessage or NextWriter
// methods to send a control message to the peer.
//
// Connections handle received close messages by sending a close message to the
// peer and returning a *CloseError from the the NextReader, ReadMessage or the
// message Read method.
//
// Connections handle received ping and pong messages by invoking callback
// functions set with SetPingHandler and SetPongHandler methods. The callback
// functions are called from the NextReader, ReadMessage and the message Read
// methods.
//
// The default ping handler sends a pong to the peer. The application's reading
// goroutine can block for a short time while the handler writes the pong data
// to the connection.
//
// The application must read the connection to process ping, pong and close
// messages sent from the peer. If the application is not otherwise interested
// in messages from the peer, then the application should start a goroutine to
// read and discard messages from the peer. A simple example is:
//
// func readLoop(c *websocket.Conn) {
// for {
// if _, _, err := c.NextReader(); err != nil {
// c.Close()
// break
// }
// }
// }
//
// Concurrency
//
// Connections support one concurrent reader and one concurrent writer.
//
// Applications are responsible for ensuring that no more than one goroutine
// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
// WriteJSON) concurrently and that no more than one goroutine calls the read
// methods (NextReader, SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler,
// SetPingHandler) concurrently.
//
// The Close and WriteControl methods can be called concurrently with all other
// methods.
//
// Origin Considerations
//
// Web browsers allow Javascript applications to open a WebSocket connection to
// any host. It's up to the server to enforce an origin policy using the Origin
// request header sent by the browser.
//
// The Upgrader calls the function specified in the CheckOrigin field to check
// the origin. If the CheckOrigin function returns false, then the Upgrade
// method fails the WebSocket handshake with HTTP status 403.
//
// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail
// the handshake if the Origin request header is present and not equal to the
// Host request header.
//
// An application can allow connections from any origin by specifying a
// function that always returns true:
//
// var upgrader = websocket.Upgrader{
// CheckOrigin: func(r *http.Request) bool { return true },
// }
//
// The deprecated Upgrade function does not enforce an origin policy. It's the
// application's responsibility to check the Origin header before calling
// Upgrade.
package websocket

55
vendor/github.com/gorilla/websocket/json.go generated vendored Normal file
View File

@ -0,0 +1,55 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"encoding/json"
"io"
)
// WriteJSON is deprecated, use c.WriteJSON instead.
func WriteJSON(c *Conn, v interface{}) error {
return c.WriteJSON(v)
}
// WriteJSON writes the JSON encoding of v to the connection.
//
// See the documentation for encoding/json Marshal for details about the
// conversion of Go values to JSON.
func (c *Conn) WriteJSON(v interface{}) error {
w, err := c.NextWriter(TextMessage)
if err != nil {
return err
}
err1 := json.NewEncoder(w).Encode(v)
err2 := w.Close()
if err1 != nil {
return err1
}
return err2
}
// ReadJSON is deprecated, use c.ReadJSON instead.
func ReadJSON(c *Conn, v interface{}) error {
return c.ReadJSON(v)
}
// ReadJSON reads the next JSON-encoded message from the connection and stores
// it in the value pointed to by v.
//
// See the documentation for the encoding/json Unmarshal function for details
// about the conversion of JSON to a Go value.
func (c *Conn) ReadJSON(v interface{}) error {
_, r, err := c.NextReader()
if err != nil {
return err
}
err = json.NewDecoder(r).Decode(v)
if err == io.EOF {
// One value is expected in the message.
err = io.ErrUnexpectedEOF
}
return err
}

260
vendor/github.com/gorilla/websocket/server.go generated vendored Normal file
View File

@ -0,0 +1,260 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"bufio"
"errors"
"net"
"net/http"
"net/url"
"strings"
"time"
)
// HandshakeError describes an error with the handshake from the peer.
type HandshakeError struct {
message string
}
func (e HandshakeError) Error() string { return e.message }
// Upgrader specifies parameters for upgrading an HTTP connection to a
// WebSocket connection.
type Upgrader struct {
// HandshakeTimeout specifies the duration for the handshake to complete.
HandshakeTimeout time.Duration
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer
// size is zero, then a default value of 4096 is used. The I/O buffer sizes
// do not limit the size of the messages that can be sent or received.
ReadBufferSize, WriteBufferSize int
// Subprotocols specifies the server's supported protocols in order of
// preference. If this field is set, then the Upgrade method negotiates a
// subprotocol by selecting the first match in this list with a protocol
// requested by the client.
Subprotocols []string
// Error specifies the function for generating HTTP error responses. If Error
// is nil, then http.Error is used to generate the HTTP response.
Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
// CheckOrigin returns true if the request Origin header is acceptable. If
// CheckOrigin is nil, the host in the Origin header must not be set or
// must match the host of the request.
CheckOrigin func(r *http.Request) bool
}
func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {
err := HandshakeError{reason}
if u.Error != nil {
u.Error(w, r, status, err)
} else {
http.Error(w, http.StatusText(status), status)
}
return nil, err
}
// checkSameOrigin returns true if the origin is not set or is equal to the request host.
func checkSameOrigin(r *http.Request) bool {
origin := r.Header["Origin"]
if len(origin) == 0 {
return true
}
u, err := url.Parse(origin[0])
if err != nil {
return false
}
return u.Host == r.Host
}
func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {
if u.Subprotocols != nil {
clientProtocols := Subprotocols(r)
for _, serverProtocol := range u.Subprotocols {
for _, clientProtocol := range clientProtocols {
if clientProtocol == serverProtocol {
return clientProtocol
}
}
}
} else if responseHeader != nil {
return responseHeader.Get("Sec-Websocket-Protocol")
}
return ""
}
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
//
// The responseHeader is included in the response to the client's upgrade
// request. Use the responseHeader to specify cookies (Set-Cookie) and the
// application negotiated subprotocol (Sec-Websocket-Protocol).
//
// If the upgrade fails, then Upgrade replies to the client with an HTTP error
// response.
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
if r.Method != "GET" {
return u.returnError(w, r, http.StatusMethodNotAllowed, "websocket: method not GET")
}
if values := r.Header["Sec-Websocket-Version"]; len(values) == 0 || values[0] != "13" {
return u.returnError(w, r, http.StatusBadRequest, "websocket: version != 13")
}
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find connection header with token 'upgrade'")
}
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find upgrade header with token 'websocket'")
}
checkOrigin := u.CheckOrigin
if checkOrigin == nil {
checkOrigin = checkSameOrigin
}
if !checkOrigin(r) {
return u.returnError(w, r, http.StatusForbidden, "websocket: origin not allowed")
}
challengeKey := r.Header.Get("Sec-Websocket-Key")
if challengeKey == "" {
return u.returnError(w, r, http.StatusBadRequest, "websocket: key missing or blank")
}
subprotocol := u.selectSubprotocol(r, responseHeader)
var (
netConn net.Conn
br *bufio.Reader
err error
)
h, ok := w.(http.Hijacker)
if !ok {
return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
}
var rw *bufio.ReadWriter
netConn, rw, err = h.Hijack()
if err != nil {
return u.returnError(w, r, http.StatusInternalServerError, err.Error())
}
br = rw.Reader
if br.Buffered() > 0 {
netConn.Close()
return nil, errors.New("websocket: client sent data before handshake is complete")
}
c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize)
c.subprotocol = subprotocol
p := c.writeBuf[:0]
p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...)
p = append(p, computeAcceptKey(challengeKey)...)
p = append(p, "\r\n"...)
if c.subprotocol != "" {
p = append(p, "Sec-Websocket-Protocol: "...)
p = append(p, c.subprotocol...)
p = append(p, "\r\n"...)
}
for k, vs := range responseHeader {
if k == "Sec-Websocket-Protocol" {
continue
}
for _, v := range vs {
p = append(p, k...)
p = append(p, ": "...)
for i := 0; i < len(v); i++ {
b := v[i]
if b <= 31 {
// prevent response splitting.
b = ' '
}
p = append(p, b)
}
p = append(p, "\r\n"...)
}
}
p = append(p, "\r\n"...)
// Clear deadlines set by HTTP server.
netConn.SetDeadline(time.Time{})
if u.HandshakeTimeout > 0 {
netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout))
}
if _, err = netConn.Write(p); err != nil {
netConn.Close()
return nil, err
}
if u.HandshakeTimeout > 0 {
netConn.SetWriteDeadline(time.Time{})
}
return c, nil
}
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
//
// This function is deprecated, use websocket.Upgrader instead.
//
// The application is responsible for checking the request origin before
// calling Upgrade. An example implementation of the same origin policy is:
//
// if req.Header.Get("Origin") != "http://"+req.Host {
// http.Error(w, "Origin not allowed", 403)
// return
// }
//
// If the endpoint supports subprotocols, then the application is responsible
// for negotiating the protocol used on the connection. Use the Subprotocols()
// function to get the subprotocols requested by the client. Use the
// Sec-Websocket-Protocol response header to specify the subprotocol selected
// by the application.
//
// The responseHeader is included in the response to the client's upgrade
// request. Use the responseHeader to specify cookies (Set-Cookie) and the
// negotiated subprotocol (Sec-Websocket-Protocol).
//
// The connection buffers IO to the underlying network connection. The
// readBufSize and writeBufSize parameters specify the size of the buffers to
// use. Messages can be larger than the buffers.
//
// If the request is not a valid WebSocket handshake, then Upgrade returns an
// error of type HandshakeError. Applications should handle this error by
// replying to the client with an HTTP error response.
func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) {
u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize}
u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) {
// don't return errors to maintain backwards compatibility
}
u.CheckOrigin = func(r *http.Request) bool {
// allow all connections by default
return true
}
return u.Upgrade(w, r, responseHeader)
}
// Subprotocols returns the subprotocols requested by the client in the
// Sec-Websocket-Protocol header.
func Subprotocols(r *http.Request) []string {
h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol"))
if h == "" {
return nil
}
protocols := strings.Split(h, ",")
for i := range protocols {
protocols[i] = strings.TrimSpace(protocols[i])
}
return protocols
}
// IsWebSocketUpgrade returns true if the client requested upgrade to the
// WebSocket protocol.
func IsWebSocketUpgrade(r *http.Request) bool {
return tokenListContainsValue(r.Header, "Connection", "upgrade") &&
tokenListContainsValue(r.Header, "Upgrade", "websocket")
}

214
vendor/github.com/gorilla/websocket/util.go generated vendored Normal file
View File

@ -0,0 +1,214 @@
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package websocket
import (
"crypto/rand"
"crypto/sha1"
"encoding/base64"
"io"
"net/http"
"strings"
)
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
func computeAcceptKey(challengeKey string) string {
h := sha1.New()
h.Write([]byte(challengeKey))
h.Write(keyGUID)
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
func generateChallengeKey() (string, error) {
p := make([]byte, 16)
if _, err := io.ReadFull(rand.Reader, p); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(p), nil
}
// Octet types from RFC 2616.
var octetTypes [256]byte
const (
isTokenOctet = 1 << iota
isSpaceOctet
)
func init() {
// From RFC 2616
//
// OCTET = <any 8-bit sequence of data>
// CHAR = <any US-ASCII character (octets 0 - 127)>
// CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
// CR = <US-ASCII CR, carriage return (13)>
// LF = <US-ASCII LF, linefeed (10)>
// SP = <US-ASCII SP, space (32)>
// HT = <US-ASCII HT, horizontal-tab (9)>
// <"> = <US-ASCII double-quote mark (34)>
// CRLF = CR LF
// LWS = [CRLF] 1*( SP | HT )
// TEXT = <any OCTET except CTLs, but including LWS>
// separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
// | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
// token = 1*<any CHAR except CTLs or separators>
// qdtext = <any TEXT except <">>
for c := 0; c < 256; c++ {
var t byte
isCtl := c <= 31 || c == 127
isChar := 0 <= c && c <= 127
isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0
if strings.IndexRune(" \t\r\n", rune(c)) >= 0 {
t |= isSpaceOctet
}
if isChar && !isCtl && !isSeparator {
t |= isTokenOctet
}
octetTypes[c] = t
}
}
func skipSpace(s string) (rest string) {
i := 0
for ; i < len(s); i++ {
if octetTypes[s[i]]&isSpaceOctet == 0 {
break
}
}
return s[i:]
}
func nextToken(s string) (token, rest string) {
i := 0
for ; i < len(s); i++ {
if octetTypes[s[i]]&isTokenOctet == 0 {
break
}
}
return s[:i], s[i:]
}
func nextTokenOrQuoted(s string) (value string, rest string) {
if !strings.HasPrefix(s, "\"") {
return nextToken(s)
}
s = s[1:]
for i := 0; i < len(s); i++ {
switch s[i] {
case '"':
return s[:i], s[i+1:]
case '\\':
p := make([]byte, len(s)-1)
j := copy(p, s[:i])
escape := true
for i = i + 1; i < len(s); i++ {
b := s[i]
switch {
case escape:
escape = false
p[j] = b
j += 1
case b == '\\':
escape = true
case b == '"':
return string(p[:j]), s[i+1:]
default:
p[j] = b
j += 1
}
}
return "", ""
}
}
return "", ""
}
// tokenListContainsValue returns true if the 1#token header with the given
// name contains token.
func tokenListContainsValue(header http.Header, name string, value string) bool {
headers:
for _, s := range header[name] {
for {
var t string
t, s = nextToken(skipSpace(s))
if t == "" {
continue headers
}
s = skipSpace(s)
if s != "" && s[0] != ',' {
continue headers
}
if strings.EqualFold(t, value) {
return true
}
if s == "" {
continue headers
}
s = s[1:]
}
}
return false
}
// parseExtensiosn parses WebSocket extensions from a header.
func parseExtensions(header http.Header) []map[string]string {
// From RFC 6455:
//
// Sec-WebSocket-Extensions = extension-list
// extension-list = 1#extension
// extension = extension-token *( ";" extension-param )
// extension-token = registered-token
// registered-token = token
// extension-param = token [ "=" (token | quoted-string) ]
// ;When using the quoted-string syntax variant, the value
// ;after quoted-string unescaping MUST conform to the
// ;'token' ABNF.
var result []map[string]string
headers:
for _, s := range header["Sec-Websocket-Extensions"] {
for {
var t string
t, s = nextToken(skipSpace(s))
if t == "" {
continue headers
}
ext := map[string]string{"": t}
for {
s = skipSpace(s)
if !strings.HasPrefix(s, ";") {
break
}
var k string
k, s = nextToken(skipSpace(s[1:]))
if k == "" {
continue headers
}
s = skipSpace(s)
var v string
if strings.HasPrefix(s, "=") {
v, s = nextTokenOrQuoted(skipSpace(s[1:]))
s = skipSpace(s)
}
if s != "" && s[0] != ',' && s[0] != ';' {
continue headers
}
ext[k] = v
}
if s != "" && s[0] != ',' {
continue headers
}
result = append(result, ext)
if s == "" {
continue headers
}
s = s[1:]
}
}
return result
}

6
vendor/vendor.json vendored
View File

@ -26,6 +26,12 @@
"revision": "6fd058ce0d6b7ee43174e80d5a3e7f483c4dfbe5", "revision": "6fd058ce0d6b7ee43174e80d5a3e7f483c4dfbe5",
"revisionTime": "2016-01-13T11:48:05Z" "revisionTime": "2016-01-13T11:48:05Z"
}, },
{
"checksumSHA1": "dyGQP5AipmInoTQmnqvbFmUORpk=",
"path": "github.com/gorilla/websocket",
"revision": "a68708917c6a4f06314ab4e52493cc61359c9d42",
"revisionTime": "2016-06-06T23:20:22Z"
},
{ {
"checksumSHA1": "ZxzYc1JwJ3U6kZbw/KGuPko5lSY=", "checksumSHA1": "ZxzYc1JwJ3U6kZbw/KGuPko5lSY=",
"path": "github.com/howeyc/fsnotify", "path": "github.com/howeyc/fsnotify",

View File

@ -3,13 +3,18 @@ package main
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"encoding/json"
"flag"
"fmt" "fmt"
"io" "io"
"os" "os"
"os/exec" "os/exec"
path "path/filepath" path "path/filepath"
"regexp" "regexp"
"runtime"
"strings" "strings"
"gopkg.in/yaml.v2"
) )
var cmdVersion = &Command{ var cmdVersion = &Command{
@ -17,8 +22,8 @@ var cmdVersion = &Command{
Short: "Prints the current Bee version", Short: "Prints the current Bee version",
Long: ` Long: `
Prints the current Bee, Beego and Go version alongside the platform information. Prints the current Bee, Beego and Go version alongside the platform information.
`, `,
Run: versionCmd,
} }
const verboseVersionBanner string = `%s%s______ const verboseVersionBanner string = `%s%s______
@ -39,36 +44,66 @@ const verboseVersionBanner string = `%s%s______
Date : {{ Now "Monday, 2 Jan 2006" }}%s Date : {{ Now "Monday, 2 Jan 2006" }}%s
` `
const shortVersionBanner = `%s%s______ const shortVersionBanner = `______
| ___ \ | ___ \
| |_/ / ___ ___ | |_/ / ___ ___
| ___ \ / _ \ / _ \ | ___ \ / _ \ / _ \
| |_/ /| __/| __/ | |_/ /| __/| __/
\____/ \___| \___| v{{ .BeeVersion }}%s \____/ \___| \___| v{{ .BeeVersion }}
` `
var outputFormat string
func init() { func init() {
cmdVersion.Run = versionCmd fs := flag.NewFlagSet("version", flag.ContinueOnError)
fs.StringVar(&outputFormat, "o", "", "Set the output format. Either json or yaml.")
cmdVersion.Flag = *fs
} }
func versionCmd(cmd *Command, args []string) int { func versionCmd(cmd *Command, args []string) int {
ShowVerboseVersionBanner() cmd.Flag.Parse(args)
stdout := cmd.Out()
if outputFormat != "" {
runtimeInfo := RuntimeInfo{
getGoVersion(),
runtime.GOOS,
runtime.GOARCH,
runtime.NumCPU(),
os.Getenv("GOPATH"),
runtime.GOROOT(),
runtime.Compiler,
version,
getBeegoVersion(),
}
switch outputFormat {
case "json":
{
b, err := json.MarshalIndent(runtimeInfo, "", " ")
MustCheck(err)
fmt.Println(string(b))
return 0
}
case "yaml":
{
b, err := yaml.Marshal(&runtimeInfo)
MustCheck(err)
fmt.Println(string(b))
return 0
}
}
}
coloredBanner := fmt.Sprintf(verboseVersionBanner, "\x1b[35m", "\x1b[1m",
"\x1b[0m", "\x1b[32m", "\x1b[1m", "\x1b[0m")
InitBanner(stdout, bytes.NewBufferString(coloredBanner))
return 0 return 0
} }
// ShowVerboseVersionBanner prints the verbose version banner // ShowShortVersionBanner prints the short version banner.
func ShowVerboseVersionBanner() {
w := NewColorWriter(os.Stdout)
coloredBanner := fmt.Sprintf(verboseVersionBanner, "\x1b[35m", "\x1b[1m", "\x1b[0m",
"\x1b[32m", "\x1b[1m", "\x1b[0m")
InitBanner(w, bytes.NewBufferString(coloredBanner))
}
// ShowShortVersionBanner prints the short version banner
func ShowShortVersionBanner() { func ShowShortVersionBanner() {
w := NewColorWriter(os.Stdout) output := NewColorWriter(os.Stdout)
coloredBanner := fmt.Sprintf(shortVersionBanner, "\x1b[35m", "\x1b[1m", "\x1b[0m") InitBanner(output, bytes.NewBufferString(MagentaBold(shortVersionBanner)))
InitBanner(w, bytes.NewBufferString(coloredBanner))
} }
func getBeegoVersion() string { func getBeegoVersion() string {

View File

@ -28,10 +28,18 @@ import (
) )
var ( var (
cmd *exec.Cmd cmd *exec.Cmd
state sync.Mutex state sync.Mutex
eventTime = make(map[string]int64) eventTime = make(map[string]int64)
scheduleTime time.Time scheduleTime time.Time
watchExts = []string{".go"}
watchExtsStatic = []string{".html", ".tpl", ".js", ".css"}
ignoredFilesRegExps = []string{
`.#(\w+).go`,
`.(\w+).go.swp`,
`(\w+).go~`,
`(\w+).tmp`,
}
) )
// NewWatcher starts an fsnotify Watcher on the specified paths // NewWatcher starts an fsnotify Watcher on the specified paths
@ -45,37 +53,34 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
for { for {
select { select {
case e := <-watcher.Events: case e := <-watcher.Events:
isbuild := true isBuild := true
if ifStaticFile(e.Name) && conf.EnableReload {
sendReload(e.String())
continue
}
// Skip ignored files // Skip ignored files
if shouldIgnoreFile(e.Name) { if shouldIgnoreFile(e.Name) {
continue continue
} }
if !checkIfWatchExt(e.Name) { if !shouldWatchFileWithExtension(e.Name) {
continue continue
} }
mt := getFileModTime(e.Name) mt := getFileModTime(e.Name)
if t := eventTime[e.Name]; mt == t { if t := eventTime[e.Name]; mt == t {
logger.Infof(bold("Skipping: ")+"%s", e.String()) logger.Infof(bold("Skipping: ")+"%s", e.String())
isbuild = false isBuild = false
} }
eventTime[e.Name] = mt eventTime[e.Name] = mt
if isbuild { if isBuild {
logger.Infof("Event fired: %s", e) logger.Infof("Event fired: %s", e)
go func() { go func() {
// Wait 1s before autobuild util there is no file change. // Wait 1s before autobuild until there is no file change.
scheduleTime = time.Now().Add(1 * time.Second) scheduleTime = time.Now().Add(1 * time.Second)
for { time.Sleep(scheduleTime.Sub(time.Now()))
time.Sleep(scheduleTime.Sub(time.Now()))
if time.Now().After(scheduleTime) {
break
}
return
}
AutoBuild(files, isgenerate) AutoBuild(files, isgenerate)
}() }()
} }
@ -93,7 +98,6 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
logger.Fatalf("Failed to watch directory: %s", err) logger.Fatalf("Failed to watch directory: %s", err)
} }
} }
} }
// getFileModTime returns unix timestamp of `os.File.ModTime` for the given path. // getFileModTime returns unix timestamp of `os.File.ModTime` for the given path.
@ -243,6 +247,15 @@ func Start(appname string) {
started <- true started <- true
} }
func ifStaticFile(filename string) bool {
for _, s := range watchExtsStatic {
if strings.HasSuffix(filename, s) {
return true
}
}
return false
}
// shouldIgnoreFile ignores filenames generated by Emacs, Vim or SublimeText. // shouldIgnoreFile ignores filenames generated by Emacs, Vim or SublimeText.
// It returns true if the file should be ignored, false otherwise. // It returns true if the file should be ignored, false otherwise.
func shouldIgnoreFile(filename string) bool { func shouldIgnoreFile(filename string) bool {
@ -259,16 +272,9 @@ func shouldIgnoreFile(filename string) bool {
return false return false
} }
var watchExts = []string{".go"} // shouldWatchFileWithExtension returns true if the name of the file
var ignoredFilesRegExps = []string{ // hash a suffix that should be watched.
`.#(\w+).go`, func shouldWatchFileWithExtension(name string) bool {
`.(\w+).go.swp`,
`(\w+).go~`,
`(\w+).tmp`,
}
// checkIfWatchExt returns true if the name HasSuffix <watch_ext>.
func checkIfWatchExt(name string) bool {
for _, s := range watchExts { for _, s := range watchExts {
if strings.HasSuffix(name, s) { if strings.HasSuffix(name, s) {
return true return true