Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
caddy
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
caddy
Commits
f9bc7462
Commit
f9bc7462
authored
Oct 09, 2015
by
Zac Bergquist
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Address various lint and gocyclo warnings. Fixes #253
parent
17c91152
Changes
29
Hide whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
293 additions
and
258 deletions
+293
-258
app/app.go
app/app.go
+2
-2
config/config.go
config/config.go
+3
-0
config/directives.go
config/directives.go
+2
-2
config/parse/dispenser.go
config/parse/dispenser.go
+4
-3
config/parse/parsing.go
config/parse/parsing.go
+2
-2
config/parse/parsing_test.go
config/parse/parsing_test.go
+3
-3
config/setup/markdown.go
config/setup/markdown.go
+69
-56
config/setup/proxy.go
config/setup/proxy.go
+5
-5
config/setup/startupshutdown.go
config/setup/startupshutdown.go
+1
-2
main.go
main.go
+2
-2
middleware/basicauth/basicauth.go
middleware/basicauth/basicauth.go
+3
-0
middleware/browse/browse_test.go
middleware/browse/browse_test.go
+6
-7
middleware/errors/errors.go
middleware/errors/errors.go
+1
-2
middleware/fastcgi/fastcgi.go
middleware/fastcgi/fastcgi.go
+22
-18
middleware/fastcgi/fcgiclient.go
middleware/fastcgi/fcgiclient.go
+40
-38
middleware/fastcgi/fcgiclient_test.go
middleware/fastcgi/fcgiclient_test.go
+28
-28
middleware/gzip/filter.go
middleware/gzip/filter.go
+1
-1
middleware/gzip/gzip_test.go
middleware/gzip/gzip_test.go
+1
-1
middleware/markdown/markdown_test.go
middleware/markdown/markdown_test.go
+4
-4
middleware/markdown/process.go
middleware/markdown/process.go
+2
-2
middleware/markdown/watcher.go
middleware/markdown/watcher.go
+2
-0
middleware/proxy/policy_test.go
middleware/proxy/policy_test.go
+3
-3
middleware/proxy/upstream.go
middleware/proxy/upstream.go
+66
-60
middleware/replacer_test.go
middleware/replacer_test.go
+4
-4
middleware/rewrite/rewrite.go
middleware/rewrite/rewrite.go
+1
-1
middleware/rewrite/rewrite_test.go
middleware/rewrite/rewrite_test.go
+9
-9
middleware/roller.go
middleware/roller.go
+2
-0
middleware/templates/templates_test.go
middleware/templates/templates_test.go
+3
-3
server/server.go
server/server.go
+2
-0
No files found.
app/app.go
View file @
f9bc7462
...
@@ -33,8 +33,8 @@ var (
...
@@ -33,8 +33,8 @@ var (
// Wg is used to wait for all servers to shut down
// Wg is used to wait for all servers to shut down
Wg
sync
.
WaitGroup
Wg
sync
.
WaitGroup
// H
ttp
2 indicates whether HTTP2 is enabled or not
// H
TTP
2 indicates whether HTTP2 is enabled or not
H
ttp
2
bool
// TODO: temporary flag until http2 is standard
H
TTP
2
bool
// TODO: temporary flag until http2 is standard
// Quiet mode hides non-error initialization output
// Quiet mode hides non-error initialization output
Quiet
bool
Quiet
bool
...
...
config/config.go
View file @
f9bc7462
...
@@ -234,6 +234,8 @@ func validDirective(d string) bool {
...
@@ -234,6 +234,8 @@ func validDirective(d string) bool {
return
false
return
false
}
}
// NewDefault creates a default configuration using the default
// root, host, and port.
func
NewDefault
()
server
.
Config
{
func
NewDefault
()
server
.
Config
{
return
server
.
Config
{
return
server
.
Config
{
Root
:
Root
,
Root
:
Root
,
...
@@ -256,4 +258,5 @@ var (
...
@@ -256,4 +258,5 @@ var (
Port
=
DefaultPort
Port
=
DefaultPort
)
)
// Group maps network addresses to their configurations.
type
Group
map
[
*
net
.
TCPAddr
][]
server
.
Config
type
Group
map
[
*
net
.
TCPAddr
][]
server
.
Config
config/directives.go
View file @
f9bc7462
...
@@ -74,7 +74,7 @@ type directive struct {
...
@@ -74,7 +74,7 @@ type directive struct {
setup
SetupFunc
setup
SetupFunc
}
}
//
A setup function takes a setup controller. Its return values may
//
SetupFunc takes a controller and may optionally return a middleware.
//
both be nil. If
middleware is not nil, it will be chained into
//
If the resulting
middleware is not nil, it will be chained into
// the HTTP handlers in the order specified in this package.
// the HTTP handlers in the order specified in this package.
type
SetupFunc
func
(
c
*
setup
.
Controller
)
(
middleware
.
Middleware
,
error
)
type
SetupFunc
func
(
c
*
setup
.
Controller
)
(
middleware
.
Middleware
,
error
)
config/parse/dispenser.go
View file @
f9bc7462
...
@@ -119,6 +119,7 @@ func (d *Dispenser) NextBlock() bool {
...
@@ -119,6 +119,7 @@ func (d *Dispenser) NextBlock() bool {
return
true
return
true
}
}
// IncrNest adds a level of nesting to the dispenser.
func
(
d
*
Dispenser
)
IncrNest
()
{
func
(
d
*
Dispenser
)
IncrNest
()
{
d
.
nesting
++
d
.
nesting
++
return
return
...
@@ -208,9 +209,9 @@ func (d *Dispenser) SyntaxErr(expected string) error {
...
@@ -208,9 +209,9 @@ func (d *Dispenser) SyntaxErr(expected string) error {
return
errors
.
New
(
msg
)
return
errors
.
New
(
msg
)
}
}
// E
ofErr returns an EOF error, meaning that end of input
// E
OFErr returns an error indicating that the dispenser reached
//
was found when another token was expected
.
//
the end of the input when searching for the next token
.
func
(
d
*
Dispenser
)
E
of
Err
()
error
{
func
(
d
*
Dispenser
)
E
OF
Err
()
error
{
return
d
.
Errf
(
"Unexpected EOF"
)
return
d
.
Errf
(
"Unexpected EOF"
)
}
}
...
...
config/parse/parsing.go
View file @
f9bc7462
...
@@ -108,7 +108,7 @@ func (p *parser) addresses() error {
...
@@ -108,7 +108,7 @@ func (p *parser) addresses() error {
// Advance token and possibly break out of loop or return error
// Advance token and possibly break out of loop or return error
hasNext
:=
p
.
Next
()
hasNext
:=
p
.
Next
()
if
expectingAnother
&&
!
hasNext
{
if
expectingAnother
&&
!
hasNext
{
return
p
.
E
of
Err
()
return
p
.
E
OF
Err
()
}
}
if
!
hasNext
{
if
!
hasNext
{
p
.
eof
=
true
p
.
eof
=
true
...
@@ -242,7 +242,7 @@ func (p *parser) directive() error {
...
@@ -242,7 +242,7 @@ func (p *parser) directive() error {
}
}
if
nesting
>
0
{
if
nesting
>
0
{
return
p
.
E
of
Err
()
return
p
.
E
OF
Err
()
}
}
return
nil
return
nil
}
}
...
...
config/parse/parsing_test.go
View file @
f9bc7462
...
@@ -338,9 +338,9 @@ func TestParseAll(t *testing.T) {
...
@@ -338,9 +338,9 @@ func TestParseAll(t *testing.T) {
func
setupParseTests
()
{
func
setupParseTests
()
{
// Set up some bogus directives for testing
// Set up some bogus directives for testing
ValidDirectives
=
map
[
string
]
struct
{}{
ValidDirectives
=
map
[
string
]
struct
{}{
"dir1"
:
struct
{}
{},
"dir1"
:
{},
"dir2"
:
struct
{}
{},
"dir2"
:
{},
"dir3"
:
struct
{}
{},
"dir3"
:
{},
}
}
}
}
...
...
config/setup/markdown.go
View file @
f9bc7462
...
@@ -68,62 +68,8 @@ func markdownParse(c *Controller) ([]*markdown.Config, error) {
...
@@ -68,62 +68,8 @@ func markdownParse(c *Controller) ([]*markdown.Config, error) {
// Load any other configuration parameters
// Load any other configuration parameters
for
c
.
NextBlock
()
{
for
c
.
NextBlock
()
{
switch
c
.
Val
()
{
if
err
:=
loadParams
(
c
,
md
);
err
!=
nil
{
case
"ext"
:
return
mdconfigs
,
err
exts
:=
c
.
RemainingArgs
()
if
len
(
exts
)
==
0
{
return
mdconfigs
,
c
.
ArgErr
()
}
md
.
Extensions
=
append
(
md
.
Extensions
,
exts
...
)
case
"css"
:
if
!
c
.
NextArg
()
{
return
mdconfigs
,
c
.
ArgErr
()
}
md
.
Styles
=
append
(
md
.
Styles
,
c
.
Val
())
case
"js"
:
if
!
c
.
NextArg
()
{
return
mdconfigs
,
c
.
ArgErr
()
}
md
.
Scripts
=
append
(
md
.
Scripts
,
c
.
Val
())
case
"template"
:
tArgs
:=
c
.
RemainingArgs
()
switch
len
(
tArgs
)
{
case
0
:
return
mdconfigs
,
c
.
ArgErr
()
case
1
:
if
_
,
ok
:=
md
.
Templates
[
markdown
.
DefaultTemplate
];
ok
{
return
mdconfigs
,
c
.
Err
(
"only one default template is allowed, use alias."
)
}
fpath
:=
filepath
.
Clean
(
c
.
Root
+
string
(
filepath
.
Separator
)
+
tArgs
[
0
])
md
.
Templates
[
markdown
.
DefaultTemplate
]
=
fpath
case
2
:
fpath
:=
filepath
.
Clean
(
c
.
Root
+
string
(
filepath
.
Separator
)
+
tArgs
[
1
])
md
.
Templates
[
tArgs
[
0
]]
=
fpath
default
:
return
mdconfigs
,
c
.
ArgErr
()
}
case
"sitegen"
:
if
c
.
NextArg
()
{
md
.
StaticDir
=
path
.
Join
(
c
.
Root
,
c
.
Val
())
}
else
{
md
.
StaticDir
=
path
.
Join
(
c
.
Root
,
markdown
.
DefaultStaticDir
)
}
if
c
.
NextArg
()
{
// only 1 argument allowed
return
mdconfigs
,
c
.
ArgErr
()
}
case
"dev"
:
if
c
.
NextArg
()
{
md
.
Development
=
strings
.
ToLower
(
c
.
Val
())
==
"true"
}
else
{
md
.
Development
=
true
}
if
c
.
NextArg
()
{
// only 1 argument allowed
return
mdconfigs
,
c
.
ArgErr
()
}
default
:
return
mdconfigs
,
c
.
Err
(
"Expected valid markdown configuration property"
)
}
}
}
}
...
@@ -137,3 +83,70 @@ func markdownParse(c *Controller) ([]*markdown.Config, error) {
...
@@ -137,3 +83,70 @@ func markdownParse(c *Controller) ([]*markdown.Config, error) {
return
mdconfigs
,
nil
return
mdconfigs
,
nil
}
}
func
loadParams
(
c
*
Controller
,
mdc
*
markdown
.
Config
)
error
{
switch
c
.
Val
()
{
case
"ext"
:
exts
:=
c
.
RemainingArgs
()
if
len
(
exts
)
==
0
{
return
c
.
ArgErr
()
}
mdc
.
Extensions
=
append
(
mdc
.
Extensions
,
exts
...
)
return
nil
case
"css"
:
if
!
c
.
NextArg
()
{
return
c
.
ArgErr
()
}
mdc
.
Styles
=
append
(
mdc
.
Styles
,
c
.
Val
())
return
nil
case
"js"
:
if
!
c
.
NextArg
()
{
return
c
.
ArgErr
()
}
mdc
.
Scripts
=
append
(
mdc
.
Scripts
,
c
.
Val
())
return
nil
case
"template"
:
tArgs
:=
c
.
RemainingArgs
()
switch
len
(
tArgs
)
{
case
0
:
return
c
.
ArgErr
()
case
1
:
if
_
,
ok
:=
mdc
.
Templates
[
markdown
.
DefaultTemplate
];
ok
{
return
c
.
Err
(
"only one default template is allowed, use alias."
)
}
fpath
:=
filepath
.
Clean
(
c
.
Root
+
string
(
filepath
.
Separator
)
+
tArgs
[
0
])
mdc
.
Templates
[
markdown
.
DefaultTemplate
]
=
fpath
return
nil
case
2
:
fpath
:=
filepath
.
Clean
(
c
.
Root
+
string
(
filepath
.
Separator
)
+
tArgs
[
1
])
mdc
.
Templates
[
tArgs
[
0
]]
=
fpath
return
nil
default
:
return
c
.
ArgErr
()
}
case
"sitegen"
:
if
c
.
NextArg
()
{
mdc
.
StaticDir
=
path
.
Join
(
c
.
Root
,
c
.
Val
())
}
else
{
mdc
.
StaticDir
=
path
.
Join
(
c
.
Root
,
markdown
.
DefaultStaticDir
)
}
if
c
.
NextArg
()
{
// only 1 argument allowed
return
c
.
ArgErr
()
}
return
nil
case
"dev"
:
if
c
.
NextArg
()
{
mdc
.
Development
=
strings
.
ToLower
(
c
.
Val
())
==
"true"
}
else
{
mdc
.
Development
=
true
}
if
c
.
NextArg
()
{
// only 1 argument allowed
return
c
.
ArgErr
()
}
return
nil
default
:
return
c
.
Err
(
"Expected valid markdown configuration property"
)
}
}
config/setup/proxy.go
View file @
f9bc7462
...
@@ -7,11 +7,11 @@ import (
...
@@ -7,11 +7,11 @@ import (
// Proxy configures a new Proxy middleware instance.
// Proxy configures a new Proxy middleware instance.
func
Proxy
(
c
*
Controller
)
(
middleware
.
Middleware
,
error
)
{
func
Proxy
(
c
*
Controller
)
(
middleware
.
Middleware
,
error
)
{
if
upstreams
,
err
:=
proxy
.
NewStaticUpstreams
(
c
.
Dispenser
);
err
==
nil
{
upstreams
,
err
:=
proxy
.
NewStaticUpstreams
(
c
.
Dispenser
)
return
func
(
next
middleware
.
Handler
)
middleware
.
Handler
{
if
err
!=
nil
{
return
proxy
.
Proxy
{
Next
:
next
,
Upstreams
:
upstreams
}
},
nil
}
else
{
return
nil
,
err
return
nil
,
err
}
}
return
func
(
next
middleware
.
Handler
)
middleware
.
Handler
{
return
proxy
.
Proxy
{
Next
:
next
,
Upstreams
:
upstreams
}
},
nil
}
}
config/setup/startupshutdown.go
View file @
f9bc7462
...
@@ -46,9 +46,8 @@ func registerCallback(c *Controller, list *[]func() error) error {
...
@@ -46,9 +46,8 @@ func registerCallback(c *Controller, list *[]func() error) error {
if
nonblock
{
if
nonblock
{
return
cmd
.
Start
()
return
cmd
.
Start
()
}
else
{
return
cmd
.
Run
()
}
}
return
cmd
.
Run
()
}
}
*
list
=
append
(
*
list
,
fn
)
*
list
=
append
(
*
list
,
fn
)
...
...
main.go
View file @
f9bc7462
...
@@ -26,7 +26,7 @@ var (
...
@@ -26,7 +26,7 @@ var (
func
init
()
{
func
init
()
{
flag
.
StringVar
(
&
conf
,
"conf"
,
""
,
"Configuration file to use (default="
+
config
.
DefaultConfigFile
+
")"
)
flag
.
StringVar
(
&
conf
,
"conf"
,
""
,
"Configuration file to use (default="
+
config
.
DefaultConfigFile
+
")"
)
flag
.
BoolVar
(
&
app
.
H
ttp
2
,
"http2"
,
true
,
"Enable HTTP/2 support"
)
// TODO: temporary flag until http2 merged into std lib
flag
.
BoolVar
(
&
app
.
H
TTP
2
,
"http2"
,
true
,
"Enable HTTP/2 support"
)
// TODO: temporary flag until http2 merged into std lib
flag
.
BoolVar
(
&
app
.
Quiet
,
"quiet"
,
false
,
"Quiet mode (no initialization output)"
)
flag
.
BoolVar
(
&
app
.
Quiet
,
"quiet"
,
false
,
"Quiet mode (no initialization output)"
)
flag
.
StringVar
(
&
cpu
,
"cpu"
,
"100%"
,
"CPU cap"
)
flag
.
StringVar
(
&
cpu
,
"cpu"
,
"100%"
,
"CPU cap"
)
flag
.
StringVar
(
&
config
.
Root
,
"root"
,
config
.
DefaultRoot
,
"Root path to default site"
)
flag
.
StringVar
(
&
config
.
Root
,
"root"
,
config
.
DefaultRoot
,
"Root path to default site"
)
...
@@ -61,7 +61,7 @@ func main() {
...
@@ -61,7 +61,7 @@ func main() {
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Fatal
(
err
)
log
.
Fatal
(
err
)
}
}
s
.
HTTP2
=
app
.
H
ttp
2
// TODO: This setting is temporary
s
.
HTTP2
=
app
.
H
TTP
2
// TODO: This setting is temporary
app
.
Wg
.
Add
(
1
)
app
.
Wg
.
Add
(
1
)
go
func
(
s
*
server
.
Server
)
{
go
func
(
s
*
server
.
Server
)
{
defer
app
.
Wg
.
Done
()
defer
app
.
Wg
.
Done
()
...
...
middleware/basicauth/basicauth.go
View file @
f9bc7462
...
@@ -78,6 +78,7 @@ type Rule struct {
...
@@ -78,6 +78,7 @@ type Rule struct {
Resources
[]
string
Resources
[]
string
}
}
// PasswordMatcher determines whether a password mathes a rule.
type
PasswordMatcher
func
(
pw
string
)
bool
type
PasswordMatcher
func
(
pw
string
)
bool
var
(
var
(
...
@@ -137,6 +138,8 @@ func parseHtpasswd(pm map[string]PasswordMatcher, r io.Reader) error {
...
@@ -137,6 +138,8 @@ func parseHtpasswd(pm map[string]PasswordMatcher, r io.Reader) error {
return
scanner
.
Err
()
return
scanner
.
Err
()
}
}
// PlainMatcher returns a PasswordMatcher that does a constant-time
// byte-wise comparison.
func
PlainMatcher
(
passw
string
)
PasswordMatcher
{
func
PlainMatcher
(
passw
string
)
PasswordMatcher
{
return
func
(
pw
string
)
bool
{
return
func
(
pw
string
)
bool
{
return
subtle
.
ConstantTimeCompare
([]
byte
(
pw
),
[]
byte
(
passw
))
==
1
return
subtle
.
ConstantTimeCompare
([]
byte
(
pw
),
[]
byte
(
passw
))
==
1
...
...
middleware/browse/browse_test.go
View file @
f9bc7462
...
@@ -117,7 +117,7 @@ func TestBrowseTemplate(t *testing.T) {
...
@@ -117,7 +117,7 @@ func TestBrowseTemplate(t *testing.T) {
}),
}),
Root
:
"./testdata"
,
Root
:
"./testdata"
,
Configs
:
[]
Config
{
Configs
:
[]
Config
{
Config
{
{
PathScope
:
"/photos"
,
PathScope
:
"/photos"
,
Template
:
tmpl
,
Template
:
tmpl
,
},
},
...
@@ -172,7 +172,7 @@ func TestBrowseJson(t *testing.T) {
...
@@ -172,7 +172,7 @@ func TestBrowseJson(t *testing.T) {
}),
}),
Root
:
"./testdata"
,
Root
:
"./testdata"
,
Configs
:
[]
Config
{
Configs
:
[]
Config
{
Config
{
{
PathScope
:
"/photos/"
,
PathScope
:
"/photos/"
,
},
},
},
},
...
@@ -283,7 +283,7 @@ func TestBrowseJson(t *testing.T) {
...
@@ -283,7 +283,7 @@ func TestBrowseJson(t *testing.T) {
t
.
Fatalf
(
"Expected Content type to be application/json; charset=utf-8, but got %s "
,
rec
.
HeaderMap
.
Get
(
"Content-Type"
))
t
.
Fatalf
(
"Expected Content type to be application/json; charset=utf-8, but got %s "
,
rec
.
HeaderMap
.
Get
(
"Content-Type"
))
}
}
actualJ
sonResponseString
:=
rec
.
Body
.
String
()
actualJ
SONResponse
:=
rec
.
Body
.
String
()
copyOflisting
:=
listing
copyOflisting
:=
listing
if
test
.
SortBy
==
""
{
if
test
.
SortBy
==
""
{
copyOflisting
.
Sort
=
"name"
copyOflisting
.
Sort
=
"name"
...
@@ -308,12 +308,11 @@ func TestBrowseJson(t *testing.T) {
...
@@ -308,12 +308,11 @@ func TestBrowseJson(t *testing.T) {
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"Unable to Marshal the listing "
)
t
.
Fatalf
(
"Unable to Marshal the listing "
)
}
}
expectedJ
sonString
:=
string
(
marsh
)
expectedJ
SON
:=
string
(
marsh
)
if
actualJ
sonResponseString
!=
expectedJsonString
{
if
actualJ
SONResponse
!=
expectedJSON
{
t
.
Errorf
(
"JSON response doesn't match the expected for test number %d with sort=%s, order=%s
\n
Expected response %s
\n
Actual response = %s
\n
"
,
t
.
Errorf
(
"JSON response doesn't match the expected for test number %d with sort=%s, order=%s
\n
Expected response %s
\n
Actual response = %s
\n
"
,
i
+
1
,
test
.
SortBy
,
test
.
OrderBy
,
expectedJ
sonString
,
actualJsonResponseString
)
i
+
1
,
test
.
SortBy
,
test
.
OrderBy
,
expectedJ
SON
,
actualJSONResponse
)
}
}
}
}
}
}
middleware/errors/errors.go
View file @
f9bc7462
...
@@ -37,9 +37,8 @@ func (h ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, er
...
@@ -37,9 +37,8 @@ func (h ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, er
w
.
WriteHeader
(
status
)
w
.
WriteHeader
(
status
)
fmt
.
Fprintln
(
w
,
errMsg
)
fmt
.
Fprintln
(
w
,
errMsg
)
return
0
,
err
// returning < 400 signals that a response has been written
return
0
,
err
// returning < 400 signals that a response has been written
}
else
{
h
.
Log
.
Println
(
errMsg
)
}
}
h
.
Log
.
Println
(
errMsg
)
}
}
if
status
>=
400
{
if
status
>=
400
{
...
...
middleware/fastcgi/fastcgi.go
View file @
f9bc7462
...
@@ -58,17 +58,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
...
@@ -58,17 +58,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
}
}
// Connect to FastCGI gateway
// Connect to FastCGI gateway
var
fcgi
*
FCGIClient
fcgi
,
err
:=
getClient
(
&
rule
)
// check if unix socket or tcp
if
strings
.
HasPrefix
(
rule
.
Address
,
"/"
)
||
strings
.
HasPrefix
(
rule
.
Address
,
"unix:"
)
{
if
strings
.
HasPrefix
(
rule
.
Address
,
"unix:"
)
{
rule
.
Address
=
rule
.
Address
[
len
(
"unix:"
)
:
]
}
fcgi
,
err
=
Dial
(
"unix"
,
rule
.
Address
)
}
else
{
fcgi
,
err
=
Dial
(
"tcp"
,
rule
.
Address
)
}
if
err
!=
nil
{
if
err
!=
nil
{
return
http
.
StatusBadGateway
,
err
return
http
.
StatusBadGateway
,
err
}
}
...
@@ -102,13 +92,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
...
@@ -102,13 +92,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
return
http
.
StatusBadGateway
,
err
return
http
.
StatusBadGateway
,
err
}
}
// Write the response header
writeHeader
(
w
,
resp
)
for
key
,
vals
:=
range
resp
.
Header
{
for
_
,
val
:=
range
vals
{
w
.
Header
()
.
Add
(
key
,
val
)
}
}
w
.
WriteHeader
(
resp
.
StatusCode
)
// Write the response body
// Write the response body
// TODO: If this has an error, the response will already be
// TODO: If this has an error, the response will already be
...
@@ -126,6 +110,26 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
...
@@ -126,6 +110,26 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
return
h
.
Next
.
ServeHTTP
(
w
,
r
)
return
h
.
Next
.
ServeHTTP
(
w
,
r
)
}
}
func
getClient
(
r
*
Rule
)
(
*
FCGIClient
,
error
)
{
// check if unix socket or TCP
if
trim
:=
strings
.
HasPrefix
(
r
.
Address
,
"unix"
);
strings
.
HasPrefix
(
r
.
Address
,
"/"
)
||
trim
{
if
trim
{
r
.
Address
=
r
.
Address
[
len
(
"unix:"
)
:
]
}
return
Dial
(
"unix"
,
r
.
Address
)
}
return
Dial
(
"tcp"
,
r
.
Address
)
}
func
writeHeader
(
w
http
.
ResponseWriter
,
r
*
http
.
Response
)
{
for
key
,
vals
:=
range
r
.
Header
{
for
_
,
val
:=
range
vals
{
w
.
Header
()
.
Add
(
key
,
val
)
}
}
w
.
WriteHeader
(
r
.
StatusCode
)
}
func
(
h
Handler
)
exists
(
path
string
)
bool
{
func
(
h
Handler
)
exists
(
path
string
)
bool
{
if
_
,
err
:=
os
.
Stat
(
h
.
Root
+
path
);
err
==
nil
{
if
_
,
err
:=
os
.
Stat
(
h
.
Root
+
path
);
err
==
nil
{
return
true
return
true
...
...
middleware/fastcgi/fcgiclient.go
View file @
f9bc7462
...
@@ -30,45 +30,45 @@ import (
...
@@ -30,45 +30,45 @@ import (
"sync"
"sync"
)
)
const
FCGI
_LISTENSOCK_FILENO
uint8
=
0
const
FCGI
ListenSockFileno
uint8
=
0
const
FCGI
_HEADER_LEN
uint8
=
8
const
FCGI
HeaderLen
uint8
=
8
const
V
ERSION_
1
uint8
=
1
const
V
ersion
1
uint8
=
1
const
FCGI
_NULL_REQUEST_
ID
uint8
=
0
const
FCGI
NullRequest
ID
uint8
=
0
const
FCGI
_KEEP_CONN
uint8
=
1
const
FCGI
KeepConn
uint8
=
1
const
doubleCRLF
=
"
\r\n\r\n
"
const
doubleCRLF
=
"
\r\n\r\n
"
const
(
const
(
FCGI_BEGIN_REQUEST
uint8
=
iota
+
1
BeginRequest
uint8
=
iota
+
1
FCGI_ABORT_REQUEST
AbortRequest
FCGI_END_REQUEST
EndRequest
FCGI_PARAMS
Params
FCGI_STDIN
Stdin
FCGI_STDOUT
Stdout
FCGI_STDERR
Stderr
FCGI_DATA
Data
FCGI_GET_VALUES
GetValues
FCGI_GET_VALUES_RESULT
GetValuesResult
FCGI_UNKNOWN_TYPE
UnknownType
FCGI_MAXTYPE
=
FCGI_UNKNOWN_TYPE
MaxType
=
UnknownType
)
)
const
(
const
(
FCGI_RESPONDER
uint8
=
iota
+
1
Responder
uint8
=
iota
+
1
FCGI_AUTHORIZER
Authorizer
F
CGI_FILTER
F
ilter
)
)
const
(
const
(
FCGI_REQUEST_COMPLETE
uint8
=
iota
RequestComplete
uint8
=
iota
FCGI_CANT_MPX_CONN
CantMultiplexConns
FCGI_OVERLOADED
Overloaded
FCGI_UNKNOWN_ROLE
UnknownRole
)
)
const
(
const
(
FCGI_MAX_CONNS
string
=
"MAX_CONNS"
MaxConns
string
=
"MAX_CONNS"
FCGI_MAX_REQS
string
=
"MAX_REQS"
MaxRequests
string
=
"MAX_REQS"
FCGI_MPXS_CONNS
string
=
"MPXS_CONNS"
MultiplexConns
string
=
"MPXS_CONNS"
)
)
const
(
const
(
...
@@ -79,7 +79,7 @@ const (
...
@@ -79,7 +79,7 @@ const (
type
header
struct
{
type
header
struct
{
Version
uint8
Version
uint8
Type
uint8
Type
uint8
I
d
uint16
I
D
uint16
ContentLength
uint16
ContentLength
uint16
PaddingLength
uint8
PaddingLength
uint8
Reserved
uint8
Reserved
uint8
...
@@ -92,7 +92,7 @@ var pad [maxPad]byte
...
@@ -92,7 +92,7 @@ var pad [maxPad]byte
func
(
h
*
header
)
init
(
recType
uint8
,
reqID
uint16
,
contentLength
int
)
{
func
(
h
*
header
)
init
(
recType
uint8
,
reqID
uint16
,
contentLength
int
)
{
h
.
Version
=
1
h
.
Version
=
1
h
.
Type
=
recType
h
.
Type
=
recType
h
.
I
d
=
reqID
h
.
I
D
=
reqID
h
.
ContentLength
=
uint16
(
contentLength
)
h
.
ContentLength
=
uint16
(
contentLength
)
h
.
PaddingLength
=
uint8
(
-
contentLength
&
7
)
h
.
PaddingLength
=
uint8
(
-
contentLength
&
7
)
}
}
...
@@ -110,7 +110,7 @@ func (rec *record) read(r io.Reader) (buf []byte, err error) {
...
@@ -110,7 +110,7 @@ func (rec *record) read(r io.Reader) (buf []byte, err error) {
err
=
errors
.
New
(
"fcgi: invalid header version"
)
err
=
errors
.
New
(
"fcgi: invalid header version"
)
return
return
}
}
if
rec
.
h
.
Type
==
FCGI_END_REQUEST
{
if
rec
.
h
.
Type
==
EndRequest
{
err
=
io
.
EOF
err
=
io
.
EOF
return
return
}
}
...
@@ -126,13 +126,15 @@ func (rec *record) read(r io.Reader) (buf []byte, err error) {
...
@@ -126,13 +126,15 @@ func (rec *record) read(r io.Reader) (buf []byte, err error) {
return
return
}
}
// FCGIClient implements a FastCGI client, which is a standard for
// interfacing external applications with Web servers.
type
FCGIClient
struct
{
type
FCGIClient
struct
{
mutex
sync
.
Mutex
mutex
sync
.
Mutex
rwc
io
.
ReadWriteCloser
rwc
io
.
ReadWriteCloser
h
header
h
header
buf
bytes
.
Buffer
buf
bytes
.
Buffer
keepAlive
bool
keepAlive
bool
reqI
d
uint16
reqI
D
uint16
}
}
// Dial connects to the fcgi responder at the specified network address.
// Dial connects to the fcgi responder at the specified network address.
...
@@ -148,7 +150,7 @@ func Dial(network, address string) (fcgi *FCGIClient, err error) {
...
@@ -148,7 +150,7 @@ func Dial(network, address string) (fcgi *FCGIClient, err error) {
fcgi
=
&
FCGIClient
{
fcgi
=
&
FCGIClient
{
rwc
:
conn
,
rwc
:
conn
,
keepAlive
:
false
,
keepAlive
:
false
,
reqI
d
:
1
,
reqI
D
:
1
,
}
}
return
return
...
@@ -163,7 +165,7 @@ func (c *FCGIClient) writeRecord(recType uint8, content []byte) (err error) {
...
@@ -163,7 +165,7 @@ func (c *FCGIClient) writeRecord(recType uint8, content []byte) (err error) {
c
.
mutex
.
Lock
()
c
.
mutex
.
Lock
()
defer
c
.
mutex
.
Unlock
()
defer
c
.
mutex
.
Unlock
()
c
.
buf
.
Reset
()
c
.
buf
.
Reset
()
c
.
h
.
init
(
recType
,
c
.
reqI
d
,
len
(
content
))
c
.
h
.
init
(
recType
,
c
.
reqI
D
,
len
(
content
))
if
err
:=
binary
.
Write
(
&
c
.
buf
,
binary
.
BigEndian
,
c
.
h
);
err
!=
nil
{
if
err
:=
binary
.
Write
(
&
c
.
buf
,
binary
.
BigEndian
,
c
.
h
);
err
!=
nil
{
return
err
return
err
}
}
...
@@ -179,14 +181,14 @@ func (c *FCGIClient) writeRecord(recType uint8, content []byte) (err error) {
...
@@ -179,14 +181,14 @@ func (c *FCGIClient) writeRecord(recType uint8, content []byte) (err error) {
func
(
c
*
FCGIClient
)
writeBeginRequest
(
role
uint16
,
flags
uint8
)
error
{
func
(
c
*
FCGIClient
)
writeBeginRequest
(
role
uint16
,
flags
uint8
)
error
{
b
:=
[
8
]
byte
{
byte
(
role
>>
8
),
byte
(
role
),
flags
}
b
:=
[
8
]
byte
{
byte
(
role
>>
8
),
byte
(
role
),
flags
}
return
c
.
writeRecord
(
FCGI_BEGIN_REQUEST
,
b
[
:
])
return
c
.
writeRecord
(
BeginRequest
,
b
[
:
])
}
}
func
(
c
*
FCGIClient
)
writeEndRequest
(
appStatus
int
,
protocolStatus
uint8
)
error
{
func
(
c
*
FCGIClient
)
writeEndRequest
(
appStatus
int
,
protocolStatus
uint8
)
error
{
b
:=
make
([]
byte
,
8
)
b
:=
make
([]
byte
,
8
)
binary
.
BigEndian
.
PutUint32
(
b
,
uint32
(
appStatus
))
binary
.
BigEndian
.
PutUint32
(
b
,
uint32
(
appStatus
))
b
[
4
]
=
protocolStatus
b
[
4
]
=
protocolStatus
return
c
.
writeRecord
(
FCGI_END_REQUEST
,
b
)
return
c
.
writeRecord
(
EndRequest
,
b
)
}
}
func
(
c
*
FCGIClient
)
writePairs
(
recType
uint8
,
pairs
map
[
string
]
string
)
error
{
func
(
c
*
FCGIClient
)
writePairs
(
recType
uint8
,
pairs
map
[
string
]
string
)
error
{
...
@@ -334,17 +336,17 @@ func (w *streamReader) Read(p []byte) (n int, err error) {
...
@@ -334,17 +336,17 @@ func (w *streamReader) Read(p []byte) (n int, err error) {
// Do made the request and returns a io.Reader that translates the data read
// Do made the request and returns a io.Reader that translates the data read
// from fcgi responder out of fcgi packet before returning it.
// from fcgi responder out of fcgi packet before returning it.
func
(
c
*
FCGIClient
)
Do
(
p
map
[
string
]
string
,
req
io
.
Reader
)
(
r
io
.
Reader
,
err
error
)
{
func
(
c
*
FCGIClient
)
Do
(
p
map
[
string
]
string
,
req
io
.
Reader
)
(
r
io
.
Reader
,
err
error
)
{
err
=
c
.
writeBeginRequest
(
uint16
(
FCGI_RESPONDER
),
0
)
err
=
c
.
writeBeginRequest
(
uint16
(
Responder
),
0
)
if
err
!=
nil
{
if
err
!=
nil
{
return
return
}
}
err
=
c
.
writePairs
(
FCGI_PARAMS
,
p
)
err
=
c
.
writePairs
(
Params
,
p
)
if
err
!=
nil
{
if
err
!=
nil
{
return
return
}
}
body
:=
newWriter
(
c
,
FCGI_STDIN
)
body
:=
newWriter
(
c
,
Stdin
)
if
req
!=
nil
{
if
req
!=
nil
{
io
.
Copy
(
body
,
req
)
io
.
Copy
(
body
,
req
)
}
}
...
...
middleware/fastcgi/fcgiclient_test.go
View file @
f9bc7462
...
@@ -34,13 +34,13 @@ import (
...
@@ -34,13 +34,13 @@ import (
// test failed if the remote fcgi(script) failed md5 verification
// test failed if the remote fcgi(script) failed md5 verification
// and output "FAILED" in response
// and output "FAILED" in response
const
(
const
(
script
_f
ile
=
"/tank/www/fcgic_test.php"
script
F
ile
=
"/tank/www/fcgic_test.php"
//ip
_p
ort = "remote-php-serv:59000"
//ip
P
ort = "remote-php-serv:59000"
ip
_p
ort
=
"127.0.0.1:59000"
ip
P
ort
=
"127.0.0.1:59000"
)
)
var
(
var
(
t_
*
testing
.
T
=
nil
t_
*
testing
.
T
)
)
type
FastCGIServer
struct
{}
type
FastCGIServer
struct
{}
...
@@ -51,7 +51,7 @@ func (s FastCGIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
...
@@ -51,7 +51,7 @@ func (s FastCGIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
stat
:=
"PASSED"
stat
:=
"PASSED"
fmt
.
Fprintln
(
resp
,
"-"
)
fmt
.
Fprintln
(
resp
,
"-"
)
file
_n
um
:=
0
file
N
um
:=
0
{
{
length
:=
0
length
:=
0
for
k0
,
v0
:=
range
req
.
Form
{
for
k0
,
v0
:=
range
req
.
Form
{
...
@@ -69,7 +69,7 @@ func (s FastCGIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
...
@@ -69,7 +69,7 @@ func (s FastCGIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
}
}
}
}
if
req
.
MultipartForm
!=
nil
{
if
req
.
MultipartForm
!=
nil
{
file
_n
um
=
len
(
req
.
MultipartForm
.
File
)
file
N
um
=
len
(
req
.
MultipartForm
.
File
)
for
kn
,
fns
:=
range
req
.
MultipartForm
.
File
{
for
kn
,
fns
:=
range
req
.
MultipartForm
.
File
{
//fmt.Fprintln(resp, "server:filekey ", kn )
//fmt.Fprintln(resp, "server:filekey ", kn )
length
+=
len
(
kn
)
length
+=
len
(
kn
)
...
@@ -101,11 +101,11 @@ func (s FastCGIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
...
@@ -101,11 +101,11 @@ func (s FastCGIServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
fmt
.
Fprintln
(
resp
,
"server:got data length"
,
length
)
fmt
.
Fprintln
(
resp
,
"server:got data length"
,
length
)
}
}
fmt
.
Fprintln
(
resp
,
"-"
+
stat
+
"-POST("
,
len
(
req
.
Form
),
")-FILE("
,
file
_n
um
,
")--"
)
fmt
.
Fprintln
(
resp
,
"-"
+
stat
+
"-POST("
,
len
(
req
.
Form
),
")-FILE("
,
file
N
um
,
")--"
)
}
}
func
sendFcgi
(
reqType
int
,
fcgi
_p
arams
map
[
string
]
string
,
data
[]
byte
,
posts
map
[
string
]
string
,
files
map
[
string
]
string
)
(
content
[]
byte
)
{
func
sendFcgi
(
reqType
int
,
fcgi
P
arams
map
[
string
]
string
,
data
[]
byte
,
posts
map
[
string
]
string
,
files
map
[
string
]
string
)
(
content
[]
byte
)
{
fcgi
,
err
:=
Dial
(
"tcp"
,
ip
_p
ort
)
fcgi
,
err
:=
Dial
(
"tcp"
,
ip
P
ort
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Println
(
"err:"
,
err
)
log
.
Println
(
"err:"
,
err
)
return
return
...
@@ -119,16 +119,16 @@ func sendFcgi(reqType int, fcgi_params map[string]string, data []byte, posts map
...
@@ -119,16 +119,16 @@ func sendFcgi(reqType int, fcgi_params map[string]string, data []byte, posts map
if
len
(
data
)
>
0
{
if
len
(
data
)
>
0
{
length
=
len
(
data
)
length
=
len
(
data
)
rd
:=
bytes
.
NewReader
(
data
)
rd
:=
bytes
.
NewReader
(
data
)
resp
,
err
=
fcgi
.
Post
(
fcgi
_p
arams
,
""
,
rd
,
rd
.
Len
())
resp
,
err
=
fcgi
.
Post
(
fcgi
P
arams
,
""
,
rd
,
rd
.
Len
())
}
else
if
len
(
posts
)
>
0
{
}
else
if
len
(
posts
)
>
0
{
values
:=
url
.
Values
{}
values
:=
url
.
Values
{}
for
k
,
v
:=
range
posts
{
for
k
,
v
:=
range
posts
{
values
.
Set
(
k
,
v
)
values
.
Set
(
k
,
v
)
length
+=
len
(
k
)
+
2
+
len
(
v
)
length
+=
len
(
k
)
+
2
+
len
(
v
)
}
}
resp
,
err
=
fcgi
.
PostForm
(
fcgi
_p
arams
,
values
)
resp
,
err
=
fcgi
.
PostForm
(
fcgi
P
arams
,
values
)
}
else
{
}
else
{
resp
,
err
=
fcgi
.
Get
(
fcgi
_p
arams
)
resp
,
err
=
fcgi
.
Get
(
fcgi
P
arams
)
}
}
default
:
default
:
...
@@ -142,7 +142,7 @@ func sendFcgi(reqType int, fcgi_params map[string]string, data []byte, posts map
...
@@ -142,7 +142,7 @@ func sendFcgi(reqType int, fcgi_params map[string]string, data []byte, posts map
fi
,
_
:=
os
.
Lstat
(
v
)
fi
,
_
:=
os
.
Lstat
(
v
)
length
+=
len
(
k
)
+
int
(
fi
.
Size
())
length
+=
len
(
k
)
+
int
(
fi
.
Size
())
}
}
resp
,
err
=
fcgi
.
PostFile
(
fcgi
_p
arams
,
values
,
files
)
resp
,
err
=
fcgi
.
PostFile
(
fcgi
P
arams
,
values
,
files
)
}
}
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -191,7 +191,7 @@ func generateRandFile(size int) (p string, m string) {
...
@@ -191,7 +191,7 @@ func generateRandFile(size int) (p string, m string) {
return
return
}
}
func
Disabled
_
Test
(
t
*
testing
.
T
)
{
func
DisabledTest
(
t
*
testing
.
T
)
{
// TODO: test chunked reader
// TODO: test chunked reader
t_
=
t
t_
=
t
...
@@ -199,7 +199,7 @@ func Disabled_Test(t *testing.T) {
...
@@ -199,7 +199,7 @@ func Disabled_Test(t *testing.T) {
// server
// server
go
func
()
{
go
func
()
{
listener
,
err
:=
net
.
Listen
(
"tcp"
,
ip
_p
ort
)
listener
,
err
:=
net
.
Listen
(
"tcp"
,
ip
P
ort
)
if
err
!=
nil
{
if
err
!=
nil
{
// handle error
// handle error
log
.
Println
(
"listener creatation failed: "
,
err
)
log
.
Println
(
"listener creatation failed: "
,
err
)
...
@@ -212,19 +212,19 @@ func Disabled_Test(t *testing.T) {
...
@@ -212,19 +212,19 @@ func Disabled_Test(t *testing.T) {
time
.
Sleep
(
1
*
time
.
Second
)
time
.
Sleep
(
1
*
time
.
Second
)
// init
// init
fcgi
_p
arams
:=
make
(
map
[
string
]
string
)
fcgi
P
arams
:=
make
(
map
[
string
]
string
)
fcgi
_p
arams
[
"REQUEST_METHOD"
]
=
"GET"
fcgi
P
arams
[
"REQUEST_METHOD"
]
=
"GET"
fcgi
_p
arams
[
"SERVER_PROTOCOL"
]
=
"HTTP/1.1"
fcgi
P
arams
[
"SERVER_PROTOCOL"
]
=
"HTTP/1.1"
//fcgi_params["GATEWAY_INTERFACE"] = "CGI/1.1"
//fcgi_params["GATEWAY_INTERFACE"] = "CGI/1.1"
fcgi
_params
[
"SCRIPT_FILENAME"
]
=
script_f
ile
fcgi
Params
[
"SCRIPT_FILENAME"
]
=
scriptF
ile
// simple GET
// simple GET
log
.
Println
(
"test:"
,
"get"
)
log
.
Println
(
"test:"
,
"get"
)
sendFcgi
(
0
,
fcgi
_p
arams
,
nil
,
nil
,
nil
)
sendFcgi
(
0
,
fcgi
P
arams
,
nil
,
nil
,
nil
)
// simple post data
// simple post data
log
.
Println
(
"test:"
,
"post"
)
log
.
Println
(
"test:"
,
"post"
)
sendFcgi
(
0
,
fcgi
_p
arams
,
[]
byte
(
"c4ca4238a0b923820dcc509a6f75849b=1&7b8b965ad4bca0e41ab51de7b31363a1=n"
),
nil
,
nil
)
sendFcgi
(
0
,
fcgi
P
arams
,
[]
byte
(
"c4ca4238a0b923820dcc509a6f75849b=1&7b8b965ad4bca0e41ab51de7b31363a1=n"
),
nil
,
nil
)
log
.
Println
(
"test:"
,
"post data (more than 60KB)"
)
log
.
Println
(
"test:"
,
"post data (more than 60KB)"
)
data
:=
""
data
:=
""
...
@@ -240,13 +240,13 @@ func Disabled_Test(t *testing.T) {
...
@@ -240,13 +240,13 @@ func Disabled_Test(t *testing.T) {
data
+=
k0
+
"="
+
url
.
QueryEscape
(
v0
)
+
"&"
data
+=
k0
+
"="
+
url
.
QueryEscape
(
v0
)
+
"&"
}
}
sendFcgi
(
0
,
fcgi
_p
arams
,
[]
byte
(
data
),
nil
,
nil
)
sendFcgi
(
0
,
fcgi
P
arams
,
[]
byte
(
data
),
nil
,
nil
)
log
.
Println
(
"test:"
,
"post form (use url.Values)"
)
log
.
Println
(
"test:"
,
"post form (use url.Values)"
)
p0
:=
make
(
map
[
string
]
string
,
1
)
p0
:=
make
(
map
[
string
]
string
,
1
)
p0
[
"c4ca4238a0b923820dcc509a6f75849b"
]
=
"1"
p0
[
"c4ca4238a0b923820dcc509a6f75849b"
]
=
"1"
p0
[
"7b8b965ad4bca0e41ab51de7b31363a1"
]
=
"n"
p0
[
"7b8b965ad4bca0e41ab51de7b31363a1"
]
=
"n"
sendFcgi
(
1
,
fcgi
_p
arams
,
nil
,
p0
,
nil
)
sendFcgi
(
1
,
fcgi
P
arams
,
nil
,
p0
,
nil
)
log
.
Println
(
"test:"
,
"post forms (256 keys, more than 1MB)"
)
log
.
Println
(
"test:"
,
"post forms (256 keys, more than 1MB)"
)
p1
:=
make
(
map
[
string
]
string
,
1
)
p1
:=
make
(
map
[
string
]
string
,
1
)
...
@@ -257,25 +257,25 @@ func Disabled_Test(t *testing.T) {
...
@@ -257,25 +257,25 @@ func Disabled_Test(t *testing.T) {
k0
:=
fmt
.
Sprintf
(
"%x"
,
h
.
Sum
(
nil
))
k0
:=
fmt
.
Sprintf
(
"%x"
,
h
.
Sum
(
nil
))
p1
[
k0
]
=
v0
p1
[
k0
]
=
v0
}
}
sendFcgi
(
1
,
fcgi
_p
arams
,
nil
,
p1
,
nil
)
sendFcgi
(
1
,
fcgi
P
arams
,
nil
,
p1
,
nil
)
log
.
Println
(
"test:"
,
"post file (1 file, 500KB)) "
)
log
.
Println
(
"test:"
,
"post file (1 file, 500KB)) "
)
f0
:=
make
(
map
[
string
]
string
,
1
)
f0
:=
make
(
map
[
string
]
string
,
1
)
path0
,
m0
:=
generateRandFile
(
500000
)
path0
,
m0
:=
generateRandFile
(
500000
)
f0
[
m0
]
=
path0
f0
[
m0
]
=
path0
sendFcgi
(
1
,
fcgi
_p
arams
,
nil
,
p1
,
f0
)
sendFcgi
(
1
,
fcgi
P
arams
,
nil
,
p1
,
f0
)
log
.
Println
(
"test:"
,
"post multiple files (2 files, 5M each) and forms (256 keys, more than 1MB data"
)
log
.
Println
(
"test:"
,
"post multiple files (2 files, 5M each) and forms (256 keys, more than 1MB data"
)
path1
,
m1
:=
generateRandFile
(
5000000
)
path1
,
m1
:=
generateRandFile
(
5000000
)
f0
[
m1
]
=
path1
f0
[
m1
]
=
path1
sendFcgi
(
1
,
fcgi
_p
arams
,
nil
,
p1
,
f0
)
sendFcgi
(
1
,
fcgi
P
arams
,
nil
,
p1
,
f0
)
log
.
Println
(
"test:"
,
"post only files (2 files, 5M each)"
)
log
.
Println
(
"test:"
,
"post only files (2 files, 5M each)"
)
sendFcgi
(
1
,
fcgi
_p
arams
,
nil
,
nil
,
f0
)
sendFcgi
(
1
,
fcgi
P
arams
,
nil
,
nil
,
f0
)
log
.
Println
(
"test:"
,
"post only 1 file"
)
log
.
Println
(
"test:"
,
"post only 1 file"
)
delete
(
f0
,
"m0"
)
delete
(
f0
,
"m0"
)
sendFcgi
(
1
,
fcgi
_p
arams
,
nil
,
nil
,
f0
)
sendFcgi
(
1
,
fcgi
P
arams
,
nil
,
nil
,
f0
)
os
.
Remove
(
path0
)
os
.
Remove
(
path0
)
os
.
Remove
(
path1
)
os
.
Remove
(
path1
)
...
...
middleware/gzip/filter.go
View file @
f9bc7462
...
@@ -81,7 +81,7 @@ func (s Set) Contains(value string) bool {
...
@@ -81,7 +81,7 @@ func (s Set) Contains(value string) bool {
// elements in the set and passes each to f. It returns true
// elements in the set and passes each to f. It returns true
// on the first call to f that returns true and false otherwise.
// on the first call to f that returns true and false otherwise.
func
(
s
Set
)
ContainsFunc
(
f
func
(
string
)
bool
)
bool
{
func
(
s
Set
)
ContainsFunc
(
f
func
(
string
)
bool
)
bool
{
for
k
,
_
:=
range
s
{
for
k
:=
range
s
{
if
f
(
k
)
{
if
f
(
k
)
{
return
true
return
true
}
}
...
...
middleware/gzip/gzip_test.go
View file @
f9bc7462
...
@@ -21,7 +21,7 @@ func TestGzipHandler(t *testing.T) {
...
@@ -21,7 +21,7 @@ func TestGzipHandler(t *testing.T) {
extFilter
.
Exts
.
Add
(
e
)
extFilter
.
Exts
.
Add
(
e
)
}
}
gz
:=
Gzip
{
Configs
:
[]
Config
{
gz
:=
Gzip
{
Configs
:
[]
Config
{
Config
{
Filters
:
[]
Filter
{
pathFilter
,
extFilter
}},
{
Filters
:
[]
Filter
{
pathFilter
,
extFilter
}},
}}
}}
w
:=
httptest
.
NewRecorder
()
w
:=
httptest
.
NewRecorder
()
...
...
middleware/markdown/markdown_test.go
View file @
f9bc7462
...
@@ -22,7 +22,7 @@ func TestMarkdown(t *testing.T) {
...
@@ -22,7 +22,7 @@ func TestMarkdown(t *testing.T) {
Root
:
"./testdata"
,
Root
:
"./testdata"
,
FileSys
:
http
.
Dir
(
"./testdata"
),
FileSys
:
http
.
Dir
(
"./testdata"
),
Configs
:
[]
*
Config
{
Configs
:
[]
*
Config
{
&
Config
{
{
Renderer
:
blackfriday
.
HtmlRenderer
(
0
,
""
,
""
),
Renderer
:
blackfriday
.
HtmlRenderer
(
0
,
""
,
""
),
PathScope
:
"/blog"
,
PathScope
:
"/blog"
,
Extensions
:
[]
string
{
".md"
},
Extensions
:
[]
string
{
".md"
},
...
@@ -32,7 +32,7 @@ func TestMarkdown(t *testing.T) {
...
@@ -32,7 +32,7 @@ func TestMarkdown(t *testing.T) {
StaticDir
:
DefaultStaticDir
,
StaticDir
:
DefaultStaticDir
,
StaticFiles
:
make
(
map
[
string
]
string
),
StaticFiles
:
make
(
map
[
string
]
string
),
},
},
&
Config
{
{
Renderer
:
blackfriday
.
HtmlRenderer
(
0
,
""
,
""
),
Renderer
:
blackfriday
.
HtmlRenderer
(
0
,
""
,
""
),
PathScope
:
"/log"
,
PathScope
:
"/log"
,
Extensions
:
[]
string
{
".md"
},
Extensions
:
[]
string
{
".md"
},
...
@@ -42,7 +42,7 @@ func TestMarkdown(t *testing.T) {
...
@@ -42,7 +42,7 @@ func TestMarkdown(t *testing.T) {
StaticDir
:
DefaultStaticDir
,
StaticDir
:
DefaultStaticDir
,
StaticFiles
:
make
(
map
[
string
]
string
),
StaticFiles
:
make
(
map
[
string
]
string
),
},
},
&
Config
{
{
Renderer
:
blackfriday
.
HtmlRenderer
(
0
,
""
,
""
),
Renderer
:
blackfriday
.
HtmlRenderer
(
0
,
""
,
""
),
PathScope
:
"/og"
,
PathScope
:
"/og"
,
Extensions
:
[]
string
{
".md"
},
Extensions
:
[]
string
{
".md"
},
...
@@ -52,7 +52,7 @@ func TestMarkdown(t *testing.T) {
...
@@ -52,7 +52,7 @@ func TestMarkdown(t *testing.T) {
StaticDir
:
"testdata/og_static"
,
StaticDir
:
"testdata/og_static"
,
StaticFiles
:
map
[
string
]
string
{
"/og/first.md"
:
"testdata/og_static/og/first.md/index.html"
},
StaticFiles
:
map
[
string
]
string
{
"/og/first.md"
:
"testdata/og_static/og/first.md/index.html"
},
Links
:
[]
PageLink
{
Links
:
[]
PageLink
{
PageLink
{
{
Title
:
"first"
,
Title
:
"first"
,
Summary
:
""
,
Summary
:
""
,
Date
:
time
.
Now
(),
Date
:
time
.
Now
(),
...
...
middleware/markdown/process.go
View file @
f9bc7462
...
@@ -18,7 +18,7 @@ const (
...
@@ -18,7 +18,7 @@ const (
DefaultStaticDir
=
"generated_site"
DefaultStaticDir
=
"generated_site"
)
)
type
Markdown
Data
struct
{
type
Data
struct
{
middleware
.
Context
middleware
.
Context
Doc
map
[
string
]
string
Doc
map
[
string
]
string
Links
[]
PageLink
Links
[]
PageLink
...
@@ -95,7 +95,7 @@ func (md Markdown) processTemplate(c *Config, requestPath string, tmpl []byte, m
...
@@ -95,7 +95,7 @@ func (md Markdown) processTemplate(c *Config, requestPath string, tmpl []byte, m
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
mdData
:=
Markdown
Data
{
mdData
:=
Data
{
Context
:
ctx
,
Context
:
ctx
,
Doc
:
metadata
.
Variables
,
Doc
:
metadata
.
Variables
,
Links
:
c
.
Links
,
Links
:
c
.
Links
,
...
...
middleware/markdown/watcher.go
View file @
f9bc7462
...
@@ -5,6 +5,8 @@ import (
...
@@ -5,6 +5,8 @@ import (
"time"
"time"
)
)
// DefaultInterval is the default interval at which the markdown watcher
// checks for changes.
const
DefaultInterval
=
time
.
Second
*
60
const
DefaultInterval
=
time
.
Second
*
60
// Watch monitors the configured markdown directory for changes. It calls GenerateLinks
// Watch monitors the configured markdown directory for changes. It calls GenerateLinks
...
...
middleware/proxy/policy_test.go
View file @
f9bc7462
...
@@ -12,13 +12,13 @@ func (r *customPolicy) Select(pool HostPool) *UpstreamHost {
...
@@ -12,13 +12,13 @@ func (r *customPolicy) Select(pool HostPool) *UpstreamHost {
func
testPool
()
HostPool
{
func
testPool
()
HostPool
{
pool
:=
[]
*
UpstreamHost
{
pool
:=
[]
*
UpstreamHost
{
&
UpstreamHost
{
{
Name
:
"http://google.com"
,
// this should resolve (healthcheck test)
Name
:
"http://google.com"
,
// this should resolve (healthcheck test)
},
},
&
UpstreamHost
{
{
Name
:
"http://shouldnot.resolve"
,
// this shouldn't
Name
:
"http://shouldnot.resolve"
,
// this shouldn't
},
},
&
UpstreamHost
{
{
Name
:
"http://C"
,
Name
:
"http://C"
,
},
},
}
}
...
...
middleware/proxy/upstream.go
View file @
f9bc7462
...
@@ -13,8 +13,8 @@ import (
...
@@ -13,8 +13,8 @@ import (
)
)
var
(
var
(
supportedPolicies
map
[
string
]
func
()
Policy
=
make
(
map
[
string
]
func
()
Policy
)
supportedPolicies
=
make
(
map
[
string
]
func
()
Policy
)
proxyHeaders
http
.
Header
=
make
(
http
.
Header
)
proxyHeaders
=
make
(
http
.
Header
)
)
)
type
staticUpstream
struct
{
type
staticUpstream
struct
{
...
@@ -53,64 +53,8 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) {
...
@@ -53,64 +53,8 @@ func NewStaticUpstreams(c parse.Dispenser) ([]Upstream, error) {
}
}
for
c
.
NextBlock
()
{
for
c
.
NextBlock
()
{
switch
c
.
Val
()
{
if
err
:=
parseBlock
(
&
c
,
upstream
);
err
!=
nil
{
case
"policy"
:
return
upstreams
,
err
if
!
c
.
NextArg
()
{
return
upstreams
,
c
.
ArgErr
()
}
if
policyCreateFunc
,
ok
:=
supportedPolicies
[
c
.
Val
()];
ok
{
upstream
.
Policy
=
policyCreateFunc
()
}
else
{
return
upstreams
,
c
.
ArgErr
()
}
case
"fail_timeout"
:
if
!
c
.
NextArg
()
{
return
upstreams
,
c
.
ArgErr
()
}
if
dur
,
err
:=
time
.
ParseDuration
(
c
.
Val
());
err
==
nil
{
upstream
.
FailTimeout
=
dur
}
else
{
return
upstreams
,
err
}
case
"max_fails"
:
if
!
c
.
NextArg
()
{
return
upstreams
,
c
.
ArgErr
()
}
if
n
,
err
:=
strconv
.
Atoi
(
c
.
Val
());
err
==
nil
{
upstream
.
MaxFails
=
int32
(
n
)
}
else
{
return
upstreams
,
err
}
case
"health_check"
:
if
!
c
.
NextArg
()
{
return
upstreams
,
c
.
ArgErr
()
}
upstream
.
HealthCheck
.
Path
=
c
.
Val
()
upstream
.
HealthCheck
.
Interval
=
30
*
time
.
Second
if
c
.
NextArg
()
{
if
dur
,
err
:=
time
.
ParseDuration
(
c
.
Val
());
err
==
nil
{
upstream
.
HealthCheck
.
Interval
=
dur
}
else
{
return
upstreams
,
err
}
}
case
"proxy_header"
:
var
header
,
value
string
if
!
c
.
Args
(
&
header
,
&
value
)
{
return
upstreams
,
c
.
ArgErr
()
}
proxyHeaders
.
Add
(
header
,
value
)
case
"websocket"
:
proxyHeaders
.
Add
(
"Connection"
,
"{>Connection}"
)
proxyHeaders
.
Add
(
"Upgrade"
,
"{>Upgrade}"
)
case
"without"
:
if
!
c
.
NextArg
()
{
return
upstreams
,
c
.
ArgErr
()
}
upstream
.
WithoutPathPrefix
=
c
.
Val
()
default
:
return
upstreams
,
c
.
Errf
(
"unknown property '%s'"
,
c
.
Val
())
}
}
}
}
...
@@ -165,6 +109,68 @@ func (u *staticUpstream) From() string {
...
@@ -165,6 +109,68 @@ func (u *staticUpstream) From() string {
return
u
.
from
return
u
.
from
}
}
func
parseBlock
(
c
*
parse
.
Dispenser
,
u
*
staticUpstream
)
error
{
switch
c
.
Val
()
{
case
"policy"
:
if
!
c
.
NextArg
()
{
return
c
.
ArgErr
()
}
policyCreateFunc
,
ok
:=
supportedPolicies
[
c
.
Val
()]
if
!
ok
{
return
c
.
ArgErr
()
}
u
.
Policy
=
policyCreateFunc
()
case
"fail_timeout"
:
if
!
c
.
NextArg
()
{
return
c
.
ArgErr
()
}
dur
,
err
:=
time
.
ParseDuration
(
c
.
Val
())
if
err
!=
nil
{
return
err
}
u
.
FailTimeout
=
dur
case
"max_fails"
:
if
!
c
.
NextArg
()
{
return
c
.
ArgErr
()
}
n
,
err
:=
strconv
.
Atoi
(
c
.
Val
())
if
err
!=
nil
{
return
err
}
u
.
MaxFails
=
int32
(
n
)
case
"health_check"
:
if
!
c
.
NextArg
()
{
return
c
.
ArgErr
()
}
u
.
HealthCheck
.
Path
=
c
.
Val
()
u
.
HealthCheck
.
Interval
=
30
*
time
.
Second
if
c
.
NextArg
()
{
dur
,
err
:=
time
.
ParseDuration
(
c
.
Val
())
if
err
!=
nil
{
return
err
}
u
.
HealthCheck
.
Interval
=
dur
}
case
"proxy_header"
:
var
header
,
value
string
if
!
c
.
Args
(
&
header
,
&
value
)
{
return
c
.
ArgErr
()
}
proxyHeaders
.
Add
(
header
,
value
)
case
"websocket"
:
proxyHeaders
.
Add
(
"Connection"
,
"{>Connection}"
)
proxyHeaders
.
Add
(
"Upgrade"
,
"{>Upgrade}"
)
case
"without"
:
if
!
c
.
NextArg
()
{
return
c
.
ArgErr
()
}
u
.
WithoutPathPrefix
=
c
.
Val
()
default
:
return
c
.
Errf
(
"unknown property '%s'"
,
c
.
Val
())
}
return
nil
}
func
(
u
*
staticUpstream
)
healthCheck
()
{
func
(
u
*
staticUpstream
)
healthCheck
()
{
for
_
,
host
:=
range
u
.
Hosts
{
for
_
,
host
:=
range
u
.
Hosts
{
hostURL
:=
host
.
Name
+
u
.
HealthCheck
.
Path
hostURL
:=
host
.
Name
+
u
.
HealthCheck
.
Path
...
...
middleware/replacer_test.go
View file @
f9bc7462
...
@@ -10,9 +10,9 @@ import (
...
@@ -10,9 +10,9 @@ import (
func
TestNewReplacer
(
t
*
testing
.
T
)
{
func
TestNewReplacer
(
t
*
testing
.
T
)
{
w
:=
httptest
.
NewRecorder
()
w
:=
httptest
.
NewRecorder
()
recordRequest
:=
NewResponseRecorder
(
w
)
recordRequest
:=
NewResponseRecorder
(
w
)
userJ
son
:=
`{"username": "dennis"}`
userJ
SON
:=
`{"username": "dennis"}`
reader
:=
strings
.
NewReader
(
userJ
son
)
//Convert string to reader
reader
:=
strings
.
NewReader
(
userJ
SON
)
//Convert string to reader
request
,
err
:=
http
.
NewRequest
(
"POST"
,
"http://caddyserver.com"
,
reader
)
//Create request with JSON body
request
,
err
:=
http
.
NewRequest
(
"POST"
,
"http://caddyserver.com"
,
reader
)
//Create request with JSON body
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -41,9 +41,9 @@ func TestNewReplacer(t *testing.T) {
...
@@ -41,9 +41,9 @@ func TestNewReplacer(t *testing.T) {
func
TestReplace
(
t
*
testing
.
T
)
{
func
TestReplace
(
t
*
testing
.
T
)
{
w
:=
httptest
.
NewRecorder
()
w
:=
httptest
.
NewRecorder
()
recordRequest
:=
NewResponseRecorder
(
w
)
recordRequest
:=
NewResponseRecorder
(
w
)
userJ
son
:=
`{"username": "dennis"}`
userJ
SON
:=
`{"username": "dennis"}`
reader
:=
strings
.
NewReader
(
userJ
son
)
//Convert string to reader
reader
:=
strings
.
NewReader
(
userJ
SON
)
//Convert string to reader
request
,
err
:=
http
.
NewRequest
(
"POST"
,
"http://caddyserver.com"
,
reader
)
//Create request with JSON body
request
,
err
:=
http
.
NewRequest
(
"POST"
,
"http://caddyserver.com"
,
reader
)
//Create request with JSON body
if
err
!=
nil
{
if
err
!=
nil
{
...
...
middleware/rewrite/rewrite.go
View file @
f9bc7462
...
@@ -115,7 +115,7 @@ func (r *RegexpRule) Rewrite(req *http.Request) bool {
...
@@ -115,7 +115,7 @@ func (r *RegexpRule) Rewrite(req *http.Request) bool {
// include trailing slash in regexp if present
// include trailing slash in regexp if present
start
:=
len
(
r
.
Base
)
start
:=
len
(
r
.
Base
)
if
strings
.
HasSuffix
(
r
.
Base
,
"/"
)
{
if
strings
.
HasSuffix
(
r
.
Base
,
"/"
)
{
start
-=
1
start
--
}
}
// validate regexp
// validate regexp
...
...
middleware/rewrite/rewrite_test.go
View file @
f9bc7462
...
@@ -21,15 +21,15 @@ func TestRewrite(t *testing.T) {
...
@@ -21,15 +21,15 @@ func TestRewrite(t *testing.T) {
}
}
regexpRules
:=
[][]
string
{
regexpRules
:=
[][]
string
{
[]
string
{
"/reg/"
,
".*"
,
"/to"
,
""
},
{
"/reg/"
,
".*"
,
"/to"
,
""
},
[]
string
{
"/r/"
,
"[a-z]+"
,
"/toaz"
,
"!.html|"
},
{
"/r/"
,
"[a-z]+"
,
"/toaz"
,
"!.html|"
},
[]
string
{
"/url/"
,
"a([a-z0-9]*)s([A-Z]{2})"
,
"/to/{path}"
,
""
},
{
"/url/"
,
"a([a-z0-9]*)s([A-Z]{2})"
,
"/to/{path}"
,
""
},
[]
string
{
"/ab/"
,
"ab"
,
"/ab?{query}"
,
".txt|"
},
{
"/ab/"
,
"ab"
,
"/ab?{query}"
,
".txt|"
},
[]
string
{
"/ab/"
,
"ab"
,
"/ab?type=html&{query}"
,
".html|"
},
{
"/ab/"
,
"ab"
,
"/ab?type=html&{query}"
,
".html|"
},
[]
string
{
"/abc/"
,
"ab"
,
"/abc/{file}"
,
".html|"
},
{
"/abc/"
,
"ab"
,
"/abc/{file}"
,
".html|"
},
[]
string
{
"/abcd/"
,
"ab"
,
"/a/{dir}/{file}"
,
".html|"
},
{
"/abcd/"
,
"ab"
,
"/a/{dir}/{file}"
,
".html|"
},
[]
string
{
"/abcde/"
,
"ab"
,
"/a#{fragment}"
,
".html|"
},
{
"/abcde/"
,
"ab"
,
"/a#{fragment}"
,
".html|"
},
[]
string
{
"/ab/"
,
`.*\.jpg`
,
"/ajpg"
,
""
},
{
"/ab/"
,
`.*\.jpg`
,
"/ajpg"
,
""
},
}
}
for
_
,
regexpRule
:=
range
regexpRules
{
for
_
,
regexpRule
:=
range
regexpRules
{
...
...
middleware/roller.go
View file @
f9bc7462
...
@@ -6,6 +6,7 @@ import (
...
@@ -6,6 +6,7 @@ import (
"gopkg.in/natefinch/lumberjack.v2"
"gopkg.in/natefinch/lumberjack.v2"
)
)
// LogRoller implements a middleware that provides a rolling logger.
type
LogRoller
struct
{
type
LogRoller
struct
{
Filename
string
Filename
string
MaxSize
int
MaxSize
int
...
@@ -14,6 +15,7 @@ type LogRoller struct {
...
@@ -14,6 +15,7 @@ type LogRoller struct {
LocalTime
bool
LocalTime
bool
}
}
// GetLogWriter returns an io.Writer that writes to a rolling logger.
func
(
l
LogRoller
)
GetLogWriter
()
io
.
Writer
{
func
(
l
LogRoller
)
GetLogWriter
()
io
.
Writer
{
return
&
lumberjack
.
Logger
{
return
&
lumberjack
.
Logger
{
Filename
:
l
.
Filename
,
Filename
:
l
.
Filename
,
...
...
middleware/templates/templates_test.go
View file @
f9bc7462
...
@@ -14,12 +14,12 @@ func Test(t *testing.T) {
...
@@ -14,12 +14,12 @@ func Test(t *testing.T) {
return
0
,
nil
return
0
,
nil
}),
}),
Rules
:
[]
Rule
{
Rules
:
[]
Rule
{
Rule
{
{
Extensions
:
[]
string
{
".html"
},
Extensions
:
[]
string
{
".html"
},
IndexFiles
:
[]
string
{
"index.html"
},
IndexFiles
:
[]
string
{
"index.html"
},
Path
:
"/photos"
,
Path
:
"/photos"
,
},
},
Rule
{
{
Extensions
:
[]
string
{
".html"
,
".htm"
},
Extensions
:
[]
string
{
".html"
,
".htm"
},
IndexFiles
:
[]
string
{
"index.html"
,
"index.htm"
},
IndexFiles
:
[]
string
{
"index.html"
,
"index.htm"
},
Path
:
"/images"
,
Path
:
"/images"
,
...
@@ -34,7 +34,7 @@ func Test(t *testing.T) {
...
@@ -34,7 +34,7 @@ func Test(t *testing.T) {
return
0
,
nil
return
0
,
nil
}),
}),
Rules
:
[]
Rule
{
Rules
:
[]
Rule
{
Rule
{
{
Extensions
:
[]
string
{
".html"
},
Extensions
:
[]
string
{
".html"
},
IndexFiles
:
[]
string
{
"index.html"
},
IndexFiles
:
[]
string
{
"index.html"
},
Path
:
"/"
,
Path
:
"/"
,
...
...
server/server.go
View file @
f9bc7462
...
@@ -264,6 +264,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
...
@@ -264,6 +264,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
}
}
// DefaultErrorFunc responds to an HTTP request with a simple description
// of the specified HTTP status code.
func
DefaultErrorFunc
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
status
int
)
{
func
DefaultErrorFunc
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
,
status
int
)
{
w
.
WriteHeader
(
status
)
w
.
WriteHeader
(
status
)
fmt
.
Fprintf
(
w
,
"%d %s"
,
status
,
http
.
StatusText
(
status
))
fmt
.
Fprintf
(
w
,
"%d %s"
,
status
,
http
.
StatusText
(
status
))
...
...
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