From f02ed95ef1e1b1d7fbcaa630d6f5b63f3e6dcf8c Mon Sep 17 00:00:00 2001 From: Hurricanezwf <1094646850@qq.com> Date: Mon, 29 Feb 2016 12:38:45 +0800 Subject: [PATCH 01/21] enable client to connect non-local [ip:port] --- conf/frpc.ini | 1 + src/frp/models/client/client.go | 3 ++- src/frp/models/client/config.go | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/conf/frpc.ini b/conf/frpc.ini index 447cdc8..c424fe8 100644 --- a/conf/frpc.ini +++ b/conf/frpc.ini @@ -11,4 +11,5 @@ log_way = console # test1即为name [test1] passwd = 123 +local_ip = 127.0.0.1 local_port = 22 diff --git a/src/frp/models/client/client.go b/src/frp/models/client/client.go index 69d256d..3a2073f 100644 --- a/src/frp/models/client/client.go +++ b/src/frp/models/client/client.go @@ -12,11 +12,12 @@ import ( type ProxyClient struct { Name string Passwd string + LocalIp string LocalPort int64 } func (p *ProxyClient) GetLocalConn() (c *conn.Conn, err error) { - c, err = conn.ConnectServer("127.0.0.1", p.LocalPort) + c, err = conn.ConnectServer(p.LocalIp, p.LocalPort) if err != nil { log.Error("ProxyName [%s], connect to local port error, %v", p.Name, err) } diff --git a/src/frp/models/client/config.go b/src/frp/models/client/config.go index f82e6ae..0e3f814 100644 --- a/src/frp/models/client/config.go +++ b/src/frp/models/client/config.go @@ -66,6 +66,11 @@ func LoadConf(confFile string) (err error) { return fmt.Errorf("Parse ini file error: proxy [%s] no passwd found", proxyClient.Name) } + proxyClient.LocalIp, ok = section["local_ip"] + if !ok { + return fmt.Errorf("Parse ini file error: proxy [%s] no local_ip found", proxyClient.Name) + } + portStr, ok := section["local_port"] if ok { proxyClient.LocalPort, err = strconv.ParseInt(portStr, 10, 64) From 3cbe432889d368ec4ab97f4621be24e1093744f7 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 29 Feb 2016 14:56:47 +0800 Subject: [PATCH 02/21] cmd/client: if no local_ip set in proxy config, use 127.0.0.1 as default --- conf/frpc.ini | 6 +++--- conf/frps.ini | 6 +++--- src/frp/models/client/config.go | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/conf/frpc.ini b/conf/frpc.ini index c424fe8..cf9c52a 100644 --- a/conf/frpc.ini +++ b/conf/frpc.ini @@ -1,4 +1,4 @@ -# common是必须的section +# [common] is integral section [common] server_addr = 127.0.0.1 server_port = 7000 @@ -6,9 +6,9 @@ log_file = ./frpc.log # debug, info, warn, error log_level = debug # file, console -log_way = console +log_way = file -# test1即为name +# test1 is the proxy name same as server's configuration [test1] passwd = 123 local_ip = 127.0.0.1 diff --git a/conf/frps.ini b/conf/frps.ini index 0c44cb1..f07a73a 100644 --- a/conf/frps.ini +++ b/conf/frps.ini @@ -1,4 +1,4 @@ -# common是必须的section +# [common] is integral section [common] bind_addr = 0.0.0.0 bind_port = 7000 @@ -6,9 +6,9 @@ log_file = ./frps.log # debug, info, warn, error log_level = debug # file, console -log_way = console +log_way = file -# test1即为name +# test1 is the proxy name, client will use this name and passwd to connect to server [test1] passwd = 123 bind_addr = 0.0.0.0 diff --git a/src/frp/models/client/config.go b/src/frp/models/client/config.go index 0e3f814..e74c3ac 100644 --- a/src/frp/models/client/config.go +++ b/src/frp/models/client/config.go @@ -55,7 +55,7 @@ func LoadConf(confFile string) (err error) { LogWay = tmpStr } - // servers + // proxies for name, section := range conf { if name != "common" { proxyClient := &ProxyClient{} @@ -68,7 +68,8 @@ func LoadConf(confFile string) (err error) { proxyClient.LocalIp, ok = section["local_ip"] if !ok { - return fmt.Errorf("Parse ini file error: proxy [%s] no local_ip found", proxyClient.Name) + // use 127.0.0.1 as default + proxyClient.LocalIp = "127.0.0.1" } portStr, ok := section["local_port"] From 838dc10c6e6fcc5955209e23f5e583c82837b517 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 7 Mar 2016 01:19:07 +0800 Subject: [PATCH 03/21] [utils/version] add version.go --- src/frp/utils/version/version.go | 44 +++++++++++++++++++++++++++ src/frp/utils/version/version_test.go | 42 +++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/frp/utils/version/version.go create mode 100644 src/frp/utils/version/version_test.go diff --git a/src/frp/utils/version/version.go b/src/frp/utils/version/version.go new file mode 100644 index 0000000..8f1020e --- /dev/null +++ b/src/frp/utils/version/version.go @@ -0,0 +1,44 @@ +package version + +import ( + "strconv" + "strings" +) + +var version string = "0.2.0" + +func Full() string { + return version +} + +func Proto(v string) int64 { + arr := strings.Split(v, ".") + if len(arr) < 2 { + return 0 + } + res, _ := strconv.ParseInt(arr[0], 10, 64) + return res +} + +func Major(v string) int64 { + arr := strings.Split(v, ".") + if len(arr) < 2 { + return 0 + } + res, _ := strconv.ParseInt(arr[1], 10, 64) + return res +} + +func Minor(v string) int64 { + arr := strings.Split(v, ".") + if len(arr) < 2 { + return 0 + } + res, _ := strconv.ParseInt(arr[2], 10, 64) + return res +} + +// add every case there if server will not accept client's protocol and return false +func Compat(client string, server string) bool { + return true +} diff --git a/src/frp/utils/version/version_test.go b/src/frp/utils/version/version_test.go new file mode 100644 index 0000000..417cc54 --- /dev/null +++ b/src/frp/utils/version/version_test.go @@ -0,0 +1,42 @@ +package version + +import ( + "fmt" + "strconv" + "strings" + "testing" +) + +func TestFull(t *testing.T) { + version := Full() + arr := strings.Split(version, ".") + if len(arr) != 3 { + t.Errorf("Version string error: %s", version) + } + + proto, err := strconv.ParseInt(arr[0], 10, 64) + if err != nil || proto < 0 { + t.Errorf("Version proto error") + } + + major, err := strconv.ParseInt(arr[1], 10, 64) + if err != nil || major < 0 { + t.Errorf("Version major error") + } + + minor, err := strconv.ParseInt(arr[2], 10, 64) + if err != nil || minor < 0 { + t.Errorf("Version minor error") + } +} + +func TestVersion(t *testing.T) { + proto := Proto(Full()) + major := Major(Full()) + minor := Minor(Full()) + parseVerion := fmt.Sprintf("%d.%d.%d", proto, major, minor) + version := Full() + if parseVerion != version { + t.Errorf("Get version incorrect, version [%s], parseVerion [%s]", version, parseVerion) + } +} From 975c2a97c804aa06de4bc90dc8890d4a029095f8 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 14 Mar 2016 00:21:40 +0800 Subject: [PATCH 04/21] add support for command line --- Godeps/Godeps.json | 5 + .../github.com/docopt/docopt-go/.gitignore | 25 + .../github.com/docopt/docopt-go/.travis.yml | 31 + .../src/github.com/docopt/docopt-go/LICENSE | 20 + .../src/github.com/docopt/docopt-go/README.md | 88 ++ .../src/github.com/docopt/docopt-go/docopt.go | 1239 +++++++++++++++++ .../docopt/docopt-go/test_golang.docopt | 9 + .../docopt/docopt-go/testcases.docopt | 957 +++++++++++++ Makefile | 3 +- conf/frpc.ini | 3 +- conf/frps.ini | 5 +- src/frp/cmd/frps/main.go | 62 +- src/frp/models/server/config.go | 18 +- 13 files changed, 2449 insertions(+), 16 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/docopt/docopt-go/.gitignore create mode 100644 Godeps/_workspace/src/github.com/docopt/docopt-go/.travis.yml create mode 100644 Godeps/_workspace/src/github.com/docopt/docopt-go/LICENSE create mode 100644 Godeps/_workspace/src/github.com/docopt/docopt-go/README.md create mode 100644 Godeps/_workspace/src/github.com/docopt/docopt-go/docopt.go create mode 100644 Godeps/_workspace/src/github.com/docopt/docopt-go/test_golang.docopt create mode 100644 Godeps/_workspace/src/github.com/docopt/docopt-go/testcases.docopt diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 70fd281..3614cb4 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -10,6 +10,11 @@ "Comment": "v1.5.0-9-gfb7314f", "Rev": "fb7314f8ac86b83ccd34386518d97cf2363e2ae5" }, + { + "ImportPath": "github.com/docopt/docopt-go", + "Comment": "0.6.2", + "Rev": "784ddc588536785e7299f7272f39101f7faccc3f" + }, { "ImportPath": "github.com/vaughan0/go-ini", "Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1" diff --git a/Godeps/_workspace/src/github.com/docopt/docopt-go/.gitignore b/Godeps/_workspace/src/github.com/docopt/docopt-go/.gitignore new file mode 100644 index 0000000..49ad16c --- /dev/null +++ b/Godeps/_workspace/src/github.com/docopt/docopt-go/.gitignore @@ -0,0 +1,25 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe + +# coverage droppings +profile.cov diff --git a/Godeps/_workspace/src/github.com/docopt/docopt-go/.travis.yml b/Godeps/_workspace/src/github.com/docopt/docopt-go/.travis.yml new file mode 100644 index 0000000..65fad59 --- /dev/null +++ b/Godeps/_workspace/src/github.com/docopt/docopt-go/.travis.yml @@ -0,0 +1,31 @@ +# Travis CI (http://travis-ci.org/) is a continuous integration +# service for open source projects. This file configures it +# to run unit tests for docopt-go. + +language: go + +go: + - 1.4 + - 1.5 + - tip + +matrix: + fast_finish: true + +before_install: + - go get golang.org/x/tools/cmd/vet + - go get golang.org/x/tools/cmd/cover + - go get github.com/golang/lint/golint + - go get github.com/mattn/goveralls + +install: + - go get -d -v ./... && go build -v ./... + +script: + - go vet -x ./... + - $HOME/gopath/bin/golint ./... + - go test -v ./... + - go test -covermode=count -coverprofile=profile.cov . + +after_script: + - $HOME/gopath/bin/goveralls -coverprofile=profile.cov -service=travis-ci diff --git a/Godeps/_workspace/src/github.com/docopt/docopt-go/LICENSE b/Godeps/_workspace/src/github.com/docopt/docopt-go/LICENSE new file mode 100644 index 0000000..8841af1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/docopt/docopt-go/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Keith Batten + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Godeps/_workspace/src/github.com/docopt/docopt-go/README.md b/Godeps/_workspace/src/github.com/docopt/docopt-go/README.md new file mode 100644 index 0000000..71c92aa --- /dev/null +++ b/Godeps/_workspace/src/github.com/docopt/docopt-go/README.md @@ -0,0 +1,88 @@ +docopt-go +========= + +[![Build Status](https://travis-ci.org/docopt/docopt.go.svg?branch=master)](https://travis-ci.org/docopt/docopt.go) +[![Coverage Status](https://coveralls.io/repos/docopt/docopt.go/badge.png)](https://coveralls.io/r/docopt/docopt.go) +[![GoDoc](https://godoc.org/github.com/docopt/docopt.go?status.png)](https://godoc.org/github.com/docopt/docopt.go) + +An implementation of [docopt](http://docopt.org/) in the +[Go](http://golang.org/) programming language. + +**docopt** helps you create *beautiful* command-line interfaces easily: + +```go +package main + +import ( + "fmt" + "github.com/docopt/docopt-go" +) + +func main() { + usage := `Naval Fate. + +Usage: + naval_fate ship new ... + naval_fate ship move [--speed=] + naval_fate ship shoot + naval_fate mine (set|remove) [--moored|--drifting] + naval_fate -h | --help + naval_fate --version + +Options: + -h --help Show this screen. + --version Show version. + --speed= Speed in knots [default: 10]. + --moored Moored (anchored) mine. + --drifting Drifting mine.` + + arguments, _ := docopt.Parse(usage, nil, true, "Naval Fate 2.0", false) + fmt.Println(arguments) +} +``` + +**docopt** parses command-line arguments based on a help message. Don't +write parser code: a good help message already has all the necessary +information in it. + +## Installation + +⚠ Use the alias “docopt-go”. To use docopt in your Go code: + +```go +import "github.com/docopt/docopt-go" +``` + +To install docopt according to your `$GOPATH`: + +```console +$ go get github.com/docopt/docopt-go +``` + +## API + +```go +func Parse(doc string, argv []string, help bool, version string, + optionsFirst bool, exit ...bool) (map[string]interface{}, error) +``` +Parse `argv` based on the command-line interface described in `doc`. + +Given a conventional command-line help message, docopt creates a parser and +processes the arguments. See +https://github.com/docopt/docopt#help-message-format for a description of the +help message format. If `argv` is `nil`, `os.Args[1:]` is used. + +docopt returns a map of option names to the values parsed from `argv`, and an +error or `nil`. + +More documentation for docopt is available at +[GoDoc.org](https://godoc.org/github.com/docopt/docopt.go). + +## Testing + +All tests from the Python version are implemented and passing +at [Travis CI](https://travis-ci.org/docopt/docopt.go). New +language-agnostic tests have been added +to [test_golang.docopt](test_golang.docopt). + +To run tests for docopt-go, use `go test`. diff --git a/Godeps/_workspace/src/github.com/docopt/docopt-go/docopt.go b/Godeps/_workspace/src/github.com/docopt/docopt-go/docopt.go new file mode 100644 index 0000000..d929fc3 --- /dev/null +++ b/Godeps/_workspace/src/github.com/docopt/docopt-go/docopt.go @@ -0,0 +1,1239 @@ +// Licensed under terms of MIT license (see LICENSE-MIT) +// Copyright (c) 2013 Keith Batten, kbatten@gmail.com + +/* +Package docopt parses command-line arguments based on a help message. + +⚠ Use the alias “docopt-go”: + import "github.com/docopt/docopt-go" +or + $ go get github.com/docopt/docopt-go +*/ +package docopt + +import ( + "fmt" + "os" + "reflect" + "regexp" + "strings" + "unicode" +) + +/* +Parse `argv` based on the command-line interface described in `doc`. + +Given a conventional command-line help message, docopt creates a parser and +processes the arguments. See +https://github.com/docopt/docopt#help-message-format for a description of the +help message format. If `argv` is `nil`, `os.Args[1:]` is used. + +docopt returns a map of option names to the values parsed from `argv`, and an +error or `nil`. + +Set `help` to `false` to disable automatic help messages on `-h` or `--help`. +If `version` is a non-empty string, it will be printed when `--version` is +specified. Set `optionsFirst` to `true` to require that options always come +before positional arguments; otherwise they can overlap. + +By default, docopt calls `os.Exit(0)` if it handled a built-in option such as +`-h` or `--version`. If the user errored with a wrong command or options, +docopt exits with a return code of 1. To stop docopt from calling `os.Exit()` +and to handle your own return codes, pass an optional last parameter of `false` +for `exit`. +*/ +func Parse(doc string, argv []string, help bool, version string, + optionsFirst bool, exit ...bool) (map[string]interface{}, error) { + // if "false" was the (optional) last arg, don't call os.Exit() + exitOk := true + if len(exit) > 0 { + exitOk = exit[0] + } + args, output, err := parse(doc, argv, help, version, optionsFirst) + if _, ok := err.(*UserError); ok { + // the user gave us bad input + fmt.Fprintln(os.Stderr, output) + if exitOk { + os.Exit(1) + } + } else if len(output) > 0 && err == nil { + // the user asked for help or `--version` + fmt.Println(output) + if exitOk { + os.Exit(0) + } + } + return args, err +} + +// parse and return a map of args, output and all errors +func parse(doc string, argv []string, help bool, version string, optionsFirst bool) (args map[string]interface{}, output string, err error) { + if argv == nil && len(os.Args) > 1 { + argv = os.Args[1:] + } + + usageSections := parseSection("usage:", doc) + + if len(usageSections) == 0 { + err = newLanguageError("\"usage:\" (case-insensitive) not found.") + return + } + if len(usageSections) > 1 { + err = newLanguageError("More than one \"usage:\" (case-insensitive).") + return + } + usage := usageSections[0] + + options := parseDefaults(doc) + formal, err := formalUsage(usage) + if err != nil { + output = handleError(err, usage) + return + } + + pat, err := parsePattern(formal, &options) + if err != nil { + output = handleError(err, usage) + return + } + + patternArgv, err := parseArgv(newTokenList(argv, errorUser), &options, optionsFirst) + if err != nil { + output = handleError(err, usage) + return + } + patFlat, err := pat.flat(patternOption) + if err != nil { + output = handleError(err, usage) + return + } + patternOptions := patFlat.unique() + + patFlat, err = pat.flat(patternOptionSSHORTCUT) + if err != nil { + output = handleError(err, usage) + return + } + for _, optionsShortcut := range patFlat { + docOptions := parseDefaults(doc) + optionsShortcut.children = docOptions.unique().diff(patternOptions) + } + + if output = extras(help, version, patternArgv, doc); len(output) > 0 { + return + } + + err = pat.fix() + if err != nil { + output = handleError(err, usage) + return + } + matched, left, collected := pat.match(&patternArgv, nil) + if matched && len(*left) == 0 { + patFlat, err = pat.flat(patternDefault) + if err != nil { + output = handleError(err, usage) + return + } + args = append(patFlat, *collected...).dictionary() + return + } + + err = newUserError("") + output = handleError(err, usage) + return +} + +func handleError(err error, usage string) string { + if _, ok := err.(*UserError); ok { + return strings.TrimSpace(fmt.Sprintf("%s\n%s", err, usage)) + } + return "" +} + +func parseSection(name, source string) []string { + p := regexp.MustCompile(`(?im)^([^\n]*` + name + `[^\n]*\n?(?:[ \t].*?(?:\n|$))*)`) + s := p.FindAllString(source, -1) + if s == nil { + s = []string{} + } + for i, v := range s { + s[i] = strings.TrimSpace(v) + } + return s +} + +func parseDefaults(doc string) patternList { + defaults := patternList{} + p := regexp.MustCompile(`\n[ \t]*(-\S+?)`) + for _, s := range parseSection("options:", doc) { + // FIXME corner case "bla: options: --foo" + _, _, s = stringPartition(s, ":") // get rid of "options:" + split := p.Split("\n"+s, -1)[1:] + match := p.FindAllStringSubmatch("\n"+s, -1) + for i := range split { + optionDescription := match[i][1] + split[i] + if strings.HasPrefix(optionDescription, "-") { + defaults = append(defaults, parseOption(optionDescription)) + } + } + } + return defaults +} + +func parsePattern(source string, options *patternList) (*pattern, error) { + tokens := tokenListFromPattern(source) + result, err := parseExpr(tokens, options) + if err != nil { + return nil, err + } + if tokens.current() != nil { + return nil, tokens.errorFunc("unexpected ending: %s" + strings.Join(tokens.tokens, " ")) + } + return newRequired(result...), nil +} + +func parseArgv(tokens *tokenList, options *patternList, optionsFirst bool) (patternList, error) { + /* + Parse command-line argument vector. + + If options_first: + argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ; + else: + argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ; + */ + parsed := patternList{} + for tokens.current() != nil { + if tokens.current().eq("--") { + for _, v := range tokens.tokens { + parsed = append(parsed, newArgument("", v)) + } + return parsed, nil + } else if tokens.current().hasPrefix("--") { + pl, err := parseLong(tokens, options) + if err != nil { + return nil, err + } + parsed = append(parsed, pl...) + } else if tokens.current().hasPrefix("-") && !tokens.current().eq("-") { + ps, err := parseShorts(tokens, options) + if err != nil { + return nil, err + } + parsed = append(parsed, ps...) + } else if optionsFirst { + for _, v := range tokens.tokens { + parsed = append(parsed, newArgument("", v)) + } + return parsed, nil + } else { + parsed = append(parsed, newArgument("", tokens.move().String())) + } + } + return parsed, nil +} + +func parseOption(optionDescription string) *pattern { + optionDescription = strings.TrimSpace(optionDescription) + options, _, description := stringPartition(optionDescription, " ") + options = strings.Replace(options, ",", " ", -1) + options = strings.Replace(options, "=", " ", -1) + + short := "" + long := "" + argcount := 0 + var value interface{} + value = false + + reDefault := regexp.MustCompile(`(?i)\[default: (.*)\]`) + for _, s := range strings.Fields(options) { + if strings.HasPrefix(s, "--") { + long = s + } else if strings.HasPrefix(s, "-") { + short = s + } else { + argcount = 1 + } + if argcount > 0 { + matched := reDefault.FindAllStringSubmatch(description, -1) + if len(matched) > 0 { + value = matched[0][1] + } else { + value = nil + } + } + } + return newOption(short, long, argcount, value) +} + +func parseExpr(tokens *tokenList, options *patternList) (patternList, error) { + // expr ::= seq ( '|' seq )* ; + seq, err := parseSeq(tokens, options) + if err != nil { + return nil, err + } + if !tokens.current().eq("|") { + return seq, nil + } + var result patternList + if len(seq) > 1 { + result = patternList{newRequired(seq...)} + } else { + result = seq + } + for tokens.current().eq("|") { + tokens.move() + seq, err = parseSeq(tokens, options) + if err != nil { + return nil, err + } + if len(seq) > 1 { + result = append(result, newRequired(seq...)) + } else { + result = append(result, seq...) + } + } + if len(result) > 1 { + return patternList{newEither(result...)}, nil + } + return result, nil +} + +func parseSeq(tokens *tokenList, options *patternList) (patternList, error) { + // seq ::= ( atom [ '...' ] )* ; + result := patternList{} + for !tokens.current().match(true, "]", ")", "|") { + atom, err := parseAtom(tokens, options) + if err != nil { + return nil, err + } + if tokens.current().eq("...") { + atom = patternList{newOneOrMore(atom...)} + tokens.move() + } + result = append(result, atom...) + } + return result, nil +} + +func parseAtom(tokens *tokenList, options *patternList) (patternList, error) { + // atom ::= '(' expr ')' | '[' expr ']' | 'options' | long | shorts | argument | command ; + tok := tokens.current() + result := patternList{} + if tokens.current().match(false, "(", "[") { + tokens.move() + var matching string + pl, err := parseExpr(tokens, options) + if err != nil { + return nil, err + } + if tok.eq("(") { + matching = ")" + result = patternList{newRequired(pl...)} + } else if tok.eq("[") { + matching = "]" + result = patternList{newOptional(pl...)} + } + moved := tokens.move() + if !moved.eq(matching) { + return nil, tokens.errorFunc("unmatched '%s', expected: '%s' got: '%s'", tok, matching, moved) + } + return result, nil + } else if tok.eq("options") { + tokens.move() + return patternList{newOptionsShortcut()}, nil + } else if tok.hasPrefix("--") && !tok.eq("--") { + return parseLong(tokens, options) + } else if tok.hasPrefix("-") && !tok.eq("-") && !tok.eq("--") { + return parseShorts(tokens, options) + } else if tok.hasPrefix("<") && tok.hasSuffix(">") || tok.isUpper() { + return patternList{newArgument(tokens.move().String(), nil)}, nil + } + return patternList{newCommand(tokens.move().String(), false)}, nil +} + +func parseLong(tokens *tokenList, options *patternList) (patternList, error) { + // long ::= '--' chars [ ( ' ' | '=' ) chars ] ; + long, eq, v := stringPartition(tokens.move().String(), "=") + var value interface{} + var opt *pattern + if eq == "" && v == "" { + value = nil + } else { + value = v + } + + if !strings.HasPrefix(long, "--") { + return nil, newError("long option '%s' doesn't start with --", long) + } + similar := patternList{} + for _, o := range *options { + if o.long == long { + similar = append(similar, o) + } + } + if tokens.err == errorUser && len(similar) == 0 { // if no exact match + similar = patternList{} + for _, o := range *options { + if strings.HasPrefix(o.long, long) { + similar = append(similar, o) + } + } + } + if len(similar) > 1 { // might be simply specified ambiguously 2+ times? + similarLong := make([]string, len(similar)) + for i, s := range similar { + similarLong[i] = s.long + } + return nil, tokens.errorFunc("%s is not a unique prefix: %s?", long, strings.Join(similarLong, ", ")) + } else if len(similar) < 1 { + argcount := 0 + if eq == "=" { + argcount = 1 + } + opt = newOption("", long, argcount, false) + *options = append(*options, opt) + if tokens.err == errorUser { + var val interface{} + if argcount > 0 { + val = value + } else { + val = true + } + opt = newOption("", long, argcount, val) + } + } else { + opt = newOption(similar[0].short, similar[0].long, similar[0].argcount, similar[0].value) + if opt.argcount == 0 { + if value != nil { + return nil, tokens.errorFunc("%s must not have an argument", opt.long) + } + } else { + if value == nil { + if tokens.current().match(true, "--") { + return nil, tokens.errorFunc("%s requires argument", opt.long) + } + moved := tokens.move() + if moved != nil { + value = moved.String() // only set as string if not nil + } + } + } + if tokens.err == errorUser { + if value != nil { + opt.value = value + } else { + opt.value = true + } + } + } + + return patternList{opt}, nil +} + +func parseShorts(tokens *tokenList, options *patternList) (patternList, error) { + // shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ; + tok := tokens.move() + if !tok.hasPrefix("-") || tok.hasPrefix("--") { + return nil, newError("short option '%s' doesn't start with -", tok) + } + left := strings.TrimLeft(tok.String(), "-") + parsed := patternList{} + for left != "" { + var opt *pattern + short := "-" + left[0:1] + left = left[1:] + similar := patternList{} + for _, o := range *options { + if o.short == short { + similar = append(similar, o) + } + } + if len(similar) > 1 { + return nil, tokens.errorFunc("%s is specified ambiguously %d times", short, len(similar)) + } else if len(similar) < 1 { + opt = newOption(short, "", 0, false) + *options = append(*options, opt) + if tokens.err == errorUser { + opt = newOption(short, "", 0, true) + } + } else { // why copying is necessary here? + opt = newOption(short, similar[0].long, similar[0].argcount, similar[0].value) + var value interface{} + if opt.argcount > 0 { + if left == "" { + if tokens.current().match(true, "--") { + return nil, tokens.errorFunc("%s requires argument", short) + } + value = tokens.move().String() + } else { + value = left + left = "" + } + } + if tokens.err == errorUser { + if value != nil { + opt.value = value + } else { + opt.value = true + } + } + } + parsed = append(parsed, opt) + } + return parsed, nil +} + +func newTokenList(source []string, err errorType) *tokenList { + errorFunc := newError + if err == errorUser { + errorFunc = newUserError + } else if err == errorLanguage { + errorFunc = newLanguageError + } + return &tokenList{source, errorFunc, err} +} + +func tokenListFromString(source string) *tokenList { + return newTokenList(strings.Fields(source), errorUser) +} + +func tokenListFromPattern(source string) *tokenList { + p := regexp.MustCompile(`([\[\]\(\)\|]|\.\.\.)`) + source = p.ReplaceAllString(source, ` $1 `) + p = regexp.MustCompile(`\s+|(\S*<.*?>)`) + split := p.Split(source, -1) + match := p.FindAllStringSubmatch(source, -1) + var result []string + l := len(split) + for i := 0; i < l; i++ { + if len(split[i]) > 0 { + result = append(result, split[i]) + } + if i < l-1 && len(match[i][1]) > 0 { + result = append(result, match[i][1]) + } + } + return newTokenList(result, errorLanguage) +} + +func formalUsage(section string) (string, error) { + _, _, section = stringPartition(section, ":") // drop "usage:" + pu := strings.Fields(section) + + if len(pu) == 0 { + return "", newLanguageError("no fields found in usage (perhaps a spacing error).") + } + + result := "( " + for _, s := range pu[1:] { + if s == pu[0] { + result += ") | ( " + } else { + result += s + " " + } + } + result += ")" + + return result, nil +} + +func extras(help bool, version string, options patternList, doc string) string { + if help { + for _, o := range options { + if (o.name == "-h" || o.name == "--help") && o.value == true { + return strings.Trim(doc, "\n") + } + } + } + if version != "" { + for _, o := range options { + if (o.name == "--version") && o.value == true { + return version + } + } + } + return "" +} + +type errorType int + +const ( + errorUser errorType = iota + errorLanguage +) + +func (e errorType) String() string { + switch e { + case errorUser: + return "errorUser" + case errorLanguage: + return "errorLanguage" + } + return "" +} + +// UserError records an error with program arguments. +type UserError struct { + msg string + Usage string +} + +func (e UserError) Error() string { + return e.msg +} +func newUserError(msg string, f ...interface{}) error { + return &UserError{fmt.Sprintf(msg, f...), ""} +} + +// LanguageError records an error with the doc string. +type LanguageError struct { + msg string +} + +func (e LanguageError) Error() string { + return e.msg +} +func newLanguageError(msg string, f ...interface{}) error { + return &LanguageError{fmt.Sprintf(msg, f...)} +} + +var newError = fmt.Errorf + +type tokenList struct { + tokens []string + errorFunc func(string, ...interface{}) error + err errorType +} +type token string + +func (t *token) eq(s string) bool { + if t == nil { + return false + } + return string(*t) == s +} +func (t *token) match(matchNil bool, tokenStrings ...string) bool { + if t == nil && matchNil { + return true + } else if t == nil && !matchNil { + return false + } + + for _, tok := range tokenStrings { + if tok == string(*t) { + return true + } + } + return false +} +func (t *token) hasPrefix(prefix string) bool { + if t == nil { + return false + } + return strings.HasPrefix(string(*t), prefix) +} +func (t *token) hasSuffix(suffix string) bool { + if t == nil { + return false + } + return strings.HasSuffix(string(*t), suffix) +} +func (t *token) isUpper() bool { + if t == nil { + return false + } + return isStringUppercase(string(*t)) +} +func (t *token) String() string { + if t == nil { + return "" + } + return string(*t) +} + +func (tl *tokenList) current() *token { + if len(tl.tokens) > 0 { + return (*token)(&(tl.tokens[0])) + } + return nil +} + +func (tl *tokenList) length() int { + return len(tl.tokens) +} + +func (tl *tokenList) move() *token { + if len(tl.tokens) > 0 { + t := tl.tokens[0] + tl.tokens = tl.tokens[1:] + return (*token)(&t) + } + return nil +} + +type patternType uint + +const ( + // leaf + patternArgument patternType = 1 << iota + patternCommand + patternOption + + // branch + patternRequired + patternOptionAL + patternOptionSSHORTCUT // Marker/placeholder for [options] shortcut. + patternOneOrMore + patternEither + + patternLeaf = patternArgument + + patternCommand + + patternOption + patternBranch = patternRequired + + patternOptionAL + + patternOptionSSHORTCUT + + patternOneOrMore + + patternEither + patternAll = patternLeaf + patternBranch + patternDefault = 0 +) + +func (pt patternType) String() string { + switch pt { + case patternArgument: + return "argument" + case patternCommand: + return "command" + case patternOption: + return "option" + case patternRequired: + return "required" + case patternOptionAL: + return "optional" + case patternOptionSSHORTCUT: + return "optionsshortcut" + case patternOneOrMore: + return "oneormore" + case patternEither: + return "either" + case patternLeaf: + return "leaf" + case patternBranch: + return "branch" + case patternAll: + return "all" + case patternDefault: + return "default" + } + return "" +} + +type pattern struct { + t patternType + + children patternList + + name string + value interface{} + + short string + long string + argcount int +} + +type patternList []*pattern + +func newBranchPattern(t patternType, pl ...*pattern) *pattern { + var p pattern + p.t = t + p.children = make(patternList, len(pl)) + copy(p.children, pl) + return &p +} + +func newRequired(pl ...*pattern) *pattern { + return newBranchPattern(patternRequired, pl...) +} + +func newEither(pl ...*pattern) *pattern { + return newBranchPattern(patternEither, pl...) +} + +func newOneOrMore(pl ...*pattern) *pattern { + return newBranchPattern(patternOneOrMore, pl...) +} + +func newOptional(pl ...*pattern) *pattern { + return newBranchPattern(patternOptionAL, pl...) +} + +func newOptionsShortcut() *pattern { + var p pattern + p.t = patternOptionSSHORTCUT + return &p +} + +func newLeafPattern(t patternType, name string, value interface{}) *pattern { + // default: value=nil + var p pattern + p.t = t + p.name = name + p.value = value + return &p +} + +func newArgument(name string, value interface{}) *pattern { + // default: value=nil + return newLeafPattern(patternArgument, name, value) +} + +func newCommand(name string, value interface{}) *pattern { + // default: value=false + var p pattern + p.t = patternCommand + p.name = name + p.value = value + return &p +} + +func newOption(short, long string, argcount int, value interface{}) *pattern { + // default: "", "", 0, false + var p pattern + p.t = patternOption + p.short = short + p.long = long + if long != "" { + p.name = long + } else { + p.name = short + } + p.argcount = argcount + if value == false && argcount > 0 { + p.value = nil + } else { + p.value = value + } + return &p +} + +func (p *pattern) flat(types patternType) (patternList, error) { + if p.t&patternLeaf != 0 { + if types == patternDefault { + types = patternAll + } + if p.t&types != 0 { + return patternList{p}, nil + } + return patternList{}, nil + } + + if p.t&patternBranch != 0 { + if p.t&types != 0 { + return patternList{p}, nil + } + result := patternList{} + for _, child := range p.children { + childFlat, err := child.flat(types) + if err != nil { + return nil, err + } + result = append(result, childFlat...) + } + return result, nil + } + return nil, newError("unknown pattern type: %d, %d", p.t, types) +} + +func (p *pattern) fix() error { + err := p.fixIdentities(nil) + if err != nil { + return err + } + p.fixRepeatingArguments() + return nil +} + +func (p *pattern) fixIdentities(uniq patternList) error { + // Make pattern-tree tips point to same object if they are equal. + if p.t&patternBranch == 0 { + return nil + } + if uniq == nil { + pFlat, err := p.flat(patternDefault) + if err != nil { + return err + } + uniq = pFlat.unique() + } + for i, child := range p.children { + if child.t&patternBranch == 0 { + ind, err := uniq.index(child) + if err != nil { + return err + } + p.children[i] = uniq[ind] + } else { + err := child.fixIdentities(uniq) + if err != nil { + return err + } + } + } + return nil +} + +func (p *pattern) fixRepeatingArguments() { + // Fix elements that should accumulate/increment values. + var either []patternList + + for _, child := range p.transform().children { + either = append(either, child.children) + } + for _, cas := range either { + casMultiple := patternList{} + for _, e := range cas { + if cas.count(e) > 1 { + casMultiple = append(casMultiple, e) + } + } + for _, e := range casMultiple { + if e.t == patternArgument || e.t == patternOption && e.argcount > 0 { + switch e.value.(type) { + case string: + e.value = strings.Fields(e.value.(string)) + case []string: + default: + e.value = []string{} + } + } + if e.t == patternCommand || e.t == patternOption && e.argcount == 0 { + e.value = 0 + } + } + } +} + +func (p *pattern) match(left *patternList, collected *patternList) (bool, *patternList, *patternList) { + if collected == nil { + collected = &patternList{} + } + if p.t&patternRequired != 0 { + l := left + c := collected + for _, p := range p.children { + var matched bool + matched, l, c = p.match(l, c) + if !matched { + return false, left, collected + } + } + return true, l, c + } else if p.t&patternOptionAL != 0 || p.t&patternOptionSSHORTCUT != 0 { + for _, p := range p.children { + _, left, collected = p.match(left, collected) + } + return true, left, collected + } else if p.t&patternOneOrMore != 0 { + if len(p.children) != 1 { + panic("OneOrMore.match(): assert len(p.children) == 1") + } + l := left + c := collected + var lAlt *patternList + matched := true + times := 0 + for matched { + // could it be that something didn't match but changed l or c? + matched, l, c = p.children[0].match(l, c) + if matched { + times++ + } + if lAlt == l { + break + } + lAlt = l + } + if times >= 1 { + return true, l, c + } + return false, left, collected + } else if p.t&patternEither != 0 { + type outcomeStruct struct { + matched bool + left *patternList + collected *patternList + length int + } + outcomes := []outcomeStruct{} + for _, p := range p.children { + matched, l, c := p.match(left, collected) + outcome := outcomeStruct{matched, l, c, len(*l)} + if matched { + outcomes = append(outcomes, outcome) + } + } + if len(outcomes) > 0 { + minLen := outcomes[0].length + minIndex := 0 + for i, v := range outcomes { + if v.length < minLen { + minIndex = i + } + } + return outcomes[minIndex].matched, outcomes[minIndex].left, outcomes[minIndex].collected + } + return false, left, collected + } else if p.t&patternLeaf != 0 { + pos, match := p.singleMatch(left) + var increment interface{} + if match == nil { + return false, left, collected + } + leftAlt := make(patternList, len((*left)[:pos]), len((*left)[:pos])+len((*left)[pos+1:])) + copy(leftAlt, (*left)[:pos]) + leftAlt = append(leftAlt, (*left)[pos+1:]...) + sameName := patternList{} + for _, a := range *collected { + if a.name == p.name { + sameName = append(sameName, a) + } + } + + switch p.value.(type) { + case int, []string: + switch p.value.(type) { + case int: + increment = 1 + case []string: + switch match.value.(type) { + case string: + increment = []string{match.value.(string)} + default: + increment = match.value + } + } + if len(sameName) == 0 { + match.value = increment + collectedMatch := make(patternList, len(*collected), len(*collected)+1) + copy(collectedMatch, *collected) + collectedMatch = append(collectedMatch, match) + return true, &leftAlt, &collectedMatch + } + switch sameName[0].value.(type) { + case int: + sameName[0].value = sameName[0].value.(int) + increment.(int) + case []string: + sameName[0].value = append(sameName[0].value.([]string), increment.([]string)...) + } + return true, &leftAlt, collected + } + collectedMatch := make(patternList, len(*collected), len(*collected)+1) + copy(collectedMatch, *collected) + collectedMatch = append(collectedMatch, match) + return true, &leftAlt, &collectedMatch + } + panic("unmatched type") +} + +func (p *pattern) singleMatch(left *patternList) (int, *pattern) { + if p.t&patternArgument != 0 { + for n, pat := range *left { + if pat.t&patternArgument != 0 { + return n, newArgument(p.name, pat.value) + } + } + return -1, nil + } else if p.t&patternCommand != 0 { + for n, pat := range *left { + if pat.t&patternArgument != 0 { + if pat.value == p.name { + return n, newCommand(p.name, true) + } + break + } + } + return -1, nil + } else if p.t&patternOption != 0 { + for n, pat := range *left { + if p.name == pat.name { + return n, pat + } + } + return -1, nil + } + panic("unmatched type") +} + +func (p *pattern) String() string { + if p.t&patternOption != 0 { + return fmt.Sprintf("%s(%s, %s, %d, %+v)", p.t, p.short, p.long, p.argcount, p.value) + } else if p.t&patternLeaf != 0 { + return fmt.Sprintf("%s(%s, %+v)", p.t, p.name, p.value) + } else if p.t&patternBranch != 0 { + result := "" + for i, child := range p.children { + if i > 0 { + result += ", " + } + result += child.String() + } + return fmt.Sprintf("%s(%s)", p.t, result) + } + panic("unmatched type") +} + +func (p *pattern) transform() *pattern { + /* + Expand pattern into an (almost) equivalent one, but with single Either. + + Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d) + Quirks: [-a] => (-a), (-a...) => (-a -a) + */ + result := []patternList{} + groups := []patternList{patternList{p}} + parents := patternRequired + + patternOptionAL + + patternOptionSSHORTCUT + + patternEither + + patternOneOrMore + for len(groups) > 0 { + children := groups[0] + groups = groups[1:] + var child *pattern + for _, c := range children { + if c.t&parents != 0 { + child = c + break + } + } + if child != nil { + children.remove(child) + if child.t&patternEither != 0 { + for _, c := range child.children { + r := patternList{} + r = append(r, c) + r = append(r, children...) + groups = append(groups, r) + } + } else if child.t&patternOneOrMore != 0 { + r := patternList{} + r = append(r, child.children.double()...) + r = append(r, children...) + groups = append(groups, r) + } else { + r := patternList{} + r = append(r, child.children...) + r = append(r, children...) + groups = append(groups, r) + } + } else { + result = append(result, children) + } + } + either := patternList{} + for _, e := range result { + either = append(either, newRequired(e...)) + } + return newEither(either...) +} + +func (p *pattern) eq(other *pattern) bool { + return reflect.DeepEqual(p, other) +} + +func (pl patternList) unique() patternList { + table := make(map[string]bool) + result := patternList{} + for _, v := range pl { + if !table[v.String()] { + table[v.String()] = true + result = append(result, v) + } + } + return result +} + +func (pl patternList) index(p *pattern) (int, error) { + for i, c := range pl { + if c.eq(p) { + return i, nil + } + } + return -1, newError("%s not in list", p) +} + +func (pl patternList) count(p *pattern) int { + count := 0 + for _, c := range pl { + if c.eq(p) { + count++ + } + } + return count +} + +func (pl patternList) diff(l patternList) patternList { + lAlt := make(patternList, len(l)) + copy(lAlt, l) + result := make(patternList, 0, len(pl)) + for _, v := range pl { + if v != nil { + match := false + for i, w := range lAlt { + if w.eq(v) { + match = true + lAlt[i] = nil + break + } + } + if match == false { + result = append(result, v) + } + } + } + return result +} + +func (pl patternList) double() patternList { + l := len(pl) + result := make(patternList, l*2) + copy(result, pl) + copy(result[l:2*l], pl) + return result +} + +func (pl *patternList) remove(p *pattern) { + (*pl) = pl.diff(patternList{p}) +} + +func (pl patternList) dictionary() map[string]interface{} { + dict := make(map[string]interface{}) + for _, a := range pl { + dict[a.name] = a.value + } + return dict +} + +func stringPartition(s, sep string) (string, string, string) { + sepPos := strings.Index(s, sep) + if sepPos == -1 { // no seperator found + return s, "", "" + } + split := strings.SplitN(s, sep, 2) + return split[0], sep, split[1] +} + +// returns true if all cased characters in the string are uppercase +// and there are there is at least one cased charcter +func isStringUppercase(s string) bool { + if strings.ToUpper(s) != s { + return false + } + for _, c := range []rune(s) { + if unicode.IsUpper(c) { + return true + } + } + return false +} diff --git a/Godeps/_workspace/src/github.com/docopt/docopt-go/test_golang.docopt b/Godeps/_workspace/src/github.com/docopt/docopt-go/test_golang.docopt new file mode 100644 index 0000000..323fd67 --- /dev/null +++ b/Godeps/_workspace/src/github.com/docopt/docopt-go/test_golang.docopt @@ -0,0 +1,9 @@ +r"""usage: prog [NAME_-2]...""" +$ prog 10 20 +{"NAME_-2": ["10", "20"]} + +$ prog 10 +{"NAME_-2": ["10"]} + +$ prog +{"NAME_-2": []} diff --git a/Godeps/_workspace/src/github.com/docopt/docopt-go/testcases.docopt b/Godeps/_workspace/src/github.com/docopt/docopt-go/testcases.docopt new file mode 100644 index 0000000..efe9a07 --- /dev/null +++ b/Godeps/_workspace/src/github.com/docopt/docopt-go/testcases.docopt @@ -0,0 +1,957 @@ +r"""Usage: prog + +""" +$ prog +{} + +$ prog --xxx +"user-error" + + +r"""Usage: prog [options] + +Options: -a All. + +""" +$ prog +{"-a": false} + +$ prog -a +{"-a": true} + +$ prog -x +"user-error" + + +r"""Usage: prog [options] + +Options: --all All. + +""" +$ prog +{"--all": false} + +$ prog --all +{"--all": true} + +$ prog --xxx +"user-error" + + +r"""Usage: prog [options] + +Options: -v, --verbose Verbose. + +""" +$ prog --verbose +{"--verbose": true} + +$ prog --ver +{"--verbose": true} + +$ prog -v +{"--verbose": true} + + +r"""Usage: prog [options] + +Options: -p PATH + +""" +$ prog -p home/ +{"-p": "home/"} + +$ prog -phome/ +{"-p": "home/"} + +$ prog -p +"user-error" + + +r"""Usage: prog [options] + +Options: --path + +""" +$ prog --path home/ +{"--path": "home/"} + +$ prog --path=home/ +{"--path": "home/"} + +$ prog --pa home/ +{"--path": "home/"} + +$ prog --pa=home/ +{"--path": "home/"} + +$ prog --path +"user-error" + + +r"""Usage: prog [options] + +Options: -p PATH, --path= Path to files. + +""" +$ prog -proot +{"--path": "root"} + + +r"""Usage: prog [options] + +Options: -p --path PATH Path to files. + +""" +$ prog -p root +{"--path": "root"} + +$ prog --path root +{"--path": "root"} + + +r"""Usage: prog [options] + +Options: + -p PATH Path to files [default: ./] + +""" +$ prog +{"-p": "./"} + +$ prog -phome +{"-p": "home"} + + +r"""UsAgE: prog [options] + +OpTiOnS: --path= Path to files + [dEfAuLt: /root] + +""" +$ prog +{"--path": "/root"} + +$ prog --path=home +{"--path": "home"} + + +r"""usage: prog [options] + +options: + -a Add + -r Remote + -m Message + +""" +$ prog -a -r -m Hello +{"-a": true, + "-r": true, + "-m": "Hello"} + +$ prog -armyourass +{"-a": true, + "-r": true, + "-m": "yourass"} + +$ prog -a -r +{"-a": true, + "-r": true, + "-m": null} + + +r"""Usage: prog [options] + +Options: --version + --verbose + +""" +$ prog --version +{"--version": true, + "--verbose": false} + +$ prog --verbose +{"--version": false, + "--verbose": true} + +$ prog --ver +"user-error" + +$ prog --verb +{"--version": false, + "--verbose": true} + + +r"""usage: prog [-a -r -m ] + +options: + -a Add + -r Remote + -m Message + +""" +$ prog -armyourass +{"-a": true, + "-r": true, + "-m": "yourass"} + + +r"""usage: prog [-armmsg] + +options: -a Add + -r Remote + -m Message + +""" +$ prog -a -r -m Hello +{"-a": true, + "-r": true, + "-m": "Hello"} + + +r"""usage: prog -a -b + +options: + -a + -b + +""" +$ prog -a -b +{"-a": true, "-b": true} + +$ prog -b -a +{"-a": true, "-b": true} + +$ prog -a +"user-error" + +$ prog +"user-error" + + +r"""usage: prog (-a -b) + +options: -a + -b + +""" +$ prog -a -b +{"-a": true, "-b": true} + +$ prog -b -a +{"-a": true, "-b": true} + +$ prog -a +"user-error" + +$ prog +"user-error" + + +r"""usage: prog [-a] -b + +options: -a + -b + +""" +$ prog -a -b +{"-a": true, "-b": true} + +$ prog -b -a +{"-a": true, "-b": true} + +$ prog -a +"user-error" + +$ prog -b +{"-a": false, "-b": true} + +$ prog +"user-error" + + +r"""usage: prog [(-a -b)] + +options: -a + -b + +""" +$ prog -a -b +{"-a": true, "-b": true} + +$ prog -b -a +{"-a": true, "-b": true} + +$ prog -a +"user-error" + +$ prog -b +"user-error" + +$ prog +{"-a": false, "-b": false} + + +r"""usage: prog (-a|-b) + +options: -a + -b + +""" +$ prog -a -b +"user-error" + +$ prog +"user-error" + +$ prog -a +{"-a": true, "-b": false} + +$ prog -b +{"-a": false, "-b": true} + + +r"""usage: prog [ -a | -b ] + +options: -a + -b + +""" +$ prog -a -b +"user-error" + +$ prog +{"-a": false, "-b": false} + +$ prog -a +{"-a": true, "-b": false} + +$ prog -b +{"-a": false, "-b": true} + + +r"""usage: prog """ +$ prog 10 +{"": "10"} + +$ prog 10 20 +"user-error" + +$ prog +"user-error" + + +r"""usage: prog []""" +$ prog 10 +{"": "10"} + +$ prog 10 20 +"user-error" + +$ prog +{"": null} + + +r"""usage: prog """ +$ prog 10 20 40 +{"": "10", "": "20", "": "40"} + +$ prog 10 20 +"user-error" + +$ prog +"user-error" + + +r"""usage: prog [ ]""" +$ prog 10 20 40 +{"": "10", "": "20", "": "40"} + +$ prog 10 20 +{"": "10", "": "20", "": null} + +$ prog +"user-error" + + +r"""usage: prog [ | ]""" +$ prog 10 20 40 +"user-error" + +$ prog 20 40 +{"": null, "": "20", "": "40"} + +$ prog +{"": null, "": null, "": null} + + +r"""usage: prog ( --all | ) + +options: + --all + +""" +$ prog 10 --all +{"": "10", "--all": true, "": null} + +$ prog 10 +{"": null, "--all": false, "": "10"} + +$ prog +"user-error" + + +r"""usage: prog [ ]""" +$ prog 10 20 +{"": ["10", "20"]} + +$ prog 10 +{"": ["10"]} + +$ prog +{"": []} + + +r"""usage: prog [( )]""" +$ prog 10 20 +{"": ["10", "20"]} + +$ prog 10 +"user-error" + +$ prog +{"": []} + + +r"""usage: prog NAME...""" +$ prog 10 20 +{"NAME": ["10", "20"]} + +$ prog 10 +{"NAME": ["10"]} + +$ prog +"user-error" + + +r"""usage: prog [NAME]...""" +$ prog 10 20 +{"NAME": ["10", "20"]} + +$ prog 10 +{"NAME": ["10"]} + +$ prog +{"NAME": []} + + +r"""usage: prog [NAME...]""" +$ prog 10 20 +{"NAME": ["10", "20"]} + +$ prog 10 +{"NAME": ["10"]} + +$ prog +{"NAME": []} + + +r"""usage: prog [NAME [NAME ...]]""" +$ prog 10 20 +{"NAME": ["10", "20"]} + +$ prog 10 +{"NAME": ["10"]} + +$ prog +{"NAME": []} + + +r"""usage: prog (NAME | --foo NAME) + +options: --foo + +""" +$ prog 10 +{"NAME": "10", "--foo": false} + +$ prog --foo 10 +{"NAME": "10", "--foo": true} + +$ prog --foo=10 +"user-error" + + +r"""usage: prog (NAME | --foo) [--bar | NAME] + +options: --foo +options: --bar + +""" +$ prog 10 +{"NAME": ["10"], "--foo": false, "--bar": false} + +$ prog 10 20 +{"NAME": ["10", "20"], "--foo": false, "--bar": false} + +$ prog --foo --bar +{"NAME": [], "--foo": true, "--bar": true} + + +r"""Naval Fate. + +Usage: + prog ship new ... + prog ship [] move [--speed=] + prog ship shoot + prog mine (set|remove) [--moored|--drifting] + prog -h | --help + prog --version + +Options: + -h --help Show this screen. + --version Show version. + --speed= Speed in knots [default: 10]. + --moored Mored (anchored) mine. + --drifting Drifting mine. + +""" +$ prog ship Guardian move 150 300 --speed=20 +{"--drifting": false, + "--help": false, + "--moored": false, + "--speed": "20", + "--version": false, + "": ["Guardian"], + "": "150", + "": "300", + "mine": false, + "move": true, + "new": false, + "remove": false, + "set": false, + "ship": true, + "shoot": false} + + +r"""usage: prog --hello""" +$ prog --hello +{"--hello": true} + + +r"""usage: prog [--hello=]""" +$ prog +{"--hello": null} + +$ prog --hello wrld +{"--hello": "wrld"} + + +r"""usage: prog [-o]""" +$ prog +{"-o": false} + +$ prog -o +{"-o": true} + + +r"""usage: prog [-opr]""" +$ prog -op +{"-o": true, "-p": true, "-r": false} + + +r"""usage: prog --aabb | --aa""" +$ prog --aa +{"--aabb": false, "--aa": true} + +$ prog --a +"user-error" # not a unique prefix + +# +# Counting number of flags +# + +r"""Usage: prog -v""" +$ prog -v +{"-v": true} + + +r"""Usage: prog [-v -v]""" +$ prog +{"-v": 0} + +$ prog -v +{"-v": 1} + +$ prog -vv +{"-v": 2} + + +r"""Usage: prog -v ...""" +$ prog +"user-error" + +$ prog -v +{"-v": 1} + +$ prog -vv +{"-v": 2} + +$ prog -vvvvvv +{"-v": 6} + + +r"""Usage: prog [-v | -vv | -vvv] + +This one is probably most readable user-friednly variant. + +""" +$ prog +{"-v": 0} + +$ prog -v +{"-v": 1} + +$ prog -vv +{"-v": 2} + +$ prog -vvvv +"user-error" + + +r"""usage: prog [--ver --ver]""" +$ prog --ver --ver +{"--ver": 2} + + +# +# Counting commands +# + +r"""usage: prog [go]""" +$ prog go +{"go": true} + + +r"""usage: prog [go go]""" +$ prog +{"go": 0} + +$ prog go +{"go": 1} + +$ prog go go +{"go": 2} + +$ prog go go go +"user-error" + +r"""usage: prog go...""" +$ prog go go go go go +{"go": 5} + +# +# [options] does not include options from usage-pattern +# +r"""usage: prog [options] [-a] + +options: -a + -b +""" +$ prog -a +{"-a": true, "-b": false} + +$ prog -aa +"user-error" + +# +# Test [options] shourtcut +# + +r"""Usage: prog [options] A +Options: + -q Be quiet + -v Be verbose. + +""" +$ prog arg +{"A": "arg", "-v": false, "-q": false} + +$ prog -v arg +{"A": "arg", "-v": true, "-q": false} + +$ prog -q arg +{"A": "arg", "-v": false, "-q": true} + +# +# Test single dash +# + +r"""usage: prog [-]""" + +$ prog - +{"-": true} + +$ prog +{"-": false} + +# +# If argument is repeated, its value should always be a list +# + +r"""usage: prog [NAME [NAME ...]]""" + +$ prog a b +{"NAME": ["a", "b"]} + +$ prog +{"NAME": []} + +# +# Option's argument defaults to null/None +# + +r"""usage: prog [options] +options: + -a Add + -m Message + +""" +$ prog -a +{"-m": null, "-a": true} + +# +# Test options without description +# + +r"""usage: prog --hello""" +$ prog --hello +{"--hello": true} + +r"""usage: prog [--hello=]""" +$ prog +{"--hello": null} + +$ prog --hello wrld +{"--hello": "wrld"} + +r"""usage: prog [-o]""" +$ prog +{"-o": false} + +$ prog -o +{"-o": true} + +r"""usage: prog [-opr]""" +$ prog -op +{"-o": true, "-p": true, "-r": false} + +r"""usage: git [-v | --verbose]""" +$ prog -v +{"-v": true, "--verbose": false} + +r"""usage: git remote [-v | --verbose]""" +$ prog remote -v +{"remote": true, "-v": true, "--verbose": false} + +# +# Test empty usage pattern +# + +r"""usage: prog""" +$ prog +{} + +r"""usage: prog + prog +""" +$ prog 1 2 +{"": "1", "": "2"} + +$ prog +{"": null, "": null} + +r"""usage: prog + prog +""" +$ prog +{"": null, "": null} + +# +# Option's argument should not capture default value from usage pattern +# + +r"""usage: prog [--file=]""" +$ prog +{"--file": null} + +r"""usage: prog [--file=] + +options: --file + +""" +$ prog +{"--file": null} + +r"""Usage: prog [-a ] + +Options: -a, --address TCP address [default: localhost:6283]. + +""" +$ prog +{"--address": "localhost:6283"} + +# +# If option with argument could be repeated, +# its arguments should be accumulated into a list +# + +r"""usage: prog --long= ...""" + +$ prog --long one +{"--long": ["one"]} + +$ prog --long one --long two +{"--long": ["one", "two"]} + +# +# Test multiple elements repeated at once +# + +r"""usage: prog (go --speed=)...""" +$ prog go left --speed=5 go right --speed=9 +{"go": 2, "": ["left", "right"], "--speed": ["5", "9"]} + +# +# Required options should work with option shortcut +# + +r"""usage: prog [options] -a + +options: -a + +""" +$ prog -a +{"-a": true} + +# +# If option could be repeated its defaults should be split into a list +# + +r"""usage: prog [-o ]... + +options: -o [default: x] + +""" +$ prog -o this -o that +{"-o": ["this", "that"]} + +$ prog +{"-o": ["x"]} + +r"""usage: prog [-o ]... + +options: -o [default: x y] + +""" +$ prog -o this +{"-o": ["this"]} + +$ prog +{"-o": ["x", "y"]} + +# +# Test stacked option's argument +# + +r"""usage: prog -pPATH + +options: -p PATH + +""" +$ prog -pHOME +{"-p": "HOME"} + +# +# Issue 56: Repeated mutually exclusive args give nested lists sometimes +# + +r"""Usage: foo (--xx=x|--yy=y)...""" +$ prog --xx=1 --yy=2 +{"--xx": ["1"], "--yy": ["2"]} + +# +# POSIXly correct tokenization +# + +r"""usage: prog []""" +$ prog f.txt +{"": "f.txt"} + +r"""usage: prog [--input=]...""" +$ prog --input a.txt --input=b.txt +{"--input": ["a.txt", "b.txt"]} + +# +# Issue 85: `[options]` shourtcut with multiple subcommands +# + +r"""usage: prog good [options] + prog fail [options] + +options: --loglevel=N + +""" +$ prog fail --loglevel 5 +{"--loglevel": "5", "fail": true, "good": false} + +# +# Usage-section syntax +# + +r"""usage:prog --foo""" +$ prog --foo +{"--foo": true} + +r"""PROGRAM USAGE: prog --foo""" +$ prog --foo +{"--foo": true} + +r"""Usage: prog --foo + prog --bar +NOT PART OF SECTION""" +$ prog --foo +{"--foo": true, "--bar": false} + +r"""Usage: + prog --foo + prog --bar + +NOT PART OF SECTION""" +$ prog --foo +{"--foo": true, "--bar": false} + +r"""Usage: + prog --foo + prog --bar +NOT PART OF SECTION""" +$ prog --foo +{"--foo": true, "--bar": false} + +# +# Options-section syntax +# + +r"""Usage: prog [options] + +global options: --foo +local options: --baz + --bar +other options: + --egg + --spam +-not-an-option- + +""" +$ prog --baz --egg +{"--foo": false, "--baz": true, "--bar": false, "--egg": true, "--spam": false} diff --git a/Makefile b/Makefile index 797e11f..1488621 100644 --- a/Makefile +++ b/Makefile @@ -7,10 +7,9 @@ build: godep fmt frps frpc godep: @go get github.com/tools/godep - godep restore fmt: - @GOPATH=$(NEW_GOPATH) godep go fmt ./... + GOPATH=$(NEW_GOPATH) godep go fmt ./... frps: GOPATH=$(NEW_GOPATH) godep go build -o bin/frps ./src/frp/cmd/frps diff --git a/conf/frpc.ini b/conf/frpc.ini index cf9c52a..39232c3 100644 --- a/conf/frpc.ini +++ b/conf/frpc.ini @@ -2,7 +2,8 @@ [common] server_addr = 127.0.0.1 server_port = 7000 -log_file = ./frpc.log +# console or real logFile path like ./frpc.log +log_file = console # debug, info, warn, error log_level = debug # file, console diff --git a/conf/frps.ini b/conf/frps.ini index f07a73a..0358a7a 100644 --- a/conf/frps.ini +++ b/conf/frps.ini @@ -2,11 +2,10 @@ [common] bind_addr = 0.0.0.0 bind_port = 7000 -log_file = ./frps.log +# console or real logFile path like ./frps.log +log_file = console # debug, info, warn, error log_level = debug -# file, console -log_way = file # test1 is the proxy name, client will use this name and passwd to connect to server [test1] diff --git a/src/frp/cmd/frps/main.go b/src/frp/cmd/frps/main.go index c4ce4d7..d21bb4c 100644 --- a/src/frp/cmd/frps/main.go +++ b/src/frp/cmd/frps/main.go @@ -1,19 +1,79 @@ package main import ( + "fmt" "os" + "strconv" + "strings" + + docopt "github.com/docopt/docopt-go" "frp/models/server" "frp/utils/conn" "frp/utils/log" + "frp/utils/version" ) +var ( + configFile string = "./frps.ini" +) + +var usage string = `frps is the server of frp + +Usage: + frps [-c config_file] [-L log_file] [--log-level=] [--addr=] + frps -h | --help | --version + +Options: + -c config_file set config file + -L log_file set output log file, including console + --log-level= set log level: debug, info, warn, error + --addr= listen addr for client, example: 0.0.0.0:7000 + -h --help show this screen + --version show version +` + func main() { - err := server.LoadConf("./frps.ini") + // the configures parsed from file will be replaced by those from command line if exist + args, err := docopt.Parse(usage, nil, true, version.Full(), false) + + if args["-c"] != nil { + configFile = args["-c"].(string) + } + err = server.LoadConf(configFile) if err != nil { + fmt.Println(err) os.Exit(-1) } + if args["-L"] != nil { + if args["-L"].(string) == "console" { + server.LogWay = "console" + } else { + server.LogWay = "file" + server.LogFile = args["-L"].(string) + } + } + + if args["--log-level"] != nil { + server.LogLevel = args["--log-level"].(string) + } + + if args["--addr"] != nil { + addr := strings.Split(args["--addr"].(string), ":") + if len(addr) != 2 { + fmt.Println("--addr format error: example 0.0.0.0:7000") + os.Exit(1) + } + bindPort, err := strconv.ParseInt(addr[1], 10, 64) + if err != nil { + fmt.Println("--addr format error, example 0.0.0.0:7000") + os.Exit(1) + } + server.BindAddr = addr[0] + server.BindPort = bindPort + } + log.InitLog(server.LogWay, server.LogFile, server.LogLevel) l, err := conn.Listen(server.BindAddr, server.BindPort) diff --git a/src/frp/models/server/config.go b/src/frp/models/server/config.go index ec70071..019e1cb 100644 --- a/src/frp/models/server/config.go +++ b/src/frp/models/server/config.go @@ -10,10 +10,10 @@ import ( // common config var ( BindAddr string = "0.0.0.0" - BindPort int64 = 9527 - LogFile string = "./frps.log" - LogLevel string = "warn" - LogWay string = "file" + BindPort int64 = 7000 + LogFile string = "console" + LogWay string = "console" // console or file + LogLevel string = "info" HeartBeatTimeout int64 = 30 UserConnTimeout int64 = 10 ) @@ -43,6 +43,11 @@ func LoadConf(confFile string) (err error) { tmpStr, ok = conf.Get("common", "log_file") if ok { LogFile = tmpStr + if LogFile == "console" { + LogWay = "console" + } else { + LogWay = "file" + } } tmpStr, ok = conf.Get("common", "log_level") @@ -50,11 +55,6 @@ func LoadConf(confFile string) (err error) { LogLevel = tmpStr } - tmpStr, ok = conf.Get("common", "log_way") - if ok { - LogWay = tmpStr - } - // servers for name, section := range conf { if name != "common" { From 3218eda4819a5a41537ab31ccc2d2ecab6353a75 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 14 Mar 2016 00:39:32 +0800 Subject: [PATCH 05/21] models/server: fix bug, program will core if listener is nil --- src/frp/models/server/server.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/frp/models/server/server.go b/src/frp/models/server/server.go index e8d6d81..0b9f38a 100644 --- a/src/frp/models/server/server.go +++ b/src/frp/models/server/server.go @@ -128,7 +128,9 @@ func (p *ProxyServer) Start() (err error) { func (p *ProxyServer) Close() { p.Lock() p.Status = consts.Idle - p.listener.Close() + if p.listener != nil { + p.listener.Close() + } close(p.ctlMsgChan) close(p.cliConnChan) p.userConnList = list.New() From a56b29b153bf7294ecb5b691c1614a61504f4384 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 14 Mar 2016 00:48:22 +0800 Subject: [PATCH 06/21] add support for command line with frpc --- .gitignore | 1 - conf/frpc.ini | 4 +-- src/frp/cmd/frpc/main.go | 62 ++++++++++++++++++++++++++++++++- src/frp/models/client/config.go | 20 +++++------ 4 files changed, 72 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index e237cc4..72c9fd3 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,3 @@ bin/ # Cache *.swp -*.swo diff --git a/conf/frpc.ini b/conf/frpc.ini index 39232c3..09dfeb7 100644 --- a/conf/frpc.ini +++ b/conf/frpc.ini @@ -1,13 +1,11 @@ # [common] is integral section [common] -server_addr = 127.0.0.1 +server_addr = 0.0.0.0 server_port = 7000 # console or real logFile path like ./frpc.log log_file = console # debug, info, warn, error log_level = debug -# file, console -log_way = file # test1 is the proxy name same as server's configuration [test1] diff --git a/src/frp/cmd/frpc/main.go b/src/frp/cmd/frpc/main.go index 0177234..92f3ac7 100644 --- a/src/frp/cmd/frpc/main.go +++ b/src/frp/cmd/frpc/main.go @@ -1,19 +1,79 @@ package main import ( + "fmt" "os" + "strconv" + "strings" "sync" + docopt "github.com/docopt/docopt-go" + "frp/models/client" "frp/utils/log" + "frp/utils/version" ) +var ( + configFile string = "./frpc.ini" +) + +var usage string = `frpc is the client of frp + +Usage: + frpc [-c config_file] [-L log_file] [--log-level=] [--server-addr=] + frpc -h | --help | --version + +Options: + -c config_file set config file + -L log_file set output log file, including console + --log-level= set log level: debug, info, warn, error + --server-addr= addr which frps is listening for, example: 0.0.0.0:7000 + -h --help show this screen + --version show version +` + func main() { - err := client.LoadConf("./frpc.ini") + // the configures parsed from file will be replaced by those from command line if exist + args, err := docopt.Parse(usage, nil, true, version.Full(), false) + + if args["-c"] != nil { + configFile = args["-c"].(string) + } + err = client.LoadConf(configFile) if err != nil { + fmt.Println(err) os.Exit(-1) } + if args["-L"] != nil { + if args["-L"].(string) == "console" { + client.LogWay = "console" + } else { + client.LogWay = "file" + client.LogFile = args["-L"].(string) + } + } + + if args["--log-level"] != nil { + client.LogLevel = args["--log-level"].(string) + } + + if args["--server-addr"] != nil { + addr := strings.Split(args["--server-addr"].(string), ":") + if len(addr) != 2 { + fmt.Println("--server-addr format error: example 0.0.0.0:7000") + os.Exit(1) + } + serverPort, err := strconv.ParseInt(addr[1], 10, 64) + if err != nil { + fmt.Println("--server-addr format error, example 0.0.0.0:7000") + os.Exit(1) + } + client.ServerAddr = addr[0] + client.ServerPort = serverPort + } + log.InitLog(client.LogWay, client.LogFile, client.LogLevel) // wait until all control goroutine exit diff --git a/src/frp/models/client/config.go b/src/frp/models/client/config.go index e74c3ac..0a2a383 100644 --- a/src/frp/models/client/config.go +++ b/src/frp/models/client/config.go @@ -11,11 +11,11 @@ import ( var ( ServerAddr string = "0.0.0.0" ServerPort int64 = 7000 - LogFile string = "./frpc.log" - LogLevel string = "warn" - LogWay string = "file" - HeartBeatInterval int64 = 5 - HeartBeatTimeout int64 = 30 + LogFile string = "console" + LogWay string = "console" + LogLevel string = "info" + HeartBeatInterval int64 = 20 + HeartBeatTimeout int64 = 60 ) var ProxyClients map[string]*ProxyClient = make(map[string]*ProxyClient) @@ -43,6 +43,11 @@ func LoadConf(confFile string) (err error) { tmpStr, ok = conf.Get("common", "log_file") if ok { LogFile = tmpStr + if LogFile == "console" { + LogWay = "console" + } else { + LogWay = "file" + } } tmpStr, ok = conf.Get("common", "log_level") @@ -50,11 +55,6 @@ func LoadConf(confFile string) (err error) { LogLevel = tmpStr } - tmpStr, ok = conf.Get("common", "log_way") - if ok { - LogWay = tmpStr - } - // proxies for name, section := range conf { if name != "common" { From 6e458229f60be23b278bbeb4065941f928eb89bc Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 14 Mar 2016 11:18:24 +0800 Subject: [PATCH 07/21] add license info --- src/frp/cmd/frpc/control.go | 14 ++++++++++++++ src/frp/cmd/frpc/main.go | 14 ++++++++++++++ src/frp/cmd/frps/control.go | 14 ++++++++++++++ src/frp/cmd/frps/main.go | 14 ++++++++++++++ src/frp/models/client/client.go | 14 ++++++++++++++ src/frp/models/client/config.go | 14 ++++++++++++++ src/frp/models/consts/consts.go | 14 ++++++++++++++ src/frp/models/msg/msg.go | 14 ++++++++++++++ src/frp/models/server/config.go | 14 ++++++++++++++ src/frp/models/server/server.go | 14 ++++++++++++++ src/frp/utils/broadcast/broadcast.go | 14 ++++++++++++++ src/frp/utils/broadcast/broadcast_test.go | 14 ++++++++++++++ src/frp/utils/conn/conn.go | 14 ++++++++++++++ src/frp/utils/log/log.go | 14 ++++++++++++++ src/frp/utils/pcrypto/pcrypto.go | 14 ++++++++++++++ src/frp/utils/pcrypto/pcrypto_test.go | 14 ++++++++++++++ src/frp/utils/version/version.go | 14 ++++++++++++++ src/frp/utils/version/version_test.go | 14 ++++++++++++++ 18 files changed, 252 insertions(+) diff --git a/src/frp/cmd/frpc/control.go b/src/frp/cmd/frpc/control.go index 799e8d7..bba7d0c 100644 --- a/src/frp/cmd/frpc/control.go +++ b/src/frp/cmd/frpc/control.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/src/frp/cmd/frpc/main.go b/src/frp/cmd/frpc/main.go index 92f3ac7..2f8a7f1 100644 --- a/src/frp/cmd/frpc/main.go +++ b/src/frp/cmd/frpc/main.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/src/frp/cmd/frps/control.go b/src/frp/cmd/frps/control.go index 06203aa..b60365b 100644 --- a/src/frp/cmd/frps/control.go +++ b/src/frp/cmd/frps/control.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/src/frp/cmd/frps/main.go b/src/frp/cmd/frps/main.go index d21bb4c..4c918f0 100644 --- a/src/frp/cmd/frps/main.go +++ b/src/frp/cmd/frps/main.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/src/frp/models/client/client.go b/src/frp/models/client/client.go index 3a2073f..ce99f09 100644 --- a/src/frp/models/client/client.go +++ b/src/frp/models/client/client.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package client import ( diff --git a/src/frp/models/client/config.go b/src/frp/models/client/config.go index 0a2a383..363049c 100644 --- a/src/frp/models/client/config.go +++ b/src/frp/models/client/config.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package client import ( diff --git a/src/frp/models/consts/consts.go b/src/frp/models/consts/consts.go index d48332a..56191ff 100644 --- a/src/frp/models/consts/consts.go +++ b/src/frp/models/consts/consts.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package consts // server status diff --git a/src/frp/models/msg/msg.go b/src/frp/models/msg/msg.go index 6555296..5c62bfb 100644 --- a/src/frp/models/msg/msg.go +++ b/src/frp/models/msg/msg.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package msg type GeneralRes struct { diff --git a/src/frp/models/server/config.go b/src/frp/models/server/config.go index 019e1cb..7bef192 100644 --- a/src/frp/models/server/config.go +++ b/src/frp/models/server/config.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package server import ( diff --git a/src/frp/models/server/server.go b/src/frp/models/server/server.go index 0b9f38a..0b2c3d4 100644 --- a/src/frp/models/server/server.go +++ b/src/frp/models/server/server.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package server import ( diff --git a/src/frp/utils/broadcast/broadcast.go b/src/frp/utils/broadcast/broadcast.go index 4d45012..940b783 100644 --- a/src/frp/utils/broadcast/broadcast.go +++ b/src/frp/utils/broadcast/broadcast.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package broadcast type Broadcast struct { diff --git a/src/frp/utils/broadcast/broadcast_test.go b/src/frp/utils/broadcast/broadcast_test.go index 3354adc..b1314fe 100644 --- a/src/frp/utils/broadcast/broadcast_test.go +++ b/src/frp/utils/broadcast/broadcast_test.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package broadcast import ( diff --git a/src/frp/utils/conn/conn.go b/src/frp/utils/conn/conn.go index dceabd9..fbf4be9 100644 --- a/src/frp/utils/conn/conn.go +++ b/src/frp/utils/conn/conn.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package conn import ( diff --git a/src/frp/utils/log/log.go b/src/frp/utils/log/log.go index f6587cd..ed66ca0 100644 --- a/src/frp/utils/log/log.go +++ b/src/frp/utils/log/log.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package log import ( diff --git a/src/frp/utils/pcrypto/pcrypto.go b/src/frp/utils/pcrypto/pcrypto.go index 729db90..e260a3e 100644 --- a/src/frp/utils/pcrypto/pcrypto.go +++ b/src/frp/utils/pcrypto/pcrypto.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package pcrypto import ( diff --git a/src/frp/utils/pcrypto/pcrypto_test.go b/src/frp/utils/pcrypto/pcrypto_test.go index 73377e3..ea88c63 100644 --- a/src/frp/utils/pcrypto/pcrypto_test.go +++ b/src/frp/utils/pcrypto/pcrypto_test.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package pcrypto import ( diff --git a/src/frp/utils/version/version.go b/src/frp/utils/version/version.go index 8f1020e..680fa8c 100644 --- a/src/frp/utils/version/version.go +++ b/src/frp/utils/version/version.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package version import ( diff --git a/src/frp/utils/version/version_test.go b/src/frp/utils/version/version_test.go index 417cc54..0906d8a 100644 --- a/src/frp/utils/version/version_test.go +++ b/src/frp/utils/version/version_test.go @@ -1,3 +1,17 @@ +// Copyright 2016 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package version import ( From b4acba94802094838457115ec1965952b5a21e3e Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 14 Mar 2016 14:28:37 +0800 Subject: [PATCH 08/21] doc: add architecture.png --- doc/pic/architecture.png | Bin 0 -> 11000 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/pic/architecture.png diff --git a/doc/pic/architecture.png b/doc/pic/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..566269f1942f45e402d9800c9eb839c7a2b3b426 GIT binary patch literal 11000 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

