Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
B
beego
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
beego
Commits
efe0f673
Unverified
Commit
efe0f673
authored
Jul 31, 2018
by
astaxie
Committed by
GitHub
Jul 31, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #3267 from astaxie/develop
beego 1.10.1
parents
0e4fe4d1
de66d2bd
Show whitespace changes
Inline
Side-by-side
Showing
35 changed files
with
4618 additions
and
35 deletions
+4618
-35
.travis.yml
.travis.yml
+4
-4
beego.go
beego.go
+1
-1
yaml.go
config/yaml/yaml.go
+4
-1
file.go
logs/file.go
+71
-10
file_test.go
logs/file_test.go
+127
-10
logger.go
logs/logger.go
+3
-3
logger_test.go
logs/logger_test.go
+2
-2
db.go
orm/db.go
+2
-0
orm.go
orm/orm.go
+8
-1
orm_log.go
orm/orm_log.go
+8
-0
orm_test.go
orm/orm_test.go
+72
-3
types.go
orm/types.go
+13
-0
LICENSE
vendor/golang.org/x/crypto/LICENSE
+27
-0
PATENTS
vendor/golang.org/x/crypto/PATENTS
+22
-0
acme.go
vendor/golang.org/x/crypto/acme/acme.go
+921
-0
autocert.go
vendor/golang.org/x/crypto/acme/autocert/autocert.go
+1127
-0
cache.go
vendor/golang.org/x/crypto/acme/autocert/cache.go
+130
-0
listener.go
vendor/golang.org/x/crypto/acme/autocert/listener.go
+157
-0
renewal.go
vendor/golang.org/x/crypto/acme/autocert/renewal.go
+141
-0
http.go
vendor/golang.org/x/crypto/acme/http.go
+281
-0
jws.go
vendor/golang.org/x/crypto/acme/jws.go
+153
-0
types.go
vendor/golang.org/x/crypto/acme/types.go
+329
-0
pbkdf2.go
vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go
+77
-0
LICENSE
vendor/golang.org/x/net/LICENSE
+27
-0
PATENTS
vendor/golang.org/x/net/PATENTS
+22
-0
context.go
vendor/golang.org/x/net/context/context.go
+54
-0
go17.go
vendor/golang.org/x/net/context/go17.go
+72
-0
go19.go
vendor/golang.org/x/net/context/go19.go
+20
-0
pre_go17.go
vendor/golang.org/x/net/context/pre_go17.go
+300
-0
pre_go19.go
vendor/golang.org/x/net/context/pre_go19.go
+109
-0
LICENSE
vendor/google.golang.org/appengine/LICENSE
+202
-0
cloudsql.go
vendor/google.golang.org/appengine/cloudsql/cloudsql.go
+62
-0
cloudsql_classic.go
.../google.golang.org/appengine/cloudsql/cloudsql_classic.go
+17
-0
cloudsql_vm.go
vendor/google.golang.org/appengine/cloudsql/cloudsql_vm.go
+16
-0
vendor.json
vendor/vendor.json
+37
-0
No files found.
.travis.yml
View file @
efe0f673
language
:
go
go
:
-
"
1.9.
2
"
-
"
1.9.
7
"
-
"
1.10.3"
services
:
-
redis-server
...
...
@@ -44,8 +44,8 @@ before_script:
-
sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi"
-
sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi"
-
sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi"
-
sh -c "
if [ $(go version) == *1.[5-9]* ]; then go get github.com/golang/lint/golint; golint ./...; fi
"
-
sh -c "
if [ $(go version) == *1.[5-9]* ]; then go tool vet .; fi
"
-
sh -c "
go get github.com/golang/lint/golint; golint ./...;
"
-
sh -c "
go list ./... | grep -v vendor | xargs go vet -v
"
-
mkdir -p res/var
-
./ssdb/ssdb-server ./ssdb/ssdb.conf -d
after_script
:
...
...
@@ -59,4 +59,4 @@ script:
-
find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s
-
golint ./...
addons
:
postgresql
:
"
9.
4
"
postgresql
:
"
9.
6
"
beego.go
View file @
efe0f673
...
...
@@ -23,7 +23,7 @@ import (
const
(
// VERSION represent beego web framework version.
VERSION
=
"1.10.
0
"
VERSION
=
"1.10.
1
"
// DEV is for develop
DEV
=
"dev"
...
...
config/yaml/yaml.go
View file @
efe0f673
...
...
@@ -290,12 +290,15 @@ func (c *ConfigContainer) getData(key string) (interface{}, error) {
keys
:=
strings
.
Split
(
key
,
"."
)
tmpData
:=
c
.
data
for
_
,
k
:=
range
keys
{
for
idx
,
k
:=
range
keys
{
if
v
,
ok
:=
tmpData
[
k
];
ok
{
switch
v
.
(
type
)
{
case
map
[
string
]
interface
{}
:
{
tmpData
=
v
.
(
map
[
string
]
interface
{})
if
idx
==
len
(
keys
)
-
1
{
return
tmpData
,
nil
}
}
default
:
{
...
...
logs/file.go
View file @
efe0f673
...
...
@@ -54,6 +54,12 @@ type fileLogWriter struct {
dailyOpenDate
int
dailyOpenTime
time
.
Time
// Rotate hourly
Hourly
bool
`json:"hourly"`
MaxHours
int64
`json:"maxhours"`
hourlyOpenDate
int
hourlyOpenTime
time
.
Time
Rotate
bool
`json:"rotate"`
Level
int
`json:"level"`
...
...
@@ -70,6 +76,8 @@ func newFileWriter() Logger {
w
:=
&
fileLogWriter
{
Daily
:
true
,
MaxDays
:
7
,
Hourly
:
false
,
MaxHours
:
168
,
Rotate
:
true
,
RotatePerm
:
"0440"
,
Level
:
LevelTrace
,
...
...
@@ -122,10 +130,16 @@ func (w *fileLogWriter) startLogger() error {
return
w
.
initFd
()
}
func
(
w
*
fileLogWriter
)
needRotate
(
size
int
,
day
int
)
bool
{
func
(
w
*
fileLogWriter
)
needRotate
Daily
(
size
int
,
day
int
)
bool
{
return
(
w
.
MaxLines
>
0
&&
w
.
maxLinesCurLines
>=
w
.
MaxLines
)
||
(
w
.
MaxSize
>
0
&&
w
.
maxSizeCurSize
>=
w
.
MaxSize
)
||
(
w
.
Daily
&&
day
!=
w
.
dailyOpenDate
)
}
func
(
w
*
fileLogWriter
)
needRotateHourly
(
size
int
,
hour
int
)
bool
{
return
(
w
.
MaxLines
>
0
&&
w
.
maxLinesCurLines
>=
w
.
MaxLines
)
||
(
w
.
MaxSize
>
0
&&
w
.
maxSizeCurSize
>=
w
.
MaxSize
)
||
(
w
.
Hourly
&&
hour
!=
w
.
hourlyOpenDate
)
}
...
...
@@ -134,14 +148,23 @@ func (w *fileLogWriter) WriteMsg(when time.Time, msg string, level int) error {
if
level
>
w
.
Level
{
return
nil
}
h
,
d
:=
formatTimeHeader
(
when
)
msg
=
string
(
h
)
+
msg
+
"
\n
"
h
d
,
d
,
h
:=
formatTimeHeader
(
when
)
msg
=
string
(
h
d
)
+
msg
+
"
\n
"
if
w
.
Rotate
{
w
.
RLock
()
if
w
.
needRotate
(
len
(
msg
),
d
)
{
if
w
.
needRotateHourly
(
len
(
msg
),
h
)
{
w
.
RUnlock
()
w
.
Lock
()
if
w
.
needRotateHourly
(
len
(
msg
),
h
)
{
if
err
:=
w
.
doRotate
(
when
);
err
!=
nil
{
fmt
.
Fprintf
(
os
.
Stderr
,
"FileLogWriter(%q): %s
\n
"
,
w
.
Filename
,
err
)
}
}
w
.
Unlock
()
}
else
if
w
.
needRotateDaily
(
len
(
msg
),
d
)
{
w
.
RUnlock
()
w
.
Lock
()
if
w
.
needRotate
(
len
(
msg
),
d
)
{
if
w
.
needRotate
Daily
(
len
(
msg
),
d
)
{
if
err
:=
w
.
doRotate
(
when
);
err
!=
nil
{
fmt
.
Fprintf
(
os
.
Stderr
,
"FileLogWriter(%q): %s
\n
"
,
w
.
Filename
,
err
)
}
...
...
@@ -189,8 +212,12 @@ func (w *fileLogWriter) initFd() error {
w
.
maxSizeCurSize
=
int
(
fInfo
.
Size
())
w
.
dailyOpenTime
=
time
.
Now
()
w
.
dailyOpenDate
=
w
.
dailyOpenTime
.
Day
()
w
.
hourlyOpenTime
=
time
.
Now
()
w
.
hourlyOpenDate
=
w
.
hourlyOpenTime
.
Hour
()
w
.
maxLinesCurLines
=
0
if
w
.
Daily
{
if
w
.
Hourly
{
go
w
.
hourlyRotate
(
w
.
hourlyOpenTime
)
}
else
if
w
.
Daily
{
go
w
.
dailyRotate
(
w
.
dailyOpenTime
)
}
if
fInfo
.
Size
()
>
0
&&
w
.
MaxLines
>
0
{
...
...
@@ -209,7 +236,22 @@ func (w *fileLogWriter) dailyRotate(openTime time.Time) {
tm
:=
time
.
NewTimer
(
time
.
Duration
(
nextDay
.
UnixNano
()
-
openTime
.
UnixNano
()
+
100
))
<-
tm
.
C
w
.
Lock
()
if
w
.
needRotate
(
0
,
time
.
Now
()
.
Day
())
{
if
w
.
needRotateDaily
(
0
,
time
.
Now
()
.
Day
())
{
if
err
:=
w
.
doRotate
(
time
.
Now
());
err
!=
nil
{
fmt
.
Fprintf
(
os
.
Stderr
,
"FileLogWriter(%q): %s
\n
"
,
w
.
Filename
,
err
)
}
}
w
.
Unlock
()
}
func
(
w
*
fileLogWriter
)
hourlyRotate
(
openTime
time
.
Time
)
{
y
,
m
,
d
:=
openTime
.
Add
(
1
*
time
.
Hour
)
.
Date
()
h
,
_
,
_
:=
openTime
.
Add
(
1
*
time
.
Hour
)
.
Clock
()
nextHour
:=
time
.
Date
(
y
,
m
,
d
,
h
,
0
,
0
,
0
,
openTime
.
Location
())
tm
:=
time
.
NewTimer
(
time
.
Duration
(
nextHour
.
UnixNano
()
-
openTime
.
UnixNano
()
+
100
))
<-
tm
.
C
w
.
Lock
()
if
w
.
needRotateHourly
(
0
,
time
.
Now
()
.
Hour
())
{
if
err
:=
w
.
doRotate
(
time
.
Now
());
err
!=
nil
{
fmt
.
Fprintf
(
os
.
Stderr
,
"FileLogWriter(%q): %s
\n
"
,
w
.
Filename
,
err
)
}
...
...
@@ -251,6 +293,8 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error {
// Find the next available number
num
:=
w
.
MaxFilesCurFiles
+
1
fName
:=
""
format
:=
""
var
openTime
time
.
Time
rotatePerm
,
err
:=
strconv
.
ParseInt
(
w
.
RotatePerm
,
8
,
64
)
if
err
!=
nil
{
return
err
...
...
@@ -262,17 +306,26 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error {
goto
RESTART_LOGGER
}
if
w
.
Hourly
{
format
=
"2006010215"
openTime
=
w
.
hourlyOpenTime
}
else
if
w
.
Daily
{
format
=
"2006-01-02"
openTime
=
w
.
dailyOpenTime
}
// only when one of them be setted, then the file would be splited
if
w
.
MaxLines
>
0
||
w
.
MaxSize
>
0
{
for
;
err
==
nil
&&
num
<=
w
.
MaxFiles
;
num
++
{
fName
=
w
.
fileNameOnly
+
fmt
.
Sprintf
(
".%s.%03d%s"
,
logTime
.
Format
(
"2006-01-02"
),
num
,
w
.
suffix
)
fName
=
w
.
fileNameOnly
+
fmt
.
Sprintf
(
".%s.%03d%s"
,
logTime
.
Format
(
format
),
num
,
w
.
suffix
)
_
,
err
=
os
.
Lstat
(
fName
)
}
}
else
{
fName
=
w
.
fileNameOnly
+
fmt
.
Sprintf
(
".%s.%03d%s"
,
w
.
dailyOpenTime
.
Format
(
"2006-01-02"
),
num
,
w
.
suffix
)
fName
=
w
.
fileNameOnly
+
fmt
.
Sprintf
(
".%s.%03d%s"
,
openTime
.
Format
(
format
),
num
,
w
.
suffix
)
_
,
err
=
os
.
Lstat
(
fName
)
w
.
MaxFilesCurFiles
=
num
}
// return error if the last file checked still existed
if
err
==
nil
{
return
fmt
.
Errorf
(
"Rotate: Cannot find free log number to rename %s"
,
w
.
Filename
)
...
...
@@ -316,12 +369,20 @@ func (w *fileLogWriter) deleteOldLog() {
if
info
==
nil
{
return
}
if
w
.
Hourly
{
if
!
info
.
IsDir
()
&&
info
.
ModTime
()
.
Add
(
1
*
time
.
Hour
*
time
.
Duration
(
w
.
MaxHours
))
.
Before
(
time
.
Now
())
{
if
strings
.
HasPrefix
(
filepath
.
Base
(
path
),
filepath
.
Base
(
w
.
fileNameOnly
))
&&
strings
.
HasSuffix
(
filepath
.
Base
(
path
),
w
.
suffix
)
{
os
.
Remove
(
path
)
}
}
}
else
if
w
.
Daily
{
if
!
info
.
IsDir
()
&&
info
.
ModTime
()
.
Add
(
24
*
time
.
Hour
*
time
.
Duration
(
w
.
MaxDays
))
.
Before
(
time
.
Now
())
{
if
strings
.
HasPrefix
(
filepath
.
Base
(
path
),
filepath
.
Base
(
w
.
fileNameOnly
))
&&
strings
.
HasSuffix
(
filepath
.
Base
(
path
),
w
.
suffix
)
{
os
.
Remove
(
path
)
}
}
}
return
})
...
...
logs/file_test.go
View file @
efe0f673
...
...
@@ -112,7 +112,7 @@ func TestFile2(t *testing.T) {
os
.
Remove
(
"test2.log"
)
}
func
TestFileRotate_01
(
t
*
testing
.
T
)
{
func
TestFile
Daily
Rotate_01
(
t
*
testing
.
T
)
{
log
:=
NewLogger
(
10000
)
log
.
SetLogger
(
"file"
,
`{"filename":"test3.log","maxlines":4}`
)
log
.
Debug
(
"debug"
)
...
...
@@ -133,28 +133,28 @@ func TestFileRotate_01(t *testing.T) {
os
.
Remove
(
"test3.log"
)
}
func
TestFileRotate_02
(
t
*
testing
.
T
)
{
func
TestFile
Daily
Rotate_02
(
t
*
testing
.
T
)
{
fn1
:=
"rotate_day.log"
fn2
:=
"rotate_day."
+
time
.
Now
()
.
Add
(
-
24
*
time
.
Hour
)
.
Format
(
"2006-01-02"
)
+
".001.log"
testFileRotate
(
t
,
fn1
,
fn2
)
testFileRotate
(
t
,
fn1
,
fn2
,
true
,
false
)
}
func
TestFileRotate_03
(
t
*
testing
.
T
)
{
func
TestFile
Daily
Rotate_03
(
t
*
testing
.
T
)
{
fn1
:=
"rotate_day.log"
fn
:=
"rotate_day."
+
time
.
Now
()
.
Add
(
-
24
*
time
.
Hour
)
.
Format
(
"2006-01-02"
)
+
".log"
os
.
Create
(
fn
)
fn2
:=
"rotate_day."
+
time
.
Now
()
.
Add
(
-
24
*
time
.
Hour
)
.
Format
(
"2006-01-02"
)
+
".001.log"
testFileRotate
(
t
,
fn1
,
fn2
)
testFileRotate
(
t
,
fn1
,
fn2
,
true
,
false
)
os
.
Remove
(
fn
)
}
func
TestFileRotate_04
(
t
*
testing
.
T
)
{
func
TestFile
Daily
Rotate_04
(
t
*
testing
.
T
)
{
fn1
:=
"rotate_day.log"
fn2
:=
"rotate_day."
+
time
.
Now
()
.
Add
(
-
24
*
time
.
Hour
)
.
Format
(
"2006-01-02"
)
+
".001.log"
testFileDailyRotate
(
t
,
fn1
,
fn2
)
}
func
TestFileRotate_05
(
t
*
testing
.
T
)
{
func
TestFile
Daily
Rotate_05
(
t
*
testing
.
T
)
{
fn1
:=
"rotate_day.log"
fn
:=
"rotate_day."
+
time
.
Now
()
.
Add
(
-
24
*
time
.
Hour
)
.
Format
(
"2006-01-02"
)
+
".log"
os
.
Create
(
fn
)
...
...
@@ -162,7 +162,7 @@ func TestFileRotate_05(t *testing.T) {
testFileDailyRotate
(
t
,
fn1
,
fn2
)
os
.
Remove
(
fn
)
}
func
TestFileRotate_06
(
t
*
testing
.
T
)
{
//test file mode
func
TestFile
Daily
Rotate_06
(
t
*
testing
.
T
)
{
//test file mode
log
:=
NewLogger
(
10000
)
log
.
SetLogger
(
"file"
,
`{"filename":"test3.log","maxlines":4}`
)
log
.
Debug
(
"debug"
)
...
...
@@ -183,18 +183,104 @@ func TestFileRotate_06(t *testing.T) { //test file mode
os
.
Remove
(
rotateName
)
os
.
Remove
(
"test3.log"
)
}
func
testFileRotate
(
t
*
testing
.
T
,
fn1
,
fn2
string
)
{
func
TestFileHourlyRotate_01
(
t
*
testing
.
T
)
{
log
:=
NewLogger
(
10000
)
log
.
SetLogger
(
"file"
,
`{"filename":"test3.log","hourly":true,"maxlines":4}`
)
log
.
Debug
(
"debug"
)
log
.
Info
(
"info"
)
log
.
Notice
(
"notice"
)
log
.
Warning
(
"warning"
)
log
.
Error
(
"error"
)
log
.
Alert
(
"alert"
)
log
.
Critical
(
"critical"
)
log
.
Emergency
(
"emergency"
)
rotateName
:=
"test3"
+
fmt
.
Sprintf
(
".%s.%03d"
,
time
.
Now
()
.
Format
(
"2006010215"
),
1
)
+
".log"
b
,
err
:=
exists
(
rotateName
)
if
!
b
||
err
!=
nil
{
os
.
Remove
(
"test3.log"
)
t
.
Fatal
(
"rotate not generated"
)
}
os
.
Remove
(
rotateName
)
os
.
Remove
(
"test3.log"
)
}
func
TestFileHourlyRotate_02
(
t
*
testing
.
T
)
{
fn1
:=
"rotate_hour.log"
fn2
:=
"rotate_hour."
+
time
.
Now
()
.
Add
(
-
1
*
time
.
Hour
)
.
Format
(
"2006010215"
)
+
".001.log"
testFileRotate
(
t
,
fn1
,
fn2
,
false
,
true
)
}
func
TestFileHourlyRotate_03
(
t
*
testing
.
T
)
{
fn1
:=
"rotate_hour.log"
fn
:=
"rotate_hour."
+
time
.
Now
()
.
Add
(
-
1
*
time
.
Hour
)
.
Format
(
"2006010215"
)
+
".log"
os
.
Create
(
fn
)
fn2
:=
"rotate_hour."
+
time
.
Now
()
.
Add
(
-
1
*
time
.
Hour
)
.
Format
(
"2006010215"
)
+
".001.log"
testFileRotate
(
t
,
fn1
,
fn2
,
false
,
true
)
os
.
Remove
(
fn
)
}
func
TestFileHourlyRotate_04
(
t
*
testing
.
T
)
{
fn1
:=
"rotate_hour.log"
fn2
:=
"rotate_hour."
+
time
.
Now
()
.
Add
(
-
1
*
time
.
Hour
)
.
Format
(
"2006010215"
)
+
".001.log"
testFileHourlyRotate
(
t
,
fn1
,
fn2
)
}
func
TestFileHourlyRotate_05
(
t
*
testing
.
T
)
{
fn1
:=
"rotate_hour.log"
fn
:=
"rotate_hour."
+
time
.
Now
()
.
Add
(
-
1
*
time
.
Hour
)
.
Format
(
"2006010215"
)
+
".log"
os
.
Create
(
fn
)
fn2
:=
"rotate_hour."
+
time
.
Now
()
.
Add
(
-
1
*
time
.
Hour
)
.
Format
(
"2006010215"
)
+
".001.log"
testFileHourlyRotate
(
t
,
fn1
,
fn2
)
os
.
Remove
(
fn
)
}
func
TestFileHourlyRotate_06
(
t
*
testing
.
T
)
{
//test file mode
log
:=
NewLogger
(
10000
)
log
.
SetLogger
(
"file"
,
`{"filename":"test3.log", "hourly":true, "maxlines":4}`
)
log
.
Debug
(
"debug"
)
log
.
Info
(
"info"
)
log
.
Notice
(
"notice"
)
log
.
Warning
(
"warning"
)
log
.
Error
(
"error"
)
log
.
Alert
(
"alert"
)
log
.
Critical
(
"critical"
)
log
.
Emergency
(
"emergency"
)
rotateName
:=
"test3"
+
fmt
.
Sprintf
(
".%s.%03d"
,
time
.
Now
()
.
Format
(
"2006010215"
),
1
)
+
".log"
s
,
_
:=
os
.
Lstat
(
rotateName
)
if
s
.
Mode
()
!=
0440
{
os
.
Remove
(
rotateName
)
os
.
Remove
(
"test3.log"
)
t
.
Fatal
(
"rotate file mode error"
)
}
os
.
Remove
(
rotateName
)
os
.
Remove
(
"test3.log"
)
}
func
testFileRotate
(
t
*
testing
.
T
,
fn1
,
fn2
string
,
daily
,
hourly
bool
)
{
fw
:=
&
fileLogWriter
{
Daily
:
true
,
Daily
:
daily
,
MaxDays
:
7
,
Hourly
:
hourly
,
MaxHours
:
168
,
Rotate
:
true
,
Level
:
LevelTrace
,
Perm
:
"0660"
,
RotatePerm
:
"0440"
,
}
if
daily
{
fw
.
Init
(
fmt
.
Sprintf
(
`{"filename":"%v","maxdays":1}`
,
fn1
))
fw
.
dailyOpenTime
=
time
.
Now
()
.
Add
(
-
24
*
time
.
Hour
)
fw
.
dailyOpenDate
=
fw
.
dailyOpenTime
.
Day
()
}
if
hourly
{
fw
.
Init
(
fmt
.
Sprintf
(
`{"filename":"%v","maxhours":1}`
,
fn1
))
fw
.
hourlyOpenTime
=
time
.
Now
()
.
Add
(
-
1
*
time
.
Hour
)
fw
.
hourlyOpenDate
=
fw
.
hourlyOpenTime
.
Day
()
}
fw
.
WriteMsg
(
time
.
Now
(),
"this is a msg for test"
,
LevelDebug
)
for
_
,
file
:=
range
[]
string
{
fn1
,
fn2
}
{
...
...
@@ -240,6 +326,37 @@ func testFileDailyRotate(t *testing.T, fn1, fn2 string) {
fw
.
Destroy
()
}
func
testFileHourlyRotate
(
t
*
testing
.
T
,
fn1
,
fn2
string
)
{
fw
:=
&
fileLogWriter
{
Hourly
:
true
,
MaxHours
:
168
,
Rotate
:
true
,
Level
:
LevelTrace
,
Perm
:
"0660"
,
RotatePerm
:
"0440"
,
}
fw
.
Init
(
fmt
.
Sprintf
(
`{"filename":"%v","maxhours":1}`
,
fn1
))
fw
.
hourlyOpenTime
=
time
.
Now
()
.
Add
(
-
1
*
time
.
Hour
)
fw
.
hourlyOpenDate
=
fw
.
hourlyOpenTime
.
Hour
()
hour
,
_
:=
time
.
ParseInLocation
(
"2006010215"
,
time
.
Now
()
.
Format
(
"2006010215"
),
fw
.
hourlyOpenTime
.
Location
())
hour
=
hour
.
Add
(
-
1
*
time
.
Second
)
fw
.
hourlyRotate
(
hour
)
for
_
,
file
:=
range
[]
string
{
fn1
,
fn2
}
{
_
,
err
:=
os
.
Stat
(
file
)
if
err
!=
nil
{
t
.
FailNow
()
}
content
,
err
:=
ioutil
.
ReadFile
(
file
)
if
err
!=
nil
{
t
.
FailNow
()
}
if
len
(
content
)
>
0
{
t
.
FailNow
()
}
os
.
Remove
(
file
)
}
fw
.
Destroy
()
}
func
exists
(
path
string
)
(
bool
,
error
)
{
_
,
err
:=
os
.
Stat
(
path
)
if
err
==
nil
{
...
...
logs/logger.go
View file @
efe0f673
...
...
@@ -33,7 +33,7 @@ func newLogWriter(wr io.Writer) *logWriter {
func
(
lg
*
logWriter
)
println
(
when
time
.
Time
,
msg
string
)
{
lg
.
Lock
()
h
,
_
:=
formatTimeHeader
(
when
)
h
,
_
,
_
:=
formatTimeHeader
(
when
)
lg
.
writer
.
Write
(
append
(
append
(
h
,
msg
...
),
'\n'
))
lg
.
Unlock
()
}
...
...
@@ -90,7 +90,7 @@ const (
ns1
=
`0123456789`
)
func
formatTimeHeader
(
when
time
.
Time
)
([]
byte
,
int
)
{
func
formatTimeHeader
(
when
time
.
Time
)
([]
byte
,
int
,
int
)
{
y
,
mo
,
d
:=
when
.
Date
()
h
,
mi
,
s
:=
when
.
Clock
()
ns
:=
when
.
Nanosecond
()
/
1000000
...
...
@@ -123,7 +123,7 @@ func formatTimeHeader(when time.Time) ([]byte, int) {
buf
[
23
]
=
' '
return
buf
[
0
:
],
d
return
buf
[
0
:
],
d
,
h
}
var
(
...
...
logs/logger_test.go
View file @
efe0f673
...
...
@@ -30,7 +30,7 @@ func TestFormatHeader_0(t *testing.T) {
if
tm
.
Year
()
>=
2100
{
break
}
h
,
_
:=
formatTimeHeader
(
tm
)
h
,
_
,
_
:=
formatTimeHeader
(
tm
)
if
tm
.
Format
(
"2006/01/02 15:04:05.000 "
)
!=
string
(
h
)
{
t
.
Log
(
tm
)
t
.
FailNow
()
...
...
@@ -48,7 +48,7 @@ func TestFormatHeader_1(t *testing.T) {
if
tm
.
Year
()
>=
year
+
1
{
break
}
h
,
_
:=
formatTimeHeader
(
tm
)
h
,
_
,
_
:=
formatTimeHeader
(
tm
)
if
tm
.
Format
(
"2006/01/02 15:04:05.000 "
)
!=
string
(
h
)
{
t
.
Log
(
tm
)
t
.
FailNow
()
...
...
orm/db.go
View file @
efe0f673
...
...
@@ -536,6 +536,8 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a
updates
:=
make
([]
string
,
len
(
names
))
var
conflitValue
interface
{}
for
i
,
v
:=
range
names
{
// identifier in database may not be case-sensitive, so quote it
v
=
fmt
.
Sprintf
(
"%s%s%s"
,
Q
,
v
,
Q
)
marks
[
i
]
=
"?"
valueStr
:=
argsMap
[
strings
.
ToLower
(
v
)]
if
v
==
args0
{
...
...
orm/orm.go
View file @
efe0f673
...
...
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// +build go1.8
// Package orm provide ORM for MySQL/PostgreSQL/sqlite
// Simple Usage
//
...
...
@@ -52,6 +54,7 @@
package
orm
import
(
"context"
"database/sql"
"errors"
"fmt"
...
...
@@ -458,11 +461,15 @@ func (o *orm) Using(name string) error {
// begin transaction
func
(
o
*
orm
)
Begin
()
error
{
return
o
.
BeginTx
(
context
.
Background
(),
nil
)
}
func
(
o
*
orm
)
BeginTx
(
ctx
context
.
Context
,
opts
*
sql
.
TxOptions
)
error
{
if
o
.
isTx
{
return
ErrTxHasBegan
}
var
tx
*
sql
.
Tx
tx
,
err
:=
o
.
db
.
(
txer
)
.
Begin
(
)
tx
,
err
:=
o
.
db
.
(
txer
)
.
Begin
Tx
(
ctx
,
opts
)
if
err
!=
nil
{
return
err
}
...
...
orm/orm_log.go
View file @
efe0f673
...
...
@@ -15,6 +15,7 @@
package
orm
import
(
"context"
"database/sql"
"fmt"
"io"
...
...
@@ -150,6 +151,13 @@ func (d *dbQueryLog) Begin() (*sql.Tx, error) {
return
tx
,
err
}
func
(
d
*
dbQueryLog
)
BeginTx
(
ctx
context
.
Context
,
opts
*
sql
.
TxOptions
)
(
*
sql
.
Tx
,
error
)
{
a
:=
time
.
Now
()
tx
,
err
:=
d
.
db
.
(
txer
)
.
BeginTx
(
ctx
,
opts
)
debugLogQueies
(
d
.
alias
,
"db.BeginTx"
,
"START TRANSACTION"
,
a
,
err
)
return
tx
,
err
}
func
(
d
*
dbQueryLog
)
Commit
()
error
{
a
:=
time
.
Now
()
err
:=
d
.
db
.
(
txEnder
)
.
Commit
()
...
...
orm/orm_test.go
View file @
efe0f673
...
...
@@ -12,10 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// +build go1.8
package
orm
import
(
"bytes"
"context"
"database/sql"
"fmt"
"io/ioutil"
...
...
@@ -452,9 +455,9 @@ func TestNullDataTypes(t *testing.T) {
throwFail
(
t
,
AssertIs
(
*
d
.
Float32Ptr
,
float32Ptr
))
throwFail
(
t
,
AssertIs
(
*
d
.
Float64Ptr
,
float64Ptr
))
throwFail
(
t
,
AssertIs
(
*
d
.
DecimalPtr
,
decimalPtr
))
throwFail
(
t
,
AssertIs
((
*
d
.
TimePtr
)
.
Format
(
testTime
),
timePtr
.
Format
(
testTime
)))
throwFail
(
t
,
AssertIs
((
*
d
.
DatePtr
)
.
Format
(
testDate
),
datePtr
.
Format
(
testDate
)))
throwFail
(
t
,
AssertIs
((
*
d
.
DateTimePtr
)
.
Format
(
testDateTime
),
dateTimePtr
.
Format
(
testDateTime
)))
throwFail
(
t
,
AssertIs
((
*
d
.
TimePtr
)
.
UTC
()
.
Format
(
testTime
),
timePtr
.
UTC
()
.
Format
(
testTime
)))
throwFail
(
t
,
AssertIs
((
*
d
.
DatePtr
)
.
UTC
()
.
Format
(
testDate
),
datePtr
.
UTC
()
.
Format
(
testDate
)))
throwFail
(
t
,
AssertIs
((
*
d
.
DateTimePtr
)
.
UTC
()
.
Format
(
testDateTime
),
dateTimePtr
.
UTC
()
.
Format
(
testDateTime
)))
}
func
TestDataCustomTypes
(
t
*
testing
.
T
)
{
...
...
@@ -1990,6 +1993,66 @@ func TestTransaction(t *testing.T) {
}
func
TestTransactionIsolationLevel
(
t
*
testing
.
T
)
{
// this test worked when database support transaction isolation level
if
IsSqlite
{
return
}
o1
:=
NewOrm
()
o2
:=
NewOrm
()
// start two transaction with isolation level repeatable read
err
:=
o1
.
BeginTx
(
context
.
Background
(),
&
sql
.
TxOptions
{
Isolation
:
sql
.
LevelRepeatableRead
})
throwFail
(
t
,
err
)
err
=
o2
.
BeginTx
(
context
.
Background
(),
&
sql
.
TxOptions
{
Isolation
:
sql
.
LevelRepeatableRead
})
throwFail
(
t
,
err
)
// o1 insert tag
var
tag
Tag
tag
.
Name
=
"test-transaction"
id
,
err
:=
o1
.
Insert
(
&
tag
)
throwFail
(
t
,
err
)
throwFail
(
t
,
AssertIs
(
id
>
0
,
true
))
// o2 query tag table, no result
num
,
err
:=
o2
.
QueryTable
(
"tag"
)
.
Filter
(
"name"
,
"test-transaction"
)
.
Count
()
throwFail
(
t
,
err
)
throwFail
(
t
,
AssertIs
(
num
,
0
))
// o1 commit
o1
.
Commit
()
// o2 query tag table, still no result
num
,
err
=
o2
.
QueryTable
(
"tag"
)
.
Filter
(
"name"
,
"test-transaction"
)
.
Count
()
throwFail
(
t
,
err
)
throwFail
(
t
,
AssertIs
(
num
,
0
))
// o2 commit and query tag table, get the result
o2
.
Commit
()
num
,
err
=
o2
.
QueryTable
(
"tag"
)
.
Filter
(
"name"
,
"test-transaction"
)
.
Count
()
throwFail
(
t
,
err
)
throwFail
(
t
,
AssertIs
(
num
,
1
))
num
,
err
=
o1
.
QueryTable
(
"tag"
)
.
Filter
(
"name"
,
"test-transaction"
)
.
Delete
()
throwFail
(
t
,
err
)
throwFail
(
t
,
AssertIs
(
num
,
1
))
}
func
TestBeginTxWithContextCanceled
(
t
*
testing
.
T
)
{
o
:=
NewOrm
()
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
o
.
BeginTx
(
ctx
,
nil
)
id
,
err
:=
o
.
Insert
(
&
Tag
{
Name
:
"test-context"
})
throwFail
(
t
,
err
)
throwFail
(
t
,
AssertIs
(
id
>
0
,
true
))
// cancel the context before commit to make it error
cancel
()
err
=
o
.
Commit
()
throwFail
(
t
,
AssertIs
(
err
,
context
.
Canceled
))
}
func
TestReadOrCreate
(
t
*
testing
.
T
)
{
u
:=
&
User
{
UserName
:
"Kyle"
,
...
...
@@ -2260,6 +2323,7 @@ func TestIgnoreCaseTag(t *testing.T) {
throwFail
(
t
,
AssertIs
(
info
.
fields
.
GetByName
(
"Name02"
)
.
column
,
"Name"
))
throwFail
(
t
,
AssertIs
(
info
.
fields
.
GetByName
(
"Name03"
)
.
column
,
"name"
))
}
func
TestInsertOrUpdate
(
t
*
testing
.
T
)
{
RegisterModel
(
new
(
User
))
user
:=
User
{
UserName
:
"unique_username133"
,
Status
:
1
,
Password
:
"o"
}
...
...
@@ -2297,6 +2361,11 @@ func TestInsertOrUpdate(t *testing.T) {
throwFailNow
(
t
,
AssertIs
(
user2
.
Status
,
test
.
Status
))
throwFailNow
(
t
,
AssertIs
(
user2
.
Password
,
strings
.
TrimSpace
(
test
.
Password
)))
}
//postgres ON CONFLICT DO UPDATE SET can`t use colu=colu+values
if
IsPostgres
{
return
}
//test3 +
_
,
err
=
dORM
.
InsertOrUpdate
(
&
user2
,
"user_name"
,
"status=status+1"
)
if
err
!=
nil
{
...
...
orm/types.go
View file @
efe0f673
...
...
@@ -15,6 +15,7 @@
package
orm
import
(
"context"
"database/sql"
"reflect"
"time"
...
...
@@ -106,6 +107,17 @@ type Ormer interface {
// ...
// err = o.Rollback()
Begin
()
error
// begin transaction with provided context and option
// the provided context is used until the transaction is committed or rolled back.
// if the context is canceled, the transaction will be rolled back.
// the provided TxOptions is optional and may be nil if defaults should be used.
// if a non-default isolation level is used that the driver doesn't support, an error will be returned.
// for example:
// o := NewOrm()
// err := o.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
// ...
// err = o.Rollback()
BeginTx
(
ctx
context
.
Context
,
opts
*
sql
.
TxOptions
)
error
// commit transaction
Commit
()
error
// rollback transaction
...
...
@@ -401,6 +413,7 @@ type dbQuerier interface {
// transaction beginner
type
txer
interface
{
Begin
()
(
*
sql
.
Tx
,
error
)
BeginTx
(
ctx
context
.
Context
,
opts
*
sql
.
TxOptions
)
(
*
sql
.
Tx
,
error
)
}
// transaction ending
...
...
vendor/golang.org/x/crypto/LICENSE
0 → 100644
View file @
efe0f673
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. 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.
vendor/golang.org/x/crypto/PATENTS
0 → 100644
View file @
efe0f673
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.
vendor/golang.org/x/crypto/acme/acme.go
0 → 100644
View file @
efe0f673
// Copyright 2015 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 acme provides an implementation of the
// Automatic Certificate Management Environment (ACME) spec.
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02 for details.
//
// Most common scenarios will want to use autocert subdirectory instead,
// which provides automatic access to certificates from Let's Encrypt
// and any other ACME-based CA.
//
// This package is a work in progress and makes no API stability promises.
package
acme
import
(
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/base64"
"encoding/hex"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io"
"io/ioutil"
"math/big"
"net/http"
"strings"
"sync"
"time"
)
const
(
// LetsEncryptURL is the Directory endpoint of Let's Encrypt CA.
LetsEncryptURL
=
"https://acme-v01.api.letsencrypt.org/directory"
// ALPNProto is the ALPN protocol name used by a CA server when validating
// tls-alpn-01 challenges.
//
// Package users must ensure their servers can negotiate the ACME ALPN
// in order for tls-alpn-01 challenge verifications to succeed.
ALPNProto
=
"acme-tls/1"
)
// idPeACMEIdentifierV1 is the OID for the ACME extension for the TLS-ALPN challenge.
var
idPeACMEIdentifierV1
=
asn1
.
ObjectIdentifier
{
1
,
3
,
6
,
1
,
5
,
5
,
7
,
1
,
30
,
1
}
const
(
maxChainLen
=
5
// max depth and breadth of a certificate chain
maxCertSize
=
1
<<
20
// max size of a certificate, in bytes
// Max number of collected nonces kept in memory.
// Expect usual peak of 1 or 2.
maxNonces
=
100
)
// Client is an ACME client.
// The only required field is Key. An example of creating a client with a new key
// is as follows:
//
// key, err := rsa.GenerateKey(rand.Reader, 2048)
// if err != nil {
// log.Fatal(err)
// }
// client := &Client{Key: key}
//
type
Client
struct
{
// Key is the account key used to register with a CA and sign requests.
// Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey.
Key
crypto
.
Signer
// HTTPClient optionally specifies an HTTP client to use
// instead of http.DefaultClient.
HTTPClient
*
http
.
Client
// DirectoryURL points to the CA directory endpoint.
// If empty, LetsEncryptURL is used.
// Mutating this value after a successful call of Client's Discover method
// will have no effect.
DirectoryURL
string
// RetryBackoff computes the duration after which the nth retry of a failed request
// should occur. The value of n for the first call on failure is 1.
// The values of r and resp are the request and response of the last failed attempt.
// If the returned value is negative or zero, no more retries are done and an error
// is returned to the caller of the original method.
//
// Requests which result in a 4xx client error are not retried,
// except for 400 Bad Request due to "bad nonce" errors and 429 Too Many Requests.
//
// If RetryBackoff is nil, a truncated exponential backoff algorithm
// with the ceiling of 10 seconds is used, where each subsequent retry n
// is done after either ("Retry-After" + jitter) or (2^n seconds + jitter),
// preferring the former if "Retry-After" header is found in the resp.
// The jitter is a random value up to 1 second.
RetryBackoff
func
(
n
int
,
r
*
http
.
Request
,
resp
*
http
.
Response
)
time
.
Duration
dirMu
sync
.
Mutex
// guards writes to dir
dir
*
Directory
// cached result of Client's Discover method
noncesMu
sync
.
Mutex
nonces
map
[
string
]
struct
{}
// nonces collected from previous responses
}
// Discover performs ACME server discovery using c.DirectoryURL.
//
// It caches successful result. So, subsequent calls will not result in
// a network round-trip. This also means mutating c.DirectoryURL after successful call
// of this method will have no effect.
func
(
c
*
Client
)
Discover
(
ctx
context
.
Context
)
(
Directory
,
error
)
{
c
.
dirMu
.
Lock
()
defer
c
.
dirMu
.
Unlock
()
if
c
.
dir
!=
nil
{
return
*
c
.
dir
,
nil
}
dirURL
:=
c
.
DirectoryURL
if
dirURL
==
""
{
dirURL
=
LetsEncryptURL
}
res
,
err
:=
c
.
get
(
ctx
,
dirURL
,
wantStatus
(
http
.
StatusOK
))
if
err
!=
nil
{
return
Directory
{},
err
}
defer
res
.
Body
.
Close
()
c
.
addNonce
(
res
.
Header
)
var
v
struct
{
Reg
string
`json:"new-reg"`
Authz
string
`json:"new-authz"`
Cert
string
`json:"new-cert"`
Revoke
string
`json:"revoke-cert"`
Meta
struct
{
Terms
string
`json:"terms-of-service"`
Website
string
`json:"website"`
CAA
[]
string
`json:"caa-identities"`
}
}
if
err
:=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
v
);
err
!=
nil
{
return
Directory
{},
err
}
c
.
dir
=
&
Directory
{
RegURL
:
v
.
Reg
,
AuthzURL
:
v
.
Authz
,
CertURL
:
v
.
Cert
,
RevokeURL
:
v
.
Revoke
,
Terms
:
v
.
Meta
.
Terms
,
Website
:
v
.
Meta
.
Website
,
CAA
:
v
.
Meta
.
CAA
,
}
return
*
c
.
dir
,
nil
}
// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format.
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate
// with a different duration.
// If the bundle argument is true, the returned value will also contain the CA (issuer) certificate chain.
//
// In the case where CA server does not provide the issued certificate in the response,
// CreateCert will poll certURL using c.FetchCert, which will result in additional round-trips.
// In such a scenario, the caller can cancel the polling with ctx.
//
// CreateCert returns an error if the CA's response or chain was unreasonably large.
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
func
(
c
*
Client
)
CreateCert
(
ctx
context
.
Context
,
csr
[]
byte
,
exp
time
.
Duration
,
bundle
bool
)
(
der
[][]
byte
,
certURL
string
,
err
error
)
{
if
_
,
err
:=
c
.
Discover
(
ctx
);
err
!=
nil
{
return
nil
,
""
,
err
}
req
:=
struct
{
Resource
string
`json:"resource"`
CSR
string
`json:"csr"`
NotBefore
string
`json:"notBefore,omitempty"`
NotAfter
string
`json:"notAfter,omitempty"`
}{
Resource
:
"new-cert"
,
CSR
:
base64
.
RawURLEncoding
.
EncodeToString
(
csr
),
}
now
:=
timeNow
()
req
.
NotBefore
=
now
.
Format
(
time
.
RFC3339
)
if
exp
>
0
{
req
.
NotAfter
=
now
.
Add
(
exp
)
.
Format
(
time
.
RFC3339
)
}
res
,
err
:=
c
.
post
(
ctx
,
c
.
Key
,
c
.
dir
.
CertURL
,
req
,
wantStatus
(
http
.
StatusCreated
))
if
err
!=
nil
{
return
nil
,
""
,
err
}
defer
res
.
Body
.
Close
()
curl
:=
res
.
Header
.
Get
(
"Location"
)
// cert permanent URL
if
res
.
ContentLength
==
0
{
// no cert in the body; poll until we get it
cert
,
err
:=
c
.
FetchCert
(
ctx
,
curl
,
bundle
)
return
cert
,
curl
,
err
}
// slurp issued cert and CA chain, if requested
cert
,
err
:=
c
.
responseCert
(
ctx
,
res
,
bundle
)
return
cert
,
curl
,
err
}
// FetchCert retrieves already issued certificate from the given url, in DER format.
// It retries the request until the certificate is successfully retrieved,
// context is cancelled by the caller or an error response is received.
//
// The returned value will also contain the CA (issuer) certificate if the bundle argument is true.
//
// FetchCert returns an error if the CA's response or chain was unreasonably large.
// Callers are encouraged to parse the returned value to ensure the certificate is valid
// and has expected features.
func
(
c
*
Client
)
FetchCert
(
ctx
context
.
Context
,
url
string
,
bundle
bool
)
([][]
byte
,
error
)
{
res
,
err
:=
c
.
get
(
ctx
,
url
,
wantStatus
(
http
.
StatusOK
))
if
err
!=
nil
{
return
nil
,
err
}
return
c
.
responseCert
(
ctx
,
res
,
bundle
)
}
// RevokeCert revokes a previously issued certificate cert, provided in DER format.
//
// The key argument, used to sign the request, must be authorized
// to revoke the certificate. It's up to the CA to decide which keys are authorized.
// For instance, the key pair of the certificate may be authorized.
// If the key is nil, c.Key is used instead.
func
(
c
*
Client
)
RevokeCert
(
ctx
context
.
Context
,
key
crypto
.
Signer
,
cert
[]
byte
,
reason
CRLReasonCode
)
error
{
if
_
,
err
:=
c
.
Discover
(
ctx
);
err
!=
nil
{
return
err
}
body
:=
&
struct
{
Resource
string
`json:"resource"`
Cert
string
`json:"certificate"`
Reason
int
`json:"reason"`
}{
Resource
:
"revoke-cert"
,
Cert
:
base64
.
RawURLEncoding
.
EncodeToString
(
cert
),
Reason
:
int
(
reason
),
}
if
key
==
nil
{
key
=
c
.
Key
}
res
,
err
:=
c
.
post
(
ctx
,
key
,
c
.
dir
.
RevokeURL
,
body
,
wantStatus
(
http
.
StatusOK
))
if
err
!=
nil
{
return
err
}
defer
res
.
Body
.
Close
()
return
nil
}
// AcceptTOS always returns true to indicate the acceptance of a CA's Terms of Service
// during account registration. See Register method of Client for more details.
func
AcceptTOS
(
tosURL
string
)
bool
{
return
true
}
// Register creates a new account registration by following the "new-reg" flow.
// It returns the registered account. The account is not modified.
//
// The registration may require the caller to agree to the CA's Terms of Service (TOS).
// If so, and the account has not indicated the acceptance of the terms (see Account for details),
// Register calls prompt with a TOS URL provided by the CA. Prompt should report
// whether the caller agrees to the terms. To always accept the terms, the caller can use AcceptTOS.
func
(
c
*
Client
)
Register
(
ctx
context
.
Context
,
a
*
Account
,
prompt
func
(
tosURL
string
)
bool
)
(
*
Account
,
error
)
{
if
_
,
err
:=
c
.
Discover
(
ctx
);
err
!=
nil
{
return
nil
,
err
}
var
err
error
if
a
,
err
=
c
.
doReg
(
ctx
,
c
.
dir
.
RegURL
,
"new-reg"
,
a
);
err
!=
nil
{
return
nil
,
err
}
var
accept
bool
if
a
.
CurrentTerms
!=
""
&&
a
.
CurrentTerms
!=
a
.
AgreedTerms
{
accept
=
prompt
(
a
.
CurrentTerms
)
}
if
accept
{
a
.
AgreedTerms
=
a
.
CurrentTerms
a
,
err
=
c
.
UpdateReg
(
ctx
,
a
)
}
return
a
,
err
}
// GetReg retrieves an existing registration.
// The url argument is an Account URI.
func
(
c
*
Client
)
GetReg
(
ctx
context
.
Context
,
url
string
)
(
*
Account
,
error
)
{
a
,
err
:=
c
.
doReg
(
ctx
,
url
,
"reg"
,
nil
)
if
err
!=
nil
{
return
nil
,
err
}
a
.
URI
=
url
return
a
,
nil
}
// UpdateReg updates an existing registration.
// It returns an updated account copy. The provided account is not modified.
func
(
c
*
Client
)
UpdateReg
(
ctx
context
.
Context
,
a
*
Account
)
(
*
Account
,
error
)
{
uri
:=
a
.
URI
a
,
err
:=
c
.
doReg
(
ctx
,
uri
,
"reg"
,
a
)
if
err
!=
nil
{
return
nil
,
err
}
a
.
URI
=
uri
return
a
,
nil
}
// Authorize performs the initial step in an authorization flow.
// The caller will then need to choose from and perform a set of returned
// challenges using c.Accept in order to successfully complete authorization.
//
// If an authorization has been previously granted, the CA may return
// a valid authorization (Authorization.Status is StatusValid). If so, the caller
// need not fulfill any challenge and can proceed to requesting a certificate.
func
(
c
*
Client
)
Authorize
(
ctx
context
.
Context
,
domain
string
)
(
*
Authorization
,
error
)
{
if
_
,
err
:=
c
.
Discover
(
ctx
);
err
!=
nil
{
return
nil
,
err
}
type
authzID
struct
{
Type
string
`json:"type"`
Value
string
`json:"value"`
}
req
:=
struct
{
Resource
string
`json:"resource"`
Identifier
authzID
`json:"identifier"`
}{
Resource
:
"new-authz"
,
Identifier
:
authzID
{
Type
:
"dns"
,
Value
:
domain
},
}
res
,
err
:=
c
.
post
(
ctx
,
c
.
Key
,
c
.
dir
.
AuthzURL
,
req
,
wantStatus
(
http
.
StatusCreated
))
if
err
!=
nil
{
return
nil
,
err
}
defer
res
.
Body
.
Close
()
var
v
wireAuthz
if
err
:=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
v
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"acme: invalid response: %v"
,
err
)
}
if
v
.
Status
!=
StatusPending
&&
v
.
Status
!=
StatusValid
{
return
nil
,
fmt
.
Errorf
(
"acme: unexpected status: %s"
,
v
.
Status
)
}
return
v
.
authorization
(
res
.
Header
.
Get
(
"Location"
)),
nil
}
// GetAuthorization retrieves an authorization identified by the given URL.
//
// If a caller needs to poll an authorization until its status is final,
// see the WaitAuthorization method.
func
(
c
*
Client
)
GetAuthorization
(
ctx
context
.
Context
,
url
string
)
(
*
Authorization
,
error
)
{
res
,
err
:=
c
.
get
(
ctx
,
url
,
wantStatus
(
http
.
StatusOK
,
http
.
StatusAccepted
))
if
err
!=
nil
{
return
nil
,
err
}
defer
res
.
Body
.
Close
()
var
v
wireAuthz
if
err
:=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
v
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"acme: invalid response: %v"
,
err
)
}
return
v
.
authorization
(
url
),
nil
}
// RevokeAuthorization relinquishes an existing authorization identified
// by the given URL.
// The url argument is an Authorization.URI value.
//
// If successful, the caller will be required to obtain a new authorization
// using the Authorize method before being able to request a new certificate
// for the domain associated with the authorization.
//
// It does not revoke existing certificates.
func
(
c
*
Client
)
RevokeAuthorization
(
ctx
context
.
Context
,
url
string
)
error
{
req
:=
struct
{
Resource
string
`json:"resource"`
Status
string
`json:"status"`
Delete
bool
`json:"delete"`
}{
Resource
:
"authz"
,
Status
:
"deactivated"
,
Delete
:
true
,
}
res
,
err
:=
c
.
post
(
ctx
,
c
.
Key
,
url
,
req
,
wantStatus
(
http
.
StatusOK
))
if
err
!=
nil
{
return
err
}
defer
res
.
Body
.
Close
()
return
nil
}
// WaitAuthorization polls an authorization at the given URL
// until it is in one of the final states, StatusValid or StatusInvalid,
// the ACME CA responded with a 4xx error code, or the context is done.
//
// It returns a non-nil Authorization only if its Status is StatusValid.
// In all other cases WaitAuthorization returns an error.
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
func
(
c
*
Client
)
WaitAuthorization
(
ctx
context
.
Context
,
url
string
)
(
*
Authorization
,
error
)
{
for
{
res
,
err
:=
c
.
get
(
ctx
,
url
,
wantStatus
(
http
.
StatusOK
,
http
.
StatusAccepted
))
if
err
!=
nil
{
return
nil
,
err
}
var
raw
wireAuthz
err
=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
raw
)
res
.
Body
.
Close
()
switch
{
case
err
!=
nil
:
// Skip and retry.
case
raw
.
Status
==
StatusValid
:
return
raw
.
authorization
(
url
),
nil
case
raw
.
Status
==
StatusInvalid
:
return
nil
,
raw
.
error
(
url
)
}
// Exponential backoff is implemented in c.get above.
// This is just to prevent continuously hitting the CA
// while waiting for a final authorization status.
d
:=
retryAfter
(
res
.
Header
.
Get
(
"Retry-After"
))
if
d
==
0
{
// Given that the fastest challenges TLS-SNI and HTTP-01
// require a CA to make at least 1 network round trip
// and most likely persist a challenge state,
// this default delay seems reasonable.
d
=
time
.
Second
}
t
:=
time
.
NewTimer
(
d
)
select
{
case
<-
ctx
.
Done
()
:
t
.
Stop
()
return
nil
,
ctx
.
Err
()
case
<-
t
.
C
:
// Retry.
}
}
}
// GetChallenge retrieves the current status of an challenge.
//
// A client typically polls a challenge status using this method.
func
(
c
*
Client
)
GetChallenge
(
ctx
context
.
Context
,
url
string
)
(
*
Challenge
,
error
)
{
res
,
err
:=
c
.
get
(
ctx
,
url
,
wantStatus
(
http
.
StatusOK
,
http
.
StatusAccepted
))
if
err
!=
nil
{
return
nil
,
err
}
defer
res
.
Body
.
Close
()
v
:=
wireChallenge
{
URI
:
url
}
if
err
:=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
v
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"acme: invalid response: %v"
,
err
)
}
return
v
.
challenge
(),
nil
}
// Accept informs the server that the client accepts one of its challenges
// previously obtained with c.Authorize.
//
// The server will then perform the validation asynchronously.
func
(
c
*
Client
)
Accept
(
ctx
context
.
Context
,
chal
*
Challenge
)
(
*
Challenge
,
error
)
{
auth
,
err
:=
keyAuth
(
c
.
Key
.
Public
(),
chal
.
Token
)
if
err
!=
nil
{
return
nil
,
err
}
req
:=
struct
{
Resource
string
`json:"resource"`
Type
string
`json:"type"`
Auth
string
`json:"keyAuthorization"`
}{
Resource
:
"challenge"
,
Type
:
chal
.
Type
,
Auth
:
auth
,
}
res
,
err
:=
c
.
post
(
ctx
,
c
.
Key
,
chal
.
URI
,
req
,
wantStatus
(
http
.
StatusOK
,
// according to the spec
http
.
StatusAccepted
,
// Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
))
if
err
!=
nil
{
return
nil
,
err
}
defer
res
.
Body
.
Close
()
var
v
wireChallenge
if
err
:=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
v
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"acme: invalid response: %v"
,
err
)
}
return
v
.
challenge
(),
nil
}
// DNS01ChallengeRecord returns a DNS record value for a dns-01 challenge response.
// A TXT record containing the returned value must be provisioned under
// "_acme-challenge" name of the domain being validated.
//
// The token argument is a Challenge.Token value.
func
(
c
*
Client
)
DNS01ChallengeRecord
(
token
string
)
(
string
,
error
)
{
ka
,
err
:=
keyAuth
(
c
.
Key
.
Public
(),
token
)
if
err
!=
nil
{
return
""
,
err
}
b
:=
sha256
.
Sum256
([]
byte
(
ka
))
return
base64
.
RawURLEncoding
.
EncodeToString
(
b
[
:
]),
nil
}
// HTTP01ChallengeResponse returns the response for an http-01 challenge.
// Servers should respond with the value to HTTP requests at the URL path
// provided by HTTP01ChallengePath to validate the challenge and prove control
// over a domain name.
//
// The token argument is a Challenge.Token value.
func
(
c
*
Client
)
HTTP01ChallengeResponse
(
token
string
)
(
string
,
error
)
{
return
keyAuth
(
c
.
Key
.
Public
(),
token
)
}
// HTTP01ChallengePath returns the URL path at which the response for an http-01 challenge
// should be provided by the servers.
// The response value can be obtained with HTTP01ChallengeResponse.
//
// The token argument is a Challenge.Token value.
func
(
c
*
Client
)
HTTP01ChallengePath
(
token
string
)
string
{
return
"/.well-known/acme-challenge/"
+
token
}
// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
// Servers can present the certificate to validate the challenge and prove control
// over a domain name.
//
// The implementation is incomplete in that the returned value is a single certificate,
// computed only for Z0 of the key authorization. ACME CAs are expected to update
// their implementations to use the newer version, TLS-SNI-02.
// For more details on TLS-SNI-01 see https://tools.ietf.org/html/draft-ietf-acme-acme-01#section-7.3.
//
// The token argument is a Challenge.Token value.
// If a WithKey option is provided, its private part signs the returned cert,
// and the public part is used to specify the signee.
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
//
// The returned certificate is valid for the next 24 hours and must be presented only when
// the server name of the TLS ClientHello matches exactly the returned name value.
func
(
c
*
Client
)
TLSSNI01ChallengeCert
(
token
string
,
opt
...
CertOption
)
(
cert
tls
.
Certificate
,
name
string
,
err
error
)
{
ka
,
err
:=
keyAuth
(
c
.
Key
.
Public
(),
token
)
if
err
!=
nil
{
return
tls
.
Certificate
{},
""
,
err
}
b
:=
sha256
.
Sum256
([]
byte
(
ka
))
h
:=
hex
.
EncodeToString
(
b
[
:
])
name
=
fmt
.
Sprintf
(
"%s.%s.acme.invalid"
,
h
[
:
32
],
h
[
32
:
])
cert
,
err
=
tlsChallengeCert
([]
string
{
name
},
opt
)
if
err
!=
nil
{
return
tls
.
Certificate
{},
""
,
err
}
return
cert
,
name
,
nil
}
// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
// Servers can present the certificate to validate the challenge and prove control
// over a domain name. For more details on TLS-SNI-02 see
// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7.3.
//
// The token argument is a Challenge.Token value.
// If a WithKey option is provided, its private part signs the returned cert,
// and the public part is used to specify the signee.
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
//
// The returned certificate is valid for the next 24 hours and must be presented only when
// the server name in the TLS ClientHello matches exactly the returned name value.
func
(
c
*
Client
)
TLSSNI02ChallengeCert
(
token
string
,
opt
...
CertOption
)
(
cert
tls
.
Certificate
,
name
string
,
err
error
)
{
b
:=
sha256
.
Sum256
([]
byte
(
token
))
h
:=
hex
.
EncodeToString
(
b
[
:
])
sanA
:=
fmt
.
Sprintf
(
"%s.%s.token.acme.invalid"
,
h
[
:
32
],
h
[
32
:
])
ka
,
err
:=
keyAuth
(
c
.
Key
.
Public
(),
token
)
if
err
!=
nil
{
return
tls
.
Certificate
{},
""
,
err
}
b
=
sha256
.
Sum256
([]
byte
(
ka
))
h
=
hex
.
EncodeToString
(
b
[
:
])
sanB
:=
fmt
.
Sprintf
(
"%s.%s.ka.acme.invalid"
,
h
[
:
32
],
h
[
32
:
])
cert
,
err
=
tlsChallengeCert
([]
string
{
sanA
,
sanB
},
opt
)
if
err
!=
nil
{
return
tls
.
Certificate
{},
""
,
err
}
return
cert
,
sanA
,
nil
}
// TLSALPN01ChallengeCert creates a certificate for TLS-ALPN-01 challenge response.
// Servers can present the certificate to validate the challenge and prove control
// over a domain name. For more details on TLS-ALPN-01 see
// https://tools.ietf.org/html/draft-shoemaker-acme-tls-alpn-00#section-3
//
// The token argument is a Challenge.Token value.
// If a WithKey option is provided, its private part signs the returned cert,
// and the public part is used to specify the signee.
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
//
// The returned certificate is valid for the next 24 hours and must be presented only when
// the server name in the TLS ClientHello matches the domain, and the special acme-tls/1 ALPN protocol
// has been specified.
func
(
c
*
Client
)
TLSALPN01ChallengeCert
(
token
,
domain
string
,
opt
...
CertOption
)
(
cert
tls
.
Certificate
,
err
error
)
{
ka
,
err
:=
keyAuth
(
c
.
Key
.
Public
(),
token
)
if
err
!=
nil
{
return
tls
.
Certificate
{},
err
}
shasum
:=
sha256
.
Sum256
([]
byte
(
ka
))
extValue
,
err
:=
asn1
.
Marshal
(
shasum
[
:
])
if
err
!=
nil
{
return
tls
.
Certificate
{},
err
}
acmeExtension
:=
pkix
.
Extension
{
Id
:
idPeACMEIdentifierV1
,
Critical
:
true
,
Value
:
extValue
,
}
tmpl
:=
defaultTLSChallengeCertTemplate
()
var
newOpt
[]
CertOption
for
_
,
o
:=
range
opt
{
switch
o
:=
o
.
(
type
)
{
case
*
certOptTemplate
:
t
:=
*
(
*
x509
.
Certificate
)(
o
)
// shallow copy is ok
tmpl
=
&
t
default
:
newOpt
=
append
(
newOpt
,
o
)
}
}
tmpl
.
ExtraExtensions
=
append
(
tmpl
.
ExtraExtensions
,
acmeExtension
)
newOpt
=
append
(
newOpt
,
WithTemplate
(
tmpl
))
return
tlsChallengeCert
([]
string
{
domain
},
newOpt
)
}
// doReg sends all types of registration requests.
// The type of request is identified by typ argument, which is a "resource"
// in the ACME spec terms.
//
// A non-nil acct argument indicates whether the intention is to mutate data
// of the Account. Only Contact and Agreement of its fields are used
// in such cases.
func
(
c
*
Client
)
doReg
(
ctx
context
.
Context
,
url
string
,
typ
string
,
acct
*
Account
)
(
*
Account
,
error
)
{
req
:=
struct
{
Resource
string
`json:"resource"`
Contact
[]
string
`json:"contact,omitempty"`
Agreement
string
`json:"agreement,omitempty"`
}{
Resource
:
typ
,
}
if
acct
!=
nil
{
req
.
Contact
=
acct
.
Contact
req
.
Agreement
=
acct
.
AgreedTerms
}
res
,
err
:=
c
.
post
(
ctx
,
c
.
Key
,
url
,
req
,
wantStatus
(
http
.
StatusOK
,
// updates and deletes
http
.
StatusCreated
,
// new account creation
http
.
StatusAccepted
,
// Let's Encrypt divergent implementation
))
if
err
!=
nil
{
return
nil
,
err
}
defer
res
.
Body
.
Close
()
var
v
struct
{
Contact
[]
string
Agreement
string
Authorizations
string
Certificates
string
}
if
err
:=
json
.
NewDecoder
(
res
.
Body
)
.
Decode
(
&
v
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"acme: invalid response: %v"
,
err
)
}
var
tos
string
if
v
:=
linkHeader
(
res
.
Header
,
"terms-of-service"
);
len
(
v
)
>
0
{
tos
=
v
[
0
]
}
var
authz
string
if
v
:=
linkHeader
(
res
.
Header
,
"next"
);
len
(
v
)
>
0
{
authz
=
v
[
0
]
}
return
&
Account
{
URI
:
res
.
Header
.
Get
(
"Location"
),
Contact
:
v
.
Contact
,
AgreedTerms
:
v
.
Agreement
,
CurrentTerms
:
tos
,
Authz
:
authz
,
Authorizations
:
v
.
Authorizations
,
Certificates
:
v
.
Certificates
,
},
nil
}
// popNonce returns a nonce value previously stored with c.addNonce
// or fetches a fresh one from the given URL.
func
(
c
*
Client
)
popNonce
(
ctx
context
.
Context
,
url
string
)
(
string
,
error
)
{
c
.
noncesMu
.
Lock
()
defer
c
.
noncesMu
.
Unlock
()
if
len
(
c
.
nonces
)
==
0
{
return
c
.
fetchNonce
(
ctx
,
url
)
}
var
nonce
string
for
nonce
=
range
c
.
nonces
{
delete
(
c
.
nonces
,
nonce
)
break
}
return
nonce
,
nil
}
// clearNonces clears any stored nonces
func
(
c
*
Client
)
clearNonces
()
{
c
.
noncesMu
.
Lock
()
defer
c
.
noncesMu
.
Unlock
()
c
.
nonces
=
make
(
map
[
string
]
struct
{})
}
// addNonce stores a nonce value found in h (if any) for future use.
func
(
c
*
Client
)
addNonce
(
h
http
.
Header
)
{
v
:=
nonceFromHeader
(
h
)
if
v
==
""
{
return
}
c
.
noncesMu
.
Lock
()
defer
c
.
noncesMu
.
Unlock
()
if
len
(
c
.
nonces
)
>=
maxNonces
{
return
}
if
c
.
nonces
==
nil
{
c
.
nonces
=
make
(
map
[
string
]
struct
{})
}
c
.
nonces
[
v
]
=
struct
{}{}
}
func
(
c
*
Client
)
fetchNonce
(
ctx
context
.
Context
,
url
string
)
(
string
,
error
)
{
r
,
err
:=
http
.
NewRequest
(
"HEAD"
,
url
,
nil
)
if
err
!=
nil
{
return
""
,
err
}
resp
,
err
:=
c
.
doNoRetry
(
ctx
,
r
)
if
err
!=
nil
{
return
""
,
err
}
defer
resp
.
Body
.
Close
()
nonce
:=
nonceFromHeader
(
resp
.
Header
)
if
nonce
==
""
{
if
resp
.
StatusCode
>
299
{
return
""
,
responseError
(
resp
)
}
return
""
,
errors
.
New
(
"acme: nonce not found"
)
}
return
nonce
,
nil
}
func
nonceFromHeader
(
h
http
.
Header
)
string
{
return
h
.
Get
(
"Replay-Nonce"
)
}
func
(
c
*
Client
)
responseCert
(
ctx
context
.
Context
,
res
*
http
.
Response
,
bundle
bool
)
([][]
byte
,
error
)
{
b
,
err
:=
ioutil
.
ReadAll
(
io
.
LimitReader
(
res
.
Body
,
maxCertSize
+
1
))
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"acme: response stream: %v"
,
err
)
}
if
len
(
b
)
>
maxCertSize
{
return
nil
,
errors
.
New
(
"acme: certificate is too big"
)
}
cert
:=
[][]
byte
{
b
}
if
!
bundle
{
return
cert
,
nil
}
// Append CA chain cert(s).
// At least one is required according to the spec:
// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-6.3.1
up
:=
linkHeader
(
res
.
Header
,
"up"
)
if
len
(
up
)
==
0
{
return
nil
,
errors
.
New
(
"acme: rel=up link not found"
)
}
if
len
(
up
)
>
maxChainLen
{
return
nil
,
errors
.
New
(
"acme: rel=up link is too large"
)
}
for
_
,
url
:=
range
up
{
cc
,
err
:=
c
.
chainCert
(
ctx
,
url
,
0
)
if
err
!=
nil
{
return
nil
,
err
}
cert
=
append
(
cert
,
cc
...
)
}
return
cert
,
nil
}
// chainCert fetches CA certificate chain recursively by following "up" links.
// Each recursive call increments the depth by 1, resulting in an error
// if the recursion level reaches maxChainLen.
//
// First chainCert call starts with depth of 0.
func
(
c
*
Client
)
chainCert
(
ctx
context
.
Context
,
url
string
,
depth
int
)
([][]
byte
,
error
)
{
if
depth
>=
maxChainLen
{
return
nil
,
errors
.
New
(
"acme: certificate chain is too deep"
)
}
res
,
err
:=
c
.
get
(
ctx
,
url
,
wantStatus
(
http
.
StatusOK
))
if
err
!=
nil
{
return
nil
,
err
}
defer
res
.
Body
.
Close
()
b
,
err
:=
ioutil
.
ReadAll
(
io
.
LimitReader
(
res
.
Body
,
maxCertSize
+
1
))
if
err
!=
nil
{
return
nil
,
err
}
if
len
(
b
)
>
maxCertSize
{
return
nil
,
errors
.
New
(
"acme: certificate is too big"
)
}
chain
:=
[][]
byte
{
b
}
uplink
:=
linkHeader
(
res
.
Header
,
"up"
)
if
len
(
uplink
)
>
maxChainLen
{
return
nil
,
errors
.
New
(
"acme: certificate chain is too large"
)
}
for
_
,
up
:=
range
uplink
{
cc
,
err
:=
c
.
chainCert
(
ctx
,
up
,
depth
+
1
)
if
err
!=
nil
{
return
nil
,
err
}
chain
=
append
(
chain
,
cc
...
)
}
return
chain
,
nil
}
// linkHeader returns URI-Reference values of all Link headers
// with relation-type rel.
// See https://tools.ietf.org/html/rfc5988#section-5 for details.
func
linkHeader
(
h
http
.
Header
,
rel
string
)
[]
string
{
var
links
[]
string
for
_
,
v
:=
range
h
[
"Link"
]
{
parts
:=
strings
.
Split
(
v
,
";"
)
for
_
,
p
:=
range
parts
{
p
=
strings
.
TrimSpace
(
p
)
if
!
strings
.
HasPrefix
(
p
,
"rel="
)
{
continue
}
if
v
:=
strings
.
Trim
(
p
[
4
:
],
`"`
);
v
==
rel
{
links
=
append
(
links
,
strings
.
Trim
(
parts
[
0
],
"<>"
))
}
}
}
return
links
}
// keyAuth generates a key authorization string for a given token.
func
keyAuth
(
pub
crypto
.
PublicKey
,
token
string
)
(
string
,
error
)
{
th
,
err
:=
JWKThumbprint
(
pub
)
if
err
!=
nil
{
return
""
,
err
}
return
fmt
.
Sprintf
(
"%s.%s"
,
token
,
th
),
nil
}
// defaultTLSChallengeCertTemplate is a template used to create challenge certs for TLS challenges.
func
defaultTLSChallengeCertTemplate
()
*
x509
.
Certificate
{
return
&
x509
.
Certificate
{
SerialNumber
:
big
.
NewInt
(
1
),
NotBefore
:
time
.
Now
(),
NotAfter
:
time
.
Now
()
.
Add
(
24
*
time
.
Hour
),
BasicConstraintsValid
:
true
,
KeyUsage
:
x509
.
KeyUsageKeyEncipherment
|
x509
.
KeyUsageDigitalSignature
,
ExtKeyUsage
:
[]
x509
.
ExtKeyUsage
{
x509
.
ExtKeyUsageServerAuth
},
}
}
// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
// with the given SANs and auto-generated public/private key pair.
// The Subject Common Name is set to the first SAN to aid debugging.
// To create a cert with a custom key pair, specify WithKey option.
func
tlsChallengeCert
(
san
[]
string
,
opt
[]
CertOption
)
(
tls
.
Certificate
,
error
)
{
var
key
crypto
.
Signer
tmpl
:=
defaultTLSChallengeCertTemplate
()
for
_
,
o
:=
range
opt
{
switch
o
:=
o
.
(
type
)
{
case
*
certOptKey
:
if
key
!=
nil
{
return
tls
.
Certificate
{},
errors
.
New
(
"acme: duplicate key option"
)
}
key
=
o
.
key
case
*
certOptTemplate
:
t
:=
*
(
*
x509
.
Certificate
)(
o
)
// shallow copy is ok
tmpl
=
&
t
default
:
// package's fault, if we let this happen:
panic
(
fmt
.
Sprintf
(
"unsupported option type %T"
,
o
))
}
}
if
key
==
nil
{
var
err
error
if
key
,
err
=
ecdsa
.
GenerateKey
(
elliptic
.
P256
(),
rand
.
Reader
);
err
!=
nil
{
return
tls
.
Certificate
{},
err
}
}
tmpl
.
DNSNames
=
san
if
len
(
san
)
>
0
{
tmpl
.
Subject
.
CommonName
=
san
[
0
]
}
der
,
err
:=
x509
.
CreateCertificate
(
rand
.
Reader
,
tmpl
,
tmpl
,
key
.
Public
(),
key
)
if
err
!=
nil
{
return
tls
.
Certificate
{},
err
}
return
tls
.
Certificate
{
Certificate
:
[][]
byte
{
der
},
PrivateKey
:
key
,
},
nil
}
// encodePEM returns b encoded as PEM with block of type typ.
func
encodePEM
(
typ
string
,
b
[]
byte
)
[]
byte
{
pb
:=
&
pem
.
Block
{
Type
:
typ
,
Bytes
:
b
}
return
pem
.
EncodeToMemory
(
pb
)
}
// timeNow is useful for testing for fixed current time.
var
timeNow
=
time
.
Now
vendor/golang.org/x/crypto/acme/autocert/autocert.go
0 → 100644
View file @
efe0f673
// Copyright 2016 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 autocert provides automatic access to certificates from Let's Encrypt
// and any other ACME-based CA.
//
// This package is a work in progress and makes no API stability promises.
package
autocert
import
(
"bytes"
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"io"
mathrand
"math/rand"
"net"
"net/http"
"path"
"strings"
"sync"
"time"
"golang.org/x/crypto/acme"
)
// createCertRetryAfter is how much time to wait before removing a failed state
// entry due to an unsuccessful createCert call.
// This is a variable instead of a const for testing.
// TODO: Consider making it configurable or an exp backoff?
var
createCertRetryAfter
=
time
.
Minute
// pseudoRand is safe for concurrent use.
var
pseudoRand
*
lockedMathRand
func
init
()
{
src
:=
mathrand
.
NewSource
(
timeNow
()
.
UnixNano
())
pseudoRand
=
&
lockedMathRand
{
rnd
:
mathrand
.
New
(
src
)}
}
// AcceptTOS is a Manager.Prompt function that always returns true to
// indicate acceptance of the CA's Terms of Service during account
// registration.
func
AcceptTOS
(
tosURL
string
)
bool
{
return
true
}
// HostPolicy specifies which host names the Manager is allowed to respond to.
// It returns a non-nil error if the host should be rejected.
// The returned error is accessible via tls.Conn.Handshake and its callers.
// See Manager's HostPolicy field and GetCertificate method docs for more details.
type
HostPolicy
func
(
ctx
context
.
Context
,
host
string
)
error
// HostWhitelist returns a policy where only the specified host names are allowed.
// Only exact matches are currently supported. Subdomains, regexp or wildcard
// will not match.
func
HostWhitelist
(
hosts
...
string
)
HostPolicy
{
whitelist
:=
make
(
map
[
string
]
bool
,
len
(
hosts
))
for
_
,
h
:=
range
hosts
{
whitelist
[
h
]
=
true
}
return
func
(
_
context
.
Context
,
host
string
)
error
{
if
!
whitelist
[
host
]
{
return
errors
.
New
(
"acme/autocert: host not configured"
)
}
return
nil
}
}
// defaultHostPolicy is used when Manager.HostPolicy is not set.
func
defaultHostPolicy
(
context
.
Context
,
string
)
error
{
return
nil
}
// Manager is a stateful certificate manager built on top of acme.Client.
// It obtains and refreshes certificates automatically using "tls-alpn-01",
// "tls-sni-01", "tls-sni-02" and "http-01" challenge types,
// as well as providing them to a TLS server via tls.Config.
//
// You must specify a cache implementation, such as DirCache,
// to reuse obtained certificates across program restarts.
// Otherwise your server is very likely to exceed the certificate
// issuer's request rate limits.
type
Manager
struct
{
// Prompt specifies a callback function to conditionally accept a CA's Terms of Service (TOS).
// The registration may require the caller to agree to the CA's TOS.
// If so, Manager calls Prompt with a TOS URL provided by the CA. Prompt should report
// whether the caller agrees to the terms.
//
// To always accept the terms, the callers can use AcceptTOS.
Prompt
func
(
tosURL
string
)
bool
// Cache optionally stores and retrieves previously-obtained certificates
// and other state. If nil, certs will only be cached for the lifetime of
// the Manager. Multiple Managers can share the same Cache.
//
// Using a persistent Cache, such as DirCache, is strongly recommended.
Cache
Cache
// HostPolicy controls which domains the Manager will attempt
// to retrieve new certificates for. It does not affect cached certs.
//
// If non-nil, HostPolicy is called before requesting a new cert.
// If nil, all hosts are currently allowed. This is not recommended,
// as it opens a potential attack where clients connect to a server
// by IP address and pretend to be asking for an incorrect host name.
// Manager will attempt to obtain a certificate for that host, incorrectly,
// eventually reaching the CA's rate limit for certificate requests
// and making it impossible to obtain actual certificates.
//
// See GetCertificate for more details.
HostPolicy
HostPolicy
// RenewBefore optionally specifies how early certificates should
// be renewed before they expire.
//
// If zero, they're renewed 30 days before expiration.
RenewBefore
time
.
Duration
// Client is used to perform low-level operations, such as account registration
// and requesting new certificates.
//
// If Client is nil, a zero-value acme.Client is used with acme.LetsEncryptURL
// as directory endpoint. If the Client.Key is nil, a new ECDSA P-256 key is
// generated and, if Cache is not nil, stored in cache.
//
// Mutating the field after the first call of GetCertificate method will have no effect.
Client
*
acme
.
Client
// Email optionally specifies a contact email address.
// This is used by CAs, such as Let's Encrypt, to notify about problems
// with issued certificates.
//
// If the Client's account key is already registered, Email is not used.
Email
string
// ForceRSA used to make the Manager generate RSA certificates. It is now ignored.
//
// Deprecated: the Manager will request the correct type of certificate based
// on what each client supports.
ForceRSA
bool
// ExtraExtensions are used when generating a new CSR (Certificate Request),
// thus allowing customization of the resulting certificate.
// For instance, TLS Feature Extension (RFC 7633) can be used
// to prevent an OCSP downgrade attack.
//
// The field value is passed to crypto/x509.CreateCertificateRequest
// in the template's ExtraExtensions field as is.
ExtraExtensions
[]
pkix
.
Extension
clientMu
sync
.
Mutex
client
*
acme
.
Client
// initialized by acmeClient method
stateMu
sync
.
Mutex
state
map
[
certKey
]
*
certState
// renewal tracks the set of domains currently running renewal timers.
renewalMu
sync
.
Mutex
renewal
map
[
certKey
]
*
domainRenewal
// tokensMu guards the rest of the fields: tryHTTP01, certTokens and httpTokens.
tokensMu
sync
.
RWMutex
// tryHTTP01 indicates whether the Manager should try "http-01" challenge type
// during the authorization flow.
tryHTTP01
bool
// httpTokens contains response body values for http-01 challenges
// and is keyed by the URL path at which a challenge response is expected
// to be provisioned.
// The entries are stored for the duration of the authorization flow.
httpTokens
map
[
string
][]
byte
// certTokens contains temporary certificates for tls-sni and tls-alpn challenges
// and is keyed by token domain name, which matches server name of ClientHello.
// Keys always have ".acme.invalid" suffix for tls-sni. Otherwise, they are domain names
// for tls-alpn.
// The entries are stored for the duration of the authorization flow.
certTokens
map
[
string
]
*
tls
.
Certificate
}
// certKey is the key by which certificates are tracked in state, renewal and cache.
type
certKey
struct
{
domain
string
// without trailing dot
isRSA
bool
// RSA cert for legacy clients (as opposed to default ECDSA)
isToken
bool
// tls-based challenge token cert; key type is undefined regardless of isRSA
}
func
(
c
certKey
)
String
()
string
{
if
c
.
isToken
{
return
c
.
domain
+
"+token"
}
if
c
.
isRSA
{
return
c
.
domain
+
"+rsa"
}
return
c
.
domain
}
// TLSConfig creates a new TLS config suitable for net/http.Server servers,
// supporting HTTP/2 and the tls-alpn-01 ACME challenge type.
func
(
m
*
Manager
)
TLSConfig
()
*
tls
.
Config
{
return
&
tls
.
Config
{
GetCertificate
:
m
.
GetCertificate
,
NextProtos
:
[]
string
{
"h2"
,
"http/1.1"
,
// enable HTTP/2
acme
.
ALPNProto
,
// enable tls-alpn ACME challenges
},
}
}
// GetCertificate implements the tls.Config.GetCertificate hook.
// It provides a TLS certificate for hello.ServerName host, including answering
// tls-alpn-01 and *.acme.invalid (tls-sni-01 and tls-sni-02) challenges.
// All other fields of hello are ignored.
//
// If m.HostPolicy is non-nil, GetCertificate calls the policy before requesting
// a new cert. A non-nil error returned from m.HostPolicy halts TLS negotiation.
// The error is propagated back to the caller of GetCertificate and is user-visible.
// This does not affect cached certs. See HostPolicy field description for more details.
func
(
m
*
Manager
)
GetCertificate
(
hello
*
tls
.
ClientHelloInfo
)
(
*
tls
.
Certificate
,
error
)
{
if
m
.
Prompt
==
nil
{
return
nil
,
errors
.
New
(
"acme/autocert: Manager.Prompt not set"
)
}
name
:=
hello
.
ServerName
if
name
==
""
{
return
nil
,
errors
.
New
(
"acme/autocert: missing server name"
)
}
if
!
strings
.
Contains
(
strings
.
Trim
(
name
,
"."
),
"."
)
{
return
nil
,
errors
.
New
(
"acme/autocert: server name component count invalid"
)
}
if
strings
.
ContainsAny
(
name
,
`+/\`
)
{
return
nil
,
errors
.
New
(
"acme/autocert: server name contains invalid character"
)
}
// In the worst-case scenario, the timeout needs to account for caching, host policy,
// domain ownership verification and certificate issuance.
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
5
*
time
.
Minute
)
defer
cancel
()
// Check whether this is a token cert requested for TLS-SNI or TLS-ALPN challenge.
if
wantsTokenCert
(
hello
)
{
m
.
tokensMu
.
RLock
()
defer
m
.
tokensMu
.
RUnlock
()
// It's ok to use the same token cert key for both tls-sni and tls-alpn
// because there's always at most 1 token cert per on-going domain authorization.
// See m.verify for details.
if
cert
:=
m
.
certTokens
[
name
];
cert
!=
nil
{
return
cert
,
nil
}
if
cert
,
err
:=
m
.
cacheGet
(
ctx
,
certKey
{
domain
:
name
,
isToken
:
true
});
err
==
nil
{
return
cert
,
nil
}
// TODO: cache error results?
return
nil
,
fmt
.
Errorf
(
"acme/autocert: no token cert for %q"
,
name
)
}
// regular domain
ck
:=
certKey
{
domain
:
strings
.
TrimSuffix
(
name
,
"."
),
// golang.org/issue/18114
isRSA
:
!
supportsECDSA
(
hello
),
}
cert
,
err
:=
m
.
cert
(
ctx
,
ck
)
if
err
==
nil
{
return
cert
,
nil
}
if
err
!=
ErrCacheMiss
{
return
nil
,
err
}
// first-time
if
err
:=
m
.
hostPolicy
()(
ctx
,
name
);
err
!=
nil
{
return
nil
,
err
}
cert
,
err
=
m
.
createCert
(
ctx
,
ck
)
if
err
!=
nil
{
return
nil
,
err
}
m
.
cachePut
(
ctx
,
ck
,
cert
)
return
cert
,
nil
}
// wantsTokenCert reports whether a TLS request with SNI is made by a CA server
// for a challenge verification.
func
wantsTokenCert
(
hello
*
tls
.
ClientHelloInfo
)
bool
{
// tls-alpn-01
if
len
(
hello
.
SupportedProtos
)
==
1
&&
hello
.
SupportedProtos
[
0
]
==
acme
.
ALPNProto
{
return
true
}
// tls-sni-xx
return
strings
.
HasSuffix
(
hello
.
ServerName
,
".acme.invalid"
)
}
func
supportsECDSA
(
hello
*
tls
.
ClientHelloInfo
)
bool
{
// The "signature_algorithms" extension, if present, limits the key exchange
// algorithms allowed by the cipher suites. See RFC 5246, section 7.4.1.4.1.
if
hello
.
SignatureSchemes
!=
nil
{
ecdsaOK
:=
false
schemeLoop
:
for
_
,
scheme
:=
range
hello
.
SignatureSchemes
{
const
tlsECDSAWithSHA1
tls
.
SignatureScheme
=
0x0203
// constant added in Go 1.10
switch
scheme
{
case
tlsECDSAWithSHA1
,
tls
.
ECDSAWithP256AndSHA256
,
tls
.
ECDSAWithP384AndSHA384
,
tls
.
ECDSAWithP521AndSHA512
:
ecdsaOK
=
true
break
schemeLoop
}
}
if
!
ecdsaOK
{
return
false
}
}
if
hello
.
SupportedCurves
!=
nil
{
ecdsaOK
:=
false
for
_
,
curve
:=
range
hello
.
SupportedCurves
{
if
curve
==
tls
.
CurveP256
{
ecdsaOK
=
true
break
}
}
if
!
ecdsaOK
{
return
false
}
}
for
_
,
suite
:=
range
hello
.
CipherSuites
{
switch
suite
{
case
tls
.
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
,
tls
.
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
,
tls
.
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
,
tls
.
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
,
tls
.
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
,
tls
.
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
,
tls
.
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
:
return
true
}
}
return
false
}
// HTTPHandler configures the Manager to provision ACME "http-01" challenge responses.
// It returns an http.Handler that responds to the challenges and must be
// running on port 80. If it receives a request that is not an ACME challenge,
// it delegates the request to the optional fallback handler.
//
// If fallback is nil, the returned handler redirects all GET and HEAD requests
// to the default TLS port 443 with 302 Found status code, preserving the original
// request path and query. It responds with 400 Bad Request to all other HTTP methods.
// The fallback is not protected by the optional HostPolicy.
//
// Because the fallback handler is run with unencrypted port 80 requests,
// the fallback should not serve TLS-only requests.
//
// If HTTPHandler is never called, the Manager will only use TLS SNI
// challenges for domain verification.
func
(
m
*
Manager
)
HTTPHandler
(
fallback
http
.
Handler
)
http
.
Handler
{
m
.
tokensMu
.
Lock
()
defer
m
.
tokensMu
.
Unlock
()
m
.
tryHTTP01
=
true
if
fallback
==
nil
{
fallback
=
http
.
HandlerFunc
(
handleHTTPRedirect
)
}
return
http
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
if
!
strings
.
HasPrefix
(
r
.
URL
.
Path
,
"/.well-known/acme-challenge/"
)
{
fallback
.
ServeHTTP
(
w
,
r
)
return
}
// A reasonable context timeout for cache and host policy only,
// because we don't wait for a new certificate issuance here.
ctx
,
cancel
:=
context
.
WithTimeout
(
r
.
Context
(),
time
.
Minute
)
defer
cancel
()
if
err
:=
m
.
hostPolicy
()(
ctx
,
r
.
Host
);
err
!=
nil
{
http
.
Error
(
w
,
err
.
Error
(),
http
.
StatusForbidden
)
return
}
data
,
err
:=
m
.
httpToken
(
ctx
,
r
.
URL
.
Path
)
if
err
!=
nil
{
http
.
Error
(
w
,
err
.
Error
(),
http
.
StatusNotFound
)
return
}
w
.
Write
(
data
)
})
}
func
handleHTTPRedirect
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
if
r
.
Method
!=
"GET"
&&
r
.
Method
!=
"HEAD"
{
http
.
Error
(
w
,
"Use HTTPS"
,
http
.
StatusBadRequest
)
return
}
target
:=
"https://"
+
stripPort
(
r
.
Host
)
+
r
.
URL
.
RequestURI
()
http
.
Redirect
(
w
,
r
,
target
,
http
.
StatusFound
)
}
func
stripPort
(
hostport
string
)
string
{
host
,
_
,
err
:=
net
.
SplitHostPort
(
hostport
)
if
err
!=
nil
{
return
hostport
}
return
net
.
JoinHostPort
(
host
,
"443"
)
}
// cert returns an existing certificate either from m.state or cache.
// If a certificate is found in cache but not in m.state, the latter will be filled
// with the cached value.
func
(
m
*
Manager
)
cert
(
ctx
context
.
Context
,
ck
certKey
)
(
*
tls
.
Certificate
,
error
)
{
m
.
stateMu
.
Lock
()
if
s
,
ok
:=
m
.
state
[
ck
];
ok
{
m
.
stateMu
.
Unlock
()
s
.
RLock
()
defer
s
.
RUnlock
()
return
s
.
tlscert
()
}
defer
m
.
stateMu
.
Unlock
()
cert
,
err
:=
m
.
cacheGet
(
ctx
,
ck
)
if
err
!=
nil
{
return
nil
,
err
}
signer
,
ok
:=
cert
.
PrivateKey
.
(
crypto
.
Signer
)
if
!
ok
{
return
nil
,
errors
.
New
(
"acme/autocert: private key cannot sign"
)
}
if
m
.
state
==
nil
{
m
.
state
=
make
(
map
[
certKey
]
*
certState
)
}
s
:=
&
certState
{
key
:
signer
,
cert
:
cert
.
Certificate
,
leaf
:
cert
.
Leaf
,
}
m
.
state
[
ck
]
=
s
go
m
.
renew
(
ck
,
s
.
key
,
s
.
leaf
.
NotAfter
)
return
cert
,
nil
}
// cacheGet always returns a valid certificate, or an error otherwise.
// If a cached certificate exists but is not valid, ErrCacheMiss is returned.
func
(
m
*
Manager
)
cacheGet
(
ctx
context
.
Context
,
ck
certKey
)
(
*
tls
.
Certificate
,
error
)
{
if
m
.
Cache
==
nil
{
return
nil
,
ErrCacheMiss
}
data
,
err
:=
m
.
Cache
.
Get
(
ctx
,
ck
.
String
())
if
err
!=
nil
{
return
nil
,
err
}
// private
priv
,
pub
:=
pem
.
Decode
(
data
)
if
priv
==
nil
||
!
strings
.
Contains
(
priv
.
Type
,
"PRIVATE"
)
{
return
nil
,
ErrCacheMiss
}
privKey
,
err
:=
parsePrivateKey
(
priv
.
Bytes
)
if
err
!=
nil
{
return
nil
,
err
}
// public
var
pubDER
[][]
byte
for
len
(
pub
)
>
0
{
var
b
*
pem
.
Block
b
,
pub
=
pem
.
Decode
(
pub
)
if
b
==
nil
{
break
}
pubDER
=
append
(
pubDER
,
b
.
Bytes
)
}
if
len
(
pub
)
>
0
{
// Leftover content not consumed by pem.Decode. Corrupt. Ignore.
return
nil
,
ErrCacheMiss
}
// verify and create TLS cert
leaf
,
err
:=
validCert
(
ck
,
pubDER
,
privKey
)
if
err
!=
nil
{
return
nil
,
ErrCacheMiss
}
tlscert
:=
&
tls
.
Certificate
{
Certificate
:
pubDER
,
PrivateKey
:
privKey
,
Leaf
:
leaf
,
}
return
tlscert
,
nil
}
func
(
m
*
Manager
)
cachePut
(
ctx
context
.
Context
,
ck
certKey
,
tlscert
*
tls
.
Certificate
)
error
{
if
m
.
Cache
==
nil
{
return
nil
}
// contains PEM-encoded data
var
buf
bytes
.
Buffer
// private
switch
key
:=
tlscert
.
PrivateKey
.
(
type
)
{
case
*
ecdsa
.
PrivateKey
:
if
err
:=
encodeECDSAKey
(
&
buf
,
key
);
err
!=
nil
{
return
err
}
case
*
rsa
.
PrivateKey
:
b
:=
x509
.
MarshalPKCS1PrivateKey
(
key
)
pb
:=
&
pem
.
Block
{
Type
:
"RSA PRIVATE KEY"
,
Bytes
:
b
}
if
err
:=
pem
.
Encode
(
&
buf
,
pb
);
err
!=
nil
{
return
err
}
default
:
return
errors
.
New
(
"acme/autocert: unknown private key type"
)
}
// public
for
_
,
b
:=
range
tlscert
.
Certificate
{
pb
:=
&
pem
.
Block
{
Type
:
"CERTIFICATE"
,
Bytes
:
b
}
if
err
:=
pem
.
Encode
(
&
buf
,
pb
);
err
!=
nil
{
return
err
}
}
return
m
.
Cache
.
Put
(
ctx
,
ck
.
String
(),
buf
.
Bytes
())
}
func
encodeECDSAKey
(
w
io
.
Writer
,
key
*
ecdsa
.
PrivateKey
)
error
{
b
,
err
:=
x509
.
MarshalECPrivateKey
(
key
)
if
err
!=
nil
{
return
err
}
pb
:=
&
pem
.
Block
{
Type
:
"EC PRIVATE KEY"
,
Bytes
:
b
}
return
pem
.
Encode
(
w
,
pb
)
}
// createCert starts the domain ownership verification and returns a certificate
// for that domain upon success.
//
// If the domain is already being verified, it waits for the existing verification to complete.
// Either way, createCert blocks for the duration of the whole process.
func
(
m
*
Manager
)
createCert
(
ctx
context
.
Context
,
ck
certKey
)
(
*
tls
.
Certificate
,
error
)
{
// TODO: maybe rewrite this whole piece using sync.Once
state
,
err
:=
m
.
certState
(
ck
)
if
err
!=
nil
{
return
nil
,
err
}
// state may exist if another goroutine is already working on it
// in which case just wait for it to finish
if
!
state
.
locked
{
state
.
RLock
()
defer
state
.
RUnlock
()
return
state
.
tlscert
()
}
// We are the first; state is locked.
// Unblock the readers when domain ownership is verified
// and we got the cert or the process failed.
defer
state
.
Unlock
()
state
.
locked
=
false
der
,
leaf
,
err
:=
m
.
authorizedCert
(
ctx
,
state
.
key
,
ck
)
if
err
!=
nil
{
// Remove the failed state after some time,
// making the manager call createCert again on the following TLS hello.
time
.
AfterFunc
(
createCertRetryAfter
,
func
()
{
defer
testDidRemoveState
(
ck
)
m
.
stateMu
.
Lock
()
defer
m
.
stateMu
.
Unlock
()
// Verify the state hasn't changed and it's still invalid
// before deleting.
s
,
ok
:=
m
.
state
[
ck
]
if
!
ok
{
return
}
if
_
,
err
:=
validCert
(
ck
,
s
.
cert
,
s
.
key
);
err
==
nil
{
return
}
delete
(
m
.
state
,
ck
)
})
return
nil
,
err
}
state
.
cert
=
der
state
.
leaf
=
leaf
go
m
.
renew
(
ck
,
state
.
key
,
state
.
leaf
.
NotAfter
)
return
state
.
tlscert
()
}
// certState returns a new or existing certState.
// If a new certState is returned, state.exist is false and the state is locked.
// The returned error is non-nil only in the case where a new state could not be created.
func
(
m
*
Manager
)
certState
(
ck
certKey
)
(
*
certState
,
error
)
{
m
.
stateMu
.
Lock
()
defer
m
.
stateMu
.
Unlock
()
if
m
.
state
==
nil
{
m
.
state
=
make
(
map
[
certKey
]
*
certState
)
}
// existing state
if
state
,
ok
:=
m
.
state
[
ck
];
ok
{
return
state
,
nil
}
// new locked state
var
(
err
error
key
crypto
.
Signer
)
if
ck
.
isRSA
{
key
,
err
=
rsa
.
GenerateKey
(
rand
.
Reader
,
2048
)
}
else
{
key
,
err
=
ecdsa
.
GenerateKey
(
elliptic
.
P256
(),
rand
.
Reader
)
}
if
err
!=
nil
{
return
nil
,
err
}
state
:=
&
certState
{
key
:
key
,
locked
:
true
,
}
state
.
Lock
()
// will be unlocked by m.certState caller
m
.
state
[
ck
]
=
state
return
state
,
nil
}
// authorizedCert starts the domain ownership verification process and requests a new cert upon success.
// The key argument is the certificate private key.
func
(
m
*
Manager
)
authorizedCert
(
ctx
context
.
Context
,
key
crypto
.
Signer
,
ck
certKey
)
(
der
[][]
byte
,
leaf
*
x509
.
Certificate
,
err
error
)
{
client
,
err
:=
m
.
acmeClient
(
ctx
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
if
err
:=
m
.
verify
(
ctx
,
client
,
ck
.
domain
);
err
!=
nil
{
return
nil
,
nil
,
err
}
csr
,
err
:=
certRequest
(
key
,
ck
.
domain
,
m
.
ExtraExtensions
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
der
,
_
,
err
=
client
.
CreateCert
(
ctx
,
csr
,
0
,
true
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
leaf
,
err
=
validCert
(
ck
,
der
,
key
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
return
der
,
leaf
,
nil
}
// revokePendingAuthz revokes all authorizations idenfied by the elements of uri slice.
// It ignores revocation errors.
func
(
m
*
Manager
)
revokePendingAuthz
(
ctx
context
.
Context
,
uri
[]
string
)
{
client
,
err
:=
m
.
acmeClient
(
ctx
)
if
err
!=
nil
{
return
}
for
_
,
u
:=
range
uri
{
client
.
RevokeAuthorization
(
ctx
,
u
)
}
}
// verify runs the identifier (domain) authorization flow
// using each applicable ACME challenge type.
func
(
m
*
Manager
)
verify
(
ctx
context
.
Context
,
client
*
acme
.
Client
,
domain
string
)
error
{
// The list of challenge types we'll try to fulfill
// in this specific order.
challengeTypes
:=
[]
string
{
"tls-alpn-01"
,
"tls-sni-02"
,
"tls-sni-01"
}
m
.
tokensMu
.
RLock
()
if
m
.
tryHTTP01
{
challengeTypes
=
append
(
challengeTypes
,
"http-01"
)
}
m
.
tokensMu
.
RUnlock
()
// Keep track of pending authzs and revoke the ones that did not validate.
pendingAuthzs
:=
make
(
map
[
string
]
bool
)
defer
func
()
{
var
uri
[]
string
for
k
,
pending
:=
range
pendingAuthzs
{
if
pending
{
uri
=
append
(
uri
,
k
)
}
}
if
len
(
uri
)
>
0
{
// Use "detached" background context.
// The revocations need not happen in the current verification flow.
go
m
.
revokePendingAuthz
(
context
.
Background
(),
uri
)
}
}()
// errs accumulates challenge failure errors, printed if all fail
errs
:=
make
(
map
[
*
acme
.
Challenge
]
error
)
var
nextTyp
int
// challengeType index of the next challenge type to try
for
{
// Start domain authorization and get the challenge.
authz
,
err
:=
client
.
Authorize
(
ctx
,
domain
)
if
err
!=
nil
{
return
err
}
// No point in accepting challenges if the authorization status
// is in a final state.
switch
authz
.
Status
{
case
acme
.
StatusValid
:
return
nil
// already authorized
case
acme
.
StatusInvalid
:
return
fmt
.
Errorf
(
"acme/autocert: invalid authorization %q"
,
authz
.
URI
)
}
pendingAuthzs
[
authz
.
URI
]
=
true
// Pick the next preferred challenge.
var
chal
*
acme
.
Challenge
for
chal
==
nil
&&
nextTyp
<
len
(
challengeTypes
)
{
chal
=
pickChallenge
(
challengeTypes
[
nextTyp
],
authz
.
Challenges
)
nextTyp
++
}
if
chal
==
nil
{
errorMsg
:=
fmt
.
Sprintf
(
"acme/autocert: unable to authorize %q"
,
domain
)
for
chal
,
err
:=
range
errs
{
errorMsg
+=
fmt
.
Sprintf
(
"; challenge %q failed with error: %v"
,
chal
.
Type
,
err
)
}
return
errors
.
New
(
errorMsg
)
}
cleanup
,
err
:=
m
.
fulfill
(
ctx
,
client
,
chal
,
domain
)
if
err
!=
nil
{
errs
[
chal
]
=
err
continue
}
defer
cleanup
()
if
_
,
err
:=
client
.
Accept
(
ctx
,
chal
);
err
!=
nil
{
errs
[
chal
]
=
err
continue
}
// A challenge is fulfilled and accepted: wait for the CA to validate.
if
_
,
err
:=
client
.
WaitAuthorization
(
ctx
,
authz
.
URI
);
err
!=
nil
{
errs
[
chal
]
=
err
continue
}
delete
(
pendingAuthzs
,
authz
.
URI
)
return
nil
}
}
// fulfill provisions a response to the challenge chal.
// The cleanup is non-nil only if provisioning succeeded.
func
(
m
*
Manager
)
fulfill
(
ctx
context
.
Context
,
client
*
acme
.
Client
,
chal
*
acme
.
Challenge
,
domain
string
)
(
cleanup
func
(),
err
error
)
{
switch
chal
.
Type
{
case
"tls-alpn-01"
:
cert
,
err
:=
client
.
TLSALPN01ChallengeCert
(
chal
.
Token
,
domain
)
if
err
!=
nil
{
return
nil
,
err
}
m
.
putCertToken
(
ctx
,
domain
,
&
cert
)
return
func
()
{
go
m
.
deleteCertToken
(
domain
)
},
nil
case
"tls-sni-01"
:
cert
,
name
,
err
:=
client
.
TLSSNI01ChallengeCert
(
chal
.
Token
)
if
err
!=
nil
{
return
nil
,
err
}
m
.
putCertToken
(
ctx
,
name
,
&
cert
)
return
func
()
{
go
m
.
deleteCertToken
(
name
)
},
nil
case
"tls-sni-02"
:
cert
,
name
,
err
:=
client
.
TLSSNI02ChallengeCert
(
chal
.
Token
)
if
err
!=
nil
{
return
nil
,
err
}
m
.
putCertToken
(
ctx
,
name
,
&
cert
)
return
func
()
{
go
m
.
deleteCertToken
(
name
)
},
nil
case
"http-01"
:
resp
,
err
:=
client
.
HTTP01ChallengeResponse
(
chal
.
Token
)
if
err
!=
nil
{
return
nil
,
err
}
p
:=
client
.
HTTP01ChallengePath
(
chal
.
Token
)
m
.
putHTTPToken
(
ctx
,
p
,
resp
)
return
func
()
{
go
m
.
deleteHTTPToken
(
p
)
},
nil
}
return
nil
,
fmt
.
Errorf
(
"acme/autocert: unknown challenge type %q"
,
chal
.
Type
)
}
func
pickChallenge
(
typ
string
,
chal
[]
*
acme
.
Challenge
)
*
acme
.
Challenge
{
for
_
,
c
:=
range
chal
{
if
c
.
Type
==
typ
{
return
c
}
}
return
nil
}
// putCertToken stores the token certificate with the specified name
// in both m.certTokens map and m.Cache.
func
(
m
*
Manager
)
putCertToken
(
ctx
context
.
Context
,
name
string
,
cert
*
tls
.
Certificate
)
{
m
.
tokensMu
.
Lock
()
defer
m
.
tokensMu
.
Unlock
()
if
m
.
certTokens
==
nil
{
m
.
certTokens
=
make
(
map
[
string
]
*
tls
.
Certificate
)
}
m
.
certTokens
[
name
]
=
cert
m
.
cachePut
(
ctx
,
certKey
{
domain
:
name
,
isToken
:
true
},
cert
)
}
// deleteCertToken removes the token certificate with the specified name
// from both m.certTokens map and m.Cache.
func
(
m
*
Manager
)
deleteCertToken
(
name
string
)
{
m
.
tokensMu
.
Lock
()
defer
m
.
tokensMu
.
Unlock
()
delete
(
m
.
certTokens
,
name
)
if
m
.
Cache
!=
nil
{
ck
:=
certKey
{
domain
:
name
,
isToken
:
true
}
m
.
Cache
.
Delete
(
context
.
Background
(),
ck
.
String
())
}
}
// httpToken retrieves an existing http-01 token value from an in-memory map
// or the optional cache.
func
(
m
*
Manager
)
httpToken
(
ctx
context
.
Context
,
tokenPath
string
)
([]
byte
,
error
)
{
m
.
tokensMu
.
RLock
()
defer
m
.
tokensMu
.
RUnlock
()
if
v
,
ok
:=
m
.
httpTokens
[
tokenPath
];
ok
{
return
v
,
nil
}
if
m
.
Cache
==
nil
{
return
nil
,
fmt
.
Errorf
(
"acme/autocert: no token at %q"
,
tokenPath
)
}
return
m
.
Cache
.
Get
(
ctx
,
httpTokenCacheKey
(
tokenPath
))
}
// putHTTPToken stores an http-01 token value using tokenPath as key
// in both in-memory map and the optional Cache.
//
// It ignores any error returned from Cache.Put.
func
(
m
*
Manager
)
putHTTPToken
(
ctx
context
.
Context
,
tokenPath
,
val
string
)
{
m
.
tokensMu
.
Lock
()
defer
m
.
tokensMu
.
Unlock
()
if
m
.
httpTokens
==
nil
{
m
.
httpTokens
=
make
(
map
[
string
][]
byte
)
}
b
:=
[]
byte
(
val
)
m
.
httpTokens
[
tokenPath
]
=
b
if
m
.
Cache
!=
nil
{
m
.
Cache
.
Put
(
ctx
,
httpTokenCacheKey
(
tokenPath
),
b
)
}
}
// deleteHTTPToken removes an http-01 token value from both in-memory map
// and the optional Cache, ignoring any error returned from the latter.
//
// If m.Cache is non-nil, it blocks until Cache.Delete returns without a timeout.
func
(
m
*
Manager
)
deleteHTTPToken
(
tokenPath
string
)
{
m
.
tokensMu
.
Lock
()
defer
m
.
tokensMu
.
Unlock
()
delete
(
m
.
httpTokens
,
tokenPath
)
if
m
.
Cache
!=
nil
{
m
.
Cache
.
Delete
(
context
.
Background
(),
httpTokenCacheKey
(
tokenPath
))
}
}
// httpTokenCacheKey returns a key at which an http-01 token value may be stored
// in the Manager's optional Cache.
func
httpTokenCacheKey
(
tokenPath
string
)
string
{
return
path
.
Base
(
tokenPath
)
+
"+http-01"
}
// renew starts a cert renewal timer loop, one per domain.
//
// The loop is scheduled in two cases:
// - a cert was fetched from cache for the first time (wasn't in m.state)
// - a new cert was created by m.createCert
//
// The key argument is a certificate private key.
// The exp argument is the cert expiration time (NotAfter).
func
(
m
*
Manager
)
renew
(
ck
certKey
,
key
crypto
.
Signer
,
exp
time
.
Time
)
{
m
.
renewalMu
.
Lock
()
defer
m
.
renewalMu
.
Unlock
()
if
m
.
renewal
[
ck
]
!=
nil
{
// another goroutine is already on it
return
}
if
m
.
renewal
==
nil
{
m
.
renewal
=
make
(
map
[
certKey
]
*
domainRenewal
)
}
dr
:=
&
domainRenewal
{
m
:
m
,
ck
:
ck
,
key
:
key
}
m
.
renewal
[
ck
]
=
dr
dr
.
start
(
exp
)
}
// stopRenew stops all currently running cert renewal timers.
// The timers are not restarted during the lifetime of the Manager.
func
(
m
*
Manager
)
stopRenew
()
{
m
.
renewalMu
.
Lock
()
defer
m
.
renewalMu
.
Unlock
()
for
name
,
dr
:=
range
m
.
renewal
{
delete
(
m
.
renewal
,
name
)
dr
.
stop
()
}
}
func
(
m
*
Manager
)
accountKey
(
ctx
context
.
Context
)
(
crypto
.
Signer
,
error
)
{
const
keyName
=
"acme_account+key"
// Previous versions of autocert stored the value under a different key.
const
legacyKeyName
=
"acme_account.key"
genKey
:=
func
()
(
*
ecdsa
.
PrivateKey
,
error
)
{
return
ecdsa
.
GenerateKey
(
elliptic
.
P256
(),
rand
.
Reader
)
}
if
m
.
Cache
==
nil
{
return
genKey
()
}
data
,
err
:=
m
.
Cache
.
Get
(
ctx
,
keyName
)
if
err
==
ErrCacheMiss
{
data
,
err
=
m
.
Cache
.
Get
(
ctx
,
legacyKeyName
)
}
if
err
==
ErrCacheMiss
{
key
,
err
:=
genKey
()
if
err
!=
nil
{
return
nil
,
err
}
var
buf
bytes
.
Buffer
if
err
:=
encodeECDSAKey
(
&
buf
,
key
);
err
!=
nil
{
return
nil
,
err
}
if
err
:=
m
.
Cache
.
Put
(
ctx
,
keyName
,
buf
.
Bytes
());
err
!=
nil
{
return
nil
,
err
}
return
key
,
nil
}
if
err
!=
nil
{
return
nil
,
err
}
priv
,
_
:=
pem
.
Decode
(
data
)
if
priv
==
nil
||
!
strings
.
Contains
(
priv
.
Type
,
"PRIVATE"
)
{
return
nil
,
errors
.
New
(
"acme/autocert: invalid account key found in cache"
)
}
return
parsePrivateKey
(
priv
.
Bytes
)
}
func
(
m
*
Manager
)
acmeClient
(
ctx
context
.
Context
)
(
*
acme
.
Client
,
error
)
{
m
.
clientMu
.
Lock
()
defer
m
.
clientMu
.
Unlock
()
if
m
.
client
!=
nil
{
return
m
.
client
,
nil
}
client
:=
m
.
Client
if
client
==
nil
{
client
=
&
acme
.
Client
{
DirectoryURL
:
acme
.
LetsEncryptURL
}
}
if
client
.
Key
==
nil
{
var
err
error
client
.
Key
,
err
=
m
.
accountKey
(
ctx
)
if
err
!=
nil
{
return
nil
,
err
}
}
var
contact
[]
string
if
m
.
Email
!=
""
{
contact
=
[]
string
{
"mailto:"
+
m
.
Email
}
}
a
:=
&
acme
.
Account
{
Contact
:
contact
}
_
,
err
:=
client
.
Register
(
ctx
,
a
,
m
.
Prompt
)
if
ae
,
ok
:=
err
.
(
*
acme
.
Error
);
err
==
nil
||
ok
&&
ae
.
StatusCode
==
http
.
StatusConflict
{
// conflict indicates the key is already registered
m
.
client
=
client
err
=
nil
}
return
m
.
client
,
err
}
func
(
m
*
Manager
)
hostPolicy
()
HostPolicy
{
if
m
.
HostPolicy
!=
nil
{
return
m
.
HostPolicy
}
return
defaultHostPolicy
}
func
(
m
*
Manager
)
renewBefore
()
time
.
Duration
{
if
m
.
RenewBefore
>
renewJitter
{
return
m
.
RenewBefore
}
return
720
*
time
.
Hour
// 30 days
}
// certState is ready when its mutex is unlocked for reading.
type
certState
struct
{
sync
.
RWMutex
locked
bool
// locked for read/write
key
crypto
.
Signer
// private key for cert
cert
[][]
byte
// DER encoding
leaf
*
x509
.
Certificate
// parsed cert[0]; always non-nil if cert != nil
}
// tlscert creates a tls.Certificate from s.key and s.cert.
// Callers should wrap it in s.RLock() and s.RUnlock().
func
(
s
*
certState
)
tlscert
()
(
*
tls
.
Certificate
,
error
)
{
if
s
.
key
==
nil
{
return
nil
,
errors
.
New
(
"acme/autocert: missing signer"
)
}
if
len
(
s
.
cert
)
==
0
{
return
nil
,
errors
.
New
(
"acme/autocert: missing certificate"
)
}
return
&
tls
.
Certificate
{
PrivateKey
:
s
.
key
,
Certificate
:
s
.
cert
,
Leaf
:
s
.
leaf
,
},
nil
}
// certRequest generates a CSR for the given common name cn and optional SANs.
func
certRequest
(
key
crypto
.
Signer
,
cn
string
,
ext
[]
pkix
.
Extension
,
san
...
string
)
([]
byte
,
error
)
{
req
:=
&
x509
.
CertificateRequest
{
Subject
:
pkix
.
Name
{
CommonName
:
cn
},
DNSNames
:
san
,
ExtraExtensions
:
ext
,
}
return
x509
.
CreateCertificateRequest
(
rand
.
Reader
,
req
,
key
)
}
// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
//
// Inspired by parsePrivateKey in crypto/tls/tls.go.
func
parsePrivateKey
(
der
[]
byte
)
(
crypto
.
Signer
,
error
)
{
if
key
,
err
:=
x509
.
ParsePKCS1PrivateKey
(
der
);
err
==
nil
{
return
key
,
nil
}
if
key
,
err
:=
x509
.
ParsePKCS8PrivateKey
(
der
);
err
==
nil
{
switch
key
:=
key
.
(
type
)
{
case
*
rsa
.
PrivateKey
:
return
key
,
nil
case
*
ecdsa
.
PrivateKey
:
return
key
,
nil
default
:
return
nil
,
errors
.
New
(
"acme/autocert: unknown private key type in PKCS#8 wrapping"
)
}
}
if
key
,
err
:=
x509
.
ParseECPrivateKey
(
der
);
err
==
nil
{
return
key
,
nil
}
return
nil
,
errors
.
New
(
"acme/autocert: failed to parse private key"
)
}
// validCert parses a cert chain provided as der argument and verifies the leaf and der[0]
// correspond to the private key, the domain and key type match, and expiration dates
// are valid. It doesn't do any revocation checking.
//
// The returned value is the verified leaf cert.
func
validCert
(
ck
certKey
,
der
[][]
byte
,
key
crypto
.
Signer
)
(
leaf
*
x509
.
Certificate
,
err
error
)
{
// parse public part(s)
var
n
int
for
_
,
b
:=
range
der
{
n
+=
len
(
b
)
}
pub
:=
make
([]
byte
,
n
)
n
=
0
for
_
,
b
:=
range
der
{
n
+=
copy
(
pub
[
n
:
],
b
)
}
x509Cert
,
err
:=
x509
.
ParseCertificates
(
pub
)
if
err
!=
nil
||
len
(
x509Cert
)
==
0
{
return
nil
,
errors
.
New
(
"acme/autocert: no public key found"
)
}
// verify the leaf is not expired and matches the domain name
leaf
=
x509Cert
[
0
]
now
:=
timeNow
()
if
now
.
Before
(
leaf
.
NotBefore
)
{
return
nil
,
errors
.
New
(
"acme/autocert: certificate is not valid yet"
)
}
if
now
.
After
(
leaf
.
NotAfter
)
{
return
nil
,
errors
.
New
(
"acme/autocert: expired certificate"
)
}
if
err
:=
leaf
.
VerifyHostname
(
ck
.
domain
);
err
!=
nil
{
return
nil
,
err
}
// ensure the leaf corresponds to the private key and matches the certKey type
switch
pub
:=
leaf
.
PublicKey
.
(
type
)
{
case
*
rsa
.
PublicKey
:
prv
,
ok
:=
key
.
(
*
rsa
.
PrivateKey
)
if
!
ok
{
return
nil
,
errors
.
New
(
"acme/autocert: private key type does not match public key type"
)
}
if
pub
.
N
.
Cmp
(
prv
.
N
)
!=
0
{
return
nil
,
errors
.
New
(
"acme/autocert: private key does not match public key"
)
}
if
!
ck
.
isRSA
&&
!
ck
.
isToken
{
return
nil
,
errors
.
New
(
"acme/autocert: key type does not match expected value"
)
}
case
*
ecdsa
.
PublicKey
:
prv
,
ok
:=
key
.
(
*
ecdsa
.
PrivateKey
)
if
!
ok
{
return
nil
,
errors
.
New
(
"acme/autocert: private key type does not match public key type"
)
}
if
pub
.
X
.
Cmp
(
prv
.
X
)
!=
0
||
pub
.
Y
.
Cmp
(
prv
.
Y
)
!=
0
{
return
nil
,
errors
.
New
(
"acme/autocert: private key does not match public key"
)
}
if
ck
.
isRSA
&&
!
ck
.
isToken
{
return
nil
,
errors
.
New
(
"acme/autocert: key type does not match expected value"
)
}
default
:
return
nil
,
errors
.
New
(
"acme/autocert: unknown public key algorithm"
)
}
return
leaf
,
nil
}
type
lockedMathRand
struct
{
sync
.
Mutex
rnd
*
mathrand
.
Rand
}
func
(
r
*
lockedMathRand
)
int63n
(
max
int64
)
int64
{
r
.
Lock
()
n
:=
r
.
rnd
.
Int63n
(
max
)
r
.
Unlock
()
return
n
}
// For easier testing.
var
(
timeNow
=
time
.
Now
// Called when a state is removed.
testDidRemoveState
=
func
(
certKey
)
{}
)
vendor/golang.org/x/crypto/acme/autocert/cache.go
0 → 100644
View file @
efe0f673
// Copyright 2016 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
autocert
import
(
"context"
"errors"
"io/ioutil"
"os"
"path/filepath"
)
// ErrCacheMiss is returned when a certificate is not found in cache.
var
ErrCacheMiss
=
errors
.
New
(
"acme/autocert: certificate cache miss"
)
// Cache is used by Manager to store and retrieve previously obtained certificates
// and other account data as opaque blobs.
//
// Cache implementations should not rely on the key naming pattern. Keys can
// include any printable ASCII characters, except the following: \/:*?"<>|
type
Cache
interface
{
// Get returns a certificate data for the specified key.
// If there's no such key, Get returns ErrCacheMiss.
Get
(
ctx
context
.
Context
,
key
string
)
([]
byte
,
error
)
// Put stores the data in the cache under the specified key.
// Underlying implementations may use any data storage format,
// as long as the reverse operation, Get, results in the original data.
Put
(
ctx
context
.
Context
,
key
string
,
data
[]
byte
)
error
// Delete removes a certificate data from the cache under the specified key.
// If there's no such key in the cache, Delete returns nil.
Delete
(
ctx
context
.
Context
,
key
string
)
error
}
// DirCache implements Cache using a directory on the local filesystem.
// If the directory does not exist, it will be created with 0700 permissions.
type
DirCache
string
// Get reads a certificate data from the specified file name.
func
(
d
DirCache
)
Get
(
ctx
context
.
Context
,
name
string
)
([]
byte
,
error
)
{
name
=
filepath
.
Join
(
string
(
d
),
name
)
var
(
data
[]
byte
err
error
done
=
make
(
chan
struct
{})
)
go
func
()
{
data
,
err
=
ioutil
.
ReadFile
(
name
)
close
(
done
)
}()
select
{
case
<-
ctx
.
Done
()
:
return
nil
,
ctx
.
Err
()
case
<-
done
:
}
if
os
.
IsNotExist
(
err
)
{
return
nil
,
ErrCacheMiss
}
return
data
,
err
}
// Put writes the certificate data to the specified file name.
// The file will be created with 0600 permissions.
func
(
d
DirCache
)
Put
(
ctx
context
.
Context
,
name
string
,
data
[]
byte
)
error
{
if
err
:=
os
.
MkdirAll
(
string
(
d
),
0700
);
err
!=
nil
{
return
err
}
done
:=
make
(
chan
struct
{})
var
err
error
go
func
()
{
defer
close
(
done
)
var
tmp
string
if
tmp
,
err
=
d
.
writeTempFile
(
name
,
data
);
err
!=
nil
{
return
}
select
{
case
<-
ctx
.
Done
()
:
// Don't overwrite the file if the context was canceled.
default
:
newName
:=
filepath
.
Join
(
string
(
d
),
name
)
err
=
os
.
Rename
(
tmp
,
newName
)
}
}()
select
{
case
<-
ctx
.
Done
()
:
return
ctx
.
Err
()
case
<-
done
:
}
return
err
}
// Delete removes the specified file name.
func
(
d
DirCache
)
Delete
(
ctx
context
.
Context
,
name
string
)
error
{
name
=
filepath
.
Join
(
string
(
d
),
name
)
var
(
err
error
done
=
make
(
chan
struct
{})
)
go
func
()
{
err
=
os
.
Remove
(
name
)
close
(
done
)
}()
select
{
case
<-
ctx
.
Done
()
:
return
ctx
.
Err
()
case
<-
done
:
}
if
err
!=
nil
&&
!
os
.
IsNotExist
(
err
)
{
return
err
}
return
nil
}
// writeTempFile writes b to a temporary file, closes the file and returns its path.
func
(
d
DirCache
)
writeTempFile
(
prefix
string
,
b
[]
byte
)
(
string
,
error
)
{
// TempFile uses 0600 permissions
f
,
err
:=
ioutil
.
TempFile
(
string
(
d
),
prefix
)
if
err
!=
nil
{
return
""
,
err
}
if
_
,
err
:=
f
.
Write
(
b
);
err
!=
nil
{
f
.
Close
()
return
""
,
err
}
return
f
.
Name
(),
f
.
Close
()
}
vendor/golang.org/x/crypto/acme/autocert/listener.go
0 → 100644
View file @
efe0f673
// Copyright 2017 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
autocert
import
(
"crypto/tls"
"log"
"net"
"os"
"path/filepath"
"runtime"
"time"
)
// NewListener returns a net.Listener that listens on the standard TLS
// port (443) on all interfaces and returns *tls.Conn connections with
// LetsEncrypt certificates for the provided domain or domains.
//
// It enables one-line HTTPS servers:
//
// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler))
//
// NewListener is a convenience function for a common configuration.
// More complex or custom configurations can use the autocert.Manager
// type instead.
//
// Use of this function implies acceptance of the LetsEncrypt Terms of
// Service. If domains is not empty, the provided domains are passed
// to HostWhitelist. If domains is empty, the listener will do
// LetsEncrypt challenges for any requested domain, which is not
// recommended.
//
// Certificates are cached in a "golang-autocert" directory under an
// operating system-specific cache or temp directory. This may not
// be suitable for servers spanning multiple machines.
//
// The returned listener uses a *tls.Config that enables HTTP/2, and
// should only be used with servers that support HTTP/2.
//
// The returned Listener also enables TCP keep-alives on the accepted
// connections. The returned *tls.Conn are returned before their TLS
// handshake has completed.
func
NewListener
(
domains
...
string
)
net
.
Listener
{
m
:=
&
Manager
{
Prompt
:
AcceptTOS
,
}
if
len
(
domains
)
>
0
{
m
.
HostPolicy
=
HostWhitelist
(
domains
...
)
}
dir
:=
cacheDir
()
if
err
:=
os
.
MkdirAll
(
dir
,
0700
);
err
!=
nil
{
log
.
Printf
(
"warning: autocert.NewListener not using a cache: %v"
,
err
)
}
else
{
m
.
Cache
=
DirCache
(
dir
)
}
return
m
.
Listener
()
}
// Listener listens on the standard TLS port (443) on all interfaces
// and returns a net.Listener returning *tls.Conn connections.
//
// The returned listener uses a *tls.Config that enables HTTP/2, and
// should only be used with servers that support HTTP/2.
//
// The returned Listener also enables TCP keep-alives on the accepted
// connections. The returned *tls.Conn are returned before their TLS
// handshake has completed.
//
// Unlike NewListener, it is the caller's responsibility to initialize
// the Manager m's Prompt, Cache, HostPolicy, and other desired options.
func
(
m
*
Manager
)
Listener
()
net
.
Listener
{
ln
:=
&
listener
{
m
:
m
,
conf
:
m
.
TLSConfig
(),
}
ln
.
tcpListener
,
ln
.
tcpListenErr
=
net
.
Listen
(
"tcp"
,
":443"
)
return
ln
}
type
listener
struct
{
m
*
Manager
conf
*
tls
.
Config
tcpListener
net
.
Listener
tcpListenErr
error
}
func
(
ln
*
listener
)
Accept
()
(
net
.
Conn
,
error
)
{
if
ln
.
tcpListenErr
!=
nil
{
return
nil
,
ln
.
tcpListenErr
}
conn
,
err
:=
ln
.
tcpListener
.
Accept
()
if
err
!=
nil
{
return
nil
,
err
}
tcpConn
:=
conn
.
(
*
net
.
TCPConn
)
// Because Listener is a convenience function, help out with
// this too. This is not possible for the caller to set once
// we return a *tcp.Conn wrapping an inaccessible net.Conn.
// If callers don't want this, they can do things the manual
// way and tweak as needed. But this is what net/http does
// itself, so copy that. If net/http changes, we can change
// here too.
tcpConn
.
SetKeepAlive
(
true
)
tcpConn
.
SetKeepAlivePeriod
(
3
*
time
.
Minute
)
return
tls
.
Server
(
tcpConn
,
ln
.
conf
),
nil
}
func
(
ln
*
listener
)
Addr
()
net
.
Addr
{
if
ln
.
tcpListener
!=
nil
{
return
ln
.
tcpListener
.
Addr
()
}
// net.Listen failed. Return something non-nil in case callers
// call Addr before Accept:
return
&
net
.
TCPAddr
{
IP
:
net
.
IP
{
0
,
0
,
0
,
0
},
Port
:
443
}
}
func
(
ln
*
listener
)
Close
()
error
{
if
ln
.
tcpListenErr
!=
nil
{
return
ln
.
tcpListenErr
}
return
ln
.
tcpListener
.
Close
()
}
func
homeDir
()
string
{
if
runtime
.
GOOS
==
"windows"
{
return
os
.
Getenv
(
"HOMEDRIVE"
)
+
os
.
Getenv
(
"HOMEPATH"
)
}
if
h
:=
os
.
Getenv
(
"HOME"
);
h
!=
""
{
return
h
}
return
"/"
}
func
cacheDir
()
string
{
const
base
=
"golang-autocert"
switch
runtime
.
GOOS
{
case
"darwin"
:
return
filepath
.
Join
(
homeDir
(),
"Library"
,
"Caches"
,
base
)
case
"windows"
:
for
_
,
ev
:=
range
[]
string
{
"APPDATA"
,
"CSIDL_APPDATA"
,
"TEMP"
,
"TMP"
}
{
if
v
:=
os
.
Getenv
(
ev
);
v
!=
""
{
return
filepath
.
Join
(
v
,
base
)
}
}
// Worst case:
return
filepath
.
Join
(
homeDir
(),
base
)
}
if
xdg
:=
os
.
Getenv
(
"XDG_CACHE_HOME"
);
xdg
!=
""
{
return
filepath
.
Join
(
xdg
,
base
)
}
return
filepath
.
Join
(
homeDir
(),
".cache"
,
base
)
}
vendor/golang.org/x/crypto/acme/autocert/renewal.go
0 → 100644
View file @
efe0f673
// Copyright 2016 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
autocert
import
(
"context"
"crypto"
"sync"
"time"
)
// renewJitter is the maximum deviation from Manager.RenewBefore.
const
renewJitter
=
time
.
Hour
// domainRenewal tracks the state used by the periodic timers
// renewing a single domain's cert.
type
domainRenewal
struct
{
m
*
Manager
ck
certKey
key
crypto
.
Signer
timerMu
sync
.
Mutex
timer
*
time
.
Timer
}
// start starts a cert renewal timer at the time
// defined by the certificate expiration time exp.
//
// If the timer is already started, calling start is a noop.
func
(
dr
*
domainRenewal
)
start
(
exp
time
.
Time
)
{
dr
.
timerMu
.
Lock
()
defer
dr
.
timerMu
.
Unlock
()
if
dr
.
timer
!=
nil
{
return
}
dr
.
timer
=
time
.
AfterFunc
(
dr
.
next
(
exp
),
dr
.
renew
)
}
// stop stops the cert renewal timer.
// If the timer is already stopped, calling stop is a noop.
func
(
dr
*
domainRenewal
)
stop
()
{
dr
.
timerMu
.
Lock
()
defer
dr
.
timerMu
.
Unlock
()
if
dr
.
timer
==
nil
{
return
}
dr
.
timer
.
Stop
()
dr
.
timer
=
nil
}
// renew is called periodically by a timer.
// The first renew call is kicked off by dr.start.
func
(
dr
*
domainRenewal
)
renew
()
{
dr
.
timerMu
.
Lock
()
defer
dr
.
timerMu
.
Unlock
()
if
dr
.
timer
==
nil
{
return
}
ctx
,
cancel
:=
context
.
WithTimeout
(
context
.
Background
(),
10
*
time
.
Minute
)
defer
cancel
()
// TODO: rotate dr.key at some point?
next
,
err
:=
dr
.
do
(
ctx
)
if
err
!=
nil
{
next
=
renewJitter
/
2
next
+=
time
.
Duration
(
pseudoRand
.
int63n
(
int64
(
next
)))
}
dr
.
timer
=
time
.
AfterFunc
(
next
,
dr
.
renew
)
testDidRenewLoop
(
next
,
err
)
}
// updateState locks and replaces the relevant Manager.state item with the given
// state. It additionally updates dr.key with the given state's key.
func
(
dr
*
domainRenewal
)
updateState
(
state
*
certState
)
{
dr
.
m
.
stateMu
.
Lock
()
defer
dr
.
m
.
stateMu
.
Unlock
()
dr
.
key
=
state
.
key
dr
.
m
.
state
[
dr
.
ck
]
=
state
}
// do is similar to Manager.createCert but it doesn't lock a Manager.state item.
// Instead, it requests a new certificate independently and, upon success,
// replaces dr.m.state item with a new one and updates cache for the given domain.
//
// It may lock and update the Manager.state if the expiration date of the currently
// cached cert is far enough in the future.
//
// The returned value is a time interval after which the renewal should occur again.
func
(
dr
*
domainRenewal
)
do
(
ctx
context
.
Context
)
(
time
.
Duration
,
error
)
{
// a race is likely unavoidable in a distributed environment
// but we try nonetheless
if
tlscert
,
err
:=
dr
.
m
.
cacheGet
(
ctx
,
dr
.
ck
);
err
==
nil
{
next
:=
dr
.
next
(
tlscert
.
Leaf
.
NotAfter
)
if
next
>
dr
.
m
.
renewBefore
()
+
renewJitter
{
signer
,
ok
:=
tlscert
.
PrivateKey
.
(
crypto
.
Signer
)
if
ok
{
state
:=
&
certState
{
key
:
signer
,
cert
:
tlscert
.
Certificate
,
leaf
:
tlscert
.
Leaf
,
}
dr
.
updateState
(
state
)
return
next
,
nil
}
}
}
der
,
leaf
,
err
:=
dr
.
m
.
authorizedCert
(
ctx
,
dr
.
key
,
dr
.
ck
)
if
err
!=
nil
{
return
0
,
err
}
state
:=
&
certState
{
key
:
dr
.
key
,
cert
:
der
,
leaf
:
leaf
,
}
tlscert
,
err
:=
state
.
tlscert
()
if
err
!=
nil
{
return
0
,
err
}
if
err
:=
dr
.
m
.
cachePut
(
ctx
,
dr
.
ck
,
tlscert
);
err
!=
nil
{
return
0
,
err
}
dr
.
updateState
(
state
)
return
dr
.
next
(
leaf
.
NotAfter
),
nil
}
func
(
dr
*
domainRenewal
)
next
(
expiry
time
.
Time
)
time
.
Duration
{
d
:=
expiry
.
Sub
(
timeNow
())
-
dr
.
m
.
renewBefore
()
// add a bit of randomness to renew deadline
n
:=
pseudoRand
.
int63n
(
int64
(
renewJitter
))
d
-=
time
.
Duration
(
n
)
if
d
<
0
{
return
0
}
return
d
}
var
testDidRenewLoop
=
func
(
next
time
.
Duration
,
err
error
)
{}
vendor/golang.org/x/crypto/acme/http.go
0 → 100644
View file @
efe0f673
// Copyright 2018 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
acme
import
(
"bytes"
"context"
"crypto"
"crypto/rand"
"encoding/json"
"fmt"
"io/ioutil"
"math/big"
"net/http"
"strconv"
"strings"
"time"
)
// retryTimer encapsulates common logic for retrying unsuccessful requests.
// It is not safe for concurrent use.
type
retryTimer
struct
{
// backoffFn provides backoff delay sequence for retries.
// See Client.RetryBackoff doc comment.
backoffFn
func
(
n
int
,
r
*
http
.
Request
,
res
*
http
.
Response
)
time
.
Duration
// n is the current retry attempt.
n
int
}
func
(
t
*
retryTimer
)
inc
()
{
t
.
n
++
}
// backoff pauses the current goroutine as described in Client.RetryBackoff.
func
(
t
*
retryTimer
)
backoff
(
ctx
context
.
Context
,
r
*
http
.
Request
,
res
*
http
.
Response
)
error
{
d
:=
t
.
backoffFn
(
t
.
n
,
r
,
res
)
if
d
<=
0
{
return
fmt
.
Errorf
(
"acme: no more retries for %s; tried %d time(s)"
,
r
.
URL
,
t
.
n
)
}
wakeup
:=
time
.
NewTimer
(
d
)
defer
wakeup
.
Stop
()
select
{
case
<-
ctx
.
Done
()
:
return
ctx
.
Err
()
case
<-
wakeup
.
C
:
return
nil
}
}
func
(
c
*
Client
)
retryTimer
()
*
retryTimer
{
f
:=
c
.
RetryBackoff
if
f
==
nil
{
f
=
defaultBackoff
}
return
&
retryTimer
{
backoffFn
:
f
}
}
// defaultBackoff provides default Client.RetryBackoff implementation
// using a truncated exponential backoff algorithm,
// as described in Client.RetryBackoff.
//
// The n argument is always bounded between 1 and 30.
// The returned value is always greater than 0.
func
defaultBackoff
(
n
int
,
r
*
http
.
Request
,
res
*
http
.
Response
)
time
.
Duration
{
const
max
=
10
*
time
.
Second
var
jitter
time
.
Duration
if
x
,
err
:=
rand
.
Int
(
rand
.
Reader
,
big
.
NewInt
(
1000
));
err
==
nil
{
// Set the minimum to 1ms to avoid a case where
// an invalid Retry-After value is parsed into 0 below,
// resulting in the 0 returned value which would unintentionally
// stop the retries.
jitter
=
(
1
+
time
.
Duration
(
x
.
Int64
()))
*
time
.
Millisecond
}
if
v
,
ok
:=
res
.
Header
[
"Retry-After"
];
ok
{
return
retryAfter
(
v
[
0
])
+
jitter
}
if
n
<
1
{
n
=
1
}
if
n
>
30
{
n
=
30
}
d
:=
time
.
Duration
(
1
<<
uint
(
n
-
1
))
*
time
.
Second
+
jitter
if
d
>
max
{
return
max
}
return
d
}
// retryAfter parses a Retry-After HTTP header value,
// trying to convert v into an int (seconds) or use http.ParseTime otherwise.
// It returns zero value if v cannot be parsed.
func
retryAfter
(
v
string
)
time
.
Duration
{
if
i
,
err
:=
strconv
.
Atoi
(
v
);
err
==
nil
{
return
time
.
Duration
(
i
)
*
time
.
Second
}
t
,
err
:=
http
.
ParseTime
(
v
)
if
err
!=
nil
{
return
0
}
return
t
.
Sub
(
timeNow
())
}
// resOkay is a function that reports whether the provided response is okay.
// It is expected to keep the response body unread.
type
resOkay
func
(
*
http
.
Response
)
bool
// wantStatus returns a function which reports whether the code
// matches the status code of a response.
func
wantStatus
(
codes
...
int
)
resOkay
{
return
func
(
res
*
http
.
Response
)
bool
{
for
_
,
code
:=
range
codes
{
if
code
==
res
.
StatusCode
{
return
true
}
}
return
false
}
}
// get issues an unsigned GET request to the specified URL.
// It returns a non-error value only when ok reports true.
//
// get retries unsuccessful attempts according to c.RetryBackoff
// until the context is done or a non-retriable error is received.
func
(
c
*
Client
)
get
(
ctx
context
.
Context
,
url
string
,
ok
resOkay
)
(
*
http
.
Response
,
error
)
{
retry
:=
c
.
retryTimer
()
for
{
req
,
err
:=
http
.
NewRequest
(
"GET"
,
url
,
nil
)
if
err
!=
nil
{
return
nil
,
err
}
res
,
err
:=
c
.
doNoRetry
(
ctx
,
req
)
switch
{
case
err
!=
nil
:
return
nil
,
err
case
ok
(
res
)
:
return
res
,
nil
case
isRetriable
(
res
.
StatusCode
)
:
retry
.
inc
()
resErr
:=
responseError
(
res
)
res
.
Body
.
Close
()
// Ignore the error value from retry.backoff
// and return the one from last retry, as received from the CA.
if
retry
.
backoff
(
ctx
,
req
,
res
)
!=
nil
{
return
nil
,
resErr
}
default
:
defer
res
.
Body
.
Close
()
return
nil
,
responseError
(
res
)
}
}
}
// post issues a signed POST request in JWS format using the provided key
// to the specified URL.
// It returns a non-error value only when ok reports true.
//
// post retries unsuccessful attempts according to c.RetryBackoff
// until the context is done or a non-retriable error is received.
// It uses postNoRetry to make individual requests.
func
(
c
*
Client
)
post
(
ctx
context
.
Context
,
key
crypto
.
Signer
,
url
string
,
body
interface
{},
ok
resOkay
)
(
*
http
.
Response
,
error
)
{
retry
:=
c
.
retryTimer
()
for
{
res
,
req
,
err
:=
c
.
postNoRetry
(
ctx
,
key
,
url
,
body
)
if
err
!=
nil
{
return
nil
,
err
}
if
ok
(
res
)
{
return
res
,
nil
}
resErr
:=
responseError
(
res
)
res
.
Body
.
Close
()
switch
{
// Check for bad nonce before isRetriable because it may have been returned
// with an unretriable response code such as 400 Bad Request.
case
isBadNonce
(
resErr
)
:
// Consider any previously stored nonce values to be invalid.
c
.
clearNonces
()
case
!
isRetriable
(
res
.
StatusCode
)
:
return
nil
,
resErr
}
retry
.
inc
()
// Ignore the error value from retry.backoff
// and return the one from last retry, as received from the CA.
if
err
:=
retry
.
backoff
(
ctx
,
req
,
res
);
err
!=
nil
{
return
nil
,
resErr
}
}
}
// postNoRetry signs the body with the given key and POSTs it to the provided url.
// The body argument must be JSON-serializable.
// It is used by c.post to retry unsuccessful attempts.
func
(
c
*
Client
)
postNoRetry
(
ctx
context
.
Context
,
key
crypto
.
Signer
,
url
string
,
body
interface
{})
(
*
http
.
Response
,
*
http
.
Request
,
error
)
{
nonce
,
err
:=
c
.
popNonce
(
ctx
,
url
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
b
,
err
:=
jwsEncodeJSON
(
body
,
key
,
nonce
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
req
,
err
:=
http
.
NewRequest
(
"POST"
,
url
,
bytes
.
NewReader
(
b
))
if
err
!=
nil
{
return
nil
,
nil
,
err
}
req
.
Header
.
Set
(
"Content-Type"
,
"application/jose+json"
)
res
,
err
:=
c
.
doNoRetry
(
ctx
,
req
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
c
.
addNonce
(
res
.
Header
)
return
res
,
req
,
nil
}
// doNoRetry issues a request req, replacing its context (if any) with ctx.
func
(
c
*
Client
)
doNoRetry
(
ctx
context
.
Context
,
req
*
http
.
Request
)
(
*
http
.
Response
,
error
)
{
res
,
err
:=
c
.
httpClient
()
.
Do
(
req
.
WithContext
(
ctx
))
if
err
!=
nil
{
select
{
case
<-
ctx
.
Done
()
:
// Prefer the unadorned context error.
// (The acme package had tests assuming this, previously from ctxhttp's
// behavior, predating net/http supporting contexts natively)
// TODO(bradfitz): reconsider this in the future. But for now this
// requires no test updates.
return
nil
,
ctx
.
Err
()
default
:
return
nil
,
err
}
}
return
res
,
nil
}
func
(
c
*
Client
)
httpClient
()
*
http
.
Client
{
if
c
.
HTTPClient
!=
nil
{
return
c
.
HTTPClient
}
return
http
.
DefaultClient
}
// isBadNonce reports whether err is an ACME "badnonce" error.
func
isBadNonce
(
err
error
)
bool
{
// According to the spec badNonce is urn:ietf:params:acme:error:badNonce.
// However, ACME servers in the wild return their versions of the error.
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02#section-5.4
// and https://github.com/letsencrypt/boulder/blob/0e07eacb/docs/acme-divergences.md#section-66.
ae
,
ok
:=
err
.
(
*
Error
)
return
ok
&&
strings
.
HasSuffix
(
strings
.
ToLower
(
ae
.
ProblemType
),
":badnonce"
)
}
// isRetriable reports whether a request can be retried
// based on the response status code.
//
// Note that a "bad nonce" error is returned with a non-retriable 400 Bad Request code.
// Callers should parse the response and check with isBadNonce.
func
isRetriable
(
code
int
)
bool
{
return
code
<=
399
||
code
>=
500
||
code
==
http
.
StatusTooManyRequests
}
// responseError creates an error of Error type from resp.
func
responseError
(
resp
*
http
.
Response
)
error
{
// don't care if ReadAll returns an error:
// json.Unmarshal will fail in that case anyway
b
,
_
:=
ioutil
.
ReadAll
(
resp
.
Body
)
e
:=
&
wireError
{
Status
:
resp
.
StatusCode
}
if
err
:=
json
.
Unmarshal
(
b
,
e
);
err
!=
nil
{
// this is not a regular error response:
// populate detail with anything we received,
// e.Status will already contain HTTP response code value
e
.
Detail
=
string
(
b
)
if
e
.
Detail
==
""
{
e
.
Detail
=
resp
.
Status
}
}
return
e
.
error
(
resp
.
Header
)
}
vendor/golang.org/x/crypto/acme/jws.go
0 → 100644
View file @
efe0f673
// Copyright 2015 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
acme
import
(
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
_
"crypto/sha512"
// need for EC keys
"encoding/base64"
"encoding/json"
"fmt"
"math/big"
)
// jwsEncodeJSON signs claimset using provided key and a nonce.
// The result is serialized in JSON format.
// See https://tools.ietf.org/html/rfc7515#section-7.
func
jwsEncodeJSON
(
claimset
interface
{},
key
crypto
.
Signer
,
nonce
string
)
([]
byte
,
error
)
{
jwk
,
err
:=
jwkEncode
(
key
.
Public
())
if
err
!=
nil
{
return
nil
,
err
}
alg
,
sha
:=
jwsHasher
(
key
)
if
alg
==
""
||
!
sha
.
Available
()
{
return
nil
,
ErrUnsupportedKey
}
phead
:=
fmt
.
Sprintf
(
`{"alg":%q,"jwk":%s,"nonce":%q}`
,
alg
,
jwk
,
nonce
)
phead
=
base64
.
RawURLEncoding
.
EncodeToString
([]
byte
(
phead
))
cs
,
err
:=
json
.
Marshal
(
claimset
)
if
err
!=
nil
{
return
nil
,
err
}
payload
:=
base64
.
RawURLEncoding
.
EncodeToString
(
cs
)
hash
:=
sha
.
New
()
hash
.
Write
([]
byte
(
phead
+
"."
+
payload
))
sig
,
err
:=
jwsSign
(
key
,
sha
,
hash
.
Sum
(
nil
))
if
err
!=
nil
{
return
nil
,
err
}
enc
:=
struct
{
Protected
string
`json:"protected"`
Payload
string
`json:"payload"`
Sig
string
`json:"signature"`
}{
Protected
:
phead
,
Payload
:
payload
,
Sig
:
base64
.
RawURLEncoding
.
EncodeToString
(
sig
),
}
return
json
.
Marshal
(
&
enc
)
}
// jwkEncode encodes public part of an RSA or ECDSA key into a JWK.
// The result is also suitable for creating a JWK thumbprint.
// https://tools.ietf.org/html/rfc7517
func
jwkEncode
(
pub
crypto
.
PublicKey
)
(
string
,
error
)
{
switch
pub
:=
pub
.
(
type
)
{
case
*
rsa
.
PublicKey
:
// https://tools.ietf.org/html/rfc7518#section-6.3.1
n
:=
pub
.
N
e
:=
big
.
NewInt
(
int64
(
pub
.
E
))
// Field order is important.
// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
return
fmt
.
Sprintf
(
`{"e":"%s","kty":"RSA","n":"%s"}`
,
base64
.
RawURLEncoding
.
EncodeToString
(
e
.
Bytes
()),
base64
.
RawURLEncoding
.
EncodeToString
(
n
.
Bytes
()),
),
nil
case
*
ecdsa
.
PublicKey
:
// https://tools.ietf.org/html/rfc7518#section-6.2.1
p
:=
pub
.
Curve
.
Params
()
n
:=
p
.
BitSize
/
8
if
p
.
BitSize
%
8
!=
0
{
n
++
}
x
:=
pub
.
X
.
Bytes
()
if
n
>
len
(
x
)
{
x
=
append
(
make
([]
byte
,
n
-
len
(
x
)),
x
...
)
}
y
:=
pub
.
Y
.
Bytes
()
if
n
>
len
(
y
)
{
y
=
append
(
make
([]
byte
,
n
-
len
(
y
)),
y
...
)
}
// Field order is important.
// See https://tools.ietf.org/html/rfc7638#section-3.3 for details.
return
fmt
.
Sprintf
(
`{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`
,
p
.
Name
,
base64
.
RawURLEncoding
.
EncodeToString
(
x
),
base64
.
RawURLEncoding
.
EncodeToString
(
y
),
),
nil
}
return
""
,
ErrUnsupportedKey
}
// jwsSign signs the digest using the given key.
// It returns ErrUnsupportedKey if the key type is unknown.
// The hash is used only for RSA keys.
func
jwsSign
(
key
crypto
.
Signer
,
hash
crypto
.
Hash
,
digest
[]
byte
)
([]
byte
,
error
)
{
switch
key
:=
key
.
(
type
)
{
case
*
rsa
.
PrivateKey
:
return
key
.
Sign
(
rand
.
Reader
,
digest
,
hash
)
case
*
ecdsa
.
PrivateKey
:
r
,
s
,
err
:=
ecdsa
.
Sign
(
rand
.
Reader
,
key
,
digest
)
if
err
!=
nil
{
return
nil
,
err
}
rb
,
sb
:=
r
.
Bytes
(),
s
.
Bytes
()
size
:=
key
.
Params
()
.
BitSize
/
8
if
size
%
8
>
0
{
size
++
}
sig
:=
make
([]
byte
,
size
*
2
)
copy
(
sig
[
size
-
len
(
rb
)
:
],
rb
)
copy
(
sig
[
size
*
2
-
len
(
sb
)
:
],
sb
)
return
sig
,
nil
}
return
nil
,
ErrUnsupportedKey
}
// jwsHasher indicates suitable JWS algorithm name and a hash function
// to use for signing a digest with the provided key.
// It returns ("", 0) if the key is not supported.
func
jwsHasher
(
key
crypto
.
Signer
)
(
string
,
crypto
.
Hash
)
{
switch
key
:=
key
.
(
type
)
{
case
*
rsa
.
PrivateKey
:
return
"RS256"
,
crypto
.
SHA256
case
*
ecdsa
.
PrivateKey
:
switch
key
.
Params
()
.
Name
{
case
"P-256"
:
return
"ES256"
,
crypto
.
SHA256
case
"P-384"
:
return
"ES384"
,
crypto
.
SHA384
case
"P-521"
:
return
"ES512"
,
crypto
.
SHA512
}
}
return
""
,
0
}
// JWKThumbprint creates a JWK thumbprint out of pub
// as specified in https://tools.ietf.org/html/rfc7638.
func
JWKThumbprint
(
pub
crypto
.
PublicKey
)
(
string
,
error
)
{
jwk
,
err
:=
jwkEncode
(
pub
)
if
err
!=
nil
{
return
""
,
err
}
b
:=
sha256
.
Sum256
([]
byte
(
jwk
))
return
base64
.
RawURLEncoding
.
EncodeToString
(
b
[
:
]),
nil
}
vendor/golang.org/x/crypto/acme/types.go
0 → 100644
View file @
efe0f673
// Copyright 2016 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
acme
import
(
"crypto"
"crypto/x509"
"errors"
"fmt"
"net/http"
"strings"
"time"
)
// ACME server response statuses used to describe Authorization and Challenge states.
const
(
StatusUnknown
=
"unknown"
StatusPending
=
"pending"
StatusProcessing
=
"processing"
StatusValid
=
"valid"
StatusInvalid
=
"invalid"
StatusRevoked
=
"revoked"
)
// CRLReasonCode identifies the reason for a certificate revocation.
type
CRLReasonCode
int
// CRL reason codes as defined in RFC 5280.
const
(
CRLReasonUnspecified
CRLReasonCode
=
0
CRLReasonKeyCompromise
CRLReasonCode
=
1
CRLReasonCACompromise
CRLReasonCode
=
2
CRLReasonAffiliationChanged
CRLReasonCode
=
3
CRLReasonSuperseded
CRLReasonCode
=
4
CRLReasonCessationOfOperation
CRLReasonCode
=
5
CRLReasonCertificateHold
CRLReasonCode
=
6
CRLReasonRemoveFromCRL
CRLReasonCode
=
8
CRLReasonPrivilegeWithdrawn
CRLReasonCode
=
9
CRLReasonAACompromise
CRLReasonCode
=
10
)
// ErrUnsupportedKey is returned when an unsupported key type is encountered.
var
ErrUnsupportedKey
=
errors
.
New
(
"acme: unknown key type; only RSA and ECDSA are supported"
)
// Error is an ACME error, defined in Problem Details for HTTP APIs doc
// http://tools.ietf.org/html/draft-ietf-appsawg-http-problem.
type
Error
struct
{
// StatusCode is The HTTP status code generated by the origin server.
StatusCode
int
// ProblemType is a URI reference that identifies the problem type,
// typically in a "urn:acme:error:xxx" form.
ProblemType
string
// Detail is a human-readable explanation specific to this occurrence of the problem.
Detail
string
// Header is the original server error response headers.
// It may be nil.
Header
http
.
Header
}
func
(
e
*
Error
)
Error
()
string
{
return
fmt
.
Sprintf
(
"%d %s: %s"
,
e
.
StatusCode
,
e
.
ProblemType
,
e
.
Detail
)
}
// AuthorizationError indicates that an authorization for an identifier
// did not succeed.
// It contains all errors from Challenge items of the failed Authorization.
type
AuthorizationError
struct
{
// URI uniquely identifies the failed Authorization.
URI
string
// Identifier is an AuthzID.Value of the failed Authorization.
Identifier
string
// Errors is a collection of non-nil error values of Challenge items
// of the failed Authorization.
Errors
[]
error
}
func
(
a
*
AuthorizationError
)
Error
()
string
{
e
:=
make
([]
string
,
len
(
a
.
Errors
))
for
i
,
err
:=
range
a
.
Errors
{
e
[
i
]
=
err
.
Error
()
}
return
fmt
.
Sprintf
(
"acme: authorization error for %s: %s"
,
a
.
Identifier
,
strings
.
Join
(
e
,
"; "
))
}
// RateLimit reports whether err represents a rate limit error and
// any Retry-After duration returned by the server.
//
// See the following for more details on rate limiting:
// https://tools.ietf.org/html/draft-ietf-acme-acme-05#section-5.6
func
RateLimit
(
err
error
)
(
time
.
Duration
,
bool
)
{
e
,
ok
:=
err
.
(
*
Error
)
if
!
ok
{
return
0
,
false
}
// Some CA implementations may return incorrect values.
// Use case-insensitive comparison.
if
!
strings
.
HasSuffix
(
strings
.
ToLower
(
e
.
ProblemType
),
":ratelimited"
)
{
return
0
,
false
}
if
e
.
Header
==
nil
{
return
0
,
true
}
return
retryAfter
(
e
.
Header
.
Get
(
"Retry-After"
)),
true
}
// Account is a user account. It is associated with a private key.
type
Account
struct
{
// URI is the account unique ID, which is also a URL used to retrieve
// account data from the CA.
URI
string
// Contact is a slice of contact info used during registration.
Contact
[]
string
// The terms user has agreed to.
// A value not matching CurrentTerms indicates that the user hasn't agreed
// to the actual Terms of Service of the CA.
AgreedTerms
string
// Actual terms of a CA.
CurrentTerms
string
// Authz is the authorization URL used to initiate a new authz flow.
Authz
string
// Authorizations is a URI from which a list of authorizations
// granted to this account can be fetched via a GET request.
Authorizations
string
// Certificates is a URI from which a list of certificates
// issued for this account can be fetched via a GET request.
Certificates
string
}
// Directory is ACME server discovery data.
type
Directory
struct
{
// RegURL is an account endpoint URL, allowing for creating new
// and modifying existing accounts.
RegURL
string
// AuthzURL is used to initiate Identifier Authorization flow.
AuthzURL
string
// CertURL is a new certificate issuance endpoint URL.
CertURL
string
// RevokeURL is used to initiate a certificate revocation flow.
RevokeURL
string
// Term is a URI identifying the current terms of service.
Terms
string
// Website is an HTTP or HTTPS URL locating a website
// providing more information about the ACME server.
Website
string
// CAA consists of lowercase hostname elements, which the ACME server
// recognises as referring to itself for the purposes of CAA record validation
// as defined in RFC6844.
CAA
[]
string
}
// Challenge encodes a returned CA challenge.
// Its Error field may be non-nil if the challenge is part of an Authorization
// with StatusInvalid.
type
Challenge
struct
{
// Type is the challenge type, e.g. "http-01", "tls-sni-02", "dns-01".
Type
string
// URI is where a challenge response can be posted to.
URI
string
// Token is a random value that uniquely identifies the challenge.
Token
string
// Status identifies the status of this challenge.
Status
string
// Error indicates the reason for an authorization failure
// when this challenge was used.
// The type of a non-nil value is *Error.
Error
error
}
// Authorization encodes an authorization response.
type
Authorization
struct
{
// URI uniquely identifies a authorization.
URI
string
// Status identifies the status of an authorization.
Status
string
// Identifier is what the account is authorized to represent.
Identifier
AuthzID
// Challenges that the client needs to fulfill in order to prove possession
// of the identifier (for pending authorizations).
// For final authorizations, the challenges that were used.
Challenges
[]
*
Challenge
// A collection of sets of challenges, each of which would be sufficient
// to prove possession of the identifier.
// Clients must complete a set of challenges that covers at least one set.
// Challenges are identified by their indices in the challenges array.
// If this field is empty, the client needs to complete all challenges.
Combinations
[][]
int
}
// AuthzID is an identifier that an account is authorized to represent.
type
AuthzID
struct
{
Type
string
// The type of identifier, e.g. "dns".
Value
string
// The identifier itself, e.g. "example.org".
}
// wireAuthz is ACME JSON representation of Authorization objects.
type
wireAuthz
struct
{
Status
string
Challenges
[]
wireChallenge
Combinations
[][]
int
Identifier
struct
{
Type
string
Value
string
}
}
func
(
z
*
wireAuthz
)
authorization
(
uri
string
)
*
Authorization
{
a
:=
&
Authorization
{
URI
:
uri
,
Status
:
z
.
Status
,
Identifier
:
AuthzID
{
Type
:
z
.
Identifier
.
Type
,
Value
:
z
.
Identifier
.
Value
},
Combinations
:
z
.
Combinations
,
// shallow copy
Challenges
:
make
([]
*
Challenge
,
len
(
z
.
Challenges
)),
}
for
i
,
v
:=
range
z
.
Challenges
{
a
.
Challenges
[
i
]
=
v
.
challenge
()
}
return
a
}
func
(
z
*
wireAuthz
)
error
(
uri
string
)
*
AuthorizationError
{
err
:=
&
AuthorizationError
{
URI
:
uri
,
Identifier
:
z
.
Identifier
.
Value
,
}
for
_
,
raw
:=
range
z
.
Challenges
{
if
raw
.
Error
!=
nil
{
err
.
Errors
=
append
(
err
.
Errors
,
raw
.
Error
.
error
(
nil
))
}
}
return
err
}
// wireChallenge is ACME JSON challenge representation.
type
wireChallenge
struct
{
URI
string
`json:"uri"`
Type
string
Token
string
Status
string
Error
*
wireError
}
func
(
c
*
wireChallenge
)
challenge
()
*
Challenge
{
v
:=
&
Challenge
{
URI
:
c
.
URI
,
Type
:
c
.
Type
,
Token
:
c
.
Token
,
Status
:
c
.
Status
,
}
if
v
.
Status
==
""
{
v
.
Status
=
StatusPending
}
if
c
.
Error
!=
nil
{
v
.
Error
=
c
.
Error
.
error
(
nil
)
}
return
v
}
// wireError is a subset of fields of the Problem Details object
// as described in https://tools.ietf.org/html/rfc7807#section-3.1.
type
wireError
struct
{
Status
int
Type
string
Detail
string
}
func
(
e
*
wireError
)
error
(
h
http
.
Header
)
*
Error
{
return
&
Error
{
StatusCode
:
e
.
Status
,
ProblemType
:
e
.
Type
,
Detail
:
e
.
Detail
,
Header
:
h
,
}
}
// CertOption is an optional argument type for the TLS ChallengeCert methods for
// customizing a temporary certificate for TLS-based challenges.
type
CertOption
interface
{
privateCertOpt
()
}
// WithKey creates an option holding a private/public key pair.
// The private part signs a certificate, and the public part represents the signee.
func
WithKey
(
key
crypto
.
Signer
)
CertOption
{
return
&
certOptKey
{
key
}
}
type
certOptKey
struct
{
key
crypto
.
Signer
}
func
(
*
certOptKey
)
privateCertOpt
()
{}
// WithTemplate creates an option for specifying a certificate template.
// See x509.CreateCertificate for template usage details.
//
// In TLS ChallengeCert methods, the template is also used as parent,
// resulting in a self-signed certificate.
// The DNSNames field of t is always overwritten for tls-sni challenge certs.
func
WithTemplate
(
t
*
x509
.
Certificate
)
CertOption
{
return
(
*
certOptTemplate
)(
t
)
}
type
certOptTemplate
x509
.
Certificate
func
(
*
certOptTemplate
)
privateCertOpt
()
{}
vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go
0 → 100644
View file @
efe0f673
// 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 pbkdf2 implements the key derivation function PBKDF2 as defined in RFC
2898 / PKCS #5 v2.0.
A key derivation function is useful when encrypting data based on a password
or any other not-fully-random data. It uses a pseudorandom function to derive
a secure encryption key based on the password.
While v2.0 of the standard defines only one pseudorandom function to use,
HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved
Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To
choose, you can pass the `New` functions from the different SHA packages to
pbkdf2.Key.
*/
package
pbkdf2
// import "golang.org/x/crypto/pbkdf2"
import
(
"crypto/hmac"
"hash"
)
// Key derives a key from the password, salt and iteration count, returning a
// []byte of length keylen that can be used as cryptographic key. The key is
// derived based on the method described as PBKDF2 with the HMAC variant using
// the supplied hash function.
//
// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you
// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
// doing:
//
// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
//
// Remember to get a good random salt. At least 8 bytes is recommended by the
// RFC.
//
// Using a higher iteration count will increase the cost of an exhaustive
// search but will also make derivation proportionally slower.
func
Key
(
password
,
salt
[]
byte
,
iter
,
keyLen
int
,
h
func
()
hash
.
Hash
)
[]
byte
{
prf
:=
hmac
.
New
(
h
,
password
)
hashLen
:=
prf
.
Size
()
numBlocks
:=
(
keyLen
+
hashLen
-
1
)
/
hashLen
var
buf
[
4
]
byte
dk
:=
make
([]
byte
,
0
,
numBlocks
*
hashLen
)
U
:=
make
([]
byte
,
hashLen
)
for
block
:=
1
;
block
<=
numBlocks
;
block
++
{
// N.B.: || means concatenation, ^ means XOR
// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
// U_1 = PRF(password, salt || uint(i))
prf
.
Reset
()
prf
.
Write
(
salt
)
buf
[
0
]
=
byte
(
block
>>
24
)
buf
[
1
]
=
byte
(
block
>>
16
)
buf
[
2
]
=
byte
(
block
>>
8
)
buf
[
3
]
=
byte
(
block
)
prf
.
Write
(
buf
[
:
4
])
dk
=
prf
.
Sum
(
dk
)
T
:=
dk
[
len
(
dk
)
-
hashLen
:
]
copy
(
U
,
T
)
// U_n = PRF(password, U_(n-1))
for
n
:=
2
;
n
<=
iter
;
n
++
{
prf
.
Reset
()
prf
.
Write
(
U
)
U
=
U
[
:
0
]
U
=
prf
.
Sum
(
U
)
for
x
:=
range
U
{
T
[
x
]
^=
U
[
x
]
}
}
}
return
dk
[
:
keyLen
]
}
vendor/golang.org/x/net/LICENSE
0 → 100644
View file @
efe0f673
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. 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.
vendor/golang.org/x/net/PATENTS
0 → 100644
View file @
efe0f673
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.
vendor/golang.org/x/net/context/context.go
0 → 100644
View file @
efe0f673
// Copyright 2014 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 context defines the Context type, which carries deadlines,
// cancelation signals, and other request-scoped values across API boundaries
// and between processes.
//
// Incoming requests to a server should create a Context, and outgoing calls to
// servers should accept a Context. The chain of function calls between must
// propagate the Context, optionally replacing it with a modified copy created
// using WithDeadline, WithTimeout, WithCancel, or WithValue.
//
// Programs that use Contexts should follow these rules to keep interfaces
// consistent across packages and enable static analysis tools to check context
// propagation:
//
// Do not store Contexts inside a struct type; instead, pass a Context
// explicitly to each function that needs it. The Context should be the first
// parameter, typically named ctx:
//
// func DoSomething(ctx context.Context, arg Arg) error {
// // ... use ctx ...
// }
//
// Do not pass a nil Context, even if a function permits it. Pass context.TODO
// if you are unsure about which Context to use.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
//
// The same Context may be passed to functions running in different goroutines;
// Contexts are safe for simultaneous use by multiple goroutines.
//
// See http://blog.golang.org/context for example code for a server that uses
// Contexts.
package
context
// import "golang.org/x/net/context"
// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func
Background
()
Context
{
return
background
}
// TODO returns a non-nil, empty Context. Code should use context.TODO when
// it's unclear which Context to use or it is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter). TODO is recognized by static analysis tools that determine
// whether Contexts are propagated correctly in a program.
func
TODO
()
Context
{
return
todo
}
vendor/golang.org/x/net/context/go17.go
0 → 100644
View file @
efe0f673
// Copyright 2016 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.
// +build go1.7
package
context
import
(
"context"
// standard library's context, as of Go 1.7
"time"
)
var
(
todo
=
context
.
TODO
()
background
=
context
.
Background
()
)
// Canceled is the error returned by Context.Err when the context is canceled.
var
Canceled
=
context
.
Canceled
// DeadlineExceeded is the error returned by Context.Err when the context's
// deadline passes.
var
DeadlineExceeded
=
context
.
DeadlineExceeded
// WithCancel returns a copy of parent with a new Done channel. The returned
// context's Done channel is closed when the returned cancel function is called
// or when the parent context's Done channel is closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func
WithCancel
(
parent
Context
)
(
ctx
Context
,
cancel
CancelFunc
)
{
ctx
,
f
:=
context
.
WithCancel
(
parent
)
return
ctx
,
CancelFunc
(
f
)
}
// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d. If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
// context's Done channel is closed when the deadline expires, when the returned
// cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func
WithDeadline
(
parent
Context
,
deadline
time
.
Time
)
(
Context
,
CancelFunc
)
{
ctx
,
f
:=
context
.
WithDeadline
(
parent
,
deadline
)
return
ctx
,
CancelFunc
(
f
)
}
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete:
//
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
// defer cancel() // releases resources if slowOperation completes before timeout elapses
// return slowOperation(ctx)
// }
func
WithTimeout
(
parent
Context
,
timeout
time
.
Duration
)
(
Context
,
CancelFunc
)
{
return
WithDeadline
(
parent
,
time
.
Now
()
.
Add
(
timeout
))
}
// WithValue returns a copy of parent in which the value associated with key is
// val.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
func
WithValue
(
parent
Context
,
key
interface
{},
val
interface
{})
Context
{
return
context
.
WithValue
(
parent
,
key
,
val
)
}
vendor/golang.org/x/net/context/go19.go
0 → 100644
View file @
efe0f673
// Copyright 2017 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.
// +build go1.9
package
context
import
"context"
// standard library's context, as of Go 1.7
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type
Context
=
context
.
Context
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type
CancelFunc
=
context
.
CancelFunc
vendor/golang.org/x/net/context/pre_go17.go
0 → 100644
View file @
efe0f673
// Copyright 2014 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.
// +build !go1.7
package
context
import
(
"errors"
"fmt"
"sync"
"time"
)
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
type
emptyCtx
int
func
(
*
emptyCtx
)
Deadline
()
(
deadline
time
.
Time
,
ok
bool
)
{
return
}
func
(
*
emptyCtx
)
Done
()
<-
chan
struct
{}
{
return
nil
}
func
(
*
emptyCtx
)
Err
()
error
{
return
nil
}
func
(
*
emptyCtx
)
Value
(
key
interface
{})
interface
{}
{
return
nil
}
func
(
e
*
emptyCtx
)
String
()
string
{
switch
e
{
case
background
:
return
"context.Background"
case
todo
:
return
"context.TODO"
}
return
"unknown empty Context"
}
var
(
background
=
new
(
emptyCtx
)
todo
=
new
(
emptyCtx
)
)
// Canceled is the error returned by Context.Err when the context is canceled.
var
Canceled
=
errors
.
New
(
"context canceled"
)
// DeadlineExceeded is the error returned by Context.Err when the context's
// deadline passes.
var
DeadlineExceeded
=
errors
.
New
(
"context deadline exceeded"
)
// WithCancel returns a copy of parent with a new Done channel. The returned
// context's Done channel is closed when the returned cancel function is called
// or when the parent context's Done channel is closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func
WithCancel
(
parent
Context
)
(
ctx
Context
,
cancel
CancelFunc
)
{
c
:=
newCancelCtx
(
parent
)
propagateCancel
(
parent
,
c
)
return
c
,
func
()
{
c
.
cancel
(
true
,
Canceled
)
}
}
// newCancelCtx returns an initialized cancelCtx.
func
newCancelCtx
(
parent
Context
)
*
cancelCtx
{
return
&
cancelCtx
{
Context
:
parent
,
done
:
make
(
chan
struct
{}),
}
}
// propagateCancel arranges for child to be canceled when parent is.
func
propagateCancel
(
parent
Context
,
child
canceler
)
{
if
parent
.
Done
()
==
nil
{
return
// parent is never canceled
}
if
p
,
ok
:=
parentCancelCtx
(
parent
);
ok
{
p
.
mu
.
Lock
()
if
p
.
err
!=
nil
{
// parent has already been canceled
child
.
cancel
(
false
,
p
.
err
)
}
else
{
if
p
.
children
==
nil
{
p
.
children
=
make
(
map
[
canceler
]
bool
)
}
p
.
children
[
child
]
=
true
}
p
.
mu
.
Unlock
()
}
else
{
go
func
()
{
select
{
case
<-
parent
.
Done
()
:
child
.
cancel
(
false
,
parent
.
Err
())
case
<-
child
.
Done
()
:
}
}()
}
}
// parentCancelCtx follows a chain of parent references until it finds a
// *cancelCtx. This function understands how each of the concrete types in this
// package represents its parent.
func
parentCancelCtx
(
parent
Context
)
(
*
cancelCtx
,
bool
)
{
for
{
switch
c
:=
parent
.
(
type
)
{
case
*
cancelCtx
:
return
c
,
true
case
*
timerCtx
:
return
c
.
cancelCtx
,
true
case
*
valueCtx
:
parent
=
c
.
Context
default
:
return
nil
,
false
}
}
}
// removeChild removes a context from its parent.
func
removeChild
(
parent
Context
,
child
canceler
)
{
p
,
ok
:=
parentCancelCtx
(
parent
)
if
!
ok
{
return
}
p
.
mu
.
Lock
()
if
p
.
children
!=
nil
{
delete
(
p
.
children
,
child
)
}
p
.
mu
.
Unlock
()
}
// A canceler is a context type that can be canceled directly. The
// implementations are *cancelCtx and *timerCtx.
type
canceler
interface
{
cancel
(
removeFromParent
bool
,
err
error
)
Done
()
<-
chan
struct
{}
}
// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type
cancelCtx
struct
{
Context
done
chan
struct
{}
// closed by the first cancel call.
mu
sync
.
Mutex
children
map
[
canceler
]
bool
// set to nil by the first cancel call
err
error
// set to non-nil by the first cancel call
}
func
(
c
*
cancelCtx
)
Done
()
<-
chan
struct
{}
{
return
c
.
done
}
func
(
c
*
cancelCtx
)
Err
()
error
{
c
.
mu
.
Lock
()
defer
c
.
mu
.
Unlock
()
return
c
.
err
}
func
(
c
*
cancelCtx
)
String
()
string
{
return
fmt
.
Sprintf
(
"%v.WithCancel"
,
c
.
Context
)
}
// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
func
(
c
*
cancelCtx
)
cancel
(
removeFromParent
bool
,
err
error
)
{
if
err
==
nil
{
panic
(
"context: internal error: missing cancel error"
)
}
c
.
mu
.
Lock
()
if
c
.
err
!=
nil
{
c
.
mu
.
Unlock
()
return
// already canceled
}
c
.
err
=
err
close
(
c
.
done
)
for
child
:=
range
c
.
children
{
// NOTE: acquiring the child's lock while holding parent's lock.
child
.
cancel
(
false
,
err
)
}
c
.
children
=
nil
c
.
mu
.
Unlock
()
if
removeFromParent
{
removeChild
(
c
.
Context
,
c
)
}
}
// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d. If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
// context's Done channel is closed when the deadline expires, when the returned
// cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func
WithDeadline
(
parent
Context
,
deadline
time
.
Time
)
(
Context
,
CancelFunc
)
{
if
cur
,
ok
:=
parent
.
Deadline
();
ok
&&
cur
.
Before
(
deadline
)
{
// The current deadline is already sooner than the new one.
return
WithCancel
(
parent
)
}
c
:=
&
timerCtx
{
cancelCtx
:
newCancelCtx
(
parent
),
deadline
:
deadline
,
}
propagateCancel
(
parent
,
c
)
d
:=
deadline
.
Sub
(
time
.
Now
())
if
d
<=
0
{
c
.
cancel
(
true
,
DeadlineExceeded
)
// deadline has already passed
return
c
,
func
()
{
c
.
cancel
(
true
,
Canceled
)
}
}
c
.
mu
.
Lock
()
defer
c
.
mu
.
Unlock
()
if
c
.
err
==
nil
{
c
.
timer
=
time
.
AfterFunc
(
d
,
func
()
{
c
.
cancel
(
true
,
DeadlineExceeded
)
})
}
return
c
,
func
()
{
c
.
cancel
(
true
,
Canceled
)
}
}
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
// implement Done and Err. It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type
timerCtx
struct
{
*
cancelCtx
timer
*
time
.
Timer
// Under cancelCtx.mu.
deadline
time
.
Time
}
func
(
c
*
timerCtx
)
Deadline
()
(
deadline
time
.
Time
,
ok
bool
)
{
return
c
.
deadline
,
true
}
func
(
c
*
timerCtx
)
String
()
string
{
return
fmt
.
Sprintf
(
"%v.WithDeadline(%s [%s])"
,
c
.
cancelCtx
.
Context
,
c
.
deadline
,
c
.
deadline
.
Sub
(
time
.
Now
()))
}
func
(
c
*
timerCtx
)
cancel
(
removeFromParent
bool
,
err
error
)
{
c
.
cancelCtx
.
cancel
(
false
,
err
)
if
removeFromParent
{
// Remove this timerCtx from its parent cancelCtx's children.
removeChild
(
c
.
cancelCtx
.
Context
,
c
)
}
c
.
mu
.
Lock
()
if
c
.
timer
!=
nil
{
c
.
timer
.
Stop
()
c
.
timer
=
nil
}
c
.
mu
.
Unlock
()
}
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete:
//
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
// defer cancel() // releases resources if slowOperation completes before timeout elapses
// return slowOperation(ctx)
// }
func
WithTimeout
(
parent
Context
,
timeout
time
.
Duration
)
(
Context
,
CancelFunc
)
{
return
WithDeadline
(
parent
,
time
.
Now
()
.
Add
(
timeout
))
}
// WithValue returns a copy of parent in which the value associated with key is
// val.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
func
WithValue
(
parent
Context
,
key
interface
{},
val
interface
{})
Context
{
return
&
valueCtx
{
parent
,
key
,
val
}
}
// A valueCtx carries a key-value pair. It implements Value for that key and
// delegates all other calls to the embedded Context.
type
valueCtx
struct
{
Context
key
,
val
interface
{}
}
func
(
c
*
valueCtx
)
String
()
string
{
return
fmt
.
Sprintf
(
"%v.WithValue(%#v, %#v)"
,
c
.
Context
,
c
.
key
,
c
.
val
)
}
func
(
c
*
valueCtx
)
Value
(
key
interface
{})
interface
{}
{
if
c
.
key
==
key
{
return
c
.
val
}
return
c
.
Context
.
Value
(
key
)
}
vendor/golang.org/x/net/context/pre_go19.go
0 → 100644
View file @
efe0f673
// Copyright 2014 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.
// +build !go1.9
package
context
import
"time"
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type
Context
interface
{
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline
()
(
deadline
time
.
Time
,
ok
bool
)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See http://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancelation.
Done
()
<-
chan
struct
{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err
()
error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stores using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "golang.org/x/net/context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value
(
key
interface
{})
interface
{}
}
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// After the first call, subsequent calls to a CancelFunc do nothing.
type
CancelFunc
func
()
vendor/google.golang.org/appengine/LICENSE
0 → 100644
View file @
efe0f673
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
vendor/google.golang.org/appengine/cloudsql/cloudsql.go
0 → 100644
View file @
efe0f673
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
/*
Package cloudsql exposes access to Google Cloud SQL databases.
This package does not work in App Engine "flexible environment".
This package is intended for MySQL drivers to make App Engine-specific
connections. Applications should use this package through database/sql:
Select a pure Go MySQL driver that supports this package, and use sql.Open
with protocol "cloudsql" and an address of the Cloud SQL instance.
A Go MySQL driver that has been tested to work well with Cloud SQL
is the go-sql-driver:
import "database/sql"
import _ "github.com/go-sql-driver/mysql"
db, err := sql.Open("mysql", "user@cloudsql(project-id:instance-name)/dbname")
Another driver that works well with Cloud SQL is the mymysql driver:
import "database/sql"
import _ "github.com/ziutek/mymysql/godrv"
db, err := sql.Open("mymysql", "cloudsql:instance-name*dbname/user/password")
Using either of these drivers, you can perform a standard SQL query.
This example assumes there is a table named 'users' with
columns 'first_name' and 'last_name':
rows, err := db.Query("SELECT first_name, last_name FROM users")
if err != nil {
log.Errorf(ctx, "db.Query: %v", err)
}
defer rows.Close()
for rows.Next() {
var firstName string
var lastName string
if err := rows.Scan(&firstName, &lastName); err != nil {
log.Errorf(ctx, "rows.Scan: %v", err)
continue
}
log.Infof(ctx, "First: %v - Last: %v", firstName, lastName)
}
if err := rows.Err(); err != nil {
log.Errorf(ctx, "Row error: %v", err)
}
*/
package
cloudsql
import
(
"net"
)
// Dial connects to the named Cloud SQL instance.
func
Dial
(
instance
string
)
(
net
.
Conn
,
error
)
{
return
connect
(
instance
)
}
vendor/google.golang.org/appengine/cloudsql/cloudsql_classic.go
0 → 100644
View file @
efe0f673
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// +build appengine
package
cloudsql
import
(
"net"
"appengine/cloudsql"
)
func
connect
(
instance
string
)
(
net
.
Conn
,
error
)
{
return
cloudsql
.
Dial
(
instance
)
}
vendor/google.golang.org/appengine/cloudsql/cloudsql_vm.go
0 → 100644
View file @
efe0f673
// Copyright 2013 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.
// +build !appengine
package
cloudsql
import
(
"errors"
"net"
)
func
connect
(
instance
string
)
(
net
.
Conn
,
error
)
{
return
nil
,
errors
.
New
(
`cloudsql: not supported in App Engine "flexible environment"`
)
}
vendor/vendor.json
0 → 100644
View file @
efe0f673
{
"comment"
:
""
,
"ignore"
:
"test"
,
"package"
:
[
{
"checksumSHA1"
:
"J6lNRPdrYhKft6S8x33K9brxyhE="
,
"path"
:
"golang.org/x/crypto/acme"
,
"revision"
:
"c126467f60eb25f8f27e5a981f32a87e3965053f"
,
"revisionTime"
:
"2018-07-23T15:26:11Z"
},
{
"checksumSHA1"
:
"EFjIi/zCZ1Cte0MQtyxGCTgSzk8="
,
"path"
:
"golang.org/x/crypto/acme/autocert"
,
"revision"
:
"c126467f60eb25f8f27e5a981f32a87e3965053f"
,
"revisionTime"
:
"2018-07-23T15:26:11Z"
},
{
"checksumSHA1"
:
"1MGpGDQqnUoRpv7VEcQrXOBydXE="
,
"path"
:
"golang.org/x/crypto/pbkdf2"
,
"revision"
:
"c126467f60eb25f8f27e5a981f32a87e3965053f"
,
"revisionTime"
:
"2018-07-23T15:26:11Z"
},
{
"checksumSHA1"
:
"dr5+PfIRzXeN+l1VG+s0lea9qz8="
,
"path"
:
"golang.org/x/net/context"
,
"revision"
:
"b60f3a92103dfd93dfcb900ec77c6d0643510868"
,
"revisionTime"
:
"2017-09-18T06:10:02Z"
},
{
"checksumSHA1"
:
"LiyXfqOzaeQ8vgYZH3t2hUEdVTw="
,
"path"
:
"google.golang.org/appengine/cloudsql"
,
"revision"
:
"b1f26356af11148e710935ed1ac8a7f5702c7612"
,
"revisionTime"
:
"2018-05-21T22:34:13Z"
}
],
"rootPath"
:
"github.com/astaxie/beego"
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment