adding vendor folder

This commit is contained in:
evilsocket 2018-03-23 15:25:11 +01:00
parent 49c65021ea
commit c304ca4696
No known key found for this signature in database
GPG key ID: 1564D7F30393A456
1145 changed files with 369961 additions and 2 deletions

1
.gitignore vendored
View file

@ -4,7 +4,6 @@
pcaps pcaps
caplets caplets
build build
vendor
bettercap*.* bettercap*.*
bettercap* bettercap*
bettercap.history bettercap.history

View file

@ -18,7 +18,7 @@ Make sure you have a correctly configured **Go >= 1.8** environment, that `$GOPA
$ go get github.com/bettercap/bettercap $ go get github.com/bettercap/bettercap
This command will download bettercap, install its dependencies, compile it and move the `bettercap` executable to `$GOPATH/bin`. This command will download bettercap, install its dependencies, compile it and move the `bettercap` executable to `$GOPATH/bin`.
Now you can use `sudo bettercap -h` to show the basic command line options and just `sudo bettercap` to start an Now you can use `sudo bettercap -h` to show the basic command line options and just `sudo bettercap` to start an
[interactive session](https://github.com/bettercap/bettercap/wiki/Interactive-Mode) on your default network interface, otherwise you can [load a caplet](https://github.com/bettercap/bettercap/wiki/Caplets) from [the dedicated repository](https://github.com/bettercap/caplets). [interactive session](https://github.com/bettercap/bettercap/wiki/Interactive-Mode) on your default network interface, otherwise you can [load a caplet](https://github.com/bettercap/bettercap/wiki/Caplets) from [the dedicated repository](https://github.com/bettercap/caplets).

30
vendor/github.com/adrianmo/go-nmea/.travis.yml generated vendored Normal file
View file

@ -0,0 +1,30 @@
# Travis CI (http://travis-ci.org/) is a continuous integration
# service for open source projects. This file configures it
# to run unit tests for go-nmea.
language: go
go:
- 1.7
- tip
matrix:
fast_finish: true
before_install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/golang/lint/golint
- go get github.com/mattn/goveralls
- go get github.com/stretchr/testify/assert
install:
- go get -d -v ./... && go build -v ./...
script:
- go vet -x ./...
- $HOME/gopath/bin/golint -set_exit_status ./...
- go test -v ./...
- go test -covermode=count -coverprofile=profile.cov .
after_script:
- $HOME/gopath/bin/goveralls -coverprofile=profile.cov -service=travis-ci

21
vendor/github.com/adrianmo/go-nmea/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Adrian Moreno
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.

52
vendor/github.com/adrianmo/go-nmea/README.md generated vendored Normal file
View file

@ -0,0 +1,52 @@
# go-nmea [![Build Status](https://travis-ci.org/adrianmo/go-nmea.svg?branch=master)](https://travis-ci.org/adrianmo/go-nmea) [![Go Report Card](https://goreportcard.com/badge/github.com/adrianmo/go-nmea)](https://goreportcard.com/report/github.com/adrianmo/go-nmea) [![Coverage Status](https://coveralls.io/repos/adrianmo/go-nmea/badge.svg?branch=master&service=github)](https://coveralls.io/github/adrianmo/go-nmea?branch=master) [![GoDoc](https://godoc.org/github.com/adrianmo/go-nmea?status.svg)](https://godoc.org/github.com/adrianmo/go-nmea)
This is a NMEA library for the Go programming language (http://golang.org).
## Installing
### Using `go get`
go get github.com/adrianmo/go-nmea
After this command *go-nmea* is ready to use. Its source will be in:
$GOPATH/src/github.com/adrianmo/go-nmea
## Supported sentences
At this moment, this library supports the following sentence types:
- [GPRMC](http://aprs.gids.nl/nmea/#rmc) - Recommended Minimum Specific GPS/Transit data
- [GNRMC](http://aprs.gids.nl/nmea/#rmc) - Recommended Minimum Specific GNSS data
- [GPGGA](http://aprs.gids.nl/nmea/#gga) - GPS Positioning System Fix Data
- [GNGGA](http://aprs.gids.nl/nmea/#gga) - GNSS Positioning System Fix Data
- [GPGSA](http://aprs.gids.nl/nmea/#gsa) - GPS DOP and active satellites
- [GPGSV](http://aprs.gids.nl/nmea/#gsv) - GPS Satellites in view
- [GLGSV](http://aprs.gids.nl/nmea/#gsv) - GLONASS Satellites in view
- [GPGLL](http://aprs.gids.nl/nmea/#gll) - Geographic Position, Latitude / Longitude and time
- [GPVTG](http://aprs.gids.nl/nmea/#vtg) - Track Made Good and Ground Speed
- [GPZDA](http://aprs.gids.nl/nmea/#zda) - Date & time data
- [PGRME](http://aprs.gids.nl/nmea/#rme) - Estimated Position Error (Garmin proprietary sentence)
## Example
```go
package main
import (
"fmt"
"github.com/adrianmo/go-nmea"
)
func main() {
m, err := nmea.Parse("$GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70")
if err == nil {
fmt.Printf("%+v\n", m)
}
}
```
## Contributions
Please, feel free to implement support for new sentences, fix bugs, refactor code, etc. and send a pull-request to update the library.

47
vendor/github.com/adrianmo/go-nmea/glgsv.go generated vendored Normal file
View file

@ -0,0 +1,47 @@
package nmea
const (
// PrefixGLGSV prefix
PrefixGLGSV = "GLGSV"
)
// GLGSV represents the GPS Satellites in view
// http://aprs.gids.nl/nmea/#glgsv
type GLGSV struct {
Sent
TotalMessages int64 // Total number of messages of this type in this cycle
MessageNumber int64 // Message number
NumberSVsInView int64 // Total number of SVs in view
Info []GLGSVInfo // visible satellite info (0-4 of these)
}
// GLGSVInfo represents information about a visible satellite
type GLGSVInfo struct {
SVPRNNumber int64 // SV PRN number, pseudo-random noise or gold code
Elevation int64 // Elevation in degrees, 90 maximum
Azimuth int64 // Azimuth, degrees from true north, 000 to 359
SNR int64 // SNR, 00-99 dB (null when not tracking)
}
// NewGLGSV constructor
func NewGLGSV(s Sent) (GLGSV, error) {
p := newParser(s, PrefixGLGSV)
m := GLGSV{
Sent: s,
TotalMessages: p.Int64(0, "total number of messages"),
MessageNumber: p.Int64(1, "message number"),
NumberSVsInView: p.Int64(2, "number of SVs in view"),
}
for i := 0; i < 4; i++ {
if 5*i+4 > len(m.Fields) {
break
}
m.Info = append(m.Info, GLGSVInfo{
SVPRNNumber: p.Int64(3+i*4, "SV prn number"),
Elevation: p.Int64(4+i*4, "elevation"),
Azimuth: p.Int64(5+i*4, "azimuth"),
SNR: p.Int64(6+i*4, "SNR"),
})
}
return m, p.Err()
}

96
vendor/github.com/adrianmo/go-nmea/glgsv_test.go generated vendored Normal file
View file

@ -0,0 +1,96 @@
package nmea
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGLGSVGoodSentence(t *testing.T) {
goodMsg := "$GLGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*6B"
s, err := Parse(goodMsg)
assert.NoError(t, err, "Unexpected error parsing good sentence")
assert.Equal(t, PrefixGLGSV, s.Prefix(), "Prefix does not match")
sentence := s.(GLGSV)
assert.Equal(t, int64(3), sentence.TotalMessages, "Total messages does not match")
assert.Equal(t, int64(1), sentence.MessageNumber, "Message number does not match")
assert.Equal(t, int64(11), sentence.NumberSVsInView, "Number of SVs in view does not match")
assert.Equal(t, int64(3), sentence.Info[0].SVPRNNumber, "Number of Info[0] SV PRN does not match")
assert.Equal(t, int64(3), sentence.Info[0].Elevation, "Number of Info[0] Elevation does not match")
assert.Equal(t, int64(111), sentence.Info[0].Azimuth, "Number of Info[0] Azimuth does not match")
assert.Equal(t, int64(0), sentence.Info[0].SNR, "Number of Info[0] SNR does not match")
assert.Equal(t, int64(4), sentence.Info[1].SVPRNNumber, "Number of Info[1] SV PRN does not match")
assert.Equal(t, int64(15), sentence.Info[1].Elevation, "Number of Info[1] Elevation does not match")
assert.Equal(t, int64(270), sentence.Info[1].Azimuth, "Number of Info[1] Azimuth does not match")
assert.Equal(t, int64(0), sentence.Info[1].SNR, "Number of Info[1] SNR does not match")
assert.Equal(t, int64(6), sentence.Info[2].SVPRNNumber, "Number of Info[2] SV PRN does not match")
assert.Equal(t, int64(1), sentence.Info[2].Elevation, "Number of Info[2] Elevation does not match")
assert.Equal(t, int64(10), sentence.Info[2].Azimuth, "Number of Info[2] Azimuth does not match")
assert.Equal(t, int64(12), sentence.Info[2].SNR, "Number of Info[2] SNR does not match")
assert.Equal(t, int64(13), sentence.Info[3].SVPRNNumber, "Number of Info[3] SV PRN does not match")
assert.Equal(t, int64(6), sentence.Info[3].Elevation, "Number of Info[3] Elevation does not match")
assert.Equal(t, int64(292), sentence.Info[3].Azimuth, "Number of Info[3] Azimuth does not match")
assert.Equal(t, int64(0), sentence.Info[3].SNR, "Number of Info[3] SNR does not match")
}
func TestGLGSVShort(t *testing.T) {
goodMsg := "$GLGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,12*56"
s, err := Parse(goodMsg)
assert.NoError(t, err, "Unexpected error parsing good sentence")
assert.Equal(t, PrefixGLGSV, s.Prefix(), "Prefix does not match")
sentence := s.(GLGSV)
assert.Equal(t, int64(3), sentence.TotalMessages, "Total messages does not match")
assert.Equal(t, int64(1), sentence.MessageNumber, "Message number does not match")
assert.Equal(t, int64(11), sentence.NumberSVsInView, "Number of SVs in view does not match")
assert.Equal(t, int64(3), sentence.Info[0].SVPRNNumber, "Number of Info[0] SV PRN does not match")
assert.Equal(t, int64(3), sentence.Info[0].Elevation, "Number of Info[0] Elevation does not match")
assert.Equal(t, int64(111), sentence.Info[0].Azimuth, "Number of Info[0] Azimuth does not match")
assert.Equal(t, int64(0), sentence.Info[0].SNR, "Number of Info[0] SNR does not match")
assert.Equal(t, int64(4), sentence.Info[1].SVPRNNumber, "Number of Info[1] SV PRN does not match")
assert.Equal(t, int64(15), sentence.Info[1].Elevation, "Number of Info[1] Elevation does not match")
assert.Equal(t, int64(270), sentence.Info[1].Azimuth, "Number of Info[1] Azimuth does not match")
assert.Equal(t, int64(0), sentence.Info[1].SNR, "Number of Info[1] SNR does not match")
assert.Equal(t, int64(6), sentence.Info[2].SVPRNNumber, "Number of Info[2] SV PRN does not match")
assert.Equal(t, int64(1), sentence.Info[2].Elevation, "Number of Info[2] Elevation does not match")
assert.Equal(t, int64(10), sentence.Info[2].Azimuth, "Number of Info[2] Azimuth does not match")
assert.Equal(t, int64(12), sentence.Info[2].SNR, "Number of Info[2] SNR does not match")
}
func TestGLGSVBadSentence(t *testing.T) {
tests := []struct {
Input string
Error string
}{
{"$GLGSV,3,1,11.2,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*77", "nmea: GLGSV invalid number of SVs in view: 11.2"},
{"$GLGSV,A3,1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*2A", "nmea: GLGSV invalid total number of messages: A3"},
{"$GLGSV,3,A1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*2A", "nmea: GLGSV invalid message number: A1"},
{"$GLGSV,3,1,11,A03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*2A", "nmea: GLGSV invalid SV prn number: A03"},
{"$GLGSV,3,1,11,03,A03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*2A", "nmea: GLGSV invalid elevation: A03"},
{"$GLGSV,3,1,11,03,03,A111,00,04,15,270,00,06,01,010,12,13,06,292,00*2A", "nmea: GLGSV invalid azimuth: A111"},
{"$GLGSV,3,1,11,03,03,111,A00,04,15,270,00,06,01,010,12,13,06,292,00*2A", "nmea: GLGSV invalid SNR: A00"},
}
for _, tc := range tests {
_, err := Parse(tc.Input)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, tc.Error, err.Error(), "Incorrect error message")
}
}
func TestGLGSVWrongSentence(t *testing.T) {
wrongMsg := "$GPXTE,A,A,4.07,L,N*6D"
sent, _ := ParseSentence(wrongMsg)
_, err := NewGLGSV(sent)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: GLGSV invalid prefix: GPXTE", err.Error(), "Incorrect error message")
}

39
vendor/github.com/adrianmo/go-nmea/gngga.go generated vendored Normal file
View file

@ -0,0 +1,39 @@
package nmea
const (
// PrefixGNGGA prefix
PrefixGNGGA = "GNGGA"
)
// GNGGA is the Time, position, and fix related data of the receiver.
type GNGGA struct {
Sent
Time Time // Time of fix.
Latitude LatLong // Latitude.
Longitude LatLong // Longitude.
FixQuality string // Quality of fix.
NumSatellites int64 // Number of satellites in use.
HDOP float64 // Horizontal dilution of precision.
Altitude float64 // Altitude.
Separation float64 // Geoidal separation
DGPSAge string // Age of differential GPD data.
DGPSId string // DGPS reference station ID.
}
// NewGNGGA constructor
func NewGNGGA(s Sent) (GNGGA, error) {
p := newParser(s, PrefixGNGGA)
return GNGGA{
Sent: s,
Time: p.Time(0, "time"),
Latitude: p.LatLong(1, 2, "latitude"),
Longitude: p.LatLong(3, 4, "longitude"),
FixQuality: p.EnumString(5, "fix quality", Invalid, GPS, DGPS),
NumSatellites: p.Int64(6, "number of satelites"),
HDOP: p.Float64(7, "hdop"),
Altitude: p.Float64(8, "altitude"),
Separation: p.Float64(10, "separation"),
DGPSAge: p.String(12, "dgps age"),
DGPSId: p.String(13, "dgps id"),
}, p.Err()
}

71
vendor/github.com/adrianmo/go-nmea/gngga_test.go generated vendored Normal file
View file

@ -0,0 +1,71 @@
package nmea
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGNGGAGoodSentence(t *testing.T) {
goodMsg := "$GNGGA,203415.000,6325.6138,N,01021.4290,E,1,8,2.42,72.5,M,41.5,M,,*7C"
sentence, err := Parse(goodMsg)
assert.NoError(t, err, "Unexpected error parsing good sentence")
lat, _ := ParseLatLong("6325.6138 N")
lon, _ := ParseLatLong("01021.4290 E")
// Attributes of the parsed sentence, and their expected values.
expected := GNGGA{
Sent: Sent{
Type: "GNGGA",
Fields: []string{"203415.000", "6325.6138", "N", "01021.4290", "E", "1", "8", "2.42", "72.5", "M", "41.5", "M", "", ""},
Checksum: "7C",
Raw: "$GNGGA,203415.000,6325.6138,N,01021.4290,E,1,8,2.42,72.5,M,41.5,M,,*7C",
},
Time: Time{true, 20, 34, 15, 0},
Latitude: lat,
Longitude: lon,
FixQuality: GPS,
NumSatellites: 8,
HDOP: 2.42,
Altitude: 72.5,
Separation: 41.5,
DGPSAge: "",
DGPSId: "",
}
assert.EqualValues(t, expected, sentence, "Sentence values do not match")
}
func TestGNGGABadType(t *testing.T) {
badType := "$GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70"
s, err := Parse(badType)
assert.NoError(t, err, "Unexpected error parsing sentence")
assert.NotEqual(t, "GNGGA", s.Prefix(), "Unexpected sentence type")
}
func TestGNGGABadLatitude(t *testing.T) {
badLat := "$GNGGA,034225.077,A,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*24"
_, err := Parse(badLat)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: GNGGA invalid latitude: cannot parse [A S], unknown format", err.Error(), "Error message does not match")
}
func TestGNGGABadLongitude(t *testing.T) {
badLon := "$GNGGA,034225.077,3356.4650,S,A,E,1,03,9.7,-25.0,M,21.0,M,,0000*12"
_, err := Parse(badLon)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: GNGGA invalid longitude: cannot parse [A E], unknown format", err.Error(), "Error message does not match")
}
func TestGNGGABadFixQuality(t *testing.T) {
// Make sure bad fix mode is detected.
badMode := "$GNGGA,034225.077,3356.4650,S,15124.5567,E,5,03,9.7,-25.0,M,21.0,M,,0000*4B"
_, err := Parse(badMode)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, err.Error(), "nmea: GNGGA invalid fix quality: 5", "Error message not as expected")
}

40
vendor/github.com/adrianmo/go-nmea/gnrmc.go generated vendored Normal file
View file

@ -0,0 +1,40 @@
package nmea
const (
// PrefixGNRMC prefix of GNRMC sentence type
PrefixGNRMC = "GNRMC"
)
// GNRMC is the Recommended Minimum Specific GNSS data.
// http://aprs.gids.nl/nmea/#rmc
type GNRMC struct {
Sent
Time Time // Time Stamp
Validity string // validity - A-ok, V-invalid
Latitude LatLong // Latitude
Longitude LatLong // Longitude
Speed float64 // Speed in knots
Course float64 // True course
Date Date // Date
Variation float64 // Magnetic variation
}
// NewGNRMC constructor
func NewGNRMC(s Sent) (GNRMC, error) {
p := newParser(s, PrefixGNRMC)
m := GNRMC{
Sent: s,
Time: p.Time(0, "time"),
Validity: p.EnumString(1, "validity", ValidRMC, InvalidRMC),
Latitude: p.LatLong(2, 3, "latitude"),
Longitude: p.LatLong(4, 5, "longitude"),
Speed: p.Float64(6, "speed"),
Course: p.Float64(7, "course"),
Date: p.Date(8, "date"),
Variation: p.Float64(9, "variation"),
}
if p.EnumString(10, "direction", West, East) == West {
m.Variation = 0 - m.Variation
}
return m, p.Err()
}

78
vendor/github.com/adrianmo/go-nmea/gnrmc_test.go generated vendored Normal file
View file

@ -0,0 +1,78 @@
package nmea
import (
"testing"
"github.com/stretchr/testify/assert"
)
var gnrmctests = []struct {
Input string
Output GNRMC
}{
{
"$GNRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*6E",
GNRMC{
Time: Time{true, 22, 05, 16, 0},
Validity: "A",
Speed: 173.8,
Course: 231.8,
Date: Date{true, 13, 06, 94},
Variation: -4.2,
Latitude: MustParseGPS("5133.82 N"),
Longitude: MustParseGPS("00042.24 W"),
},
},
{
"$GNRMC,142754.0,A,4302.539570,N,07920.379823,W,0.0,,070617,0.0,E,A*21",
GNRMC{
Time: Time{true, 14, 27, 54, 0},
Validity: "A",
Speed: 0,
Course: 0,
Date: Date{true, 7, 6, 17},
Variation: 0,
Latitude: MustParseGPS("4302.539570 N"),
Longitude: MustParseGPS("07920.379823 W"),
},
},
}
func TestGNRMCGoodSentence(t *testing.T) {
for _, tt := range gnrmctests {
s, err := Parse(tt.Input)
assert.NoError(t, err, "Unexpected error parsing good sentence")
assert.Equal(t, PrefixGNRMC, s.Prefix(), "Prefix does not match")
sentence := s.(GNRMC)
assert.Equal(t, tt.Output.Time, sentence.Time, "Time does not match")
assert.Equal(t, tt.Output.Validity, sentence.Validity, "Status does not match")
assert.Equal(t, tt.Output.Speed, sentence.Speed, "Speed does not match")
assert.Equal(t, tt.Output.Course, sentence.Course, "Course does not match")
assert.Equal(t, tt.Output.Date, sentence.Date, "Date does not match")
assert.Equal(t, tt.Output.Variation, sentence.Variation, "Variation does not match")
assert.Equal(t, tt.Output.Latitude, sentence.Latitude, "Latitude does not match")
assert.Equal(t, tt.Output.Longitude, sentence.Longitude, "Longitude does not match")
}
}
func TestGNRMCBadSentence(t *testing.T) {
badMsg := "$GNRMC,220516,D,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*6B"
_, err := Parse(badMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: GNRMC invalid validity: D", err.Error(), "Incorrect error message")
}
func TestGNRMCWrongSentence(t *testing.T) {
wrongMsg := "$GPXTE,A,A,4.07,L,N*6D"
_, err := Parse(wrongMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: sentence type 'GPXTE' not implemented", err.Error(), "Incorrect error message")
}

47
vendor/github.com/adrianmo/go-nmea/gpgga.go generated vendored Normal file
View file

@ -0,0 +1,47 @@
package nmea
const (
// PrefixGPGGA prefix
PrefixGPGGA = "GPGGA"
// Invalid fix quality.
Invalid = "0"
// GPS fix quality
GPS = "1"
// DGPS fix quality
DGPS = "2"
)
// GPGGA represents fix data.
// http://aprs.gids.nl/nmea/#gga
type GPGGA struct {
Sent
Time Time // Time of fix.
Latitude LatLong // Latitude.
Longitude LatLong // Longitude.
FixQuality string // Quality of fix.
NumSatellites int64 // Number of satellites in use.
HDOP float64 // Horizontal dilution of precision.
Altitude float64 // Altitude.
Separation float64 // Geoidal separation
DGPSAge string // Age of differential GPD data.
DGPSId string // DGPS reference station ID.
}
// NewGPGGA parses the GPGGA sentence into this struct.
// e.g: $GPGGA,034225.077,3356.4650,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*58
func NewGPGGA(s Sent) (GPGGA, error) {
p := newParser(s, PrefixGPGGA)
return GPGGA{
Sent: s,
Time: p.Time(0, "time"),
Latitude: p.LatLong(1, 2, "latitude"),
Longitude: p.LatLong(3, 4, "longitude"),
FixQuality: p.EnumString(5, "fix quality", Invalid, GPS, DGPS),
NumSatellites: p.Int64(6, "number of satelites"),
HDOP: p.Float64(7, "hdap"),
Altitude: p.Float64(8, "altitude"),
Separation: p.Float64(10, "separation"),
DGPSAge: p.String(12, "dgps age"),
DGPSId: p.String(13, "dgps id"),
}, p.Err()
}

71
vendor/github.com/adrianmo/go-nmea/gpgga_test.go generated vendored Normal file
View file

@ -0,0 +1,71 @@
package nmea
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGPGGAGoodSentence(t *testing.T) {
goodMsg := "$GPGGA,034225.077,3356.4650,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*51"
sentence, err := Parse(goodMsg)
assert.NoError(t, err, "Unexpected error parsing good sentence")
lat, _ := ParseLatLong("3356.4650 S")
lon, _ := ParseLatLong("15124.5567 E")
// Attributes of the parsed sentence, and their expected values.
expected := GPGGA{
Sent: Sent{
Type: "GPGGA",
Fields: []string{"034225.077", "3356.4650", "S", "15124.5567", "E", "1", "03", "9.7", "-25.0", "M", "21.0", "M", "", "0000"},
Checksum: "51",
Raw: "$GPGGA,034225.077,3356.4650,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*51",
},
Time: Time{true, 3, 42, 25, 77},
Latitude: lat,
Longitude: lon,
FixQuality: GPS,
NumSatellites: 03,
HDOP: 9.7,
Altitude: -25.0,
Separation: 21.0,
DGPSAge: "",
DGPSId: "0000",
}
assert.EqualValues(t, expected, sentence, "Sentence values do not match")
}
func TestGPGGABadType(t *testing.T) {
badType := "$GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70"
s, err := Parse(badType)
assert.NoError(t, err, "Unexpected error parsing sentence")
assert.NotEqual(t, "GPGGA", s.Prefix(), "Unexpected sentence type")
}
func TestGPGGABadLatitude(t *testing.T) {
badLat := "$GPGGA,034225.077,A,S,15124.5567,E,1,03,9.7,-25.0,M,21.0,M,,0000*3A"
_, err := Parse(badLat)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: GPGGA invalid latitude: cannot parse [A S], unknown format", err.Error(), "Error message does not match")
}
func TestGPGGABadLongitude(t *testing.T) {
badLon := "$GPGGA,034225.077,3356.4650,S,A,E,1,03,9.7,-25.0,M,21.0,M,,0000*0C"
_, err := Parse(badLon)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: GPGGA invalid longitude: cannot parse [A E], unknown format", err.Error(), "Error message does not match")
}
func TestGPGGABadFixQuality(t *testing.T) {
// Make sure bad fix mode is detected.
badMode := "$GPGGA,034225.077,3356.4650,S,15124.5567,E,5,03,9.7,-25.0,M,21.0,M,,0000*55"
_, err := Parse(badMode)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, err.Error(), "nmea: GPGGA invalid fix quality: 5", "Error message not as expected")
}

32
vendor/github.com/adrianmo/go-nmea/gpgll.go generated vendored Normal file
View file

@ -0,0 +1,32 @@
package nmea
const (
// PrefixGPGLL prefix for GPGLL sentence type
PrefixGPGLL = "GPGLL"
// ValidGLL character
ValidGLL = "A"
// InvalidGLL character
InvalidGLL = "V"
)
// GPGLL is Geographic Position, Latitude / Longitude and time.
// http://aprs.gids.nl/nmea/#gll
type GPGLL struct {
Sent
Latitude LatLong // Latitude
Longitude LatLong // Longitude
Time Time // Time Stamp
Validity string // validity - A-valid
}
// NewGPGLL constructor
func NewGPGLL(s Sent) (GPGLL, error) {
p := newParser(s, PrefixGPGLL)
return GPGLL{
Sent: s,
Latitude: p.LatLong(0, 1, "latitude"),
Longitude: p.LatLong(2, 3, "longitude"),
Time: p.Time(4, "time"),
Validity: p.EnumString(5, "validity", ValidGLL, InvalidGLL),
}, p.Err()
}

38
vendor/github.com/adrianmo/go-nmea/gpgll_test.go generated vendored Normal file
View file

@ -0,0 +1,38 @@
package nmea
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGPGLLGoodSentence(t *testing.T) {
goodMsg := "$GPGLL,3926.7952,N,12000.5947,W,022732,A,A*58"
s, err := Parse(goodMsg)
assert.NoError(t, err, "Unexpected error parsing good sentence")
assert.Equal(t, PrefixGPGLL, s.Prefix(), "Prefix does not match")
sentence := s.(GPGLL)
assert.Equal(t, "3926.7952", sentence.Latitude.PrintGPS(), "Latitude does not match")
assert.Equal(t, "12000.5947", sentence.Longitude.PrintGPS(), "Longitude does not match")
assert.Equal(t, Time{true, 2, 27, 32, 0}, sentence.Time, "Time does not match")
assert.Equal(t, "A", sentence.Validity, "Status does not match")
}
func TestGPGLLBadSentence(t *testing.T) {
badMsg := "$GPGLL,3926.7952,N,12000.5947,W,022732,D,A*5D"
_, err := Parse(badMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: GPGLL invalid validity: D", err.Error(), "Incorrect error message")
}
func TestGPGLLWrongSentence(t *testing.T) {
wrongMsg := "$GPXTE,A,A,4.07,L,N*6D"
_, err := Parse(wrongMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: sentence type 'GPXTE' not implemented", err.Error(), "Incorrect error message")
}

49
vendor/github.com/adrianmo/go-nmea/gpgsa.go generated vendored Normal file
View file

@ -0,0 +1,49 @@
package nmea
const (
// PrefixGPGSA prefix of GPGSA sentence type
PrefixGPGSA = "GPGSA"
// Auto - Field 1, auto or manual fix.
Auto = "A"
// Manual - Field 1, auto or manual fix.
Manual = "M"
// FixNone - Field 2, fix type.
FixNone = "1"
// Fix2D - Field 2, fix type.
Fix2D = "2"
// Fix3D - Field 2, fix type.
Fix3D = "3"
)
// GPGSA represents overview satellite data.
// http://aprs.gids.nl/nmea/#gsa
type GPGSA struct {
Sent
Mode string // The selection mode.
FixType string // The fix type.
SV []string // List of satellite PRNs used for this fix.
PDOP float64 // Dilution of precision.
HDOP float64 // Horizontal dilution of precision.
VDOP float64 // Vertical dilution of precision.
}
// NewGPGSA parses the GPGSA sentence into this struct.
func NewGPGSA(s Sent) (GPGSA, error) {
p := newParser(s, PrefixGPGSA)
m := GPGSA{
Sent: s,
Mode: p.EnumString(0, "selection mode", Auto, Manual),
FixType: p.EnumString(1, "fix type", FixNone, Fix2D, Fix3D),
}
// Satellites in view.
for i := 2; i < 14; i++ {
if v := p.String(i, "satelite in view"); v != "" {
m.SV = append(m.SV, v)
}
}
// Dilution of precision.
m.PDOP = p.Float64(14, "pdop")
m.HDOP = p.Float64(15, "hdop")
m.VDOP = p.Float64(16, "vdop")
return m, p.Err()
}

50
vendor/github.com/adrianmo/go-nmea/gpgsa_test.go generated vendored Normal file
View file

@ -0,0 +1,50 @@
package nmea
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGPGSAGoodSentence(t *testing.T) {
goodMsg := "$GPGSA,A,3,22,19,18,27,14,03,,,,,,,3.1,2.0,2.4*36"
sentence, err := Parse(goodMsg)
assert.NoError(t, err, "Unexpected error parsing good sentence")
// Attributes of the parsed sentence, and their expected values.
expected := GPGSA{
Sent: Sent{
Type: "GPGSA",
Fields: []string{"A", "3", "22", "19", "18", "27", "14", "03", "", "", "", "", "", "", "3.1", "2.0", "2.4"},
Checksum: "36",
Raw: "$GPGSA,A,3,22,19,18,27,14,03,,,,,,,3.1,2.0,2.4*36",
},
Mode: Auto,
FixType: Fix3D,
PDOP: 3.1,
HDOP: 2.0,
VDOP: 2.4,
SV: []string{"22", "19", "18", "27", "14", "03"},
}
assert.EqualValues(t, expected, sentence, "Sentence values do not match")
}
func TestGPGSABadMode(t *testing.T) {
// Make sure bad fix mode is detected.
badMode := "$GPGSA,F,3,22,19,18,27,14,03,,,,,,,3.1,2.0,2.4*31"
_, err := Parse(badMode)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: GPGSA invalid selection mode: F", err.Error(), "Error message does not match")
}
func TestGPGSABadFix(t *testing.T) {
// Make sure bad fix type is detected.
badFixType := "$GPGSA,A,6,22,19,18,27,14,03,,,,,,,3.1,2.0,2.4*33"
_, err := Parse(badFixType)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: GPGSA invalid fix type: 6", err.Error(), "Error message does not match")
}

47
vendor/github.com/adrianmo/go-nmea/gpgsv.go generated vendored Normal file
View file

@ -0,0 +1,47 @@
package nmea
const (
// PrefixGPGSV prefix
PrefixGPGSV = "GPGSV"
)
// GPGSV represents the GPS Satellites in view
// http://aprs.gids.nl/nmea/#gpgsv
type GPGSV struct {
Sent
TotalMessages int64 // Total number of messages of this type in this cycle
MessageNumber int64 // Message number
NumberSVsInView int64 // Total number of SVs in view
Info []GPGSVInfo // visible satellite info (0-4 of these)
}
// GPGSVInfo represents information about a visible satellite
type GPGSVInfo struct {
SVPRNNumber int64 // SV PRN number, pseudo-random noise or gold code
Elevation int64 // Elevation in degrees, 90 maximum
Azimuth int64 // Azimuth, degrees from true north, 000 to 359
SNR int64 // SNR, 00-99 dB (null when not tracking)
}
// NewGPGSV constructor
func NewGPGSV(s Sent) (GPGSV, error) {
p := newParser(s, PrefixGPGSV)
m := GPGSV{
Sent: s,
TotalMessages: p.Int64(0, "total number of messages"),
MessageNumber: p.Int64(1, "message number"),
NumberSVsInView: p.Int64(2, "number of SVs in view"),
}
for i := 0; i < 4; i++ {
if 5*i+4 > len(m.Fields) {
break
}
m.Info = append(m.Info, GPGSVInfo{
SVPRNNumber: p.Int64(3+i*4, "SV prn number"),
Elevation: p.Int64(4+i*4, "elevation"),
Azimuth: p.Int64(5+i*4, "azimuth"),
SNR: p.Int64(6+i*4, "SNR"),
})
}
return m, p.Err()
}

96
vendor/github.com/adrianmo/go-nmea/gpgsv_test.go generated vendored Normal file
View file

@ -0,0 +1,96 @@
package nmea
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGPGSVGoodSentence(t *testing.T) {
goodMsg := "$GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*77"
s, err := Parse(goodMsg)
assert.NoError(t, err, "Unexpected error parsing good sentence")
assert.Equal(t, PrefixGPGSV, s.Prefix(), "Prefix does not match")
sentence := s.(GPGSV)
assert.Equal(t, int64(3), sentence.TotalMessages, "Total messages does not match")
assert.Equal(t, int64(1), sentence.MessageNumber, "Message number does not match")
assert.Equal(t, int64(11), sentence.NumberSVsInView, "Number of SVs in view does not match")
assert.Equal(t, int64(3), sentence.Info[0].SVPRNNumber, "Number of Info[0] SV PRN does not match")
assert.Equal(t, int64(3), sentence.Info[0].Elevation, "Number of Info[0] Elevation does not match")
assert.Equal(t, int64(111), sentence.Info[0].Azimuth, "Number of Info[0] Azimuth does not match")
assert.Equal(t, int64(0), sentence.Info[0].SNR, "Number of Info[0] SNR does not match")
assert.Equal(t, int64(4), sentence.Info[1].SVPRNNumber, "Number of Info[1] SV PRN does not match")
assert.Equal(t, int64(15), sentence.Info[1].Elevation, "Number of Info[1] Elevation does not match")
assert.Equal(t, int64(270), sentence.Info[1].Azimuth, "Number of Info[1] Azimuth does not match")
assert.Equal(t, int64(0), sentence.Info[1].SNR, "Number of Info[1] SNR does not match")
assert.Equal(t, int64(6), sentence.Info[2].SVPRNNumber, "Number of Info[2] SV PRN does not match")
assert.Equal(t, int64(1), sentence.Info[2].Elevation, "Number of Info[2] Elevation does not match")
assert.Equal(t, int64(10), sentence.Info[2].Azimuth, "Number of Info[2] Azimuth does not match")
assert.Equal(t, int64(12), sentence.Info[2].SNR, "Number of Info[2] SNR does not match")
assert.Equal(t, int64(13), sentence.Info[3].SVPRNNumber, "Number of Info[3] SV PRN does not match")
assert.Equal(t, int64(6), sentence.Info[3].Elevation, "Number of Info[3] Elevation does not match")
assert.Equal(t, int64(292), sentence.Info[3].Azimuth, "Number of Info[3] Azimuth does not match")
assert.Equal(t, int64(0), sentence.Info[3].SNR, "Number of Info[3] SNR does not match")
}
func TestGPGSVShort(t *testing.T) {
goodMsg := "$GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,12*4A"
s, err := Parse(goodMsg)
assert.NoError(t, err, "Unexpected error parsing good sentence")
assert.Equal(t, PrefixGPGSV, s.Prefix(), "Prefix does not match")
sentence := s.(GPGSV)
assert.Equal(t, int64(3), sentence.TotalMessages, "Total messages does not match")
assert.Equal(t, int64(1), sentence.MessageNumber, "Message number does not match")
assert.Equal(t, int64(11), sentence.NumberSVsInView, "Number of SVs in view does not match")
assert.Equal(t, int64(3), sentence.Info[0].SVPRNNumber, "Number of Info[0] SV PRN does not match")
assert.Equal(t, int64(3), sentence.Info[0].Elevation, "Number of Info[0] Elevation does not match")
assert.Equal(t, int64(111), sentence.Info[0].Azimuth, "Number of Info[0] Azimuth does not match")
assert.Equal(t, int64(0), sentence.Info[0].SNR, "Number of Info[0] SNR does not match")
assert.Equal(t, int64(4), sentence.Info[1].SVPRNNumber, "Number of Info[1] SV PRN does not match")
assert.Equal(t, int64(15), sentence.Info[1].Elevation, "Number of Info[1] Elevation does not match")
assert.Equal(t, int64(270), sentence.Info[1].Azimuth, "Number of Info[1] Azimuth does not match")
assert.Equal(t, int64(0), sentence.Info[1].SNR, "Number of Info[1] SNR does not match")
assert.Equal(t, int64(6), sentence.Info[2].SVPRNNumber, "Number of Info[2] SV PRN does not match")
assert.Equal(t, int64(1), sentence.Info[2].Elevation, "Number of Info[2] Elevation does not match")
assert.Equal(t, int64(10), sentence.Info[2].Azimuth, "Number of Info[2] Azimuth does not match")
assert.Equal(t, int64(12), sentence.Info[2].SNR, "Number of Info[2] SNR does not match")
}
func TestGPGSVBadSentence(t *testing.T) {
tests := []struct {
Input string
Error string
}{
{"$GPGSV,3,1,11.2,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*6b", "nmea: GPGSV invalid number of SVs in view: 11.2"},
{"$GPGSV,A3,1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "nmea: GPGSV invalid total number of messages: A3"},
{"$GPGSV,3,A1,11,03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "nmea: GPGSV invalid message number: A1"},
{"$GPGSV,3,1,11,A03,03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "nmea: GPGSV invalid SV prn number: A03"},
{"$GPGSV,3,1,11,03,A03,111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "nmea: GPGSV invalid elevation: A03"},
{"$GPGSV,3,1,11,03,03,A111,00,04,15,270,00,06,01,010,12,13,06,292,00*36", "nmea: GPGSV invalid azimuth: A111"},
{"$GPGSV,3,1,11,03,03,111,A00,04,15,270,00,06,01,010,12,13,06,292,00*36", "nmea: GPGSV invalid SNR: A00"},
}
for _, tc := range tests {
_, err := Parse(tc.Input)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, tc.Error, err.Error(), "Incorrect error message")
}
}
func TestGPGSVWrongSentence(t *testing.T) {
wrongMsg := "$GPXTE,A,A,4.07,L,N*6D"
sent, _ := ParseSentence(wrongMsg)
_, err := NewGPGSV(sent)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: GPGSV invalid prefix: GPXTE", err.Error(), "Incorrect error message")
}

44
vendor/github.com/adrianmo/go-nmea/gprmc.go generated vendored Normal file
View file

@ -0,0 +1,44 @@
package nmea
const (
// PrefixGPRMC prefix of GPRMC sentence type
PrefixGPRMC = "GPRMC"
// ValidRMC character
ValidRMC = "A"
// InvalidRMC character
InvalidRMC = "V"
)
// GPRMC is the Recommended Minimum Specific GNSS data.
// http://aprs.gids.nl/nmea/#rmc
type GPRMC struct {
Sent
Time Time // Time Stamp
Validity string // validity - A-ok, V-invalid
Latitude LatLong // Latitude
Longitude LatLong // Longitude
Speed float64 // Speed in knots
Course float64 // True course
Date Date // Date
Variation float64 // Magnetic variation
}
// NewGPRMC constructor
func NewGPRMC(s Sent) (GPRMC, error) {
p := newParser(s, PrefixGPRMC)
m := GPRMC{
Sent: s,
Time: p.Time(0, "time"),
Validity: p.EnumString(1, "validity", ValidRMC, InvalidRMC),
Latitude: p.LatLong(2, 3, "latitude"),
Longitude: p.LatLong(4, 5, "longitude"),
Speed: p.Float64(6, "speed"),
Course: p.Float64(7, "course"),
Date: p.Date(8, "date"),
Variation: p.Float64(9, "variation"),
}
if p.EnumString(10, "variation", West, East) == West {
m.Variation = 0 - m.Variation
}
return m, p.Err()
}

86
vendor/github.com/adrianmo/go-nmea/gprmc_test.go generated vendored Normal file
View file

@ -0,0 +1,86 @@
package nmea
import (
"testing"
"github.com/stretchr/testify/assert"
)
func MustParseGPS(s string) LatLong {
ll, err := ParseGPS(s)
if err != nil {
panic(err)
}
return ll
}
var gprmctests = []struct {
Input string
Output GPRMC
}{
{
"$GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70",
GPRMC{
Time: Time{true, 22, 05, 16, 0},
Validity: "A",
Speed: 173.8,
Course: 231.8,
Date: Date{true, 13, 6, 94},
Variation: -4.2,
Latitude: MustParseGPS("5133.82 N"),
Longitude: MustParseGPS("00042.24 W"),
},
},
{
"$GPRMC,142754.0,A,4302.539570,N,07920.379823,W,0.0,,070617,0.0,E,A*3F",
GPRMC{
Time: Time{true, 14, 27, 54, 0},
Validity: "A",
Speed: 0,
Course: 0,
Date: Date{true, 7, 6, 17},
Variation: 0,
Latitude: MustParseGPS("4302.539570 N"),
Longitude: MustParseGPS("07920.379823 W"),
},
},
}
func TestGPRMCGoodSentence(t *testing.T) {
for _, tt := range gprmctests {
s, err := Parse(tt.Input)
assert.NoError(t, err, "Unexpected error parsing good sentence")
assert.Equal(t, PrefixGPRMC, s.Prefix(), "Prefix does not match")
sentence := s.(GPRMC)
assert.Equal(t, tt.Output.Time, sentence.Time, "Time does not match")
assert.Equal(t, tt.Output.Validity, sentence.Validity, "Status does not match")
assert.Equal(t, tt.Output.Speed, sentence.Speed, "Speed does not match")
assert.Equal(t, tt.Output.Course, sentence.Course, "Course does not match")
assert.Equal(t, tt.Output.Date, sentence.Date, "Date does not match")
assert.Equal(t, tt.Output.Variation, sentence.Variation, "Variation does not match")
assert.Equal(t, tt.Output.Latitude, sentence.Latitude, "Latitude does not match")
assert.Equal(t, tt.Output.Longitude, sentence.Longitude, "Longitude does not match")
}
}
func TestGPRMCBadSentence(t *testing.T) {
badMsg := "$GPRMC,220516,D,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*75"
_, err := Parse(badMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: GPRMC invalid validity: D", err.Error(), "Incorrect error message")
}
func TestGPRMCWrongSentence(t *testing.T) {
wrongMsg := "$GPXTE,A,A,4.07,L,N*6D"
_, err := Parse(wrongMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: sentence type 'GPXTE' not implemented", err.Error(), "Incorrect error message")
}

29
vendor/github.com/adrianmo/go-nmea/gpvtg.go generated vendored Normal file
View file

@ -0,0 +1,29 @@
package nmea
const (
// PrefixGPVTG prefix
PrefixGPVTG = "GPVTG"
)
// GPVTG represents track & speed data.
// http://aprs.gids.nl/nmea/#vtg
type GPVTG struct {
Sent
TrueTrack float64
MagneticTrack float64
GroundSpeedKnots float64
GroundSpeedKPH float64
}
// NewGPVTG parses the GPVTG sentence into this struct.
// e.g: $GPVTG,360.0,T,348.7,M,000.0,N,000.0,K*43
func NewGPVTG(s Sent) (GPVTG, error) {
p := newParser(s, PrefixGPVTG)
return GPVTG{
Sent: s,
TrueTrack: p.Float64(0, "true track"),
MagneticTrack: p.Float64(2, "magnetic track"),
GroundSpeedKnots: p.Float64(4, "ground speed (knots)"),
GroundSpeedKPH: p.Float64(6, "ground speed (km/h)"),
}, p.Err()
}

38
vendor/github.com/adrianmo/go-nmea/gpvtg_test.go generated vendored Normal file
View file

@ -0,0 +1,38 @@
package nmea
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGPVTGGoodSentence(t *testing.T) {
goodMsg := "$GPVTG,45.5,T,67.5,M,30.45,N,56.40,K*4B"
s, err := Parse(goodMsg)
assert.NoError(t, err, "Unexpected error parsing good sentence")
assert.Equal(t, PrefixGPVTG, s.Prefix(), "Prefix does not match")
sentence := s.(GPVTG)
assert.Equal(t, 45.5, sentence.TrueTrack, "True track does not match")
assert.Equal(t, 67.5, sentence.MagneticTrack, "Magnetic track does not match")
assert.Equal(t, 30.45, sentence.GroundSpeedKnots, "Ground speed (knots) does not match")
assert.Equal(t, 56.40, sentence.GroundSpeedKPH, "Ground speed (km/h) does not match")
}
func TestGPVTGBadSentence(t *testing.T) {
badMsg := "$GPVTG,T,45.5,67.5,M,30.45,N,56.40,K*4B"
_, err := Parse(badMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: GPVTG invalid true track: T", err.Error(), "Incorrect error message")
}
func TestGPVTGWrongSentence(t *testing.T) {
wrongMsg := "$GPXTE,A,A,4.07,L,N*6D"
_, err := Parse(wrongMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: sentence type 'GPXTE' not implemented", err.Error(), "Incorrect error message")
}

32
vendor/github.com/adrianmo/go-nmea/gpzda.go generated vendored Normal file
View file

@ -0,0 +1,32 @@
package nmea
const (
// PrefixGPZDA prefix
PrefixGPZDA = "GPZDA"
)
// GPZDA represents date & time data.
// http://aprs.gids.nl/nmea/#zda
type GPZDA struct {
Sent
Time Time
Day int64
Month int64
Year int64
OffsetHours int64 // Local time zone offset from GMT, hours
OffsetMinutes int64 // Local time zone offset from GMT, minutes
}
// NewGPZDA constructor
func NewGPZDA(s Sent) (GPZDA, error) {
p := newParser(s, PrefixGPZDA)
return GPZDA{
Sent: s,
Time: p.Time(0, "time"),
Day: p.Int64(1, "day"),
Month: p.Int64(2, "month"),
Year: p.Int64(3, "year"),
OffsetHours: p.Int64(4, "offset (hours)"),
OffsetMinutes: p.Int64(5, "offset (minutes)"),
}, p.Err()
}

40
vendor/github.com/adrianmo/go-nmea/gpzda_test.go generated vendored Normal file
View file

@ -0,0 +1,40 @@
package nmea
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGPZDAGoodSentence(t *testing.T) {
goodMsg := "$GPZDA,172809.456,12,07,1996,00,00*57"
s, err := Parse(goodMsg)
assert.NoError(t, err, "Unexpected error parsing good sentence")
assert.Equal(t, PrefixGPZDA, s.Prefix(), "Prefix does not match")
sentence := s.(GPZDA)
assert.Equal(t, Time{true, 17, 28, 9, 456}, sentence.Time, "Time does not match")
assert.Equal(t, int64(12), sentence.Day, "Day does not match")
assert.Equal(t, int64(7), sentence.Month, "Month does not match")
assert.Equal(t, int64(1996), sentence.Year, "Yeah does not match")
assert.Equal(t, int64(0), sentence.OffsetHours, "Offset (hours) does not match")
assert.Equal(t, int64(0), sentence.OffsetMinutes, "Offset (minutes) does not match")
}
func TestGPZDABadSentence(t *testing.T) {
badMsg := "$GPZDA,220516,D,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*76"
_, err := Parse(badMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: GPZDA invalid day: D", err.Error(), "Incorrect error message")
}
func TestGPZDAWrongSentence(t *testing.T) {
wrongMsg := "$GPXTE,A,A,4.07,L,N*6D"
_, err := Parse(wrongMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: sentence type 'GPXTE' not implemented", err.Error(), "Incorrect error message")
}

141
vendor/github.com/adrianmo/go-nmea/parser.go generated vendored Normal file
View file

@ -0,0 +1,141 @@
package nmea
import (
"fmt"
"strconv"
)
// parser provides a simple way of accessing and parsing
// sentence fields
type parser struct {
Sent
prefix string
err error
}
// newParser constructor
func newParser(s Sent, prefix string) *parser {
p := &parser{Sent: s, prefix: prefix}
if p.Type != prefix {
p.SetErr("prefix", p.Type)
}
return p
}
// Err returns the first error encounterd during the parser's usage.
func (p *parser) Err() error {
return p.err
}
// SetErr assigns an error. Calling this method has no
// effect if there is already an error.
func (p *parser) SetErr(context, value string) {
if p.err == nil {
p.err = fmt.Errorf("nmea: %s invalid %s: %s", p.prefix, context, value)
}
}
// String returns the field value at the specified index.
func (p *parser) String(i int, context string) string {
if p.err != nil {
return ""
}
if i < 0 || i >= len(p.Fields) {
p.SetErr(context, "index out of range")
return ""
}
return p.Fields[i]
}
// EnumString returns the field value at the specified index.
// An error occurs if the value is not one of the options.
func (p *parser) EnumString(i int, context string, options ...string) string {
s := p.String(i, context)
if p.err != nil {
return ""
}
for _, o := range options {
if o == s {
return s
}
}
p.SetErr(context, s)
return ""
}
// Int64 returns the int64 value at the specified index.
// If the value is an emtpy string, 0 is returned.
func (p *parser) Int64(i int, context string) int64 {
s := p.String(i, context)
if p.err != nil {
return 0
}
if s == "" {
return 0
}
v, err := strconv.ParseInt(s, 10, 64)
if err != nil {
p.SetErr(context, s)
}
return v
}
// Float64 returns the float64 value at the specified index.
// If the value is an empty string, 0 is returned.
func (p *parser) Float64(i int, context string) float64 {
s := p.String(i, context)
if p.err != nil {
return 0
}
if s == "" {
return 0
}
v, err := strconv.ParseFloat(s, 64)
if err != nil {
p.SetErr(context, s)
}
return v
}
// Time returns the Time value at the specified index.
// If the value is empty, the Time is marked as invalid.
func (p *parser) Time(i int, context string) Time {
s := p.String(i, context)
if p.err != nil {
return Time{}
}
v, err := ParseTime(s)
if err != nil {
p.SetErr(context, s)
}
return v
}
// Date returns the Date value at the specified index.
// If the value is empty, the Date is marked as invalid.
func (p *parser) Date(i int, context string) Date {
s := p.String(i, context)
if p.err != nil {
return Date{}
}
v, err := ParseDate(s)
if err != nil {
p.SetErr(context, s)
}
return v
}
// LatLong returns the coordinate value of the specified fields.
func (p *parser) LatLong(i, j int, context string) LatLong {
a := p.String(i, context)
b := p.String(j, context)
if p.err != nil {
return 0
}
s := fmt.Sprintf("%s %s", a, b)
v, err := ParseLatLong(s)
if err != nil {
p.SetErr(context, err.Error())
}
return v
}

226
vendor/github.com/adrianmo/go-nmea/parser_test.go generated vendored Normal file
View file

@ -0,0 +1,226 @@
package nmea
import (
"testing"
"github.com/stretchr/testify/assert"
)
var parsertests = []struct {
name string
fields []string
expected interface{}
hasErr bool
parse func(p *parser) interface{}
}{
{
name: "String",
fields: []string{"foo", "bar"},
expected: "bar",
parse: func(p *parser) interface{} {
return p.String(1, "")
},
},
{
name: "String out of range",
fields: []string{"wot"},
expected: "",
hasErr: true,
parse: func(p *parser) interface{} {
return p.String(5, "thing")
},
},
{
name: "String with existing error",
expected: "",
hasErr: true,
parse: func(p *parser) interface{} {
p.SetErr("context", "value")
return p.String(123, "blah")
},
},
{
name: "EnumString",
fields: []string{"a", "b", "c"},
expected: "b",
parse: func(p *parser) interface{} {
return p.EnumString(1, "context", "b", "d")
},
},
{
name: "EnumString invalid",
fields: []string{"a", "b", "c"},
expected: "",
hasErr: true,
parse: func(p *parser) interface{} {
return p.EnumString(1, "context", "x", "y")
},
},
{
name: "EnumString with existing error",
fields: []string{"a", "b", "c"},
expected: "",
hasErr: true,
parse: func(p *parser) interface{} {
p.SetErr("context", "value")
return p.EnumString(1, "context", "a", "b")
},
},
{
name: "Int64",
fields: []string{"123"},
expected: int64(123),
parse: func(p *parser) interface{} {
return p.Int64(0, "context")
},
},
{
name: "Int64 empty field is zero",
fields: []string{""},
expected: int64(0),
parse: func(p *parser) interface{} {
return p.Int64(0, "context")
},
},
{
name: "Int64 invalid",
fields: []string{"abc"},
expected: int64(0),
hasErr: true,
parse: func(p *parser) interface{} {
return p.Int64(0, "context")
},
},
{
name: "Int64 with existing error",
fields: []string{"123"},
expected: int64(0),
hasErr: true,
parse: func(p *parser) interface{} {
p.SetErr("context", "value")
return p.Int64(0, "context")
},
},
{
name: "Float64",
fields: []string{"123.123"},
expected: float64(123.123),
parse: func(p *parser) interface{} {
return p.Float64(0, "context")
},
},
{
name: "Float64 empty field is zero",
fields: []string{""},
expected: float64(0),
parse: func(p *parser) interface{} {
return p.Float64(0, "context")
},
},
{
name: "Float64 invalid",
fields: []string{"abc"},
expected: float64(0),
hasErr: true,
parse: func(p *parser) interface{} {
return p.Float64(0, "context")
},
},
{
name: "Float64 with existing error",
fields: []string{"123.123"},
expected: float64(0),
hasErr: true,
parse: func(p *parser) interface{} {
p.SetErr("context", "value")
return p.Float64(0, "context")
},
},
{
name: "Time",
fields: []string{"123456"},
expected: Time{true, 12, 34, 56, 0},
parse: func(p *parser) interface{} {
return p.Time(0, "context")
},
},
{
name: "Time empty field is zero",
fields: []string{""},
expected: Time{},
parse: func(p *parser) interface{} {
return p.Time(0, "context")
},
},
{
name: "Time with existing error",
fields: []string{"123456"},
expected: Time{},
hasErr: true,
parse: func(p *parser) interface{} {
p.SetErr("context", "value")
return p.Time(0, "context")
},
},
{
name: "Time invalid",
fields: []string{"wrong"},
expected: Time{},
hasErr: true,
parse: func(p *parser) interface{} {
return p.Time(0, "context")
},
},
{
name: "Date",
fields: []string{"010203"},
expected: Date{true, 1, 2, 3},
parse: func(p *parser) interface{} {
return p.Date(0, "context")
},
},
{
name: "Date empty field is zero",
fields: []string{""},
expected: Date{},
parse: func(p *parser) interface{} {
return p.Date(0, "context")
},
},
{
name: "Date invalid",
fields: []string{"Hello"},
expected: Date{},
hasErr: true,
parse: func(p *parser) interface{} {
return p.Date(0, "context")
},
},
{
name: "Date with existing error",
fields: []string{"010203"},
expected: Date{},
hasErr: true,
parse: func(p *parser) interface{} {
p.SetErr("context", "value")
return p.Date(0, "context")
},
},
}
func TestParser(t *testing.T) {
for _, tt := range parsertests {
t.Run(tt.name, func(t *testing.T) {
p := newParser(Sent{
Type: "type",
Fields: tt.fields,
}, "type")
assert.Equal(t, tt.expected, tt.parse(p))
if tt.hasErr {
assert.Error(t, p.Err())
} else {
assert.NoError(t, p.Err())
}
})
}
}

38
vendor/github.com/adrianmo/go-nmea/pgrme.go generated vendored Normal file
View file

@ -0,0 +1,38 @@
package nmea
const (
// PrefixPGRME prefix for PGRME sentence type
PrefixPGRME = "PGRME"
// ErrorUnit must be meters (M)
ErrorUnit = "M"
)
// PGRME is Estimated Position Error (Garmin proprietary sentence)
// http://aprs.gids.nl/nmea/#rme
type PGRME struct {
Sent
Horizontal float64 // Estimated horizontal position error (HPE) in metres
Vertical float64 // Estimated vertical position error (VPE) in metres
Spherical float64 // Overall spherical equivalent position error in meters
}
// NewPGRME constructor
func NewPGRME(s Sent) (PGRME, error) {
p := newParser(s, PrefixPGRME)
horizontal := p.Float64(0, "horizontal error")
_ = p.EnumString(1, "horizontal error unit", ErrorUnit)
vertial := p.Float64(2, "vertical error")
_ = p.EnumString(3, "vertical error unit", ErrorUnit)
spherical := p.Float64(4, "spherical error")
_ = p.EnumString(5, "spherical error unit", ErrorUnit)
return PGRME{
Sent: s,
Horizontal: horizontal,
Vertical: vertial,
Spherical: spherical,
}, p.Err()
}

70
vendor/github.com/adrianmo/go-nmea/pgrme_test.go generated vendored Normal file
View file

@ -0,0 +1,70 @@
package nmea
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestPGRMEGoodSentence(t *testing.T) {
goodMsg := "$PGRME,3.3,M,4.9,M,6.0,M*25"
s, err := Parse(goodMsg)
assert.NoError(t, err, "Unexpected error parsing good sentence")
assert.Equal(t, PrefixPGRME, s.Prefix(), "Prefix does not match")
sentence := s.(PGRME)
assert.Equal(t, 3.3, sentence.Horizontal, "Horizontal error does not match")
assert.Equal(t, 4.9, sentence.Vertical, "Vertical error does not match")
assert.Equal(t, 6.0, sentence.Spherical, "Spherical error does not match")
}
func TestPGRMEInvalidHorizontalError(t *testing.T) {
badMsg := "$PGRME,A,M,4.9,M,6.0,M*4A"
_, err := Parse(badMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: PGRME invalid horizontal error: A", err.Error(), "Incorrect error message")
}
func TestPGRMEInvalidHorizontalErrorUnit(t *testing.T) {
badMsg := "$PGRME,3.3,A,4.9,M,6.0,M*29"
_, err := Parse(badMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: PGRME invalid horizontal error unit: A", err.Error(), "Incorrect error message")
}
func TestPGRMEInvalidVerticalError(t *testing.T) {
badMsg := "$PGRME,3.3,M,A,M,6.0,M*47"
_, err := Parse(badMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: PGRME invalid vertical error: A", err.Error(), "Incorrect error message")
}
func TestPGRMEInvalidVerticalErrorUnit(t *testing.T) {
badMsg := "$PGRME,3.3,M,4.9,A,6.0,M*29"
_, err := Parse(badMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: PGRME invalid vertical error unit: A", err.Error(), "Incorrect error message")
}
func TestPGRMEInvalidSphericalError(t *testing.T) {
badMsg := "$PGRME,3.3,M,4.9,M,A,M*4C"
_, err := Parse(badMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: PGRME invalid spherical error: A", err.Error(), "Incorrect error message")
}
func TestPGRMEInvalidSphericalErrorUnit(t *testing.T) {
badMsg := "$PGRME,3.3,M,4.9,M,6.0,A*29"
_, err := Parse(badMsg)
assert.Error(t, err, "Parse error not returned")
assert.Equal(t, "nmea: PGRME invalid spherical error unit: A", err.Error(), "Incorrect error message")
}

118
vendor/github.com/adrianmo/go-nmea/sentence.go generated vendored Normal file
View file

@ -0,0 +1,118 @@
package nmea
import (
"fmt"
"strings"
)
const (
// SentenceStart is the token to indicate the start of a sentence.
SentenceStart = "$"
// FieldSep is the token to delimit fields of a sentence.
FieldSep = ","
// ChecksumSep is the token to delimit the checksum of a sentence.
ChecksumSep = "*"
)
// Message interface for all NMEA sentence
type Message interface {
fmt.Stringer
Sentence() Sent
Prefix() string
Validate() error
}
// Sent contains the information about the NMEA sentence
type Sent struct {
Type string // The sentence type (e.g $GPGSA)
Fields []string // Array of fields
Checksum string // The Checksum
Raw string // The raw NMEA sentence received
}
// Sentence returns the Messages Sent
func (s Sent) Sentence() Sent { return s }
// Prefix returns the type of the message
func (s Sent) Prefix() string { return s.Type }
// String formats the sentence into a string
func (s Sent) String() string { return s.Raw }
// Validate returns an error if the sentence is not valid
func (s Sent) Validate() error { return nil }
// ParseSentence parses a raw message into it's fields
func ParseSentence(raw string) (Sent, error) {
startIndex := strings.Index(raw, SentenceStart)
if startIndex != 0 {
return Sent{}, fmt.Errorf("nmea: sentence does not start with a '$'")
}
sumSepIndex := strings.Index(raw, ChecksumSep)
if sumSepIndex == -1 {
return Sent{}, fmt.Errorf("nmea: sentence does not contain checksum separator")
}
var (
fieldsRaw = raw[startIndex+1 : sumSepIndex]
fields = strings.Split(fieldsRaw, FieldSep)
checksumRaw = strings.ToUpper(raw[sumSepIndex+1:])
checksum = xorChecksum(fieldsRaw)
)
// Validate the checksum
if checksum != checksumRaw {
return Sent{}, fmt.Errorf(
"nmea: sentence checksum mismatch [%s != %s]", checksum, checksumRaw)
}
return Sent{
Type: fields[0],
Fields: fields[1:],
Checksum: checksumRaw,
Raw: raw,
}, nil
}
// xor all the bytes in a string an return it
// as an uppercase hex string
func xorChecksum(s string) string {
var checksum uint8
for i := 0; i < len(s); i++ {
checksum ^= s[i]
}
return fmt.Sprintf("%02X", checksum)
}
// Parse parses the given string into the correct sentence type.
func Parse(raw string) (Message, error) {
s, err := ParseSentence(raw)
if err != nil {
return nil, err
}
switch s.Type {
case PrefixGPRMC:
return NewGPRMC(s)
case PrefixGNRMC:
return NewGNRMC(s)
case PrefixGPGGA:
return NewGPGGA(s)
case PrefixGNGGA:
return NewGNGGA(s)
case PrefixGPGSA:
return NewGPGSA(s)
case PrefixGPGLL:
return NewGPGLL(s)
case PrefixGPVTG:
return NewGPVTG(s)
case PrefixGPZDA:
return NewGPZDA(s)
case PrefixPGRME:
return NewPGRME(s)
case PrefixGPGSV:
return NewGPGSV(s)
case PrefixGLGSV:
return NewGLGSV(s)
default:
return nil, fmt.Errorf("nmea: sentence type '%s' not implemented", s.Type)
}
}

96
vendor/github.com/adrianmo/go-nmea/sentence_test.go generated vendored Normal file
View file

@ -0,0 +1,96 @@
package nmea
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestChecksumOK(t *testing.T) {
_, err := ParseSentence("$GPFOO,1,2,3.3,x,y,zz,*51")
assert.NoError(t, err, "Checksum check failed")
}
func TestChecksumBad(t *testing.T) {
_, err := ParseSentence("$GPFOO,1,2,3.4,x,y,zz,*51")
assert.Error(t, err, "Checksum check failed")
}
func TestChecksumBadRaw(t *testing.T) {
badRaw := "$GPFOO,1,2,3.3,x,y,zz,*33"
_, err := Parse(badRaw)
assert.Error(t, err, "Expected 'Sentence checksum mismatch [51 != 33]'")
}
func TestBadStartCharacter(t *testing.T) {
// Check that a bad start character is flagged.
rawBadStart := "%GPFOO,1,2,3,x,y,z*1A"
_, err := Parse(rawBadStart)
assert.Error(t, err, "Expected 'Sentence does not start with a '$''")
}
func TestBadChecksumDelimiter(t *testing.T) {
// Check that a bad checksum delimiter is flagged.
rawBadSumSep := "$GPFOO,1,2,3,x,y,z"
_, err := Parse(rawBadSumSep)
assert.Error(t, err, "Expected 'Sentence does not contain single checksum separator'")
}
func TestGoodParsing(t *testing.T) {
// Check for good parsing.
raw := "$GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0C"
_, err := Parse(raw)
assert.NoError(t, err, "Parse error")
}
func TestGoodFields(t *testing.T) {
raw := "$GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0C"
expectedFields := []string{"235236", "A", "3925.9479", "N", "11945.9211", "W", "44.7", "153.6", "250905", "15.2", "E", "A"}
m, _ := Parse(raw)
assert.EqualValues(t, expectedFields, m.Sentence().Fields, "Got '%q', expected '%q'", m.Sentence().Fields, expectedFields)
}
func TestGoodSentenceType(t *testing.T) {
raw := "$GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0C"
expected := "GPRMC"
m, _ := Parse(raw)
assert.Equal(t, expected, m.Prefix(), "Got '%s', expected '%s'", m.Prefix(), expected)
}
func TestGoodRawSentence(t *testing.T) {
raw := "$GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0C"
m, _ := Parse(raw)
assert.Equal(t, raw, m.String(), "Bad raw sentence")
}
func TestMultipleStartDelimiterSentence(t *testing.T) {
raw := "$$$$GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0C"
result, err := Parse(raw)
assert.Nil(t, result, "Result should be nil")
assert.NotNil(t, err, "Err should be an error")
assert.Equal(t, "nmea: sentence checksum mismatch [28 != 0C]", err.Error(), "Error sentence mismatch")
}
func TestNoStartDelimiterSentence(t *testing.T) {
raw := "abc$GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0C"
result, err := Parse(raw)
assert.Nil(t, result, "Result should be nil")
assert.NotNil(t, err, "Err should be an error")
assert.Equal(t, "nmea: sentence does not start with a '$'", err.Error(), "Error sentence mismatch")
}
func TestNoContainDelimiterSentence(t *testing.T) {
raw := "GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0C"
result, err := Parse(raw)
assert.Nil(t, result, "Result should be nil")
assert.NotNil(t, err, "Err should be an error")
assert.Equal(t, "nmea: sentence does not start with a '$'", err.Error(), "Error sentence mismatch")
}
func TestReturnValues(t *testing.T) {
// Ensure Parse returns errors when appropriate.
result, err := Parse("$GPRMC,235236,A,3925.9479,N,11945.9211,W,44.7,153.6,250905,15.2,E,A*0A")
assert.Nil(t, result, "Result should be nil")
assert.NotNil(t, err, "Err should be an error")
assert.Equal(t, "nmea: sentence checksum mismatch [0C != 0A]", err.Error(), "Error sentence mismatch")
}

259
vendor/github.com/adrianmo/go-nmea/types.go generated vendored Normal file
View file

@ -0,0 +1,259 @@
package nmea
// Latitude / longitude representation.
import (
"errors"
"fmt"
"math"
"strconv"
"strings"
"unicode"
// "unicode/utf8"
)
const (
// Degrees value
Degrees = '\u00B0'
// Minutes value
Minutes = '\''
// Seconds value
Seconds = '"'
// Point value
Point = '.'
// North value
North = "N"
// South value
South = "S"
// East value
East = "E"
// West value
West = "W"
)
// LatLong type
type LatLong float64
// PrintGPS returns the GPS format for the given LatLong.
func (l LatLong) PrintGPS() string {
padding := ""
value := float64(l)
degrees := math.Floor(math.Abs(value))
fraction := (math.Abs(value) - degrees) * 60
if fraction < 10 {
padding = "0"
}
return fmt.Sprintf("%d%s%.4f", int(degrees), padding, fraction)
}
// PrintDMS returns the degrees, minutes, seconds format for the given LatLong.
func (l LatLong) PrintDMS() string {
val := math.Abs(float64(l))
degrees := int(math.Floor(val))
minutes := int(math.Floor(60 * (val - float64(degrees))))
seconds := 3600 * (val - float64(degrees) - (float64(minutes) / 60))
return fmt.Sprintf("%d\u00B0 %d' %f\"", degrees, minutes, seconds)
}
//ValidRange validates if the range is between -180 and +180.
func (l LatLong) ValidRange() bool {
return -180.0 <= l && l <= 180.0
}
// IsNear returns whether the coordinate is near the other coordinate,
// by no further than the given distance away.
func (l LatLong) IsNear(o LatLong, max float64) bool {
return math.Abs(float64(l-o)) <= max
}
// ParseLatLong parses the supplied string into the LatLong.
//
// Supported formats are:
// - DMS (e.g. 33° 23' 22")
// - Decimal (e.g. 33.23454)
// - GPS (e.g 15113.4322S)
//
func ParseLatLong(s string) (LatLong, error) {
var l LatLong
var err error
invalid := LatLong(0.0) // The invalid value to return.
if l, err = ParseDMS(s); err == nil {
return l, nil
} else if l, err = ParseGPS(s); err == nil {
return l, nil
} else if l, err = ParseDecimal(s); err == nil {
return l, nil
}
if !l.ValidRange() {
return invalid, errors.New("coordinate is not in range -180, 180")
}
return invalid, fmt.Errorf("cannot parse [%s], unknown format", s)
}
// ParseGPS parses a GPS/NMEA coordinate.
// e.g 15113.4322S
func ParseGPS(s string) (LatLong, error) {
parts := strings.Split(s, " ")
dir := parts[1]
value, err := strconv.ParseFloat(parts[0], 64)
if err != nil {
return 0, fmt.Errorf("parse error: %s", err.Error())
}
degrees := math.Floor(value / 100)
minutes := value - (degrees * 100)
value = degrees + minutes/60
if dir == North || dir == East {
return LatLong(value), nil
} else if dir == South || dir == West {
return LatLong(0 - value), nil
} else {
return 0, fmt.Errorf("invalid direction [%s]", dir)
}
}
// ParseDecimal parses a decimal format coordinate.
// e.g: 151.196019
func ParseDecimal(s string) (LatLong, error) {
// Make sure it parses as a float.
l, err := strconv.ParseFloat(s, 64)
if err != nil || s[0] != '-' && len(strings.Split(s, ".")[0]) > 3 {
return LatLong(0.0), errors.New("parse error (not decimal coordinate)")
}
return LatLong(l), nil
}
// ParseDMS parses a coordinate in degrees, minutes, seconds.
// - e.g. 33° 23' 22"
func ParseDMS(s string) (LatLong, error) {
degrees := 0
minutes := 0
seconds := 0.0
// Whether a number has finished parsing (i.e whitespace after it)
endNumber := false
// Temporary parse buffer.
tmpBytes := []byte{}
var err error
for i, r := range s {
if unicode.IsNumber(r) || r == '.' {
if !endNumber {
tmpBytes = append(tmpBytes, s[i])
} else {
return 0, errors.New("parse error (no delimiter)")
}
} else if unicode.IsSpace(r) && len(tmpBytes) > 0 {
endNumber = true
} else if r == Degrees {
if degrees, err = strconv.Atoi(string(tmpBytes)); err != nil {
return 0, errors.New("parse error (degrees)")
}
tmpBytes = tmpBytes[:0]
endNumber = false
} else if s[i] == Minutes {
if minutes, err = strconv.Atoi(string(tmpBytes)); err != nil {
return 0, errors.New("parse error (minutes)")
}
tmpBytes = tmpBytes[:0]
endNumber = false
} else if s[i] == Seconds {
if seconds, err = strconv.ParseFloat(string(tmpBytes), 64); err != nil {
return 0, errors.New("parse error (seconds)")
}
tmpBytes = tmpBytes[:0]
endNumber = false
} else if unicode.IsSpace(r) && len(tmpBytes) == 0 {
continue
} else {
return 0, fmt.Errorf("parse error (unknown symbol [%d])", s[i])
}
}
val := LatLong(float64(degrees) + (float64(minutes) / 60.0) + (float64(seconds) / 60.0 / 60.0))
return val, nil
}
// Time type
type Time struct {
Valid bool
Hour int
Minute int
Second int
Millisecond int
}
// String representation of Time
func (t Time) String() string {
return fmt.Sprintf("%02d:%02d:%02d.%04d", t.Hour, t.Minute, t.Second, t.Millisecond)
}
// ParseTime parses wall clock time.
// e.g. hhmmss.ssss
// An empty time string will result in an invalid time.
func ParseTime(s string) (Time, error) {
if s == "" {
return Time{}, nil
}
ms := "0000"
hhmmss := s
if parts := strings.SplitN(s, ".", 2); len(parts) > 1 {
hhmmss, ms = parts[0], parts[1]
}
if len(hhmmss) != 6 {
return Time{}, fmt.Errorf("parse time: exptected hhmmss.ss format, got '%s'", s)
}
hour, err := strconv.Atoi(hhmmss[0:2])
if err != nil {
return Time{}, errors.New(hhmmss)
}
minute, err := strconv.Atoi(hhmmss[2:4])
if err != nil {
return Time{}, errors.New(hhmmss)
}
second, err := strconv.Atoi(hhmmss[4:6])
if err != nil {
return Time{}, errors.New(hhmmss)
}
millisecond, err := strconv.Atoi(ms)
if err != nil {
return Time{}, errors.New(hhmmss)
}
return Time{true, hour, minute, second, millisecond}, nil
}
// Date type
type Date struct {
Valid bool
DD int
MM int
YY int
}
// String representation of date
func (d Date) String() string {
return fmt.Sprintf("%02d/%02d/%02d", d.DD, d.MM, d.YY)
}
// ParseDate field ddmmyy format
func ParseDate(ddmmyy string) (Date, error) {
if ddmmyy == "" {
return Date{}, nil
}
if len(ddmmyy) != 6 {
return Date{}, fmt.Errorf("parse date: exptected ddmmyy format, got '%s'", ddmmyy)
}
dd, err := strconv.Atoi(ddmmyy[0:2])
if err != nil {
return Date{}, errors.New(ddmmyy)
}
mm, err := strconv.Atoi(ddmmyy[2:4])
if err != nil {
return Date{}, errors.New(ddmmyy)
}
yy, err := strconv.Atoi(ddmmyy[4:6])
if err != nil {
return Date{}, errors.New(ddmmyy)
}
return Date{true, dd, mm, yy}, nil
}

152
vendor/github.com/adrianmo/go-nmea/types_test.go generated vendored Normal file
View file

@ -0,0 +1,152 @@
package nmea
import "testing"
var nearDistance = 0.001
func TestLatLongParse(t *testing.T) {
var l LatLong
var err error
value, expected := "3345.1232 N", LatLong(33.752054)
if l, err = ParseGPS(value); err != nil {
t.Errorf("ParseGPS error: %s", err)
} else if !l.IsNear(expected, nearDistance) {
t.Errorf("ParseGPS got %f, expected %f", l, expected)
}
value, expected = "15145.9877 S", LatLong(-151.76646)
if l, err = ParseGPS(value); err != nil {
t.Errorf("ParseGPS error: %s", err)
} else if !l.IsNear(expected, nearDistance) {
t.Errorf("ParseGPS got %f, expected %f", l, expected)
}
value, expected = "33\u00B0 12' 34.3423\"", LatLong(33.209540)
if l, err = ParseDMS(value); err != nil {
t.Errorf("ParseDMS error: %s", err)
} else if !l.IsNear(expected, nearDistance) {
t.Errorf("ParseDMS got %f, expected %f", l, expected)
}
value, expected = "151.234532", LatLong(151.234532)
if l, err = ParseDecimal(value); err != nil {
t.Errorf("ParseDecimal error: %s", err)
} else if !l.IsNear(expected, nearDistance) {
t.Errorf("ParseDecimal got %f, expected %f", l, expected)
}
value, expected = "-151.234532", LatLong(-151.234532)
if l, err = ParseDecimal(value); err != nil {
t.Errorf("ParseDecimal error: %s", err)
} else if !l.IsNear(expected, nearDistance) {
t.Errorf("ParseDecimal got %f, expected %f", l, expected)
}
}
func TestLatLongPrint(t *testing.T) {
l, _ := ParseDecimal("151.434367")
exp := "15126.0620"
if s := l.PrintGPS(); s != exp {
t.Errorf("PrintGPS() got %s expected %s", s, exp)
}
l, _ = ParseGPS("3356.4343 N")
exp = "3356.4343"
if s := l.PrintGPS(); s != exp {
t.Errorf("PrintGPS() got %s expected %s", s, exp)
}
exp = "33° 56' 26.058000\""
if s := l.PrintDMS(); s != exp {
t.Errorf("PrintDMS() got %s expected %s", s, exp)
}
}
func TestTimeParse(t *testing.T) {
timetests := []struct {
value string
expected Time
ok bool
}{
{"123456", Time{true, 12, 34, 56, 0}, true},
{"", Time{}, true},
{"112233.123", Time{true, 11, 22, 33, 123}, true},
{"010203.04", Time{true, 1, 2, 3, 4}, true},
{"10203.04", Time{}, false},
{"x0u2xd", Time{}, false},
{"xx2233.123", Time{}, false},
{"11xx33.123", Time{}, false},
{"1122xx.123", Time{}, false},
{"112233.xxx", Time{}, false},
}
for _, tt := range timetests {
actual, err := ParseTime(tt.value)
if !tt.ok {
if err == nil {
t.Errorf("ParseTime(%s) expected error", tt.value)
}
} else {
if err != nil {
t.Errorf("ParseTime(%s) %s", tt.value, err)
}
if actual != tt.expected {
t.Errorf("ParseTime(%s) got %s expected %s", tt.value, actual, tt.expected)
}
}
}
}
func TestTimeString(t *testing.T) {
d := Time{
Hour: 1,
Minute: 2,
Second: 3,
Millisecond: 4,
}
expected := "01:02:03.0004"
if s := d.String(); s != expected {
t.Fatalf("got %s, expected %s", s, expected)
}
}
func TestDateParse(t *testing.T) {
datetests := []struct {
value string
expected Date
ok bool
}{
{"010203", Date{true, 1, 2, 3}, true},
{"01003", Date{}, false},
{"", Date{}, true},
{"xx0203", Date{}, false},
{"01xx03", Date{}, false},
{"0102xx", Date{}, false},
}
for _, tt := range datetests {
actual, err := ParseDate(tt.value)
if !tt.ok {
if err == nil {
t.Errorf("ParseDate(%s) expected error", tt.value)
}
} else {
if err != nil {
t.Errorf("ParseDate(%s) %s", tt.value, err)
}
if actual != tt.expected {
t.Errorf("ParseDate(%s) got %s expected %s", tt.value, actual, tt.expected)
}
}
}
}
func TestDateString(t *testing.T) {
d := Date{
DD: 1,
MM: 2,
YY: 3,
}
expected := "01/02/03"
if s := d.String(); s != expected {
t.Fatalf("got %s expected %s", s, expected)
}
}

21
vendor/github.com/dustin/go-humanize/.travis.yml generated vendored Normal file
View file

@ -0,0 +1,21 @@
sudo: false
language: go
go:
- 1.3.x
- 1.5.x
- 1.6.x
- 1.7.x
- 1.8.x
- 1.9.x
- master
matrix:
allow_failures:
- go: master
fast_finish: true
install:
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
script:
- go get -t -v ./...
- diff -u <(echo -n) <(gofmt -d -s .)
- go tool vet .
- go test -v -race ./...

21
vendor/github.com/dustin/go-humanize/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
Copyright (c) 2005-2008 Dustin Sallings <dustin@spy.net>
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.
<http://www.opensource.org/licenses/mit-license.php>

124
vendor/github.com/dustin/go-humanize/README.markdown generated vendored Normal file
View file

@ -0,0 +1,124 @@
# Humane Units [![Build Status](https://travis-ci.org/dustin/go-humanize.svg?branch=master)](https://travis-ci.org/dustin/go-humanize) [![GoDoc](https://godoc.org/github.com/dustin/go-humanize?status.svg)](https://godoc.org/github.com/dustin/go-humanize)
Just a few functions for helping humanize times and sizes.
`go get` it as `github.com/dustin/go-humanize`, import it as
`"github.com/dustin/go-humanize"`, use it as `humanize`.
See [godoc](https://godoc.org/github.com/dustin/go-humanize) for
complete documentation.
## Sizes
This lets you take numbers like `82854982` and convert them to useful
strings like, `83 MB` or `79 MiB` (whichever you prefer).
Example:
```go
fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB.
```
## Times
This lets you take a `time.Time` and spit it out in relative terms.
For example, `12 seconds ago` or `3 days from now`.
Example:
```go
fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago.
```
Thanks to Kyle Lemons for the time implementation from an IRC
conversation one day. It's pretty neat.
## Ordinals
From a [mailing list discussion][odisc] where a user wanted to be able
to label ordinals.
0 -> 0th
1 -> 1st
2 -> 2nd
3 -> 3rd
4 -> 4th
[...]
Example:
```go
fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend.
```
## Commas
Want to shove commas into numbers? Be my guest.
0 -> 0
100 -> 100
1000 -> 1,000
1000000000 -> 1,000,000,000
-100000 -> -100,000
Example:
```go
fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491.
```
## Ftoa
Nicer float64 formatter that removes trailing zeros.
```go
fmt.Printf("%f", 2.24) // 2.240000
fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24
fmt.Printf("%f", 2.0) // 2.000000
fmt.Printf("%s", humanize.Ftoa(2.0)) // 2
```
## SI notation
Format numbers with [SI notation][sinotation].
Example:
```go
humanize.SI(0.00000000223, "M") // 2.23 nM
```
## English-specific functions
The following functions are in the `humanize/english` subpackage.
### Plurals
Simple English pluralization
```go
english.PluralWord(1, "object", "") // object
english.PluralWord(42, "object", "") // objects
english.PluralWord(2, "bus", "") // buses
english.PluralWord(99, "locus", "loci") // loci
english.Plural(1, "object", "") // 1 object
english.Plural(42, "object", "") // 42 objects
english.Plural(2, "bus", "") // 2 buses
english.Plural(99, "locus", "loci") // 99 loci
```
### Word series
Format comma-separated words lists with conjuctions:
```go
english.WordSeries([]string{"foo"}, "and") // foo
english.WordSeries([]string{"foo", "bar"}, "and") // foo and bar
english.WordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar and baz
english.OxfordWordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar, and baz
```
[odisc]: https://groups.google.com/d/topic/golang-nuts/l8NhI74jl-4/discussion
[sinotation]: http://en.wikipedia.org/wiki/Metric_prefix

31
vendor/github.com/dustin/go-humanize/big.go generated vendored Normal file
View file

@ -0,0 +1,31 @@
package humanize
import (
"math/big"
)
// order of magnitude (to a max order)
func oomm(n, b *big.Int, maxmag int) (float64, int) {
mag := 0
m := &big.Int{}
for n.Cmp(b) >= 0 {
n.DivMod(n, b, m)
mag++
if mag == maxmag && maxmag >= 0 {
break
}
}
return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag
}
// total order of magnitude
// (same as above, but with no upper limit)
func oom(n, b *big.Int) (float64, int) {
mag := 0
m := &big.Int{}
for n.Cmp(b) >= 0 {
n.DivMod(n, b, m)
mag++
}
return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag
}

173
vendor/github.com/dustin/go-humanize/bigbytes.go generated vendored Normal file
View file

@ -0,0 +1,173 @@
package humanize
import (
"fmt"
"math/big"
"strings"
"unicode"
)
var (
bigIECExp = big.NewInt(1024)
// BigByte is one byte in bit.Ints
BigByte = big.NewInt(1)
// BigKiByte is 1,024 bytes in bit.Ints
BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp)
// BigMiByte is 1,024 k bytes in bit.Ints
BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp)
// BigGiByte is 1,024 m bytes in bit.Ints
BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp)
// BigTiByte is 1,024 g bytes in bit.Ints
BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp)
// BigPiByte is 1,024 t bytes in bit.Ints
BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp)
// BigEiByte is 1,024 p bytes in bit.Ints
BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp)
// BigZiByte is 1,024 e bytes in bit.Ints
BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp)
// BigYiByte is 1,024 z bytes in bit.Ints
BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp)
)
var (
bigSIExp = big.NewInt(1000)
// BigSIByte is one SI byte in big.Ints
BigSIByte = big.NewInt(1)
// BigKByte is 1,000 SI bytes in big.Ints
BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp)
// BigMByte is 1,000 SI k bytes in big.Ints
BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp)
// BigGByte is 1,000 SI m bytes in big.Ints
BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp)
// BigTByte is 1,000 SI g bytes in big.Ints
BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp)
// BigPByte is 1,000 SI t bytes in big.Ints
BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp)
// BigEByte is 1,000 SI p bytes in big.Ints
BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp)
// BigZByte is 1,000 SI e bytes in big.Ints
BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp)
// BigYByte is 1,000 SI z bytes in big.Ints
BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp)
)
var bigBytesSizeTable = map[string]*big.Int{
"b": BigByte,
"kib": BigKiByte,
"kb": BigKByte,
"mib": BigMiByte,
"mb": BigMByte,
"gib": BigGiByte,
"gb": BigGByte,
"tib": BigTiByte,
"tb": BigTByte,
"pib": BigPiByte,
"pb": BigPByte,
"eib": BigEiByte,
"eb": BigEByte,
"zib": BigZiByte,
"zb": BigZByte,
"yib": BigYiByte,
"yb": BigYByte,
// Without suffix
"": BigByte,
"ki": BigKiByte,
"k": BigKByte,
"mi": BigMiByte,
"m": BigMByte,
"gi": BigGiByte,
"g": BigGByte,
"ti": BigTiByte,
"t": BigTByte,
"pi": BigPiByte,
"p": BigPByte,
"ei": BigEiByte,
"e": BigEByte,
"z": BigZByte,
"zi": BigZiByte,
"y": BigYByte,
"yi": BigYiByte,
}
var ten = big.NewInt(10)
func humanateBigBytes(s, base *big.Int, sizes []string) string {
if s.Cmp(ten) < 0 {
return fmt.Sprintf("%d B", s)
}
c := (&big.Int{}).Set(s)
val, mag := oomm(c, base, len(sizes)-1)
suffix := sizes[mag]
f := "%.0f %s"
if val < 10 {
f = "%.1f %s"
}
return fmt.Sprintf(f, val, suffix)
}
// BigBytes produces a human readable representation of an SI size.
//
// See also: ParseBigBytes.
//
// BigBytes(82854982) -> 83 MB
func BigBytes(s *big.Int) string {
sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
return humanateBigBytes(s, bigSIExp, sizes)
}
// BigIBytes produces a human readable representation of an IEC size.
//
// See also: ParseBigBytes.
//
// BigIBytes(82854982) -> 79 MiB
func BigIBytes(s *big.Int) string {
sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
return humanateBigBytes(s, bigIECExp, sizes)
}
// ParseBigBytes parses a string representation of bytes into the number
// of bytes it represents.
//
// See also: BigBytes, BigIBytes.
//
// ParseBigBytes("42 MB") -> 42000000, nil
// ParseBigBytes("42 mib") -> 44040192, nil
func ParseBigBytes(s string) (*big.Int, error) {
lastDigit := 0
hasComma := false
for _, r := range s {
if !(unicode.IsDigit(r) || r == '.' || r == ',') {
break
}
if r == ',' {
hasComma = true
}
lastDigit++
}
num := s[:lastDigit]
if hasComma {
num = strings.Replace(num, ",", "", -1)
}
val := &big.Rat{}
_, err := fmt.Sscanf(num, "%f", val)
if err != nil {
return nil, err
}
extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
if m, ok := bigBytesSizeTable[extra]; ok {
mv := (&big.Rat{}).SetInt(m)
val.Mul(val, mv)
rv := &big.Int{}
rv.Div(val.Num(), val.Denom())
return rv, nil
}
return nil, fmt.Errorf("unhandled size name: %v", extra)
}

220
vendor/github.com/dustin/go-humanize/bigbytes_test.go generated vendored Normal file
View file

@ -0,0 +1,220 @@
package humanize
import (
"math/big"
"testing"
)
func TestBigByteParsing(t *testing.T) {
tests := []struct {
in string
exp uint64
}{
{"42", 42},
{"42MB", 42000000},
{"42MiB", 44040192},
{"42mb", 42000000},
{"42mib", 44040192},
{"42MIB", 44040192},
{"42 MB", 42000000},
{"42 MiB", 44040192},
{"42 mb", 42000000},
{"42 mib", 44040192},
{"42 MIB", 44040192},
{"42.5MB", 42500000},
{"42.5MiB", 44564480},
{"42.5 MB", 42500000},
{"42.5 MiB", 44564480},
// No need to say B
{"42M", 42000000},
{"42Mi", 44040192},
{"42m", 42000000},
{"42mi", 44040192},
{"42MI", 44040192},
{"42 M", 42000000},
{"42 Mi", 44040192},
{"42 m", 42000000},
{"42 mi", 44040192},
{"42 MI", 44040192},
{"42.5M", 42500000},
{"42.5Mi", 44564480},
{"42.5 M", 42500000},
{"42.5 Mi", 44564480},
{"1,005.03 MB", 1005030000},
// Large testing, breaks when too much larger than
// this.
{"12.5 EB", uint64(12.5 * float64(EByte))},
{"12.5 E", uint64(12.5 * float64(EByte))},
{"12.5 EiB", uint64(12.5 * float64(EiByte))},
}
for _, p := range tests {
got, err := ParseBigBytes(p.in)
if err != nil {
t.Errorf("Couldn't parse %v: %v", p.in, err)
} else {
if got.Uint64() != p.exp {
t.Errorf("Expected %v for %v, got %v",
p.exp, p.in, got)
}
}
}
}
func TestBigByteErrors(t *testing.T) {
got, err := ParseBigBytes("84 JB")
if err == nil {
t.Errorf("Expected error, got %v", got)
}
got, err = ParseBigBytes("")
if err == nil {
t.Errorf("Expected error parsing nothing")
}
}
func bbyte(in uint64) string {
return BigBytes((&big.Int{}).SetUint64(in))
}
func bibyte(in uint64) string {
return BigIBytes((&big.Int{}).SetUint64(in))
}
func TestBigBytes(t *testing.T) {
testList{
{"bytes(0)", bbyte(0), "0 B"},
{"bytes(1)", bbyte(1), "1 B"},
{"bytes(803)", bbyte(803), "803 B"},
{"bytes(999)", bbyte(999), "999 B"},
{"bytes(1024)", bbyte(1024), "1.0 kB"},
{"bytes(1MB - 1)", bbyte(MByte - Byte), "1000 kB"},
{"bytes(1MB)", bbyte(1024 * 1024), "1.0 MB"},
{"bytes(1GB - 1K)", bbyte(GByte - KByte), "1000 MB"},
{"bytes(1GB)", bbyte(GByte), "1.0 GB"},
{"bytes(1TB - 1M)", bbyte(TByte - MByte), "1000 GB"},
{"bytes(1TB)", bbyte(TByte), "1.0 TB"},
{"bytes(1PB - 1T)", bbyte(PByte - TByte), "999 TB"},
{"bytes(1PB)", bbyte(PByte), "1.0 PB"},
{"bytes(1PB - 1T)", bbyte(EByte - PByte), "999 PB"},
{"bytes(1EB)", bbyte(EByte), "1.0 EB"},
// Overflows.
// {"bytes(1EB - 1P)", Bytes((KByte*EByte)-PByte), "1023EB"},
{"bytes(0)", bibyte(0), "0 B"},
{"bytes(1)", bibyte(1), "1 B"},
{"bytes(803)", bibyte(803), "803 B"},
{"bytes(1023)", bibyte(1023), "1023 B"},
{"bytes(1024)", bibyte(1024), "1.0 KiB"},
{"bytes(1MB - 1)", bibyte(MiByte - IByte), "1024 KiB"},
{"bytes(1MB)", bibyte(1024 * 1024), "1.0 MiB"},
{"bytes(1GB - 1K)", bibyte(GiByte - KiByte), "1024 MiB"},
{"bytes(1GB)", bibyte(GiByte), "1.0 GiB"},
{"bytes(1TB - 1M)", bibyte(TiByte - MiByte), "1024 GiB"},
{"bytes(1TB)", bibyte(TiByte), "1.0 TiB"},
{"bytes(1PB - 1T)", bibyte(PiByte - TiByte), "1023 TiB"},
{"bytes(1PB)", bibyte(PiByte), "1.0 PiB"},
{"bytes(1PB - 1T)", bibyte(EiByte - PiByte), "1023 PiB"},
{"bytes(1EiB)", bibyte(EiByte), "1.0 EiB"},
// Overflows.
// {"bytes(1EB - 1P)", bibyte((KIByte*EIByte)-PiByte), "1023EB"},
{"bytes(5.5GiB)", bibyte(5.5 * GiByte), "5.5 GiB"},
{"bytes(5.5GB)", bbyte(5.5 * GByte), "5.5 GB"},
}.validate(t)
}
func TestVeryBigBytes(t *testing.T) {
b, _ := (&big.Int{}).SetString("15347691069326346944512", 10)
s := BigBytes(b)
if s != "15 ZB" {
t.Errorf("Expected 15 ZB, got %v", s)
}
s = BigIBytes(b)
if s != "13 ZiB" {
t.Errorf("Expected 13 ZiB, got %v", s)
}
b, _ = (&big.Int{}).SetString("15716035654990179271180288", 10)
s = BigBytes(b)
if s != "16 YB" {
t.Errorf("Expected 16 YB, got %v", s)
}
s = BigIBytes(b)
if s != "13 YiB" {
t.Errorf("Expected 13 YiB, got %v", s)
}
}
func TestVeryVeryBigBytes(t *testing.T) {
b, _ := (&big.Int{}).SetString("16093220510709943573688614912", 10)
s := BigBytes(b)
if s != "16093 YB" {
t.Errorf("Expected 16093 YB, got %v", s)
}
s = BigIBytes(b)
if s != "13312 YiB" {
t.Errorf("Expected 13312 YiB, got %v", s)
}
}
func TestParseVeryBig(t *testing.T) {
tests := []struct {
in string
out string
}{
{"16 ZB", "16000000000000000000000"},
{"16 ZiB", "18889465931478580854784"},
{"16.5 ZB", "16500000000000000000000"},
{"16.5 ZiB", "19479761741837286506496"},
{"16 Z", "16000000000000000000000"},
{"16 Zi", "18889465931478580854784"},
{"16.5 Z", "16500000000000000000000"},
{"16.5 Zi", "19479761741837286506496"},
{"16 YB", "16000000000000000000000000"},
{"16 YiB", "19342813113834066795298816"},
{"16.5 YB", "16500000000000000000000000"},
{"16.5 YiB", "19947276023641381382651904"},
{"16 Y", "16000000000000000000000000"},
{"16 Yi", "19342813113834066795298816"},
{"16.5 Y", "16500000000000000000000000"},
{"16.5 Yi", "19947276023641381382651904"},
}
for _, test := range tests {
x, err := ParseBigBytes(test.in)
if err != nil {
t.Errorf("Error parsing %q: %v", test.in, err)
continue
}
if x.String() != test.out {
t.Errorf("Expected %q for %q, got %v", test.out, test.in, x)
}
}
}
func BenchmarkParseBigBytes(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseBigBytes("16.5 Z")
}
}
func BenchmarkBigBytes(b *testing.B) {
for i := 0; i < b.N; i++ {
bibyte(16.5 * GByte)
}
}

143
vendor/github.com/dustin/go-humanize/bytes.go generated vendored Normal file
View file

@ -0,0 +1,143 @@
package humanize
import (
"fmt"
"math"
"strconv"
"strings"
"unicode"
)
// IEC Sizes.
// kibis of bits
const (
Byte = 1 << (iota * 10)
KiByte
MiByte
GiByte
TiByte
PiByte
EiByte
)
// SI Sizes.
const (
IByte = 1
KByte = IByte * 1000
MByte = KByte * 1000
GByte = MByte * 1000
TByte = GByte * 1000
PByte = TByte * 1000
EByte = PByte * 1000
)
var bytesSizeTable = map[string]uint64{
"b": Byte,
"kib": KiByte,
"kb": KByte,
"mib": MiByte,
"mb": MByte,
"gib": GiByte,
"gb": GByte,
"tib": TiByte,
"tb": TByte,
"pib": PiByte,
"pb": PByte,
"eib": EiByte,
"eb": EByte,
// Without suffix
"": Byte,
"ki": KiByte,
"k": KByte,
"mi": MiByte,
"m": MByte,
"gi": GiByte,
"g": GByte,
"ti": TiByte,
"t": TByte,
"pi": PiByte,
"p": PByte,
"ei": EiByte,
"e": EByte,
}
func logn(n, b float64) float64 {
return math.Log(n) / math.Log(b)
}
func humanateBytes(s uint64, base float64, sizes []string) string {
if s < 10 {
return fmt.Sprintf("%d B", s)
}
e := math.Floor(logn(float64(s), base))
suffix := sizes[int(e)]
val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10
f := "%.0f %s"
if val < 10 {
f = "%.1f %s"
}
return fmt.Sprintf(f, val, suffix)
}
// Bytes produces a human readable representation of an SI size.
//
// See also: ParseBytes.
//
// Bytes(82854982) -> 83 MB
func Bytes(s uint64) string {
sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
return humanateBytes(s, 1000, sizes)
}
// IBytes produces a human readable representation of an IEC size.
//
// See also: ParseBytes.
//
// IBytes(82854982) -> 79 MiB
func IBytes(s uint64) string {
sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
return humanateBytes(s, 1024, sizes)
}
// ParseBytes parses a string representation of bytes into the number
// of bytes it represents.
//
// See Also: Bytes, IBytes.
//
// ParseBytes("42 MB") -> 42000000, nil
// ParseBytes("42 mib") -> 44040192, nil
func ParseBytes(s string) (uint64, error) {
lastDigit := 0
hasComma := false
for _, r := range s {
if !(unicode.IsDigit(r) || r == '.' || r == ',') {
break
}
if r == ',' {
hasComma = true
}
lastDigit++
}
num := s[:lastDigit]
if hasComma {
num = strings.Replace(num, ",", "", -1)
}
f, err := strconv.ParseFloat(num, 64)
if err != nil {
return 0, err
}
extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
if m, ok := bytesSizeTable[extra]; ok {
f *= float64(m)
if f >= math.MaxUint64 {
return 0, fmt.Errorf("too large: %v", s)
}
return uint64(f), nil
}
return 0, fmt.Errorf("unhandled size name: %v", extra)
}

146
vendor/github.com/dustin/go-humanize/bytes_test.go generated vendored Normal file
View file

@ -0,0 +1,146 @@
package humanize
import (
"testing"
)
func TestByteParsing(t *testing.T) {
tests := []struct {
in string
exp uint64
}{
{"42", 42},
{"42MB", 42000000},
{"42MiB", 44040192},
{"42mb", 42000000},
{"42mib", 44040192},
{"42MIB", 44040192},
{"42 MB", 42000000},
{"42 MiB", 44040192},
{"42 mb", 42000000},
{"42 mib", 44040192},
{"42 MIB", 44040192},
{"42.5MB", 42500000},
{"42.5MiB", 44564480},
{"42.5 MB", 42500000},
{"42.5 MiB", 44564480},
// No need to say B
{"42M", 42000000},
{"42Mi", 44040192},
{"42m", 42000000},
{"42mi", 44040192},
{"42MI", 44040192},
{"42 M", 42000000},
{"42 Mi", 44040192},
{"42 m", 42000000},
{"42 mi", 44040192},
{"42 MI", 44040192},
{"42.5M", 42500000},
{"42.5Mi", 44564480},
{"42.5 M", 42500000},
{"42.5 Mi", 44564480},
// Bug #42
{"1,005.03 MB", 1005030000},
// Large testing, breaks when too much larger than
// this.
{"12.5 EB", uint64(12.5 * float64(EByte))},
{"12.5 E", uint64(12.5 * float64(EByte))},
{"12.5 EiB", uint64(12.5 * float64(EiByte))},
}
for _, p := range tests {
got, err := ParseBytes(p.in)
if err != nil {
t.Errorf("Couldn't parse %v: %v", p.in, err)
}
if got != p.exp {
t.Errorf("Expected %v for %v, got %v",
p.exp, p.in, got)
}
}
}
func TestByteErrors(t *testing.T) {
got, err := ParseBytes("84 JB")
if err == nil {
t.Errorf("Expected error, got %v", got)
}
got, err = ParseBytes("")
if err == nil {
t.Errorf("Expected error parsing nothing")
}
got, err = ParseBytes("16 EiB")
if err == nil {
t.Errorf("Expected error, got %v", got)
}
}
func TestBytes(t *testing.T) {
testList{
{"bytes(0)", Bytes(0), "0 B"},
{"bytes(1)", Bytes(1), "1 B"},
{"bytes(803)", Bytes(803), "803 B"},
{"bytes(999)", Bytes(999), "999 B"},
{"bytes(1024)", Bytes(1024), "1.0 kB"},
{"bytes(9999)", Bytes(9999), "10 kB"},
{"bytes(1MB - 1)", Bytes(MByte - Byte), "1000 kB"},
{"bytes(1MB)", Bytes(1024 * 1024), "1.0 MB"},
{"bytes(1GB - 1K)", Bytes(GByte - KByte), "1000 MB"},
{"bytes(1GB)", Bytes(GByte), "1.0 GB"},
{"bytes(1TB - 1M)", Bytes(TByte - MByte), "1000 GB"},
{"bytes(10MB)", Bytes(9999 * 1000), "10 MB"},
{"bytes(1TB)", Bytes(TByte), "1.0 TB"},
{"bytes(1PB - 1T)", Bytes(PByte - TByte), "999 TB"},
{"bytes(1PB)", Bytes(PByte), "1.0 PB"},
{"bytes(1PB - 1T)", Bytes(EByte - PByte), "999 PB"},
{"bytes(1EB)", Bytes(EByte), "1.0 EB"},
// Overflows.
// {"bytes(1EB - 1P)", Bytes((KByte*EByte)-PByte), "1023EB"},
{"bytes(0)", IBytes(0), "0 B"},
{"bytes(1)", IBytes(1), "1 B"},
{"bytes(803)", IBytes(803), "803 B"},
{"bytes(1023)", IBytes(1023), "1023 B"},
{"bytes(1024)", IBytes(1024), "1.0 KiB"},
{"bytes(1MB - 1)", IBytes(MiByte - IByte), "1024 KiB"},
{"bytes(1MB)", IBytes(1024 * 1024), "1.0 MiB"},
{"bytes(1GB - 1K)", IBytes(GiByte - KiByte), "1024 MiB"},
{"bytes(1GB)", IBytes(GiByte), "1.0 GiB"},
{"bytes(1TB - 1M)", IBytes(TiByte - MiByte), "1024 GiB"},
{"bytes(1TB)", IBytes(TiByte), "1.0 TiB"},
{"bytes(1PB - 1T)", IBytes(PiByte - TiByte), "1023 TiB"},
{"bytes(1PB)", IBytes(PiByte), "1.0 PiB"},
{"bytes(1PB - 1T)", IBytes(EiByte - PiByte), "1023 PiB"},
{"bytes(1EiB)", IBytes(EiByte), "1.0 EiB"},
// Overflows.
// {"bytes(1EB - 1P)", IBytes((KIByte*EIByte)-PiByte), "1023EB"},
{"bytes(5.5GiB)", IBytes(5.5 * GiByte), "5.5 GiB"},
{"bytes(5.5GB)", Bytes(5.5 * GByte), "5.5 GB"},
}.validate(t)
}
func BenchmarkParseBytes(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseBytes("16.5 GB")
}
}
func BenchmarkBytes(b *testing.B) {
for i := 0; i < b.N; i++ {
Bytes(16.5 * GByte)
}
}

108
vendor/github.com/dustin/go-humanize/comma.go generated vendored Normal file
View file

@ -0,0 +1,108 @@
package humanize
import (
"bytes"
"math"
"math/big"
"strconv"
"strings"
)
// Comma produces a string form of the given number in base 10 with
// commas after every three orders of magnitude.
//
// e.g. Comma(834142) -> 834,142
func Comma(v int64) string {
sign := ""
// Min int64 can't be negated to a usable value, so it has to be special cased.
if v == math.MinInt64 {
return "-9,223,372,036,854,775,808"
}
if v < 0 {
sign = "-"
v = 0 - v
}
parts := []string{"", "", "", "", "", "", ""}
j := len(parts) - 1
for v > 999 {
parts[j] = strconv.FormatInt(v%1000, 10)
switch len(parts[j]) {
case 2:
parts[j] = "0" + parts[j]
case 1:
parts[j] = "00" + parts[j]
}
v = v / 1000
j--
}
parts[j] = strconv.Itoa(int(v))
return sign + strings.Join(parts[j:], ",")
}
// Commaf produces a string form of the given number in base 10 with
// commas after every three orders of magnitude.
//
// e.g. Commaf(834142.32) -> 834,142.32
func Commaf(v float64) string {
buf := &bytes.Buffer{}
if v < 0 {
buf.Write([]byte{'-'})
v = 0 - v
}
comma := []byte{','}
parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".")
pos := 0
if len(parts[0])%3 != 0 {
pos += len(parts[0]) % 3
buf.WriteString(parts[0][:pos])
buf.Write(comma)
}
for ; pos < len(parts[0]); pos += 3 {
buf.WriteString(parts[0][pos : pos+3])
buf.Write(comma)
}
buf.Truncate(buf.Len() - 1)
if len(parts) > 1 {
buf.Write([]byte{'.'})
buf.WriteString(parts[1])
}
return buf.String()
}
// BigComma produces a string form of the given big.Int in base 10
// with commas after every three orders of magnitude.
func BigComma(b *big.Int) string {
sign := ""
if b.Sign() < 0 {
sign = "-"
b.Abs(b)
}
athousand := big.NewInt(1000)
c := (&big.Int{}).Set(b)
_, m := oom(c, athousand)
parts := make([]string, m+1)
j := len(parts) - 1
mod := &big.Int{}
for b.Cmp(athousand) >= 0 {
b.DivMod(b, athousand, mod)
parts[j] = strconv.FormatInt(mod.Int64(), 10)
switch len(parts[j]) {
case 2:
parts[j] = "0" + parts[j]
case 1:
parts[j] = "00" + parts[j]
}
j--
}
parts[j] = strconv.Itoa(int(b.Int64()))
return sign + strings.Join(parts[j:], ",")
}

136
vendor/github.com/dustin/go-humanize/comma_test.go generated vendored Normal file
View file

@ -0,0 +1,136 @@
package humanize
import (
"math"
"math/big"
"testing"
)
func TestCommas(t *testing.T) {
testList{
{"0", Comma(0), "0"},
{"10", Comma(10), "10"},
{"100", Comma(100), "100"},
{"1,000", Comma(1000), "1,000"},
{"10,000", Comma(10000), "10,000"},
{"100,000", Comma(100000), "100,000"},
{"10,000,000", Comma(10000000), "10,000,000"},
{"10,100,000", Comma(10100000), "10,100,000"},
{"10,010,000", Comma(10010000), "10,010,000"},
{"10,001,000", Comma(10001000), "10,001,000"},
{"123,456,789", Comma(123456789), "123,456,789"},
{"maxint", Comma(9.223372e+18), "9,223,372,000,000,000,000"},
{"math.maxint", Comma(math.MaxInt64), "9,223,372,036,854,775,807"},
{"math.minint", Comma(math.MinInt64), "-9,223,372,036,854,775,808"},
{"minint", Comma(-9.223372e+18), "-9,223,372,000,000,000,000"},
{"-123,456,789", Comma(-123456789), "-123,456,789"},
{"-10,100,000", Comma(-10100000), "-10,100,000"},
{"-10,010,000", Comma(-10010000), "-10,010,000"},
{"-10,001,000", Comma(-10001000), "-10,001,000"},
{"-10,000,000", Comma(-10000000), "-10,000,000"},
{"-100,000", Comma(-100000), "-100,000"},
{"-10,000", Comma(-10000), "-10,000"},
{"-1,000", Comma(-1000), "-1,000"},
{"-100", Comma(-100), "-100"},
{"-10", Comma(-10), "-10"},
}.validate(t)
}
func TestCommafs(t *testing.T) {
testList{
{"0", Commaf(0), "0"},
{"10.11", Commaf(10.11), "10.11"},
{"100", Commaf(100), "100"},
{"1,000", Commaf(1000), "1,000"},
{"10,000", Commaf(10000), "10,000"},
{"100,000", Commaf(100000), "100,000"},
{"834,142.32", Commaf(834142.32), "834,142.32"},
{"10,000,000", Commaf(10000000), "10,000,000"},
{"10,100,000", Commaf(10100000), "10,100,000"},
{"10,010,000", Commaf(10010000), "10,010,000"},
{"10,001,000", Commaf(10001000), "10,001,000"},
{"123,456,789", Commaf(123456789), "123,456,789"},
{"maxf64", Commaf(math.MaxFloat64), "179,769,313,486,231,570,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000"},
{"minf64", Commaf(math.SmallestNonzeroFloat64), "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005"},
{"-123,456,789", Commaf(-123456789), "-123,456,789"},
{"-10,100,000", Commaf(-10100000), "-10,100,000"},
{"-10,010,000", Commaf(-10010000), "-10,010,000"},
{"-10,001,000", Commaf(-10001000), "-10,001,000"},
{"-10,000,000", Commaf(-10000000), "-10,000,000"},
{"-100,000", Commaf(-100000), "-100,000"},
{"-10,000", Commaf(-10000), "-10,000"},
{"-1,000", Commaf(-1000), "-1,000"},
{"-100.11", Commaf(-100.11), "-100.11"},
{"-10", Commaf(-10), "-10"},
}.validate(t)
}
func BenchmarkCommas(b *testing.B) {
for i := 0; i < b.N; i++ {
Comma(1234567890)
}
}
func BenchmarkCommaf(b *testing.B) {
for i := 0; i < b.N; i++ {
Commaf(1234567890.83584)
}
}
func BenchmarkBigCommas(b *testing.B) {
for i := 0; i < b.N; i++ {
BigComma(big.NewInt(1234567890))
}
}
func bigComma(i int64) string {
return BigComma(big.NewInt(i))
}
func TestBigCommas(t *testing.T) {
testList{
{"0", bigComma(0), "0"},
{"10", bigComma(10), "10"},
{"100", bigComma(100), "100"},
{"1,000", bigComma(1000), "1,000"},
{"10,000", bigComma(10000), "10,000"},
{"100,000", bigComma(100000), "100,000"},
{"10,000,000", bigComma(10000000), "10,000,000"},
{"10,100,000", bigComma(10100000), "10,100,000"},
{"10,010,000", bigComma(10010000), "10,010,000"},
{"10,001,000", bigComma(10001000), "10,001,000"},
{"123,456,789", bigComma(123456789), "123,456,789"},
{"maxint", bigComma(9.223372e+18), "9,223,372,000,000,000,000"},
{"minint", bigComma(-9.223372e+18), "-9,223,372,000,000,000,000"},
{"-123,456,789", bigComma(-123456789), "-123,456,789"},
{"-10,100,000", bigComma(-10100000), "-10,100,000"},
{"-10,010,000", bigComma(-10010000), "-10,010,000"},
{"-10,001,000", bigComma(-10001000), "-10,001,000"},
{"-10,000,000", bigComma(-10000000), "-10,000,000"},
{"-100,000", bigComma(-100000), "-100,000"},
{"-10,000", bigComma(-10000), "-10,000"},
{"-1,000", bigComma(-1000), "-1,000"},
{"-100", bigComma(-100), "-100"},
{"-10", bigComma(-10), "-10"},
}.validate(t)
}
func TestVeryBigCommas(t *testing.T) {
tests := []struct{ in, exp string }{
{
"84889279597249724975972597249849757294578485",
"84,889,279,597,249,724,975,972,597,249,849,757,294,578,485",
},
{
"-84889279597249724975972597249849757294578485",
"-84,889,279,597,249,724,975,972,597,249,849,757,294,578,485",
},
}
for _, test := range tests {
n, _ := (&big.Int{}).SetString(test.in, 10)
got := BigComma(n)
if test.exp != got {
t.Errorf("Expected %q, got %q", test.exp, got)
}
}
}

40
vendor/github.com/dustin/go-humanize/commaf.go generated vendored Normal file
View file

@ -0,0 +1,40 @@
// +build go1.6
package humanize
import (
"bytes"
"math/big"
"strings"
)
// BigCommaf produces a string form of the given big.Float in base 10
// with commas after every three orders of magnitude.
func BigCommaf(v *big.Float) string {
buf := &bytes.Buffer{}
if v.Sign() < 0 {
buf.Write([]byte{'-'})
v.Abs(v)
}
comma := []byte{','}
parts := strings.Split(v.Text('f', -1), ".")
pos := 0
if len(parts[0])%3 != 0 {
pos += len(parts[0]) % 3
buf.WriteString(parts[0][:pos])
buf.Write(comma)
}
for ; pos < len(parts[0]); pos += 3 {
buf.WriteString(parts[0][pos : pos+3])
buf.Write(comma)
}
buf.Truncate(buf.Len() - 1)
if len(parts) > 1 {
buf.Write([]byte{'.'})
buf.WriteString(parts[1])
}
return buf.String()
}

44
vendor/github.com/dustin/go-humanize/commaf_test.go generated vendored Normal file
View file

@ -0,0 +1,44 @@
// +build go1.6
package humanize
import (
"math"
"math/big"
"testing"
)
func BenchmarkBigCommaf(b *testing.B) {
for i := 0; i < b.N; i++ {
Commaf(1234567890.83584)
}
}
func TestBigCommafs(t *testing.T) {
testList{
{"0", BigCommaf(big.NewFloat(0)), "0"},
{"10.11", BigCommaf(big.NewFloat(10.11)), "10.11"},
{"100", BigCommaf(big.NewFloat(100)), "100"},
{"1,000", BigCommaf(big.NewFloat(1000)), "1,000"},
{"10,000", BigCommaf(big.NewFloat(10000)), "10,000"},
{"100,000", BigCommaf(big.NewFloat(100000)), "100,000"},
{"834,142.32", BigCommaf(big.NewFloat(834142.32)), "834,142.32"},
{"10,000,000", BigCommaf(big.NewFloat(10000000)), "10,000,000"},
{"10,100,000", BigCommaf(big.NewFloat(10100000)), "10,100,000"},
{"10,010,000", BigCommaf(big.NewFloat(10010000)), "10,010,000"},
{"10,001,000", BigCommaf(big.NewFloat(10001000)), "10,001,000"},
{"123,456,789", BigCommaf(big.NewFloat(123456789)), "123,456,789"},
{"maxf64", BigCommaf(big.NewFloat(math.MaxFloat64)), "179,769,313,486,231,570,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000"},
{"minf64", BigCommaf(big.NewFloat(math.SmallestNonzeroFloat64)), "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004940656458412465"},
{"-123,456,789", BigCommaf(big.NewFloat(-123456789)), "-123,456,789"},
{"-10,100,000", BigCommaf(big.NewFloat(-10100000)), "-10,100,000"},
{"-10,010,000", BigCommaf(big.NewFloat(-10010000)), "-10,010,000"},
{"-10,001,000", BigCommaf(big.NewFloat(-10001000)), "-10,001,000"},
{"-10,000,000", BigCommaf(big.NewFloat(-10000000)), "-10,000,000"},
{"-100,000", BigCommaf(big.NewFloat(-100000)), "-100,000"},
{"-10,000", BigCommaf(big.NewFloat(-10000)), "-10,000"},
{"-1,000", BigCommaf(big.NewFloat(-1000)), "-1,000"},
{"-100.11", BigCommaf(big.NewFloat(-100.11)), "-100.11"},
{"-10", BigCommaf(big.NewFloat(-10)), "-10"},
}.validate(t)
}

18
vendor/github.com/dustin/go-humanize/common_test.go generated vendored Normal file
View file

@ -0,0 +1,18 @@
package humanize
import (
"testing"
)
type testList []struct {
name, got, exp string
}
func (tl testList) validate(t *testing.T) {
for _, test := range tl {
if test.got != test.exp {
t.Errorf("On %v, expected '%v', but got '%v'",
test.name, test.exp, test.got)
}
}
}

96
vendor/github.com/dustin/go-humanize/english/words.go generated vendored Normal file
View file

@ -0,0 +1,96 @@
// Package english provides utilities to generate more user-friendly English output.
package english
import (
"fmt"
"strings"
)
// These are included because they are common technical terms.
var specialPlurals = map[string]string{
"index": "indices",
"matrix": "matrices",
"vertex": "vertices",
}
var sibilantEndings = []string{"s", "sh", "tch", "x"}
var isVowel = map[byte]bool{
'A': true, 'E': true, 'I': true, 'O': true, 'U': true,
'a': true, 'e': true, 'i': true, 'o': true, 'u': true,
}
// PluralWord builds the plural form of an English word.
// The simple English rules of regular pluralization will be used
// if the plural form is an empty string (i.e. not explicitly given).
// The special cases are not guaranteed to work for strings outside ASCII.
func PluralWord(quantity int, singular, plural string) string {
if quantity == 1 {
return singular
}
if plural != "" {
return plural
}
if plural = specialPlurals[singular]; plural != "" {
return plural
}
// We need to guess what the English plural might be. Keep this
// function simple! It doesn't need to know about every possiblity;
// only regular rules and the most common special cases.
//
// Reference: http://en.wikipedia.org/wiki/English_plural
for _, ending := range sibilantEndings {
if strings.HasSuffix(singular, ending) {
return singular + "es"
}
}
l := len(singular)
if l >= 2 && singular[l-1] == 'o' && !isVowel[singular[l-2]] {
return singular + "es"
}
if l >= 2 && singular[l-1] == 'y' && !isVowel[singular[l-2]] {
return singular[:l-1] + "ies"
}
return singular + "s"
}
// Plural formats an integer and a string into a single pluralized string.
// The simple English rules of regular pluralization will be used
// if the plural form is an empty string (i.e. not explicitly given).
func Plural(quantity int, singular, plural string) string {
return fmt.Sprintf("%d %s", quantity, PluralWord(quantity, singular, plural))
}
// WordSeries converts a list of words into a word series in English.
// It returns a string containing all the given words separated by commas,
// the coordinating conjunction, and a serial comma, as appropriate.
func WordSeries(words []string, conjunction string) string {
switch len(words) {
case 0:
return ""
case 1:
return words[0]
default:
return fmt.Sprintf("%s %s %s", strings.Join(words[:len(words)-1], ", "), conjunction, words[len(words)-1])
}
}
// OxfordWordSeries converts a list of words into a word series in English,
// using an Oxford comma (https://en.wikipedia.org/wiki/Serial_comma). It
// returns a string containing all the given words separated by commas, the
// coordinating conjunction, and a serial comma, as appropriate.
func OxfordWordSeries(words []string, conjunction string) string {
switch len(words) {
case 0:
return ""
case 1:
return words[0]
case 2:
return strings.Join(words, fmt.Sprintf(" %s ", conjunction))
default:
return fmt.Sprintf("%s, %s %s", strings.Join(words[:len(words)-1], ", "), conjunction, words[len(words)-1])
}
}

View file

@ -0,0 +1,94 @@
package english
import (
"testing"
)
func TestPluralWord(t *testing.T) {
tests := []struct {
n int
singular, plural string
want string
}{
{0, "object", "", "objects"},
{1, "object", "", "object"},
{-1, "object", "", "objects"},
{42, "object", "", "objects"},
{2, "vax", "vaxen", "vaxen"},
// special cases
{2, "index", "", "indices"},
// ending in a sibilant sound
{2, "bus", "", "buses"},
{2, "bush", "", "bushes"},
{2, "watch", "", "watches"},
{2, "box", "", "boxes"},
// ending with 'o' preceded by a consonant
{2, "hero", "", "heroes"},
// ending with 'y' preceded by a consonant
{2, "lady", "", "ladies"},
{2, "day", "", "days"},
}
for _, tt := range tests {
if got := PluralWord(tt.n, tt.singular, tt.plural); got != tt.want {
t.Errorf("PluralWord(%d, %q, %q)=%q; want: %q", tt.n, tt.singular, tt.plural, got, tt.want)
}
}
}
func TestPlural(t *testing.T) {
tests := []struct {
n int
singular, plural string
want string
}{
{1, "object", "", "1 object"},
{42, "object", "", "42 objects"},
}
for _, tt := range tests {
if got := Plural(tt.n, tt.singular, tt.plural); got != tt.want {
t.Errorf("Plural(%d, %q, %q)=%q; want: %q", tt.n, tt.singular, tt.plural, got, tt.want)
}
}
}
func TestWordSeries(t *testing.T) {
tests := []struct {
words []string
conjunction string
want string
}{
{[]string{}, "and", ""},
{[]string{"foo"}, "and", "foo"},
{[]string{"foo", "bar"}, "and", "foo and bar"},
{[]string{"foo", "bar", "baz"}, "and", "foo, bar and baz"},
{[]string{"foo", "bar", "baz"}, "or", "foo, bar or baz"},
}
for _, tt := range tests {
if got := WordSeries(tt.words, tt.conjunction); got != tt.want {
t.Errorf("WordSeries(%q, %q)=%q; want: %q", tt.words, tt.conjunction, got, tt.want)
}
}
}
func TestOxfordWordSeries(t *testing.T) {
tests := []struct {
words []string
conjunction string
want string
}{
{[]string{}, "and", ""},
{[]string{"foo"}, "and", "foo"},
{[]string{"foo", "bar"}, "and", "foo and bar"},
{[]string{"foo", "bar", "baz"}, "and", "foo, bar, and baz"},
{[]string{"foo", "bar", "baz"}, "or", "foo, bar, or baz"},
}
for _, tt := range tests {
if got := OxfordWordSeries(tt.words, tt.conjunction); got != tt.want {
t.Errorf("OxfordWordSeries(%q, %q)=%q; want: %q", tt.words, tt.conjunction, got, tt.want)
}
}
}

23
vendor/github.com/dustin/go-humanize/ftoa.go generated vendored Normal file
View file

@ -0,0 +1,23 @@
package humanize
import "strconv"
func stripTrailingZeros(s string) string {
offset := len(s) - 1
for offset > 0 {
if s[offset] == '.' {
offset--
break
}
if s[offset] != '0' {
break
}
offset--
}
return s[:offset+1]
}
// Ftoa converts a float to a string with no trailing zeros.
func Ftoa(num float64) string {
return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64))
}

55
vendor/github.com/dustin/go-humanize/ftoa_test.go generated vendored Normal file
View file

@ -0,0 +1,55 @@
package humanize
import (
"fmt"
"regexp"
"strconv"
"testing"
)
func TestFtoa(t *testing.T) {
testList{
{"200", Ftoa(200), "200"},
{"2", Ftoa(2), "2"},
{"2.2", Ftoa(2.2), "2.2"},
{"2.02", Ftoa(2.02), "2.02"},
{"200.02", Ftoa(200.02), "200.02"},
}.validate(t)
}
func BenchmarkFtoaRegexTrailing(b *testing.B) {
trailingZerosRegex := regexp.MustCompile(`\.?0+$`)
b.ResetTimer()
for i := 0; i < b.N; i++ {
trailingZerosRegex.ReplaceAllString("2.00000", "")
trailingZerosRegex.ReplaceAllString("2.0000", "")
trailingZerosRegex.ReplaceAllString("2.000", "")
trailingZerosRegex.ReplaceAllString("2.00", "")
trailingZerosRegex.ReplaceAllString("2.0", "")
trailingZerosRegex.ReplaceAllString("2", "")
}
}
func BenchmarkFtoaFunc(b *testing.B) {
for i := 0; i < b.N; i++ {
stripTrailingZeros("2.00000")
stripTrailingZeros("2.0000")
stripTrailingZeros("2.000")
stripTrailingZeros("2.00")
stripTrailingZeros("2.0")
stripTrailingZeros("2")
}
}
func BenchmarkFmtF(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = fmt.Sprintf("%f", 2.03584)
}
}
func BenchmarkStrconvF(b *testing.B) {
for i := 0; i < b.N; i++ {
strconv.FormatFloat(2.03584, 'f', 6, 64)
}
}

8
vendor/github.com/dustin/go-humanize/humanize.go generated vendored Normal file
View file

@ -0,0 +1,8 @@
/*
Package humanize converts boring ugly numbers to human-friendly strings and back.
Durations can be turned into strings such as "3 days ago", numbers
representing sizes like 82854982 into useful strings like, "83 MB" or
"79 MiB" (whichever you prefer).
*/
package humanize

192
vendor/github.com/dustin/go-humanize/number.go generated vendored Normal file
View file

@ -0,0 +1,192 @@
package humanize
/*
Slightly adapted from the source to fit go-humanize.
Author: https://github.com/gorhill
Source: https://gist.github.com/gorhill/5285193
*/
import (
"math"
"strconv"
)
var (
renderFloatPrecisionMultipliers = [...]float64{
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
}
renderFloatPrecisionRounders = [...]float64{
0.5,
0.05,
0.005,
0.0005,
0.00005,
0.000005,
0.0000005,
0.00000005,
0.000000005,
0.0000000005,
}
)
// FormatFloat produces a formatted number as string based on the following user-specified criteria:
// * thousands separator
// * decimal separator
// * decimal precision
//
// Usage: s := RenderFloat(format, n)
// The format parameter tells how to render the number n.
//
// See examples: http://play.golang.org/p/LXc1Ddm1lJ
//
// Examples of format strings, given n = 12345.6789:
// "#,###.##" => "12,345.67"
// "#,###." => "12,345"
// "#,###" => "12345,678"
// "#\u202F###,##" => "12345,68"
// "#.###,###### => 12.345,678900
// "" (aka default format) => 12,345.67
//
// The highest precision allowed is 9 digits after the decimal symbol.
// There is also a version for integer number, FormatInteger(),
// which is convenient for calls within template.
func FormatFloat(format string, n float64) string {
// Special cases:
// NaN = "NaN"
// +Inf = "+Infinity"
// -Inf = "-Infinity"
if math.IsNaN(n) {
return "NaN"
}
if n > math.MaxFloat64 {
return "Infinity"
}
if n < -math.MaxFloat64 {
return "-Infinity"
}
// default format
precision := 2
decimalStr := "."
thousandStr := ","
positiveStr := ""
negativeStr := "-"
if len(format) > 0 {
format := []rune(format)
// If there is an explicit format directive,
// then default values are these:
precision = 9
thousandStr = ""
// collect indices of meaningful formatting directives
formatIndx := []int{}
for i, char := range format {
if char != '#' && char != '0' {
formatIndx = append(formatIndx, i)
}
}
if len(formatIndx) > 0 {
// Directive at index 0:
// Must be a '+'
// Raise an error if not the case
// index: 0123456789
// +0.000,000
// +000,000.0
// +0000.00
// +0000
if formatIndx[0] == 0 {
if format[formatIndx[0]] != '+' {
panic("RenderFloat(): invalid positive sign directive")
}
positiveStr = "+"
formatIndx = formatIndx[1:]
}
// Two directives:
// First is thousands separator
// Raise an error if not followed by 3-digit
// 0123456789
// 0.000,000
// 000,000.00
if len(formatIndx) == 2 {
if (formatIndx[1] - formatIndx[0]) != 4 {
panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers")
}
thousandStr = string(format[formatIndx[0]])
formatIndx = formatIndx[1:]
}
// One directive:
// Directive is decimal separator
// The number of digit-specifier following the separator indicates wanted precision
// 0123456789
// 0.00
// 000,0000
if len(formatIndx) == 1 {
decimalStr = string(format[formatIndx[0]])
precision = len(format) - formatIndx[0] - 1
}
}
}
// generate sign part
var signStr string
if n >= 0.000000001 {
signStr = positiveStr
} else if n <= -0.000000001 {
signStr = negativeStr
n = -n
} else {
signStr = ""
n = 0.0
}
// split number into integer and fractional parts
intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision])
// generate integer part string
intStr := strconv.FormatInt(int64(intf), 10)
// add thousand separator if required
if len(thousandStr) > 0 {
for i := len(intStr); i > 3; {
i -= 3
intStr = intStr[:i] + thousandStr + intStr[i:]
}
}
// no fractional part, we can leave now
if precision == 0 {
return signStr + intStr
}
// generate fractional part
fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision]))
// may need padding
if len(fracStr) < precision {
fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr
}
return signStr + intStr + decimalStr + fracStr
}
// FormatInteger produces a formatted number as string.
// See FormatFloat.
func FormatInteger(format string, n int) string {
return FormatFloat(format, float64(n))
}

79
vendor/github.com/dustin/go-humanize/number_test.go generated vendored Normal file
View file

@ -0,0 +1,79 @@
package humanize
import (
"math"
"testing"
)
type TestStruct struct {
name string
format string
num float64
formatted string
}
func TestFormatFloat(t *testing.T) {
tests := []TestStruct{
{"default", "", 12345.6789, "12,345.68"},
{"#", "#", 12345.6789, "12345.678900000"},
{"#.", "#.", 12345.6789, "12346"},
{"#,#", "#,#", 12345.6789, "12345,7"},
{"#,##", "#,##", 12345.6789, "12345,68"},
{"#,###", "#,###", 12345.6789, "12345,679"},
{"#,###.", "#,###.", 12345.6789, "12,346"},
{"#,###.##", "#,###.##", 12345.6789, "12,345.68"},
{"#,###.###", "#,###.###", 12345.6789, "12,345.679"},
{"#,###.####", "#,###.####", 12345.6789, "12,345.6789"},
{"#.###,######", "#.###,######", 12345.6789, "12.345,678900"},
{"bug46", "#,###.##", 52746220055.92342, "52,746,220,055.92"},
{"#\u202f###,##", "#\u202f###,##", 12345.6789, "12345,68"},
// special cases
{"NaN", "#", math.NaN(), "NaN"},
{"+Inf", "#", math.Inf(1), "Infinity"},
{"-Inf", "#", math.Inf(-1), "-Infinity"},
{"signStr <= -0.000000001", "", -0.000000002, "-0.00"},
{"signStr = 0", "", 0, "0.00"},
{"Format directive must start with +", "+000", 12345.6789, "+12345.678900000"},
}
for _, test := range tests {
got := FormatFloat(test.format, test.num)
if got != test.formatted {
t.Errorf("On %v (%v, %v), got %v, wanted %v",
test.name, test.format, test.num, got, test.formatted)
}
}
// Test a single integer
got := FormatInteger("#", 12345)
if got != "12345.000000000" {
t.Errorf("On %v (%v, %v), got %v, wanted %v",
"integerTest", "#", 12345, got, "12345.000000000")
}
// Test the things that could panic
panictests := []TestStruct{
{"RenderFloat(): invalid positive sign directive", "-", 12345.6789, "12,345.68"},
{"RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers", "0.01", 12345.6789, "12,345.68"},
}
for _, test := range panictests {
didPanic := false
var message interface{}
func() {
defer func() {
if message = recover(); message != nil {
didPanic = true
}
}()
// call the target function
_ = FormatFloat(test.format, test.num)
}()
if didPanic != true {
t.Errorf("On %v, should have panic and did not.",
test.name)
}
}
}

25
vendor/github.com/dustin/go-humanize/ordinals.go generated vendored Normal file
View file

@ -0,0 +1,25 @@
package humanize
import "strconv"
// Ordinal gives you the input number in a rank/ordinal format.
//
// Ordinal(3) -> 3rd
func Ordinal(x int) string {
suffix := "th"
switch x % 10 {
case 1:
if x%100 != 11 {
suffix = "st"
}
case 2:
if x%100 != 12 {
suffix = "nd"
}
case 3:
if x%100 != 13 {
suffix = "rd"
}
}
return strconv.Itoa(x) + suffix
}

22
vendor/github.com/dustin/go-humanize/ordinals_test.go generated vendored Normal file
View file

@ -0,0 +1,22 @@
package humanize
import (
"testing"
)
func TestOrdinals(t *testing.T) {
testList{
{"0", Ordinal(0), "0th"},
{"1", Ordinal(1), "1st"},
{"2", Ordinal(2), "2nd"},
{"3", Ordinal(3), "3rd"},
{"4", Ordinal(4), "4th"},
{"10", Ordinal(10), "10th"},
{"11", Ordinal(11), "11th"},
{"12", Ordinal(12), "12th"},
{"13", Ordinal(13), "13th"},
{"101", Ordinal(101), "101st"},
{"102", Ordinal(102), "102nd"},
{"103", Ordinal(103), "103rd"},
}.validate(t)
}

113
vendor/github.com/dustin/go-humanize/si.go generated vendored Normal file
View file

@ -0,0 +1,113 @@
package humanize
import (
"errors"
"math"
"regexp"
"strconv"
)
var siPrefixTable = map[float64]string{
-24: "y", // yocto
-21: "z", // zepto
-18: "a", // atto
-15: "f", // femto
-12: "p", // pico
-9: "n", // nano
-6: "µ", // micro
-3: "m", // milli
0: "",
3: "k", // kilo
6: "M", // mega
9: "G", // giga
12: "T", // tera
15: "P", // peta
18: "E", // exa
21: "Z", // zetta
24: "Y", // yotta
}
var revSIPrefixTable = revfmap(siPrefixTable)
// revfmap reverses the map and precomputes the power multiplier
func revfmap(in map[float64]string) map[string]float64 {
rv := map[string]float64{}
for k, v := range in {
rv[v] = math.Pow(10, k)
}
return rv
}
var riParseRegex *regexp.Regexp
func init() {
ri := `^([\-0-9.]+)\s?([`
for _, v := range siPrefixTable {
ri += v
}
ri += `]?)(.*)`
riParseRegex = regexp.MustCompile(ri)
}
// ComputeSI finds the most appropriate SI prefix for the given number
// and returns the prefix along with the value adjusted to be within
// that prefix.
//
// See also: SI, ParseSI.
//
// e.g. ComputeSI(2.2345e-12) -> (2.2345, "p")
func ComputeSI(input float64) (float64, string) {
if input == 0 {
return 0, ""
}
mag := math.Abs(input)
exponent := math.Floor(logn(mag, 10))
exponent = math.Floor(exponent/3) * 3
value := mag / math.Pow(10, exponent)
// Handle special case where value is exactly 1000.0
// Should return 1 M instead of 1000 k
if value == 1000.0 {
exponent += 3
value = mag / math.Pow(10, exponent)
}
value = math.Copysign(value, input)
prefix := siPrefixTable[exponent]
return value, prefix
}
// SI returns a string with default formatting.
//
// SI uses Ftoa to format float value, removing trailing zeros.
//
// See also: ComputeSI, ParseSI.
//
// e.g. SI(1000000, "B") -> 1 MB
// e.g. SI(2.2345e-12, "F") -> 2.2345 pF
func SI(input float64, unit string) string {
value, prefix := ComputeSI(input)
return Ftoa(value) + " " + prefix + unit
}
var errInvalid = errors.New("invalid input")
// ParseSI parses an SI string back into the number and unit.
//
// See also: SI, ComputeSI.
//
// e.g. ParseSI("2.2345 pF") -> (2.2345e-12, "F", nil)
func ParseSI(input string) (float64, string, error) {
found := riParseRegex.FindStringSubmatch(input)
if len(found) != 4 {
return 0, "", errInvalid
}
mag := revSIPrefixTable[found[2]]
unit := found[3]
base, err := strconv.ParseFloat(found[1], 64)
return base * mag, unit, err
}

101
vendor/github.com/dustin/go-humanize/si_test.go generated vendored Normal file
View file

@ -0,0 +1,101 @@
package humanize
import (
"math"
"testing"
)
func TestSI(t *testing.T) {
tests := []struct {
name string
num float64
formatted string
}{
{"e-24", 1e-24, "1 yF"},
{"e-21", 1e-21, "1 zF"},
{"e-18", 1e-18, "1 aF"},
{"e-15", 1e-15, "1 fF"},
{"e-12", 1e-12, "1 pF"},
{"e-12", 2.2345e-12, "2.2345 pF"},
{"e-12", 2.23e-12, "2.23 pF"},
{"e-11", 2.23e-11, "22.3 pF"},
{"e-10", 2.2e-10, "220 pF"},
{"e-9", 2.2e-9, "2.2 nF"},
{"e-8", 2.2e-8, "22 nF"},
{"e-7", 2.2e-7, "220 nF"},
{"e-6", 2.2e-6, "2.2 µF"},
{"e-6", 1e-6, "1 µF"},
{"e-5", 2.2e-5, "22 µF"},
{"e-4", 2.2e-4, "220 µF"},
{"e-3", 2.2e-3, "2.2 mF"},
{"e-2", 2.2e-2, "22 mF"},
{"e-1", 2.2e-1, "220 mF"},
{"e+0", 2.2e-0, "2.2 F"},
{"e+0", 2.2, "2.2 F"},
{"e+1", 2.2e+1, "22 F"},
{"0", 0, "0 F"},
{"e+1", 22, "22 F"},
{"e+2", 2.2e+2, "220 F"},
{"e+2", 220, "220 F"},
{"e+3", 2.2e+3, "2.2 kF"},
{"e+3", 2200, "2.2 kF"},
{"e+4", 2.2e+4, "22 kF"},
{"e+4", 22000, "22 kF"},
{"e+5", 2.2e+5, "220 kF"},
{"e+6", 2.2e+6, "2.2 MF"},
{"e+6", 1e+6, "1 MF"},
{"e+7", 2.2e+7, "22 MF"},
{"e+8", 2.2e+8, "220 MF"},
{"e+9", 2.2e+9, "2.2 GF"},
{"e+10", 2.2e+10, "22 GF"},
{"e+11", 2.2e+11, "220 GF"},
{"e+12", 2.2e+12, "2.2 TF"},
{"e+15", 2.2e+15, "2.2 PF"},
{"e+18", 2.2e+18, "2.2 EF"},
{"e+21", 2.2e+21, "2.2 ZF"},
{"e+24", 2.2e+24, "2.2 YF"},
// special case
{"1F", 1000 * 1000, "1 MF"},
{"1F", 1e6, "1 MF"},
// negative number
{"-100 F", -100, "-100 F"},
}
for _, test := range tests {
got := SI(test.num, "F")
if got != test.formatted {
t.Errorf("On %v (%v), got %v, wanted %v",
test.name, test.num, got, test.formatted)
}
gotf, gotu, err := ParseSI(test.formatted)
if err != nil {
t.Errorf("Error parsing %v (%v): %v", test.name, test.formatted, err)
continue
}
if math.Abs(1-(gotf/test.num)) > 0.01 {
t.Errorf("On %v (%v), got %v, wanted %v (±%v)",
test.name, test.formatted, gotf, test.num,
math.Abs(1-(gotf/test.num)))
}
if gotu != "F" {
t.Errorf("On %v (%v), expected unit F, got %v",
test.name, test.formatted, gotu)
}
}
// Parse error
gotf, gotu, err := ParseSI("x1.21JW") // 1.21 jigga whats
if err == nil {
t.Errorf("Expected error on x1.21JW, got %v %v", gotf, gotu)
}
}
func BenchmarkParseSI(b *testing.B) {
for i := 0; i < b.N; i++ {
ParseSI("2.2346ZB")
}
}

117
vendor/github.com/dustin/go-humanize/times.go generated vendored Normal file
View file

@ -0,0 +1,117 @@
package humanize
import (
"fmt"
"math"
"sort"
"time"
)
// Seconds-based time units
const (
Day = 24 * time.Hour
Week = 7 * Day
Month = 30 * Day
Year = 12 * Month
LongTime = 37 * Year
)
// Time formats a time into a relative string.
//
// Time(someT) -> "3 weeks ago"
func Time(then time.Time) string {
return RelTime(then, time.Now(), "ago", "from now")
}
// A RelTimeMagnitude struct contains a relative time point at which
// the relative format of time will switch to a new format string. A
// slice of these in ascending order by their "D" field is passed to
// CustomRelTime to format durations.
//
// The Format field is a string that may contain a "%s" which will be
// replaced with the appropriate signed label (e.g. "ago" or "from
// now") and a "%d" that will be replaced by the quantity.
//
// The DivBy field is the amount of time the time difference must be
// divided by in order to display correctly.
//
// e.g. if D is 2*time.Minute and you want to display "%d minutes %s"
// DivBy should be time.Minute so whatever the duration is will be
// expressed in minutes.
type RelTimeMagnitude struct {
D time.Duration
Format string
DivBy time.Duration
}
var defaultMagnitudes = []RelTimeMagnitude{
{time.Second, "now", time.Second},
{2 * time.Second, "1 second %s", 1},
{time.Minute, "%d seconds %s", time.Second},
{2 * time.Minute, "1 minute %s", 1},
{time.Hour, "%d minutes %s", time.Minute},
{2 * time.Hour, "1 hour %s", 1},
{Day, "%d hours %s", time.Hour},
{2 * Day, "1 day %s", 1},
{Week, "%d days %s", Day},
{2 * Week, "1 week %s", 1},
{Month, "%d weeks %s", Week},
{2 * Month, "1 month %s", 1},
{Year, "%d months %s", Month},
{18 * Month, "1 year %s", 1},
{2 * Year, "2 years %s", 1},
{LongTime, "%d years %s", Year},
{math.MaxInt64, "a long while %s", 1},
}
// RelTime formats a time into a relative string.
//
// It takes two times and two labels. In addition to the generic time
// delta string (e.g. 5 minutes), the labels are used applied so that
// the label corresponding to the smaller time is applied.
//
// RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier"
func RelTime(a, b time.Time, albl, blbl string) string {
return CustomRelTime(a, b, albl, blbl, defaultMagnitudes)
}
// CustomRelTime formats a time into a relative string.
//
// It takes two times two labels and a table of relative time formats.
// In addition to the generic time delta string (e.g. 5 minutes), the
// labels are used applied so that the label corresponding to the
// smaller time is applied.
func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string {
lbl := albl
diff := b.Sub(a)
if a.After(b) {
lbl = blbl
diff = a.Sub(b)
}
n := sort.Search(len(magnitudes), func(i int) bool {
return magnitudes[i].D > diff
})
if n >= len(magnitudes) {
n = len(magnitudes) - 1
}
mag := magnitudes[n]
args := []interface{}{}
escaped := false
for _, ch := range mag.Format {
if escaped {
switch ch {
case 's':
args = append(args, lbl)
case 'd':
args = append(args, diff/mag.DivBy)
}
escaped = false
} else {
escaped = ch == '%'
}
}
return fmt.Sprintf(mag.Format, args...)
}

124
vendor/github.com/dustin/go-humanize/times_test.go generated vendored Normal file
View file

@ -0,0 +1,124 @@
package humanize
import (
"math"
"testing"
"time"
)
func TestPast(t *testing.T) {
now := time.Now()
testList{
{"now", Time(now), "now"},
{"1 second ago", Time(now.Add(-1 * time.Second)), "1 second ago"},
{"12 seconds ago", Time(now.Add(-12 * time.Second)), "12 seconds ago"},
{"30 seconds ago", Time(now.Add(-30 * time.Second)), "30 seconds ago"},
{"45 seconds ago", Time(now.Add(-45 * time.Second)), "45 seconds ago"},
{"1 minute ago", Time(now.Add(-63 * time.Second)), "1 minute ago"},
{"15 minutes ago", Time(now.Add(-15 * time.Minute)), "15 minutes ago"},
{"1 hour ago", Time(now.Add(-63 * time.Minute)), "1 hour ago"},
{"2 hours ago", Time(now.Add(-2 * time.Hour)), "2 hours ago"},
{"21 hours ago", Time(now.Add(-21 * time.Hour)), "21 hours ago"},
{"1 day ago", Time(now.Add(-26 * time.Hour)), "1 day ago"},
{"2 days ago", Time(now.Add(-49 * time.Hour)), "2 days ago"},
{"3 days ago", Time(now.Add(-3 * Day)), "3 days ago"},
{"1 week ago (1)", Time(now.Add(-7 * Day)), "1 week ago"},
{"1 week ago (2)", Time(now.Add(-12 * Day)), "1 week ago"},
{"2 weeks ago", Time(now.Add(-15 * Day)), "2 weeks ago"},
{"1 month ago", Time(now.Add(-39 * Day)), "1 month ago"},
{"3 months ago", Time(now.Add(-99 * Day)), "3 months ago"},
{"1 year ago (1)", Time(now.Add(-365 * Day)), "1 year ago"},
{"1 year ago (1)", Time(now.Add(-400 * Day)), "1 year ago"},
{"2 years ago (1)", Time(now.Add(-548 * Day)), "2 years ago"},
{"2 years ago (2)", Time(now.Add(-725 * Day)), "2 years ago"},
{"2 years ago (3)", Time(now.Add(-800 * Day)), "2 years ago"},
{"3 years ago", Time(now.Add(-3 * Year)), "3 years ago"},
{"long ago", Time(now.Add(-LongTime)), "a long while ago"},
}.validate(t)
}
func TestReltimeOffbyone(t *testing.T) {
testList{
{"1w-1", RelTime(time.Unix(0, 0), time.Unix(7*24*60*60, -1), "ago", ""), "6 days ago"},
{"1w±0", RelTime(time.Unix(0, 0), time.Unix(7*24*60*60, 0), "ago", ""), "1 week ago"},
{"1w+1", RelTime(time.Unix(0, 0), time.Unix(7*24*60*60, 1), "ago", ""), "1 week ago"},
{"2w-1", RelTime(time.Unix(0, 0), time.Unix(14*24*60*60, -1), "ago", ""), "1 week ago"},
{"2w±0", RelTime(time.Unix(0, 0), time.Unix(14*24*60*60, 0), "ago", ""), "2 weeks ago"},
{"2w+1", RelTime(time.Unix(0, 0), time.Unix(14*24*60*60, 1), "ago", ""), "2 weeks ago"},
}.validate(t)
}
func TestFuture(t *testing.T) {
// Add a little time so that these things properly line up in
// the future.
now := time.Now().Add(time.Millisecond * 250)
testList{
{"now", Time(now), "now"},
{"1 second from now", Time(now.Add(+1 * time.Second)), "1 second from now"},
{"12 seconds from now", Time(now.Add(+12 * time.Second)), "12 seconds from now"},
{"30 seconds from now", Time(now.Add(+30 * time.Second)), "30 seconds from now"},
{"45 seconds from now", Time(now.Add(+45 * time.Second)), "45 seconds from now"},
{"15 minutes from now", Time(now.Add(+15 * time.Minute)), "15 minutes from now"},
{"2 hours from now", Time(now.Add(+2 * time.Hour)), "2 hours from now"},
{"21 hours from now", Time(now.Add(+21 * time.Hour)), "21 hours from now"},
{"1 day from now", Time(now.Add(+26 * time.Hour)), "1 day from now"},
{"2 days from now", Time(now.Add(+49 * time.Hour)), "2 days from now"},
{"3 days from now", Time(now.Add(+3 * Day)), "3 days from now"},
{"1 week from now (1)", Time(now.Add(+7 * Day)), "1 week from now"},
{"1 week from now (2)", Time(now.Add(+12 * Day)), "1 week from now"},
{"2 weeks from now", Time(now.Add(+15 * Day)), "2 weeks from now"},
{"1 month from now", Time(now.Add(+30 * Day)), "1 month from now"},
{"1 year from now", Time(now.Add(+365 * Day)), "1 year from now"},
{"2 years from now", Time(now.Add(+2 * Year)), "2 years from now"},
{"a while from now", Time(now.Add(+LongTime)), "a long while from now"},
}.validate(t)
}
func TestRange(t *testing.T) {
start := time.Time{}
end := time.Unix(math.MaxInt64, math.MaxInt64)
x := RelTime(start, end, "ago", "from now")
if x != "a long while from now" {
t.Errorf("Expected a long while from now, got %q", x)
}
}
func TestCustomRelTime(t *testing.T) {
now := time.Now().Add(time.Millisecond * 250)
magnitudes := []RelTimeMagnitude{
{time.Second, "now", time.Second},
{2 * time.Second, "1 second %s", 1},
{time.Minute, "%d seconds %s", time.Second},
{Day - time.Second, "%d minutes %s", time.Minute},
{Day, "%d hours %s", time.Hour},
{2 * Day, "1 day %s", 1},
{Week, "%d days %s", Day},
{2 * Week, "1 week %s", 1},
{6 * Month, "%d weeks %s", Week},
{Year, "%d months %s", Month},
}
customRelTime := func(then time.Time) string {
return CustomRelTime(then, time.Now(), "ago", "from now", magnitudes)
}
testList{
{"now", customRelTime(now), "now"},
{"1 second from now", customRelTime(now.Add(+1 * time.Second)), "1 second from now"},
{"12 seconds from now", customRelTime(now.Add(+12 * time.Second)), "12 seconds from now"},
{"30 seconds from now", customRelTime(now.Add(+30 * time.Second)), "30 seconds from now"},
{"45 seconds from now", customRelTime(now.Add(+45 * time.Second)), "45 seconds from now"},
{"15 minutes from now", customRelTime(now.Add(+15 * time.Minute)), "15 minutes from now"},
{"2 hours from now", customRelTime(now.Add(+2 * time.Hour)), "120 minutes from now"},
{"21 hours from now", customRelTime(now.Add(+21 * time.Hour)), "1260 minutes from now"},
{"1 day from now", customRelTime(now.Add(+26 * time.Hour)), "1 day from now"},
{"2 days from now", customRelTime(now.Add(+49 * time.Hour)), "2 days from now"},
{"3 days from now", customRelTime(now.Add(+3 * Day)), "3 days from now"},
{"1 week from now (1)", customRelTime(now.Add(+7 * Day)), "1 week from now"},
{"1 week from now (2)", customRelTime(now.Add(+12 * Day)), "1 week from now"},
{"2 weeks from now", customRelTime(now.Add(+15 * Day)), "2 weeks from now"},
{"1 month from now", customRelTime(now.Add(+30 * Day)), "4 weeks from now"},
{"6 months from now", customRelTime(now.Add(+6*Month - time.Second)), "25 weeks from now"},
{"1 year from now", customRelTime(now.Add(+365 * Day)), "12 months from now"},
{"2 years from now", customRelTime(now.Add(+2 * Year)), "24 months from now"},
{"a while from now", customRelTime(now.Add(+LongTime)), "444 months from now"},
}.validate(t)
}

2
vendor/github.com/elazarl/goproxy/.gitignore generated vendored Normal file
View file

@ -0,0 +1,2 @@
bin
*.swp

27
vendor/github.com/elazarl/goproxy/LICENSE generated vendored Normal file
View file

@ -0,0 +1,27 @@
Copyright (c) 2012 Elazar Leibovich. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Elazar Leibovich. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

122
vendor/github.com/elazarl/goproxy/README.md generated vendored Normal file
View file

@ -0,0 +1,122 @@
# Introduction
[![GoDoc](https://godoc.org/github.com/elazarl/goproxy?status.svg)](https://godoc.org/github.com/elazarl/goproxy)
[![Join the chat at https://gitter.im/elazarl/goproxy](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/elazarl/goproxy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Package goproxy provides a customizable HTTP proxy library for Go (golang),
It supports regular HTTP proxy, HTTPS through CONNECT, and "hijacking" HTTPS
connection using "Man in the Middle" style attack.
The intent of the proxy, is to be usable with reasonable amount of traffic
yet, customizable and programmable.
The proxy itself is simply a `net/http` handler.
In order to use goproxy, one should set their browser to use goproxy as an HTTP
proxy. Here is how you do that [in Chrome](https://support.google.com/chrome/answer/96815?hl=en)
and [in Firefox](http://www.wikihow.com/Enter-Proxy-Settings-in-Firefox).
For example, the URL you should use as proxy when running `./bin/basic` is
`localhost:8080`, as this is the default binding for the basic proxy.
## Mailing List
New features would be discussed on the [mailing list](https://groups.google.com/forum/#!forum/goproxy-dev)
before their development.
## Latest Stable Release
Get the latest goproxy from `gopkg.in/elazarl/goproxy.v1`.
# Why not Fiddler2?
Fiddler is an excellent software with similar intent. However, Fiddler is not
as customizable as goproxy intend to be. The main difference is, Fiddler is not
intended to be used as a real proxy.
A possible use case that suits goproxy but
not Fiddler, is, gathering statistics on page load times for a certain website over a week.
With goproxy you could ask all your users to set their proxy to a dedicated machine running a
goproxy server. Fiddler is a GUI app not designed to be ran like a server for multiple users.
# A taste of goproxy
To get a taste of `goproxy`, a basic HTTP/HTTPS transparent proxy
```go
package main
import (
"github.com/elazarl/goproxy"
"log"
"net/http"
)
func main() {
proxy := goproxy.NewProxyHttpServer()
proxy.Verbose = true
log.Fatal(http.ListenAndServe(":8080", proxy))
}
```
This line will add `X-GoProxy: yxorPoG-X` header to all requests sent through the proxy
```go
proxy.OnRequest().DoFunc(
func(r *http.Request,ctx *goproxy.ProxyCtx)(*http.Request,*http.Response) {
r.Header.Set("X-GoProxy","yxorPoG-X")
return r,nil
})
```
`DoFunc` will process all incoming requests to the proxy. It will add a header to the request
and return it. The proxy will send the modified request.
Note that we returned nil value as the response. Had we returned a response, goproxy would
have discarded the request and sent the new response to the client.
In order to refuse connections to reddit at work time
```go
proxy.OnRequest(goproxy.DstHostIs("www.reddit.com")).DoFunc(
func(r *http.Request,ctx *goproxy.ProxyCtx)(*http.Request,*http.Response) {
if h,_,_ := time.Now().Clock(); h >= 8 && h <= 17 {
return r,goproxy.NewResponse(r,
goproxy.ContentTypeText,http.StatusForbidden,
"Don't waste your time!")
}
return r,nil
})
```
`DstHostIs` returns a `ReqCondition`, that is a function receiving a `Request` and returning a boolean
we will only process requests that matches the condition. `DstHostIs("www.reddit.com")` will return
a `ReqCondition` accepting only requests directed to "www.reddit.com".
`DoFunc` will receive a function that will preprocess the request. We can change the request, or
return a response. If the time is between 8:00am and 17:00pm, we will neglect the request, and
return a precanned text response saying "do not waste your time".
See additional examples in the examples directory.
# What's New
1. Ability to `Hijack` CONNECT requests. See
[the eavesdropper example](https://github.com/elazarl/goproxy/blob/master/examples/goproxy-eavesdropper/main.go#L27)
2. Transparent proxy support for http/https including MITM certificate generation for TLS. See the [transparent example.](https://github.com/elazarl/goproxy/tree/master/examples/goproxy-transparent)
# License
I put the software temporarily under the Go-compatible BSD license,
if this prevents someone from using the software, do let me know and I'll consider changing it.
At any rate, user feedback is very important for me, so I'll be delighted to know if you're using this package.
# Beta Software
I've received a positive feedback from a few people who use goproxy in production settings.
I believe it is good enough for usage.
I'll try to keep reasonable backwards compatibility. In case of a major API change,
I'll change the import path.

57
vendor/github.com/elazarl/goproxy/actions.go generated vendored Normal file
View file

@ -0,0 +1,57 @@
package goproxy
import "net/http"
// ReqHandler will "tamper" with the request coming to the proxy server
// If Handle returns req,nil the proxy will send the returned request
// to the destination server. If it returns nil,resp the proxy will
// skip sending any requests, and will simply return the response `resp`
// to the client.
type ReqHandler interface {
Handle(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response)
}
// A wrapper that would convert a function to a ReqHandler interface type
type FuncReqHandler func(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response)
// FuncReqHandler.Handle(req,ctx) <=> FuncReqHandler(req,ctx)
func (f FuncReqHandler) Handle(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response) {
return f(req, ctx)
}
// after the proxy have sent the request to the destination server, it will
// "filter" the response through the RespHandlers it has.
// The proxy server will send to the client the response returned by the RespHandler.
// In case of error, resp will be nil, and ctx.RoundTrip.Error will contain the error
type RespHandler interface {
Handle(resp *http.Response, ctx *ProxyCtx) *http.Response
}
// A wrapper that would convert a function to a RespHandler interface type
type FuncRespHandler func(resp *http.Response, ctx *ProxyCtx) *http.Response
// FuncRespHandler.Handle(req,ctx) <=> FuncRespHandler(req,ctx)
func (f FuncRespHandler) Handle(resp *http.Response, ctx *ProxyCtx) *http.Response {
return f(resp, ctx)
}
// When a client send a CONNECT request to a host, the request is filtered through
// all the HttpsHandlers the proxy has, and if one returns true, the connection is
// sniffed using Man in the Middle attack.
// That is, the proxy will create a TLS connection with the client, another TLS
// connection with the destination the client wished to connect to, and would
// send back and forth all messages from the server to the client and vice versa.
// The request and responses sent in this Man In the Middle channel are filtered
// through the usual flow (request and response filtered through the ReqHandlers
// and RespHandlers)
type HttpsHandler interface {
HandleConnect(req string, ctx *ProxyCtx) (*ConnectAction, string)
}
// A wrapper that would convert a function to a HttpsHandler interface type
type FuncHttpsHandler func(host string, ctx *ProxyCtx) (*ConnectAction, string)
// FuncHttpsHandler should implement the RespHandler interface
func (f FuncHttpsHandler) HandleConnect(host string, ctx *ProxyCtx) (*ConnectAction, string) {
return f(host, ctx)
}

15
vendor/github.com/elazarl/goproxy/all.bash generated vendored Executable file
View file

@ -0,0 +1,15 @@
#!/bin/bash
go test || exit
for action in $@; do go $action; done
mkdir -p bin
find regretable examples/* ext/* -maxdepth 0 -type d | while read d; do
(cd $d
go build -o ../../bin/$(basename $d)
find *_test.go -maxdepth 0 2>/dev/null|while read f;do
for action in $@; do go $action; done
go test
break
done)
done

34
vendor/github.com/elazarl/goproxy/ca.pem generated vendored Normal file
View file

@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
MIIF9DCCA9ygAwIBAgIJAODqYUwoVjJkMA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD
VQQGEwJJTDEPMA0GA1UECAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoM
B0dvUHJveHkxEDAOBgNVBAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0
aHViLmlvMSAwHgYJKoZIhvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTAeFw0xNzA0
MDUyMDAwMTBaFw0zNzAzMzEyMDAwMTBaMIGOMQswCQYDVQQGEwJJTDEPMA0GA1UE
CAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoMB0dvUHJveHkxEDAOBgNV
BAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0aHViLmlvMSAwHgYJKoZI
hvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
ADCCAgoCggIBAJ4Qy+H6hhoY1s0QRcvIhxrjSHaO/RbaFj3rwqcnpOgFq07gRdI9
3c0TFKQJHpgv6feLRhEvX/YllFYu4J35lM9ZcYY4qlKFuStcX8Jm8fqpgtmAMBzP
sqtqDi8M9RQGKENzU9IFOnCV7SAeh45scMuI3wz8wrjBcH7zquHkvqUSYZz035t9
V6WTrHyTEvT4w+lFOVN2bA/6DAIxrjBiF6DhoJqnha0SZtDfv77XpwGG3EhA/qoh
hiYrDruYK7zJdESQL44LwzMPupVigqalfv+YHfQjbhT951IVurW2NJgRyBE62dLr
lHYdtT9tCTCrd+KJNMJ+jp9hAjdIu1Br/kifU4F4+4ZLMR9Ueji0GkkPKsYdyMnq
j0p0PogyvP1l4qmboPImMYtaoFuYmMYlebgC9LN10bL91K4+jLt0I1YntEzrqgJo
WsJztYDw543NzSy5W+/cq4XRYgtq1b0RWwuUiswezmMoeyHZ8BQJe2xMjAOllASD
fqa8OK3WABHJpy4zUrnUBiMuPITzD/FuDx4C5IwwlC68gHAZblNqpBZCX0nFCtKj
YOcI2So5HbQ2OC8QF+zGVuduHUSok4hSy2BBfZ1pfvziqBeetWJwFvapGB44nIHh
WKNKvqOxLNIy7e+TGRiWOomrAWM18VSR9LZbBxpJK7PLSzWqYJYTRCZHAgMBAAGj
UzBRMB0GA1UdDgQWBBR4uDD9Y6x7iUoHO+32ioOcw1ICZTAfBgNVHSMEGDAWgBR4
uDD9Y6x7iUoHO+32ioOcw1ICZTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB
CwUAA4ICAQAaCEupzGGqcdh+L7BzhX7zyd7yzAKUoLxFrxaZY34Xyj3lcx1XoK6F
AqsH2JM25GixgadzhNt92JP7vzoWeHZtLfstrPS638Y1zZi6toy4E49viYjFk5J0
C6ZcFC04VYWWx6z0HwJuAS08tZ37JuFXpJGfXJOjZCQyxse0Lg0tuKLMeXDCk2Y3
Ba0noeuNyHRoWXXPyiUoeApkVCU5gIsyiJSWOjhJ5hpJG06rQNfNYexgKrrraEin
o0jmEMtJMx5TtD83hSnLCnFGBBq5lkE7jgXME1KsbIE3lJZzRX1mQwUK8CJDYxye
i6M/dzSvy0SsPvz8fTAlprXRtWWtJQmxgWENp3Dv+0Pmux/l+ilk7KA4sMXGhsfr
bvTOeWl1/uoFTPYiWR/ww7QEPLq23yDFY04Q7Un0qjIk8ExvaY8lCkXMgc8i7sGY
VfvOYb0zm67EfAQl3TW8Ky5fl5CcxpVCD360Bzi6hwjYixa3qEeBggOixFQBFWft
8wrkKTHpOQXjn4sDPtet8imm9UYEtzWrFX6T9MFYkBR0/yye0FIh9+YPiTA6WB86
NCNwK5Yl6HuvF97CIH5CdgO+5C7KifUtqTOL8pQKbNwy0S3sNYvB+njGvRpR7pKV
BUnFpB/Atptqr4CUlTXrc5IPLAqAfmwk5IKcwy3EXUbruf9Dwz69YA==
-----END CERTIFICATE-----

111
vendor/github.com/elazarl/goproxy/certs.go generated vendored Normal file
View file

@ -0,0 +1,111 @@
package goproxy
import (
"crypto/tls"
"crypto/x509"
)
func init() {
if goproxyCaErr != nil {
panic("Error parsing builtin CA " + goproxyCaErr.Error())
}
var err error
if GoproxyCa.Leaf, err = x509.ParseCertificate(GoproxyCa.Certificate[0]); err != nil {
panic("Error parsing builtin CA " + err.Error())
}
}
var tlsClientSkipVerify = &tls.Config{InsecureSkipVerify: true}
var defaultTLSConfig = &tls.Config{
InsecureSkipVerify: true,
}
var CA_CERT = []byte(`-----BEGIN CERTIFICATE-----
MIIF9DCCA9ygAwIBAgIJAODqYUwoVjJkMA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD
VQQGEwJJTDEPMA0GA1UECAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoM
B0dvUHJveHkxEDAOBgNVBAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0
aHViLmlvMSAwHgYJKoZIhvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTAeFw0xNzA0
MDUyMDAwMTBaFw0zNzAzMzEyMDAwMTBaMIGOMQswCQYDVQQGEwJJTDEPMA0GA1UE
CAwGQ2VudGVyMQwwCgYDVQQHDANMb2QxEDAOBgNVBAoMB0dvUHJveHkxEDAOBgNV
BAsMB0dvUHJveHkxGjAYBgNVBAMMEWdvcHJveHkuZ2l0aHViLmlvMSAwHgYJKoZI
hvcNAQkBFhFlbGF6YXJsQGdtYWlsLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
ADCCAgoCggIBAJ4Qy+H6hhoY1s0QRcvIhxrjSHaO/RbaFj3rwqcnpOgFq07gRdI9
3c0TFKQJHpgv6feLRhEvX/YllFYu4J35lM9ZcYY4qlKFuStcX8Jm8fqpgtmAMBzP
sqtqDi8M9RQGKENzU9IFOnCV7SAeh45scMuI3wz8wrjBcH7zquHkvqUSYZz035t9
V6WTrHyTEvT4w+lFOVN2bA/6DAIxrjBiF6DhoJqnha0SZtDfv77XpwGG3EhA/qoh
hiYrDruYK7zJdESQL44LwzMPupVigqalfv+YHfQjbhT951IVurW2NJgRyBE62dLr
lHYdtT9tCTCrd+KJNMJ+jp9hAjdIu1Br/kifU4F4+4ZLMR9Ueji0GkkPKsYdyMnq
j0p0PogyvP1l4qmboPImMYtaoFuYmMYlebgC9LN10bL91K4+jLt0I1YntEzrqgJo
WsJztYDw543NzSy5W+/cq4XRYgtq1b0RWwuUiswezmMoeyHZ8BQJe2xMjAOllASD
fqa8OK3WABHJpy4zUrnUBiMuPITzD/FuDx4C5IwwlC68gHAZblNqpBZCX0nFCtKj
YOcI2So5HbQ2OC8QF+zGVuduHUSok4hSy2BBfZ1pfvziqBeetWJwFvapGB44nIHh
WKNKvqOxLNIy7e+TGRiWOomrAWM18VSR9LZbBxpJK7PLSzWqYJYTRCZHAgMBAAGj
UzBRMB0GA1UdDgQWBBR4uDD9Y6x7iUoHO+32ioOcw1ICZTAfBgNVHSMEGDAWgBR4
uDD9Y6x7iUoHO+32ioOcw1ICZTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB
CwUAA4ICAQAaCEupzGGqcdh+L7BzhX7zyd7yzAKUoLxFrxaZY34Xyj3lcx1XoK6F
AqsH2JM25GixgadzhNt92JP7vzoWeHZtLfstrPS638Y1zZi6toy4E49viYjFk5J0
C6ZcFC04VYWWx6z0HwJuAS08tZ37JuFXpJGfXJOjZCQyxse0Lg0tuKLMeXDCk2Y3
Ba0noeuNyHRoWXXPyiUoeApkVCU5gIsyiJSWOjhJ5hpJG06rQNfNYexgKrrraEin
o0jmEMtJMx5TtD83hSnLCnFGBBq5lkE7jgXME1KsbIE3lJZzRX1mQwUK8CJDYxye
i6M/dzSvy0SsPvz8fTAlprXRtWWtJQmxgWENp3Dv+0Pmux/l+ilk7KA4sMXGhsfr
bvTOeWl1/uoFTPYiWR/ww7QEPLq23yDFY04Q7Un0qjIk8ExvaY8lCkXMgc8i7sGY
VfvOYb0zm67EfAQl3TW8Ky5fl5CcxpVCD360Bzi6hwjYixa3qEeBggOixFQBFWft
8wrkKTHpOQXjn4sDPtet8imm9UYEtzWrFX6T9MFYkBR0/yye0FIh9+YPiTA6WB86
NCNwK5Yl6HuvF97CIH5CdgO+5C7KifUtqTOL8pQKbNwy0S3sNYvB+njGvRpR7pKV
BUnFpB/Atptqr4CUlTXrc5IPLAqAfmwk5IKcwy3EXUbruf9Dwz69YA==
-----END CERTIFICATE-----`)
var CA_KEY = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEAnhDL4fqGGhjWzRBFy8iHGuNIdo79FtoWPevCpyek6AWrTuBF
0j3dzRMUpAkemC/p94tGES9f9iWUVi7gnfmUz1lxhjiqUoW5K1xfwmbx+qmC2YAw
HM+yq2oOLwz1FAYoQ3NT0gU6cJXtIB6Hjmxwy4jfDPzCuMFwfvOq4eS+pRJhnPTf
m31XpZOsfJMS9PjD6UU5U3ZsD/oMAjGuMGIXoOGgmqeFrRJm0N+/vtenAYbcSED+
qiGGJisOu5grvMl0RJAvjgvDMw+6lWKCpqV+/5gd9CNuFP3nUhW6tbY0mBHIETrZ
0uuUdh21P20JMKt34ok0wn6On2ECN0i7UGv+SJ9TgXj7hksxH1R6OLQaSQ8qxh3I
yeqPSnQ+iDK8/WXiqZug8iYxi1qgW5iYxiV5uAL0s3XRsv3Urj6Mu3QjVie0TOuq
AmhawnO1gPDnjc3NLLlb79yrhdFiC2rVvRFbC5SKzB7OYyh7IdnwFAl7bEyMA6WU
BIN+prw4rdYAEcmnLjNSudQGIy48hPMP8W4PHgLkjDCULryAcBluU2qkFkJfScUK
0qNg5wjZKjkdtDY4LxAX7MZW524dRKiTiFLLYEF9nWl+/OKoF561YnAW9qkYHjic
geFYo0q+o7Es0jLt75MZGJY6iasBYzXxVJH0tlsHGkkrs8tLNapglhNEJkcCAwEA
AQKCAgAwSuNvxHHqUUJ3XoxkiXy1u1EtX9x1eeYnvvs2xMb+WJURQTYz2NEGUdkR
kPO2/ZSXHAcpQvcnpi2e8y2PNmy/uQ0VPATVt6NuWweqxncR5W5j82U/uDlXY8y3
lVbfak4s5XRri0tikHvlP06dNgZ0OPok5qi7d+Zd8yZ3Y8LXfjkykiIrSG1Z2jdt
zCWTkNmSUKMGG/1CGFxI41Lb12xuq+C8v4f469Fb6bCUpyCQN9rffHQSGLH6wVb7
+68JO+d49zCATpmx5RFViMZwEcouXxRvvc9pPHXLP3ZPBD8nYu9kTD220mEGgWcZ
3L9dDlZPcSocbjw295WMvHz2QjhrDrb8gXwdpoRyuyofqgCyNxSnEC5M13SjOxtf
pjGzjTqh0kDlKXg2/eTkd9xIHjVhFYiHIEeITM/lHCfWwBCYxViuuF7pSRPzTe8U
C440b62qZSPMjVoquaMg+qx0n9fKSo6n1FIKHypv3Kue2G0WhDeK6u0U288vQ1t4
Ood3Qa13gZ+9hwDLbM/AoBfVBDlP/tpAwa7AIIU1ZRDNbZr7emFdctx9B6kLINv3
4PDOGM2xrjOuACSGMq8Zcu7LBz35PpIZtviJOeKNwUd8/xHjWC6W0itgfJb5I1Nm
V6Vj368pGlJx6Se26lvXwyyrc9pSw6jSAwARBeU4YkNWpi4i6QKCAQEA0T7u3P/9
jZJSnDN1o2PXymDrJulE61yguhc/QSmLccEPZe7or06/DmEhhKuCbv+1MswKDeag
/1JdFPGhL2+4G/f/9BK3BJPdcOZSz7K6Ty8AMMBf8AehKTcSBqwkJWcbEvpHpKJ6
eDqn1B6brXTNKMT6fEEXCuZJGPBpNidyLv/xXDcN7kCOo3nGYKfB5OhFpNiL63tw
+LntU56WESZwEqr8Pf80uFvsyXQK3a5q5HhIQtxl6tqQuPlNjsDBvCqj0x72mmaJ
ZVsVWlv7khUrCwAXz7Y8K7mKKBd2ekF5hSbryfJsxFyvEaWUPhnJpTKV85lAS+tt
FQuIp9TvKYlRQwKCAQEAwWJN8jysapdhi67jO0HtYOEl9wwnF4w6XtiOYtllkMmC
06/e9h7RsRyWPMdu3qRDPUYFaVDy6+dpUDSQ0+E2Ot6AHtVyvjeUTIL651mFIo/7
OSUCEc+HRo3SfPXdPhSQ2thNTxl6y9XcFacuvbthgr70KXbvC4k6IEmdpf/0Kgs9
7QTZCG26HDrEZ2q9yMRlRaL2SRD+7Y2xra7gB+cQGFj6yn0Wd/07er49RqMXidQf
KR2oYfev2BDtHXoSZFfhFGHlOdLvWRh90D4qZf4vQ+g/EIMgcNSoxjvph1EShmKt
sjhTHtoHuu+XmEQvIewk2oCI+JvofBkcnpFrVvUUrQKCAQAaTIufETmgCo0BfuJB
N/JOSGIl0NnNryWwXe2gVgVltbsmt6FdL0uKFiEtWJUbOF5g1Q5Kcvs3O/XhBQGa
QbNlKIVt+tAv7hm97+Tmn/MUsraWagdk1sCluns0hXxBizT27KgGhDlaVRz05yfv
5CdJAYDuDwxDXXBAhy7iFJEgYSDH00+X61tCJrMNQOh4ycy/DEyBu1EWod+3S85W
t3sMjZsIe8P3i+4137Th6eMbdha2+JaCrxfTd9oMoCN5b+6JQXIDM/H+4DTN15PF
540yY7+aZrAnWrmHknNcqFAKsTqfdi2/fFqwoBwCtiEG91WreU6AfEWIiJuTZIru
sIibAoIBAAqIwlo5t+KukF+9jR9DPh0S5rCIdvCvcNaN0WPNF91FPN0vLWQW1bFi
L0TsUDvMkuUZlV3hTPpQxsnZszH3iK64RB5p3jBCcs+gKu7DT59MXJEGVRCHT4Um
YJryAbVKBYIGWl++sZO8+JotWzx2op8uq7o+glMMjKAJoo7SXIiVyC/LHc95urOi
9+PySphPKn0anXPpexmRqGYfqpCDo7rPzgmNutWac80B4/CfHb8iUPg6Z1u+1FNe
yKvcZHgW2Wn00znNJcCitufLGyAnMofudND/c5rx2qfBx7zZS7sKUQ/uRYjes6EZ
QBbJUA/2/yLv8YYpaAaqj4aLwV8hRpkCggEBAIh3e25tr3avCdGgtCxS7Y1blQ2c
ue4erZKmFP1u8wTNHQ03T6sECZbnIfEywRD/esHpclfF3kYAKDRqIP4K905Rb0iH
759ZWt2iCbqZznf50XTvptdmjm5KxvouJzScnQ52gIV6L+QrCKIPelLBEIqCJREh
pmcjjocD/UCCSuHgbAYNNnO/JdhnSylz1tIg26I+2iLNyeTKIepSNlsBxnkLmqM1
cj/azKBaT04IOMLaN8xfSqitJYSraWMVNgGJM5vfcVaivZnNh0lZBv+qu6YkdM88
4/avCJ8IutT+FcMM+GbGazOm5ALWqUyhrnbLGc4CQMPfe7Il6NxwcrOxT8w=
-----END RSA PRIVATE KEY-----`)
var GoproxyCa, goproxyCaErr = tls.X509KeyPair(CA_CERT, CA_KEY)

