Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
D
dex
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
dex
Commits
ec9d1607
Commit
ec9d1607
authored
Jan 08, 2017
by
Eric Chiang
Committed by
GitHub
Jan 08, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #760 from xeonx/master
Allow CORS on discovery endpoint
parents
4ddc5eb0
6cbf7125
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
873 additions
and
6 deletions
+873
-6
config.go
cmd/dex/config.go
+1
-0
serve.go
cmd/dex/serve.go
+4
-0
glide.lock
glide.lock
+4
-2
glide.yaml
glide.yaml
+2
-0
handlers.go
server/handlers.go
+11
-3
handlers_test.go
server/handlers_test.go
+58
-0
server.go
server/server.go
+12
-1
LICENSE
vendor/github.com/gorilla/handlers/LICENSE
+22
-0
canonical.go
vendor/github.com/gorilla/handlers/canonical.go
+74
-0
compress.go
vendor/github.com/gorilla/handlers/compress.go
+148
-0
cors.go
vendor/github.com/gorilla/handlers/cors.go
+317
-0
doc.go
vendor/github.com/gorilla/handlers/doc.go
+9
-0
handlers.go
vendor/github.com/gorilla/handlers/handlers.go
+0
-0
proxy_headers.go
vendor/github.com/gorilla/handlers/proxy_headers.go
+120
-0
recovery.go
vendor/github.com/gorilla/handlers/recovery.go
+91
-0
No files found.
cmd/dex/config.go
View file @
ec9d1607
...
@@ -102,6 +102,7 @@ type Web struct {
...
@@ -102,6 +102,7 @@ type Web struct {
HTTPS
string
`json:"https"`
HTTPS
string
`json:"https"`
TLSCert
string
`json:"tlsCert"`
TLSCert
string
`json:"tlsCert"`
TLSKey
string
`json:"tlsKey"`
TLSKey
string
`json:"tlsKey"`
DiscoveryAllowedOrigins
[]
string
`json:"discoveryAllowedOrigins"`
}
}
// GRPC is the config for the gRPC API.
// GRPC is the config for the gRPC API.
...
...
cmd/dex/serve.go
View file @
ec9d1607
...
@@ -179,6 +179,9 @@ func serve(cmd *cobra.Command, args []string) error {
...
@@ -179,6 +179,9 @@ func serve(cmd *cobra.Command, args []string) error {
if
c
.
OAuth2
.
SkipApprovalScreen
{
if
c
.
OAuth2
.
SkipApprovalScreen
{
logger
.
Infof
(
"config skipping approval screen"
)
logger
.
Infof
(
"config skipping approval screen"
)
}
}
if
len
(
c
.
Web
.
DiscoveryAllowedOrigins
)
>
0
{
logger
.
Infof
(
"config discovery allowed origins: %s"
,
c
.
Web
.
DiscoveryAllowedOrigins
)
}
// explicitly convert to UTC.
// explicitly convert to UTC.
now
:=
func
()
time
.
Time
{
return
time
.
Now
()
.
UTC
()
}
now
:=
func
()
time
.
Time
{
return
time
.
Now
()
.
UTC
()
}
...
@@ -186,6 +189,7 @@ func serve(cmd *cobra.Command, args []string) error {
...
@@ -186,6 +189,7 @@ func serve(cmd *cobra.Command, args []string) error {
serverConfig
:=
server
.
Config
{
serverConfig
:=
server
.
Config
{
SupportedResponseTypes
:
c
.
OAuth2
.
ResponseTypes
,
SupportedResponseTypes
:
c
.
OAuth2
.
ResponseTypes
,
SkipApprovalScreen
:
c
.
OAuth2
.
SkipApprovalScreen
,
SkipApprovalScreen
:
c
.
OAuth2
.
SkipApprovalScreen
,
DiscoveryAllowedOrigins
:
c
.
Web
.
DiscoveryAllowedOrigins
,
Issuer
:
c
.
Issuer
,
Issuer
:
c
.
Issuer
,
Connectors
:
connectors
,
Connectors
:
connectors
,
Storage
:
s
,
Storage
:
s
,
...
...
glide.lock
View file @
ec9d1607
hash:
22c01c4265c210fbf3cd2d55e9451b924a3301e35053c82ebe35171ab7286c83
hash:
4d7d84f09a330d27458fb821ae7ada243cfa825808dc7ab116db28a08f9166a2
updated: 2017-01-0
6T15:38:29.812891187-08
:00
updated: 2017-01-0
8T19:23:40.352046548+01
:00
imports:
imports:
- name: github.com/cockroachdb/cockroach-go
- name: github.com/cockroachdb/cockroach-go
version: 31611c0501c812f437d4861d87d117053967c955
version: 31611c0501c812f437d4861d87d117053967c955
...
@@ -18,6 +18,8 @@ imports:
...
@@ -18,6 +18,8 @@ imports:
- protoc-gen-go
- protoc-gen-go
- name: github.com/gorilla/context
- name: github.com/gorilla/context
version: aed02d124ae4a0e94fea4541c8effd05bf0c8296
version: aed02d124ae4a0e94fea4541c8effd05bf0c8296
- name: github.com/gorilla/handlers
version: 3a5767ca75ece5f7f1440b1d16975247f8d8b221
- name: github.com/gorilla/mux
- name: github.com/gorilla/mux
version: 9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e
version: 9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e
- name: github.com/gtank/cryptopasta
- name: github.com/gtank/cryptopasta
...
...
glide.yaml
View file @
ec9d1607
...
@@ -53,6 +53,8 @@ import:
...
@@ -53,6 +53,8 @@ import:
version
:
9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e
version
:
9fa818a44c2bf1396a17f9d5a3c0f6dd39d2ff8e
-
package
:
github.com/gorilla/context
-
package
:
github.com/gorilla/context
version
:
aed02d124ae4a0e94fea4541c8effd05bf0c8296
version
:
aed02d124ae4a0e94fea4541c8effd05bf0c8296
-
package
:
github.com/gorilla/handlers
version
:
3a5767ca75ece5f7f1440b1d16975247f8d8b221
# Package with a bunch of sane crypto defaults. Consider just
# Package with a bunch of sane crypto defaults. Consider just
# copy the code (as recommended by the repo itself) instead
# copy the code (as recommended by the repo itself) instead
...
...
server/handlers.go
View file @
ec9d1607
...
@@ -11,6 +11,7 @@ import (
...
@@ -11,6 +11,7 @@ import (
"strings"
"strings"
"time"
"time"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/gorilla/mux"
jose
"gopkg.in/square/go-jose.v2"
jose
"gopkg.in/square/go-jose.v2"
...
@@ -101,7 +102,7 @@ type discovery struct {
...
@@ -101,7 +102,7 @@ type discovery struct {
Claims
[]
string
`json:"claims_supported"`
Claims
[]
string
`json:"claims_supported"`
}
}
func
(
s
*
Server
)
discoveryHandler
()
(
http
.
Handler
Func
,
error
)
{
func
(
s
*
Server
)
discoveryHandler
()
(
http
.
Handler
,
error
)
{
d
:=
discovery
{
d
:=
discovery
{
Issuer
:
s
.
issuerURL
.
String
(),
Issuer
:
s
.
issuerURL
.
String
(),
Auth
:
s
.
absURL
(
"/auth"
),
Auth
:
s
.
absURL
(
"/auth"
),
...
@@ -127,11 +128,18 @@ func (s *Server) discoveryHandler() (http.HandlerFunc, error) {
...
@@ -127,11 +128,18 @@ func (s *Server) discoveryHandler() (http.HandlerFunc, error) {
return
nil
,
fmt
.
Errorf
(
"failed to marshal discovery data: %v"
,
err
)
return
nil
,
fmt
.
Errorf
(
"failed to marshal discovery data: %v"
,
err
)
}
}
return
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
var
discoveryHandler
http
.
Handler
discoveryHandler
=
http
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
w
.
Header
()
.
Set
(
"Content-Type"
,
"application/json"
)
w
.
Header
()
.
Set
(
"Content-Type"
,
"application/json"
)
w
.
Header
()
.
Set
(
"Content-Length"
,
strconv
.
Itoa
(
len
(
data
)))
w
.
Header
()
.
Set
(
"Content-Length"
,
strconv
.
Itoa
(
len
(
data
)))
w
.
Write
(
data
)
w
.
Write
(
data
)
},
nil
})
if
len
(
s
.
discoveryAllowedOrigins
)
>
0
{
corsOption
:=
handlers
.
AllowedOrigins
(
s
.
discoveryAllowedOrigins
)
discoveryHandler
=
handlers
.
CORS
(
corsOption
)(
discoveryHandler
)
}
return
discoveryHandler
,
nil
}
}
// handleAuthorization handles the OAuth2 auth endpoint.
// handleAuthorization handles the OAuth2 auth endpoint.
...
...
server/handlers_test.go
View file @
ec9d1607
...
@@ -22,3 +22,61 @@ func TestHandleHealth(t *testing.T) {
...
@@ -22,3 +22,61 @@ func TestHandleHealth(t *testing.T) {
}
}
}
}
var
discoveryHandlerCORSTests
=
[]
struct
{
DiscoveryAllowedOrigins
[]
string
Origin
string
ResponseAllowOrigin
string
//The expected response: same as Origin in case of valid CORS flow
}{
{
nil
,
"http://foo.example"
,
""
},
//Default behavior: cross origin requests not allowed
{[]
string
{},
"http://foo.example"
,
""
},
{[]
string
{
"http://foo.example"
},
"http://foo.example"
,
"http://foo.example"
},
{[]
string
{
"http://bar.example"
,
"http://foo.example"
},
"http://foo.example"
,
"http://foo.example"
},
{[]
string
{
"*"
},
"http://foo.example"
,
"http://foo.example"
},
{[]
string
{
"http://bar.example"
},
"http://foo.example"
,
""
},
}
func
TestDiscoveryHandlerCORS
(
t
*
testing
.
T
)
{
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
defer
cancel
()
for
_
,
testcase
:=
range
discoveryHandlerCORSTests
{
httpServer
,
server
:=
newTestServer
(
ctx
,
t
,
func
(
c
*
Config
)
{
c
.
DiscoveryAllowedOrigins
=
testcase
.
DiscoveryAllowedOrigins
})
defer
httpServer
.
Close
()
discoveryHandler
,
err
:=
server
.
discoveryHandler
()
if
err
!=
nil
{
t
.
Fatalf
(
"failed to get discovery handler: %v"
,
err
)
}
//Perform preflight request
rrPreflight
:=
httptest
.
NewRecorder
()
reqPreflight
:=
httptest
.
NewRequest
(
"OPTIONS"
,
"/.well-kown/openid-configuration"
,
nil
)
reqPreflight
.
Header
.
Set
(
"Origin"
,
testcase
.
Origin
)
reqPreflight
.
Header
.
Set
(
"Access-Control-Request-Method"
,
"GET"
)
discoveryHandler
.
ServeHTTP
(
rrPreflight
,
reqPreflight
)
if
rrPreflight
.
Code
!=
http
.
StatusOK
{
t
.
Errorf
(
"expected 200 got %d"
,
rrPreflight
.
Code
)
}
headerAccessControlPreflight
:=
rrPreflight
.
HeaderMap
.
Get
(
"Access-Control-Allow-Origin"
)
if
headerAccessControlPreflight
!=
testcase
.
ResponseAllowOrigin
{
t
.
Errorf
(
"expected '%s' got '%s'"
,
testcase
.
ResponseAllowOrigin
,
headerAccessControlPreflight
)
}
//Perform request
rr
:=
httptest
.
NewRecorder
()
req
:=
httptest
.
NewRequest
(
"GET"
,
"/.well-kown/openid-configuration"
,
nil
)
req
.
Header
.
Set
(
"Origin"
,
testcase
.
Origin
)
discoveryHandler
.
ServeHTTP
(
rr
,
req
)
if
rr
.
Code
!=
http
.
StatusOK
{
t
.
Errorf
(
"expected 200 got %d"
,
rr
.
Code
)
}
headerAccessControl
:=
rr
.
HeaderMap
.
Get
(
"Access-Control-Allow-Origin"
)
if
headerAccessControl
!=
testcase
.
ResponseAllowOrigin
{
t
.
Errorf
(
"expected '%s' got '%s'"
,
testcase
.
ResponseAllowOrigin
,
headerAccessControl
)
}
}
}
server/server.go
View file @
ec9d1607
...
@@ -42,6 +42,11 @@ type Config struct {
...
@@ -42,6 +42,11 @@ type Config struct {
// flow. If no response types are supplied this value defaults to "code".
// flow. If no response types are supplied this value defaults to "code".
SupportedResponseTypes
[]
string
SupportedResponseTypes
[]
string
// List of allowed origins for CORS requests on discovery endpoint.
// If none are indicated, CORS requests are disabled. Passing in "*" will allow any
// domain.
DiscoveryAllowedOrigins
[]
string
// If enabled, the server won't prompt the user to approve authorization requests.
// If enabled, the server won't prompt the user to approve authorization requests.
// Logging in implies approval.
// Logging in implies approval.
SkipApprovalScreen
bool
SkipApprovalScreen
bool
...
@@ -111,6 +116,8 @@ type Server struct {
...
@@ -111,6 +116,8 @@ type Server struct {
supportedResponseTypes
map
[
string
]
bool
supportedResponseTypes
map
[
string
]
bool
discoveryAllowedOrigins
[]
string
now
func
()
time
.
Time
now
func
()
time
.
Time
idTokensValidFor
time
.
Duration
idTokensValidFor
time
.
Duration
...
@@ -182,6 +189,7 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy)
...
@@ -182,6 +189,7 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy)
connectors
:
make
(
map
[
string
]
Connector
),
connectors
:
make
(
map
[
string
]
Connector
),
storage
:
newKeyCacher
(
c
.
Storage
,
now
),
storage
:
newKeyCacher
(
c
.
Storage
,
now
),
supportedResponseTypes
:
supported
,
supportedResponseTypes
:
supported
,
discoveryAllowedOrigins
:
c
.
DiscoveryAllowedOrigins
,
idTokensValidFor
:
value
(
c
.
IDTokensValidFor
,
24
*
time
.
Hour
),
idTokensValidFor
:
value
(
c
.
IDTokensValidFor
,
24
*
time
.
Hour
),
skipApproval
:
c
.
SkipApprovalScreen
,
skipApproval
:
c
.
SkipApprovalScreen
,
now
:
now
,
now
:
now
,
...
@@ -197,6 +205,9 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy)
...
@@ -197,6 +205,9 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy)
handleFunc
:=
func
(
p
string
,
h
http
.
HandlerFunc
)
{
handleFunc
:=
func
(
p
string
,
h
http
.
HandlerFunc
)
{
r
.
HandleFunc
(
path
.
Join
(
issuerURL
.
Path
,
p
),
h
)
r
.
HandleFunc
(
path
.
Join
(
issuerURL
.
Path
,
p
),
h
)
}
}
handle
:=
func
(
p
string
,
h
http
.
Handler
)
{
r
.
Handle
(
path
.
Join
(
issuerURL
.
Path
,
p
),
h
)
}
handlePrefix
:=
func
(
p
string
,
h
http
.
Handler
)
{
handlePrefix
:=
func
(
p
string
,
h
http
.
Handler
)
{
prefix
:=
path
.
Join
(
issuerURL
.
Path
,
p
)
prefix
:=
path
.
Join
(
issuerURL
.
Path
,
p
)
r
.
PathPrefix
(
prefix
)
.
Handler
(
http
.
StripPrefix
(
prefix
,
h
))
r
.
PathPrefix
(
prefix
)
.
Handler
(
http
.
StripPrefix
(
prefix
,
h
))
...
@@ -207,7 +218,7 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy)
...
@@ -207,7 +218,7 @@ func newServer(ctx context.Context, c Config, rotationStrategy rotationStrategy)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
handle
Func
(
"/.well-known/openid-configuration"
,
discoveryHandler
)
handle
(
"/.well-known/openid-configuration"
,
discoveryHandler
)
// TODO(ericchiang): rate limit certain paths based on IP.
// TODO(ericchiang): rate limit certain paths based on IP.
handleFunc
(
"/token"
,
s
.
handleToken
)
handleFunc
(
"/token"
,
s
.
handleToken
)
...
...
vendor/github.com/gorilla/handlers/LICENSE
0 → 100644
View file @
ec9d1607
Copyright (c) 2013 The Gorilla Handlers 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.
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 HOLDER 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/github.com/gorilla/handlers/canonical.go
0 → 100644
View file @
ec9d1607
package
handlers
import
(
"net/http"
"net/url"
"strings"
)
type
canonical
struct
{
h
http
.
Handler
domain
string
code
int
}
// CanonicalHost is HTTP middleware that re-directs requests to the canonical
// domain. It accepts a domain and a status code (e.g. 301 or 302) and
// re-directs clients to this domain. The existing request path is maintained.
//
// Note: If the provided domain is considered invalid by url.Parse or otherwise
// returns an empty scheme or host, clients are not re-directed.
//
// Example:
//
// r := mux.NewRouter()
// canonical := handlers.CanonicalHost("http://www.gorillatoolkit.org", 302)
// r.HandleFunc("/route", YourHandler)
//
// log.Fatal(http.ListenAndServe(":7000", canonical(r)))
//
func
CanonicalHost
(
domain
string
,
code
int
)
func
(
h
http
.
Handler
)
http
.
Handler
{
fn
:=
func
(
h
http
.
Handler
)
http
.
Handler
{
return
canonical
{
h
,
domain
,
code
}
}
return
fn
}
func
(
c
canonical
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
dest
,
err
:=
url
.
Parse
(
c
.
domain
)
if
err
!=
nil
{
// Call the next handler if the provided domain fails to parse.
c
.
h
.
ServeHTTP
(
w
,
r
)
return
}
if
dest
.
Scheme
==
""
||
dest
.
Host
==
""
{
// Call the next handler if the scheme or host are empty.
// Note that url.Parse won't fail on in this case.
c
.
h
.
ServeHTTP
(
w
,
r
)
return
}
if
!
strings
.
EqualFold
(
cleanHost
(
r
.
Host
),
dest
.
Host
)
{
// Re-build the destination URL
dest
:=
dest
.
Scheme
+
"://"
+
dest
.
Host
+
r
.
URL
.
Path
if
r
.
URL
.
RawQuery
!=
""
{
dest
+=
"?"
+
r
.
URL
.
RawQuery
}
http
.
Redirect
(
w
,
r
,
dest
,
c
.
code
)
return
}
c
.
h
.
ServeHTTP
(
w
,
r
)
}
// cleanHost cleans invalid Host headers by stripping anything after '/' or ' '.
// This is backported from Go 1.5 (in response to issue #11206) and attempts to
// mitigate malformed Host headers that do not match the format in RFC7230.
func
cleanHost
(
in
string
)
string
{
if
i
:=
strings
.
IndexAny
(
in
,
" /"
);
i
!=
-
1
{
return
in
[
:
i
]
}
return
in
}
vendor/github.com/gorilla/handlers/compress.go
0 → 100644
View file @
ec9d1607
// Copyright 2013 The Gorilla 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
handlers
import
(
"compress/flate"
"compress/gzip"
"io"
"net/http"
"strings"
)
type
compressResponseWriter
struct
{
io
.
Writer
http
.
ResponseWriter
http
.
Hijacker
http
.
Flusher
http
.
CloseNotifier
}
func
(
w
*
compressResponseWriter
)
WriteHeader
(
c
int
)
{
w
.
ResponseWriter
.
Header
()
.
Del
(
"Content-Length"
)
w
.
ResponseWriter
.
WriteHeader
(
c
)
}
func
(
w
*
compressResponseWriter
)
Header
()
http
.
Header
{
return
w
.
ResponseWriter
.
Header
()
}
func
(
w
*
compressResponseWriter
)
Write
(
b
[]
byte
)
(
int
,
error
)
{
h
:=
w
.
ResponseWriter
.
Header
()
if
h
.
Get
(
"Content-Type"
)
==
""
{
h
.
Set
(
"Content-Type"
,
http
.
DetectContentType
(
b
))
}
h
.
Del
(
"Content-Length"
)
return
w
.
Writer
.
Write
(
b
)
}
type
flusher
interface
{
Flush
()
error
}
func
(
w
*
compressResponseWriter
)
Flush
()
{
// Flush compressed data if compressor supports it.
if
f
,
ok
:=
w
.
Writer
.
(
flusher
);
ok
{
f
.
Flush
()
}
// Flush HTTP response.
if
w
.
Flusher
!=
nil
{
w
.
Flusher
.
Flush
()
}
}
// CompressHandler gzip compresses HTTP responses for clients that support it
// via the 'Accept-Encoding' header.
//
// Compressing TLS traffic may leak the page contents to an attacker if the
// page contains user input: http://security.stackexchange.com/a/102015/12208
func
CompressHandler
(
h
http
.
Handler
)
http
.
Handler
{
return
CompressHandlerLevel
(
h
,
gzip
.
DefaultCompression
)
}
// CompressHandlerLevel gzip compresses HTTP responses with specified compression level
// for clients that support it via the 'Accept-Encoding' header.
//
// The compression level should be gzip.DefaultCompression, gzip.NoCompression,
// or any integer value between gzip.BestSpeed and gzip.BestCompression inclusive.
// gzip.DefaultCompression is used in case of invalid compression level.
func
CompressHandlerLevel
(
h
http
.
Handler
,
level
int
)
http
.
Handler
{
if
level
<
gzip
.
DefaultCompression
||
level
>
gzip
.
BestCompression
{
level
=
gzip
.
DefaultCompression
}
return
http
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
L
:
for
_
,
enc
:=
range
strings
.
Split
(
r
.
Header
.
Get
(
"Accept-Encoding"
),
","
)
{
switch
strings
.
TrimSpace
(
enc
)
{
case
"gzip"
:
w
.
Header
()
.
Set
(
"Content-Encoding"
,
"gzip"
)
w
.
Header
()
.
Add
(
"Vary"
,
"Accept-Encoding"
)
gw
,
_
:=
gzip
.
NewWriterLevel
(
w
,
level
)
defer
gw
.
Close
()
h
,
hok
:=
w
.
(
http
.
Hijacker
)
if
!
hok
{
/* w is not Hijacker... oh well... */
h
=
nil
}
f
,
fok
:=
w
.
(
http
.
Flusher
)
if
!
fok
{
f
=
nil
}
cn
,
cnok
:=
w
.
(
http
.
CloseNotifier
)
if
!
cnok
{
cn
=
nil
}
w
=
&
compressResponseWriter
{
Writer
:
gw
,
ResponseWriter
:
w
,
Hijacker
:
h
,
Flusher
:
f
,
CloseNotifier
:
cn
,
}
break
L
case
"deflate"
:
w
.
Header
()
.
Set
(
"Content-Encoding"
,
"deflate"
)
w
.
Header
()
.
Add
(
"Vary"
,
"Accept-Encoding"
)
fw
,
_
:=
flate
.
NewWriter
(
w
,
level
)
defer
fw
.
Close
()
h
,
hok
:=
w
.
(
http
.
Hijacker
)
if
!
hok
{
/* w is not Hijacker... oh well... */
h
=
nil
}
f
,
fok
:=
w
.
(
http
.
Flusher
)
if
!
fok
{
f
=
nil
}
cn
,
cnok
:=
w
.
(
http
.
CloseNotifier
)
if
!
cnok
{
cn
=
nil
}
w
=
&
compressResponseWriter
{
Writer
:
fw
,
ResponseWriter
:
w
,
Hijacker
:
h
,
Flusher
:
f
,
CloseNotifier
:
cn
,
}
break
L
}
}
h
.
ServeHTTP
(
w
,
r
)
})
}
vendor/github.com/gorilla/handlers/cors.go
0 → 100644
View file @
ec9d1607
package
handlers
import
(
"net/http"
"strconv"
"strings"
)
// CORSOption represents a functional option for configuring the CORS middleware.
type
CORSOption
func
(
*
cors
)
error
type
cors
struct
{
h
http
.
Handler
allowedHeaders
[]
string
allowedMethods
[]
string
allowedOrigins
[]
string
allowedOriginValidator
OriginValidator
exposedHeaders
[]
string
maxAge
int
ignoreOptions
bool
allowCredentials
bool
}
// OriginValidator takes an origin string and returns whether or not that origin is allowed.
type
OriginValidator
func
(
string
)
bool
var
(
defaultCorsMethods
=
[]
string
{
"GET"
,
"HEAD"
,
"POST"
}
defaultCorsHeaders
=
[]
string
{
"Accept"
,
"Accept-Language"
,
"Content-Language"
,
"Origin"
}
// (WebKit/Safari v9 sends the Origin header by default in AJAX requests)
)
const
(
corsOptionMethod
string
=
"OPTIONS"
corsAllowOriginHeader
string
=
"Access-Control-Allow-Origin"
corsExposeHeadersHeader
string
=
"Access-Control-Expose-Headers"
corsMaxAgeHeader
string
=
"Access-Control-Max-Age"
corsAllowMethodsHeader
string
=
"Access-Control-Allow-Methods"
corsAllowHeadersHeader
string
=
"Access-Control-Allow-Headers"
corsAllowCredentialsHeader
string
=
"Access-Control-Allow-Credentials"
corsRequestMethodHeader
string
=
"Access-Control-Request-Method"
corsRequestHeadersHeader
string
=
"Access-Control-Request-Headers"
corsOriginHeader
string
=
"Origin"
corsVaryHeader
string
=
"Vary"
corsOriginMatchAll
string
=
"*"
)
func
(
ch
*
cors
)
ServeHTTP
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
origin
:=
r
.
Header
.
Get
(
corsOriginHeader
)
if
!
ch
.
isOriginAllowed
(
origin
)
{
ch
.
h
.
ServeHTTP
(
w
,
r
)
return
}
if
r
.
Method
==
corsOptionMethod
{
if
ch
.
ignoreOptions
{
ch
.
h
.
ServeHTTP
(
w
,
r
)
return
}
if
_
,
ok
:=
r
.
Header
[
corsRequestMethodHeader
];
!
ok
{
w
.
WriteHeader
(
http
.
StatusBadRequest
)
return
}
method
:=
r
.
Header
.
Get
(
corsRequestMethodHeader
)
if
!
ch
.
isMatch
(
method
,
ch
.
allowedMethods
)
{
w
.
WriteHeader
(
http
.
StatusMethodNotAllowed
)
return
}
requestHeaders
:=
strings
.
Split
(
r
.
Header
.
Get
(
corsRequestHeadersHeader
),
","
)
allowedHeaders
:=
[]
string
{}
for
_
,
v
:=
range
requestHeaders
{
canonicalHeader
:=
http
.
CanonicalHeaderKey
(
strings
.
TrimSpace
(
v
))
if
canonicalHeader
==
""
||
ch
.
isMatch
(
canonicalHeader
,
defaultCorsHeaders
)
{
continue
}
if
!
ch
.
isMatch
(
canonicalHeader
,
ch
.
allowedHeaders
)
{
w
.
WriteHeader
(
http
.
StatusForbidden
)
return
}
allowedHeaders
=
append
(
allowedHeaders
,
canonicalHeader
)
}
if
len
(
allowedHeaders
)
>
0
{
w
.
Header
()
.
Set
(
corsAllowHeadersHeader
,
strings
.
Join
(
allowedHeaders
,
","
))
}
if
ch
.
maxAge
>
0
{
w
.
Header
()
.
Set
(
corsMaxAgeHeader
,
strconv
.
Itoa
(
ch
.
maxAge
))
}
if
!
ch
.
isMatch
(
method
,
defaultCorsMethods
)
{
w
.
Header
()
.
Set
(
corsAllowMethodsHeader
,
method
)
}
}
else
{
if
len
(
ch
.
exposedHeaders
)
>
0
{
w
.
Header
()
.
Set
(
corsExposeHeadersHeader
,
strings
.
Join
(
ch
.
exposedHeaders
,
","
))
}
}
if
ch
.
allowCredentials
{
w
.
Header
()
.
Set
(
corsAllowCredentialsHeader
,
"true"
)
}
if
len
(
ch
.
allowedOrigins
)
>
1
{
w
.
Header
()
.
Set
(
corsVaryHeader
,
corsOriginHeader
)
}
w
.
Header
()
.
Set
(
corsAllowOriginHeader
,
origin
)
if
r
.
Method
==
corsOptionMethod
{
return
}
ch
.
h
.
ServeHTTP
(
w
,
r
)
}
// CORS provides Cross-Origin Resource Sharing middleware.
// Example:
//
// import (
// "net/http"
//
// "github.com/gorilla/handlers"
// "github.com/gorilla/mux"
// )
//
// func main() {
// r := mux.NewRouter()
// r.HandleFunc("/users", UserEndpoint)
// r.HandleFunc("/projects", ProjectEndpoint)
//
// // Apply the CORS middleware to our top-level router, with the defaults.
// http.ListenAndServe(":8000", handlers.CORS()(r))
// }
//
func
CORS
(
opts
...
CORSOption
)
func
(
http
.
Handler
)
http
.
Handler
{
return
func
(
h
http
.
Handler
)
http
.
Handler
{
ch
:=
parseCORSOptions
(
opts
...
)
ch
.
h
=
h
return
ch
}
}
func
parseCORSOptions
(
opts
...
CORSOption
)
*
cors
{
ch
:=
&
cors
{
allowedMethods
:
defaultCorsMethods
,
allowedHeaders
:
defaultCorsHeaders
,
allowedOrigins
:
[]
string
{
corsOriginMatchAll
},
}
for
_
,
option
:=
range
opts
{
option
(
ch
)
}
return
ch
}
//
// Functional options for configuring CORS.
//
// AllowedHeaders adds the provided headers to the list of allowed headers in a
// CORS request.
// This is an append operation so the headers Accept, Accept-Language,
// and Content-Language are always allowed.
// Content-Type must be explicitly declared if accepting Content-Types other than
// application/x-www-form-urlencoded, multipart/form-data, or text/plain.
func
AllowedHeaders
(
headers
[]
string
)
CORSOption
{
return
func
(
ch
*
cors
)
error
{
for
_
,
v
:=
range
headers
{
normalizedHeader
:=
http
.
CanonicalHeaderKey
(
strings
.
TrimSpace
(
v
))
if
normalizedHeader
==
""
{
continue
}
if
!
ch
.
isMatch
(
normalizedHeader
,
ch
.
allowedHeaders
)
{
ch
.
allowedHeaders
=
append
(
ch
.
allowedHeaders
,
normalizedHeader
)
}
}
return
nil
}
}
// AllowedMethods can be used to explicitly allow methods in the
// Access-Control-Allow-Methods header.
// This is a replacement operation so you must also
// pass GET, HEAD, and POST if you wish to support those methods.
func
AllowedMethods
(
methods
[]
string
)
CORSOption
{
return
func
(
ch
*
cors
)
error
{
ch
.
allowedMethods
=
[]
string
{}
for
_
,
v
:=
range
methods
{
normalizedMethod
:=
strings
.
ToUpper
(
strings
.
TrimSpace
(
v
))
if
normalizedMethod
==
""
{
continue
}
if
!
ch
.
isMatch
(
normalizedMethod
,
ch
.
allowedMethods
)
{
ch
.
allowedMethods
=
append
(
ch
.
allowedMethods
,
normalizedMethod
)
}
}
return
nil
}
}
// AllowedOrigins sets the allowed origins for CORS requests, as used in the
// 'Allow-Access-Control-Origin' HTTP header.
// Note: Passing in a []string{"*"} will allow any domain.
func
AllowedOrigins
(
origins
[]
string
)
CORSOption
{
return
func
(
ch
*
cors
)
error
{
for
_
,
v
:=
range
origins
{
if
v
==
corsOriginMatchAll
{
ch
.
allowedOrigins
=
[]
string
{
corsOriginMatchAll
}
return
nil
}
}
ch
.
allowedOrigins
=
origins
return
nil
}
}
// AllowedOriginValidator sets a function for evaluating allowed origins in CORS requests, represented by the
// 'Allow-Access-Control-Origin' HTTP header.
func
AllowedOriginValidator
(
fn
OriginValidator
)
CORSOption
{
return
func
(
ch
*
cors
)
error
{
ch
.
allowedOriginValidator
=
fn
return
nil
}
}
// ExposeHeaders can be used to specify headers that are available
// and will not be stripped out by the user-agent.
func
ExposedHeaders
(
headers
[]
string
)
CORSOption
{
return
func
(
ch
*
cors
)
error
{
ch
.
exposedHeaders
=
[]
string
{}
for
_
,
v
:=
range
headers
{
normalizedHeader
:=
http
.
CanonicalHeaderKey
(
strings
.
TrimSpace
(
v
))
if
normalizedHeader
==
""
{
continue
}
if
!
ch
.
isMatch
(
normalizedHeader
,
ch
.
exposedHeaders
)
{
ch
.
exposedHeaders
=
append
(
ch
.
exposedHeaders
,
normalizedHeader
)
}
}
return
nil
}
}
// MaxAge determines the maximum age (in seconds) between preflight requests. A
// maximum of 10 minutes is allowed. An age above this value will default to 10
// minutes.
func
MaxAge
(
age
int
)
CORSOption
{
return
func
(
ch
*
cors
)
error
{
// Maximum of 10 minutes.
if
age
>
600
{
age
=
600
}
ch
.
maxAge
=
age
return
nil
}
}
// IgnoreOptions causes the CORS middleware to ignore OPTIONS requests, instead
// passing them through to the next handler. This is useful when your application
// or framework has a pre-existing mechanism for responding to OPTIONS requests.
func
IgnoreOptions
()
CORSOption
{
return
func
(
ch
*
cors
)
error
{
ch
.
ignoreOptions
=
true
return
nil
}
}
// AllowCredentials can be used to specify that the user agent may pass
// authentication details along with the request.
func
AllowCredentials
()
CORSOption
{
return
func
(
ch
*
cors
)
error
{
ch
.
allowCredentials
=
true
return
nil
}
}
func
(
ch
*
cors
)
isOriginAllowed
(
origin
string
)
bool
{
if
origin
==
""
{
return
false
}
if
ch
.
allowedOriginValidator
!=
nil
{
return
ch
.
allowedOriginValidator
(
origin
)
}
for
_
,
allowedOrigin
:=
range
ch
.
allowedOrigins
{
if
allowedOrigin
==
origin
||
allowedOrigin
==
corsOriginMatchAll
{
return
true
}
}
return
false
}
func
(
ch
*
cors
)
isMatch
(
needle
string
,
haystack
[]
string
)
bool
{
for
_
,
v
:=
range
haystack
{
if
v
==
needle
{
return
true
}
}
return
false
}
vendor/github.com/gorilla/handlers/doc.go
0 → 100644
View file @
ec9d1607
/*
Package handlers is a collection of handlers (aka "HTTP middleware") for use
with Go's net/http package (or any framework supporting http.Handler).
The package includes handlers for logging in standardised formats, compressing
HTTP responses, validating content types and other useful tools for manipulating
requests and responses.
*/
package
handlers
vendor/github.com/gorilla/handlers/handlers.go
0 → 100644
View file @
ec9d1607
This diff is collapsed.
Click to expand it.
vendor/github.com/gorilla/handlers/proxy_headers.go
0 → 100644
View file @
ec9d1607
package
handlers
import
(
"net/http"
"regexp"
"strings"
)
var
(
// De-facto standard header keys.
xForwardedFor
=
http
.
CanonicalHeaderKey
(
"X-Forwarded-For"
)
xForwardedHost
=
http
.
CanonicalHeaderKey
(
"X-Forwarded-Host"
)
xForwardedProto
=
http
.
CanonicalHeaderKey
(
"X-Forwarded-Proto"
)
xForwardedScheme
=
http
.
CanonicalHeaderKey
(
"X-Forwarded-Scheme"
)
xRealIP
=
http
.
CanonicalHeaderKey
(
"X-Real-IP"
)
)
var
(
// RFC7239 defines a new "Forwarded: " header designed to replace the
// existing use of X-Forwarded-* headers.
// e.g. Forwarded: for=192.0.2.60;proto=https;by=203.0.113.43
forwarded
=
http
.
CanonicalHeaderKey
(
"Forwarded"
)
// Allows for a sub-match of the first value after 'for=' to the next
// comma, semi-colon or space. The match is case-insensitive.
forRegex
=
regexp
.
MustCompile
(
`(?i)(?:for=)([^(;|,| )]+)`
)
// Allows for a sub-match for the first instance of scheme (http|https)
// prefixed by 'proto='. The match is case-insensitive.
protoRegex
=
regexp
.
MustCompile
(
`(?i)(?:proto=)(https|http)`
)
)
// ProxyHeaders inspects common reverse proxy headers and sets the corresponding
// fields in the HTTP request struct. These are X-Forwarded-For and X-Real-IP
// for the remote (client) IP address, X-Forwarded-Proto or X-Forwarded-Scheme
// for the scheme (http|https) and the RFC7239 Forwarded header, which may
// include both client IPs and schemes.
//
// NOTE: This middleware should only be used when behind a reverse
// proxy like nginx, HAProxy or Apache. Reverse proxies that don't (or are
// configured not to) strip these headers from client requests, or where these
// headers are accepted "as is" from a remote client (e.g. when Go is not behind
// a proxy), can manifest as a vulnerability if your application uses these
// headers for validating the 'trustworthiness' of a request.
func
ProxyHeaders
(
h
http
.
Handler
)
http
.
Handler
{
fn
:=
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
// Set the remote IP with the value passed from the proxy.
if
fwd
:=
getIP
(
r
);
fwd
!=
""
{
r
.
RemoteAddr
=
fwd
}
// Set the scheme (proto) with the value passed from the proxy.
if
scheme
:=
getScheme
(
r
);
scheme
!=
""
{
r
.
URL
.
Scheme
=
scheme
}
// Set the host with the value passed by the proxy
if
r
.
Header
.
Get
(
xForwardedHost
)
!=
""
{
r
.
Host
=
r
.
Header
.
Get
(
xForwardedHost
)
}
// Call the next handler in the chain.
h
.
ServeHTTP
(
w
,
r
)
}
return
http
.
HandlerFunc
(
fn
)
}
// getIP retrieves the IP from the X-Forwarded-For, X-Real-IP and RFC7239
// Forwarded headers (in that order).
func
getIP
(
r
*
http
.
Request
)
string
{
var
addr
string
if
fwd
:=
r
.
Header
.
Get
(
xForwardedFor
);
fwd
!=
""
{
// Only grab the first (client) address. Note that '192.168.0.1,
// 10.1.1.1' is a valid key for X-Forwarded-For where addresses after
// the first may represent forwarding proxies earlier in the chain.
s
:=
strings
.
Index
(
fwd
,
", "
)
if
s
==
-
1
{
s
=
len
(
fwd
)
}
addr
=
fwd
[
:
s
]
}
else
if
fwd
:=
r
.
Header
.
Get
(
xRealIP
);
fwd
!=
""
{
// X-Real-IP should only contain one IP address (the client making the
// request).
addr
=
fwd
}
else
if
fwd
:=
r
.
Header
.
Get
(
forwarded
);
fwd
!=
""
{
// match should contain at least two elements if the protocol was
// specified in the Forwarded header. The first element will always be
// the 'for=' capture, which we ignore. In the case of multiple IP
// addresses (for=8.8.8.8, 8.8.4.4,172.16.1.20 is valid) we only
// extract the first, which should be the client IP.
if
match
:=
forRegex
.
FindStringSubmatch
(
fwd
);
len
(
match
)
>
1
{
// IPv6 addresses in Forwarded headers are quoted-strings. We strip
// these quotes.
addr
=
strings
.
Trim
(
match
[
1
],
`"`
)
}
}
return
addr
}
// getScheme retrieves the scheme from the X-Forwarded-Proto and RFC7239
// Forwarded headers (in that order).
func
getScheme
(
r
*
http
.
Request
)
string
{
var
scheme
string
// Retrieve the scheme from X-Forwarded-Proto.
if
proto
:=
r
.
Header
.
Get
(
xForwardedProto
);
proto
!=
""
{
scheme
=
strings
.
ToLower
(
proto
)
}
else
if
proto
=
r
.
Header
.
Get
(
xForwardedScheme
);
proto
!=
""
{
scheme
=
strings
.
ToLower
(
proto
)
}
else
if
proto
=
r
.
Header
.
Get
(
forwarded
);
proto
!=
""
{
// match should contain at least two elements if the protocol was
// specified in the Forwarded header. The first element will always be
// the 'proto=' capture, which we ignore. In the case of multiple proto
// parameters (invalid) we only extract the first.
if
match
:=
protoRegex
.
FindStringSubmatch
(
proto
);
len
(
match
)
>
1
{
scheme
=
strings
.
ToLower
(
match
[
1
])
}
}
return
scheme
}
vendor/github.com/gorilla/handlers/recovery.go
0 → 100644
View file @
ec9d1607
package
handlers
import
(
"log"
"net/http"
"runtime/debug"
)
// RecoveryHandlerLogger is an interface used by the recovering handler to print logs.
type
RecoveryHandlerLogger
interface
{
Println
(
...
interface
{})
}
type
recoveryHandler
struct
{
handler
http
.
Handler
logger
RecoveryHandlerLogger
printStack
bool
}
// RecoveryOption provides a functional approach to define
// configuration for a handler; such as setting the logging
// whether or not to print strack traces on panic.
type
RecoveryOption
func
(
http
.
Handler
)
func
parseRecoveryOptions
(
h
http
.
Handler
,
opts
...
RecoveryOption
)
http
.
Handler
{
for
_
,
option
:=
range
opts
{
option
(
h
)
}
return
h
}
// RecoveryHandler is HTTP middleware that recovers from a panic,
// logs the panic, writes http.StatusInternalServerError, and
// continues to the next handler.
//
// Example:
//
// r := mux.NewRouter()
// r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// panic("Unexpected error!")
// })
//
// http.ListenAndServe(":1123", handlers.RecoveryHandler()(r))
func
RecoveryHandler
(
opts
...
RecoveryOption
)
func
(
h
http
.
Handler
)
http
.
Handler
{
return
func
(
h
http
.
Handler
)
http
.
Handler
{
r
:=
&
recoveryHandler
{
handler
:
h
}
return
parseRecoveryOptions
(
r
,
opts
...
)
}
}
// RecoveryLogger is a functional option to override
// the default logger
func
RecoveryLogger
(
logger
RecoveryHandlerLogger
)
RecoveryOption
{
return
func
(
h
http
.
Handler
)
{
r
:=
h
.
(
*
recoveryHandler
)
r
.
logger
=
logger
}
}
// PrintRecoveryStack is a functional option to enable
// or disable printing stack traces on panic.
func
PrintRecoveryStack
(
print
bool
)
RecoveryOption
{
return
func
(
h
http
.
Handler
)
{
r
:=
h
.
(
*
recoveryHandler
)
r
.
printStack
=
print
}
}
func
(
h
recoveryHandler
)
ServeHTTP
(
w
http
.
ResponseWriter
,
req
*
http
.
Request
)
{
defer
func
()
{
if
err
:=
recover
();
err
!=
nil
{
w
.
WriteHeader
(
http
.
StatusInternalServerError
)
h
.
log
(
err
)
}
}()
h
.
handler
.
ServeHTTP
(
w
,
req
)
}
func
(
h
recoveryHandler
)
log
(
v
...
interface
{})
{
if
h
.
logger
!=
nil
{
h
.
logger
.
Println
(
v
...
)
}
else
{
log
.
Println
(
v
...
)
}
if
h
.
printStack
{
debug
.
PrintStack
()
}
}
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