├── Dockerfile.build ├── Godeps ├── Godeps.json ├── Readme └── _workspace │ ├── .gitignore │ └── src │ ├── github.com │ ├── codegangsta │ │ └── cli │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── app.go │ │ │ ├── app_test.go │ │ │ ├── autocomplete │ │ │ ├── bash_autocomplete │ │ │ └── zsh_autocomplete │ │ │ ├── cli.go │ │ │ ├── cli_test.go │ │ │ ├── command.go │ │ │ ├── command_test.go │ │ │ ├── context.go │ │ │ ├── context_test.go │ │ │ ├── flag.go │ │ │ ├── flag_test.go │ │ │ ├── help.go │ │ │ ├── help_test.go │ │ │ └── helpers_test.go │ ├── gorilla │ │ └── websocket │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── AUTHORS │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── bench_test.go │ │ │ ├── client.go │ │ │ ├── client_server_test.go │ │ │ ├── client_test.go │ │ │ ├── conn.go │ │ │ ├── conn_test.go │ │ │ ├── doc.go │ │ │ ├── examples │ │ │ ├── autobahn │ │ │ │ ├── README.md │ │ │ │ ├── fuzzingclient.json │ │ │ │ └── server.go │ │ │ ├── chat │ │ │ │ ├── README.md │ │ │ │ ├── conn.go │ │ │ │ ├── home.html │ │ │ │ ├── hub.go │ │ │ │ └── main.go │ │ │ └── filewatch │ │ │ │ ├── README.md │ │ │ │ └── main.go │ │ │ ├── json.go │ │ │ ├── json_test.go │ │ │ ├── server.go │ │ │ ├── server_test.go │ │ │ ├── util.go │ │ │ └── util_test.go │ └── jaschaephraim │ │ └── lrserver │ │ ├── LICENSE │ │ ├── README.md │ │ ├── connection.go │ │ ├── example_test.go │ │ ├── handlers.go │ │ ├── javascript.go │ │ ├── lrserver.go │ │ ├── lrserver_test.go │ │ ├── messages.go │ │ └── server.go │ └── gopkg.in │ └── fsnotify.v1 │ ├── .gitignore │ ├── .travis.yml │ ├── AUTHORS │ ├── CHANGELOG.md │ ├── CONTRIBUTING.md │ ├── LICENSE │ ├── NotUsed.xcworkspace │ ├── README.md │ ├── circle.yml │ ├── example_test.go │ ├── fsnotify.go │ ├── inotify.go │ ├── inotify_poller.go │ ├── inotify_poller_test.go │ ├── inotify_test.go │ ├── integration_test.go │ ├── kqueue.go │ ├── open_mode_bsd.go │ ├── open_mode_darwin.go │ └── windows.go ├── README.md ├── license.md ├── main.go └── make.sh /Dockerfile.build: -------------------------------------------------------------------------------- 1 | FROM golang:1.4-cross 2 | RUN go get github.com/tools/godep 3 | ADD . /go/src/github.com/cpuguy83/docker-compose-watcher 4 | WORKDIR /go/src/github.com/cpuguy83/docker-compose-watcher 5 | ENV GOOS darwin 6 | CMD ./make.sh 7 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/docker-compose-watcher", 3 | "GoVersion": "go1.4.2", 4 | "Deps": [ 5 | { 6 | "ImportPath": "github.com/codegangsta/cli", 7 | "Comment": "1.2.0-137-gbca61c4", 8 | "Rev": "bca61c476e3c752594983e4c9bcd5f62fb09f157" 9 | }, 10 | { 11 | "ImportPath": "github.com/gorilla/websocket", 12 | "Rev": "6eb6ad425a89d9da7a5549bc6da8f79ba5c17844" 13 | }, 14 | { 15 | "ImportPath": "github.com/jaschaephraim/lrserver", 16 | "Comment": "2.0.1", 17 | "Rev": "cf18adaa26dfc50c5587db0d2fb116d8a54150da" 18 | }, 19 | { 20 | "ImportPath": "gopkg.in/fsnotify.v1", 21 | "Comment": "v1.2.0", 22 | "Rev": "96c060f6a6b7e0d6f75fddd10efeaca3e5d1bcb0" 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/.gitignore: -------------------------------------------------------------------------------- 1 | /pkg 2 | /bin 3 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | 4 | go: 5 | - 1.0.3 6 | - 1.1.2 7 | - 1.2.2 8 | - 1.3.3 9 | - 1.4.2 10 | 11 | script: 12 | - go vet ./... 13 | - go test -v ./... 14 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 Jeremy Saenz 2 | All Rights Reserved. 3 | 4 | MIT LICENSE 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/codegangsta/cli.png?branch=master)](https://travis-ci.org/codegangsta/cli) 2 | 3 | # cli.go 4 | cli.go is simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way. 5 | 6 | You can view the API docs here: 7 | http://godoc.org/github.com/codegangsta/cli 8 | 9 | ## Overview 10 | Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app. 11 | 12 | **This is where cli.go comes into play.** cli.go makes command line programming fun, organized, and expressive! 13 | 14 | ## Installation 15 | Make sure you have a working Go environment (go 1.1+ is *required*). [See the install instructions](http://golang.org/doc/install.html). 16 | 17 | To install `cli.go`, simply run: 18 | ``` 19 | $ go get github.com/codegangsta/cli 20 | ``` 21 | 22 | Make sure your `PATH` includes to the `$GOPATH/bin` directory so your commands can be easily used: 23 | ``` 24 | export PATH=$PATH:$GOPATH/bin 25 | ``` 26 | 27 | ## Getting Started 28 | One of the philosophies behind cli.go is that an API should be playful and full of discovery. So a cli.go app can be as little as one line of code in `main()`. 29 | 30 | ``` go 31 | package main 32 | 33 | import ( 34 | "os" 35 | "github.com/codegangsta/cli" 36 | ) 37 | 38 | func main() { 39 | cli.NewApp().Run(os.Args) 40 | } 41 | ``` 42 | 43 | This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation: 44 | 45 | ``` go 46 | package main 47 | 48 | import ( 49 | "os" 50 | "github.com/codegangsta/cli" 51 | ) 52 | 53 | func main() { 54 | app := cli.NewApp() 55 | app.Name = "boom" 56 | app.Usage = "make an explosive entrance" 57 | app.Action = func(c *cli.Context) { 58 | println("boom! I say!") 59 | } 60 | 61 | app.Run(os.Args) 62 | } 63 | ``` 64 | 65 | Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below. 66 | 67 | ## Example 68 | 69 | Being a programmer can be a lonely job. Thankfully by the power of automation that is not the case! Let's create a greeter app to fend off our demons of loneliness! 70 | 71 | Start by creating a directory named `greet`, and within it, add a file, `greet.go` with the following code in it: 72 | 73 | ``` go 74 | package main 75 | 76 | import ( 77 | "os" 78 | "github.com/codegangsta/cli" 79 | ) 80 | 81 | func main() { 82 | app := cli.NewApp() 83 | app.Name = "greet" 84 | app.Usage = "fight the loneliness!" 85 | app.Action = func(c *cli.Context) { 86 | println("Hello friend!") 87 | } 88 | 89 | app.Run(os.Args) 90 | } 91 | ``` 92 | 93 | Install our command to the `$GOPATH/bin` directory: 94 | 95 | ``` 96 | $ go install 97 | ``` 98 | 99 | Finally run our new command: 100 | 101 | ``` 102 | $ greet 103 | Hello friend! 104 | ``` 105 | 106 | cli.go also generates some bitchass help text: 107 | ``` 108 | $ greet help 109 | NAME: 110 | greet - fight the loneliness! 111 | 112 | USAGE: 113 | greet [global options] command [command options] [arguments...] 114 | 115 | VERSION: 116 | 0.0.0 117 | 118 | COMMANDS: 119 | help, h Shows a list of commands or help for one command 120 | 121 | GLOBAL OPTIONS 122 | --version Shows version information 123 | ``` 124 | 125 | ### Arguments 126 | You can lookup arguments by calling the `Args` function on `cli.Context`. 127 | 128 | ``` go 129 | ... 130 | app.Action = func(c *cli.Context) { 131 | println("Hello", c.Args()[0]) 132 | } 133 | ... 134 | ``` 135 | 136 | ### Flags 137 | Setting and querying flags is simple. 138 | ``` go 139 | ... 140 | app.Flags = []cli.Flag { 141 | cli.StringFlag{ 142 | Name: "lang", 143 | Value: "english", 144 | Usage: "language for the greeting", 145 | }, 146 | } 147 | app.Action = func(c *cli.Context) { 148 | name := "someone" 149 | if len(c.Args()) > 0 { 150 | name = c.Args()[0] 151 | } 152 | if c.String("lang") == "spanish" { 153 | println("Hola", name) 154 | } else { 155 | println("Hello", name) 156 | } 157 | } 158 | ... 159 | ``` 160 | 161 | See full list of flags at http://godoc.org/github.com/codegangsta/cli 162 | 163 | #### Alternate Names 164 | 165 | You can set alternate (or short) names for flags by providing a comma-delimited list for the `Name`. e.g. 166 | 167 | ``` go 168 | app.Flags = []cli.Flag { 169 | cli.StringFlag{ 170 | Name: "lang, l", 171 | Value: "english", 172 | Usage: "language for the greeting", 173 | }, 174 | } 175 | ``` 176 | 177 | That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error. 178 | 179 | #### Values from the Environment 180 | 181 | You can also have the default value set from the environment via `EnvVar`. e.g. 182 | 183 | ``` go 184 | app.Flags = []cli.Flag { 185 | cli.StringFlag{ 186 | Name: "lang, l", 187 | Value: "english", 188 | Usage: "language for the greeting", 189 | EnvVar: "APP_LANG", 190 | }, 191 | } 192 | ``` 193 | 194 | The `EnvVar` may also be given as a comma-delimited "cascade", where the first environment variable that resolves is used as the default. 195 | 196 | ``` go 197 | app.Flags = []cli.Flag { 198 | cli.StringFlag{ 199 | Name: "lang, l", 200 | Value: "english", 201 | Usage: "language for the greeting", 202 | EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG", 203 | }, 204 | } 205 | ``` 206 | 207 | ### Subcommands 208 | 209 | Subcommands can be defined for a more git-like command line app. 210 | ```go 211 | ... 212 | app.Commands = []cli.Command{ 213 | { 214 | Name: "add", 215 | Aliases: []string{"a"}, 216 | Usage: "add a task to the list", 217 | Action: func(c *cli.Context) { 218 | println("added task: ", c.Args().First()) 219 | }, 220 | }, 221 | { 222 | Name: "complete", 223 | Aliases: []string{"c"}, 224 | Usage: "complete a task on the list", 225 | Action: func(c *cli.Context) { 226 | println("completed task: ", c.Args().First()) 227 | }, 228 | }, 229 | { 230 | Name: "template", 231 | Aliases: []string{"r"}, 232 | Usage: "options for task templates", 233 | Subcommands: []cli.Command{ 234 | { 235 | Name: "add", 236 | Usage: "add a new template", 237 | Action: func(c *cli.Context) { 238 | println("new task template: ", c.Args().First()) 239 | }, 240 | }, 241 | { 242 | Name: "remove", 243 | Usage: "remove an existing template", 244 | Action: func(c *cli.Context) { 245 | println("removed task template: ", c.Args().First()) 246 | }, 247 | }, 248 | }, 249 | }, 250 | } 251 | ... 252 | ``` 253 | 254 | ### Bash Completion 255 | 256 | You can enable completion commands by setting the `EnableBashCompletion` 257 | flag on the `App` object. By default, this setting will only auto-complete to 258 | show an app's subcommands, but you can write your own completion methods for 259 | the App or its subcommands. 260 | ```go 261 | ... 262 | var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"} 263 | app := cli.NewApp() 264 | app.EnableBashCompletion = true 265 | app.Commands = []cli.Command{ 266 | { 267 | Name: "complete", 268 | Aliases: []string{"c"}, 269 | Usage: "complete a task on the list", 270 | Action: func(c *cli.Context) { 271 | println("completed task: ", c.Args().First()) 272 | }, 273 | BashComplete: func(c *cli.Context) { 274 | // This will complete if no args are passed 275 | if len(c.Args()) > 0 { 276 | return 277 | } 278 | for _, t := range tasks { 279 | fmt.Println(t) 280 | } 281 | }, 282 | } 283 | } 284 | ... 285 | ``` 286 | 287 | #### To Enable 288 | 289 | Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while 290 | setting the `PROG` variable to the name of your program: 291 | 292 | `PROG=myprogram source /.../cli/autocomplete/bash_autocomplete` 293 | 294 | #### To Distribute 295 | 296 | Copy and modify `autocomplete/bash_autocomplete` to use your program name 297 | rather than `$PROG` and have the user copy the file into 298 | `/etc/bash_completion.d/` (or automatically install it there if you are 299 | distributing a package). Alternatively you can just document that users should 300 | source the generic `autocomplete/bash_autocomplete` with `$PROG` set to your 301 | program name in their bash configuration. 302 | 303 | ## Contribution Guidelines 304 | Feel free to put up a pull request to fix a bug or maybe add a feature. I will give it a code review and make sure that it does not break backwards compatibility. If I or any other collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch. 305 | 306 | If you have contributed something significant to the project, I will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you have any questions feel free to link @codegangsta to the issue in question and we can review it together. 307 | 308 | If you feel like you have contributed to the project but have not yet been added as a collaborator, I probably forgot to add you. Hit @codegangsta up over email and we will get it figured out. 309 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/app.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | "time" 9 | ) 10 | 11 | // App is the main structure of a cli application. It is recomended that 12 | // an app be created with the cli.NewApp() function 13 | type App struct { 14 | // The name of the program. Defaults to os.Args[0] 15 | Name string 16 | // Description of the program. 17 | Usage string 18 | // Version of the program 19 | Version string 20 | // List of commands to execute 21 | Commands []Command 22 | // List of flags to parse 23 | Flags []Flag 24 | // Boolean to enable bash completion commands 25 | EnableBashCompletion bool 26 | // Boolean to hide built-in help command 27 | HideHelp bool 28 | // Boolean to hide built-in version flag 29 | HideVersion bool 30 | // An action to execute when the bash-completion flag is set 31 | BashComplete func(context *Context) 32 | // An action to execute before any subcommands are run, but after the context is ready 33 | // If a non-nil error is returned, no subcommands are run 34 | Before func(context *Context) error 35 | // An action to execute after any subcommands are run, but after the subcommand has finished 36 | // It is run even if Action() panics 37 | After func(context *Context) error 38 | // The action to execute when no subcommands are specified 39 | Action func(context *Context) 40 | // Execute this function if the proper command cannot be found 41 | CommandNotFound func(context *Context, command string) 42 | // Compilation date 43 | Compiled time.Time 44 | // List of all authors who contributed 45 | Authors []Author 46 | // Copyright of the binary if any 47 | Copyright string 48 | // Name of Author (Note: Use App.Authors, this is deprecated) 49 | Author string 50 | // Email of Author (Note: Use App.Authors, this is deprecated) 51 | Email string 52 | // Writer writer to write output to 53 | Writer io.Writer 54 | } 55 | 56 | // Tries to find out when this binary was compiled. 57 | // Returns the current time if it fails to find it. 58 | func compileTime() time.Time { 59 | info, err := os.Stat(os.Args[0]) 60 | if err != nil { 61 | return time.Now() 62 | } 63 | return info.ModTime() 64 | } 65 | 66 | // Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action. 67 | func NewApp() *App { 68 | return &App{ 69 | Name: os.Args[0], 70 | Usage: "A new cli application", 71 | Version: "0.0.0", 72 | BashComplete: DefaultAppComplete, 73 | Action: helpCommand.Action, 74 | Compiled: compileTime(), 75 | Writer: os.Stdout, 76 | } 77 | } 78 | 79 | // Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination 80 | func (a *App) Run(arguments []string) (err error) { 81 | if a.Author != "" || a.Email != "" { 82 | a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email}) 83 | } 84 | 85 | // append help to commands 86 | if a.Command(helpCommand.Name) == nil && !a.HideHelp { 87 | a.Commands = append(a.Commands, helpCommand) 88 | if (HelpFlag != BoolFlag{}) { 89 | a.appendFlag(HelpFlag) 90 | } 91 | } 92 | 93 | //append version/help flags 94 | if a.EnableBashCompletion { 95 | a.appendFlag(BashCompletionFlag) 96 | } 97 | 98 | if !a.HideVersion { 99 | a.appendFlag(VersionFlag) 100 | } 101 | 102 | // parse flags 103 | set := flagSet(a.Name, a.Flags) 104 | set.SetOutput(ioutil.Discard) 105 | err = set.Parse(arguments[1:]) 106 | nerr := normalizeFlags(a.Flags, set) 107 | if nerr != nil { 108 | fmt.Fprintln(a.Writer, nerr) 109 | context := NewContext(a, set, nil) 110 | ShowAppHelp(context) 111 | return nerr 112 | } 113 | context := NewContext(a, set, nil) 114 | 115 | if err != nil { 116 | fmt.Fprintln(a.Writer, "Incorrect Usage.") 117 | fmt.Fprintln(a.Writer) 118 | ShowAppHelp(context) 119 | return err 120 | } 121 | 122 | if checkCompletions(context) { 123 | return nil 124 | } 125 | 126 | if checkHelp(context) { 127 | return nil 128 | } 129 | 130 | if checkVersion(context) { 131 | return nil 132 | } 133 | 134 | if a.After != nil { 135 | defer func() { 136 | afterErr := a.After(context) 137 | if afterErr != nil { 138 | if err != nil { 139 | err = NewMultiError(err, afterErr) 140 | } else { 141 | err = afterErr 142 | } 143 | } 144 | }() 145 | } 146 | 147 | if a.Before != nil { 148 | err := a.Before(context) 149 | if err != nil { 150 | return err 151 | } 152 | } 153 | 154 | args := context.Args() 155 | if args.Present() { 156 | name := args.First() 157 | c := a.Command(name) 158 | if c != nil { 159 | return c.Run(context) 160 | } 161 | } 162 | 163 | // Run default Action 164 | a.Action(context) 165 | return nil 166 | } 167 | 168 | // Another entry point to the cli app, takes care of passing arguments and error handling 169 | func (a *App) RunAndExitOnError() { 170 | if err := a.Run(os.Args); err != nil { 171 | fmt.Fprintln(os.Stderr, err) 172 | os.Exit(1) 173 | } 174 | } 175 | 176 | // Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags 177 | func (a *App) RunAsSubcommand(ctx *Context) (err error) { 178 | // append help to commands 179 | if len(a.Commands) > 0 { 180 | if a.Command(helpCommand.Name) == nil && !a.HideHelp { 181 | a.Commands = append(a.Commands, helpCommand) 182 | if (HelpFlag != BoolFlag{}) { 183 | a.appendFlag(HelpFlag) 184 | } 185 | } 186 | } 187 | 188 | // append flags 189 | if a.EnableBashCompletion { 190 | a.appendFlag(BashCompletionFlag) 191 | } 192 | 193 | // parse flags 194 | set := flagSet(a.Name, a.Flags) 195 | set.SetOutput(ioutil.Discard) 196 | err = set.Parse(ctx.Args().Tail()) 197 | nerr := normalizeFlags(a.Flags, set) 198 | context := NewContext(a, set, ctx) 199 | 200 | if nerr != nil { 201 | fmt.Fprintln(a.Writer, nerr) 202 | fmt.Fprintln(a.Writer) 203 | if len(a.Commands) > 0 { 204 | ShowSubcommandHelp(context) 205 | } else { 206 | ShowCommandHelp(ctx, context.Args().First()) 207 | } 208 | return nerr 209 | } 210 | 211 | if err != nil { 212 | fmt.Fprintln(a.Writer, "Incorrect Usage.") 213 | fmt.Fprintln(a.Writer) 214 | ShowSubcommandHelp(context) 215 | return err 216 | } 217 | 218 | if checkCompletions(context) { 219 | return nil 220 | } 221 | 222 | if len(a.Commands) > 0 { 223 | if checkSubcommandHelp(context) { 224 | return nil 225 | } 226 | } else { 227 | if checkCommandHelp(ctx, context.Args().First()) { 228 | return nil 229 | } 230 | } 231 | 232 | if a.After != nil { 233 | defer func() { 234 | afterErr := a.After(context) 235 | if afterErr != nil { 236 | if err != nil { 237 | err = NewMultiError(err, afterErr) 238 | } else { 239 | err = afterErr 240 | } 241 | } 242 | }() 243 | } 244 | 245 | if a.Before != nil { 246 | err := a.Before(context) 247 | if err != nil { 248 | return err 249 | } 250 | } 251 | 252 | args := context.Args() 253 | if args.Present() { 254 | name := args.First() 255 | c := a.Command(name) 256 | if c != nil { 257 | return c.Run(context) 258 | } 259 | } 260 | 261 | // Run default Action 262 | a.Action(context) 263 | 264 | return nil 265 | } 266 | 267 | // Returns the named command on App. Returns nil if the command does not exist 268 | func (a *App) Command(name string) *Command { 269 | for _, c := range a.Commands { 270 | if c.HasName(name) { 271 | return &c 272 | } 273 | } 274 | 275 | return nil 276 | } 277 | 278 | func (a *App) hasFlag(flag Flag) bool { 279 | for _, f := range a.Flags { 280 | if flag == f { 281 | return true 282 | } 283 | } 284 | 285 | return false 286 | } 287 | 288 | func (a *App) appendFlag(flag Flag) { 289 | if !a.hasFlag(flag) { 290 | a.Flags = append(a.Flags, flag) 291 | } 292 | } 293 | 294 | // Author represents someone who has contributed to a cli project. 295 | type Author struct { 296 | Name string // The Authors name 297 | Email string // The Authors email 298 | } 299 | 300 | // String makes Author comply to the Stringer interface, to allow an easy print in the templating process 301 | func (a Author) String() string { 302 | e := "" 303 | if a.Email != "" { 304 | e = "<" + a.Email + "> " 305 | } 306 | 307 | return fmt.Sprintf("%v %v", a.Name, e) 308 | } 309 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/bash_autocomplete: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | _cli_bash_autocomplete() { 4 | local cur prev opts base 5 | COMPREPLY=() 6 | cur="${COMP_WORDS[COMP_CWORD]}" 7 | prev="${COMP_WORDS[COMP_CWORD-1]}" 8 | opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) 9 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 10 | return 0 11 | } 12 | 13 | complete -F _cli_bash_autocomplete $PROG -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/autocomplete/zsh_autocomplete: -------------------------------------------------------------------------------- 1 | autoload -U compinit && compinit 2 | autoload -U bashcompinit && bashcompinit 3 | 4 | script_dir=$(dirname $0) 5 | source ${script_dir}/bash_autocomplete 6 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/cli.go: -------------------------------------------------------------------------------- 1 | // Package cli provides a minimal framework for creating and organizing command line 2 | // Go applications. cli is designed to be easy to understand and write, the most simple 3 | // cli application can be written as follows: 4 | // func main() { 5 | // cli.NewApp().Run(os.Args) 6 | // } 7 | // 8 | // Of course this application does not do much, so let's make this an actual application: 9 | // func main() { 10 | // app := cli.NewApp() 11 | // app.Name = "greet" 12 | // app.Usage = "say a greeting" 13 | // app.Action = func(c *cli.Context) { 14 | // println("Greetings") 15 | // } 16 | // 17 | // app.Run(os.Args) 18 | // } 19 | package cli 20 | 21 | import ( 22 | "strings" 23 | ) 24 | 25 | type MultiError struct { 26 | Errors []error 27 | } 28 | 29 | func NewMultiError(err ...error) MultiError { 30 | return MultiError{Errors: err} 31 | } 32 | 33 | func (m MultiError) Error() string { 34 | errs := make([]string, len(m.Errors)) 35 | for i, err := range m.Errors { 36 | errs[i] = err.Error() 37 | } 38 | 39 | return strings.Join(errs, "\n") 40 | } 41 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/cli_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/codegangsta/cli" 7 | ) 8 | 9 | func Example() { 10 | app := cli.NewApp() 11 | app.Name = "todo" 12 | app.Usage = "task list on the command line" 13 | app.Commands = []cli.Command{ 14 | { 15 | Name: "add", 16 | Aliases: []string{"a"}, 17 | Usage: "add a task to the list", 18 | Action: func(c *cli.Context) { 19 | println("added task: ", c.Args().First()) 20 | }, 21 | }, 22 | { 23 | Name: "complete", 24 | Aliases: []string{"c"}, 25 | Usage: "complete a task on the list", 26 | Action: func(c *cli.Context) { 27 | println("completed task: ", c.Args().First()) 28 | }, 29 | }, 30 | } 31 | 32 | app.Run(os.Args) 33 | } 34 | 35 | func ExampleSubcommand() { 36 | app := cli.NewApp() 37 | app.Name = "say" 38 | app.Commands = []cli.Command{ 39 | { 40 | Name: "hello", 41 | Aliases: []string{"hi"}, 42 | Usage: "use it to see a description", 43 | Description: "This is how we describe hello the function", 44 | Subcommands: []cli.Command{ 45 | { 46 | Name: "english", 47 | Aliases: []string{"en"}, 48 | Usage: "sends a greeting in english", 49 | Description: "greets someone in english", 50 | Flags: []cli.Flag{ 51 | cli.StringFlag{ 52 | Name: "name", 53 | Value: "Bob", 54 | Usage: "Name of the person to greet", 55 | }, 56 | }, 57 | Action: func(c *cli.Context) { 58 | println("Hello, ", c.String("name")) 59 | }, 60 | }, { 61 | Name: "spanish", 62 | Aliases: []string{"sp"}, 63 | Usage: "sends a greeting in spanish", 64 | Flags: []cli.Flag{ 65 | cli.StringFlag{ 66 | Name: "surname", 67 | Value: "Jones", 68 | Usage: "Surname of the person to greet", 69 | }, 70 | }, 71 | Action: func(c *cli.Context) { 72 | println("Hola, ", c.String("surname")) 73 | }, 74 | }, { 75 | Name: "french", 76 | Aliases: []string{"fr"}, 77 | Usage: "sends a greeting in french", 78 | Flags: []cli.Flag{ 79 | cli.StringFlag{ 80 | Name: "nickname", 81 | Value: "Stevie", 82 | Usage: "Nickname of the person to greet", 83 | }, 84 | }, 85 | Action: func(c *cli.Context) { 86 | println("Bonjour, ", c.String("nickname")) 87 | }, 88 | }, 89 | }, 90 | }, { 91 | Name: "bye", 92 | Usage: "says goodbye", 93 | Action: func(c *cli.Context) { 94 | println("bye") 95 | }, 96 | }, 97 | } 98 | 99 | app.Run(os.Args) 100 | } 101 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/command.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "strings" 7 | ) 8 | 9 | // Command is a subcommand for a cli.App. 10 | type Command struct { 11 | // The name of the command 12 | Name string 13 | // short name of the command. Typically one character (deprecated, use `Aliases`) 14 | ShortName string 15 | // A list of aliases for the command 16 | Aliases []string 17 | // A short description of the usage of this command 18 | Usage string 19 | // A longer explanation of how the command works 20 | Description string 21 | // The function to call when checking for bash command completions 22 | BashComplete func(context *Context) 23 | // An action to execute before any sub-subcommands are run, but after the context is ready 24 | // If a non-nil error is returned, no sub-subcommands are run 25 | Before func(context *Context) error 26 | // An action to execute after any subcommands are run, but after the subcommand has finished 27 | // It is run even if Action() panics 28 | After func(context *Context) error 29 | // The function to call when this command is invoked 30 | Action func(context *Context) 31 | // List of child commands 32 | Subcommands []Command 33 | // List of flags to parse 34 | Flags []Flag 35 | // Treat all flags as normal arguments if true 36 | SkipFlagParsing bool 37 | // Boolean to hide built-in help command 38 | HideHelp bool 39 | 40 | commandNamePath []string 41 | } 42 | 43 | // Returns the full name of the command. 44 | // For subcommands this ensures that parent commands are part of the command path 45 | func (c Command) FullName() string { 46 | if c.commandNamePath == nil { 47 | return c.Name 48 | } 49 | return strings.Join(c.commandNamePath, " ") 50 | } 51 | 52 | // Invokes the command given the context, parses ctx.Args() to generate command-specific flags 53 | func (c Command) Run(ctx *Context) error { 54 | if len(c.Subcommands) > 0 || c.Before != nil || c.After != nil { 55 | return c.startApp(ctx) 56 | } 57 | 58 | if !c.HideHelp && (HelpFlag != BoolFlag{}) { 59 | // append help to flags 60 | c.Flags = append( 61 | c.Flags, 62 | HelpFlag, 63 | ) 64 | } 65 | 66 | if ctx.App.EnableBashCompletion { 67 | c.Flags = append(c.Flags, BashCompletionFlag) 68 | } 69 | 70 | set := flagSet(c.Name, c.Flags) 71 | set.SetOutput(ioutil.Discard) 72 | 73 | firstFlagIndex := -1 74 | terminatorIndex := -1 75 | for index, arg := range ctx.Args() { 76 | if arg == "--" { 77 | terminatorIndex = index 78 | break 79 | } else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 { 80 | firstFlagIndex = index 81 | } 82 | } 83 | 84 | var err error 85 | if firstFlagIndex > -1 && !c.SkipFlagParsing { 86 | args := ctx.Args() 87 | regularArgs := make([]string, len(args[1:firstFlagIndex])) 88 | copy(regularArgs, args[1:firstFlagIndex]) 89 | 90 | var flagArgs []string 91 | if terminatorIndex > -1 { 92 | flagArgs = args[firstFlagIndex:terminatorIndex] 93 | regularArgs = append(regularArgs, args[terminatorIndex:]...) 94 | } else { 95 | flagArgs = args[firstFlagIndex:] 96 | } 97 | 98 | err = set.Parse(append(flagArgs, regularArgs...)) 99 | } else { 100 | err = set.Parse(ctx.Args().Tail()) 101 | } 102 | 103 | if err != nil { 104 | fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.") 105 | fmt.Fprintln(ctx.App.Writer) 106 | ShowCommandHelp(ctx, c.Name) 107 | return err 108 | } 109 | 110 | nerr := normalizeFlags(c.Flags, set) 111 | if nerr != nil { 112 | fmt.Fprintln(ctx.App.Writer, nerr) 113 | fmt.Fprintln(ctx.App.Writer) 114 | ShowCommandHelp(ctx, c.Name) 115 | return nerr 116 | } 117 | context := NewContext(ctx.App, set, ctx) 118 | 119 | if checkCommandCompletions(context, c.Name) { 120 | return nil 121 | } 122 | 123 | if checkCommandHelp(context, c.Name) { 124 | return nil 125 | } 126 | context.Command = c 127 | c.Action(context) 128 | return nil 129 | } 130 | 131 | func (c Command) Names() []string { 132 | names := []string{c.Name} 133 | 134 | if c.ShortName != "" { 135 | names = append(names, c.ShortName) 136 | } 137 | 138 | return append(names, c.Aliases...) 139 | } 140 | 141 | // Returns true if Command.Name or Command.ShortName matches given name 142 | func (c Command) HasName(name string) bool { 143 | for _, n := range c.Names() { 144 | if n == name { 145 | return true 146 | } 147 | } 148 | return false 149 | } 150 | 151 | func (c Command) startApp(ctx *Context) error { 152 | app := NewApp() 153 | 154 | // set the name and usage 155 | app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name) 156 | if c.Description != "" { 157 | app.Usage = c.Description 158 | } else { 159 | app.Usage = c.Usage 160 | } 161 | 162 | // set CommandNotFound 163 | app.CommandNotFound = ctx.App.CommandNotFound 164 | 165 | // set the flags and commands 166 | app.Commands = c.Subcommands 167 | app.Flags = c.Flags 168 | app.HideHelp = c.HideHelp 169 | 170 | app.Version = ctx.App.Version 171 | app.HideVersion = ctx.App.HideVersion 172 | app.Compiled = ctx.App.Compiled 173 | app.Author = ctx.App.Author 174 | app.Email = ctx.App.Email 175 | app.Writer = ctx.App.Writer 176 | 177 | // bash completion 178 | app.EnableBashCompletion = ctx.App.EnableBashCompletion 179 | if c.BashComplete != nil { 180 | app.BashComplete = c.BashComplete 181 | } 182 | 183 | // set the actions 184 | app.Before = c.Before 185 | app.After = c.After 186 | if c.Action != nil { 187 | app.Action = c.Action 188 | } else { 189 | app.Action = helpSubcommand.Action 190 | } 191 | 192 | var newCmds []Command 193 | for _, cc := range app.Commands { 194 | cc.commandNamePath = []string{c.Name, cc.Name} 195 | newCmds = append(newCmds, cc) 196 | } 197 | app.Commands = newCmds 198 | 199 | return app.RunAsSubcommand(ctx) 200 | } 201 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/command_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "flag" 5 | "testing" 6 | 7 | "github.com/codegangsta/cli" 8 | ) 9 | 10 | func TestCommandDoNotIgnoreFlags(t *testing.T) { 11 | app := cli.NewApp() 12 | set := flag.NewFlagSet("test", 0) 13 | test := []string{"blah", "blah", "-break"} 14 | set.Parse(test) 15 | 16 | c := cli.NewContext(app, set, nil) 17 | 18 | command := cli.Command{ 19 | Name: "test-cmd", 20 | Aliases: []string{"tc"}, 21 | Usage: "this is for testing", 22 | Description: "testing", 23 | Action: func(_ *cli.Context) {}, 24 | } 25 | err := command.Run(c) 26 | 27 | expect(t, err.Error(), "flag provided but not defined: -break") 28 | } 29 | 30 | func TestCommandIgnoreFlags(t *testing.T) { 31 | app := cli.NewApp() 32 | set := flag.NewFlagSet("test", 0) 33 | test := []string{"blah", "blah"} 34 | set.Parse(test) 35 | 36 | c := cli.NewContext(app, set, nil) 37 | 38 | command := cli.Command{ 39 | Name: "test-cmd", 40 | Aliases: []string{"tc"}, 41 | Usage: "this is for testing", 42 | Description: "testing", 43 | Action: func(_ *cli.Context) {}, 44 | SkipFlagParsing: true, 45 | } 46 | err := command.Run(c) 47 | 48 | expect(t, err, nil) 49 | } 50 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/context.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "strconv" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | // Context is a type that is passed through to 12 | // each Handler action in a cli application. Context 13 | // can be used to retrieve context-specific Args and 14 | // parsed command-line options. 15 | type Context struct { 16 | App *App 17 | Command Command 18 | flagSet *flag.FlagSet 19 | setFlags map[string]bool 20 | globalSetFlags map[string]bool 21 | parentContext *Context 22 | } 23 | 24 | // Creates a new context. For use in when invoking an App or Command action. 25 | func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { 26 | return &Context{App: app, flagSet: set, parentContext: parentCtx} 27 | } 28 | 29 | // Looks up the value of a local int flag, returns 0 if no int flag exists 30 | func (c *Context) Int(name string) int { 31 | return lookupInt(name, c.flagSet) 32 | } 33 | 34 | // Looks up the value of a local time.Duration flag, returns 0 if no time.Duration flag exists 35 | func (c *Context) Duration(name string) time.Duration { 36 | return lookupDuration(name, c.flagSet) 37 | } 38 | 39 | // Looks up the value of a local float64 flag, returns 0 if no float64 flag exists 40 | func (c *Context) Float64(name string) float64 { 41 | return lookupFloat64(name, c.flagSet) 42 | } 43 | 44 | // Looks up the value of a local bool flag, returns false if no bool flag exists 45 | func (c *Context) Bool(name string) bool { 46 | return lookupBool(name, c.flagSet) 47 | } 48 | 49 | // Looks up the value of a local boolT flag, returns false if no bool flag exists 50 | func (c *Context) BoolT(name string) bool { 51 | return lookupBoolT(name, c.flagSet) 52 | } 53 | 54 | // Looks up the value of a local string flag, returns "" if no string flag exists 55 | func (c *Context) String(name string) string { 56 | return lookupString(name, c.flagSet) 57 | } 58 | 59 | // Looks up the value of a local string slice flag, returns nil if no string slice flag exists 60 | func (c *Context) StringSlice(name string) []string { 61 | return lookupStringSlice(name, c.flagSet) 62 | } 63 | 64 | // Looks up the value of a local int slice flag, returns nil if no int slice flag exists 65 | func (c *Context) IntSlice(name string) []int { 66 | return lookupIntSlice(name, c.flagSet) 67 | } 68 | 69 | // Looks up the value of a local generic flag, returns nil if no generic flag exists 70 | func (c *Context) Generic(name string) interface{} { 71 | return lookupGeneric(name, c.flagSet) 72 | } 73 | 74 | // Looks up the value of a global int flag, returns 0 if no int flag exists 75 | func (c *Context) GlobalInt(name string) int { 76 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 77 | return lookupInt(name, fs) 78 | } 79 | return 0 80 | } 81 | 82 | // Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists 83 | func (c *Context) GlobalDuration(name string) time.Duration { 84 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 85 | return lookupDuration(name, fs) 86 | } 87 | return 0 88 | } 89 | 90 | // Looks up the value of a global bool flag, returns false if no bool flag exists 91 | func (c *Context) GlobalBool(name string) bool { 92 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 93 | return lookupBool(name, fs) 94 | } 95 | return false 96 | } 97 | 98 | // Looks up the value of a global string flag, returns "" if no string flag exists 99 | func (c *Context) GlobalString(name string) string { 100 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 101 | return lookupString(name, fs) 102 | } 103 | return "" 104 | } 105 | 106 | // Looks up the value of a global string slice flag, returns nil if no string slice flag exists 107 | func (c *Context) GlobalStringSlice(name string) []string { 108 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 109 | return lookupStringSlice(name, fs) 110 | } 111 | return nil 112 | } 113 | 114 | // Looks up the value of a global int slice flag, returns nil if no int slice flag exists 115 | func (c *Context) GlobalIntSlice(name string) []int { 116 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 117 | return lookupIntSlice(name, fs) 118 | } 119 | return nil 120 | } 121 | 122 | // Looks up the value of a global generic flag, returns nil if no generic flag exists 123 | func (c *Context) GlobalGeneric(name string) interface{} { 124 | if fs := lookupGlobalFlagSet(name, c); fs != nil { 125 | return lookupGeneric(name, fs) 126 | } 127 | return nil 128 | } 129 | 130 | // Returns the number of flags set 131 | func (c *Context) NumFlags() int { 132 | return c.flagSet.NFlag() 133 | } 134 | 135 | // Determines if the flag was actually set 136 | func (c *Context) IsSet(name string) bool { 137 | if c.setFlags == nil { 138 | c.setFlags = make(map[string]bool) 139 | c.flagSet.Visit(func(f *flag.Flag) { 140 | c.setFlags[f.Name] = true 141 | }) 142 | } 143 | return c.setFlags[name] == true 144 | } 145 | 146 | // Determines if the global flag was actually set 147 | func (c *Context) GlobalIsSet(name string) bool { 148 | if c.globalSetFlags == nil { 149 | c.globalSetFlags = make(map[string]bool) 150 | ctx := c 151 | if ctx.parentContext != nil { 152 | ctx = ctx.parentContext 153 | } 154 | for ; ctx != nil && c.globalSetFlags[name] == false; ctx = ctx.parentContext { 155 | ctx.flagSet.Visit(func(f *flag.Flag) { 156 | c.globalSetFlags[f.Name] = true 157 | }) 158 | } 159 | } 160 | return c.globalSetFlags[name] 161 | } 162 | 163 | // Returns a slice of flag names used in this context. 164 | func (c *Context) FlagNames() (names []string) { 165 | for _, flag := range c.Command.Flags { 166 | name := strings.Split(flag.getName(), ",")[0] 167 | if name == "help" { 168 | continue 169 | } 170 | names = append(names, name) 171 | } 172 | return 173 | } 174 | 175 | // Returns a slice of global flag names used by the app. 176 | func (c *Context) GlobalFlagNames() (names []string) { 177 | for _, flag := range c.App.Flags { 178 | name := strings.Split(flag.getName(), ",")[0] 179 | if name == "help" || name == "version" { 180 | continue 181 | } 182 | names = append(names, name) 183 | } 184 | return 185 | } 186 | 187 | // Returns the parent context, if any 188 | func (c *Context) Parent() *Context { 189 | return c.parentContext 190 | } 191 | 192 | type Args []string 193 | 194 | // Returns the command line arguments associated with the context. 195 | func (c *Context) Args() Args { 196 | args := Args(c.flagSet.Args()) 197 | return args 198 | } 199 | 200 | // Returns the nth argument, or else a blank string 201 | func (a Args) Get(n int) string { 202 | if len(a) > n { 203 | return a[n] 204 | } 205 | return "" 206 | } 207 | 208 | // Returns the first argument, or else a blank string 209 | func (a Args) First() string { 210 | return a.Get(0) 211 | } 212 | 213 | // Return the rest of the arguments (not the first one) 214 | // or else an empty string slice 215 | func (a Args) Tail() []string { 216 | if len(a) >= 2 { 217 | return []string(a)[1:] 218 | } 219 | return []string{} 220 | } 221 | 222 | // Checks if there are any arguments present 223 | func (a Args) Present() bool { 224 | return len(a) != 0 225 | } 226 | 227 | // Swaps arguments at the given indexes 228 | func (a Args) Swap(from, to int) error { 229 | if from >= len(a) || to >= len(a) { 230 | return errors.New("index out of range") 231 | } 232 | a[from], a[to] = a[to], a[from] 233 | return nil 234 | } 235 | 236 | func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { 237 | if ctx.parentContext != nil { 238 | ctx = ctx.parentContext 239 | } 240 | for ; ctx != nil; ctx = ctx.parentContext { 241 | if f := ctx.flagSet.Lookup(name); f != nil { 242 | return ctx.flagSet 243 | } 244 | } 245 | return nil 246 | } 247 | 248 | func lookupInt(name string, set *flag.FlagSet) int { 249 | f := set.Lookup(name) 250 | if f != nil { 251 | val, err := strconv.Atoi(f.Value.String()) 252 | if err != nil { 253 | return 0 254 | } 255 | return val 256 | } 257 | 258 | return 0 259 | } 260 | 261 | func lookupDuration(name string, set *flag.FlagSet) time.Duration { 262 | f := set.Lookup(name) 263 | if f != nil { 264 | val, err := time.ParseDuration(f.Value.String()) 265 | if err == nil { 266 | return val 267 | } 268 | } 269 | 270 | return 0 271 | } 272 | 273 | func lookupFloat64(name string, set *flag.FlagSet) float64 { 274 | f := set.Lookup(name) 275 | if f != nil { 276 | val, err := strconv.ParseFloat(f.Value.String(), 64) 277 | if err != nil { 278 | return 0 279 | } 280 | return val 281 | } 282 | 283 | return 0 284 | } 285 | 286 | func lookupString(name string, set *flag.FlagSet) string { 287 | f := set.Lookup(name) 288 | if f != nil { 289 | return f.Value.String() 290 | } 291 | 292 | return "" 293 | } 294 | 295 | func lookupStringSlice(name string, set *flag.FlagSet) []string { 296 | f := set.Lookup(name) 297 | if f != nil { 298 | return (f.Value.(*StringSlice)).Value() 299 | 300 | } 301 | 302 | return nil 303 | } 304 | 305 | func lookupIntSlice(name string, set *flag.FlagSet) []int { 306 | f := set.Lookup(name) 307 | if f != nil { 308 | return (f.Value.(*IntSlice)).Value() 309 | 310 | } 311 | 312 | return nil 313 | } 314 | 315 | func lookupGeneric(name string, set *flag.FlagSet) interface{} { 316 | f := set.Lookup(name) 317 | if f != nil { 318 | return f.Value 319 | } 320 | return nil 321 | } 322 | 323 | func lookupBool(name string, set *flag.FlagSet) bool { 324 | f := set.Lookup(name) 325 | if f != nil { 326 | val, err := strconv.ParseBool(f.Value.String()) 327 | if err != nil { 328 | return false 329 | } 330 | return val 331 | } 332 | 333 | return false 334 | } 335 | 336 | func lookupBoolT(name string, set *flag.FlagSet) bool { 337 | f := set.Lookup(name) 338 | if f != nil { 339 | val, err := strconv.ParseBool(f.Value.String()) 340 | if err != nil { 341 | return true 342 | } 343 | return val 344 | } 345 | 346 | return false 347 | } 348 | 349 | func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { 350 | switch ff.Value.(type) { 351 | case *StringSlice: 352 | default: 353 | set.Set(name, ff.Value.String()) 354 | } 355 | } 356 | 357 | func normalizeFlags(flags []Flag, set *flag.FlagSet) error { 358 | visited := make(map[string]bool) 359 | set.Visit(func(f *flag.Flag) { 360 | visited[f.Name] = true 361 | }) 362 | for _, f := range flags { 363 | parts := strings.Split(f.getName(), ",") 364 | if len(parts) == 1 { 365 | continue 366 | } 367 | var ff *flag.Flag 368 | for _, name := range parts { 369 | name = strings.Trim(name, " ") 370 | if visited[name] { 371 | if ff != nil { 372 | return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) 373 | } 374 | ff = set.Lookup(name) 375 | } 376 | } 377 | if ff == nil { 378 | continue 379 | } 380 | for _, name := range parts { 381 | name = strings.Trim(name, " ") 382 | if !visited[name] { 383 | copyFlag(name, ff, set) 384 | } 385 | } 386 | } 387 | return nil 388 | } 389 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/context_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "flag" 5 | "testing" 6 | "time" 7 | 8 | "github.com/codegangsta/cli" 9 | ) 10 | 11 | func TestNewContext(t *testing.T) { 12 | set := flag.NewFlagSet("test", 0) 13 | set.Int("myflag", 12, "doc") 14 | globalSet := flag.NewFlagSet("test", 0) 15 | globalSet.Int("myflag", 42, "doc") 16 | globalCtx := cli.NewContext(nil, globalSet, nil) 17 | command := cli.Command{Name: "mycommand"} 18 | c := cli.NewContext(nil, set, globalCtx) 19 | c.Command = command 20 | expect(t, c.Int("myflag"), 12) 21 | expect(t, c.GlobalInt("myflag"), 42) 22 | expect(t, c.Command.Name, "mycommand") 23 | } 24 | 25 | func TestContext_Int(t *testing.T) { 26 | set := flag.NewFlagSet("test", 0) 27 | set.Int("myflag", 12, "doc") 28 | c := cli.NewContext(nil, set, nil) 29 | expect(t, c.Int("myflag"), 12) 30 | } 31 | 32 | func TestContext_Duration(t *testing.T) { 33 | set := flag.NewFlagSet("test", 0) 34 | set.Duration("myflag", time.Duration(12*time.Second), "doc") 35 | c := cli.NewContext(nil, set, nil) 36 | expect(t, c.Duration("myflag"), time.Duration(12*time.Second)) 37 | } 38 | 39 | func TestContext_String(t *testing.T) { 40 | set := flag.NewFlagSet("test", 0) 41 | set.String("myflag", "hello world", "doc") 42 | c := cli.NewContext(nil, set, nil) 43 | expect(t, c.String("myflag"), "hello world") 44 | } 45 | 46 | func TestContext_Bool(t *testing.T) { 47 | set := flag.NewFlagSet("test", 0) 48 | set.Bool("myflag", false, "doc") 49 | c := cli.NewContext(nil, set, nil) 50 | expect(t, c.Bool("myflag"), false) 51 | } 52 | 53 | func TestContext_BoolT(t *testing.T) { 54 | set := flag.NewFlagSet("test", 0) 55 | set.Bool("myflag", true, "doc") 56 | c := cli.NewContext(nil, set, nil) 57 | expect(t, c.BoolT("myflag"), true) 58 | } 59 | 60 | func TestContext_Args(t *testing.T) { 61 | set := flag.NewFlagSet("test", 0) 62 | set.Bool("myflag", false, "doc") 63 | c := cli.NewContext(nil, set, nil) 64 | set.Parse([]string{"--myflag", "bat", "baz"}) 65 | expect(t, len(c.Args()), 2) 66 | expect(t, c.Bool("myflag"), true) 67 | } 68 | 69 | func TestContext_IsSet(t *testing.T) { 70 | set := flag.NewFlagSet("test", 0) 71 | set.Bool("myflag", false, "doc") 72 | set.String("otherflag", "hello world", "doc") 73 | globalSet := flag.NewFlagSet("test", 0) 74 | globalSet.Bool("myflagGlobal", true, "doc") 75 | globalCtx := cli.NewContext(nil, globalSet, nil) 76 | c := cli.NewContext(nil, set, globalCtx) 77 | set.Parse([]string{"--myflag", "bat", "baz"}) 78 | globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) 79 | expect(t, c.IsSet("myflag"), true) 80 | expect(t, c.IsSet("otherflag"), false) 81 | expect(t, c.IsSet("bogusflag"), false) 82 | expect(t, c.IsSet("myflagGlobal"), false) 83 | } 84 | 85 | func TestContext_GlobalIsSet(t *testing.T) { 86 | set := flag.NewFlagSet("test", 0) 87 | set.Bool("myflag", false, "doc") 88 | set.String("otherflag", "hello world", "doc") 89 | globalSet := flag.NewFlagSet("test", 0) 90 | globalSet.Bool("myflagGlobal", true, "doc") 91 | globalSet.Bool("myflagGlobalUnset", true, "doc") 92 | globalCtx := cli.NewContext(nil, globalSet, nil) 93 | c := cli.NewContext(nil, set, globalCtx) 94 | set.Parse([]string{"--myflag", "bat", "baz"}) 95 | globalSet.Parse([]string{"--myflagGlobal", "bat", "baz"}) 96 | expect(t, c.GlobalIsSet("myflag"), false) 97 | expect(t, c.GlobalIsSet("otherflag"), false) 98 | expect(t, c.GlobalIsSet("bogusflag"), false) 99 | expect(t, c.GlobalIsSet("myflagGlobal"), true) 100 | expect(t, c.GlobalIsSet("myflagGlobalUnset"), false) 101 | expect(t, c.GlobalIsSet("bogusGlobal"), false) 102 | } 103 | 104 | func TestContext_NumFlags(t *testing.T) { 105 | set := flag.NewFlagSet("test", 0) 106 | set.Bool("myflag", false, "doc") 107 | set.String("otherflag", "hello world", "doc") 108 | globalSet := flag.NewFlagSet("test", 0) 109 | globalSet.Bool("myflagGlobal", true, "doc") 110 | globalCtx := cli.NewContext(nil, globalSet, nil) 111 | c := cli.NewContext(nil, set, globalCtx) 112 | set.Parse([]string{"--myflag", "--otherflag=foo"}) 113 | globalSet.Parse([]string{"--myflagGlobal"}) 114 | expect(t, c.NumFlags(), 2) 115 | } 116 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/help.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | "text/tabwriter" 8 | "text/template" 9 | ) 10 | 11 | // The text template for the Default help topic. 12 | // cli.go uses text/template to render templates. You can 13 | // render custom help text by setting this variable. 14 | var AppHelpTemplate = `NAME: 15 | {{.Name}} - {{.Usage}} 16 | 17 | USAGE: 18 | {{.Name}} {{if .Flags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} [arguments...] 19 | {{if .Version}} 20 | VERSION: 21 | {{.Version}} 22 | {{end}}{{if len .Authors}} 23 | AUTHOR(S): 24 | {{range .Authors}}{{ . }}{{end}} 25 | {{end}}{{if .Commands}} 26 | COMMANDS: 27 | {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} 28 | {{end}}{{end}}{{if .Flags}} 29 | GLOBAL OPTIONS: 30 | {{range .Flags}}{{.}} 31 | {{end}}{{end}}{{if .Copyright }} 32 | COPYRIGHT: 33 | {{.Copyright}} 34 | {{end}} 35 | ` 36 | 37 | // The text template for the command help topic. 38 | // cli.go uses text/template to render templates. You can 39 | // render custom help text by setting this variable. 40 | var CommandHelpTemplate = `NAME: 41 | {{.FullName}} - {{.Usage}} 42 | 43 | USAGE: 44 | command {{.FullName}}{{if .Flags}} [command options]{{end}} [arguments...]{{if .Description}} 45 | 46 | DESCRIPTION: 47 | {{.Description}}{{end}}{{if .Flags}} 48 | 49 | OPTIONS: 50 | {{range .Flags}}{{.}} 51 | {{end}}{{ end }} 52 | ` 53 | 54 | // The text template for the subcommand help topic. 55 | // cli.go uses text/template to render templates. You can 56 | // render custom help text by setting this variable. 57 | var SubcommandHelpTemplate = `NAME: 58 | {{.Name}} - {{.Usage}} 59 | 60 | USAGE: 61 | {{.Name}} command{{if .Flags}} [command options]{{end}} [arguments...] 62 | 63 | COMMANDS: 64 | {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}} 65 | {{end}}{{if .Flags}} 66 | OPTIONS: 67 | {{range .Flags}}{{.}} 68 | {{end}}{{end}} 69 | ` 70 | 71 | var helpCommand = Command{ 72 | Name: "help", 73 | Aliases: []string{"h"}, 74 | Usage: "Shows a list of commands or help for one command", 75 | Action: func(c *Context) { 76 | args := c.Args() 77 | if args.Present() { 78 | ShowCommandHelp(c, args.First()) 79 | } else { 80 | ShowAppHelp(c) 81 | } 82 | }, 83 | } 84 | 85 | var helpSubcommand = Command{ 86 | Name: "help", 87 | Aliases: []string{"h"}, 88 | Usage: "Shows a list of commands or help for one command", 89 | Action: func(c *Context) { 90 | args := c.Args() 91 | if args.Present() { 92 | ShowCommandHelp(c, args.First()) 93 | } else { 94 | ShowSubcommandHelp(c) 95 | } 96 | }, 97 | } 98 | 99 | // Prints help for the App or Command 100 | type helpPrinter func(w io.Writer, templ string, data interface{}) 101 | 102 | var HelpPrinter helpPrinter = printHelp 103 | 104 | // Prints version for the App 105 | var VersionPrinter = printVersion 106 | 107 | func ShowAppHelp(c *Context) { 108 | HelpPrinter(c.App.Writer, AppHelpTemplate, c.App) 109 | } 110 | 111 | // Prints the list of subcommands as the default app completion method 112 | func DefaultAppComplete(c *Context) { 113 | for _, command := range c.App.Commands { 114 | for _, name := range command.Names() { 115 | fmt.Fprintln(c.App.Writer, name) 116 | } 117 | } 118 | } 119 | 120 | // Prints help for the given command 121 | func ShowCommandHelp(ctx *Context, command string) { 122 | // show the subcommand help for a command with subcommands 123 | if command == "" { 124 | HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) 125 | return 126 | } 127 | 128 | for _, c := range ctx.App.Commands { 129 | if c.HasName(command) { 130 | HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c) 131 | return 132 | } 133 | } 134 | 135 | if ctx.App.CommandNotFound != nil { 136 | ctx.App.CommandNotFound(ctx, command) 137 | } else { 138 | fmt.Fprintf(ctx.App.Writer, "No help topic for '%v'\n", command) 139 | } 140 | } 141 | 142 | // Prints help for the given subcommand 143 | func ShowSubcommandHelp(c *Context) { 144 | ShowCommandHelp(c, c.Command.Name) 145 | } 146 | 147 | // Prints the version number of the App 148 | func ShowVersion(c *Context) { 149 | VersionPrinter(c) 150 | } 151 | 152 | func printVersion(c *Context) { 153 | fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version) 154 | } 155 | 156 | // Prints the lists of commands within a given context 157 | func ShowCompletions(c *Context) { 158 | a := c.App 159 | if a != nil && a.BashComplete != nil { 160 | a.BashComplete(c) 161 | } 162 | } 163 | 164 | // Prints the custom completions for a given command 165 | func ShowCommandCompletions(ctx *Context, command string) { 166 | c := ctx.App.Command(command) 167 | if c != nil && c.BashComplete != nil { 168 | c.BashComplete(ctx) 169 | } 170 | } 171 | 172 | func printHelp(out io.Writer, templ string, data interface{}) { 173 | funcMap := template.FuncMap{ 174 | "join": strings.Join, 175 | } 176 | 177 | w := tabwriter.NewWriter(out, 0, 8, 1, '\t', 0) 178 | t := template.Must(template.New("help").Funcs(funcMap).Parse(templ)) 179 | err := t.Execute(w, data) 180 | if err != nil { 181 | panic(err) 182 | } 183 | w.Flush() 184 | } 185 | 186 | func checkVersion(c *Context) bool { 187 | if c.GlobalBool("version") || c.GlobalBool("v") || c.Bool("version") || c.Bool("v") { 188 | ShowVersion(c) 189 | return true 190 | } 191 | 192 | return false 193 | } 194 | 195 | func checkHelp(c *Context) bool { 196 | if c.GlobalBool("h") || c.GlobalBool("help") || c.Bool("h") || c.Bool("help") { 197 | ShowAppHelp(c) 198 | return true 199 | } 200 | 201 | return false 202 | } 203 | 204 | func checkCommandHelp(c *Context, name string) bool { 205 | if c.Bool("h") || c.Bool("help") { 206 | ShowCommandHelp(c, name) 207 | return true 208 | } 209 | 210 | return false 211 | } 212 | 213 | func checkSubcommandHelp(c *Context) bool { 214 | if c.GlobalBool("h") || c.GlobalBool("help") { 215 | ShowSubcommandHelp(c) 216 | return true 217 | } 218 | 219 | return false 220 | } 221 | 222 | func checkCompletions(c *Context) bool { 223 | if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion { 224 | ShowCompletions(c) 225 | return true 226 | } 227 | 228 | return false 229 | } 230 | 231 | func checkCommandCompletions(c *Context, name string) bool { 232 | if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion { 233 | ShowCommandCompletions(c, name) 234 | return true 235 | } 236 | 237 | return false 238 | } 239 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/help_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/codegangsta/cli" 8 | ) 9 | 10 | func Test_ShowAppHelp_NoAuthor(t *testing.T) { 11 | output := new(bytes.Buffer) 12 | app := cli.NewApp() 13 | app.Writer = output 14 | 15 | c := cli.NewContext(app, nil, nil) 16 | 17 | cli.ShowAppHelp(c) 18 | 19 | if bytes.Index(output.Bytes(), []byte("AUTHOR(S):")) != -1 { 20 | t.Errorf("expected\n%snot to include %s", output.String(), "AUTHOR(S):") 21 | } 22 | } 23 | 24 | func Test_ShowAppHelp_NoVersion(t *testing.T) { 25 | output := new(bytes.Buffer) 26 | app := cli.NewApp() 27 | app.Writer = output 28 | 29 | app.Version = "" 30 | 31 | c := cli.NewContext(app, nil, nil) 32 | 33 | cli.ShowAppHelp(c) 34 | 35 | if bytes.Index(output.Bytes(), []byte("VERSION:")) != -1 { 36 | t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/codegangsta/cli/helpers_test.go: -------------------------------------------------------------------------------- 1 | package cli_test 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | /* Test Helpers */ 9 | func expect(t *testing.T, a interface{}, b interface{}) { 10 | if a != b { 11 | t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 12 | } 13 | } 14 | 15 | func refute(t *testing.T, a interface{}, b interface{}) { 16 | if a == b { 17 | t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.1 5 | - 1.2 6 | - tip 7 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of Gorilla WebSocket authors for copyright 2 | # purposes. 3 | # 4 | # Please keep the list sorted. 5 | 6 | Gary Burd 7 | Joachim Bauch 8 | 9 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/README.md: -------------------------------------------------------------------------------- 1 | # Gorilla WebSocket 2 | 3 | Gorilla WebSocket is a [Go](http://golang.org/) implementation of the 4 | [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. 5 | 6 | ### Documentation 7 | 8 | * [API Reference](http://godoc.org/github.com/gorilla/websocket) 9 | * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) 10 | * [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch) 11 | 12 | ### Status 13 | 14 | The Gorilla WebSocket package provides a complete and tested implementation of 15 | the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The 16 | package API is stable. 17 | 18 | ### Installation 19 | 20 | go get github.com/gorilla/websocket 21 | 22 | ### Protocol Compliance 23 | 24 | The Gorilla WebSocket package passes the server tests in the [Autobahn Test 25 | Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn 26 | subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). 27 | 28 | ### Gorilla WebSocket compared with other packages 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
github.com/gorillagolang.org/x/net
RFC 6455 Features
Passes Autobahn Test SuiteYesNo
Receive fragmented messageYesNo, see note 1
Send close messageYesNo
Send pings and receive pongsYesNo
Get the type of a received data messageYesYes, see note 2
Other Features
Limit size of received messageYesNo
Read message using io.ReaderYesNo, see note 3
Write message using io.WriteCloserYesNo, see note 3
48 | 49 | Notes: 50 | 51 | 1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html). 52 | 2. The application can get the type of a received data message by implementing 53 | a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal) 54 | function. 55 | 3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries. 56 | Read returns when the input buffer is full or a frame boundary is 57 | encountered. Each call to Write sends a single frame message. The Gorilla 58 | io.Reader and io.WriteCloser operate on a single WebSocket message. 59 | 60 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/bench_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | func BenchmarkMaskBytes(b *testing.B) { 12 | var key [4]byte 13 | data := make([]byte, 1024) 14 | pos := 0 15 | for i := 0; i < b.N; i++ { 16 | pos = maskBytes(key, pos, data) 17 | } 18 | b.SetBytes(int64(len(data))) 19 | } 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "bytes" 9 | "crypto/tls" 10 | "errors" 11 | "io" 12 | "io/ioutil" 13 | "net" 14 | "net/http" 15 | "net/url" 16 | "strings" 17 | "time" 18 | ) 19 | 20 | // ErrBadHandshake is returned when the server response to opening handshake is 21 | // invalid. 22 | var ErrBadHandshake = errors.New("websocket: bad handshake") 23 | 24 | // NewClient creates a new client connection using the given net connection. 25 | // The URL u specifies the host and request URI. Use requestHeader to specify 26 | // the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies 27 | // (Cookie). Use the response.Header to get the selected subprotocol 28 | // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). 29 | // 30 | // If the WebSocket handshake fails, ErrBadHandshake is returned along with a 31 | // non-nil *http.Response so that callers can handle redirects, authentication, 32 | // etc. 33 | func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) { 34 | challengeKey, err := generateChallengeKey() 35 | if err != nil { 36 | return nil, nil, err 37 | } 38 | acceptKey := computeAcceptKey(challengeKey) 39 | 40 | c = newConn(netConn, false, readBufSize, writeBufSize) 41 | p := c.writeBuf[:0] 42 | p = append(p, "GET "...) 43 | p = append(p, u.RequestURI()...) 44 | p = append(p, " HTTP/1.1\r\nHost: "...) 45 | p = append(p, u.Host...) 46 | // "Upgrade" is capitalized for servers that do not use case insensitive 47 | // comparisons on header tokens. 48 | p = append(p, "\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: "...) 49 | p = append(p, challengeKey...) 50 | p = append(p, "\r\n"...) 51 | for k, vs := range requestHeader { 52 | for _, v := range vs { 53 | p = append(p, k...) 54 | p = append(p, ": "...) 55 | p = append(p, v...) 56 | p = append(p, "\r\n"...) 57 | } 58 | } 59 | p = append(p, "\r\n"...) 60 | 61 | if _, err := netConn.Write(p); err != nil { 62 | return nil, nil, err 63 | } 64 | 65 | resp, err := http.ReadResponse(c.br, &http.Request{Method: "GET", URL: u}) 66 | if err != nil { 67 | return nil, nil, err 68 | } 69 | if resp.StatusCode != 101 || 70 | !strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") || 71 | !strings.EqualFold(resp.Header.Get("Connection"), "upgrade") || 72 | resp.Header.Get("Sec-Websocket-Accept") != acceptKey { 73 | return nil, resp, ErrBadHandshake 74 | } 75 | c.subprotocol = resp.Header.Get("Sec-Websocket-Protocol") 76 | return c, resp, nil 77 | } 78 | 79 | // A Dialer contains options for connecting to WebSocket server. 80 | type Dialer struct { 81 | // NetDial specifies the dial function for creating TCP connections. If 82 | // NetDial is nil, net.Dial is used. 83 | NetDial func(network, addr string) (net.Conn, error) 84 | 85 | // TLSClientConfig specifies the TLS configuration to use with tls.Client. 86 | // If nil, the default configuration is used. 87 | TLSClientConfig *tls.Config 88 | 89 | // HandshakeTimeout specifies the duration for the handshake to complete. 90 | HandshakeTimeout time.Duration 91 | 92 | // Input and output buffer sizes. If the buffer size is zero, then a 93 | // default value of 4096 is used. 94 | ReadBufferSize, WriteBufferSize int 95 | 96 | // Subprotocols specifies the client's requested subprotocols. 97 | Subprotocols []string 98 | } 99 | 100 | var errMalformedURL = errors.New("malformed ws or wss URL") 101 | 102 | // parseURL parses the URL. The url.Parse function is not used here because 103 | // url.Parse mangles the path. 104 | func parseURL(s string) (*url.URL, error) { 105 | // From the RFC: 106 | // 107 | // ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ] 108 | // wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ] 109 | // 110 | // We don't use the net/url parser here because the dialer interface does 111 | // not provide a way for applications to work around percent deocding in 112 | // the net/url parser. 113 | 114 | var u url.URL 115 | switch { 116 | case strings.HasPrefix(s, "ws://"): 117 | u.Scheme = "ws" 118 | s = s[len("ws://"):] 119 | case strings.HasPrefix(s, "wss://"): 120 | u.Scheme = "wss" 121 | s = s[len("wss://"):] 122 | default: 123 | return nil, errMalformedURL 124 | } 125 | 126 | u.Host = s 127 | u.Opaque = "/" 128 | if i := strings.Index(s, "/"); i >= 0 { 129 | u.Host = s[:i] 130 | u.Opaque = s[i:] 131 | } 132 | 133 | if strings.Contains(u.Host, "@") { 134 | // WebSocket URIs do not contain user information. 135 | return nil, errMalformedURL 136 | } 137 | 138 | return &u, nil 139 | } 140 | 141 | func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) { 142 | hostPort = u.Host 143 | hostNoPort = u.Host 144 | if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") { 145 | hostNoPort = hostNoPort[:i] 146 | } else { 147 | if u.Scheme == "wss" { 148 | hostPort += ":443" 149 | } else { 150 | hostPort += ":80" 151 | } 152 | } 153 | return hostPort, hostNoPort 154 | } 155 | 156 | // DefaultDialer is a dialer with all fields set to the default zero values. 157 | var DefaultDialer *Dialer 158 | 159 | // Dial creates a new client connection. Use requestHeader to specify the 160 | // origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie). 161 | // Use the response.Header to get the selected subprotocol 162 | // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). 163 | // 164 | // If the WebSocket handshake fails, ErrBadHandshake is returned along with a 165 | // non-nil *http.Response so that callers can handle redirects, authentication, 166 | // etcetera. The response body may not contain the entire response and does not 167 | // need to be closed by the application. 168 | func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) { 169 | u, err := parseURL(urlStr) 170 | if err != nil { 171 | return nil, nil, err 172 | } 173 | 174 | hostPort, hostNoPort := hostPortNoPort(u) 175 | 176 | if d == nil { 177 | d = &Dialer{} 178 | } 179 | 180 | var deadline time.Time 181 | if d.HandshakeTimeout != 0 { 182 | deadline = time.Now().Add(d.HandshakeTimeout) 183 | } 184 | 185 | netDial := d.NetDial 186 | if netDial == nil { 187 | netDialer := &net.Dialer{Deadline: deadline} 188 | netDial = netDialer.Dial 189 | } 190 | 191 | netConn, err := netDial("tcp", hostPort) 192 | if err != nil { 193 | return nil, nil, err 194 | } 195 | 196 | defer func() { 197 | if netConn != nil { 198 | netConn.Close() 199 | } 200 | }() 201 | 202 | if err := netConn.SetDeadline(deadline); err != nil { 203 | return nil, nil, err 204 | } 205 | 206 | if u.Scheme == "wss" { 207 | cfg := d.TLSClientConfig 208 | if cfg == nil { 209 | cfg = &tls.Config{ServerName: hostNoPort} 210 | } else if cfg.ServerName == "" { 211 | shallowCopy := *cfg 212 | cfg = &shallowCopy 213 | cfg.ServerName = hostNoPort 214 | } 215 | tlsConn := tls.Client(netConn, cfg) 216 | netConn = tlsConn 217 | if err := tlsConn.Handshake(); err != nil { 218 | return nil, nil, err 219 | } 220 | if !cfg.InsecureSkipVerify { 221 | if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil { 222 | return nil, nil, err 223 | } 224 | } 225 | } 226 | 227 | if len(d.Subprotocols) > 0 { 228 | h := http.Header{} 229 | for k, v := range requestHeader { 230 | h[k] = v 231 | } 232 | h.Set("Sec-Websocket-Protocol", strings.Join(d.Subprotocols, ", ")) 233 | requestHeader = h 234 | } 235 | 236 | if len(requestHeader["Host"]) > 0 { 237 | // This can be used to supply a Host: header which is different from 238 | // the dial address. 239 | u.Host = requestHeader.Get("Host") 240 | 241 | // Drop "Host" header 242 | h := http.Header{} 243 | for k, v := range requestHeader { 244 | if k == "Host" { 245 | continue 246 | } 247 | h[k] = v 248 | } 249 | requestHeader = h 250 | } 251 | 252 | conn, resp, err := NewClient(netConn, u, requestHeader, d.ReadBufferSize, d.WriteBufferSize) 253 | 254 | if err != nil { 255 | if err == ErrBadHandshake { 256 | // Before closing the network connection on return from this 257 | // function, slurp up some of the response to aid application 258 | // debugging. 259 | buf := make([]byte, 1024) 260 | n, _ := io.ReadFull(resp.Body, buf) 261 | resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n])) 262 | } 263 | return nil, resp, err 264 | } 265 | 266 | netConn.SetDeadline(time.Time{}) 267 | netConn = nil // to avoid close in defer. 268 | return conn, resp, nil 269 | } 270 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/client_server_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "crypto/tls" 9 | "crypto/x509" 10 | "io" 11 | "io/ioutil" 12 | "net" 13 | "net/http" 14 | "net/http/httptest" 15 | "net/url" 16 | "reflect" 17 | "strings" 18 | "testing" 19 | "time" 20 | ) 21 | 22 | var cstUpgrader = Upgrader{ 23 | Subprotocols: []string{"p0", "p1"}, 24 | ReadBufferSize: 1024, 25 | WriteBufferSize: 1024, 26 | Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) { 27 | http.Error(w, reason.Error(), status) 28 | }, 29 | } 30 | 31 | var cstDialer = Dialer{ 32 | Subprotocols: []string{"p1", "p2"}, 33 | ReadBufferSize: 1024, 34 | WriteBufferSize: 1024, 35 | } 36 | 37 | type cstHandler struct{ *testing.T } 38 | 39 | type cstServer struct { 40 | *httptest.Server 41 | URL string 42 | } 43 | 44 | func newServer(t *testing.T) *cstServer { 45 | var s cstServer 46 | s.Server = httptest.NewServer(cstHandler{t}) 47 | s.URL = makeWsProto(s.Server.URL) 48 | return &s 49 | } 50 | 51 | func newTLSServer(t *testing.T) *cstServer { 52 | var s cstServer 53 | s.Server = httptest.NewTLSServer(cstHandler{t}) 54 | s.URL = makeWsProto(s.Server.URL) 55 | return &s 56 | } 57 | 58 | func (t cstHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 59 | if r.Method != "GET" { 60 | t.Logf("method %s not allowed", r.Method) 61 | http.Error(w, "method not allowed", 405) 62 | return 63 | } 64 | subprotos := Subprotocols(r) 65 | if !reflect.DeepEqual(subprotos, cstDialer.Subprotocols) { 66 | t.Logf("subprotols=%v, want %v", subprotos, cstDialer.Subprotocols) 67 | http.Error(w, "bad protocol", 400) 68 | return 69 | } 70 | ws, err := cstUpgrader.Upgrade(w, r, http.Header{"Set-Cookie": {"sessionID=1234"}}) 71 | if err != nil { 72 | t.Logf("Upgrade: %v", err) 73 | return 74 | } 75 | defer ws.Close() 76 | 77 | if ws.Subprotocol() != "p1" { 78 | t.Logf("Subprotocol() = %s, want p1", ws.Subprotocol()) 79 | ws.Close() 80 | return 81 | } 82 | op, rd, err := ws.NextReader() 83 | if err != nil { 84 | t.Logf("NextReader: %v", err) 85 | return 86 | } 87 | wr, err := ws.NextWriter(op) 88 | if err != nil { 89 | t.Logf("NextWriter: %v", err) 90 | return 91 | } 92 | if _, err = io.Copy(wr, rd); err != nil { 93 | t.Logf("NextWriter: %v", err) 94 | return 95 | } 96 | if err := wr.Close(); err != nil { 97 | t.Logf("Close: %v", err) 98 | return 99 | } 100 | } 101 | 102 | func makeWsProto(s string) string { 103 | return "ws" + strings.TrimPrefix(s, "http") 104 | } 105 | 106 | func sendRecv(t *testing.T, ws *Conn) { 107 | const message = "Hello World!" 108 | if err := ws.SetWriteDeadline(time.Now().Add(time.Second)); err != nil { 109 | t.Fatalf("SetWriteDeadline: %v", err) 110 | } 111 | if err := ws.WriteMessage(TextMessage, []byte(message)); err != nil { 112 | t.Fatalf("WriteMessage: %v", err) 113 | } 114 | if err := ws.SetReadDeadline(time.Now().Add(time.Second)); err != nil { 115 | t.Fatalf("SetReadDeadline: %v", err) 116 | } 117 | _, p, err := ws.ReadMessage() 118 | if err != nil { 119 | t.Fatalf("ReadMessage: %v", err) 120 | } 121 | if string(p) != message { 122 | t.Fatalf("message=%s, want %s", p, message) 123 | } 124 | } 125 | 126 | func TestDial(t *testing.T) { 127 | s := newServer(t) 128 | defer s.Close() 129 | 130 | ws, _, err := cstDialer.Dial(s.URL, nil) 131 | if err != nil { 132 | t.Fatalf("Dial: %v", err) 133 | } 134 | defer ws.Close() 135 | sendRecv(t, ws) 136 | } 137 | 138 | func TestDialTLS(t *testing.T) { 139 | s := newTLSServer(t) 140 | defer s.Close() 141 | 142 | certs := x509.NewCertPool() 143 | for _, c := range s.TLS.Certificates { 144 | roots, err := x509.ParseCertificates(c.Certificate[len(c.Certificate)-1]) 145 | if err != nil { 146 | t.Fatalf("error parsing server's root cert: %v", err) 147 | } 148 | for _, root := range roots { 149 | certs.AddCert(root) 150 | } 151 | } 152 | 153 | u, _ := url.Parse(s.URL) 154 | d := cstDialer 155 | d.NetDial = func(network, addr string) (net.Conn, error) { return net.Dial(network, u.Host) } 156 | d.TLSClientConfig = &tls.Config{RootCAs: certs} 157 | ws, _, err := d.Dial("wss://example.com/", nil) 158 | if err != nil { 159 | t.Fatalf("Dial: %v", err) 160 | } 161 | defer ws.Close() 162 | sendRecv(t, ws) 163 | } 164 | 165 | func xTestDialTLSBadCert(t *testing.T) { 166 | // This test is deactivated because of noisy logging from the net/http package. 167 | s := newTLSServer(t) 168 | defer s.Close() 169 | 170 | ws, _, err := cstDialer.Dial(s.URL, nil) 171 | if err == nil { 172 | ws.Close() 173 | t.Fatalf("Dial: nil") 174 | } 175 | } 176 | 177 | func xTestDialTLSNoVerify(t *testing.T) { 178 | s := newTLSServer(t) 179 | defer s.Close() 180 | 181 | d := cstDialer 182 | d.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} 183 | ws, _, err := d.Dial(s.URL, nil) 184 | if err != nil { 185 | t.Fatalf("Dial: %v", err) 186 | } 187 | defer ws.Close() 188 | sendRecv(t, ws) 189 | } 190 | 191 | func TestDialTimeout(t *testing.T) { 192 | s := newServer(t) 193 | defer s.Close() 194 | 195 | d := cstDialer 196 | d.HandshakeTimeout = -1 197 | ws, _, err := d.Dial(s.URL, nil) 198 | if err == nil { 199 | ws.Close() 200 | t.Fatalf("Dial: nil") 201 | } 202 | } 203 | 204 | func TestDialBadScheme(t *testing.T) { 205 | s := newServer(t) 206 | defer s.Close() 207 | 208 | ws, _, err := cstDialer.Dial(s.Server.URL, nil) 209 | if err == nil { 210 | ws.Close() 211 | t.Fatalf("Dial: nil") 212 | } 213 | } 214 | 215 | func TestDialBadOrigin(t *testing.T) { 216 | s := newServer(t) 217 | defer s.Close() 218 | 219 | ws, resp, err := cstDialer.Dial(s.URL, http.Header{"Origin": {"bad"}}) 220 | if err == nil { 221 | ws.Close() 222 | t.Fatalf("Dial: nil") 223 | } 224 | if resp == nil { 225 | t.Fatalf("resp=nil, err=%v", err) 226 | } 227 | if resp.StatusCode != http.StatusForbidden { 228 | t.Fatalf("status=%d, want %d", resp.StatusCode, http.StatusForbidden) 229 | } 230 | } 231 | 232 | func TestHandshake(t *testing.T) { 233 | s := newServer(t) 234 | defer s.Close() 235 | 236 | ws, resp, err := cstDialer.Dial(s.URL, http.Header{"Origin": {s.URL}}) 237 | if err != nil { 238 | t.Fatalf("Dial: %v", err) 239 | } 240 | defer ws.Close() 241 | 242 | var sessionID string 243 | for _, c := range resp.Cookies() { 244 | if c.Name == "sessionID" { 245 | sessionID = c.Value 246 | } 247 | } 248 | if sessionID != "1234" { 249 | t.Error("Set-Cookie not received from the server.") 250 | } 251 | 252 | if ws.Subprotocol() != "p1" { 253 | t.Errorf("ws.Subprotocol() = %s, want p1", ws.Subprotocol()) 254 | } 255 | sendRecv(t, ws) 256 | } 257 | 258 | func TestRespOnBadHandshake(t *testing.T) { 259 | const expectedStatus = http.StatusGone 260 | const expectedBody = "This is the response body." 261 | 262 | s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 263 | w.WriteHeader(expectedStatus) 264 | io.WriteString(w, expectedBody) 265 | })) 266 | defer s.Close() 267 | 268 | ws, resp, err := cstDialer.Dial(makeWsProto(s.URL), nil) 269 | if err == nil { 270 | ws.Close() 271 | t.Fatalf("Dial: nil") 272 | } 273 | 274 | if resp == nil { 275 | t.Fatalf("resp=nil, err=%v", err) 276 | } 277 | 278 | if resp.StatusCode != expectedStatus { 279 | t.Errorf("resp.StatusCode=%d, want %d", resp.StatusCode, expectedStatus) 280 | } 281 | 282 | p, err := ioutil.ReadAll(resp.Body) 283 | if err != nil { 284 | t.Fatalf("ReadFull(resp.Body) returned error %v", err) 285 | } 286 | 287 | if string(p) != expectedBody { 288 | t.Errorf("resp.Body=%s, want %s", p, expectedBody) 289 | } 290 | } 291 | 292 | // If the Host header is specified in `Dial()`, the server must receive it as 293 | // the `Host:` header. 294 | func TestHostHeader(t *testing.T) { 295 | s := newServer(t) 296 | defer s.Close() 297 | 298 | specifiedHost := make(chan string, 1) 299 | origHandler := s.Server.Config.Handler 300 | 301 | // Capture the request Host header. 302 | s.Server.Config.Handler = http.HandlerFunc( 303 | func(w http.ResponseWriter, r *http.Request) { 304 | specifiedHost <- r.Host 305 | origHandler.ServeHTTP(w, r) 306 | }) 307 | 308 | ws, resp, err := cstDialer.Dial(s.URL, http.Header{"Host": {"testhost"}}) 309 | if err != nil { 310 | t.Fatalf("Dial: %v", err) 311 | } 312 | defer ws.Close() 313 | 314 | if resp.StatusCode != http.StatusSwitchingProtocols { 315 | t.Fatalf("resp.StatusCode = %v, want http.StatusSwitchingProtocols", resp.StatusCode) 316 | } 317 | 318 | if gotHost := <-specifiedHost; gotHost != "testhost" { 319 | t.Fatalf("gotHost = %q, want \"testhost\"", gotHost) 320 | } 321 | 322 | sendRecv(t, ws) 323 | } 324 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/client_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "net/url" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | var parseURLTests = []struct { 14 | s string 15 | u *url.URL 16 | }{ 17 | {"ws://example.com/", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}}, 18 | {"ws://example.com", &url.URL{Scheme: "ws", Host: "example.com", Opaque: "/"}}, 19 | {"ws://example.com:7777/", &url.URL{Scheme: "ws", Host: "example.com:7777", Opaque: "/"}}, 20 | {"wss://example.com/", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/"}}, 21 | {"wss://example.com/a/b", &url.URL{Scheme: "wss", Host: "example.com", Opaque: "/a/b"}}, 22 | {"ss://example.com/a/b", nil}, 23 | {"ws://webmaster@example.com/", nil}, 24 | } 25 | 26 | func TestParseURL(t *testing.T) { 27 | for _, tt := range parseURLTests { 28 | u, err := parseURL(tt.s) 29 | if tt.u != nil && err != nil { 30 | t.Errorf("parseURL(%q) returned error %v", tt.s, err) 31 | continue 32 | } 33 | if tt.u == nil && err == nil { 34 | t.Errorf("parseURL(%q) did not return error", tt.s) 35 | continue 36 | } 37 | if !reflect.DeepEqual(u, tt.u) { 38 | t.Errorf("parseURL(%q) returned %v, want %v", tt.s, u, tt.u) 39 | continue 40 | } 41 | } 42 | } 43 | 44 | var hostPortNoPortTests = []struct { 45 | u *url.URL 46 | hostPort, hostNoPort string 47 | }{ 48 | {&url.URL{Scheme: "ws", Host: "example.com"}, "example.com:80", "example.com"}, 49 | {&url.URL{Scheme: "wss", Host: "example.com"}, "example.com:443", "example.com"}, 50 | {&url.URL{Scheme: "ws", Host: "example.com:7777"}, "example.com:7777", "example.com"}, 51 | {&url.URL{Scheme: "wss", Host: "example.com:7777"}, "example.com:7777", "example.com"}, 52 | } 53 | 54 | func TestHostPortNoPort(t *testing.T) { 55 | for _, tt := range hostPortNoPortTests { 56 | hostPort, hostNoPort := hostPortNoPort(tt.u) 57 | if hostPort != tt.hostPort { 58 | t.Errorf("hostPortNoPort(%v) returned hostPort %q, want %q", tt.u, hostPort, tt.hostPort) 59 | } 60 | if hostNoPort != tt.hostNoPort { 61 | t.Errorf("hostPortNoPort(%v) returned hostNoPort %q, want %q", tt.u, hostNoPort, tt.hostNoPort) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/conn_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | "io/ioutil" 12 | "net" 13 | "testing" 14 | "testing/iotest" 15 | "time" 16 | ) 17 | 18 | var _ net.Error = errWriteTimeout 19 | 20 | type fakeNetConn struct { 21 | io.Reader 22 | io.Writer 23 | } 24 | 25 | func (c fakeNetConn) Close() error { return nil } 26 | func (c fakeNetConn) LocalAddr() net.Addr { return nil } 27 | func (c fakeNetConn) RemoteAddr() net.Addr { return nil } 28 | func (c fakeNetConn) SetDeadline(t time.Time) error { return nil } 29 | func (c fakeNetConn) SetReadDeadline(t time.Time) error { return nil } 30 | func (c fakeNetConn) SetWriteDeadline(t time.Time) error { return nil } 31 | 32 | func TestFraming(t *testing.T) { 33 | frameSizes := []int{0, 1, 2, 124, 125, 126, 127, 128, 129, 65534, 65535, 65536, 65537} 34 | var readChunkers = []struct { 35 | name string 36 | f func(io.Reader) io.Reader 37 | }{ 38 | {"half", iotest.HalfReader}, 39 | {"one", iotest.OneByteReader}, 40 | {"asis", func(r io.Reader) io.Reader { return r }}, 41 | } 42 | 43 | writeBuf := make([]byte, 65537) 44 | for i := range writeBuf { 45 | writeBuf[i] = byte(i) 46 | } 47 | 48 | for _, isServer := range []bool{true, false} { 49 | for _, chunker := range readChunkers { 50 | 51 | var connBuf bytes.Buffer 52 | wc := newConn(fakeNetConn{Reader: nil, Writer: &connBuf}, isServer, 1024, 1024) 53 | rc := newConn(fakeNetConn{Reader: chunker.f(&connBuf), Writer: nil}, !isServer, 1024, 1024) 54 | 55 | for _, n := range frameSizes { 56 | for _, iocopy := range []bool{true, false} { 57 | name := fmt.Sprintf("s:%v, r:%s, n:%d c:%v", isServer, chunker.name, n, iocopy) 58 | 59 | w, err := wc.NextWriter(TextMessage) 60 | if err != nil { 61 | t.Errorf("%s: wc.NextWriter() returned %v", name, err) 62 | continue 63 | } 64 | var nn int 65 | if iocopy { 66 | var n64 int64 67 | n64, err = io.Copy(w, bytes.NewReader(writeBuf[:n])) 68 | nn = int(n64) 69 | } else { 70 | nn, err = w.Write(writeBuf[:n]) 71 | } 72 | if err != nil || nn != n { 73 | t.Errorf("%s: w.Write(writeBuf[:n]) returned %d, %v", name, nn, err) 74 | continue 75 | } 76 | err = w.Close() 77 | if err != nil { 78 | t.Errorf("%s: w.Close() returned %v", name, err) 79 | continue 80 | } 81 | 82 | opCode, r, err := rc.NextReader() 83 | if err != nil || opCode != TextMessage { 84 | t.Errorf("%s: NextReader() returned %d, r, %v", name, opCode, err) 85 | continue 86 | } 87 | rbuf, err := ioutil.ReadAll(r) 88 | if err != nil { 89 | t.Errorf("%s: ReadFull() returned rbuf, %v", name, err) 90 | continue 91 | } 92 | 93 | if len(rbuf) != n { 94 | t.Errorf("%s: len(rbuf) is %d, want %d", name, len(rbuf), n) 95 | continue 96 | } 97 | 98 | for i, b := range rbuf { 99 | if byte(i) != b { 100 | t.Errorf("%s: bad byte at offset %d", name, i) 101 | break 102 | } 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | func TestControl(t *testing.T) { 111 | const message = "this is a ping/pong messsage" 112 | for _, isServer := range []bool{true, false} { 113 | for _, isWriteControl := range []bool{true, false} { 114 | name := fmt.Sprintf("s:%v, wc:%v", isServer, isWriteControl) 115 | var connBuf bytes.Buffer 116 | wc := newConn(fakeNetConn{Reader: nil, Writer: &connBuf}, isServer, 1024, 1024) 117 | rc := newConn(fakeNetConn{Reader: &connBuf, Writer: nil}, !isServer, 1024, 1024) 118 | if isWriteControl { 119 | wc.WriteControl(PongMessage, []byte(message), time.Now().Add(time.Second)) 120 | } else { 121 | w, err := wc.NextWriter(PongMessage) 122 | if err != nil { 123 | t.Errorf("%s: wc.NextWriter() returned %v", name, err) 124 | continue 125 | } 126 | if _, err := w.Write([]byte(message)); err != nil { 127 | t.Errorf("%s: w.Write() returned %v", name, err) 128 | continue 129 | } 130 | if err := w.Close(); err != nil { 131 | t.Errorf("%s: w.Close() returned %v", name, err) 132 | continue 133 | } 134 | var actualMessage string 135 | rc.SetPongHandler(func(s string) error { actualMessage = s; return nil }) 136 | rc.NextReader() 137 | if actualMessage != message { 138 | t.Errorf("%s: pong=%q, want %q", name, actualMessage, message) 139 | continue 140 | } 141 | } 142 | } 143 | } 144 | } 145 | 146 | func TestCloseBeforeFinalFrame(t *testing.T) { 147 | const bufSize = 512 148 | 149 | var b1, b2 bytes.Buffer 150 | wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, bufSize) 151 | rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, 1024, 1024) 152 | 153 | w, _ := wc.NextWriter(BinaryMessage) 154 | w.Write(make([]byte, bufSize+bufSize/2)) 155 | wc.WriteControl(CloseMessage, FormatCloseMessage(CloseNormalClosure, ""), time.Now().Add(10*time.Second)) 156 | w.Close() 157 | 158 | op, r, err := rc.NextReader() 159 | if op != BinaryMessage || err != nil { 160 | t.Fatalf("NextReader() returned %d, %v", op, err) 161 | } 162 | _, err = io.Copy(ioutil.Discard, r) 163 | if err != errUnexpectedEOF { 164 | t.Fatalf("io.Copy() returned %v, want %v", err, errUnexpectedEOF) 165 | } 166 | _, _, err = rc.NextReader() 167 | if err != io.EOF { 168 | t.Fatalf("NextReader() returned %v, want %v", err, io.EOF) 169 | } 170 | } 171 | 172 | func TestEOFBeforeFinalFrame(t *testing.T) { 173 | const bufSize = 512 174 | 175 | var b1, b2 bytes.Buffer 176 | wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, bufSize) 177 | rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, 1024, 1024) 178 | 179 | w, _ := wc.NextWriter(BinaryMessage) 180 | w.Write(make([]byte, bufSize+bufSize/2)) 181 | 182 | op, r, err := rc.NextReader() 183 | if op != BinaryMessage || err != nil { 184 | t.Fatalf("NextReader() returned %d, %v", op, err) 185 | } 186 | _, err = io.Copy(ioutil.Discard, r) 187 | if err != errUnexpectedEOF { 188 | t.Fatalf("io.Copy() returned %v, want %v", err, errUnexpectedEOF) 189 | } 190 | _, _, err = rc.NextReader() 191 | if err != errUnexpectedEOF { 192 | t.Fatalf("NextReader() returned %v, want %v", err, errUnexpectedEOF) 193 | } 194 | } 195 | 196 | func TestReadLimit(t *testing.T) { 197 | 198 | const readLimit = 512 199 | message := make([]byte, readLimit+1) 200 | 201 | var b1, b2 bytes.Buffer 202 | wc := newConn(fakeNetConn{Reader: nil, Writer: &b1}, false, 1024, readLimit-2) 203 | rc := newConn(fakeNetConn{Reader: &b1, Writer: &b2}, true, 1024, 1024) 204 | rc.SetReadLimit(readLimit) 205 | 206 | // Send message at the limit with interleaved pong. 207 | w, _ := wc.NextWriter(BinaryMessage) 208 | w.Write(message[:readLimit-1]) 209 | wc.WriteControl(PongMessage, []byte("this is a pong"), time.Now().Add(10*time.Second)) 210 | w.Write(message[:1]) 211 | w.Close() 212 | 213 | // Send message larger than the limit. 214 | wc.WriteMessage(BinaryMessage, message[:readLimit+1]) 215 | 216 | op, _, err := rc.NextReader() 217 | if op != BinaryMessage || err != nil { 218 | t.Fatalf("1: NextReader() returned %d, %v", op, err) 219 | } 220 | op, r, err := rc.NextReader() 221 | if op != BinaryMessage || err != nil { 222 | t.Fatalf("2: NextReader() returned %d, %v", op, err) 223 | } 224 | _, err = io.Copy(ioutil.Discard, r) 225 | if err != ErrReadLimit { 226 | t.Fatalf("io.Copy() returned %v", err) 227 | } 228 | } 229 | 230 | func TestUnderlyingConn(t *testing.T) { 231 | var b1, b2 bytes.Buffer 232 | fc := fakeNetConn{Reader: &b1, Writer: &b2} 233 | c := newConn(fc, true, 1024, 1024) 234 | ul := c.UnderlyingConn() 235 | if ul != fc { 236 | t.Fatalf("Underlying conn is not what it should be.") 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package websocket implements the WebSocket protocol defined in RFC 6455. 6 | // 7 | // Overview 8 | // 9 | // The Conn type represents a WebSocket connection. A server application uses 10 | // the Upgrade function from an Upgrader object with a HTTP request handler 11 | // to get a pointer to a Conn: 12 | // 13 | // var upgrader = websocket.Upgrader{ 14 | // ReadBufferSize: 1024, 15 | // WriteBufferSize: 1024, 16 | // } 17 | // 18 | // func handler(w http.ResponseWriter, r *http.Request) { 19 | // conn, err := upgrader.Upgrade(w, r, nil) 20 | // if err != nil { 21 | // log.Println(err) 22 | // return 23 | // } 24 | // ... Use conn to send and receive messages. 25 | // } 26 | // 27 | // Call the connection's WriteMessage and ReadMessage methods to send and 28 | // receive messages as a slice of bytes. This snippet of code shows how to echo 29 | // messages using these methods: 30 | // 31 | // for { 32 | // messageType, p, err := conn.ReadMessage() 33 | // if err != nil { 34 | // return 35 | // } 36 | // if err = conn.WriteMessage(messageType, p); err != nil { 37 | // return err 38 | // } 39 | // } 40 | // 41 | // In above snippet of code, p is a []byte and messageType is an int with value 42 | // websocket.BinaryMessage or websocket.TextMessage. 43 | // 44 | // An application can also send and receive messages using the io.WriteCloser 45 | // and io.Reader interfaces. To send a message, call the connection NextWriter 46 | // method to get an io.WriteCloser, write the message to the writer and close 47 | // the writer when done. To receive a message, call the connection NextReader 48 | // method to get an io.Reader and read until io.EOF is returned. This snippet 49 | // snippet shows how to echo messages using the NextWriter and NextReader 50 | // methods: 51 | // 52 | // for { 53 | // messageType, r, err := conn.NextReader() 54 | // if err != nil { 55 | // return 56 | // } 57 | // w, err := conn.NextWriter(messageType) 58 | // if err != nil { 59 | // return err 60 | // } 61 | // if _, err := io.Copy(w, r); err != nil { 62 | // return err 63 | // } 64 | // if err := w.Close(); err != nil { 65 | // return err 66 | // } 67 | // } 68 | // 69 | // Data Messages 70 | // 71 | // The WebSocket protocol distinguishes between text and binary data messages. 72 | // Text messages are interpreted as UTF-8 encoded text. The interpretation of 73 | // binary messages is left to the application. 74 | // 75 | // This package uses the TextMessage and BinaryMessage integer constants to 76 | // identify the two data message types. The ReadMessage and NextReader methods 77 | // return the type of the received message. The messageType argument to the 78 | // WriteMessage and NextWriter methods specifies the type of a sent message. 79 | // 80 | // It is the application's responsibility to ensure that text messages are 81 | // valid UTF-8 encoded text. 82 | // 83 | // Control Messages 84 | // 85 | // The WebSocket protocol defines three types of control messages: close, ping 86 | // and pong. Call the connection WriteControl, WriteMessage or NextWriter 87 | // methods to send a control message to the peer. 88 | // 89 | // Connections handle received ping and pong messages by invoking a callback 90 | // function set with SetPingHandler and SetPongHandler methods. These callback 91 | // functions can be invoked from the ReadMessage method, the NextReader method 92 | // or from a call to the data message reader returned from NextReader. 93 | // 94 | // Connections handle received close messages by returning an error from the 95 | // ReadMessage method, the NextReader method or from a call to the data message 96 | // reader returned from NextReader. 97 | // 98 | // Concurrency 99 | // 100 | // Connections do not support concurrent calls to the write methods 101 | // (NextWriter, SetWriteDeadline, WriteMessage) or concurrent calls to the read 102 | // methods methods (NextReader, SetReadDeadline, ReadMessage). Connections do 103 | // support a concurrent reader and writer. 104 | // 105 | // The Close and WriteControl methods can be called concurrently with all other 106 | // methods. 107 | // 108 | // Read is Required 109 | // 110 | // The application must read the connection to process ping and close messages 111 | // sent from the peer. If the application is not otherwise interested in 112 | // messages from the peer, then the application should start a goroutine to read 113 | // and discard messages from the peer. A simple example is: 114 | // 115 | // func readLoop(c *websocket.Conn) { 116 | // for { 117 | // if _, _, err := c.NextReader(); err != nil { 118 | // c.Close() 119 | // break 120 | // } 121 | // } 122 | // } 123 | // 124 | // Origin Considerations 125 | // 126 | // Web browsers allow Javascript applications to open a WebSocket connection to 127 | // any host. It's up to the server to enforce an origin policy using the Origin 128 | // request header sent by the browser. 129 | // 130 | // The Upgrader calls the function specified in the CheckOrigin field to check 131 | // the origin. If the CheckOrigin function returns false, then the Upgrade 132 | // method fails the WebSocket handshake with HTTP status 403. 133 | // 134 | // If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail 135 | // the handshake if the Origin request header is present and not equal to the 136 | // Host request header. 137 | // 138 | // An application can allow connections from any origin by specifying a 139 | // function that always returns true: 140 | // 141 | // var upgrader = websocket.Upgrader{ 142 | // CheckOrigin: func(r *http.Request) bool { return true }, 143 | // } 144 | // 145 | // The deprecated Upgrade function does not enforce an origin policy. It's the 146 | // application's responsibility to check the Origin header before calling 147 | // Upgrade. 148 | package websocket 149 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/examples/autobahn/README.md: -------------------------------------------------------------------------------- 1 | # Test Server 2 | 3 | This package contains a server for the [Autobahn WebSockets Test Suite](http://autobahn.ws/testsuite). 4 | 5 | To test the server, run 6 | 7 | go run server.go 8 | 9 | and start the client test driver 10 | 11 | wstest -m fuzzingclient -s fuzzingclient.json 12 | 13 | When the client completes, it writes a report to reports/clients/index.html. 14 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/examples/autobahn/fuzzingclient.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "options": {"failByDrop": false}, 4 | "outdir": "./reports/clients", 5 | "servers": [ 6 | {"agent": "ReadAllWriteMessage", "url": "ws://localhost:9000/m", "options": {"version": 18}}, 7 | {"agent": "ReadAllWrite", "url": "ws://localhost:9000/r", "options": {"version": 18}}, 8 | {"agent": "CopyFull", "url": "ws://localhost:9000/f", "options": {"version": 18}}, 9 | {"agent": "CopyWriterOnly", "url": "ws://localhost:9000/c", "options": {"version": 18}} 10 | ], 11 | "cases": ["*"], 12 | "exclude-cases": [], 13 | "exclude-agent-cases": {} 14 | } 15 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/examples/autobahn/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Command server is a test server for the Autobahn WebSockets Test Suite. 6 | package main 7 | 8 | import ( 9 | "errors" 10 | "flag" 11 | "github.com/gorilla/websocket" 12 | "io" 13 | "log" 14 | "net/http" 15 | "time" 16 | "unicode/utf8" 17 | ) 18 | 19 | var upgrader = websocket.Upgrader{ 20 | ReadBufferSize: 4096, 21 | WriteBufferSize: 4096, 22 | CheckOrigin: func(r *http.Request) bool { 23 | return true 24 | }, 25 | } 26 | 27 | // echoCopy echoes messages from the client using io.Copy. 28 | func echoCopy(w http.ResponseWriter, r *http.Request, writerOnly bool) { 29 | conn, err := upgrader.Upgrade(w, r, nil) 30 | if err != nil { 31 | log.Println("Upgrade:", err) 32 | return 33 | } 34 | defer conn.Close() 35 | for { 36 | mt, r, err := conn.NextReader() 37 | if err != nil { 38 | if err != io.EOF { 39 | log.Println("NextReader:", err) 40 | } 41 | return 42 | } 43 | if mt == websocket.TextMessage { 44 | r = &validator{r: r} 45 | } 46 | w, err := conn.NextWriter(mt) 47 | if err != nil { 48 | log.Println("NextWriter:", err) 49 | return 50 | } 51 | if mt == websocket.TextMessage { 52 | r = &validator{r: r} 53 | } 54 | if writerOnly { 55 | _, err = io.Copy(struct{ io.Writer }{w}, r) 56 | } else { 57 | _, err = io.Copy(w, r) 58 | } 59 | if err != nil { 60 | if err == errInvalidUTF8 { 61 | conn.WriteControl(websocket.CloseMessage, 62 | websocket.FormatCloseMessage(websocket.CloseInvalidFramePayloadData, ""), 63 | time.Time{}) 64 | } 65 | log.Println("Copy:", err) 66 | return 67 | } 68 | err = w.Close() 69 | if err != nil { 70 | log.Println("Close:", err) 71 | return 72 | } 73 | } 74 | } 75 | 76 | func echoCopyWriterOnly(w http.ResponseWriter, r *http.Request) { 77 | echoCopy(w, r, true) 78 | } 79 | 80 | func echoCopyFull(w http.ResponseWriter, r *http.Request) { 81 | echoCopy(w, r, false) 82 | } 83 | 84 | // echoReadAll echoes messages from the client by reading the entire message 85 | // with ioutil.ReadAll. 86 | func echoReadAll(w http.ResponseWriter, r *http.Request, writeMessage bool) { 87 | conn, err := upgrader.Upgrade(w, r, nil) 88 | if err != nil { 89 | log.Println("Upgrade:", err) 90 | return 91 | } 92 | defer conn.Close() 93 | for { 94 | mt, b, err := conn.ReadMessage() 95 | if err != nil { 96 | if err != io.EOF { 97 | log.Println("NextReader:", err) 98 | } 99 | return 100 | } 101 | if mt == websocket.TextMessage { 102 | if !utf8.Valid(b) { 103 | conn.WriteControl(websocket.CloseMessage, 104 | websocket.FormatCloseMessage(websocket.CloseInvalidFramePayloadData, ""), 105 | time.Time{}) 106 | log.Println("ReadAll: invalid utf8") 107 | } 108 | } 109 | if writeMessage { 110 | err = conn.WriteMessage(mt, b) 111 | if err != nil { 112 | log.Println("WriteMessage:", err) 113 | } 114 | } else { 115 | w, err := conn.NextWriter(mt) 116 | if err != nil { 117 | log.Println("NextWriter:", err) 118 | return 119 | } 120 | if _, err := w.Write(b); err != nil { 121 | log.Println("Writer:", err) 122 | return 123 | } 124 | if err := w.Close(); err != nil { 125 | log.Println("Close:", err) 126 | return 127 | } 128 | } 129 | } 130 | } 131 | 132 | func echoReadAllWriter(w http.ResponseWriter, r *http.Request) { 133 | echoReadAll(w, r, false) 134 | } 135 | 136 | func echoReadAllWriteMessage(w http.ResponseWriter, r *http.Request) { 137 | echoReadAll(w, r, true) 138 | } 139 | 140 | func serveHome(w http.ResponseWriter, r *http.Request) { 141 | if r.URL.Path != "/" { 142 | http.Error(w, "Not found.", 404) 143 | return 144 | } 145 | if r.Method != "GET" { 146 | http.Error(w, "Method not allowed", 405) 147 | return 148 | } 149 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 150 | io.WriteString(w, "Echo Server") 151 | } 152 | 153 | var addr = flag.String("addr", ":9000", "http service address") 154 | 155 | func main() { 156 | flag.Parse() 157 | http.HandleFunc("/", serveHome) 158 | http.HandleFunc("/c", echoCopyWriterOnly) 159 | http.HandleFunc("/f", echoCopyFull) 160 | http.HandleFunc("/r", echoReadAllWriter) 161 | http.HandleFunc("/m", echoReadAllWriteMessage) 162 | err := http.ListenAndServe(*addr, nil) 163 | if err != nil { 164 | log.Fatal("ListenAndServe: ", err) 165 | } 166 | } 167 | 168 | type validator struct { 169 | state int 170 | x rune 171 | r io.Reader 172 | } 173 | 174 | var errInvalidUTF8 = errors.New("invalid utf8") 175 | 176 | func (r *validator) Read(p []byte) (int, error) { 177 | n, err := r.r.Read(p) 178 | state := r.state 179 | x := r.x 180 | for _, b := range p[:n] { 181 | state, x = decode(state, x, b) 182 | if state == utf8Reject { 183 | break 184 | } 185 | } 186 | r.state = state 187 | r.x = x 188 | if state == utf8Reject || (err == io.EOF && state != utf8Accept) { 189 | return n, errInvalidUTF8 190 | } 191 | return n, err 192 | } 193 | 194 | // UTF-8 decoder from http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ 195 | // 196 | // Copyright (c) 2008-2009 Bjoern Hoehrmann 197 | // 198 | // Permission is hereby granted, free of charge, to any person obtaining a copy 199 | // of this software and associated documentation files (the "Software"), to 200 | // deal in the Software without restriction, including without limitation the 201 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 202 | // sell copies of the Software, and to permit persons to whom the Software is 203 | // furnished to do so, subject to the following conditions: 204 | // 205 | // The above copyright notice and this permission notice shall be included in 206 | // all copies or substantial portions of the Software. 207 | // 208 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 209 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 210 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 211 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 212 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 213 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 214 | // IN THE SOFTWARE. 215 | var utf8d = [...]byte{ 216 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1f 217 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3f 218 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5f 219 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7f 220 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9f 221 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // a0..bf 222 | 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0..df 223 | 0xa, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // e0..ef 224 | 0xb, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // f0..ff 225 | 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 226 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 227 | 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 228 | 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 229 | 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // s7..s8 230 | } 231 | 232 | const ( 233 | utf8Accept = 0 234 | utf8Reject = 1 235 | ) 236 | 237 | func decode(state int, x rune, b byte) (int, rune) { 238 | t := utf8d[b] 239 | if state != utf8Accept { 240 | x = rune(b&0x3f) | (x << 6) 241 | } else { 242 | x = rune((0xff >> t) & b) 243 | } 244 | state = int(utf8d[256+state*16+int(t)]) 245 | return state, x 246 | } 247 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/examples/chat/README.md: -------------------------------------------------------------------------------- 1 | # Chat Example 2 | 3 | This application shows how to use use the 4 | [websocket](https://github.com/gorilla/websocket) package and 5 | [jQuery](http://jquery.com) to implement a simple web chat application. 6 | 7 | ## Running the example 8 | 9 | The example requires a working Go development environment. The [Getting 10 | Started](http://golang.org/doc/install) page describes how to install the 11 | development environment. 12 | 13 | Once you have Go up and running, you can download, build and run the example 14 | using the following commands. 15 | 16 | $ go get github.com/gorilla/websocket 17 | $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/chat` 18 | $ go run *.go 19 | 20 | To use the chat example, open http://localhost:8080/ in your browser. 21 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/examples/chat/conn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "github.com/gorilla/websocket" 9 | "log" 10 | "net/http" 11 | "time" 12 | ) 13 | 14 | const ( 15 | // Time allowed to write a message to the peer. 16 | writeWait = 10 * time.Second 17 | 18 | // Time allowed to read the next pong message from the peer. 19 | pongWait = 60 * time.Second 20 | 21 | // Send pings to peer with this period. Must be less than pongWait. 22 | pingPeriod = (pongWait * 9) / 10 23 | 24 | // Maximum message size allowed from peer. 25 | maxMessageSize = 512 26 | ) 27 | 28 | var upgrader = websocket.Upgrader{ 29 | ReadBufferSize: 1024, 30 | WriteBufferSize: 1024, 31 | } 32 | 33 | // connection is an middleman between the websocket connection and the hub. 34 | type connection struct { 35 | // The websocket connection. 36 | ws *websocket.Conn 37 | 38 | // Buffered channel of outbound messages. 39 | send chan []byte 40 | } 41 | 42 | // readPump pumps messages from the websocket connection to the hub. 43 | func (c *connection) readPump() { 44 | defer func() { 45 | h.unregister <- c 46 | c.ws.Close() 47 | }() 48 | c.ws.SetReadLimit(maxMessageSize) 49 | c.ws.SetReadDeadline(time.Now().Add(pongWait)) 50 | c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) 51 | for { 52 | _, message, err := c.ws.ReadMessage() 53 | if err != nil { 54 | break 55 | } 56 | h.broadcast <- message 57 | } 58 | } 59 | 60 | // write writes a message with the given message type and payload. 61 | func (c *connection) write(mt int, payload []byte) error { 62 | c.ws.SetWriteDeadline(time.Now().Add(writeWait)) 63 | return c.ws.WriteMessage(mt, payload) 64 | } 65 | 66 | // writePump pumps messages from the hub to the websocket connection. 67 | func (c *connection) writePump() { 68 | ticker := time.NewTicker(pingPeriod) 69 | defer func() { 70 | ticker.Stop() 71 | c.ws.Close() 72 | }() 73 | for { 74 | select { 75 | case message, ok := <-c.send: 76 | if !ok { 77 | c.write(websocket.CloseMessage, []byte{}) 78 | return 79 | } 80 | if err := c.write(websocket.TextMessage, message); err != nil { 81 | return 82 | } 83 | case <-ticker.C: 84 | if err := c.write(websocket.PingMessage, []byte{}); err != nil { 85 | return 86 | } 87 | } 88 | } 89 | } 90 | 91 | // serverWs handles websocket requests from the peer. 92 | func serveWs(w http.ResponseWriter, r *http.Request) { 93 | if r.Method != "GET" { 94 | http.Error(w, "Method not allowed", 405) 95 | return 96 | } 97 | ws, err := upgrader.Upgrade(w, r, nil) 98 | if err != nil { 99 | log.Println(err) 100 | return 101 | } 102 | c := &connection{send: make(chan []byte, 256), ws: ws} 103 | h.register <- c 104 | go c.writePump() 105 | c.readPump() 106 | } 107 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/examples/chat/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Chat Example 5 | 6 | 47 | 84 | 85 | 86 |
87 |
88 | 89 | 90 |
91 | 92 | 93 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/examples/chat/hub.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | // hub maintains the set of active connections and broadcasts messages to the 8 | // connections. 9 | type hub struct { 10 | // Registered connections. 11 | connections map[*connection]bool 12 | 13 | // Inbound messages from the connections. 14 | broadcast chan []byte 15 | 16 | // Register requests from the connections. 17 | register chan *connection 18 | 19 | // Unregister requests from connections. 20 | unregister chan *connection 21 | } 22 | 23 | var h = hub{ 24 | broadcast: make(chan []byte), 25 | register: make(chan *connection), 26 | unregister: make(chan *connection), 27 | connections: make(map[*connection]bool), 28 | } 29 | 30 | func (h *hub) run() { 31 | for { 32 | select { 33 | case c := <-h.register: 34 | h.connections[c] = true 35 | case c := <-h.unregister: 36 | if _, ok := h.connections[c]; ok { 37 | delete(h.connections, c) 38 | close(c.send) 39 | } 40 | case m := <-h.broadcast: 41 | for c := range h.connections { 42 | select { 43 | case c.send <- m: 44 | default: 45 | close(c.send) 46 | delete(h.connections, c) 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/examples/chat/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "log" 10 | "net/http" 11 | "text/template" 12 | ) 13 | 14 | var addr = flag.String("addr", ":8080", "http service address") 15 | var homeTempl = template.Must(template.ParseFiles("home.html")) 16 | 17 | func serveHome(w http.ResponseWriter, r *http.Request) { 18 | if r.URL.Path != "/" { 19 | http.Error(w, "Not found", 404) 20 | return 21 | } 22 | if r.Method != "GET" { 23 | http.Error(w, "Method not allowed", 405) 24 | return 25 | } 26 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 27 | homeTempl.Execute(w, r.Host) 28 | } 29 | 30 | func main() { 31 | flag.Parse() 32 | go h.run() 33 | http.HandleFunc("/", serveHome) 34 | http.HandleFunc("/ws", serveWs) 35 | err := http.ListenAndServe(*addr, nil) 36 | if err != nil { 37 | log.Fatal("ListenAndServe: ", err) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/examples/filewatch/README.md: -------------------------------------------------------------------------------- 1 | # File Watch example. 2 | 3 | This example sends a file to the browser client for display whenever the file is modified. 4 | 5 | $ go get github.com/gorilla/websocket 6 | $ cd `go list -f '{{.Dir}}' github.com/gorilla/websocket/examples/filewatch` 7 | $ go run main.go 8 | # Open http://localhost:8080/ . 9 | # Modify the file to see it update in the browser. 10 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/examples/filewatch/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "io/ioutil" 10 | "log" 11 | "net/http" 12 | "os" 13 | "strconv" 14 | "text/template" 15 | "time" 16 | 17 | "github.com/gorilla/websocket" 18 | ) 19 | 20 | const ( 21 | // Time allowed to write the file to the client. 22 | writeWait = 10 * time.Second 23 | 24 | // Time allowed to read the next pong message from the client. 25 | pongWait = 60 * time.Second 26 | 27 | // Send pings to client with this period. Must be less than pongWait. 28 | pingPeriod = (pongWait * 9) / 10 29 | 30 | // Poll file for changes with this period. 31 | filePeriod = 10 * time.Second 32 | ) 33 | 34 | var ( 35 | addr = flag.String("addr", ":8080", "http service address") 36 | homeTempl = template.Must(template.New("").Parse(homeHTML)) 37 | filename string 38 | upgrader = websocket.Upgrader{ 39 | ReadBufferSize: 1024, 40 | WriteBufferSize: 1024, 41 | } 42 | ) 43 | 44 | func readFileIfModified(lastMod time.Time) ([]byte, time.Time, error) { 45 | fi, err := os.Stat(filename) 46 | if err != nil { 47 | return nil, lastMod, err 48 | } 49 | if !fi.ModTime().After(lastMod) { 50 | return nil, lastMod, nil 51 | } 52 | p, err := ioutil.ReadFile(filename) 53 | if err != nil { 54 | return nil, fi.ModTime(), err 55 | } 56 | return p, fi.ModTime(), nil 57 | } 58 | 59 | func reader(ws *websocket.Conn) { 60 | defer ws.Close() 61 | ws.SetReadLimit(512) 62 | ws.SetReadDeadline(time.Now().Add(pongWait)) 63 | ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) 64 | for { 65 | _, _, err := ws.ReadMessage() 66 | if err != nil { 67 | break 68 | } 69 | } 70 | } 71 | 72 | func writer(ws *websocket.Conn, lastMod time.Time) { 73 | lastError := "" 74 | pingTicker := time.NewTicker(pingPeriod) 75 | fileTicker := time.NewTicker(filePeriod) 76 | defer func() { 77 | pingTicker.Stop() 78 | fileTicker.Stop() 79 | ws.Close() 80 | }() 81 | for { 82 | select { 83 | case <-fileTicker.C: 84 | var p []byte 85 | var err error 86 | 87 | p, lastMod, err = readFileIfModified(lastMod) 88 | 89 | if err != nil { 90 | if s := err.Error(); s != lastError { 91 | lastError = s 92 | p = []byte(lastError) 93 | } 94 | } else { 95 | lastError = "" 96 | } 97 | 98 | if p != nil { 99 | ws.SetWriteDeadline(time.Now().Add(writeWait)) 100 | if err := ws.WriteMessage(websocket.TextMessage, p); err != nil { 101 | return 102 | } 103 | } 104 | case <-pingTicker.C: 105 | ws.SetWriteDeadline(time.Now().Add(writeWait)) 106 | if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { 107 | return 108 | } 109 | } 110 | } 111 | } 112 | 113 | func serveWs(w http.ResponseWriter, r *http.Request) { 114 | ws, err := upgrader.Upgrade(w, r, nil) 115 | if err != nil { 116 | if _, ok := err.(websocket.HandshakeError); !ok { 117 | log.Println(err) 118 | } 119 | return 120 | } 121 | 122 | var lastMod time.Time 123 | if n, err := strconv.ParseInt(r.FormValue("lastMod"), 16, 64); err != nil { 124 | lastMod = time.Unix(0, n) 125 | } 126 | 127 | go writer(ws, lastMod) 128 | reader(ws) 129 | } 130 | 131 | func serveHome(w http.ResponseWriter, r *http.Request) { 132 | if r.URL.Path != "/" { 133 | http.Error(w, "Not found", 404) 134 | return 135 | } 136 | if r.Method != "GET" { 137 | http.Error(w, "Method not allowed", 405) 138 | return 139 | } 140 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 141 | p, lastMod, err := readFileIfModified(time.Time{}) 142 | if err != nil { 143 | p = []byte(err.Error()) 144 | lastMod = time.Unix(0, 0) 145 | } 146 | var v = struct { 147 | Host string 148 | Data string 149 | LastMod string 150 | }{ 151 | r.Host, 152 | string(p), 153 | strconv.FormatInt(lastMod.UnixNano(), 16), 154 | } 155 | homeTempl.Execute(w, &v) 156 | } 157 | 158 | func main() { 159 | flag.Parse() 160 | if flag.NArg() != 1 { 161 | log.Fatal("filename not specified") 162 | } 163 | filename = flag.Args()[0] 164 | http.HandleFunc("/", serveHome) 165 | http.HandleFunc("/ws", serveWs) 166 | if err := http.ListenAndServe(*addr, nil); err != nil { 167 | log.Fatal(err) 168 | } 169 | } 170 | 171 | const homeHTML = ` 172 | 173 | 174 | WebSocket Example 175 | 176 | 177 |
{{.Data}}
178 | 191 | 192 | 193 | ` 194 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "encoding/json" 9 | "io" 10 | ) 11 | 12 | // WriteJSON is deprecated, use c.WriteJSON instead. 13 | func WriteJSON(c *Conn, v interface{}) error { 14 | return c.WriteJSON(v) 15 | } 16 | 17 | // WriteJSON writes the JSON encoding of v to the connection. 18 | // 19 | // See the documentation for encoding/json Marshal for details about the 20 | // conversion of Go values to JSON. 21 | func (c *Conn) WriteJSON(v interface{}) error { 22 | w, err := c.NextWriter(TextMessage) 23 | if err != nil { 24 | return err 25 | } 26 | err1 := json.NewEncoder(w).Encode(v) 27 | err2 := w.Close() 28 | if err1 != nil { 29 | return err1 30 | } 31 | return err2 32 | } 33 | 34 | // ReadJSON is deprecated, use c.ReadJSON instead. 35 | func ReadJSON(c *Conn, v interface{}) error { 36 | return c.ReadJSON(v) 37 | } 38 | 39 | // ReadJSON reads the next JSON-encoded message from the connection and stores 40 | // it in the value pointed to by v. 41 | // 42 | // See the documentation for the encoding/json Unmarshal function for details 43 | // about the conversion of JSON to a Go value. 44 | func (c *Conn) ReadJSON(v interface{}) error { 45 | _, r, err := c.NextReader() 46 | if err != nil { 47 | return err 48 | } 49 | err = json.NewDecoder(r).Decode(v) 50 | if err == io.EOF { 51 | // Decode returns io.EOF when the message is empty or all whitespace. 52 | // Convert to io.ErrUnexpectedEOF so that application can distinguish 53 | // between an error reading the JSON value and the connection closing. 54 | err = io.ErrUnexpectedEOF 55 | } 56 | return err 57 | } 58 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/json_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "bytes" 9 | "encoding/json" 10 | "io" 11 | "reflect" 12 | "testing" 13 | ) 14 | 15 | func TestJSON(t *testing.T) { 16 | var buf bytes.Buffer 17 | c := fakeNetConn{&buf, &buf} 18 | wc := newConn(c, true, 1024, 1024) 19 | rc := newConn(c, false, 1024, 1024) 20 | 21 | var actual, expect struct { 22 | A int 23 | B string 24 | } 25 | expect.A = 1 26 | expect.B = "hello" 27 | 28 | if err := wc.WriteJSON(&expect); err != nil { 29 | t.Fatal("write", err) 30 | } 31 | 32 | if err := rc.ReadJSON(&actual); err != nil { 33 | t.Fatal("read", err) 34 | } 35 | 36 | if !reflect.DeepEqual(&actual, &expect) { 37 | t.Fatal("equal", actual, expect) 38 | } 39 | } 40 | 41 | func TestPartialJsonRead(t *testing.T) { 42 | var buf bytes.Buffer 43 | c := fakeNetConn{&buf, &buf} 44 | wc := newConn(c, true, 1024, 1024) 45 | rc := newConn(c, false, 1024, 1024) 46 | 47 | var v struct { 48 | A int 49 | B string 50 | } 51 | v.A = 1 52 | v.B = "hello" 53 | 54 | messageCount := 0 55 | 56 | // Partial JSON values. 57 | 58 | data, err := json.Marshal(v) 59 | if err != nil { 60 | t.Fatal(err) 61 | } 62 | for i := len(data) - 1; i >= 0; i-- { 63 | if err := wc.WriteMessage(TextMessage, data[:i]); err != nil { 64 | t.Fatal(err) 65 | } 66 | messageCount++ 67 | } 68 | 69 | // Whitespace. 70 | 71 | if err := wc.WriteMessage(TextMessage, []byte(" ")); err != nil { 72 | t.Fatal(err) 73 | } 74 | messageCount++ 75 | 76 | // Close. 77 | 78 | if err := wc.WriteMessage(CloseMessage, FormatCloseMessage(CloseNormalClosure, "")); err != nil { 79 | t.Fatal(err) 80 | } 81 | 82 | for i := 0; i < messageCount; i++ { 83 | err := rc.ReadJSON(&v) 84 | if err != io.ErrUnexpectedEOF { 85 | t.Error("read", i, err) 86 | } 87 | } 88 | 89 | err = rc.ReadJSON(&v) 90 | if err != io.EOF { 91 | t.Error("final", err) 92 | } 93 | } 94 | 95 | func TestDeprecatedJSON(t *testing.T) { 96 | var buf bytes.Buffer 97 | c := fakeNetConn{&buf, &buf} 98 | wc := newConn(c, true, 1024, 1024) 99 | rc := newConn(c, false, 1024, 1024) 100 | 101 | var actual, expect struct { 102 | A int 103 | B string 104 | } 105 | expect.A = 1 106 | expect.B = "hello" 107 | 108 | if err := WriteJSON(wc, &expect); err != nil { 109 | t.Fatal("write", err) 110 | } 111 | 112 | if err := ReadJSON(rc, &actual); err != nil { 113 | t.Fatal("read", err) 114 | } 115 | 116 | if !reflect.DeepEqual(&actual, &expect) { 117 | t.Fatal("equal", actual, expect) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "bufio" 9 | "errors" 10 | "net" 11 | "net/http" 12 | "net/url" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | // HandshakeError describes an error with the handshake from the peer. 18 | type HandshakeError struct { 19 | message string 20 | } 21 | 22 | func (e HandshakeError) Error() string { return e.message } 23 | 24 | // Upgrader specifies parameters for upgrading an HTTP connection to a 25 | // WebSocket connection. 26 | type Upgrader struct { 27 | // HandshakeTimeout specifies the duration for the handshake to complete. 28 | HandshakeTimeout time.Duration 29 | 30 | // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer 31 | // size is zero, then a default value of 4096 is used. The I/O buffer sizes 32 | // do not limit the size of the messages that can be sent or received. 33 | ReadBufferSize, WriteBufferSize int 34 | 35 | // Subprotocols specifies the server's supported protocols in order of 36 | // preference. If this field is set, then the Upgrade method negotiates a 37 | // subprotocol by selecting the first match in this list with a protocol 38 | // requested by the client. 39 | Subprotocols []string 40 | 41 | // Error specifies the function for generating HTTP error responses. If Error 42 | // is nil, then http.Error is used to generate the HTTP response. 43 | Error func(w http.ResponseWriter, r *http.Request, status int, reason error) 44 | 45 | // CheckOrigin returns true if the request Origin header is acceptable. If 46 | // CheckOrigin is nil, the host in the Origin header must not be set or 47 | // must match the host of the request. 48 | CheckOrigin func(r *http.Request) bool 49 | } 50 | 51 | func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) { 52 | err := HandshakeError{reason} 53 | if u.Error != nil { 54 | u.Error(w, r, status, err) 55 | } else { 56 | http.Error(w, http.StatusText(status), status) 57 | } 58 | return nil, err 59 | } 60 | 61 | // checkSameOrigin returns true if the origin is not set or is equal to the request host. 62 | func checkSameOrigin(r *http.Request) bool { 63 | origin := r.Header["Origin"] 64 | if len(origin) == 0 { 65 | return true 66 | } 67 | u, err := url.Parse(origin[0]) 68 | if err != nil { 69 | return false 70 | } 71 | return u.Host == r.Host 72 | } 73 | 74 | func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string { 75 | if u.Subprotocols != nil { 76 | clientProtocols := Subprotocols(r) 77 | for _, serverProtocol := range u.Subprotocols { 78 | for _, clientProtocol := range clientProtocols { 79 | if clientProtocol == serverProtocol { 80 | return clientProtocol 81 | } 82 | } 83 | } 84 | } else if responseHeader != nil { 85 | return responseHeader.Get("Sec-Websocket-Protocol") 86 | } 87 | return "" 88 | } 89 | 90 | // Upgrade upgrades the HTTP server connection to the WebSocket protocol. 91 | // 92 | // The responseHeader is included in the response to the client's upgrade 93 | // request. Use the responseHeader to specify cookies (Set-Cookie) and the 94 | // application negotiated subprotocol (Sec-Websocket-Protocol). 95 | func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) { 96 | if values := r.Header["Sec-Websocket-Version"]; len(values) == 0 || values[0] != "13" { 97 | return u.returnError(w, r, http.StatusBadRequest, "websocket: version != 13") 98 | } 99 | 100 | if !tokenListContainsValue(r.Header, "Connection", "upgrade") { 101 | return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find connection header with token 'upgrade'") 102 | } 103 | 104 | if !tokenListContainsValue(r.Header, "Upgrade", "websocket") { 105 | return u.returnError(w, r, http.StatusBadRequest, "websocket: could not find upgrade header with token 'websocket'") 106 | } 107 | 108 | checkOrigin := u.CheckOrigin 109 | if checkOrigin == nil { 110 | checkOrigin = checkSameOrigin 111 | } 112 | if !checkOrigin(r) { 113 | return u.returnError(w, r, http.StatusForbidden, "websocket: origin not allowed") 114 | } 115 | 116 | challengeKey := r.Header.Get("Sec-Websocket-Key") 117 | if challengeKey == "" { 118 | return u.returnError(w, r, http.StatusBadRequest, "websocket: key missing or blank") 119 | } 120 | 121 | subprotocol := u.selectSubprotocol(r, responseHeader) 122 | 123 | var ( 124 | netConn net.Conn 125 | br *bufio.Reader 126 | err error 127 | ) 128 | 129 | h, ok := w.(http.Hijacker) 130 | if !ok { 131 | return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker") 132 | } 133 | var rw *bufio.ReadWriter 134 | netConn, rw, err = h.Hijack() 135 | if err != nil { 136 | return u.returnError(w, r, http.StatusInternalServerError, err.Error()) 137 | } 138 | br = rw.Reader 139 | 140 | if br.Buffered() > 0 { 141 | netConn.Close() 142 | return nil, errors.New("websocket: client sent data before handshake is complete") 143 | } 144 | 145 | c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize) 146 | c.subprotocol = subprotocol 147 | 148 | p := c.writeBuf[:0] 149 | p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...) 150 | p = append(p, computeAcceptKey(challengeKey)...) 151 | p = append(p, "\r\n"...) 152 | if c.subprotocol != "" { 153 | p = append(p, "Sec-Websocket-Protocol: "...) 154 | p = append(p, c.subprotocol...) 155 | p = append(p, "\r\n"...) 156 | } 157 | for k, vs := range responseHeader { 158 | if k == "Sec-Websocket-Protocol" { 159 | continue 160 | } 161 | for _, v := range vs { 162 | p = append(p, k...) 163 | p = append(p, ": "...) 164 | for i := 0; i < len(v); i++ { 165 | b := v[i] 166 | if b <= 31 { 167 | // prevent response splitting. 168 | b = ' ' 169 | } 170 | p = append(p, b) 171 | } 172 | p = append(p, "\r\n"...) 173 | } 174 | } 175 | p = append(p, "\r\n"...) 176 | 177 | // Clear deadlines set by HTTP server. 178 | netConn.SetDeadline(time.Time{}) 179 | 180 | if u.HandshakeTimeout > 0 { 181 | netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout)) 182 | } 183 | if _, err = netConn.Write(p); err != nil { 184 | netConn.Close() 185 | return nil, err 186 | } 187 | if u.HandshakeTimeout > 0 { 188 | netConn.SetWriteDeadline(time.Time{}) 189 | } 190 | 191 | return c, nil 192 | } 193 | 194 | // Upgrade upgrades the HTTP server connection to the WebSocket protocol. 195 | // 196 | // This function is deprecated, use websocket.Upgrader instead. 197 | // 198 | // The application is responsible for checking the request origin before 199 | // calling Upgrade. An example implementation of the same origin policy is: 200 | // 201 | // if req.Header.Get("Origin") != "http://"+req.Host { 202 | // http.Error(w, "Origin not allowed", 403) 203 | // return 204 | // } 205 | // 206 | // If the endpoint supports subprotocols, then the application is responsible 207 | // for negotiating the protocol used on the connection. Use the Subprotocols() 208 | // function to get the subprotocols requested by the client. Use the 209 | // Sec-Websocket-Protocol response header to specify the subprotocol selected 210 | // by the application. 211 | // 212 | // The responseHeader is included in the response to the client's upgrade 213 | // request. Use the responseHeader to specify cookies (Set-Cookie) and the 214 | // negotiated subprotocol (Sec-Websocket-Protocol). 215 | // 216 | // The connection buffers IO to the underlying network connection. The 217 | // readBufSize and writeBufSize parameters specify the size of the buffers to 218 | // use. Messages can be larger than the buffers. 219 | // 220 | // If the request is not a valid WebSocket handshake, then Upgrade returns an 221 | // error of type HandshakeError. Applications should handle this error by 222 | // replying to the client with an HTTP error response. 223 | func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) { 224 | u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize} 225 | u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) { 226 | // don't return errors to maintain backwards compatibility 227 | } 228 | u.CheckOrigin = func(r *http.Request) bool { 229 | // allow all connections by default 230 | return true 231 | } 232 | return u.Upgrade(w, r, responseHeader) 233 | } 234 | 235 | // Subprotocols returns the subprotocols requested by the client in the 236 | // Sec-Websocket-Protocol header. 237 | func Subprotocols(r *http.Request) []string { 238 | h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol")) 239 | if h == "" { 240 | return nil 241 | } 242 | protocols := strings.Split(h, ",") 243 | for i := range protocols { 244 | protocols[i] = strings.TrimSpace(protocols[i]) 245 | } 246 | return protocols 247 | } 248 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/server_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "net/http" 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | var subprotocolTests = []struct { 14 | h string 15 | protocols []string 16 | }{ 17 | {"", nil}, 18 | {"foo", []string{"foo"}}, 19 | {"foo,bar", []string{"foo", "bar"}}, 20 | {"foo, bar", []string{"foo", "bar"}}, 21 | {" foo, bar", []string{"foo", "bar"}}, 22 | {" foo, bar ", []string{"foo", "bar"}}, 23 | } 24 | 25 | func TestSubprotocols(t *testing.T) { 26 | for _, st := range subprotocolTests { 27 | r := http.Request{Header: http.Header{"Sec-Websocket-Protocol": {st.h}}} 28 | protocols := Subprotocols(&r) 29 | if !reflect.DeepEqual(st.protocols, protocols) { 30 | t.Errorf("SubProtocols(%q) returned %#v, want %#v", st.h, protocols, st.protocols) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "crypto/rand" 9 | "crypto/sha1" 10 | "encoding/base64" 11 | "io" 12 | "net/http" 13 | "strings" 14 | ) 15 | 16 | // tokenListContainsValue returns true if the 1#token header with the given 17 | // name contains token. 18 | func tokenListContainsValue(header http.Header, name string, value string) bool { 19 | for _, v := range header[name] { 20 | for _, s := range strings.Split(v, ",") { 21 | if strings.EqualFold(value, strings.TrimSpace(s)) { 22 | return true 23 | } 24 | } 25 | } 26 | return false 27 | } 28 | 29 | var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") 30 | 31 | func computeAcceptKey(challengeKey string) string { 32 | h := sha1.New() 33 | h.Write([]byte(challengeKey)) 34 | h.Write(keyGUID) 35 | return base64.StdEncoding.EncodeToString(h.Sum(nil)) 36 | } 37 | 38 | func generateChallengeKey() (string, error) { 39 | p := make([]byte, 16) 40 | if _, err := io.ReadFull(rand.Reader, p); err != nil { 41 | return "", err 42 | } 43 | return base64.StdEncoding.EncodeToString(p), nil 44 | } 45 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/gorilla/websocket/util_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Gorilla WebSocket Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package websocket 6 | 7 | import ( 8 | "net/http" 9 | "testing" 10 | ) 11 | 12 | var tokenListContainsValueTests = []struct { 13 | value string 14 | ok bool 15 | }{ 16 | {"WebSocket", true}, 17 | {"WEBSOCKET", true}, 18 | {"websocket", true}, 19 | {"websockets", false}, 20 | {"x websocket", false}, 21 | {"websocket x", false}, 22 | {"other,websocket,more", true}, 23 | {"other, websocket, more", true}, 24 | } 25 | 26 | func TestTokenListContainsValue(t *testing.T) { 27 | for _, tt := range tokenListContainsValueTests { 28 | h := http.Header{"Upgrade": {tt.value}} 29 | ok := tokenListContainsValue(h, "Upgrade", "websocket") 30 | if ok != tt.ok { 31 | t.Errorf("tokenListContainsValue(h, n, %q) = %v, want %v", tt.value, ok, tt.ok) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jaschaephraim/lrserver/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2015 Jascha Ephraim 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jaschaephraim/lrserver/README.md: -------------------------------------------------------------------------------- 1 | # `lrserver` LiveReload server for Go # 2 | 3 | Golang package that implements a simple LiveReload server as described in the [LiveReload protocol](http://feedback.livereload.com/knowledgebase/articles/86174-livereload-protocol). 4 | 5 | Using the recommended default port 35729: 6 | 7 | - `http://localhost:35729/livereload.js` serves the LiveReload client JavaScript (https://github.com/livereload/livereload-js) 8 | 9 | - `ws://localhost:35729/livereload` communicates with the client via web socket. 10 | 11 | File watching must be implemented by your own application, and reload/alert 12 | requests sent programmatically. 13 | 14 | Multiple servers can be instantiated, and each can support multiple connections. 15 | 16 | ## Full Documentation: [![GoDoc](https://godoc.org/github.com/jaschaephraim/lrserver?status.svg)](http://godoc.org/github.com/jaschaephraim/lrserver) ## 17 | 18 | ## Basic Usage ## 19 | 20 | ### Get Package ### 21 | 22 | ```bash 23 | go get github.com/jaschaephraim/lrserver 24 | ``` 25 | 26 | ### Import Package ### 27 | 28 | ```go 29 | import "github.com/jaschaephraim/lrserver" 30 | ``` 31 | 32 | ### Instantiate Server ### 33 | 34 | ```go 35 | lr, err := lrserver.New(lrserver.DefaultName, lrserver.DefaultPort) 36 | ``` 37 | 38 | ### Start Server ### 39 | 40 | ```go 41 | go func() { 42 | err = lr.ListenAndServe() 43 | if err != nil { 44 | // Handle error 45 | } 46 | }() 47 | ``` 48 | 49 | ### Send Messages to the Browser ### 50 | 51 | ```go 52 | lr.Reload("file") 53 | lr.Alert("message") 54 | ``` 55 | 56 | ## Example ## 57 | 58 | ```go 59 | import ( 60 | "log" 61 | "net/http" 62 | 63 | "github.com/jaschaephraim/lrserver" 64 | "golang.org/x/exp/fsnotify" 65 | ) 66 | 67 | // html includes the client JavaScript 68 | const html = ` 69 | 70 | 71 | Example 72 | 73 | 74 | 75 | 76 | ` 77 | 78 | func Example() { 79 | // Create file watcher 80 | watcher, err := fsnotify.NewWatcher() 81 | if err != nil { 82 | log.Fatalln(err) 83 | } 84 | defer watcher.Close() 85 | 86 | // Add dir to watcher 87 | err = watcher.Watch("/path/to/watched/dir") 88 | if err != nil { 89 | log.Fatalln(err) 90 | } 91 | 92 | // Create and start LiveReload server 93 | lr, _ := lrserver.New(lrserver.DefaultName, lrserver.DefaultPort) 94 | go lr.ListenAndServe() 95 | 96 | // Start goroutine that requests reload upon watcher event 97 | go func() { 98 | for { 99 | event := <-watcher.Events 100 | lr.Reload(event.Name) 101 | } 102 | }() 103 | 104 | // Start serving html 105 | http.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) { 106 | rw.Write([]byte(html)) 107 | }) 108 | http.ListenAndServe(":3000", nil) 109 | } 110 | ``` 111 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jaschaephraim/lrserver/connection.go: -------------------------------------------------------------------------------- 1 | package lrserver 2 | 3 | import ( 4 | "encoding/json" 5 | "strconv" 6 | "time" 7 | 8 | "github.com/gorilla/websocket" 9 | ) 10 | 11 | type conn struct { 12 | conn *websocket.Conn 13 | 14 | server *Server 15 | handshake bool 16 | 17 | reloadChan chan string 18 | alertChan chan string 19 | closeChan chan closeSignal 20 | } 21 | 22 | func (c *conn) start() { 23 | go c.receive() 24 | go c.transmit() 25 | 26 | // Say hello 27 | err := c.conn.WriteJSON(makeServerHello(c.server.Name())) 28 | if err != nil { 29 | c.close(websocket.CloseInternalServerErr, err) 30 | } 31 | 32 | // Block until close signal is sent 33 | <-c.closeChan 34 | } 35 | 36 | func (c *conn) receive() { 37 | for { 38 | // Get next message 39 | msgType, reader, err := c.conn.NextReader() 40 | if err != nil { 41 | c.close(0, err) 42 | return 43 | } 44 | 45 | // Close if binary instead of text 46 | if msgType == websocket.BinaryMessage { 47 | c.close(websocket.CloseUnsupportedData, nil) 48 | return 49 | } 50 | 51 | // Close if it's not JSON 52 | hello := new(clientHello) 53 | err = json.NewDecoder(reader).Decode(hello) 54 | if err != nil { 55 | c.close(websocket.ClosePolicyViolation, err) 56 | return 57 | } 58 | 59 | // Close if missing a command field 60 | if hello.Command == "" { 61 | c.close(websocket.ClosePolicyViolation, nil) 62 | } 63 | 64 | // Validate handshake 65 | if !c.handshake { 66 | if !validateHello(hello) { 67 | c.badHandshake() 68 | return 69 | } 70 | c.handshake = true 71 | c.server.logStatus("connected") 72 | } 73 | } 74 | } 75 | 76 | func (c *conn) transmit() { 77 | for { 78 | var resp interface{} 79 | select { 80 | 81 | // Reload 82 | case file := <-c.reloadChan: 83 | if !c.handshake { 84 | c.badHandshake() 85 | return 86 | } 87 | resp = makeServerReload(file, c.server.LiveCSS()) 88 | 89 | // Alert 90 | case msg := <-c.alertChan: 91 | if !c.handshake { 92 | c.badHandshake() 93 | return 94 | } 95 | resp = makeServerAlert(msg) 96 | } 97 | 98 | err := c.conn.WriteJSON(resp) 99 | if err != nil { 100 | c.close(websocket.CloseInternalServerErr, err) 101 | return 102 | } 103 | } 104 | } 105 | 106 | func (c *conn) badHandshake() { 107 | c.close(websocket.ClosePolicyViolation, websocket.ErrBadHandshake) 108 | } 109 | 110 | func (c *conn) close(closeCode int, closeErr error) error { 111 | var err error 112 | var errMsg string 113 | 114 | if closeErr != nil { 115 | errMsg = closeErr.Error() 116 | c.server.logError(closeErr) 117 | 118 | // Attempt to set close code from error message 119 | errMsgLen := len(errMsg) 120 | if errMsgLen >= 21 && errMsg[:17] == "websocket: close " { 121 | closeCode, err = strconv.Atoi(errMsg[17:21]) 122 | if errMsgLen > 21 { 123 | errMsg = errMsg[22:] 124 | } 125 | } 126 | } 127 | 128 | // Default close code 129 | if closeCode == 0 { 130 | closeCode = websocket.CloseNoStatusReceived 131 | } 132 | 133 | // Send close message 134 | closeMessage := websocket.FormatCloseMessage(closeCode, errMsg) 135 | deadline := time.Now().Add(time.Second) 136 | err = c.conn.WriteControl(websocket.CloseMessage, closeMessage, deadline) 137 | 138 | // Kill and remove connection 139 | c.closeChan <- closeSignal{} 140 | c.server.conns.remove(c) 141 | return err 142 | } 143 | 144 | type connSet map[*conn]struct{} 145 | 146 | func (cs connSet) add(c *conn) { 147 | cs[c] = struct{}{} 148 | } 149 | 150 | func (cs connSet) remove(c *conn) { 151 | delete(cs, c) 152 | } 153 | 154 | type closeSignal struct{} 155 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jaschaephraim/lrserver/example_test.go: -------------------------------------------------------------------------------- 1 | package lrserver_test 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "github.com/jaschaephraim/lrserver" 8 | "golang.org/x/exp/fsnotify" 9 | ) 10 | 11 | // html includes the client JavaScript 12 | const html = ` 13 | 14 | 15 | Example 16 | 17 | 18 | 19 | 20 | ` 21 | 22 | func Example() { 23 | // Create file watcher 24 | watcher, err := fsnotify.NewWatcher() 25 | if err != nil { 26 | log.Fatalln(err) 27 | } 28 | defer watcher.Close() 29 | 30 | // Watch dir 31 | err = watcher.Watch("/path/to/watched/dir") 32 | if err != nil { 33 | log.Fatalln(err) 34 | } 35 | 36 | // Start LiveReload server 37 | lr, err := lrserver.New(lrserver.DefaultName, lrserver.DefaultPort) 38 | if err != nil { 39 | log.Fatalln(err) 40 | } 41 | go lr.ListenAndServe() 42 | 43 | // Start goroutine that requests reload upon watcher event 44 | go func() { 45 | for { 46 | event := <-watcher.Event 47 | lr.Reload(event.Name) 48 | } 49 | }() 50 | 51 | // Start serving html 52 | http.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) { 53 | rw.Write([]byte(html)) 54 | }) 55 | http.ListenAndServe(":3000", nil) 56 | } 57 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jaschaephraim/lrserver/handlers.go: -------------------------------------------------------------------------------- 1 | package lrserver 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gorilla/websocket" 7 | ) 8 | 9 | func jsHandler(s *Server) http.HandlerFunc { 10 | return func(rw http.ResponseWriter, req *http.Request) { 11 | rw.Header().Set("Content-Type", "application/javascript") 12 | _, err := rw.Write([]byte(s.js)) 13 | if err != nil { 14 | s.logError(err) 15 | } 16 | } 17 | } 18 | 19 | func webSocketHandler(s *Server) http.HandlerFunc { 20 | // Do not check origin 21 | upgrader := websocket.Upgrader{ 22 | CheckOrigin: func(r *http.Request) bool { return true }, 23 | } 24 | 25 | return func(rw http.ResponseWriter, req *http.Request) { 26 | conn, err := upgrader.Upgrade(rw, req, nil) 27 | if err != nil { 28 | s.logError(err) 29 | return 30 | } 31 | s.newConn(conn) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jaschaephraim/lrserver/lrserver.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package lrserver implements a basic LiveReload server. 3 | 4 | (See http://feedback.livereload.com/knowledgebase/articles/86174-livereload-protocol .) 5 | 6 | Using the recommended default port 35729: 7 | 8 | http://localhost:35729/livereload.js 9 | 10 | serves the LiveReload client JavaScript, and: 11 | 12 | ws://localhost:35729/livereload 13 | 14 | communicates with the client via web socket. 15 | 16 | File watching must be implemented by your own application, and reload/alert 17 | requests sent programmatically. 18 | 19 | Multiple servers can be instantiated, and each can support multiple connections. 20 | */ 21 | package lrserver 22 | 23 | const ( 24 | DefaultName string = "LiveReload" 25 | DefaultPort uint16 = 35729 26 | ) 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jaschaephraim/lrserver/lrserver_test.go: -------------------------------------------------------------------------------- 1 | package lrserver_test 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | "reflect" 9 | "testing" 10 | "time" 11 | 12 | "github.com/gorilla/websocket" 13 | "github.com/jaschaephraim/lrserver" 14 | . "github.com/smartystreets/goconvey/convey" 15 | ) 16 | 17 | const localhost string = "://127.0.0.1" 18 | 19 | var clientHello = struct { 20 | Command string `json:"command"` 21 | Protocols []string `json:"protocols"` 22 | }{ 23 | "hello", 24 | []string{ 25 | "http://livereload.com/protocols/official-7", 26 | "http://livereload.com/protocols/official-8", 27 | "http://livereload.com/protocols/2.x-origin-version-negotiation", 28 | }, 29 | } 30 | 31 | var randomMessage = struct { 32 | Command string `json:"command"` 33 | }{ 34 | "random", 35 | } 36 | 37 | type serverHello struct { 38 | Command string `json:"command"` 39 | Protocols []string `json:"protocols"` 40 | ServerName string `json:"serverName"` 41 | } 42 | 43 | type serverReload struct { 44 | Command string `json:"command"` 45 | Path string `json:"path"` 46 | LiveCSS bool `json:"liveCSS"` 47 | } 48 | 49 | type serverAlert struct { 50 | Command string `json:"command"` 51 | Message string `json:"message"` 52 | } 53 | 54 | func Test(t *testing.T) { 55 | Convey("Given a new server", t, func() { 56 | srv, err := lrserver.New(lrserver.DefaultName, 0) 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | 61 | Convey("StatusLog() and ErrorLog() should return loggers", func() { 62 | logger := log.New(nil, "", 0) 63 | So(srv.StatusLog(), ShouldHaveSameTypeAs, logger) 64 | So(srv.ErrorLog(), ShouldHaveSameTypeAs, logger) 65 | }) 66 | 67 | srv.SetStatusLog(nil) 68 | srv.SetErrorLog(nil) 69 | 70 | // Start server 71 | Convey("that is running", func() { 72 | go srv.ListenAndServe() 73 | 74 | time.Sleep(time.Nanosecond) 75 | 76 | Convey("a dynamically assigned port should be updated", func() { 77 | So(srv.Port(), ShouldNotEqual, 0) 78 | }) 79 | 80 | // Test JS 81 | Convey("JS should be served successfully", func() { 82 | client := new(http.Client) 83 | resp, err := client.Get( 84 | fmt.Sprintf("http%s:%d/livereload.js", localhost, srv.Port()), 85 | ) 86 | if err != nil { 87 | t.Fatal(err) 88 | } 89 | defer resp.Body.Close() 90 | 91 | body, err := ioutil.ReadAll(resp.Body) 92 | if err != nil { 93 | t.Fatal(err) 94 | } 95 | 96 | bodyString := string(body) 97 | So(bodyString, ShouldStartWith, "(function e(t,n,r)") 98 | So(bodyString, ShouldEndWith, "},{}]},{},[8]);") 99 | }) 100 | 101 | // Connect WebSocket 102 | Convey("and a connected websocket", func() { 103 | dialer := new(websocket.Dialer) 104 | conn, resp, err := dialer.Dial( 105 | fmt.Sprintf("ws%s:%d/livereload", localhost, srv.Port()), 106 | http.Header{}, 107 | ) 108 | if err != nil { 109 | t.Fatal(err) 110 | } 111 | 112 | So(resp.StatusCode, ShouldEqual, 101) 113 | 114 | // Receive hello 115 | hello := new(serverHello) 116 | err = conn.ReadJSON(hello) 117 | if err != nil { 118 | t.Fatal(err) 119 | } 120 | 121 | So(*hello, ShouldResemble, serverHello{ 122 | "hello", 123 | []string{ 124 | "http://livereload.com/protocols/official-7", 125 | "http://livereload.com/protocols/official-8", 126 | "http://livereload.com/protocols/official-9", 127 | "http://livereload.com/protocols/2.x-origin-version-negotiation", 128 | "http://livereload.com/protocols/2.x-remote-control", 129 | }, 130 | srv.Name(), 131 | }) 132 | 133 | // Test bad handshake 134 | Convey("an invalid handshake should close the connection", func() { 135 | err = conn.WriteJSON(randomMessage) 136 | if err != nil { 137 | t.Fatal(err) 138 | } 139 | 140 | _, _, err := conn.NextReader() 141 | So(reflect.TypeOf(err).String(), ShouldEqual, "*websocket.closeError") 142 | }) 143 | 144 | // Send valid handshake 145 | Convey("and a successful handshake", func() { 146 | err = conn.WriteJSON(clientHello) 147 | if err != nil { 148 | t.Fatal(err) 149 | } 150 | 151 | time.Sleep(time.Millisecond) 152 | 153 | Convey("a valid client message should be tolerated", func() { 154 | err = conn.WriteJSON(randomMessage) 155 | if err != nil { 156 | t.Fatal(err) 157 | } 158 | 159 | errChan := make(chan error) 160 | failed := false 161 | go func() { 162 | _, _, err := conn.NextReader() 163 | errChan <- err 164 | }() 165 | go func() { 166 | <-errChan 167 | failed = true 168 | }() 169 | 170 | time.Sleep(time.Millisecond) 171 | 172 | So(failed, ShouldBeFalse) 173 | }) 174 | 175 | // Test reload 176 | Convey("reload should work", func() { 177 | file := "file" 178 | srv.Reload(file) 179 | 180 | sr := new(serverReload) 181 | err = conn.ReadJSON(sr) 182 | if err != nil { 183 | t.Fatal(err) 184 | } 185 | 186 | So(*sr, ShouldResemble, serverReload{ 187 | "reload", 188 | file, 189 | srv.LiveCSS(), 190 | }) 191 | }) 192 | 193 | // Test alert 194 | Convey("alert should work", func() { 195 | msg := "alert" 196 | srv.Alert(msg) 197 | 198 | sa := new(serverAlert) 199 | err = conn.ReadJSON(sa) 200 | if err != nil { 201 | t.Fatal(err) 202 | } 203 | 204 | So(*sa, ShouldResemble, serverAlert{ 205 | "alert", 206 | msg, 207 | }) 208 | }) 209 | }) 210 | }) 211 | }) 212 | }) 213 | } 214 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jaschaephraim/lrserver/messages.go: -------------------------------------------------------------------------------- 1 | package lrserver 2 | 3 | var protocols = []string{ 4 | "http://livereload.com/protocols/official-7", 5 | "http://livereload.com/protocols/official-8", 6 | "http://livereload.com/protocols/official-9", 7 | "http://livereload.com/protocols/2.x-origin-version-negotiation", 8 | "http://livereload.com/protocols/2.x-remote-control", 9 | } 10 | 11 | type clientHello struct { 12 | Command string `json:"command"` 13 | Protocols []string `json:"protocols"` 14 | } 15 | 16 | func validateHello(hello *clientHello) bool { 17 | if hello.Command != "hello" { 18 | return false 19 | } 20 | for _, c := range hello.Protocols { 21 | for _, s := range protocols { 22 | if c == s { 23 | return true 24 | } 25 | } 26 | } 27 | return false 28 | } 29 | 30 | type serverHello struct { 31 | Command string `json:"command"` 32 | Protocols []string `json:"protocols"` 33 | ServerName string `json:"serverName"` 34 | } 35 | 36 | func makeServerHello(name string) *serverHello { 37 | return &serverHello{ 38 | "hello", 39 | protocols, 40 | name, 41 | } 42 | } 43 | 44 | type serverReload struct { 45 | Command string `json:"command"` 46 | Path string `json:"path"` 47 | LiveCSS bool `json:"liveCSS"` 48 | } 49 | 50 | func makeServerReload(file string, liveCSS bool) *serverReload { 51 | return &serverReload{ 52 | Command: "reload", 53 | Path: file, 54 | LiveCSS: liveCSS, 55 | } 56 | } 57 | 58 | type serverAlert struct { 59 | Command string `json:"command"` 60 | Message string `json:"message"` 61 | } 62 | 63 | func makeServerAlert(msg string) *serverAlert { 64 | return &serverAlert{ 65 | Command: "alert", 66 | Message: msg, 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/github.com/jaschaephraim/lrserver/server.go: -------------------------------------------------------------------------------- 1 | package lrserver 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "net/http" 8 | "os" 9 | "strconv" 10 | 11 | "github.com/gorilla/websocket" 12 | ) 13 | 14 | type Server struct { 15 | name string 16 | port uint16 17 | server *http.Server 18 | conns connSet 19 | js string 20 | statusLog *log.Logger 21 | liveCSS bool 22 | } 23 | 24 | func New(name string, port uint16) (*Server, error) { 25 | // Create router 26 | router := http.NewServeMux() 27 | 28 | logPrefix := "[" + name + "] " 29 | 30 | // Create server 31 | s := &Server{ 32 | name: name, 33 | server: &http.Server{ 34 | Handler: router, 35 | ErrorLog: log.New(os.Stderr, logPrefix, 0), 36 | }, 37 | conns: make(connSet), 38 | statusLog: log.New(os.Stdout, logPrefix, 0), 39 | liveCSS: true, 40 | } 41 | s.setPort(port) 42 | 43 | // Handle JS 44 | router.HandleFunc("/livereload.js", jsHandler(s)) 45 | 46 | // Handle reload requests 47 | router.HandleFunc("/livereload", webSocketHandler(s)) 48 | 49 | return s, nil 50 | } 51 | 52 | func (s *Server) ListenAndServe() error { 53 | // Create listener 54 | l, err := net.Listen("tcp", makeAddr(s.port)) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | // Set assigned port if necessary 60 | if s.port == 0 { 61 | port, err := makePort(l.Addr().String()) 62 | if err != nil { 63 | return err 64 | } 65 | 66 | s.setPort(port) 67 | } 68 | 69 | s.logStatus("listening on " + s.server.Addr) 70 | return s.server.Serve(l) 71 | } 72 | 73 | // Reload sends a reload message to the client 74 | func (s *Server) Reload(file string) { 75 | s.logStatus("requesting reload: " + file) 76 | for conn := range s.conns { 77 | conn.reloadChan <- file 78 | } 79 | } 80 | 81 | // Alert sends an alert message to the client 82 | func (s *Server) Alert(msg string) { 83 | s.logStatus("requesting alert: " + msg) 84 | for conn := range s.conns { 85 | conn.alertChan <- msg 86 | } 87 | } 88 | 89 | // Name gets the server name 90 | func (s *Server) Name() string { 91 | return s.name 92 | } 93 | 94 | // Port gets the port that the server is listening on 95 | func (s *Server) Port() uint16 { 96 | return s.port 97 | } 98 | 99 | // LiveCSS gets the live CSS preference 100 | func (s *Server) LiveCSS() bool { 101 | return s.liveCSS 102 | } 103 | 104 | // StatusLog gets the server's status logger, 105 | // which writes to os.Stdout by default 106 | func (s *Server) StatusLog() *log.Logger { 107 | return s.statusLog 108 | } 109 | 110 | // ErrorLog gets the server's error logger, 111 | // which writes to os.Stderr by default 112 | func (s *Server) ErrorLog() *log.Logger { 113 | return s.server.ErrorLog 114 | } 115 | 116 | // SetLiveCSS sets the live CSS preference 117 | func (s *Server) SetLiveCSS(n bool) { 118 | s.liveCSS = n 119 | } 120 | 121 | // SetStatusLog sets the server's status logger, 122 | // which can be set to nil 123 | func (s *Server) SetStatusLog(l *log.Logger) { 124 | s.statusLog = l 125 | } 126 | 127 | // SetErrorLog sets the server's error logger, 128 | // which can be set to nil 129 | func (s *Server) SetErrorLog(l *log.Logger) { 130 | s.server.ErrorLog = l 131 | } 132 | 133 | func (s *Server) setPort(port uint16) { 134 | s.port = port 135 | s.server.Addr = makeAddr(port) 136 | 137 | if port != 0 { 138 | s.js = fmt.Sprintf(js, s.port) 139 | } 140 | } 141 | 142 | func (s *Server) newConn(wsConn *websocket.Conn) { 143 | c := &conn{ 144 | conn: wsConn, 145 | 146 | server: s, 147 | handshake: false, 148 | 149 | reloadChan: make(chan string), 150 | alertChan: make(chan string), 151 | closeChan: make(chan closeSignal), 152 | } 153 | s.conns.add(c) 154 | go c.start() 155 | } 156 | 157 | func (s *Server) logStatus(msg ...interface{}) { 158 | if s.statusLog != nil { 159 | s.statusLog.Println(msg...) 160 | } 161 | } 162 | 163 | func (s *Server) logError(msg ...interface{}) { 164 | if s.server.ErrorLog != nil { 165 | s.server.ErrorLog.Println(msg...) 166 | } 167 | } 168 | 169 | // makeAddr converts uint16(x) to ":x" 170 | func makeAddr(port uint16) string { 171 | return fmt.Sprintf(":%d", port) 172 | } 173 | 174 | // makePort converts ":x" to uint16(x) 175 | func makePort(addr string) (uint16, error) { 176 | _, portString, err := net.SplitHostPort(addr) 177 | if err != nil { 178 | return 0, err 179 | } 180 | 181 | port64, err := strconv.ParseUint(portString, 10, 16) 182 | if err != nil { 183 | return 0, err 184 | } 185 | return uint16(port64), nil 186 | } 187 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/.gitignore: -------------------------------------------------------------------------------- 1 | # Setup a Global .gitignore for OS and editor generated files: 2 | # https://help.github.com/articles/ignoring-files 3 | # git config --global core.excludesfile ~/.gitignore_global 4 | 5 | .vagrant 6 | *.sublime-project 7 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: go 3 | 4 | go: 5 | - 1.4.1 6 | 7 | before_script: 8 | - FIXED=$(go fmt ./... | wc -l); if [ $FIXED -gt 0 ]; then echo "gofmt - $FIXED file(s) not formatted correctly, please run gofmt to fix this." && exit 1; fi 9 | 10 | os: 11 | - linux 12 | - osx 13 | 14 | notifications: 15 | email: false 16 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/AUTHORS: -------------------------------------------------------------------------------- 1 | # Names should be added to this file as 2 | # Name or Organization 3 | # The email address is not required for organizations. 4 | 5 | # You can update this list using the following command: 6 | # 7 | # $ git shortlog -se | awk '{print $2 " " $3 " " $4}' 8 | 9 | # Please keep the list sorted. 10 | 11 | Adrien Bustany 12 | Caleb Spare 13 | Case Nelson 14 | Chris Howey 15 | Christoffer Buchholz 16 | Dave Cheney 17 | Francisco Souza 18 | Hari haran 19 | John C Barstow 20 | Kelvin Fo 21 | Matt Layher 22 | Nathan Youngman 23 | Paul Hammond 24 | Pieter Droogendijk 25 | Pursuit92 26 | Rob Figueiredo 27 | Soge Zhang 28 | Tilak Sharma 29 | Travis Cline 30 | Tudor Golubenco 31 | Yukang 32 | bronze1man 33 | debrando 34 | henrikedwards 35 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v1.2.0 / 2015-02-08 4 | 5 | * inotify: use epoll to wake up readEvents [#66](https://github.com/go-fsnotify/fsnotify/pull/66) (thanks @PieterD) 6 | * inotify: closing watcher should now always shut down goroutine [#63](https://github.com/go-fsnotify/fsnotify/pull/63) (thanks @PieterD) 7 | * kqueue: close kqueue after removing watches, fixes [#59](https://github.com/go-fsnotify/fsnotify/issues/59) 8 | 9 | ## v1.1.1 / 2015-02-05 10 | 11 | * inotify: Retry read on EINTR [#61](https://github.com/go-fsnotify/fsnotify/issues/61) (thanks @PieterD) 12 | 13 | ## v1.1.0 / 2014-12-12 14 | 15 | * kqueue: rework internals [#43](https://github.com/go-fsnotify/fsnotify/pull/43) 16 | * add low-level functions 17 | * only need to store flags on directories 18 | * less mutexes [#13](https://github.com/go-fsnotify/fsnotify/issues/13) 19 | * done can be an unbuffered channel 20 | * remove calls to os.NewSyscallError 21 | * More efficient string concatenation for Event.String() [#52](https://github.com/go-fsnotify/fsnotify/pull/52) (thanks @mdlayher) 22 | * kqueue: fix regression in rework causing subdirectories to be watched [#48](https://github.com/go-fsnotify/fsnotify/issues/48) 23 | * kqueue: cleanup internal watch before sending remove event [#51](https://github.com/go-fsnotify/fsnotify/issues/51) 24 | 25 | ## v1.0.4 / 2014-09-07 26 | 27 | * kqueue: add dragonfly to the build tags. 28 | * Rename source code files, rearrange code so exported APIs are at the top. 29 | * Add done channel to example code. [#37](https://github.com/go-fsnotify/fsnotify/pull/37) (thanks @chenyukang) 30 | 31 | ## v1.0.3 / 2014-08-19 32 | 33 | * [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/go-fsnotify/fsnotify/issues/36) 34 | 35 | ## v1.0.2 / 2014-08-17 36 | 37 | * [Fix] Missing create events on OS X. [#14](https://github.com/go-fsnotify/fsnotify/issues/14) (thanks @zhsso) 38 | * [Fix] Make ./path and path equivalent. (thanks @zhsso) 39 | 40 | ## v1.0.0 / 2014-08-15 41 | 42 | * [API] Remove AddWatch on Windows, use Add. 43 | * Improve documentation for exported identifiers. [#30](https://github.com/go-fsnotify/fsnotify/issues/30) 44 | * Minor updates based on feedback from golint. 45 | 46 | ## dev / 2014-07-09 47 | 48 | * Moved to [github.com/go-fsnotify/fsnotify](https://github.com/go-fsnotify/fsnotify). 49 | * Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno) 50 | 51 | ## dev / 2014-07-04 52 | 53 | * kqueue: fix incorrect mutex used in Close() 54 | * Update example to demonstrate usage of Op. 55 | 56 | ## dev / 2014-06-28 57 | 58 | * [API] Don't set the Write Op for attribute notifications [#4](https://github.com/go-fsnotify/fsnotify/issues/4) 59 | * Fix for String() method on Event (thanks Alex Brainman) 60 | * Don't build on Plan 9 or Solaris (thanks @4ad) 61 | 62 | ## dev / 2014-06-21 63 | 64 | * Events channel of type Event rather than *Event. 65 | * [internal] use syscall constants directly for inotify and kqueue. 66 | * [internal] kqueue: rename events to kevents and fileEvent to event. 67 | 68 | ## dev / 2014-06-19 69 | 70 | * Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally). 71 | * [internal] remove cookie from Event struct (unused). 72 | * [internal] Event struct has the same definition across every OS. 73 | * [internal] remove internal watch and removeWatch methods. 74 | 75 | ## dev / 2014-06-12 76 | 77 | * [API] Renamed Watch() to Add() and RemoveWatch() to Remove(). 78 | * [API] Pluralized channel names: Events and Errors. 79 | * [API] Renamed FileEvent struct to Event. 80 | * [API] Op constants replace methods like IsCreate(). 81 | 82 | ## dev / 2014-06-12 83 | 84 | * Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98) 85 | 86 | ## dev / 2014-05-23 87 | 88 | * [API] Remove current implementation of WatchFlags. 89 | * current implementation doesn't take advantage of OS for efficiency 90 | * provides little benefit over filtering events as they are received, but has extra bookkeeping and mutexes 91 | * no tests for the current implementation 92 | * not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195) 93 | 94 | ## v0.9.3 / 2014-12-31 95 | 96 | * kqueue: cleanup internal watch before sending remove event [#51](https://github.com/go-fsnotify/fsnotify/issues/51) 97 | 98 | ## v0.9.2 / 2014-08-17 99 | 100 | * [Backport] Fix missing create events on OS X. [#14](https://github.com/go-fsnotify/fsnotify/issues/14) (thanks @zhsso) 101 | 102 | ## v0.9.1 / 2014-06-12 103 | 104 | * Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98) 105 | 106 | ## v0.9.0 / 2014-01-17 107 | 108 | * IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany) 109 | * [Fix] kqueue: fix deadlock [#77][] (thanks @cespare) 110 | * [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library. 111 | 112 | ## v0.8.12 / 2013-11-13 113 | 114 | * [API] Remove FD_SET and friends from Linux adapter 115 | 116 | ## v0.8.11 / 2013-11-02 117 | 118 | * [Doc] Add Changelog [#72][] (thanks @nathany) 119 | * [Doc] Spotlight and double modify events on OS X [#62][] (reported by @paulhammond) 120 | 121 | ## v0.8.10 / 2013-10-19 122 | 123 | * [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott) 124 | * [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer) 125 | * [Doc] specify OS-specific limits in README (thanks @debrando) 126 | 127 | ## v0.8.9 / 2013-09-08 128 | 129 | * [Doc] Contributing (thanks @nathany) 130 | * [Doc] update package path in example code [#63][] (thanks @paulhammond) 131 | * [Doc] GoCI badge in README (Linux only) [#60][] 132 | * [Doc] Cross-platform testing with Vagrant [#59][] (thanks @nathany) 133 | 134 | ## v0.8.8 / 2013-06-17 135 | 136 | * [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie) 137 | 138 | ## v0.8.7 / 2013-06-03 139 | 140 | * [API] Make syscall flags internal 141 | * [Fix] inotify: ignore event changes 142 | * [Fix] race in symlink test [#45][] (reported by @srid) 143 | * [Fix] tests on Windows 144 | * lower case error messages 145 | 146 | ## v0.8.6 / 2013-05-23 147 | 148 | * kqueue: Use EVT_ONLY flag on Darwin 149 | * [Doc] Update README with full example 150 | 151 | ## v0.8.5 / 2013-05-09 152 | 153 | * [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg) 154 | 155 | ## v0.8.4 / 2013-04-07 156 | 157 | * [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz) 158 | 159 | ## v0.8.3 / 2013-03-13 160 | 161 | * [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin) 162 | * [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin) 163 | 164 | ## v0.8.2 / 2013-02-07 165 | 166 | * [Doc] add Authors 167 | * [Fix] fix data races for map access [#29][] (thanks @fsouza) 168 | 169 | ## v0.8.1 / 2013-01-09 170 | 171 | * [Fix] Windows path separators 172 | * [Doc] BSD License 173 | 174 | ## v0.8.0 / 2012-11-09 175 | 176 | * kqueue: directory watching improvements (thanks @vmirage) 177 | * inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto) 178 | * [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr) 179 | 180 | ## v0.7.4 / 2012-10-09 181 | 182 | * [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji) 183 | * [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig) 184 | * [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig) 185 | * [Fix] kqueue: modify after recreation of file 186 | 187 | ## v0.7.3 / 2012-09-27 188 | 189 | * [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage) 190 | * [Fix] kqueue: no longer get duplicate CREATE events 191 | 192 | ## v0.7.2 / 2012-09-01 193 | 194 | * kqueue: events for created directories 195 | 196 | ## v0.7.1 / 2012-07-14 197 | 198 | * [Fix] for renaming files 199 | 200 | ## v0.7.0 / 2012-07-02 201 | 202 | * [Feature] FSNotify flags 203 | * [Fix] inotify: Added file name back to event path 204 | 205 | ## v0.6.0 / 2012-06-06 206 | 207 | * kqueue: watch files after directory created (thanks @tmc) 208 | 209 | ## v0.5.1 / 2012-05-22 210 | 211 | * [Fix] inotify: remove all watches before Close() 212 | 213 | ## v0.5.0 / 2012-05-03 214 | 215 | * [API] kqueue: return errors during watch instead of sending over channel 216 | * kqueue: match symlink behavior on Linux 217 | * inotify: add `DELETE_SELF` (requested by @taralx) 218 | * [Fix] kqueue: handle EINTR (reported by @robfig) 219 | * [Doc] Godoc example [#1][] (thanks @davecheney) 220 | 221 | ## v0.4.0 / 2012-03-30 222 | 223 | * Go 1 released: build with go tool 224 | * [Feature] Windows support using winfsnotify 225 | * Windows does not have attribute change notifications 226 | * Roll attribute notifications into IsModify 227 | 228 | ## v0.3.0 / 2012-02-19 229 | 230 | * kqueue: add files when watch directory 231 | 232 | ## v0.2.0 / 2011-12-30 233 | 234 | * update to latest Go weekly code 235 | 236 | ## v0.1.0 / 2011-10-19 237 | 238 | * kqueue: add watch on file creation to match inotify 239 | * kqueue: create file event 240 | * inotify: ignore `IN_IGNORED` events 241 | * event String() 242 | * linux: common FileEvent functions 243 | * initial commit 244 | 245 | [#79]: https://github.com/howeyc/fsnotify/pull/79 246 | [#77]: https://github.com/howeyc/fsnotify/pull/77 247 | [#72]: https://github.com/howeyc/fsnotify/issues/72 248 | [#71]: https://github.com/howeyc/fsnotify/issues/71 249 | [#70]: https://github.com/howeyc/fsnotify/issues/70 250 | [#63]: https://github.com/howeyc/fsnotify/issues/63 251 | [#62]: https://github.com/howeyc/fsnotify/issues/62 252 | [#60]: https://github.com/howeyc/fsnotify/issues/60 253 | [#59]: https://github.com/howeyc/fsnotify/issues/59 254 | [#49]: https://github.com/howeyc/fsnotify/issues/49 255 | [#45]: https://github.com/howeyc/fsnotify/issues/45 256 | [#40]: https://github.com/howeyc/fsnotify/issues/40 257 | [#36]: https://github.com/howeyc/fsnotify/issues/36 258 | [#33]: https://github.com/howeyc/fsnotify/issues/33 259 | [#29]: https://github.com/howeyc/fsnotify/issues/29 260 | [#25]: https://github.com/howeyc/fsnotify/issues/25 261 | [#24]: https://github.com/howeyc/fsnotify/issues/24 262 | [#21]: https://github.com/howeyc/fsnotify/issues/21 263 | 264 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Issues 4 | 5 | * Request features and report bugs using the [GitHub Issue Tracker](https://github.com/go-fsnotify/fsnotify/issues). 6 | * Please indicate the platform you are using fsnotify on. 7 | * A code example to reproduce the problem is appreciated. 8 | 9 | ## Pull Requests 10 | 11 | ### Contributor License Agreement 12 | 13 | fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/go-fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/go-fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual). 14 | 15 | Please indicate that you have signed the CLA in your pull request. 16 | 17 | ### How fsnotify is Developed 18 | 19 | * Development is done on feature branches. 20 | * Tests are run on BSD, Linux, OS X and Windows. 21 | * Pull requests are reviewed and [applied to master][am] using [hub][]. 22 | * Maintainers may modify or squash commits rather than asking contributors to. 23 | * To issue a new release, the maintainers will: 24 | * Update the CHANGELOG 25 | * Tag a version, which will become available through gopkg.in. 26 | 27 | ### How to Fork 28 | 29 | For smooth sailing, always use the original import path. Installing with `go get` makes this easy. 30 | 31 | 1. Install from GitHub (`go get -u github.com/go-fsnotify/fsnotify`) 32 | 2. Create your feature branch (`git checkout -b my-new-feature`) 33 | 3. Ensure everything works and the tests pass (see below) 34 | 4. Commit your changes (`git commit -am 'Add some feature'`) 35 | 36 | Contribute upstream: 37 | 38 | 1. Fork fsnotify on GitHub 39 | 2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`) 40 | 3. Push to the branch (`git push fork my-new-feature`) 41 | 4. Create a new Pull Request on GitHub 42 | 43 | This workflow is [thoroughly explained by Katrina Owen](https://blog.splice.com/contributing-open-source-git-repositories-go/). 44 | 45 | ### Testing 46 | 47 | fsnotify uses build tags to compile different code on Linux, BSD, OS X, and Windows. 48 | 49 | Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on. 50 | 51 | To aid in cross-platform testing there is a Vagrantfile for Linux and BSD. 52 | 53 | * Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/) 54 | * Setup [Vagrant Gopher](https://github.com/nathany/vagrant-gopher) in your `src` folder. 55 | * Run `vagrant up` from the project folder. You can also setup just one box with `vagrant up linux` or `vagrant up bsd` (note: the BSD box doesn't support Windows hosts at this time, and NFS may prompt for your host OS password) 56 | * Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd go-fsnotify/fsnotify; go test'`. 57 | * When you're done, you will want to halt or destroy the Vagrant boxes. 58 | 59 | Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory. 60 | 61 | Right now there is no equivalent solution for Windows and OS X, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads). 62 | 63 | ### Maintainers 64 | 65 | Help maintaining fsnotify is welcome. To be a maintainer: 66 | 67 | * Submit a pull request and sign the CLA as above. 68 | * You must be able to run the test suite on Mac, Windows, Linux and BSD. 69 | 70 | To keep master clean, the fsnotify project uses the "apply mail" workflow outlined in Nathaniel Talbott's post ["Merge pull request" Considered Harmful][am]. This requires installing [hub][]. 71 | 72 | All code changes should be internal pull requests. 73 | 74 | Releases are tagged using [Semantic Versioning](http://semver.org/). 75 | 76 | [hub]: https://github.com/github/hub 77 | [am]: http://blog.spreedly.com/2014/06/24/merge-pull-request-considered-harmful/#.VGa5yZPF_Zs 78 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The Go Authors. All rights reserved. 2 | Copyright (c) 2012 fsnotify Authors. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following disclaimer 12 | in the documentation and/or other materials provided with the 13 | distribution. 14 | * Neither the name of Google Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/NotUsed.xcworkspace: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpuguy83/docker-compose-reloader/df43afcd5ce41fb49f1dda0f79e2dfca2bb67ae2/Godeps/_workspace/src/gopkg.in/fsnotify.v1/NotUsed.xcworkspace -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/README.md: -------------------------------------------------------------------------------- 1 | # File system notifications for Go 2 | 3 | [![Coverage](http://gocover.io/_badge/github.com/go-fsnotify/fsnotify)](http://gocover.io/github.com/go-fsnotify/fsnotify) [![GoDoc](https://godoc.org/gopkg.in/fsnotify.v1?status.svg)](https://godoc.org/gopkg.in/fsnotify.v1) 4 | 5 | Go 1.3+ required. 6 | 7 | Cross platform: Windows, Linux, BSD and OS X. 8 | 9 | |Adapter |OS |Status | 10 | |----------|----------|----------| 11 | |inotify |Linux, Android\*|Supported [![Build Status](https://travis-ci.org/go-fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/go-fsnotify/fsnotify)| 12 | |kqueue |BSD, OS X, iOS\*|Supported [![Circle CI](https://circleci.com/gh/go-fsnotify/fsnotify.svg?style=svg)](https://circleci.com/gh/go-fsnotify/fsnotify)| 13 | |ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)| 14 | |FSEvents |OS X |[Planned](https://github.com/go-fsnotify/fsnotify/issues/11)| 15 | |FEN |Solaris 11 |[Planned](https://github.com/go-fsnotify/fsnotify/issues/12)| 16 | |fanotify |Linux 2.6.37+ | | 17 | |USN Journals |Windows |[Maybe](https://github.com/go-fsnotify/fsnotify/issues/53)| 18 | |Polling |*All* |[Maybe](https://github.com/go-fsnotify/fsnotify/issues/9)| 19 | 20 | \* Android and iOS are untested. 21 | 22 | Please see [the documentation](https://godoc.org/gopkg.in/fsnotify.v1) for usage. Consult the [Wiki](https://github.com/go-fsnotify/fsnotify/wiki) for the FAQ and further information. 23 | 24 | ## API stability 25 | 26 | Two major versions of fsnotify exist. 27 | 28 | **[fsnotify.v0](https://gopkg.in/fsnotify.v0)** is API-compatible with [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify). Bugfixes *may* be backported, but I recommend upgrading to v1. 29 | 30 | ```go 31 | import "gopkg.in/fsnotify.v0" 32 | ``` 33 | 34 | \* Refer to the package as fsnotify (without the .v0 suffix). 35 | 36 | **[fsnotify.v1](https://gopkg.in/fsnotify.v1)** provides [a new API](https://godoc.org/gopkg.in/fsnotify.v1) based on [this design document](http://goo.gl/MrYxyA). You can import v1 with: 37 | 38 | ```go 39 | import "gopkg.in/fsnotify.v1" 40 | ``` 41 | 42 | Further API changes are [planned](https://github.com/go-fsnotify/fsnotify/milestones), but a new major revision will be tagged, so you can depend on the v1 API. 43 | 44 | **Master** may have unreleased changes. Use it to test the very latest code or when [contributing][], but don't expect it to remain API-compatible: 45 | 46 | ```go 47 | import "github.com/go-fsnotify/fsnotify" 48 | ``` 49 | 50 | ## Contributing 51 | 52 | Please refer to [CONTRIBUTING][] before opening an issue or pull request. 53 | 54 | ## Example 55 | 56 | See [example_test.go](https://github.com/go-fsnotify/fsnotify/blob/master/example_test.go). 57 | 58 | 59 | [contributing]: https://github.com/go-fsnotify/fsnotify/blob/master/CONTRIBUTING.md 60 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/circle.yml: -------------------------------------------------------------------------------- 1 | ## OS X build (CircleCI iOS beta) 2 | 3 | # Pretend like it's an Xcode project, at least to get it running. 4 | machine: 5 | environment: 6 | XCODE_WORKSPACE: NotUsed.xcworkspace 7 | XCODE_SCHEME: NotUsed 8 | # This is where the go project is actually checked out to: 9 | CIRCLE_BUILD_DIR: $HOME/.go_project/src/github.com/go-fsnotify/fsnotify 10 | 11 | dependencies: 12 | pre: 13 | - brew upgrade go 14 | 15 | test: 16 | override: 17 | - go test ./... 18 | 19 | # Idealized future config, eventually with cross-platform build matrix :-) 20 | 21 | # machine: 22 | # go: 23 | # version: 1.4 24 | # os: 25 | # - osx 26 | # - linux 27 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !plan9,!solaris 6 | 7 | package fsnotify_test 8 | 9 | import ( 10 | "log" 11 | 12 | "github.com/go-fsnotify/fsnotify" 13 | ) 14 | 15 | func ExampleNewWatcher() { 16 | watcher, err := fsnotify.NewWatcher() 17 | if err != nil { 18 | log.Fatal(err) 19 | } 20 | defer watcher.Close() 21 | 22 | done := make(chan bool) 23 | go func() { 24 | for { 25 | select { 26 | case event := <-watcher.Events: 27 | log.Println("event:", event) 28 | if event.Op&fsnotify.Write == fsnotify.Write { 29 | log.Println("modified file:", event.Name) 30 | } 31 | case err := <-watcher.Errors: 32 | log.Println("error:", err) 33 | } 34 | } 35 | }() 36 | 37 | err = watcher.Add("/tmp/foo") 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | <-done 42 | } 43 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/fsnotify.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !plan9,!solaris 6 | 7 | // Package fsnotify provides a platform-independent interface for file system notifications. 8 | package fsnotify 9 | 10 | import ( 11 | "bytes" 12 | "fmt" 13 | ) 14 | 15 | // Event represents a single file system notification. 16 | type Event struct { 17 | Name string // Relative path to the file or directory. 18 | Op Op // File operation that triggered the event. 19 | } 20 | 21 | // Op describes a set of file operations. 22 | type Op uint32 23 | 24 | // These are the generalized file operations that can trigger a notification. 25 | const ( 26 | Create Op = 1 << iota 27 | Write 28 | Remove 29 | Rename 30 | Chmod 31 | ) 32 | 33 | // String returns a string representation of the event in the form 34 | // "file: REMOVE|WRITE|..." 35 | func (e Event) String() string { 36 | // Use a buffer for efficient string concatenation 37 | var buffer bytes.Buffer 38 | 39 | if e.Op&Create == Create { 40 | buffer.WriteString("|CREATE") 41 | } 42 | if e.Op&Remove == Remove { 43 | buffer.WriteString("|REMOVE") 44 | } 45 | if e.Op&Write == Write { 46 | buffer.WriteString("|WRITE") 47 | } 48 | if e.Op&Rename == Rename { 49 | buffer.WriteString("|RENAME") 50 | } 51 | if e.Op&Chmod == Chmod { 52 | buffer.WriteString("|CHMOD") 53 | } 54 | 55 | // If buffer remains empty, return no event names 56 | if buffer.Len() == 0 { 57 | return fmt.Sprintf("%q: ", e.Name) 58 | } 59 | 60 | // Return a list of event names, with leading pipe character stripped 61 | return fmt.Sprintf("%q: %s", e.Name, buffer.String()[1:]) 62 | } 63 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/inotify.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux 6 | 7 | package fsnotify 8 | 9 | import ( 10 | "errors" 11 | "fmt" 12 | "io" 13 | "os" 14 | "path/filepath" 15 | "strings" 16 | "sync" 17 | "syscall" 18 | "unsafe" 19 | ) 20 | 21 | // Watcher watches a set of files, delivering events to a channel. 22 | type Watcher struct { 23 | Events chan Event 24 | Errors chan error 25 | mu sync.Mutex // Map access 26 | fd int 27 | poller *fdPoller 28 | watches map[string]*watch // Map of inotify watches (key: path) 29 | paths map[int]string // Map of watched paths (key: watch descriptor) 30 | done chan struct{} // Channel for sending a "quit message" to the reader goroutine 31 | doneResp chan struct{} // Channel to respond to Close 32 | } 33 | 34 | // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. 35 | func NewWatcher() (*Watcher, error) { 36 | // Create inotify fd 37 | fd, errno := syscall.InotifyInit() 38 | if fd == -1 { 39 | return nil, errno 40 | } 41 | // Create epoll 42 | poller, err := newFdPoller(fd) 43 | if err != nil { 44 | syscall.Close(fd) 45 | return nil, err 46 | } 47 | w := &Watcher{ 48 | fd: fd, 49 | poller: poller, 50 | watches: make(map[string]*watch), 51 | paths: make(map[int]string), 52 | Events: make(chan Event), 53 | Errors: make(chan error), 54 | done: make(chan struct{}), 55 | doneResp: make(chan struct{}), 56 | } 57 | 58 | go w.readEvents() 59 | return w, nil 60 | } 61 | 62 | func (w *Watcher) isClosed() bool { 63 | select { 64 | case <-w.done: 65 | return true 66 | default: 67 | return false 68 | } 69 | } 70 | 71 | // Close removes all watches and closes the events channel. 72 | func (w *Watcher) Close() error { 73 | if w.isClosed() { 74 | return nil 75 | } 76 | 77 | // Send 'close' signal to goroutine, and set the Watcher to closed. 78 | close(w.done) 79 | 80 | // Wake up goroutine 81 | w.poller.wake() 82 | 83 | // Wait for goroutine to close 84 | <-w.doneResp 85 | 86 | return nil 87 | } 88 | 89 | // Add starts watching the named file or directory (non-recursively). 90 | func (w *Watcher) Add(name string) error { 91 | name = filepath.Clean(name) 92 | if w.isClosed() { 93 | return errors.New("inotify instance already closed") 94 | } 95 | 96 | const agnosticEvents = syscall.IN_MOVED_TO | syscall.IN_MOVED_FROM | 97 | syscall.IN_CREATE | syscall.IN_ATTRIB | syscall.IN_MODIFY | 98 | syscall.IN_MOVE_SELF | syscall.IN_DELETE | syscall.IN_DELETE_SELF 99 | 100 | var flags uint32 = agnosticEvents 101 | 102 | w.mu.Lock() 103 | watchEntry, found := w.watches[name] 104 | w.mu.Unlock() 105 | if found { 106 | watchEntry.flags |= flags 107 | flags |= syscall.IN_MASK_ADD 108 | } 109 | wd, errno := syscall.InotifyAddWatch(w.fd, name, flags) 110 | if wd == -1 { 111 | return errno 112 | } 113 | 114 | w.mu.Lock() 115 | w.watches[name] = &watch{wd: uint32(wd), flags: flags} 116 | w.paths[wd] = name 117 | w.mu.Unlock() 118 | 119 | return nil 120 | } 121 | 122 | // Remove stops watching the named file or directory (non-recursively). 123 | func (w *Watcher) Remove(name string) error { 124 | name = filepath.Clean(name) 125 | 126 | // Fetch the watch. 127 | w.mu.Lock() 128 | defer w.mu.Unlock() 129 | watch, ok := w.watches[name] 130 | 131 | // Remove it from inotify. 132 | if !ok { 133 | return fmt.Errorf("can't remove non-existent inotify watch for: %s", name) 134 | } 135 | // inotify_rm_watch will return EINVAL if the file has been deleted; 136 | // the inotify will already have been removed. 137 | // That means we can safely delete it from our watches, whatever inotify_rm_watch does. 138 | delete(w.watches, name) 139 | success, errno := syscall.InotifyRmWatch(w.fd, watch.wd) 140 | if success == -1 { 141 | // TODO: Perhaps it's not helpful to return an error here in every case. 142 | // the only two possible errors are: 143 | // EBADF, which happens when w.fd is not a valid file descriptor of any kind. 144 | // EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor. 145 | // Watch descriptors are invalidated when they are removed explicitly or implicitly; 146 | // explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted. 147 | return errno 148 | } 149 | return nil 150 | } 151 | 152 | type watch struct { 153 | wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) 154 | flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) 155 | } 156 | 157 | // readEvents reads from the inotify file descriptor, converts the 158 | // received events into Event objects and sends them via the Events channel 159 | func (w *Watcher) readEvents() { 160 | var ( 161 | buf [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events 162 | n int // Number of bytes read with read() 163 | errno error // Syscall errno 164 | ok bool // For poller.wait 165 | ) 166 | 167 | defer close(w.doneResp) 168 | defer close(w.Errors) 169 | defer close(w.Events) 170 | defer syscall.Close(w.fd) 171 | defer w.poller.close() 172 | 173 | for { 174 | // See if we have been closed. 175 | if w.isClosed() { 176 | return 177 | } 178 | 179 | ok, errno = w.poller.wait() 180 | if errno != nil { 181 | select { 182 | case w.Errors <- errno: 183 | case <-w.done: 184 | return 185 | } 186 | continue 187 | } 188 | 189 | if !ok { 190 | continue 191 | } 192 | 193 | n, errno = syscall.Read(w.fd, buf[:]) 194 | // If a signal interrupted execution, see if we've been asked to close, and try again. 195 | // http://man7.org/linux/man-pages/man7/signal.7.html : 196 | // "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable" 197 | if errno == syscall.EINTR { 198 | continue 199 | } 200 | 201 | // syscall.Read might have been woken up by Close. If so, we're done. 202 | if w.isClosed() { 203 | return 204 | } 205 | 206 | if n < syscall.SizeofInotifyEvent { 207 | var err error 208 | if n == 0 { 209 | // If EOF is received. This should really never happen. 210 | err = io.EOF 211 | } else if n < 0 { 212 | // If an error occured while reading. 213 | err = errno 214 | } else { 215 | // Read was too short. 216 | err = errors.New("notify: short read in readEvents()") 217 | } 218 | select { 219 | case w.Errors <- err: 220 | case <-w.done: 221 | return 222 | } 223 | continue 224 | } 225 | 226 | var offset uint32 227 | // We don't know how many events we just read into the buffer 228 | // While the offset points to at least one whole event... 229 | for offset <= uint32(n-syscall.SizeofInotifyEvent) { 230 | // Point "raw" to the event in the buffer 231 | raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset])) 232 | 233 | mask := uint32(raw.Mask) 234 | nameLen := uint32(raw.Len) 235 | // If the event happened to the watched directory or the watched file, the kernel 236 | // doesn't append the filename to the event, but we would like to always fill the 237 | // the "Name" field with a valid filename. We retrieve the path of the watch from 238 | // the "paths" map. 239 | w.mu.Lock() 240 | name := w.paths[int(raw.Wd)] 241 | w.mu.Unlock() 242 | if nameLen > 0 { 243 | // Point "bytes" at the first byte of the filename 244 | bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent])) 245 | // The filename is padded with NULL bytes. TrimRight() gets rid of those. 246 | name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") 247 | } 248 | 249 | event := newEvent(name, mask) 250 | 251 | // Send the events that are not ignored on the events channel 252 | if !event.ignoreLinux(mask) { 253 | select { 254 | case w.Events <- event: 255 | case <-w.done: 256 | return 257 | } 258 | } 259 | 260 | // Move to the next event in the buffer 261 | offset += syscall.SizeofInotifyEvent + nameLen 262 | } 263 | } 264 | } 265 | 266 | // Certain types of events can be "ignored" and not sent over the Events 267 | // channel. Such as events marked ignore by the kernel, or MODIFY events 268 | // against files that do not exist. 269 | func (e *Event) ignoreLinux(mask uint32) bool { 270 | // Ignore anything the inotify API says to ignore 271 | if mask&syscall.IN_IGNORED == syscall.IN_IGNORED { 272 | return true 273 | } 274 | 275 | // If the event is not a DELETE or RENAME, the file must exist. 276 | // Otherwise the event is ignored. 277 | // *Note*: this was put in place because it was seen that a MODIFY 278 | // event was sent after the DELETE. This ignores that MODIFY and 279 | // assumes a DELETE will come or has come if the file doesn't exist. 280 | if !(e.Op&Remove == Remove || e.Op&Rename == Rename) { 281 | _, statErr := os.Lstat(e.Name) 282 | return os.IsNotExist(statErr) 283 | } 284 | return false 285 | } 286 | 287 | // newEvent returns an platform-independent Event based on an inotify mask. 288 | func newEvent(name string, mask uint32) Event { 289 | e := Event{Name: name} 290 | if mask&syscall.IN_CREATE == syscall.IN_CREATE || mask&syscall.IN_MOVED_TO == syscall.IN_MOVED_TO { 291 | e.Op |= Create 292 | } 293 | if mask&syscall.IN_DELETE_SELF == syscall.IN_DELETE_SELF || mask&syscall.IN_DELETE == syscall.IN_DELETE { 294 | e.Op |= Remove 295 | } 296 | if mask&syscall.IN_MODIFY == syscall.IN_MODIFY { 297 | e.Op |= Write 298 | } 299 | if mask&syscall.IN_MOVE_SELF == syscall.IN_MOVE_SELF || mask&syscall.IN_MOVED_FROM == syscall.IN_MOVED_FROM { 300 | e.Op |= Rename 301 | } 302 | if mask&syscall.IN_ATTRIB == syscall.IN_ATTRIB { 303 | e.Op |= Chmod 304 | } 305 | return e 306 | } 307 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/inotify_poller.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux 6 | 7 | package fsnotify 8 | 9 | import ( 10 | "errors" 11 | "syscall" 12 | ) 13 | 14 | type fdPoller struct { 15 | fd int // File descriptor (as returned by the inotify_init() syscall) 16 | epfd int // Epoll file descriptor 17 | pipe [2]int // Pipe for waking up 18 | } 19 | 20 | func emptyPoller(fd int) *fdPoller { 21 | poller := new(fdPoller) 22 | poller.fd = fd 23 | poller.epfd = -1 24 | poller.pipe[0] = -1 25 | poller.pipe[1] = -1 26 | return poller 27 | } 28 | 29 | // Create a new inotify poller. 30 | // This creates an inotify handler, and an epoll handler. 31 | func newFdPoller(fd int) (*fdPoller, error) { 32 | var errno error 33 | poller := emptyPoller(fd) 34 | defer func() { 35 | if errno != nil { 36 | poller.close() 37 | } 38 | }() 39 | poller.fd = fd 40 | 41 | // Create epoll fd 42 | poller.epfd, errno = syscall.EpollCreate(1) 43 | if poller.epfd == -1 { 44 | return nil, errno 45 | } 46 | // Create pipe; pipe[0] is the read end, pipe[1] the write end. 47 | errno = syscall.Pipe2(poller.pipe[:], syscall.O_NONBLOCK) 48 | if errno != nil { 49 | return nil, errno 50 | } 51 | 52 | // Register inotify fd with epoll 53 | event := syscall.EpollEvent{ 54 | Fd: int32(poller.fd), 55 | Events: syscall.EPOLLIN, 56 | } 57 | errno = syscall.EpollCtl(poller.epfd, syscall.EPOLL_CTL_ADD, poller.fd, &event) 58 | if errno != nil { 59 | return nil, errno 60 | } 61 | 62 | // Register pipe fd with epoll 63 | event = syscall.EpollEvent{ 64 | Fd: int32(poller.pipe[0]), 65 | Events: syscall.EPOLLIN, 66 | } 67 | errno = syscall.EpollCtl(poller.epfd, syscall.EPOLL_CTL_ADD, poller.pipe[0], &event) 68 | if errno != nil { 69 | return nil, errno 70 | } 71 | 72 | return poller, nil 73 | } 74 | 75 | // Wait using epoll. 76 | // Returns true if something is ready to be read, 77 | // false if there is not. 78 | func (poller *fdPoller) wait() (bool, error) { 79 | // 3 possible events per fd, and 2 fds, makes a maximum of 6 events. 80 | // I don't know whether epoll_wait returns the number of events returned, 81 | // or the total number of events ready. 82 | // I decided to catch both by making the buffer one larger than the maximum. 83 | events := make([]syscall.EpollEvent, 7) 84 | for { 85 | n, errno := syscall.EpollWait(poller.epfd, events, -1) 86 | if n == -1 { 87 | if errno == syscall.EINTR { 88 | continue 89 | } 90 | return false, errno 91 | } 92 | if n == 0 { 93 | // If there are no events, try again. 94 | continue 95 | } 96 | if n > 6 { 97 | // This should never happen. More events were returned than should be possible. 98 | return false, errors.New("epoll_wait returned more events than I know what to do with") 99 | } 100 | ready := events[:n] 101 | epollhup := false 102 | epollerr := false 103 | epollin := false 104 | for _, event := range ready { 105 | if event.Fd == int32(poller.fd) { 106 | if event.Events&syscall.EPOLLHUP != 0 { 107 | // This should not happen, but if it does, treat it as a wakeup. 108 | epollhup = true 109 | } 110 | if event.Events&syscall.EPOLLERR != 0 { 111 | // If an error is waiting on the file descriptor, we should pretend 112 | // something is ready to read, and let syscall.Read pick up the error. 113 | epollerr = true 114 | } 115 | if event.Events&syscall.EPOLLIN != 0 { 116 | // There is data to read. 117 | epollin = true 118 | } 119 | } 120 | if event.Fd == int32(poller.pipe[0]) { 121 | if event.Events&syscall.EPOLLHUP != 0 { 122 | // Write pipe descriptor was closed, by us. This means we're closing down the 123 | // watcher, and we should wake up. 124 | } 125 | if event.Events&syscall.EPOLLERR != 0 { 126 | // If an error is waiting on the pipe file descriptor. 127 | // This is an absolute mystery, and should never ever happen. 128 | return false, errors.New("Error on the pipe descriptor.") 129 | } 130 | if event.Events&syscall.EPOLLIN != 0 { 131 | // This is a regular wakeup, so we have to clear the buffer. 132 | err := poller.clearWake() 133 | if err != nil { 134 | return false, err 135 | } 136 | } 137 | } 138 | } 139 | 140 | if epollhup || epollerr || epollin { 141 | return true, nil 142 | } 143 | return false, nil 144 | } 145 | } 146 | 147 | // Close the write end of the poller. 148 | func (poller *fdPoller) wake() error { 149 | buf := make([]byte, 1) 150 | n, errno := syscall.Write(poller.pipe[1], buf) 151 | if n == -1 { 152 | if errno == syscall.EAGAIN { 153 | // Buffer is full, poller will wake. 154 | return nil 155 | } 156 | return errno 157 | } 158 | return nil 159 | } 160 | 161 | func (poller *fdPoller) clearWake() error { 162 | // You have to be woken up a LOT in order to get to 100! 163 | buf := make([]byte, 100) 164 | n, errno := syscall.Read(poller.pipe[0], buf) 165 | if n == -1 { 166 | if errno == syscall.EAGAIN { 167 | // Buffer is empty, someone else cleared our wake. 168 | return nil 169 | } 170 | return errno 171 | } 172 | return nil 173 | } 174 | 175 | // Close all poller file descriptors, but not the one passed to it. 176 | func (poller *fdPoller) close() { 177 | if poller.pipe[1] != -1 { 178 | syscall.Close(poller.pipe[1]) 179 | } 180 | if poller.pipe[0] != -1 { 181 | syscall.Close(poller.pipe[0]) 182 | } 183 | if poller.epfd != -1 { 184 | syscall.Close(poller.epfd) 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/inotify_poller_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux 6 | 7 | package fsnotify 8 | 9 | import ( 10 | "syscall" 11 | "testing" 12 | "time" 13 | ) 14 | 15 | type testFd [2]int 16 | 17 | func makeTestFd(t *testing.T) testFd { 18 | var tfd testFd 19 | errno := syscall.Pipe(tfd[:]) 20 | if errno != nil { 21 | t.Fatalf("Failed to create pipe: %v", errno) 22 | } 23 | return tfd 24 | } 25 | 26 | func (tfd testFd) fd() int { 27 | return tfd[0] 28 | } 29 | 30 | func (tfd testFd) closeWrite(t *testing.T) { 31 | errno := syscall.Close(tfd[1]) 32 | if errno != nil { 33 | t.Fatalf("Failed to close write end of pipe: %v", errno) 34 | } 35 | } 36 | 37 | func (tfd testFd) put(t *testing.T) { 38 | buf := make([]byte, 10) 39 | _, errno := syscall.Write(tfd[1], buf) 40 | if errno != nil { 41 | t.Fatalf("Failed to write to pipe: %v", errno) 42 | } 43 | } 44 | 45 | func (tfd testFd) get(t *testing.T) { 46 | buf := make([]byte, 10) 47 | _, errno := syscall.Read(tfd[0], buf) 48 | if errno != nil { 49 | t.Fatalf("Failed to read from pipe: %v", errno) 50 | } 51 | } 52 | 53 | func (tfd testFd) close() { 54 | syscall.Close(tfd[1]) 55 | syscall.Close(tfd[0]) 56 | } 57 | 58 | func makePoller(t *testing.T) (testFd, *fdPoller) { 59 | tfd := makeTestFd(t) 60 | poller, err := newFdPoller(tfd.fd()) 61 | if err != nil { 62 | t.Fatalf("Failed to create poller: %v", err) 63 | } 64 | return tfd, poller 65 | } 66 | 67 | func TestPollerWithBadFd(t *testing.T) { 68 | _, err := newFdPoller(-1) 69 | if err != syscall.EBADF { 70 | t.Fatalf("Expected EBADF, got: %v", err) 71 | } 72 | } 73 | 74 | func TestPollerWithData(t *testing.T) { 75 | tfd, poller := makePoller(t) 76 | defer tfd.close() 77 | defer poller.close() 78 | 79 | tfd.put(t) 80 | ok, err := poller.wait() 81 | if err != nil { 82 | t.Fatalf("poller failed: %v", err) 83 | } 84 | if !ok { 85 | t.Fatalf("expected poller to return true") 86 | } 87 | tfd.get(t) 88 | } 89 | 90 | func TestPollerWithWakeup(t *testing.T) { 91 | tfd, poller := makePoller(t) 92 | defer tfd.close() 93 | defer poller.close() 94 | 95 | err := poller.wake() 96 | if err != nil { 97 | t.Fatalf("wake failed: %v", err) 98 | } 99 | ok, err := poller.wait() 100 | if err != nil { 101 | t.Fatalf("poller failed: %v", err) 102 | } 103 | if ok { 104 | t.Fatalf("expected poller to return false") 105 | } 106 | } 107 | 108 | func TestPollerWithClose(t *testing.T) { 109 | tfd, poller := makePoller(t) 110 | defer tfd.close() 111 | defer poller.close() 112 | 113 | tfd.closeWrite(t) 114 | ok, err := poller.wait() 115 | if err != nil { 116 | t.Fatalf("poller failed: %v", err) 117 | } 118 | if !ok { 119 | t.Fatalf("expected poller to return true") 120 | } 121 | } 122 | 123 | func TestPollerWithWakeupAndData(t *testing.T) { 124 | tfd, poller := makePoller(t) 125 | defer tfd.close() 126 | defer poller.close() 127 | 128 | tfd.put(t) 129 | err := poller.wake() 130 | if err != nil { 131 | t.Fatalf("wake failed: %v", err) 132 | } 133 | 134 | // both data and wakeup 135 | ok, err := poller.wait() 136 | if err != nil { 137 | t.Fatalf("poller failed: %v", err) 138 | } 139 | if !ok { 140 | t.Fatalf("expected poller to return true") 141 | } 142 | 143 | // data is still in the buffer, wakeup is cleared 144 | ok, err = poller.wait() 145 | if err != nil { 146 | t.Fatalf("poller failed: %v", err) 147 | } 148 | if !ok { 149 | t.Fatalf("expected poller to return true") 150 | } 151 | 152 | tfd.get(t) 153 | // data is gone, only wakeup now 154 | err = poller.wake() 155 | if err != nil { 156 | t.Fatalf("wake failed: %v", err) 157 | } 158 | ok, err = poller.wait() 159 | if err != nil { 160 | t.Fatalf("poller failed: %v", err) 161 | } 162 | if ok { 163 | t.Fatalf("expected poller to return false") 164 | } 165 | } 166 | 167 | func TestPollerConcurrent(t *testing.T) { 168 | tfd, poller := makePoller(t) 169 | defer tfd.close() 170 | defer poller.close() 171 | 172 | oks := make(chan bool) 173 | live := make(chan bool) 174 | defer close(live) 175 | go func() { 176 | defer close(oks) 177 | for { 178 | ok, err := poller.wait() 179 | if err != nil { 180 | t.Fatalf("poller failed: %v", err) 181 | } 182 | oks <- ok 183 | if !<-live { 184 | return 185 | } 186 | } 187 | }() 188 | 189 | // Try a write 190 | select { 191 | case <-time.After(50 * time.Millisecond): 192 | case <-oks: 193 | t.Fatalf("poller did not wait") 194 | } 195 | tfd.put(t) 196 | if !<-oks { 197 | t.Fatalf("expected true") 198 | } 199 | tfd.get(t) 200 | live <- true 201 | 202 | // Try a wakeup 203 | select { 204 | case <-time.After(50 * time.Millisecond): 205 | case <-oks: 206 | t.Fatalf("poller did not wait") 207 | } 208 | err := poller.wake() 209 | if err != nil { 210 | t.Fatalf("wake failed: %v", err) 211 | } 212 | if <-oks { 213 | t.Fatalf("expected false") 214 | } 215 | live <- true 216 | 217 | // Try a close 218 | select { 219 | case <-time.After(50 * time.Millisecond): 220 | case <-oks: 221 | t.Fatalf("poller did not wait") 222 | } 223 | tfd.closeWrite(t) 224 | if !<-oks { 225 | t.Fatalf("expected true") 226 | } 227 | tfd.get(t) 228 | } 229 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/inotify_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build linux 6 | 7 | package fsnotify 8 | 9 | import ( 10 | "os" 11 | "path/filepath" 12 | "syscall" 13 | "testing" 14 | "time" 15 | ) 16 | 17 | func TestInotifyCloseRightAway(t *testing.T) { 18 | w, err := NewWatcher() 19 | if err != nil { 20 | t.Fatalf("Failed to create watcher") 21 | } 22 | 23 | // Close immediately; it won't even reach the first syscall.Read. 24 | w.Close() 25 | 26 | // Wait for the close to complete. 27 | <-time.After(50 * time.Millisecond) 28 | isWatcherReallyClosed(t, w) 29 | } 30 | 31 | func TestInotifyCloseSlightlyLater(t *testing.T) { 32 | w, err := NewWatcher() 33 | if err != nil { 34 | t.Fatalf("Failed to create watcher") 35 | } 36 | 37 | // Wait until readEvents has reached syscall.Read, and Close. 38 | <-time.After(50 * time.Millisecond) 39 | w.Close() 40 | 41 | // Wait for the close to complete. 42 | <-time.After(50 * time.Millisecond) 43 | isWatcherReallyClosed(t, w) 44 | } 45 | 46 | func TestInotifyCloseSlightlyLaterWithWatch(t *testing.T) { 47 | testDir := tempMkdir(t) 48 | defer os.RemoveAll(testDir) 49 | 50 | w, err := NewWatcher() 51 | if err != nil { 52 | t.Fatalf("Failed to create watcher") 53 | } 54 | w.Add(testDir) 55 | 56 | // Wait until readEvents has reached syscall.Read, and Close. 57 | <-time.After(50 * time.Millisecond) 58 | w.Close() 59 | 60 | // Wait for the close to complete. 61 | <-time.After(50 * time.Millisecond) 62 | isWatcherReallyClosed(t, w) 63 | } 64 | 65 | func TestInotifyCloseAfterRead(t *testing.T) { 66 | testDir := tempMkdir(t) 67 | defer os.RemoveAll(testDir) 68 | 69 | w, err := NewWatcher() 70 | if err != nil { 71 | t.Fatalf("Failed to create watcher") 72 | } 73 | 74 | err = w.Add(testDir) 75 | if err != nil { 76 | t.Fatalf("Failed to add .") 77 | } 78 | 79 | // Generate an event. 80 | os.Create(filepath.Join(testDir, "somethingSOMETHINGsomethingSOMETHING")) 81 | 82 | // Wait for readEvents to read the event, then close the watcher. 83 | <-time.After(50 * time.Millisecond) 84 | w.Close() 85 | 86 | // Wait for the close to complete. 87 | <-time.After(50 * time.Millisecond) 88 | isWatcherReallyClosed(t, w) 89 | } 90 | 91 | func isWatcherReallyClosed(t *testing.T, w *Watcher) { 92 | select { 93 | case err, ok := <-w.Errors: 94 | if ok { 95 | t.Fatalf("w.Errors is not closed; readEvents is still alive after closing (error: %v)", err) 96 | } 97 | default: 98 | t.Fatalf("w.Errors would have blocked; readEvents is still alive!") 99 | } 100 | 101 | select { 102 | case _, ok := <-w.Events: 103 | if ok { 104 | t.Fatalf("w.Events is not closed; readEvents is still alive after closing") 105 | } 106 | default: 107 | t.Fatalf("w.Events would have blocked; readEvents is still alive!") 108 | } 109 | } 110 | 111 | func TestInotifyCloseCreate(t *testing.T) { 112 | testDir := tempMkdir(t) 113 | defer os.RemoveAll(testDir) 114 | 115 | w, err := NewWatcher() 116 | if err != nil { 117 | t.Fatalf("Failed to create watcher: %v", err) 118 | } 119 | defer w.Close() 120 | 121 | err = w.Add(testDir) 122 | if err != nil { 123 | t.Fatalf("Failed to add testDir: %v", err) 124 | } 125 | h, err := os.Create(filepath.Join(testDir, "testfile")) 126 | if err != nil { 127 | t.Fatalf("Failed to create file in testdir: %v", err) 128 | } 129 | h.Close() 130 | select { 131 | case _ = <-w.Events: 132 | case err := <-w.Errors: 133 | t.Fatalf("Error from watcher: %v", err) 134 | case <-time.After(50 * time.Millisecond): 135 | t.Fatalf("Took too long to wait for event") 136 | } 137 | 138 | // At this point, we've received one event, so the goroutine is ready. 139 | // It's also blocking on syscall.Read. 140 | // Now we try to swap the file descriptor under its nose. 141 | w.Close() 142 | w, err = NewWatcher() 143 | defer w.Close() 144 | if err != nil { 145 | t.Fatalf("Failed to create second watcher: %v", err) 146 | } 147 | 148 | <-time.After(50 * time.Millisecond) 149 | err = w.Add(testDir) 150 | if err != nil { 151 | t.Fatalf("Error adding testDir again: %v", err) 152 | } 153 | } 154 | 155 | func TestInotifyStress(t *testing.T) { 156 | testDir := tempMkdir(t) 157 | defer os.RemoveAll(testDir) 158 | testFile := filepath.Join(testDir, "testfile") 159 | 160 | w, err := NewWatcher() 161 | if err != nil { 162 | t.Fatalf("Failed to create watcher: %v", err) 163 | } 164 | defer w.Close() 165 | 166 | killchan := make(chan struct{}) 167 | defer close(killchan) 168 | 169 | err = w.Add(testDir) 170 | if err != nil { 171 | t.Fatalf("Failed to add testDir: %v", err) 172 | } 173 | 174 | proc, err := os.FindProcess(os.Getpid()) 175 | if err != nil { 176 | t.Fatalf("Error finding process: %v", err) 177 | } 178 | 179 | go func() { 180 | for { 181 | select { 182 | case <-time.After(5 * time.Millisecond): 183 | err := proc.Signal(syscall.SIGUSR1) 184 | if err != nil { 185 | t.Fatalf("Signal failed: %v", err) 186 | } 187 | case <-killchan: 188 | return 189 | } 190 | } 191 | }() 192 | 193 | go func() { 194 | for { 195 | select { 196 | case <-time.After(11 * time.Millisecond): 197 | err := w.poller.wake() 198 | if err != nil { 199 | t.Fatalf("Wake failed: %v", err) 200 | } 201 | case <-killchan: 202 | return 203 | } 204 | } 205 | }() 206 | 207 | go func() { 208 | for { 209 | select { 210 | case <-killchan: 211 | return 212 | default: 213 | handle, err := os.Create(testFile) 214 | if err != nil { 215 | t.Fatalf("Create failed: %v", err) 216 | } 217 | handle.Close() 218 | time.Sleep(time.Millisecond) 219 | err = os.Remove(testFile) 220 | if err != nil { 221 | t.Fatalf("Remove failed: %v", err) 222 | } 223 | } 224 | } 225 | }() 226 | 227 | creates := 0 228 | removes := 0 229 | after := time.After(5 * time.Second) 230 | for { 231 | select { 232 | case <-after: 233 | if creates-removes > 1 || creates-removes < -1 { 234 | t.Fatalf("Creates and removes should not be off by more than one: %d creates, %d removes", creates, removes) 235 | } 236 | if creates < 50 { 237 | t.Fatalf("Expected at least 50 creates, got %d", creates) 238 | } 239 | return 240 | case err := <-w.Errors: 241 | t.Fatalf("Got an error from watcher: %v", err) 242 | case evt := <-w.Events: 243 | if evt.Name != testFile { 244 | t.Fatalf("Got an event for an unknown file: %s", evt.Name) 245 | } 246 | if evt.Op == Create { 247 | creates++ 248 | } 249 | if evt.Op == Remove { 250 | removes++ 251 | } 252 | } 253 | } 254 | } 255 | 256 | func TestInotifyRemoveTwice(t *testing.T) { 257 | testDir := tempMkdir(t) 258 | defer os.RemoveAll(testDir) 259 | testFile := filepath.Join(testDir, "testfile") 260 | 261 | handle, err := os.Create(testFile) 262 | if err != nil { 263 | t.Fatalf("Create failed: %v", err) 264 | } 265 | handle.Close() 266 | 267 | w, err := NewWatcher() 268 | if err != nil { 269 | t.Fatalf("Failed to create watcher: %v", err) 270 | } 271 | defer w.Close() 272 | 273 | err = w.Add(testFile) 274 | if err != nil { 275 | t.Fatalf("Failed to add testFile: %v", err) 276 | } 277 | 278 | err = os.Remove(testFile) 279 | if err != nil { 280 | t.Fatalf("Failed to remove testFile: %v", err) 281 | } 282 | 283 | err = w.Remove(testFile) 284 | if err != syscall.EINVAL { 285 | t.Fatalf("Expected EINVAL from Remove, got: %v", err) 286 | } 287 | 288 | err = w.Remove(testFile) 289 | if err == syscall.EINVAL { 290 | t.Fatalf("Got EINVAL again, watch was not removed") 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/open_mode_bsd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build freebsd openbsd netbsd dragonfly 6 | 7 | package fsnotify 8 | 9 | import "syscall" 10 | 11 | const openMode = syscall.O_NONBLOCK | syscall.O_RDONLY 12 | -------------------------------------------------------------------------------- /Godeps/_workspace/src/gopkg.in/fsnotify.v1/open_mode_darwin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build darwin 6 | 7 | package fsnotify 8 | 9 | import "syscall" 10 | 11 | // note: this constant is not defined on BSD 12 | const openMode = syscall.O_EVTONLY 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-compose-reloader 2 | Monitor fs events and reload docker-compose on changes 3 | 4 | Watches for FS events and reloads the docker-compose.yml from the current working dir. 5 | Also sends livereload notifications. 6 | 7 | ### Usage 8 | 9 | docker-compose-watcher --watch . --service app 10 | 11 | if `--service` is not set reload all services. 12 | If `--watch` is not set, watch current dir (and all subdirs). 13 | `--watch` can be specified multiple times 14 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/exec" 7 | "path/filepath" 8 | "strings" 9 | "time" 10 | 11 | "github.com/codegangsta/cli" 12 | "github.com/jaschaephraim/lrserver" 13 | "gopkg.in/fsnotify.v1" 14 | ) 15 | 16 | func main() { 17 | app := cli.NewApp() 18 | app.Flags = []cli.Flag{ 19 | cli.StringSliceFlag{ 20 | Name: "watch", 21 | Value: &cli.StringSlice{"."}, 22 | }, 23 | cli.StringSliceFlag{ 24 | Name: "service", 25 | Value: &cli.StringSlice{}, 26 | }, 27 | } 28 | 29 | app.Action = run 30 | app.Run(os.Args) 31 | } 32 | 33 | func run(ctx *cli.Context) { 34 | watchDirs := ctx.StringSlice("watch") 35 | services := ctx.StringSlice("service") 36 | 37 | watcher, err := fsnotify.NewWatcher() 38 | if err != nil { 39 | fmt.Fprintf(os.Stderr, "error setting up file watcher: %v", err) 40 | } 41 | for _, d := range watchDirs { 42 | watcher.Add(d) 43 | } 44 | defer watcher.Close() 45 | 46 | lr, err := lrserver.New(lrserver.DefaultName, lrserver.DefaultPort) 47 | if err != nil { 48 | fmt.Fprintf(os.Stderr, "error starting live reload: %v", err) 49 | } 50 | go lr.ListenAndServe() 51 | 52 | composeBin, err := exec.LookPath("docker-compose") 53 | if err != nil { 54 | fmt.Fprintf(os.Stderr, "could not find docker-compose path") 55 | os.Exit(1) 56 | } 57 | 58 | var cmd *exec.Cmd 59 | var t <-chan time.Time 60 | var ignore bool 61 | 62 | MainLoop: 63 | for { 64 | select { 65 | case e := <-watcher.Events: 66 | select { 67 | case <-t: 68 | ignore = false 69 | default: 70 | if ignore { 71 | continue MainLoop 72 | } 73 | 74 | match, _ := filepath.Match("*.sw*", e.Name) 75 | if match || strings.HasSuffix(e.Name, "~") { 76 | continue MainLoop 77 | } 78 | } 79 | 80 | if len(services) == 0 { 81 | cmd = exec.Command(composeBin, "up") 82 | } else { 83 | args := []string{"up", "--no-deps"} 84 | args = append(args, services...) 85 | cmd = exec.Command(composeBin, args...) 86 | } 87 | 88 | cmd.Stdout = os.Stdout 89 | cmd.Stderr = os.Stderr 90 | if err := cmd.Start(); err != nil { 91 | fmt.Fprintf(os.Stderr, "error reloading compose file: %v", err) 92 | } 93 | ignore = true 94 | time.Sleep(1 * time.Second) 95 | lr.Reload("") 96 | t = time.After(3 * time.Second) 97 | case err := <-watcher.Errors: 98 | fmt.Fprintf(os.Stderr, "error watching files: %v", err) 99 | os.Exit(1) 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | godep get 6 | godep go build 7 | --------------------------------------------------------------------------------