8
vendor/github.com/elazarl/goproxy/certs/openssl-gen.sh generated vendored Executable file
View file

@ -0,0 +1,8 @@
#!/bin/bash
set -ex
# generate CA's key
openssl genrsa -aes256 -passout pass:1 -out ca.key.pem 4096
openssl rsa -passin pass:1 -in ca.key.pem -out ca.key.pem.tmp
mv ca.key.pem.tmp ca.key.pem
openssl req -config openssl.cnf -key ca.key.pem -new -x509 -days 7300 -sha256 -extensions v3_ca -out ca.pem

39
vendor/github.com/elazarl/goproxy/certs/openssl.cnf generated vendored Normal file
View file

@ -0,0 +1,39 @@
[ ca ]
default_ca = CA_default
[ CA_default ]
default_md = sha256
[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true
[ req ]
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = IL
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Center
localityName = Locality Name (eg, city)
localityName_default = Lod
0.organizationName = Organization Name (eg, company)
0.organizationName_default = GoProxy
# we can do this but it is not needed normally :-)
#1.organizationName = Second Organization Name (eg, company)
#1.organizationName_default = World Wide Web Pty Ltd
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = GoProxy
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = goproxy.github.io
commonName_max = 64
emailAddress = Email Address
emailAddress_default = elazarl@gmail.com
emailAddress_max = 64

59
vendor/github.com/elazarl/goproxy/chunked.go generated vendored Normal file
View file

@ -0,0 +1,59 @@
// Taken from $GOROOT/src/pkg/net/http/chunked
// needed to write https responses to client.
package goproxy
import (
"io"
"strconv"
)
// newChunkedWriter returns a new chunkedWriter that translates writes into HTTP
// "chunked" format before writing them to w. Closing the returned chunkedWriter
// sends the final 0-length chunk that marks the end of the stream.
//
// newChunkedWriter is not needed by normal applications. The http
// package adds chunking automatically if handlers don't set a
// Content-Length header. Using newChunkedWriter inside a handler
// would result in double chunking or chunking with a Content-Length
// length, both of which are wrong.
func newChunkedWriter(w io.Writer) io.WriteCloser {
return &chunkedWriter{w}
}
// Writing to chunkedWriter translates to writing in HTTP chunked Transfer
// Encoding wire format to the underlying Wire chunkedWriter.
type chunkedWriter struct {
Wire io.Writer
}
// Write the contents of data as one chunk to Wire.
// NOTE: Note that the corresponding chunk-writing procedure in Conn.Write has
// a bug since it does not check for success of io.WriteString
func (cw *chunkedWriter) Write(data []byte) (n int, err error) {
// Don't send 0-length data. It looks like EOF for chunked encoding.
if len(data) == 0 {
return 0, nil
}
head := strconv.FormatInt(int64(len(data)), 16) + "\r\n"
if _, err = io.WriteString(cw.Wire, head); err != nil {
return 0, err
}
if n, err = cw.Wire.Write(data); err != nil {
return
}
if n != len(data) {
err = io.ErrShortWrite
return
}
_, err = io.WriteString(cw.Wire, "\r\n")
return
}
func (cw *chunkedWriter) Close() error {
_, err := io.WriteString(cw.Wire, "0\r\n")
return err
}

68
vendor/github.com/elazarl/goproxy/counterecryptor.go generated vendored Normal file
View file

@ -0,0 +1,68 @@
package goproxy
import (
"crypto/aes"
"crypto/cipher"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"errors"
)
type CounterEncryptorRand struct {
cipher cipher.Block
counter []byte
rand []byte
ix int
}
func NewCounterEncryptorRandFromKey(key interface{}, seed []byte) (r CounterEncryptorRand, err error) {
var keyBytes []byte
switch key := key.(type) {
case *rsa.PrivateKey:
keyBytes = x509.MarshalPKCS1PrivateKey(key)
default:
err = errors.New("only RSA keys supported")
return
}
h := sha256.New()
if r.cipher, err = aes.NewCipher(h.Sum(keyBytes)[:aes.BlockSize]); err != nil {
return
}
r.counter = make([]byte, r.cipher.BlockSize())
if seed != nil {
copy(r.counter, h.Sum(seed)[:r.cipher.BlockSize()])
}
r.rand = make([]byte, r.cipher.BlockSize())
r.ix = len(r.rand)
return
}
func (c *CounterEncryptorRand) Seed(b []byte) {
if len(b) != len(c.counter) {
panic("SetCounter: wrong counter size")
}
copy(c.counter, b)
}
func (c *CounterEncryptorRand) refill() {
c.cipher.Encrypt(c.rand, c.counter)
for i := 0; i < len(c.counter); i++ {
if c.counter[i]++; c.counter[i] != 0 {
break
}
}
c.ix = 0
}
func (c *CounterEncryptorRand) Read(b []byte) (n int, err error) {
if c.ix == len(c.rand) {
c.refill()
}
if n = len(c.rand) - c.ix; n > len(b) {
n = len(b)
}
copy(b, c.rand[c.ix:c.ix+n])
c.ix += n
return
}

View file

@ -0,0 +1,99 @@
package goproxy_test
import (
"bytes"
"crypto/rsa"
"encoding/binary"
"github.com/elazarl/goproxy"
"io"
"math"
"math/rand"
"testing"
)
type RandSeedReader struct {
r rand.Rand
}
func (r *RandSeedReader) Read(b []byte) (n int, err error) {
for i := range b {
b[i] = byte(r.r.Int() & 0xFF)
}
return len(b), nil
}
func TestCounterEncDifferentConsecutive(t *testing.T) {
k, err := rsa.GenerateKey(&RandSeedReader{*rand.New(rand.NewSource(0xFF43109))}, 128)
fatalOnErr(err, "rsa.GenerateKey", t)
c, err := goproxy.NewCounterEncryptorRandFromKey(k, []byte("the quick brown fox run over the lazy dog"))
fatalOnErr(err, "NewCounterEncryptorRandFromKey", t)
for i := 0; i < 100*1000; i++ {
var a, b int64
binary.Read(&c, binary.BigEndian, &a)
binary.Read(&c, binary.BigEndian, &b)
if a == b {
t.Fatal("two consecutive equal int64", a, b)
}
}
}
func TestCounterEncIdenticalStreams(t *testing.T) {
k, err := rsa.GenerateKey(&RandSeedReader{*rand.New(rand.NewSource(0xFF43109))}, 128)
fatalOnErr(err, "rsa.GenerateKey", t)
c1, err := goproxy.NewCounterEncryptorRandFromKey(k, []byte("the quick brown fox run over the lazy dog"))
fatalOnErr(err, "NewCounterEncryptorRandFromKey", t)
c2, err := goproxy.NewCounterEncryptorRandFromKey(k, []byte("the quick brown fox run over the lazy dog"))
fatalOnErr(err, "NewCounterEncryptorRandFromKey", t)
nout := 1000
out1, out2 := make([]byte, nout), make([]byte, nout)
io.ReadFull(&c1, out1)
tmp := out2[:]
rand.Seed(0xFF43109)
for len(tmp) > 0 {
n := 1 + rand.Intn(256)
if n > len(tmp) {
n = len(tmp)
}
n, err := c2.Read(tmp[:n])
fatalOnErr(err, "CounterEncryptorRand.Read", t)
tmp = tmp[n:]
}
if !bytes.Equal(out1, out2) {
t.Error("identical CSPRNG does not produce the same output")
}
}
func stddev(data []int) float64 {
var sum, sum_sqr float64 = 0, 0
for _, h := range data {
sum += float64(h)
sum_sqr += float64(h) * float64(h)
}
n := float64(len(data))
variance := (sum_sqr - ((sum * sum) / n)) / (n - 1)
return math.Sqrt(variance)
}
func TestCounterEncStreamHistogram(t *testing.T) {
k, err := rsa.GenerateKey(&RandSeedReader{*rand.New(rand.NewSource(0xFF43109))}, 128)
fatalOnErr(err, "rsa.GenerateKey", t)
c, err := goproxy.NewCounterEncryptorRandFromKey(k, []byte("the quick brown fox run over the lazy dog"))
fatalOnErr(err, "NewCounterEncryptorRandFromKey", t)
nout := 100 * 1000
out := make([]byte, nout)
io.ReadFull(&c, out)
refhist := make([]int, 256)
for i := 0; i < nout; i++ {
refhist[rand.Intn(256)]++
}
hist := make([]int, 256)
for _, b := range out {
hist[int(b)]++
}
refstddev, stddev := stddev(refhist), stddev(hist)
// due to lack of time, I guestimate
t.Logf("ref:%v - act:%v = %v", refstddev, stddev, math.Abs(refstddev-stddev))
if math.Abs(refstddev-stddev) >= 1 {
t.Errorf("stddev of ref histogram different than regular PRNG: %v %v", refstddev, stddev)
}
}

87
vendor/github.com/elazarl/goproxy/ctx.go generated vendored Normal file
View file

@ -0,0 +1,87 @@
package goproxy
import (
"net/http"
"regexp"
)
// ProxyCtx is the Proxy context, contains useful information about every request. It is passed to
// every user function. Also used as a logger.
type ProxyCtx struct {
// Will contain the client request from the proxy
Req *http.Request
// Will contain the remote server's response (if available. nil if the request wasn't send yet)
Resp *http.Response
RoundTripper RoundTripper
// will contain the recent error that occurred while trying to send receive or parse traffic
Error error
// A handle for the user to keep data in the context, from the call of ReqHandler to the
// call of RespHandler
UserData interface{}
// Will connect a request to a response
Session int64
proxy *ProxyHttpServer
}
type RoundTripper interface {
RoundTrip(req *http.Request, ctx *ProxyCtx) (*http.Response, error)
}
type RoundTripperFunc func(req *http.Request, ctx *ProxyCtx) (*http.Response, error)
func (f RoundTripperFunc) RoundTrip(req *http.Request, ctx *ProxyCtx) (*http.Response, error) {
return f(req, ctx)
}
func (ctx *ProxyCtx) RoundTrip(req *http.Request) (*http.Response, error) {
if ctx.RoundTripper != nil {
return ctx.RoundTripper.RoundTrip(req, ctx)
}
return ctx.proxy.Tr.RoundTrip(req)
}
func (ctx *ProxyCtx) printf(msg string, argv ...interface{}) {
ctx.proxy.Logger.Printf("[%03d] "+msg+"\n", append([]interface{}{ctx.Session & 0xFF}, argv...)...)
}
// Logf prints a message to the proxy's log. Should be used in a ProxyHttpServer's filter
// This message will be printed only if the Verbose field of the ProxyHttpServer is set to true
//
// proxy.OnRequest().DoFunc(func(r *http.Request,ctx *goproxy.ProxyCtx) (*http.Request, *http.Response){
// nr := atomic.AddInt32(&counter,1)
// ctx.Printf("So far %d requests",nr)
// return r, nil
// })
func (ctx *ProxyCtx) Logf(msg string, argv ...interface{}) {
if ctx.proxy.Verbose {
ctx.printf("INFO: "+msg, argv...)
}
}
// Warnf prints a message to the proxy's log. Should be used in a ProxyHttpServer's filter
// This message will always be printed.
//
// proxy.OnRequest().DoFunc(func(r *http.Request,ctx *goproxy.ProxyCtx) (*http.Request, *http.Response){
// f,err := os.OpenFile(cachedContent)
// if err != nil {
// ctx.Warnf("error open file %v: %v",cachedContent,err)
// return r, nil
// }
// return r, nil
// })
func (ctx *ProxyCtx) Warnf(msg string, argv ...interface{}) {
ctx.printf("WARN: "+msg, argv...)
}
var charsetFinder = regexp.MustCompile("charset=([^ ;]*)")
// Will try to infer the character set of the request from the headers.
// Returns the empty string if we don't know which character set it used.
// Currently it will look for charset=<charset> in the Content-Type header of the request.
func (ctx *ProxyCtx) Charset() string {
charsets := charsetFinder.FindStringSubmatch(ctx.Resp.Header.Get("Content-Type"))
if charsets == nil {
return ""
}
return charsets[1]
}

325
vendor/github.com/elazarl/goproxy/dispatcher.go generated vendored Normal file
View file

@ -0,0 +1,325 @@
package goproxy
import (
"bytes"
"io/ioutil"
"net"
"net/http"
"regexp"
"strings"
)
// ReqCondition.HandleReq will decide whether or not to use the ReqHandler on an HTTP request
// before sending it to the remote server
type ReqCondition interface {
RespCondition
HandleReq(req *http.Request, ctx *ProxyCtx) bool
}
// RespCondition.HandleReq will decide whether or not to use the RespHandler on an HTTP response
// before sending it to the proxy client. Note that resp might be nil, in case there was an
// error sending the request.
type RespCondition interface {
HandleResp(resp *http.Response, ctx *ProxyCtx) bool
}
// ReqConditionFunc.HandleReq(req,ctx) <=> ReqConditionFunc(req,ctx)
type ReqConditionFunc func(req *http.Request, ctx *ProxyCtx) bool
// RespConditionFunc.HandleResp(resp,ctx) <=> RespConditionFunc(resp,ctx)
type RespConditionFunc func(resp *http.Response, ctx *ProxyCtx) bool
func (c ReqConditionFunc) HandleReq(req *http.Request, ctx *ProxyCtx) bool {
return c(req, ctx)
}
// ReqConditionFunc cannot test responses. It only satisfies RespCondition interface so that
// to be usable as RespCondition.
func (c ReqConditionFunc) HandleResp(resp *http.Response, ctx *ProxyCtx) bool {
return c(ctx.Req, ctx)
}
func (c RespConditionFunc) HandleResp(resp *http.Response, ctx *ProxyCtx) bool {
return c(resp, ctx)
}
// UrlHasPrefix returns a ReqCondition checking wether the destination URL the proxy client has requested
// has the given prefix, with or without the host.
// For example UrlHasPrefix("host/x") will match requests of the form 'GET host/x', and will match
// requests to url 'http://host/x'
func UrlHasPrefix(prefix string) ReqConditionFunc {
return func(req *http.Request, ctx *ProxyCtx) bool {
return strings.HasPrefix(req.URL.Path, prefix) ||
strings.HasPrefix(req.URL.Host+req.URL.Path, prefix) ||
strings.HasPrefix(req.URL.Scheme+req.URL.Host+req.URL.Path, prefix)
}
}
// UrlIs returns a ReqCondition, testing whether or not the request URL is one of the given strings
// with or without the host prefix.
// UrlIs("google.com/","foo") will match requests 'GET /' to 'google.com', requests `'GET google.com/' to
// any host, and requests of the form 'GET foo'.
func UrlIs(urls ...string) ReqConditionFunc {
urlSet := make(map[string]bool)
for _, u := range urls {
urlSet[u] = true
}
return func(req *http.Request, ctx *ProxyCtx) bool {
_, pathOk := urlSet[req.URL.Path]
_, hostAndOk := urlSet[req.URL.Host+req.URL.Path]
return pathOk || hostAndOk
}
}
// ReqHostMatches returns a ReqCondition, testing whether the host to which the request was directed to matches
// any of the given regular expressions.
func ReqHostMatches(regexps ...*regexp.Regexp) ReqConditionFunc {
return func(req *http.Request, ctx *ProxyCtx) bool {
for _, re := range regexps {
if re.MatchString(req.Host) {
return true
}
}
return false
}
}
// ReqHostIs returns a ReqCondition, testing whether the host to which the request is directed to equal
// to one of the given strings
func ReqHostIs(hosts ...string) ReqConditionFunc {
hostSet := make(map[string]bool)
for _, h := range hosts {
hostSet[h] = true
}
return func(req *http.Request, ctx *ProxyCtx) bool {
_, ok := hostSet[req.URL.Host]
return ok
}
}
var localHostIpv4 = regexp.MustCompile(`127\.0\.0\.\d+`)
// IsLocalHost checks whether the destination host is explicitly local host
// (buggy, there can be IPv6 addresses it doesn't catch)
var IsLocalHost ReqConditionFunc = func(req *http.Request, ctx *ProxyCtx) bool {
return req.URL.Host == "::1" ||
req.URL.Host == "0:0:0:0:0:0:0:1" ||
localHostIpv4.MatchString(req.URL.Host) ||
req.URL.Host == "localhost"
}
// UrlMatches returns a ReqCondition testing whether the destination URL
// of the request matches the given regexp, with or without prefix
func UrlMatches(re *regexp.Regexp) ReqConditionFunc {
return func(req *http.Request, ctx *ProxyCtx) bool {
return re.MatchString(req.URL.Path) ||
re.MatchString(req.URL.Host+req.URL.Path)
}
}
// DstHostIs returns a ReqCondition testing wether the host in the request url is the given string
func DstHostIs(host string) ReqConditionFunc {
return func(req *http.Request, ctx *ProxyCtx) bool {
return req.URL.Host == host
}
}
// SrcIpIs returns a ReqCondition testing whether the source IP of the request is one of the given strings
func SrcIpIs(ips ...string) ReqCondition {
return ReqConditionFunc(func(req *http.Request, ctx *ProxyCtx) bool {
for _, ip := range ips {
if strings.HasPrefix(req.RemoteAddr, ip+":") {
return true
}
}
return false
})
}
// Not returns a ReqCondition negating the given ReqCondition
func Not(r ReqCondition) ReqConditionFunc {
return func(req *http.Request, ctx *ProxyCtx) bool {
return !r.HandleReq(req, ctx)
}
}
// ContentTypeIs returns a RespCondition testing whether the HTTP response has Content-Type header equal
// to one of the given strings.
func ContentTypeIs(typ string, types ...string) RespCondition {
types = append(types, typ)
return RespConditionFunc(func(resp *http.Response, ctx *ProxyCtx) bool {
if resp == nil {
return false
}
contentType := resp.Header.Get("Content-Type")
for _, typ := range types {
if contentType == typ || strings.HasPrefix(contentType, typ+";") {
return true
}
}
return false
})
}
// ProxyHttpServer.OnRequest Will return a temporary ReqProxyConds struct, aggregating the given condtions.
// You will use the ReqProxyConds struct to register a ReqHandler, that would filter
// the request, only if all the given ReqCondition matched.
// Typical usage:
// proxy.OnRequest(UrlIs("example.com/foo"),UrlMatches(regexp.MustParse(`.*\.exampl.\com\./.*`)).Do(...)
func (proxy *ProxyHttpServer) OnRequest(conds ...ReqCondition) *ReqProxyConds {
return &ReqProxyConds{proxy, conds}
}
// ReqProxyConds aggregate ReqConditions for a ProxyHttpServer. Upon calling Do, it will register a ReqHandler that would
// handle the request if all conditions on the HTTP request are met.
type ReqProxyConds struct {
proxy *ProxyHttpServer
reqConds []ReqCondition
}
// DoFunc is equivalent to proxy.OnRequest().Do(FuncReqHandler(f))
func (pcond *ReqProxyConds) DoFunc(f func(req *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response)) {
pcond.Do(FuncReqHandler(f))
}
// ReqProxyConds.Do will register the ReqHandler on the proxy,
// the ReqHandler will handle the HTTP request if all the conditions
// aggregated in the ReqProxyConds are met. Typical usage:
// proxy.OnRequest().Do(handler) // will call handler.Handle(req,ctx) on every request to the proxy
// proxy.OnRequest(cond1,cond2).Do(handler)
// // given request to the proxy, will test if cond1.HandleReq(req,ctx) && cond2.HandleReq(req,ctx) are true
// // if they are, will call handler.Handle(req,ctx)
func (pcond *ReqProxyConds) Do(h ReqHandler) {
pcond.proxy.reqHandlers = append(pcond.proxy.reqHandlers,
FuncReqHandler(func(r *http.Request, ctx *ProxyCtx) (*http.Request, *http.Response) {
for _, cond := range pcond.reqConds {
if !cond.HandleReq(r, ctx) {
return r, nil
}
}
return h.Handle(r, ctx)
}))
}
// HandleConnect is used when proxy receives an HTTP CONNECT request,
// it'll then use the HttpsHandler to determine what should it
// do with this request. The handler returns a ConnectAction struct, the Action field in the ConnectAction
// struct returned will determine what to do with this request. ConnectAccept will simply accept the request
// forwarding all bytes from the client to the remote host, ConnectReject will close the connection with the
// client, and ConnectMitm, will assume the underlying connection is an HTTPS connection, and will use Man
// in the Middle attack to eavesdrop the connection. All regular handler will be active on this eavesdropped
// connection.
// The ConnectAction struct contains possible tlsConfig that will be used for eavesdropping. If nil, the proxy
// will use the default tls configuration.
// proxy.OnRequest().HandleConnect(goproxy.AlwaysReject) // rejects all CONNECT requests
func (pcond *ReqProxyConds) HandleConnect(h HttpsHandler) {
pcond.proxy.httpsHandlers = append(pcond.proxy.httpsHandlers,
FuncHttpsHandler(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
for _, cond := range pcond.reqConds {
if !cond.HandleReq(ctx.Req, ctx) {
return nil, ""
}
}
return h.HandleConnect(host, ctx)
}))
}
// HandleConnectFunc is equivalent to HandleConnect,
// for example, accepting CONNECT request if they contain a password in header
// io.WriteString(h,password)
// passHash := h.Sum(nil)
// proxy.OnRequest().HandleConnectFunc(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
// c := sha1.New()
// io.WriteString(c,ctx.Req.Header.Get("X-GoProxy-Auth"))
// if c.Sum(nil) == passHash {
// return OkConnect, host
// }
// return RejectConnect, host
// })
func (pcond *ReqProxyConds) HandleConnectFunc(f func(host string, ctx *ProxyCtx) (*ConnectAction, string)) {
pcond.HandleConnect(FuncHttpsHandler(f))
}
func (pcond *ReqProxyConds) HijackConnect(f func(req *http.Request, client net.Conn, ctx *ProxyCtx)) {
pcond.proxy.httpsHandlers = append(pcond.proxy.httpsHandlers,
FuncHttpsHandler(func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
for _, cond := range pcond.reqConds {
if !cond.HandleReq(ctx.Req, ctx) {
return nil, ""
}
}
return &ConnectAction{Action: ConnectHijack, Hijack: f}, host
}))
}
// ProxyConds is used to aggregate RespConditions for a ProxyHttpServer.
// Upon calling ProxyConds.Do, it will register a RespHandler that would
// handle the HTTP response from remote server if all conditions on the HTTP response are met.
type ProxyConds struct {
proxy *ProxyHttpServer
reqConds []ReqCondition
respCond []RespCondition
}
// ProxyConds.DoFunc is equivalent to proxy.OnResponse().Do(FuncRespHandler(f))
func (pcond *ProxyConds) DoFunc(f func(resp *http.Response, ctx *ProxyCtx) *http.Response) {
pcond.Do(FuncRespHandler(f))
}
// ProxyConds.Do will register the RespHandler on the proxy, h.Handle(resp,ctx) will be called on every
// request that matches the conditions aggregated in pcond.
func (pcond *ProxyConds) Do(h RespHandler) {
pcond.proxy.respHandlers = append(pcond.proxy.respHandlers,
FuncRespHandler(func(resp *http.Response, ctx *ProxyCtx) *http.Response {
for _, cond := range pcond.reqConds {
if !cond.HandleReq(ctx.Req, ctx) {
return resp
}
}
for _, cond := range pcond.respCond {
if !cond.HandleResp(resp, ctx) {
return resp
}
}
return h.Handle(resp, ctx)
}))
}
// OnResponse is used when adding a response-filter to the HTTP proxy, usual pattern is
// proxy.OnResponse(cond1,cond2).Do(handler) // handler.Handle(resp,ctx) will be used
// // if cond1.HandleResp(resp) && cond2.HandleResp(resp)
func (proxy *ProxyHttpServer) OnResponse(conds ...RespCondition) *ProxyConds {
return &ProxyConds{proxy, make([]ReqCondition, 0), conds}
}
// AlwaysMitm is a HttpsHandler that always eavesdrop https connections, for example to
// eavesdrop all https connections to www.google.com, we can use
// proxy.OnRequest(goproxy.ReqHostIs("www.google.com")).HandleConnect(goproxy.AlwaysMitm)
var AlwaysMitm FuncHttpsHandler = func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
return MitmConnect, host
}
// AlwaysReject is a HttpsHandler that drops any CONNECT request, for example, this code will disallow
// connections to hosts on any other port than 443
// proxy.OnRequest(goproxy.Not(goproxy.ReqHostMatches(regexp.MustCompile(":443$"))).
// HandleConnect(goproxy.AlwaysReject)
var AlwaysReject FuncHttpsHandler = func(host string, ctx *ProxyCtx) (*ConnectAction, string) {
return RejectConnect, host
}
// HandleBytes will return a RespHandler that read the entire body of the request
// to a byte array in memory, would run the user supplied f function on the byte arra,
// and will replace the body of the original response with the resulting byte array.
func HandleBytes(f func(b []byte, ctx *ProxyCtx) []byte) RespHandler {
return FuncRespHandler(func(resp *http.Response, ctx *ProxyCtx) *http.Response {
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
ctx.Warnf("Cannot read response %s", err)
return resp
}
resp.Body.Close()
resp.Body = ioutil.NopCloser(bytes.NewBuffer(f(b, ctx)))
return resp
})
}