EW3xKMv4! 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 Date: Mon, 14 Mar 2016 14:31:48 +0800 Subject: [PATCH 09/21] doc: update architecture.png --- doc/pic/architecture.png | Bin 11000 -> 10678 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/pic/architecture.png b/doc/pic/architecture.png index 566269f1942f45e402d9800c9eb839c7a2b3b426..2ee54fc23ec71089a03a05b6878198e54d26f516 100644 GIT binary patch literal 10678 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 literal 11000 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

EW3xKMv4! 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 Date: Mon, 14 Mar 2016 15:26:26 +0800 Subject: [PATCH 10/21] doc: add quick_start_zh.md --- README.md | 22 ++++++++++++++- doc/quick_start_zh.md | 66 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 doc/quick_start_zh.md diff --git a/README.md b/README.md index 766797f..14d609d 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,24 @@ [![Build Status](https://travis-ci.org/fatedier/frp.svg)](https://travis-ci.org/fatedier/frp) -A fast reverse proxy. +## What is frp? + +frp is a fast reverse proxy which can help you expose a local server behind a NAT or firewall to the internet. + +## Status + +frp is under development and you can try it with available version 0.1. + +## Quick Start + +Read the [QuickStart](doc/quick_start_en.md) [使用文档](doc/quick_start_zh.md) + +## Architecture + +![architecture](doc/pic/architecture.png) + +## What can I do with frp? + +* Expose any http service behind a NAT or firewall to the internet by a server with public IP address. +* Expose any tcp service behind a NAT or firewall to the internet by a server with public IP address. +* Inspect all http requests/responses that are transmitted over the tunnel(future). diff --git a/doc/quick_start_zh.md b/doc/quick_start_zh.md new file mode 100644 index 0000000..8bfec44 --- /dev/null +++ b/doc/quick_start_zh.md @@ -0,0 +1,66 @@ +# frp 使用文档 + +frp 相比于其他项目而言非常易于部署和使用,这里我们用一个简单的示例演示如何通过一台拥有公网IP地址的服务器B,访问处于内网环境中的服务器A的ssh端口,服务器B的IP地址为 x.x.x.x(测试时替换为真实的IP地址)。 + +### 下载源码 + +推荐直接使用 `go get github.com/fatedier/frp` 下载源代码安装,执行命令后代码将会拷贝到 `$GOPATH/src/github.com/fatedier/frp` 目录下。 + +或者可以使用 `git clone https://github.com/fatedier/frp.git ./` 拷贝到 `$GOPATH/src` 下的任意目录。 + +### 编译 + +进入下载后的源码根目录,执行 `make` 命令,等待编译完成。 + +编译完成后, **bin** 目录下是编译好的可执行文件,**conf** 目录下是示例配置文件。 + +### 依赖 + +* go 1.4 以上版本 +* godep (如果检查不存在,编译时会通过 go get 命令安装) + +### 部署 + +1. 将 ./bin/frps 和 ./conf/frps.ini 拷贝至服务器B任意目录。 +2. 将 ./bin/frpc 和 ./conf/frpc.ini 拷贝至服务器A任意目录。 +3. 修改两边的配置文件,见下一节说明。 +4. 在服务器B执行 `nohup ./frps &` 或者 `nohup ./frps -c ./frps.ini &`。 +5. 在服务器A执行 `nohup ./frpc &` 或者 `nohup ./frpc -c ./frpc.ini &`。 +6. 通过 `ssh -oPort=6000 {user}@x.x.x.x` 测试是否能够成功连接服务器A({user}替换为服务器B上存在的真实用户)。 + +### 配置文件 + +#### frps.ini + +```ini +[common] +bind_addr = 0.0.0.0 +# 用于接收 frpc 连接的端口 +bind_port = 7000 +log_file = ./frps.log +log_level = info + +# test 为代理的自定义名称,可以有多个,不能重复,和frpc中名称对应 +[test] +passwd = 123 +bind_addr = 0.0.0.0 +# 最后将通过此端口访问后端服务 +listen_port = 6000 +``` + +#### frpc.ini + +```ini +[common] +# frps 所在服务器绑定的IP地址 +server_addr = x.x.x.x +server_port = 7000 +log_file = ./frpc.log +log_level = info + +# test需要和 frps.ini 中配置一致 +[test] +passwd = 123 +# 需要转发的本地端口 +local_port = 22 +``` From d9bca30c9bbf771927ea47dd0895804f4e67d2b1 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 14 Mar 2016 15:27:54 +0800 Subject: [PATCH 11/21] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 14d609d..59da553 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ frp is under development and you can try it with available version 0.1. ## Quick Start -Read the [QuickStart](doc/quick_start_en.md) [使用文档](doc/quick_start_zh.md) +Read the [QuickStart](doc/quick_start_en.md) | [使用文档](doc/quick_start_zh.md) ## Architecture From 9f5465b08da40a641a6e967e174143ad2046ef55 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 14 Mar 2016 15:29:43 +0800 Subject: [PATCH 12/21] Update quick_start_zh.md --- doc/quick_start_zh.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/quick_start_zh.md b/doc/quick_start_zh.md index 8bfec44..8aef382 100644 --- a/doc/quick_start_zh.md +++ b/doc/quick_start_zh.md @@ -6,7 +6,7 @@ frp 相比于其他项目而言非常易于部署和使用,这里我们用一 推荐直接使用 `go get github.com/fatedier/frp` 下载源代码安装,执行命令后代码将会拷贝到 `$GOPATH/src/github.com/fatedier/frp` 目录下。 -或者可以使用 `git clone https://github.com/fatedier/frp.git ./` 拷贝到 `$GOPATH/src` 下的任意目录。 +或者可以使用 `git clone https://github.com/fatedier/frp.git $GOPATH/src` 拷贝到 `$GOPATH/src/github.com/fatedier/frp` 目录下。 ### 编译 From a092af28a6555d1de57c522d162743c0ec2bfec3 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 14 Mar 2016 15:30:23 +0800 Subject: [PATCH 13/21] Update quick_start_zh.md --- doc/quick_start_zh.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/quick_start_zh.md b/doc/quick_start_zh.md index 8aef382..a2997af 100644 --- a/doc/quick_start_zh.md +++ b/doc/quick_start_zh.md @@ -26,7 +26,7 @@ frp 相比于其他项目而言非常易于部署和使用,这里我们用一 3. 修改两边的配置文件,见下一节说明。 4. 在服务器B执行 `nohup ./frps &` 或者 `nohup ./frps -c ./frps.ini &`。 5. 在服务器A执行 `nohup ./frpc &` 或者 `nohup ./frpc -c ./frpc.ini &`。 -6. 通过 `ssh -oPort=6000 {user}@x.x.x.x` 测试是否能够成功连接服务器A({user}替换为服务器B上存在的真实用户)。 +6. 通过 `ssh -oPort=6000 {user}@x.x.x.x` 测试是否能够成功连接服务器A({user}替换为服务器A上存在的真实用户)。 ### 配置文件 From 95ae70234d07881001c1c923fecb4556d397aee6 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 14 Mar 2016 15:42:29 +0800 Subject: [PATCH 14/21] Update quick_start_zh.md --- doc/quick_start_zh.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/quick_start_zh.md b/doc/quick_start_zh.md index a2997af..61fb3bd 100644 --- a/doc/quick_start_zh.md +++ b/doc/quick_start_zh.md @@ -6,7 +6,7 @@ frp 相比于其他项目而言非常易于部署和使用,这里我们用一 推荐直接使用 `go get github.com/fatedier/frp` 下载源代码安装,执行命令后代码将会拷贝到 `$GOPATH/src/github.com/fatedier/frp` 目录下。 -或者可以使用 `git clone https://github.com/fatedier/frp.git $GOPATH/src` 拷贝到 `$GOPATH/src/github.com/fatedier/frp` 目录下。 +或者可以使用 `git clone https://github.com/fatedier/frp.git $GOPATH/src/github.com/fatedier/frp` 拷贝到相应目录下。 ### 编译 From d1c3badce236d6d53f8e78a5c263eb82ebaf08e4 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 14 Mar 2016 16:11:02 +0800 Subject: [PATCH 15/21] doc: add quick_start_en.md --- doc/quick_start_en.md | 68 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 doc/quick_start_en.md diff --git a/doc/quick_start_en.md b/doc/quick_start_en.md new file mode 100644 index 0000000..46fa3c2 --- /dev/null +++ b/doc/quick_start_en.md @@ -0,0 +1,68 @@ +# Quick Start + +frp is easier to use compared with other similar projects. + +We will use a simple demo to demonstrate how to create a connection to server A's ssh port by server B with public IP address x.x.x.x(replace to the real IP address of your server). + +### Download SourceCode + +`go get github.com/fatedier/frp` is recommended, then the code will be copied to the directory `$GOPATH/src/github.com/fatedier/frp`. + +Or you can use `git clone https://github.com/fatedier/frp.git $GOPATH/src/github.com/fatedier/frp`. + +### Compile + +Enter the root directory and execute `make`, then wait until finished. + +**bin** include all executable programs when **conf** include corresponding configuration files. + +### Pre-requirement + +* Go environment. Version of go >= 1.4. +* Godep (if not exist, go get will be executed to download godep when compiling) + +### Deploy + +1. Move `./bin/frps` and `./conf/frps.ini` to any directory of server B. +2. Move `./bin/frpc` and `./conf/frpc.ini` to any directory of server A. +3. Modify all configuration files, details in next paragraph. +4. Execute `nohup ./frps &` or `nohup ./frps -c ./frps.ini &` in server B. +5. Execute `nohup ./frpc &` or `nohup ./frpc -c ./frpc.ini &` in server A. +6. Use `ssh -oPort=6000 {user}@x.x.x.x` to test if frp is work(replace {user} to real username in server A). + +### Configuration files + +#### frps.ini + +```ini +[common] +bind_addr = 0.0.0.0 +# for accept connections from frpc +bind_port = 7000 +log_file = ./frps.log +log_level = info + +# test is the custom name of proxy and there can be many proxies with unique name in one configure file +[test] +passwd = 123 +bind_addr = 0.0.0.0 +# finally we connect to server A by this port +listen_port = 6000 +``` + +#### frpc.ini + +```ini +[common] +# server address of frps +server_addr = x.x.x.x +server_port = 7000 +log_file = ./frpc.log +log_level = info + +# test is proxy name same with configure in frps.ini +[test] +passwd = 123 +# local port which need to be transferred +local_port = 22 +``` From a52e77f6ed56645cc1f773d256e9925c9dabc52f Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 14 Mar 2016 16:18:29 +0800 Subject: [PATCH 16/21] doc: update README.md for Contributing --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 59da553..28a1361 100644 --- a/README.md +++ b/README.md @@ -23,3 +23,11 @@ Read the [QuickStart](doc/quick_start_en.md) | [使用文档](doc/quick_start_zh * Expose any http service behind a NAT or firewall to the internet by a server with public IP address. * Expose any tcp service behind a NAT or firewall to the internet by a server with public IP address. * Inspect all http requests/responses that are transmitted over the tunnel(future). + +## Contributing + +Interested in getting involved? We would love to help you! + +For simple bug fixes, just submit a PR with the fix and we can discuss the fix directly in the PR. If the fix is more complex, start with an issue. + +If you have some wanderful ideas, send email to fatedier@gmail.com. From 8ed55e128895bccd5e60555ed77a3e090d9f6f8f Mon Sep 17 00:00:00 2001 From: fatedier Date: Tue, 15 Mar 2016 18:45:57 +0800 Subject: [PATCH 17/21] test: improve test case --- Makefile | 2 +- src/frp/utils/broadcast/broadcast_test.go | 4 ++-- src/frp/utils/pcrypto/pcrypto_test.go | 6 +++--- src/frp/utils/version/version_test.go | 10 +++++----- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 1488621..4dbf0d9 100644 --- a/Makefile +++ b/Makefile @@ -18,4 +18,4 @@ frpc: GOPATH=$(NEW_GOPATH) godep go build -o bin/frpc ./src/frp/cmd/frpc test: - @GOPATH=$(NEW_GOPATH) godep go test ./... + @GOPATH=$(NEW_GOPATH) godep go test -v ./... diff --git a/src/frp/utils/broadcast/broadcast_test.go b/src/frp/utils/broadcast/broadcast_test.go index b1314fe..57aa5a8 100644 --- a/src/frp/utils/broadcast/broadcast_test.go +++ b/src/frp/utils/broadcast/broadcast_test.go @@ -29,7 +29,7 @@ var ( func TestBroadcast(t *testing.T) { b := NewBroadcast() if b == nil { - t.Errorf("New Broadcast error, nil return") + t.Fatalf("New Broadcast error, nil return") } defer b.Close() @@ -45,7 +45,7 @@ func TestBroadcast(t *testing.T) { wait.Wait() if succNum != totalNum { - t.Errorf("TotalNum %d, FailNum(timeout) %d", totalNum, totalNum-succNum) + t.Fatalf("TotalNum %d, FailNum(timeout) %d", totalNum, totalNum-succNum) } } diff --git a/src/frp/utils/pcrypto/pcrypto_test.go b/src/frp/utils/pcrypto/pcrypto_test.go index ea88c63..db552d5 100644 --- a/src/frp/utils/pcrypto/pcrypto_test.go +++ b/src/frp/utils/pcrypto/pcrypto_test.go @@ -25,7 +25,7 @@ func TestEncrypto(t *testing.T) { pp.Init([]byte("Hana")) res, err := pp.Encrypto([]byte("Just One Test!")) if err != nil { - t.Error(err) + t.Fatalf(err) } fmt.Printf("[%x]\n", res) @@ -36,12 +36,12 @@ func TestDecrypto(t *testing.T) { pp.Init([]byte("Hana")) res, err := pp.Encrypto([]byte("Just One Test!")) if err != nil { - t.Error(err) + t.Fatalf(err) } res, err = pp.Decrypto(res) if err != nil { - t.Error(err) + t.Fatalf(err) } fmt.Printf("[%s]\n", string(res)) diff --git a/src/frp/utils/version/version_test.go b/src/frp/utils/version/version_test.go index 0906d8a..ed66186 100644 --- a/src/frp/utils/version/version_test.go +++ b/src/frp/utils/version/version_test.go @@ -25,22 +25,22 @@ func TestFull(t *testing.T) { version := Full() arr := strings.Split(version, ".") if len(arr) != 3 { - t.Errorf("Version string error: %s", version) + t.Fatalf("Version string error: %s", version) } proto, err := strconv.ParseInt(arr[0], 10, 64) if err != nil || proto < 0 { - t.Errorf("Version proto error") + t.Fatalf("Version proto error") } major, err := strconv.ParseInt(arr[1], 10, 64) if err != nil || major < 0 { - t.Errorf("Version major error") + t.Fatalf("Version major error") } minor, err := strconv.ParseInt(arr[2], 10, 64) if err != nil || minor < 0 { - t.Errorf("Version minor error") + t.Fatalf("Version minor error") } } @@ -51,6 +51,6 @@ func TestVersion(t *testing.T) { parseVerion := fmt.Sprintf("%d.%d.%d", proto, major, minor) version := Full() if parseVerion != version { - t.Errorf("Get version incorrect, version [%s], parseVerion [%s]", version, parseVerion) + t.Fatalf("Get version incorrect, version [%s], parseVerion [%s]", version, parseVerion) } } From 5c6f03afcf643acfe189a8241814e0c655acaf9d Mon Sep 17 00:00:00 2001 From: fatedier Date: Tue, 15 Mar 2016 18:50:49 +0800 Subject: [PATCH 18/21] test: typo --- src/frp/utils/pcrypto/pcrypto_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frp/utils/pcrypto/pcrypto_test.go b/src/frp/utils/pcrypto/pcrypto_test.go index db552d5..b360d05 100644 --- a/src/frp/utils/pcrypto/pcrypto_test.go +++ b/src/frp/utils/pcrypto/pcrypto_test.go @@ -25,7 +25,7 @@ func TestEncrypto(t *testing.T) { pp.Init([]byte("Hana")) res, err := pp.Encrypto([]byte("Just One Test!")) if err != nil { - t.Fatalf(err) + t.Fatal(err) } fmt.Printf("[%x]\n", res) @@ -36,12 +36,12 @@ func TestDecrypto(t *testing.T) { pp.Init([]byte("Hana")) res, err := pp.Encrypto([]byte("Just One Test!")) if err != nil { - t.Fatalf(err) + t.Fatal(err) } res, err = pp.Decrypto(res) if err != nil { - t.Fatalf(err) + t.Fatal(err) } fmt.Printf("[%s]\n", string(res)) From 30d79e66beb32aa9c93503ebf38cd8a860bd83c2 Mon Sep 17 00:00:00 2001 From: fatedier Date: Tue, 15 Mar 2016 18:53:27 +0800 Subject: [PATCH 19/21] test: typo --- src/frp/utils/pcrypto/pcrypto_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frp/utils/pcrypto/pcrypto_test.go b/src/frp/utils/pcrypto/pcrypto_test.go index b360d05..43e38f0 100644 --- a/src/frp/utils/pcrypto/pcrypto_test.go +++ b/src/frp/utils/pcrypto/pcrypto_test.go @@ -50,12 +50,12 @@ func TestDecrypto(t *testing.T) { func TestPKCS7Padding(t *testing.T) { ltt := []byte("Test_PKCS7Padding") ltt = PKCS7Padding(ltt, aes.BlockSize) - fmt.Printf("[%x]\n", (ltt)) + // fmt.Printf("[%x]\n", (ltt)) } func TestPKCS7UnPadding(t *testing.T) { ltt := []byte("Test_PKCS7Padding") ltt = PKCS7Padding(ltt, aes.BlockSize) ltt = PKCS7UnPadding(ltt) - fmt.Printf("[%x]\n", ltt) + // fmt.Printf("[%x]\n", ltt) } From cdd79aee524f3b50c5ede4003b3eeecd55c27cd9 Mon Sep 17 00:00:00 2001 From: fatedier Date: Wed, 16 Mar 2016 17:20:53 +0800 Subject: [PATCH 20/21] add Dockerfile --- Dockerfile | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bbfc6e1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM golang:1.5 + +MAINTAINER fatedier + +RUN echo "[common]\nbind_addr = 0.0.0.0\nbind_port = 7000\n[wiki]\npasswd = 123\nbind_addr = 0.0.0.0\nlisten_port = 80" > /usr/share/frps.ini + +ADD ./ /usr/share/frp/ + +RUN cd /usr/share/frp && make + +EXPOSE 80 +EXPOSE 7000 + +CMD ["/usr/share/frp/bin/frps -c /usr/share/frps.ini"] From f130886f69eb748d153c7de38f9b93f9ea4682e6 Mon Sep 17 00:00:00 2001 From: fatedier Date: Thu, 17 Mar 2016 10:25:23 +0800 Subject: [PATCH 21/21] all: change heartbeat interval time --- src/frp/cmd/frpc/control.go | 3 ++- src/frp/models/client/config.go | 2 +- src/frp/models/server/config.go | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/frp/cmd/frpc/control.go b/src/frp/cmd/frpc/control.go index bba7d0c..137be06 100644 --- a/src/frp/cmd/frpc/control.go +++ b/src/frp/cmd/frpc/control.go @@ -156,6 +156,7 @@ func startHeartBeat(c *conn.Conn) { for { time.Sleep(time.Duration(client.HeartBeatInterval) * time.Second) if c != nil && !c.IsClosed() { + log.Debug("Send heartbeat to server") err = c.Write(string(request) + "\n") if err != nil { log.Error("Send hearbeat to server failed! Err:%v", err) @@ -165,5 +166,5 @@ func startHeartBeat(c *conn.Conn) { break } } - log.Debug("Heartbeat exit") + log.Debug("Heartbeat goroutine exit") } diff --git a/src/frp/models/client/config.go b/src/frp/models/client/config.go index 363049c..e41fec8 100644 --- a/src/frp/models/client/config.go +++ b/src/frp/models/client/config.go @@ -29,7 +29,7 @@ var ( LogWay string = "console" LogLevel string = "info" HeartBeatInterval int64 = 20 - HeartBeatTimeout int64 = 60 + HeartBeatTimeout int64 = 90 ) var ProxyClients map[string]*ProxyClient = make(map[string]*ProxyClient) diff --git a/src/frp/models/server/config.go b/src/frp/models/server/config.go index 7bef192..b2eb63f 100644 --- a/src/frp/models/server/config.go +++ b/src/frp/models/server/config.go @@ -28,7 +28,7 @@ var ( LogFile string = "console" LogWay string = "console" // console or file LogLevel string = "info" - HeartBeatTimeout int64 = 30 + HeartBeatTimeout int64 = 90 UserConnTimeout int64 = 10 )