Merge pull request #7 from fatedier/dev

Release version 0.2.0
This commit is contained in:
fatedier 2016-03-17 10:45:04 +08:00
commit 51114f2afd
35 changed files with 3064 additions and 49 deletions

1
.gitignore vendored
View File

@ -28,4 +28,3 @@ bin/
# Cache
*.swp
*.swo

14
Dockerfile Normal file
View File

@ -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"]

5
Godeps/Godeps.json generated
View File

@ -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"

View File

@ -0,0 +1,25 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
# coverage droppings
profile.cov

View File

@ -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

View File

@ -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.

View File

@ -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 <name>...
naval_fate ship <name> move <x> <y> [--speed=<kn>]
naval_fate ship shoot <x> <y>
naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
naval_fate -h | --help
naval_fate --version
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> 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`.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
r"""usage: prog [NAME_-2]..."""
$ prog 10 20
{"NAME_-2": ["10", "20"]}
$ prog 10
{"NAME_-2": ["10"]}
$ prog
{"NAME_-2": []}

View File

@ -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 <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> 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=<files> Path to files
[dEfAuLt: /root]
"""
$ prog
{"--path": "/root"}
$ prog --path=home
{"--path": "home"}
r"""usage: prog [options]
options:
-a Add
-r Remote
-m <msg> 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 <msg>]
options:
-a Add
-r Remote
-m <msg> Message
"""
$ prog -armyourass
{"-a": true,
"-r": true,
"-m": "yourass"}
r"""usage: prog [-armmsg]
options: -a Add
-r Remote
-m <msg> 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 <arg>"""
$ prog 10
{"<arg>": "10"}
$ prog 10 20
"user-error"
$ prog
"user-error"
r"""usage: prog [<arg>]"""
$ prog 10
{"<arg>": "10"}
$ prog 10 20
"user-error"
$ prog
{"<arg>": null}
r"""usage: prog <kind> <name> <type>"""
$ prog 10 20 40
{"<kind>": "10", "<name>": "20", "<type>": "40"}
$ prog 10 20
"user-error"
$ prog
"user-error"
r"""usage: prog <kind> [<name> <type>]"""
$ prog 10 20 40
{"<kind>": "10", "<name>": "20", "<type>": "40"}
$ prog 10 20
{"<kind>": "10", "<name>": "20", "<type>": null}
$ prog
"user-error"
r"""usage: prog [<kind> | <name> <type>]"""
$ prog 10 20 40
"user-error"
$ prog 20 40
{"<kind>": null, "<name>": "20", "<type>": "40"}
$ prog
{"<kind>": null, "<name>": null, "<type>": null}
r"""usage: prog (<kind> --all | <name>)
options:
--all
"""
$ prog 10 --all
{"<kind>": "10", "--all": true, "<name>": null}
$ prog 10
{"<kind>": null, "--all": false, "<name>": "10"}
$ prog
"user-error"
r"""usage: prog [<name> <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
"user-error"
$ prog
{"<name>": []}
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 <name>...
prog ship [<name>] move <x> <y> [--speed=<kn>]
prog ship shoot <x> <y>
prog mine (set|remove) <x> <y> [--moored|--drifting]
prog -h | --help
prog --version
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> 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,
"<name>": ["Guardian"],
"<x>": "150",
"<y>": "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=<world>]"""
$ 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 <msg> Message
"""
$ prog -a
{"-m": null, "-a": true}
#
# Test options without description
#
r"""usage: prog --hello"""
$ prog --hello
{"--hello": true}
r"""usage: prog [--hello=<world>]"""
$ 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 <a> <b>
"""
$ prog 1 2
{"<a>": "1", "<b>": "2"}
$ prog
{"<a>": null, "<b>": null}
r"""usage: prog <a> <b>
prog
"""
$ prog
{"<a>": null, "<b>": null}
#
# Option's argument should not capture default value from usage pattern
#
r"""usage: prog [--file=<f>]"""
$ prog
{"--file": null}
r"""usage: prog [--file=<f>]
options: --file <a>
"""
$ prog
{"--file": null}
r"""Usage: prog [-a <host:port>]
Options: -a, --address <host:port> 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=<arg> ..."""
$ prog --long one
{"--long": ["one"]}
$ prog --long one --long two
{"--long": ["one", "two"]}
#
# Test multiple elements repeated at once
#
r"""usage: prog (go <direction> --speed=<km/h>)..."""
$ prog go left --speed=5 go right --speed=9
{"go": 2, "<direction>": ["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 <o>]...
options: -o <o> [default: x]
"""
$ prog -o this -o that
{"-o": ["this", "that"]}
$ prog
{"-o": ["x"]}
r"""usage: prog [-o <o>]...
options: -o <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 [<input file>]"""
$ prog f.txt
{"<input file>": "f.txt"}
r"""usage: prog [--input=<file name>]..."""
$ 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}

View File

@ -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
@ -19,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 ./...

View File

@ -2,4 +2,32 @@
[![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).
## 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.

View File

@ -1,14 +1,14 @@
# common是必须的section
# [common] is integral section
[common]
server_addr = 127.0.0.1
server_addr = 0.0.0.0
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
log_way = console
# test1即为name
# test1 is the proxy name same as server's configuration
[test1]
passwd = 123
local_ip = 127.0.0.1
local_port = 22

View File

@ -1,14 +1,13 @@
# common是必须的section
# [common] is integral section
[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 = console
# 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

BIN
doc/pic/architecture.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

68
doc/quick_start_en.md Normal file
View File

@ -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
```