100
vendor/github.com/elazarl/goproxy/doc.go generated vendored Normal file
View file

@ -0,0 +1,100 @@
/*
Package goproxy provides a customizable HTTP proxy,
supporting hijacking HTTPS connection.
The intent of the proxy, is to be usable with reasonable amount of traffic
yet, customizable and programable.
The proxy itself is simply an `net/http` handler.
Typical usage is
proxy := goproxy.NewProxyHttpServer()
proxy.OnRequest(..conditions..).Do(..requesthandler..)
proxy.OnRequest(..conditions..).DoFunc(..requesthandlerFunction..)
proxy.OnResponse(..conditions..).Do(..responesHandler..)
proxy.OnResponse(..conditions..).DoFunc(..responesHandlerFunction..)
http.ListenAndServe(":8080", proxy)
Adding a header to each request
proxy.OnRequest().DoFunc(func(r *http.Request,ctx *goproxy.ProxyCtx) (*http.Request, *http.Response){
r.Header.Set("X-GoProxy","1")
return r, nil
})
Note that the function is called before the proxy sends the request to the server
For printing the content type of all incoming responses
proxy.OnResponse().DoFunc(func(r *http.Response, ctx *goproxy.ProxyCtx)*http.Response{
println(ctx.Req.Host,"->",r.Header.Get("Content-Type"))
return r
})
note that we used the ProxyCtx context variable here. It contains the request
and the response (Req and Resp, Resp is nil if unavailable) of this specific client
interaction with the proxy.
To print the content type of all responses from a certain url, we'll add a
ReqCondition to the OnResponse function:
proxy.OnResponse(goproxy.UrlIs("golang.org/pkg")).DoFunc(func(r *http.Response, ctx *goproxy.ProxyCtx)*http.Response{
println(ctx.Req.Host,"->",r.Header.Get("Content-Type"))
return r
})
We can write the condition ourselves, conditions can be set on request and on response
var random = ReqConditionFunc(func(r *http.Request) bool {
return rand.Intn(1) == 0
})
var hasGoProxyHeader = RespConditionFunc(func(resp *http.Response,req *http.Request)bool {
return resp.Header.Get("X-GoProxy") != ""
})
Caution! If you give a RespCondition to the OnRequest function, you'll get a run time panic! It doesn't
make sense to read the response, if you still haven't got it!
Finally, we have convenience function to throw a quick response
proxy.OnResponse(hasGoProxyHeader).DoFunc(func(r*http.Response,ctx *goproxy.ProxyCtx)*http.Response {
r.Body.Close()
return goproxy.ForbiddenTextResponse(ctx.Req,"Can't see response with X-GoProxy header!")
})
we close the body of the original repsonse, and return a new 403 response with a short message.
Example use cases:
1. https://github.com/elazarl/goproxy/tree/master/examples/goproxy-avgsize
To measure the average size of an Html served in your site. One can ask
all the QA team to access the website by a proxy, and the proxy will
measure the average size of all text/html responses from your host.
2. [not yet implemented]
All requests to your web servers should be directed through the proxy,
when the proxy will detect html pieces sent as a response to AJAX
request, it'll send a warning email.
3. https://github.com/elazarl/goproxy/blob/master/examples/goproxy-httpdump/
Generate a real traffic to your website by real users using through
proxy. Record the traffic, and try it again for more real load testing.
4. https://github.com/elazarl/goproxy/tree/master/examples/goproxy-no-reddit-at-worktime
Will allow browsing to reddit.com between 8:00am and 17:00pm
5. https://github.com/elazarl/goproxy/tree/master/examples/goproxy-jquery-version
Will warn if multiple versions of jquery are used in the same domain.
6. https://github.com/elazarl/goproxy/blob/master/examples/goproxy-upside-down-ternet/
Modifies image files in an HTTP response via goproxy's image extension found in ext/.
*/
package goproxy

View file

@ -0,0 +1,29 @@
# Simple HTTP Proxy
`goproxy-basic` starts an HTTP proxy on :8080. It only handles explicit CONNECT
requests.
Start it in one shell:
```sh
goproxy-basic -v
```
Fetch goproxy homepage in another:
```sh
http_proxy=http://127.0.0.1:8080 wget -O - \
http://ripper234.com/p/introducing-goproxy-light-http-proxy/
```
The homepage HTML content should be displayed in the console. The proxy should
have logged the request being processed:
```sh
2015/04/09 18:19:17 [001] INFO: Got request /p/introducing-goproxy-light-http-proxy/ ripper234.com GET http://ripper234.com/p/introducing-goproxy-light-http-proxy/
2015/04/09 18:19:17 [001] INFO: Sending request GET http://ripper234.com/p/introducing-goproxy-light-http-proxy/
2015/04/09 18:19:18 [001] INFO: Received response 200 OK
2015/04/09 18:19:18 [001] INFO: Copying response to client 200 OK [200]
2015/04/09 18:19:18 [001] INFO: Copied 44333 bytes to client error=<nil>
```

View file

@ -0,0 +1,17 @@
package main
import (
"github.com/elazarl/goproxy"
"log"
"flag"
"net/http"
)
func main() {
verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
addr := flag.String("addr", ":8080", "proxy listen address")
flag.Parse()
proxy := goproxy.NewProxyHttpServer()
proxy.Verbose = *verbose
log.Fatal(http.ListenAndServe(*addr, proxy))
}