66
doc/quick_start_zh.md Normal file
View File

@ -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/github.com/fatedier/frp` 拷贝到相应目录下。
### 编译
进入下载后的源码根目录,执行 `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}替换为服务器A上存在的真实用户
### 配置文件
#### 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
```

View File

@ -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 (
@ -142,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)
@ -151,5 +166,5 @@ func startHeartBeat(c *conn.Conn) {
break
}
}
log.Debug("Heartbeat exit")
log.Debug("Heartbeat goroutine exit")
}

View File

@ -1,19 +1,93 @@
// 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 (
"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=<log_level>] [--server-addr=<server_addr>]
frpc -h | --help | --version
Options:
-c config_file set config file
-L log_file set output log file, including console
--log-level=<log_level> set log level: debug, info, warn, error
--server-addr=<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

View File

@ -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 (

View File

@ -1,19 +1,93 @@
// 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 (
"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=<log_level>] [--addr=<bind_addr>]
frps -h | --help | --version
Options:
-c config_file set config file
-L log_file set output log file, including console
--log-level=<log_level> set log level: debug, info, warn, error
--addr=<bind_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)

View File

@ -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 (
@ -12,11 +26,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)
}

View File

@ -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 (
@ -11,11 +25,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 = 90
)
var ProxyClients map[string]*ProxyClient = make(map[string]*ProxyClient)
@ -43,6 +57,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,12 +69,7 @@ func LoadConf(confFile string) (err error) {
LogLevel = tmpStr
}
tmpStr, ok = conf.Get("common", "log_way")
if ok {
LogWay = tmpStr
}
// servers
// proxies
for name, section := range conf {
if name != "common" {
proxyClient := &ProxyClient{}
@ -66,6 +80,12 @@ 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 {
// use 127.0.0.1 as default
proxyClient.LocalIp = "127.0.0.1"
}
portStr, ok := section["local_port"]
if ok {
proxyClient.LocalPort, err = strconv.ParseInt(portStr, 10, 64)

View File

@ -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

View File

@ -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 {

View File

@ -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 (
@ -10,11 +24,11 @@ import (
// common config
var (
BindAddr string = "0.0.0.0"
BindPort int64 = 9527
LogFile string = "./frps.log"
LogLevel string = "warn"
LogWay string = "file"
HeartBeatTimeout int64 = 30
BindPort int64 = 7000
LogFile string = "console"
LogWay string = "console" // console or file
LogLevel string = "info"
HeartBeatTimeout int64 = 90
UserConnTimeout int64 = 10
)
@ -43,6 +57,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 +69,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" {

View File

@ -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 (
@ -128,7 +142,9 @@ func (p *ProxyServer) Start() (err error) {
func (p *ProxyServer) Close() {
p.Lock()
p.Status = consts.Idle
if p.listener != nil {
p.listener.Close()
}
close(p.ctlMsgChan)
close(p.cliConnChan)
p.userConnList = list.New()

View File

@ -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 {

View File

@ -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 (
@ -15,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()
@ -31,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)
}
}

View File

@ -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 (

View File

@ -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 (

View File

@ -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 (

View File

@ -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 (
@ -11,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.Fatal(err)
}
fmt.Printf("[%x]\n", res)
@ -22,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.Fatal(err)
}
res, err = pp.Decrypto(res)
if err != nil {
t.Error(err)
t.Fatal(err)
}
fmt.Printf("[%s]\n", string(res))
@ -36,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)
}

View File

@ -0,0 +1,58 @@
// 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 (
"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
}

View File

@ -0,0 +1,56 @@
// 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 (
"fmt"
"strconv"
"strings"
"testing"
)
func TestFull(t *testing.T) {
version := Full()
arr := strings.Split(version, ".")
if len(arr) != 3 {
t.Fatalf("Version string error: %s", version)
}
proto, err := strconv.ParseInt(arr[0], 10, 64)
if err != nil || proto < 0 {
t.Fatalf("Version proto error")
}
major, err := strconv.ParseInt(arr[1], 10, 64)
if err != nil || major < 0 {
t.Fatalf("Version major error")
}
minor, err := strconv.ParseInt(arr[2], 10, 64)
if err != nil || minor < 0 {
t.Fatalf("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.Fatalf("Get version incorrect, version [%s], parseVerion [%s]", version, parseVerion)
}
}