View file

@ -0,0 +1,75 @@
package main
import (
"crypto/tls"
"crypto/x509"
"github.com/elazarl/goproxy"
)
var caCert = []byte(`-----BEGIN CERTIFICATE-----
MIIDkzCCAnugAwIBAgIJAKe/ZGdfcHdPMA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQxGTAXBgNVBAMMEGRlbW8gZm9yIGdvcHJveHkwHhcNMTYw
OTI3MTQzNzQ3WhcNMTkwOTI3MTQzNzQ3WjBgMQswCQYDVQQGEwJBVTETMBEGA1UE
CAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRk
MRkwFwYDVQQDDBBkZW1vIGZvciBnb3Byb3h5MIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA2+W48YZoch72zj0a+ZlyFVY2q2MWmqsEY9f/u53fAeTxvPE6
1/DnqsydnA3FnGvxw9Dz0oZO6xG+PZvp+lhN07NZbuXK1nie8IpxCa342axpu4C0
69lZwxikpGyJO4IL5ywp/qfb5a2DxPTAyQOQ8ROAaydoEmktRp25yicnQ2yeZW//
1SIQxt7gRxQIGmuOQ/Gqr/XN/z2cZdbGJVRUvQXk7N6NhQiCX1zlmp1hzUW9jwC+
JEKKF1XVpQbc94Bo5supxhkKJ70CREPy8TH9mAUcQUZQRohnPvvt/lKneYAGhjHK
vhpajwlbMMSocVXFvY7o/IqIE/+ZUeQTs1SUwQIDAQABo1AwTjAdBgNVHQ4EFgQU
GnlWcIbfsWJW7GId+6xZIK8YlFEwHwYDVR0jBBgwFoAUGnlWcIbfsWJW7GId+6xZ
IK8YlFEwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAoFUjSD15rKlY
xudzyVlr6n0fRNhITkiZMX3JlFOvtHNYif8RfK4TH/oHNBTmle69AgixjMgy8GGd
H90prytGQ5zCs1tKcCFsN5gRSgdAkc2PpRFOK6u8HwOITV5lV7sjucsddXJcOJbQ
4fyVe47V9TTxI+A7lRnUP2HYTR1Bd0R/IgRAH57d1ZHs7omHIuQ+Ea8ph2ppXMnP
DXVOlZ9zfczSnPnQoomqULOU9Fq2ycyi8Y/ROtAHP6O7wCFbYHXhxojdaHSdhkcd
troTflFMD2/4O6MtBKbHxSmEG6H0FBYz5xUZhZq7WUH24V3xYsfge29/lOCd5/Xf
A+j0RJc/lQ==
-----END CERTIFICATE-----`)
var caKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA2+W48YZoch72zj0a+ZlyFVY2q2MWmqsEY9f/u53fAeTxvPE6
1/DnqsydnA3FnGvxw9Dz0oZO6xG+PZvp+lhN07NZbuXK1nie8IpxCa342axpu4C0
69lZwxikpGyJO4IL5ywp/qfb5a2DxPTAyQOQ8ROAaydoEmktRp25yicnQ2yeZW//
1SIQxt7gRxQIGmuOQ/Gqr/XN/z2cZdbGJVRUvQXk7N6NhQiCX1zlmp1hzUW9jwC+
JEKKF1XVpQbc94Bo5supxhkKJ70CREPy8TH9mAUcQUZQRohnPvvt/lKneYAGhjHK
vhpajwlbMMSocVXFvY7o/IqIE/+ZUeQTs1SUwQIDAQABAoIBAHK94ww8W0G5QIWL
Qwkc9XeGvg4eLUxVknva2Ll4fkZJxY4WveKx9OCd1lv4n7WoacYIwUGIDaQBZShW
s/eKnkmqGy+PvpC87gqL4sHvQpuqqJ1LYpxylLEFqduWOuGPUVC2Lc+QnWCycsCS
CgqZzsbMq0S+kkKRGSvw32JJneZCzqLgLNssQNVk+Gm6SI3s4jJsGPesjhnvoPaa
xZK14uFpltaA05GSTDaQeZJFEdnnb3f/eNPc2xMEfi0S2ZlJ6Q92WJEOepAetDlR
cRFi004bNyTb4Bphg8s4+9Cti5is199aFkGCRDWxeqEnc6aMY3Ezu9Qg3uttLVUd
uy830GUCgYEA7qS0X+9UH1R02L3aoANyADVbFt2ZpUwQGauw9WM92pH52xeHAw1S
ohus6FI3OC8xQq2CN525tGLUbFDZnNZ3YQHqFsfgevfnTs1//gbKXomitev0oFKh
VT+WYS4lkgYtPlXzhdGuk32q99T/wIocAguvCUY3PiA7yBz93ReyausCgYEA6+P8
bugMqT8qjoiz1q/YCfxsw9bAGWjlVqme2xmp256AKtxvCf1BPsToAaJU3nFi3vkw
ICLxUWAYoMBODJ3YnbOsIZOavdXZwYHv54JqwqFealC3DG0Du6fZYZdiY8pK+E6m
3fiYzP1WoVK5tU4bH8ibuIQvpcI8j7Gy0cV6/AMCgYBHl7fZNAZro72uLD7DVGVF
9LvP/0kR0uDdoqli5JPw12w6szM40i1hHqZfyBJy042WsFDpeHL2z9Nkb1jpeVm1
C4r7rJkGqwqElJf6UHUzqVzb8N6hnkhyN7JYkyyIQzwdgFGfaslRzBiXYxoa3BQM
9Q5c3OjDxY3JuhDa3DoVYwKBgDNqrWJLSD832oHZAEIicBe1IswJKjQfriWWsV6W
mHSbdtpg0/88aZVR/DQm+xLFakSp0jifBTS0momngRu06Dtvp2xmLQuF6oIIXY97
2ON1owvPbibSOEcWDgb8pWCU/oRjOHIXts6vxctCKeKAFN93raGphm0+Ck9T72NU
BTubAoGBAMEhI/Wy9wAETuXwN84AhmPdQsyCyp37YKt2ZKaqu37x9v2iL8JTbPEz
pdBzkA2Gc0Wdb6ekIzRrTsJQl+c/0m9byFHsRsxXW2HnezfOFX1H4qAmF6KWP0ub
M8aIn6Rab4sNPSrvKGrU6rFpv/6M33eegzldVnV9ku6uPJI1fFTC
-----END RSA PRIVATE KEY-----`)
func setCA(caCert, caKey []byte) error {
goproxyCa, err := tls.X509KeyPair(caCert, caKey)
if err != nil {
return err
}
if goproxyCa.Leaf, err = x509.ParseCertificate(goproxyCa.Certificate[0]); err != nil {
return err
}
goproxy.GoproxyCa = goproxyCa
goproxy.OkConnect = &goproxy.ConnectAction{Action: goproxy.ConnectAccept, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)}
goproxy.MitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectMitm, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)}
goproxy.HTTPMitmConnect = &goproxy.ConnectAction{Action: goproxy.ConnectHTTPMitm, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)}
goproxy.RejectConnect = &goproxy.ConnectAction{Action: goproxy.ConnectReject, TLSConfig: goproxy.TLSConfigFromCA(&goproxyCa)}
return nil
}

View file

@ -0,0 +1,20 @@
package main
import (
"flag"
"log"
"net/http"
"github.com/elazarl/goproxy"
)
func main() {
verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
addr := flag.String("addr", ":8080", "proxy listen address")
flag.Parse()
setCA(caCert, caKey)
proxy := goproxy.NewProxyHttpServer()
proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
proxy.Verbose = *verbose
log.Fatal(http.ListenAndServe(*addr, proxy))
}

View file

@ -0,0 +1,56 @@
package main
import (
"bufio"
"flag"
"log"
"net"
"net/http"
"regexp"
"github.com/elazarl/goproxy"
)
func orPanic(err error) {
if err != nil {
panic(err)
}
}
func main() {
proxy := goproxy.NewProxyHttpServer()
proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*baidu.com$"))).
HandleConnect(goproxy.AlwaysReject)
proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))).
HandleConnect(goproxy.AlwaysMitm)
// enable curl -p for all hosts on port 80
proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*:80$"))).
HijackConnect(func(req *http.Request, client net.Conn, ctx *goproxy.ProxyCtx) {
defer func() {
if e := recover(); e != nil {
ctx.Logf("error connecting to remote: %v", e)
client.Write([]byte("HTTP/1.1 500 Cannot reach destination\r\n\r\n"))
}
client.Close()
}()
clientBuf := bufio.NewReadWriter(bufio.NewReader(client), bufio.NewWriter(client))
remote, err := net.Dial("tcp", req.URL.Host)
orPanic(err)
remoteBuf := bufio.NewReadWriter(bufio.NewReader(remote), bufio.NewWriter(remote))
for {
req, err := http.ReadRequest(clientBuf.Reader)
orPanic(err)
orPanic(req.Write(remoteBuf))
orPanic(remoteBuf.Flush())
resp, err := http.ReadResponse(remoteBuf.Reader, req)
orPanic(err)
orPanic(resp.Write(clientBuf.Writer))
orPanic(clientBuf.Flush())
}
})
verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
addr := flag.String("addr", ":8080", "proxy listen address")
flag.Parse()
proxy.Verbose = *verbose
log.Fatal(http.ListenAndServe(*addr, proxy))
}

View file

@ -0,0 +1,30 @@
# Trace HTTP Requests and Responses
`goproxy-httpdump` starts an HTTP proxy on :8080. It handles explicit CONNECT
requests and traces them in a "db" directory created in the proxy working
directory. Each request type and headers are logged in a "log" file, while
their bodies are dumped in files prefixed with the request session identifier.
Additionally, the example demonstrates how to:
- Log information asynchronously (see HttpLogger)
- Allow the proxy to be stopped manually while ensuring all pending requests
have been processed (in this case, logged).
Start it in one shell:
```sh
goproxy-httpdump
```
Fetch goproxy homepage in another:
```sh
http_proxy=http://127.0.0.1:8080 wget -O - \
http://ripper234.com/p/introducing-goproxy-light-http-proxy/
```
A "db" directory should have appeared where you started the proxy, containing
two files:
- log: the request/response traces
- 1\_resp: the first response body

View file

@ -0,0 +1,285 @@
package main
import (
"errors"
"flag"
"fmt"
"io"
"log"
"net"
"net/http"
"net/http/httputil"
"os"
"os/signal"
"path"
"sync"
"time"
"github.com/elazarl/goproxy"
"github.com/elazarl/goproxy/transport"
)
type FileStream struct {
path string
f *os.File
}
func NewFileStream(path string) *FileStream {
return &FileStream{path, nil}
}
func (fs *FileStream) Write(b []byte) (nr int, err error) {
if fs.f == nil {
fs.f, err = os.Create(fs.path)
if err != nil {
return 0, err
}
}
return fs.f.Write(b)
}
func (fs *FileStream) Close() error {
fmt.Println("Close", fs.path)
if fs.f == nil {
return errors.New("FileStream was never written into")
}
return fs.f.Close()
}
type Meta struct {
req *http.Request
resp *http.Response
err error
t time.Time
sess int64
bodyPath string
from string
}
func fprintf(nr *int64, err *error, w io.Writer, pat string, a ...interface{}) {
if *err != nil {
return
}
var n int
n, *err = fmt.Fprintf(w, pat, a...)
*nr += int64(n)
}
func write(nr *int64, err *error, w io.Writer, b []byte) {
if *err != nil {
return
}
var n int
n, *err = w.Write(b)
*nr += int64(n)
}
func (m *Meta) WriteTo(w io.Writer) (nr int64, err error) {
if m.req != nil {
fprintf(&nr, &err, w, "Type: request\r\n")
} else if m.resp != nil {
fprintf(&nr, &err, w, "Type: response\r\n")
}
fprintf(&nr, &err, w, "ReceivedAt: %v\r\n", m.t)
fprintf(&nr, &err, w, "Session: %d\r\n", m.sess)
fprintf(&nr, &err, w, "From: %v\r\n", m.from)
if m.err != nil {
// note the empty response
fprintf(&nr, &err, w, "Error: %v\r\n\r\n\r\n\r\n", m.err)
} else if m.req != nil {
fprintf(&nr, &err, w, "\r\n")
buf, err2 := httputil.DumpRequest(m.req, false)
if err2 != nil {
return nr, err2
}
write(&nr, &err, w, buf)
} else if m.resp != nil {
fprintf(&nr, &err, w, "\r\n")
buf, err2 := httputil.DumpResponse(m.resp, false)
if err2 != nil {
return nr, err2
}
write(&nr, &err, w, buf)
}
return
}
// HttpLogger is an asynchronous HTTP request/response logger. It traces
// requests and responses headers in a "log" file in logger directory and dumps
// their bodies in files prefixed with the session identifiers.
// Close it to ensure pending items are correctly logged.
type HttpLogger struct {
path string
c chan *Meta
errch chan error
}
func NewLogger(basepath string) (*HttpLogger, error) {
f, err := os.Create(path.Join(basepath, "log"))
if err != nil {
return nil, err
}
logger := &HttpLogger{basepath, make(chan *Meta), make(chan error)}
go func() {
for m := range logger.c {
if _, err := m.WriteTo(f); err != nil {
log.Println("Can't write meta", err)
}
}
logger.errch <- f.Close()
}()
return logger, nil
}
func (logger *HttpLogger) LogResp(resp *http.Response, ctx *goproxy.ProxyCtx) {
body := path.Join(logger.path, fmt.Sprintf("%d_resp", ctx.Session))
from := ""
if ctx.UserData != nil {
from = ctx.UserData.(*transport.RoundTripDetails).TCPAddr.String()
}
if resp == nil {
resp = emptyResp
} else {
resp.Body = NewTeeReadCloser(resp.Body, NewFileStream(body))
}
logger.LogMeta(&Meta{
resp: resp,
err: ctx.Error,
t: time.Now(),
sess: ctx.Session,
from: from})
}
var emptyResp = &http.Response{}
var emptyReq = &http.Request{}
func (logger *HttpLogger) LogReq(req *http.Request, ctx *goproxy.ProxyCtx) {
body := path.Join(logger.path, fmt.Sprintf("%d_req", ctx.Session))
if req == nil {
req = emptyReq
} else {
req.Body = NewTeeReadCloser(req.Body, NewFileStream(body))
}
logger.LogMeta(&Meta{
req: req,
err: ctx.Error,
t: time.Now(),
sess: ctx.Session,
from: req.RemoteAddr})
}
func (logger *HttpLogger) LogMeta(m *Meta) {
logger.c <- m
}
func (logger *HttpLogger) Close() error {
close(logger.c)
return <-logger.errch
}
// TeeReadCloser extends io.TeeReader by allowing reader and writer to be
// closed.
type TeeReadCloser struct {
r io.Reader
w io.WriteCloser
c io.Closer
}
func NewTeeReadCloser(r io.ReadCloser, w io.WriteCloser) io.ReadCloser {
return &TeeReadCloser{io.TeeReader(r, w), w, r}
}
func (t *TeeReadCloser) Read(b []byte) (int, error) {
return t.r.Read(b)
}
// Close attempts to close the reader and write. It returns an error if both
// failed to Close.
func (t *TeeReadCloser) Close() error {
err1 := t.c.Close()
err2 := t.w.Close()
if err1 != nil {
return err1
}
return err2
}
// stoppableListener serves stoppableConn and tracks their lifetime to notify
// when it is safe to terminate the application.
type stoppableListener struct {
net.Listener
sync.WaitGroup
}
type stoppableConn struct {
net.Conn
wg *sync.WaitGroup
}
func newStoppableListener(l net.Listener) *stoppableListener {
return &stoppableListener{l, sync.WaitGroup{}}
}
func (sl *stoppableListener) Accept() (net.Conn, error) {
c, err := sl.Listener.Accept()
if err != nil {
return c, err
}
sl.Add(1)
return &stoppableConn{c, &sl.WaitGroup}, nil
}
func (sc *stoppableConn) Close() error {
sc.wg.Done()
return sc.Conn.Close()
}
func main() {
verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
addr := flag.String("l", ":8080", "on which address should the proxy listen")
flag.Parse()
proxy := goproxy.NewProxyHttpServer()
proxy.Verbose = *verbose
if err := os.MkdirAll("db", 0755); err != nil {
log.Fatal("Can't create dir", err)
}
logger, err := NewLogger("db")
if err != nil {
log.Fatal("can't open log file", err)
}
tr := transport.Transport{Proxy: transport.ProxyFromEnvironment}
// For every incoming request, override the RoundTripper to extract
// connection information. Store it is session context log it after
// handling the response.
proxy.OnRequest().DoFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
ctx.RoundTripper = goproxy.RoundTripperFunc(func(req *http.Request, ctx *goproxy.ProxyCtx) (resp *http.Response, err error) {
ctx.UserData, resp, err = tr.DetailedRoundTrip(req)
return
})
logger.LogReq(req, ctx)
return req, nil
})
proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
logger.LogResp(resp, ctx)
return resp
})
l, err := net.Listen("tcp", *addr)
if err != nil {
log.Fatal("listen:", err)
}
sl := newStoppableListener(l)
ch := make(chan os.Signal)
signal.Notify(ch, os.Interrupt)
go func() {
<-ch
log.Println("Got SIGINT exiting")
sl.Add(1)
sl.Close()
logger.Close()
sl.Done()
}()
log.Println("Starting Proxy")
http.Serve(sl, proxy)
sl.Wait()
log.Println("All connections closed - exit")
}

View file

@ -0,0 +1,31 @@
# Content Analysis
`goproxy-jquery-version` starts an HTTP proxy on :8080. It checks HTML
responses, looks for scripts referencing jQuery library and emits warnings if
different versions of the library are being used for a given host.
Start it in one shell:
```sh
goproxy-jquery-version
```
Fetch goproxy homepage in another:
```sh
http_proxy=http://127.0.0.1:8080 wget -O - \
http://ripper234.com/p/introducing-goproxy-light-http-proxy/
```
Goproxy homepage uses jQuery and a mix of plugins. First the proxy reports the
first use of jQuery it detects for the domain. Then, because the regular
expression matching the jQuery sources is imprecise, it reports a mismatch with
a plugin reference:
```sh
2015/04/11 11:23:02 [001] WARN: ripper234.com uses //ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js
2015/04/11 11:23:02 [001] WARN: In http://ripper234.com/p/introducing-goproxy-light-http-proxy/, \
Contradicting jqueries //ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js \
http://ripper234.wpengine.netdna-cdn.com/wp-content/plugins/wp-ajax-edit-comments/js/jquery.colorbox.min.js?ver=5.0.36
```

View file

@ -0,0 +1,8 @@
<!doctype html>
<html>
<head>
<script src="jquery.1.4.js"></script>
</head>
<body/>
</html>

View file

@ -0,0 +1,8 @@
<!doctype html>
<html>
<head>
<script src="jquery.1.3.js"></script>
</head>
<body/>
</html>

View file

@ -0,0 +1,233 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>jQuery: The Write Less, Do More, JavaScript Library</title>
<link rel="stylesheet" href="http://static.jquery.com/files/rocker/css/reset.css" type="text/css" />
<link rel="stylesheet" href="http://static.jquery.com/files/rocker/css/screen.css" type="text/css" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script>!window.jQuery && document.write('<script src="http://code.jquery.com/jquery-1.4.2.min.js"><\/script>');</script>
<script src="http://static.jquery.com/files/rocker/scripts/custom.js"></script>
<link rel="alternate" type="application/rss+xml" title="jQuery Blog" href="http://jquery.com/blog/feed/" />
<link rel="shortcut icon" href="http://static.jquery.com/favicon.ico" type="image/x-icon"/>
</head>
<body>
<div id="jq-siteContain">
<div id="jq-header">
<a id="jq-siteLogo" href="http://jquery.com" title="jQuery Home"><img src="http://static.jquery.com/files/rocker/images/logo_jquery_215x53.gif" width="215" height="53" alt="jQuery: Write Less, Do More." /></a>
<div id="jq-primaryNavigation">
<ul>
<li class="jq-jquery jq-current"><a href="http://jquery.com/" title="jQuery Home">jQuery</a></li>
<li class="jq-ui"><a href="http://jqueryui.com/" title="jQuery UI">UI</a></li>
<li class="jq-mobile"><a href="http://jquerymobile.com/" title="jQuery Mobile">Mobile</a></li>
<li class="jq-plugins"><a href="http://plugins.jquery.com/" title="jQuery Plugins">Plugins</a></li>
<li class="jq-meetup"><a href="http://meetups.jquery.com/" title="jQuery Meetups">Meetups</a></li>
<li class="jq-forum"><a href="http://forum.jquery.com/" title="jQuery Forum">Forum</a></li>
<li class="jq-blog"><a href="http://blog.jquery.com/" title="jQuery Blog">Blog</a></li>
<li class="jq-about"><a href="http://jquery.org/about" title="About jQuery">About</a></li>
<li class="jq-donate"><a href="http://jquery.org/donate" title="Donate to jQuery">Donate</a></li>
</ul>
</div><!-- /#primaryNavigation -->
<div id="jq-secondaryNavigation">
<ul>
<li class="jq-download jq-first"><a href="http://docs.jquery.com/Downloading_jQuery">Download</a></li>
<li class="jq-documentation"><a href="http://docs.jquery.com">Documentation</a></li>
<li class="jq-tutorials"><a href="http://docs.jquery.com/Tutorials">Tutorials</a></li>
<li class="jq-bugTracker"><a href="http://dev.jquery.com/">Bug Tracker</a></li>
<li class="jq-discussion jq-last"><a href="http://docs.jquery.com/Discussion">Discussion</a></li>
</ul>
</div><!-- /#secondaryNavigation -->
</div><!-- /#header -->
<div id="jq-content" class="jq-clearfix">
<div id="jq-intro" class="jq-clearfix">
<h2><span class="jq-jquery"><span>jQuery</span></span> is a new kind of JavaScript Library.</h2>
<p>jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. <strong>jQuery is designed to change the way that you write JavaScript.</strong></p>
<ul class="jq-checkpoints jq-clearfix">
<li><a href="http://docs.jquery.com/Tutorials" title="Lightweight Footprint" class="jq-thickbox">Lightweight Footprint</a>
<div class="jq-checkpointSubhead">
<p>About 31KB in size <em>(Minified and Gzipped)</em></p>
</div>
</li>
<li><a href="http://docs.jquery.com/Tutorials" title="CSS3 Compliant" class="jq-thickbox">CSS3 Compliant</a>
<div class="jq-checkpointSubhead">
<p>Supports CSS 1-3 selectors and more!</p>
</div>
</li>
<li><a href="http://docs.jquery.com/Tutorials" title="Cross-browser" class="jq-thickbox">Cross-browser</a>
<div class="jq-checkpointSubhead">
<p>IE 6.0+, FF 3.6+, Safari 5.0+, Opera, Chrome</p>
</div>
</li>
</ul>
</div><!-- /#intro -->
<div id="jq-download">
<h2>Grab the latest version!</h2>
<form action="" method="get">
<fieldset>
<legend>Choose your compression level:</legend>
<div id="jq-compression" class="jq-clearfix">
<input type="radio" name="name" value="http://code.jquery.com/jquery-1.7.2.min.js" id="jq-production" checked="checked" />
<a class="jq-radioToggle name jq-checked" href="http://code.jquery.com/jquery-1.7.2.min.js">jquery-1.7.2.min.js</a>
<label for="jq-production">Production <em>(<strong>32KB</strong>, Minified and Gzipped)</em></label>
<input type="radio" name="name" value="http://code.jquery.com/jquery-1.7.2.js" id="jq-development" />
<a class="jq-radioToggle name" href="http://code.jquery.com/jquery-1.7.2.js">jquery-1.7.2.js</a>
<label for="jq-development">Development <em>(<strong>247KB</strong>, Uncompressed Code)</em></label>
</div>
<button type="submit" name="downloadBtn" id="jq-downloadBtn"><span>Download</span></button>
<p class="jq-version"><strong>Current Release:</strong> v1.7.2</p>
</fieldset>
</form>
<script>
jQuery("#jq-download form").submit(function(){
window.location = jQuery(this).find("input:checked").val();
return false;
});
</script>
</div><!-- /#download -->
<div id="jq-whosUsing">
<h2 class="jq-whosUsing">Who's using jQuery?</h2>
<ul class="jq-whosUsing">
<li><a href="http://www.google.com" class="jq-google" title="Google">Google</a></li>
<li><a href="http://www.dell.com" class="jq-dell" title="Dell">Dell</a></li>
<li><a href="http://www.bankofamerica.com" class="jq-boa" title="Bank of America">Bank of America</a></li>
<li><a href="http://www.mlb.com" class="jq-mlb" title="Major League Baseball">Major League Baseball</a></li>
<li><a href="http://www.digg.com" class="jq-digg" title="Digg">Digg</a></li>
<li><a href="http://www.nbc.com" class="jq-nbc" title="NBC">NBC</a></li>
<li><a href="http://www.cbs.com" class="jq-cbs" title="CBS News">CBS News</a></li>
<li><a href="http://www.netflix.com" class="jq-netflix" title="Netflix">Netflix</a></li>
<li><a href="http://www.technorati.com" class="jq-technorati" title="Technorati">Technorati</a></li>
<li><a href="http://www.mozilla.org" class="jq-mozilla" title="Mozilla">Mozilla</a></li>
<li><a href="http://www.wordpress.org" class="jq-wordpress" title="Wordpress">Wordpress</a></li>
<li><a href="http://www.drupal.org" class="jq-drupal" title="Drupal">Drupal</a></li>
</ul>
</div><!-- /#jq-whosUsing -->
<div id="jq-learnjQuery" class="jq-clearfix">
<div id="jq-learnNow">
<h2>Learn <span class="jq-jquery"><span>jQuery</span></span> Now!</h2>
<p>What does jQuery code look like? Here's the quick and dirty:</p>
<div class="jq-codeDemo jq-clearfix">
<pre><code>$("p.neat").addClass("ohmy").show("slow");</code></pre>
<a href="http://docs.jquery.com/Tutorials" class="jq-runCode">Run Code</a>
<p class="neat"><strong>Congratulations!</strong> You just ran a snippet of jQuery code. Wasn't that easy? There's lots of example code throughout the <strong><a href="http://docs.jquery.com/">documentation</a></strong> on this site. Be sure to give all the code a test run to see what happens.</p>
</div>
</div><!-- /#learnNow -->
<div id="jq-resources" class="clearfix">
<h2>jQuery Resources</h2>
<div class="jq-gettingStarted">
<h3>Getting Started With jQuery</h3>
<ul>
<li><a href="http://docs.jquery.com/How_jQuery_Works">How jQuery Works</a></li>
<li><a href="http://docs.jquery.com/Tutorials">Tutorials</a></li>
<li><a href="http://docs.jquery.com/Using_jQuery_with_Other_Libraries">Using jQuery with other libraries</a></li>
<li><a href="http://docs.jquery.com/">jQuery Documentation</a></li>
</ul>
</div>
<div class="jq-devResources">
<h3>Developer Resources</h3>
<ul>
<li><a href="http://docs.jquery.com/Discussion">Mailing List</a></li>
<li><a href="http://docs.jquery.com/Downloading_jQuery">Source code / Git</a></li>
<li><a href="http://docs.jquery.com/Plugins/Authoring">Plugin Authoring</a></li>
<li><a href="http://dev.jquery.com/newticket/">Submit a New Bug Report</a></li>
</ul>
</div>
</div><!-- /#resources -->
</div><!-- /#learnjQuery -->
<div id="jq-books" style="width:auto; float: none">
<h2>Books About jQuery</h2>
<ul>
<li class="jq-clearfix" style="width:270px;float:left;clear:none;">
<a href="http://link.packtpub.com/S3Fr9Q" class="jq-bookImg"><img src="http://learningjquery.kswedberg.netdna-cdn.com/wp-content/themes/ljq/images/ljq3rded.jpg" alt="Learning jQuery Third Edition" width="55" height="70" /></a>
<h3><a href="http://link.packtpub.com/S3Fr9Q">Learning jQuery Third Edition</a></h3>
<div class="jq-author">Karl Swedberg and <br />Jonathan Chaffer</div>
<a href="http://link.packtpub.com/S3Fr9Q" class="jq-buyNow">Buy Now</a>
</li>
<li class="jq-clearfix" style="width:270px;float:left;clear:none;">
<a href="http://www.packtpub.com/jquery-1-4-animation-techniques-beginners-guide/book/mid/1803111nkj15" class="jq-bookImg"><img src="http://static.jquery.com/books/jquery-animation-beginners-guide.jpg" alt="jQuery 1.4 Animation Techniques: Beginners Guide" width="55" height="70" /></a>
<h3><a href="http://www.packtpub.com/jquery-1-4-animation-techniques-beginners-guide/book/mid/1803111nkj15">jQuery 1.4 Animation Techniques: Beginners Guide</a></h3>
<div class="jq-author">Dan Wellman</div>
<a href="http://www.packtpub.com/jquery-1-4-animation-techniques-beginners-guide/book/mid/1803111nkj15" class="jq-buyNow">Buy Now</a>
</li>
<li class="jq-clearfix" style="width:270px;float:left;clear:none;">
<a href="http://www.packtpub.com/jquery-plugin-development-beginners-guide/book/mid/1911104odmdz" class="jq-bookImg"><img src="http://static.jquery.com/books/jquery-plugin-developers-guide_thumb.jpg" alt="jQuery Plugin Development Beginner's Guide" width="55" height="70" /></a>
<h3><a href="http://www.packtpub.com/jquery-plugin-development-beginners-guide/book/mid/1911104odmdz">jQuery Plugin Development Beginner's Guide</a></h3>
<div class="jq-author">Guilio Bai</div>
<a href="http://www.packtpub.com/jquery-plugin-development-beginners-guide/book/mid/1911104odmdz" class="jq-buyNow">Buy Now</a>
</li>
<li class="jq-clearfix" style="width:270px;float:left;clear:left;">
<a href="http://www.manning.com/affiliate/idevaffiliate.php?id=648_176" class="jq-bookImg"><img src="http://static.jquery.com/books/jquery-in-action-2ed_thumb.jpg" alt="jQuery in Action" width="55" height="70" /></a>
<h3><a href="http://www.manning.com/affiliate/idevaffiliate.php?id=648_176">jQuery in Action</a></h3>
<div class="jq-author">Bear Bibeault
<br />and Yehuda Katz</div>
<a href="http://www.manning.com/affiliate/idevaffiliate.php?id=648_176" class="jq-buyNow">Buy Now</a>
</li>
<li class="jq-clearfix" style="width:270px;float:left;clear:none;">
<a class="jq-bookImg" href="http://jqueryenlightenment.com/"><img src="http://static.jquery.com/books/jquery-enlightenment_thumb.jpg" alt="jQuery Enlightenment" width="55" height="70" /></a>
<h3><a href="http://jqueryenlightenment.com/">jQuery Enlightenment</a></h3>
<div class="jq-author">Cody Lindley</div>
<a href="http://jqueryenlightenment.com/" class="jq-buyNow">Buy Now</a>
</li>
</ul>
</div><!-- /#news -->
</div><!-- /#content -->
<div id="jq-footer" class="jq-clearfix">
<div id="jq-credits">
<p id="jq-copyright">&copy; 2010 <a href="http://jquery.org/">The jQuery Project</a></p>
<p id="jq-hosting">Sponsored by <a href="http://mediatemple.net" class="jq-mediaTemple">Media Temple</a> and <a href="http://jquery.org/sponsors">others</a>.</p>
</div>
<div id="jq-footerNavigation">
<ul>
<li class="jq-download jq-first"><a href="http://docs.jquery.com/Downloading_jQuery">Download</a></li>
<li class="jq-documentation"><a href="http://docs.jquery.com">Documentation</a></li>
<li class="jq-tutorials"><a href="http://docs.jquery.com/Tutorials">Tutorials</a></li>
<li class="jq-bugTracker"><a href="http://dev.jquery.com/">Bug Tracker</a></li>
<li class="jq-discussion jq-last"><a href="http://docs.jquery.com/Discussion">Discussion</a></li>
</ul>
</div><!-- /#secondaryNavigation -->
</div><!-- /#footer -->
</div><!-- /#siteContain -->
<script src="http://static.jquery.com/donate/donate.js" type="text/javascript"></script>
<script type="text/javascript">
var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-1076265-1']); _gaq.push(['_trackPageview']); _gaq.push(['_setDomainName', '.jquery.com']);
(function() {var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);})();
</script>
</body>
</html>

View file

@ -0,0 +1,118 @@
package main
import (
"bytes"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
)
func equal(u, v []string) bool {
if len(u) != len(v) {
return false
}
for i, _ := range u {
if u[i] != v[i] {
return false
}
}
return true
}
func readFile(fname string, t *testing.T) string {
b, err := ioutil.ReadFile(fname)
if err != nil {
t.Fatal("readFile", err)
}
return string(b)
}
func TestDefectiveScriptParser(t *testing.T) {
if l := len(findScriptSrc(`<!DOCTYPE HTML>
<html>
<body>
<video width="320" height="240" controls="controls">
<source src="movie.mp4" type="video/mp4" />
<source src="movie.ogg" type="video/ogg" />
<source src="movie.webm" type="video/webm" />
Your browser does not support the video tag.
</video>
</body>
</html>`)); l != 0 {
t.Fail()
}
urls := findScriptSrc(readFile("w3schools.html", t))
if !equal(urls, []string{"http://partner.googleadservices.com/gampad/google_service.js",
"//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"}) {
t.Error("w3schools.html", "src scripts are not recognized", urls)
}
urls = findScriptSrc(readFile("jquery_homepage.html", t))
if !equal(urls, []string{"http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",
"http://code.jquery.com/jquery-1.4.2.min.js",
"http://static.jquery.com/files/rocker/scripts/custom.js",
"http://static.jquery.com/donate/donate.js"}) {
t.Error("jquery_homepage.html", "src scripts are not recognized", urls)
}
}
func proxyWithLog() (*http.Client, *bytes.Buffer) {
proxy := NewJqueryVersionProxy()
proxyServer := httptest.NewServer(proxy)
buf := new(bytes.Buffer)
proxy.Logger = log.New(buf, "", 0)
proxyUrl, _ := url.Parse(proxyServer.URL)
tr := &http.Transport{Proxy: http.ProxyURL(proxyUrl)}
client := &http.Client{Transport: tr}
return client, buf
}
func get(t *testing.T, server *httptest.Server, client *http.Client, url string) {
resp, err := client.Get(server.URL + url)
if err != nil {
t.Fatal("cannot get proxy", err)
}
ioutil.ReadAll(resp.Body)
resp.Body.Close()
}
func TestProxyServiceTwoVersions(t *testing.T) {
var fs = httptest.NewServer(http.FileServer(http.Dir(".")))
defer fs.Close()
client, buf := proxyWithLog()
get(t, fs, client, "/w3schools.html")
get(t, fs, client, "/php_man.html")
if buf.String() != "" &&
!strings.Contains(buf.String(), " uses jquery ") {
t.Error("shouldn't warn on a single URL", buf.String())
}
get(t, fs, client, "/jquery1.html")
warnings := buf.String()
if !strings.Contains(warnings, "http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js") ||
!strings.Contains(warnings, "jquery.1.4.js") ||
!strings.Contains(warnings, "Contradicting") {
t.Error("contradicting jquery versions (php_man.html, w3schools.html) does not issue warning", warnings)
}
}
func TestProxyService(t *testing.T) {
var fs = httptest.NewServer(http.FileServer(http.Dir(".")))
defer fs.Close()
client, buf := proxyWithLog()
get(t, fs, client, "/jquery_homepage.html")
warnings := buf.String()
if !strings.Contains(warnings, "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js") ||
!strings.Contains(warnings, "http://code.jquery.com/jquery-1.4.2.min.js") ||
!strings.Contains(warnings, "Contradicting") {
t.Error("contradicting jquery versions does not issue warning")
}
}

View file

@ -0,0 +1,64 @@
package main
import (
"github.com/elazarl/goproxy"
"github.com/elazarl/goproxy/ext/html"
"log"
"net/http"
"regexp"
)
var (
// who said we can't parse HTML with regexp?
scriptMatcher = regexp.MustCompile(`(?i:<script\s+)`)
srcAttrMatcher = regexp.MustCompile(`^(?i:[^>]*\ssrc=["']([^"']*)["'])`)
)
// findScripts returns all sources of HTML script tags found in input text.
func findScriptSrc(html string) []string {
srcs := make([]string, 0)
matches := scriptMatcher.FindAllStringIndex(html, -1)
for _, match := range matches {
// -1 to capture the whitespace at the end of the script tag
srcMatch := srcAttrMatcher.FindStringSubmatch(html[match[1]-1:])
if srcMatch != nil {
srcs = append(srcs, srcMatch[1])
}
}
return srcs
}
// NewJQueryVersionProxy creates a proxy checking responses HTML content, looks
// for scripts referencing jQuery library and emits warnings if different
// versions of the library are being used for a given host.
func NewJqueryVersionProxy() *goproxy.ProxyHttpServer {
proxy := goproxy.NewProxyHttpServer()
m := make(map[string]string)
jqueryMatcher := regexp.MustCompile(`(?i:jquery\.)`)
proxy.OnResponse(goproxy_html.IsHtml).Do(goproxy_html.HandleString(
func(s string, ctx *goproxy.ProxyCtx) string {
for _, src := range findScriptSrc(s) {
if !jqueryMatcher.MatchString(src) {
continue
}
prev, ok := m[ctx.Req.Host]
if ok {
if prev != src {
ctx.Warnf("In %v, Contradicting jqueries %v %v",
ctx.Req.URL, prev, src)
break
}
} else {
ctx.Warnf("%s uses jquery %s", ctx.Req.Host, src)
m[ctx.Req.Host] = src
}
}
return s
}))
return proxy
}
func main() {
proxy := NewJqueryVersionProxy()
log.Fatal(http.ListenAndServe(":8080", proxy))
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,21 @@
# Request Filtering
`goproxy-no-reddit-at-work` starts an HTTP proxy on :8080. It denies requests
to "www.reddit.com" made between 8am to 5pm inclusive, local time.
Start it in one shell:
```sh
$ goproxy-no-reddit-at-work
```
Fetch reddit in another:
```sh
$ http_proxy=http://127.0.0.1:8080 wget -O - http://www.reddit.com
--2015-04-11 16:59:01-- http://www.reddit.com/
Connecting to 127.0.0.1:8080... connected.
Proxy request sent, awaiting response... 403 Forbidden
2015-04-11 16:59:01 ERROR 403: Forbidden.
```

View file

@ -0,0 +1,25 @@
package main
import (
"github.com/elazarl/goproxy"
"log"
"net/http"
"time"
)
func main() {
proxy := goproxy.NewProxyHttpServer()
proxy.OnRequest(goproxy.DstHostIs("www.reddit.com")).DoFunc(
func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
h, _, _ := time.Now().Clock()
if h >= 8 && h <= 17 {
return r, goproxy.NewResponse(r,
goproxy.ContentTypeText, http.StatusForbidden,
"Don't waste your time!")
} else {
ctx.Warnf("clock: %d, you can waste your time...", h)
}
return r, nil
})
log.Fatalln(http.ListenAndServe(":8080", proxy))
}

View file

@ -0,0 +1,25 @@
package main
import (
"flag"
"github.com/elazarl/goproxy"
"log"
"net"
"net/http"
)
func main() {
verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
addr := flag.String("addr", ":8080", "proxy listen address")
flag.Parse()
proxy := goproxy.NewProxyHttpServer()
proxy.Tr.Dial = func(network, addr string) (c net.Conn, err error) {
c, err = net.Dial(network, addr)
if c, ok := c.(*net.TCPConn); err == nil && ok {
c.SetKeepAlive(true)
}
return
}
proxy.Verbose = *verbose
log.Fatal(http.ListenAndServe(*addr, proxy))
}

View file

@ -0,0 +1,24 @@
package main
import (
"github.com/elazarl/goproxy"
"log"
"flag"
"net/http"
)
func main() {
verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
addr := flag.String("addr", ":8080", "proxy listen address")
flag.Parse()
proxy := goproxy.NewProxyHttpServer()
proxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
proxy.OnRequest().DoFunc(func (req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
if req.URL.Scheme == "https" {
req.URL.Scheme = "http"
}
return req, nil
})
proxy.Verbose = *verbose
log.Fatal(http.ListenAndServe(*addr, proxy))
}

View file

@ -0,0 +1,43 @@
# Gather Browsing Statistics
`goproxy-stats` starts an HTTP proxy on :8080, counts the bytes received for
web resources and prints the cumulative sum per URL every 20 seconds.
Start it in one shell:
```sh
goproxy-stats
```
Fetch goproxy homepage in another:
```sh
mkdir tmp
cd tmp
http_proxy=http://127.0.0.1:8080 wget -r -l 1 -H \
http://ripper234.com/p/introducing-goproxy-light-http-proxy/
```
Stop it after a moment. `goproxy-stats` should eventually print:
```sh
listening on :8080
statistics
http://www.telerik.com/fiddler -> 84335
http://msmvps.com/robots.txt -> 157
http://eli.thegreenplace.net/robots.txt -> 294
http://www.phdcomics.com/robots.txt -> 211
http://resharper.blogspot.com/robots.txt -> 221
http://idanz.blogli.co.il/robots.txt -> 271
http://ripper234.com/p/introducing-goproxy-light-http-proxy/ -> 44407
http://live.gnome.org/robots.txt -> 298
http://ponetium.wordpress.com/robots.txt -> 178
http://pilaheleg.blogli.co.il/robots.txt -> 321
http://pilaheleg.wordpress.com/robots.txt -> 178
http://blogli.co.il/ -> 9165
http://nimrod-code.org/robots.txt -> 289
http://www.joelonsoftware.com/robots.txt -> 1245
http://top-performance.blogspot.com/robots.txt -> 227
http://ooc-lang.org/robots.txt -> 345
http://blogs.jetbrains.com/robots.txt -> 293
```

View file

@ -0,0 +1,66 @@
package main
import (
"fmt"
"github.com/elazarl/goproxy"
"github.com/elazarl/goproxy/ext/html"
"io"
"log"
. "net/http"
"time"
)
type Count struct {
Id string
Count int64
}
type CountReadCloser struct {
Id string
R io.ReadCloser
ch chan<- Count
nr int64
}
func (c *CountReadCloser) Read(b []byte) (n int, err error) {
n, err = c.R.Read(b)
c.nr += int64(n)
return
}
func (c CountReadCloser) Close() error {
c.ch <- Count{c.Id, c.nr}
return c.R.Close()
}
func main() {
proxy := goproxy.NewProxyHttpServer()
timer := make(chan bool)
ch := make(chan Count, 10)
go func() {
for {
time.Sleep(20 * time.Second)
timer <- true
}
}()
go func() {
m := make(map[string]int64)
for {
select {
case c := <-ch:
m[c.Id] = m[c.Id] + c.Count
case <-timer:
fmt.Printf("statistics\n")
for k, v := range m {
fmt.Printf("%s -> %d\n", k, v)
}
}
}
}()
// IsWebRelatedText filters on html/javascript/css resources
proxy.OnResponse(goproxy_html.IsWebRelatedText).DoFunc(func(resp *Response, ctx *goproxy.ProxyCtx) *Response {
resp.Body = &CountReadCloser{ctx.Req.URL.String(), resp.Body, ch, 0}
return resp
})
fmt.Printf("listening on :8080\n")
log.Fatal(ListenAndServe(":8080", proxy))
}

View file

@ -0,0 +1,17 @@
# Transparent Proxy
This transparent example in goproxy is meant to show how to transparenty proxy and hijack all http and https connections while doing a man-in-the-middle to the TLS session. It requires that goproxy sees all the packets traversing out to the internet. Linux iptables rules deal with changing the source/destination IPs to act transparently, but you do need to setup your network configuration so that goproxy is a mandatory stop on the outgoing route. Primarily you can do this by placing the proxy inline. goproxy does not have any WCCP support itself; patches welcome.
## Why not explicit?
Transparent proxies are more difficult to maintain and setup from a server side, but they require no configuration on the client(s) which could be in unmanaged systems or systems that don't support a proxy configuration. See the [eavesdropper example](https://github.com/elazarl/goproxy/blob/master/examples/goproxy-eavesdropper/main.go) if you want to see an explicit proxy example.
## Potential Issues
Support for very old clients using HTTPS will fail. Clients need to send the SNI value in the TLS ClientHello which most modern clients do these days, but old clients will break.
If you're routing table allows for it, an explicit http request to goproxy will cause it to fail in an endless loop since it will try to request resources from itself repeatedly. This could be solved in the goproxy code by looking up the hostnames, but it adds a delay that is much easier/faster to handle on the routing side.
## Routing Rules
Example routing rules are included in [proxy.sh](https://github.com/elazarl/goproxy/blob/master/examples/goproxy-transparent/proxy.sh) but are best when setup using your distribution's configuration.

View file

@ -0,0 +1,29 @@
#!/bin/sh
# goproxy IP
GOPROXY_SERVER="10.10.10.1"
# goproxy port
GOPROXY_PORT="3129"
GOPROXY_PORT_TLS="3128"
# DO NOT MODIFY BELOW
# Load IPTABLES modules for NAT and IP conntrack support
modprobe ip_conntrack
modprobe ip_conntrack_ftp
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter
# Clean old firewall
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
# Write new rules
iptables -t nat -A PREROUTING -s $GOPROXY_SERVER -p tcp --dport $GOPROXY_PORT -j ACCEPT
iptables -t nat -A PREROUTING -s $GOPROXY_SERVER -p tcp --dport $GOPROXY_PORT_TLS -j ACCEPT
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination $GOPROXY_SERVER:$GOPROXY_PORT
iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination $GOPROXY_SERVER:$GOPROXY_PORT_TLS
# The following line supports using goproxy as an explicit proxy in addition
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination $GOPROXY_SERVER:$GOPROXY_PORT
iptables -t nat -A POSTROUTING -j MASQUERADE
iptables -t mangle -A PREROUTING -p tcp --dport $GOPROXY_PORT -j DROP
iptables -t mangle -A PREROUTING -p tcp --dport $GOPROXY_PORT_TLS -j DROP

View file

@ -0,0 +1,148 @@
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
"log"
"net"
"net/http"
"net/url"
"regexp"
"github.com/elazarl/goproxy"
"github.com/inconshreveable/go-vhost"
)
func orPanic(err error) {
if err != nil {
panic(err)
}
}
func main() {
verbose := flag.Bool("v", true, "should every proxy request be logged to stdout")
http_addr := flag.String("httpaddr", ":3129", "proxy http listen address")
https_addr := flag.String("httpsaddr", ":3128", "proxy https listen address")
flag.Parse()
proxy := goproxy.NewProxyHttpServer()
proxy.Verbose = *verbose
if proxy.Verbose {
log.Printf("Server starting up! - configured to listen on http interface %s and https interface %s", *http_addr, *https_addr)
}
proxy.NonproxyHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if req.Host == "" {
fmt.Fprintln(w, "Cannot handle requests without Host header, e.g., HTTP 1.0")
return
}
req.URL.Scheme = "http"
req.URL.Host = req.Host
proxy.ServeHTTP(w, req)
})
proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*$"))).
HandleConnect(goproxy.AlwaysMitm)
proxy.OnRequest(goproxy.ReqHostMatches(regexp.MustCompile("^.*:80$"))).
HijackConnect(func(req *http.Request, client net.Conn, ctx *goproxy.ProxyCtx) {
defer func() {
if e := recover(); e != nil {
ctx.Logf("error connecting to remote: %v", e)
client.Write([]byte("HTTP/1.1 500 Cannot reach destination\r\n\r\n"))
}
client.Close()
}()
clientBuf := bufio.NewReadWriter(bufio.NewReader(client), bufio.NewWriter(client))
remote, err := connectDial(proxy, "tcp", req.URL.Host)
orPanic(err)
remoteBuf := bufio.NewReadWriter(bufio.NewReader(remote), bufio.NewWriter(remote))
for {
req, err := http.ReadRequest(clientBuf.Reader)
orPanic(err)
orPanic(req.Write(remoteBuf))
orPanic(remoteBuf.Flush())
resp, err := http.ReadResponse(remoteBuf.Reader, req)
orPanic(err)
orPanic(resp.Write(clientBuf.Writer))
orPanic(clientBuf.Flush())
}
})
go func() {
log.Fatalln(http.ListenAndServe(*http_addr, proxy))
}()
// listen to the TLS ClientHello but make it a CONNECT request instead
ln, err := net.Listen("tcp", *https_addr)
if err != nil {
log.Fatalf("Error listening for https connections - %v", err)
}
for {
c, err := ln.Accept()
if err != nil {
log.Printf("Error accepting new connection - %v", err)
continue
}
go func(c net.Conn) {
tlsConn, err := vhost.TLS(c)
if err != nil {
log.Printf("Error accepting new connection - %v", err)
}
if tlsConn.Host() == "" {
log.Printf("Cannot support non-SNI enabled clients")
return
}
connectReq := &http.Request{
Method: "CONNECT",
URL: &url.URL{
Opaque: tlsConn.Host(),
Host: net.JoinHostPort(tlsConn.Host(), "443"),
},
Host: tlsConn.Host(),
Header: make(http.Header),
}
resp := dumbResponseWriter{tlsConn}
proxy.ServeHTTP(resp, connectReq)
}(c)
}
}
// copied/converted from https.go
func dial(proxy *goproxy.ProxyHttpServer, network, addr string) (c net.Conn, err error) {
if proxy.Tr.Dial != nil {
return proxy.Tr.Dial(network, addr)
}
return net.Dial(network, addr)
}
// copied/converted from https.go
func connectDial(proxy *goproxy.ProxyHttpServer, network, addr string) (c net.Conn, err error) {
if proxy.ConnectDial == nil {
return dial(proxy, network, addr)
}
return proxy.ConnectDial(network, addr)
}
type dumbResponseWriter struct {
net.Conn
}
func (dumb dumbResponseWriter) Header() http.Header {
panic("Header() should not be called on this ResponseWriter")
}
func (dumb dumbResponseWriter) Write(buf []byte) (int, error) {
if bytes.Equal(buf, []byte("HTTP/1.0 200 OK\r\n\r\n")) {
return len(buf), nil // throw away the HTTP OK response from the faux CONNECT request
}
return dumb.Conn.Write(buf)
}
func (dumb dumbResponseWriter) WriteHeader(code int) {
panic("WriteHeader() should not be called on this ResponseWriter")
}
func (dumb dumbResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return dumb, bufio.NewReadWriter(bufio.NewReader(dumb), bufio.NewWriter(dumb)), nil
}

View file

@ -0,0 +1,26 @@
package main
import (
"github.com/elazarl/goproxy"
"github.com/elazarl/goproxy/ext/image"
"image"
"log"
"net/http"
)
func main() {
proxy := goproxy.NewProxyHttpServer()
proxy.OnResponse().Do(goproxy_image.HandleImage(func(img image.Image, ctx *goproxy.ProxyCtx) image.Image {
dx, dy := img.Bounds().Dx(), img.Bounds().Dy()
nimg := image.NewRGBA(img.Bounds())
for i := 0; i < dx; i++ {
for j := 0; j <= dy; j++ {
nimg.Set(i, j, img.At(i, dy-j-1))
}
}
return nimg
}))
proxy.Verbose = true
log.Fatal(http.ListenAndServe(":8080", proxy))
}

View file

@ -0,0 +1,91 @@
// This example would minify standalone Javascript files (identified by their content type)
// using the command line utility YUI compressor http://yui.github.io/yuicompressor/
// Example usage:
//
// ./yui -java /usr/local/bin/java -yuicompressor ~/Downloads/yuicompressor-2.4.8.jar
// $ curl -vx localhost:8080 http://golang.org/lib/godoc/godocs.js
// (function(){function g(){var u=$("#search");if(u.length===0){return}function t(){if(....
// $ curl http://golang.org/lib/godoc/godocs.js | head -n 3
// // Copyright 2012 The Go Authors. All rights reserved.
// // Use of this source code is governed by a BSD-style
// // license that can be found in the LICENSE file.
package main
import (
"flag"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"path"
"strings"
"github.com/elazarl/goproxy"
)
func main() {
verbose := flag.Bool("v", false, "should every proxy request be logged to stdout")
addr := flag.String("addr", ":8080", "proxy listen address")
java := flag.String("javapath", "java", "where the Java executable is located")
yuicompressor := flag.String("yuicompressor", "", "where the yuicompressor is located, assumed to be in CWD")
yuicompressordir := flag.String("yuicompressordir", ".", "a folder to search yuicompressor in, will be ignored if yuicompressor is set")
flag.Parse()
if *yuicompressor == "" {
files, err := ioutil.ReadDir(*yuicompressordir)
if err != nil {
log.Fatal("Cannot find yuicompressor jar")
}
for _, file := range files {
if strings.HasPrefix(file.Name(), "yuicompressor") && strings.HasSuffix(file.Name(), ".jar") {
c := path.Join(*yuicompressordir, file.Name())
yuicompressor = &c
break
}
}
}
if *yuicompressor == "" {
log.Fatal("Can't find yuicompressor jar, searched yuicompressor*.jar in dir ", *yuicompressordir)
}
if _, err := os.Stat(*yuicompressor); os.IsNotExist(err) {
log.Fatal("Can't find yuicompressor jar specified ", *yuicompressor)
}
proxy := goproxy.NewProxyHttpServer()
proxy.Verbose = *verbose
proxy.OnResponse().DoFunc(func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
contentType := resp.Header.Get("Content-Type")
if contentType == "application/javascript" || contentType == "application/x-javascript" {
// in real code, response should be streamed as well
var err error
cmd := exec.Command(*java, "-jar", *yuicompressor, "--type", "js")
cmd.Stdin = resp.Body
resp.Body, err = cmd.StdoutPipe()
if err != nil {
ctx.Warnf("Cannot minify content in %v: %v", ctx.Req.URL, err)
return goproxy.TextResponse(ctx.Req, "Error getting stdout pipe")
}
stderr, err := cmd.StderrPipe()
if err != nil {
ctx.Logf("Error obtaining stderr from yuicompress: %s", err)
return goproxy.TextResponse(ctx.Req, "Error getting stderr pipe")
}
if err := cmd.Start(); err != nil {
ctx.Warnf("Cannot minify content in %v: %v", ctx.Req.URL, err)
}
go func() {
defer stderr.Close()
const kb = 1024
msg, err := ioutil.ReadAll(&io.LimitedReader{stderr, 50 * kb})
if len(msg) != 0 {
ctx.Logf("Error executing yuicompress: %s", string(msg))
}
if err != nil {
ctx.Logf("Error reading stderr from yuicompress: %s", string(msg))
}
}()
}
return resp
})
log.Fatal(http.ListenAndServe(*addr, proxy))
}

Some files were not shown because too many files have changed in this diff Show more