Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
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
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
2fcf1575
Commit
2fcf1575
authored
Dec 02, 2015
by
Nikhil Jindal
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #17990 from nikhiljindal/updateGoRestful
Updating go restful
parents
2670226a
8e6059ab
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
673 additions
and
171 deletions
+673
-171
Godeps.json
Godeps/Godeps.json
+2
-2
CHANGES.md
.../_workspace/src/github.com/emicklei/go-restful/CHANGES.md
+10
-0
README.md
...s/_workspace/src/github.com/emicklei/go-restful/README.md
+2
-0
compress.go
..._workspace/src/github.com/emicklei/go-restful/compress.go
+26
-3
compressor_cache.go
...ce/src/github.com/emicklei/go-restful/compressor_cache.go
+103
-0
compressor_pools.go
...ce/src/github.com/emicklei/go-restful/compressor_pools.go
+51
-23
compressors.go
...rkspace/src/github.com/emicklei/go-restful/compressors.go
+53
-0
container.go
...workspace/src/github.com/emicklei/go-restful/container.go
+1
-1
doc.go
Godeps/_workspace/src/github.com/emicklei/go-restful/doc.go
+5
-0
entity_accessors.go
...ce/src/github.com/emicklei/go-restful/entity_accessors.go
+151
-0
app.yaml
.../go-restful/examples/google_app_engine/datastore/app.yaml
+1
-1
main.go
...i/go-restful/examples/google_app_engine/datastore/main.go
+3
-3
restful-user-service.go
...estful/examples/google_app_engine/restful-user-service.go
+2
-2
restful-CORS-filter.go
...b.com/emicklei/go-restful/examples/restful-CORS-filter.go
+1
-1
restful-user-resource.go
...com/emicklei/go-restful/examples/restful-user-resource.go
+1
-2
restful-user-service.go
....com/emicklei/go-restful/examples/restful-user-service.go
+1
-2
log.go
.../_workspace/src/github.com/emicklei/go-restful/log/log.go
+1
-1
options_filter.go
...pace/src/github.com/emicklei/go-restful/options_filter.go
+3
-1
parameter.go
...workspace/src/github.com/emicklei/go-restful/parameter.go
+12
-6
request.go
.../_workspace/src/github.com/emicklei/go-restful/request.go
+21
-45
response.go
..._workspace/src/github.com/emicklei/go-restful/response.go
+0
-0
CHANGES.md
...ace/src/github.com/emicklei/go-restful/swagger/CHANGES.md
+4
-0
config.go
...pace/src/github.com/emicklei/go-restful/swagger/config.go
+2
-0
model_builder.go
...c/github.com/emicklei/go-restful/swagger/model_builder.go
+22
-6
model_property_ext.go
...hub.com/emicklei/go-restful/swagger/model_property_ext.go
+7
-0
swagger.go
...ace/src/github.com/emicklei/go-restful/swagger/swagger.go
+2
-2
swagger_builder.go
...github.com/emicklei/go-restful/swagger/swagger_builder.go
+21
-0
swagger_webservice.go
...hub.com/emicklei/go-restful/swagger/swagger_webservice.go
+100
-48
struct.go
...ub.com/emicklei/go-restful/swagger/test_package/struct.go
+5
-0
web_service.go
...rkspace/src/github.com/emicklei/go-restful/web_service.go
+39
-1
v1.json
api/swagger-spec/v1.json
+4
-4
operations.html
docs/api-reference/v1/operations.html
+17
-17
No files found.
Godeps/Godeps.json
View file @
2fcf1575
...
@@ -378,8 +378,8 @@
...
@@ -378,8 +378,8 @@
},
},
{
{
"ImportPath"
:
"github.com/emicklei/go-restful"
,
"ImportPath"
:
"github.com/emicklei/go-restful"
,
"Comment"
:
"v1.
1.3-98-g1f9a0ee
"
,
"Comment"
:
"v1.
2
"
,
"Rev"
:
"
1f9a0ee00ff93717a275e15b30cf7df356255877
"
"Rev"
:
"
777bb3f19bcafe2575ffb2a3e46af92509ae9594
"
},
},
{
{
"ImportPath"
:
"github.com/evanphx/json-patch"
,
"ImportPath"
:
"github.com/evanphx/json-patch"
,
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/CHANGES.md
View file @
2fcf1575
Change history of go-restful
Change history of go-restful
=
=
2015-09-27
-
rename new WriteStatusAnd... to WriteHeaderAnd... for consistency
2015-09-25
-
fixed problem with changing Header after WriteHeader (issue 235)
2015-09-14
-
changed behavior of WriteHeader (immediate write) and WriteEntity (no status write)
-
added support for custom EntityReaderWriters.
2015-08-06
2015-08-06
-
add support for reading entities from compressed request content
-
add support for reading entities from compressed request content
-
use sync.Pool for compressors of http response and request body
-
use sync.Pool for compressors of http response and request body
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/README.md
View file @
2fcf1575
...
@@ -54,6 +54,8 @@ func (u UserResource) findUser(request *restful.Request, response *restful.Respo
...
@@ -54,6 +54,8 @@ func (u UserResource) findUser(request *restful.Request, response *restful.Respo
-
Panic recovery to produce HTTP 500, customizable using RecoverHandler(...)
-
Panic recovery to produce HTTP 500, customizable using RecoverHandler(...)
-
Route errors produce HTTP 404/405/406/415 errors, customizable using ServiceErrorHandler(...)
-
Route errors produce HTTP 404/405/406/415 errors, customizable using ServiceErrorHandler(...)
-
Configurable (trace) logging
-
Configurable (trace) logging
-
Customizable encoding using EntityReaderWriter registration
-
Customizable gzip/deflate readers and writers using CompressorProvider registration
### Resources
### Resources
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/compress.go
View file @
2fcf1575
...
@@ -20,6 +20,7 @@ var EnableContentEncoding = false
...
@@ -20,6 +20,7 @@ var EnableContentEncoding = false
type
CompressingResponseWriter
struct
{
type
CompressingResponseWriter
struct
{
writer
http
.
ResponseWriter
writer
http
.
ResponseWriter
compressor
io
.
WriteCloser
compressor
io
.
WriteCloser
encoding
string
}
}
// Header is part of http.ResponseWriter interface
// Header is part of http.ResponseWriter interface
...
@@ -35,6 +36,9 @@ func (c *CompressingResponseWriter) WriteHeader(status int) {
...
@@ -35,6 +36,9 @@ func (c *CompressingResponseWriter) WriteHeader(status int) {
// Write is part of http.ResponseWriter interface
// Write is part of http.ResponseWriter interface
// It is passed through the compressor
// It is passed through the compressor
func
(
c
*
CompressingResponseWriter
)
Write
(
bytes
[]
byte
)
(
int
,
error
)
{
func
(
c
*
CompressingResponseWriter
)
Write
(
bytes
[]
byte
)
(
int
,
error
)
{
if
c
.
isCompressorClosed
()
{
return
-
1
,
errors
.
New
(
"Compressing error: tried to write data using closed compressor"
)
}
return
c
.
compressor
.
Write
(
bytes
)
return
c
.
compressor
.
Write
(
bytes
)
}
}
...
@@ -44,8 +48,25 @@ func (c *CompressingResponseWriter) CloseNotify() <-chan bool {
...
@@ -44,8 +48,25 @@ func (c *CompressingResponseWriter) CloseNotify() <-chan bool {
}
}
// Close the underlying compressor
// Close the underlying compressor
func
(
c
*
CompressingResponseWriter
)
Close
()
{
func
(
c
*
CompressingResponseWriter
)
Close
()
error
{
if
c
.
isCompressorClosed
()
{
return
errors
.
New
(
"Compressing error: tried to close already closed compressor"
)
}
c
.
compressor
.
Close
()
c
.
compressor
.
Close
()
if
ENCODING_GZIP
==
c
.
encoding
{
currentCompressorProvider
.
ReleaseGzipWriter
(
c
.
compressor
.
(
*
gzip
.
Writer
))
}
if
ENCODING_DEFLATE
==
c
.
encoding
{
currentCompressorProvider
.
ReleaseZlibWriter
(
c
.
compressor
.
(
*
zlib
.
Writer
))
}
// gc hint needed?
c
.
compressor
=
nil
return
nil
}
func
(
c
*
CompressingResponseWriter
)
isCompressorClosed
()
bool
{
return
nil
==
c
.
compressor
}
}
// WantsCompressedResponse reads the Accept-Encoding header to see if and which encoding is requested.
// WantsCompressedResponse reads the Accept-Encoding header to see if and which encoding is requested.
...
@@ -73,13 +94,15 @@ func NewCompressingResponseWriter(httpWriter http.ResponseWriter, encoding strin
...
@@ -73,13 +94,15 @@ func NewCompressingResponseWriter(httpWriter http.ResponseWriter, encoding strin
c
.
writer
=
httpWriter
c
.
writer
=
httpWriter
var
err
error
var
err
error
if
ENCODING_GZIP
==
encoding
{
if
ENCODING_GZIP
==
encoding
{
w
:=
GzipWriterPool
.
Get
()
.
(
*
gzip
.
Writer
)
w
:=
currentCompressorProvider
.
AcquireGzipWriter
(
)
w
.
Reset
(
httpWriter
)
w
.
Reset
(
httpWriter
)
c
.
compressor
=
w
c
.
compressor
=
w
c
.
encoding
=
ENCODING_GZIP
}
else
if
ENCODING_DEFLATE
==
encoding
{
}
else
if
ENCODING_DEFLATE
==
encoding
{
w
:=
ZlibWriterPool
.
Get
()
.
(
*
zlib
.
Writer
)
w
:=
currentCompressorProvider
.
AcquireZlibWriter
(
)
w
.
Reset
(
httpWriter
)
w
.
Reset
(
httpWriter
)
c
.
compressor
=
w
c
.
compressor
=
w
c
.
encoding
=
ENCODING_DEFLATE
}
else
{
}
else
{
return
nil
,
errors
.
New
(
"Unknown encoding:"
+
encoding
)
return
nil
,
errors
.
New
(
"Unknown encoding:"
+
encoding
)
}
}
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/compressor_cache.go
0 → 100644
View file @
2fcf1575
package
restful
// Copyright 2015 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import
(
"compress/gzip"
"compress/zlib"
)
// BoundedCachedCompressors is a CompressorProvider that uses a cache with a fixed amount
// of writers and readers (resources).
// If a new resource is acquired and all are in use, it will return a new unmanaged resource.
type
BoundedCachedCompressors
struct
{
gzipWriters
chan
*
gzip
.
Writer
gzipReaders
chan
*
gzip
.
Reader
zlibWriters
chan
*
zlib
.
Writer
writersCapacity
int
readersCapacity
int
}
// NewBoundedCachedCompressors returns a new, with filled cache, BoundedCachedCompressors.
func
NewBoundedCachedCompressors
(
writersCapacity
,
readersCapacity
int
)
*
BoundedCachedCompressors
{
b
:=
&
BoundedCachedCompressors
{
gzipWriters
:
make
(
chan
*
gzip
.
Writer
,
writersCapacity
),
gzipReaders
:
make
(
chan
*
gzip
.
Reader
,
readersCapacity
),
zlibWriters
:
make
(
chan
*
zlib
.
Writer
,
writersCapacity
),
writersCapacity
:
writersCapacity
,
readersCapacity
:
readersCapacity
,
}
for
ix
:=
0
;
ix
<
writersCapacity
;
ix
++
{
b
.
gzipWriters
<-
newGzipWriter
()
b
.
zlibWriters
<-
newZlibWriter
()
}
for
ix
:=
0
;
ix
<
readersCapacity
;
ix
++
{
b
.
gzipReaders
<-
newGzipReader
()
}
return
b
}
// AcquireGzipWriter returns an resettable *gzip.Writer. Needs to be released.
func
(
b
*
BoundedCachedCompressors
)
AcquireGzipWriter
()
*
gzip
.
Writer
{
var
writer
*
gzip
.
Writer
select
{
case
writer
,
_
=
<-
b
.
gzipWriters
:
default
:
// return a new unmanaged one
writer
=
newGzipWriter
()
}
return
writer
}
// ReleaseGzipWriter accepts a writer (does not have to be one that was cached)
// only when the cache has room for it. It will ignore it otherwise.
func
(
b
*
BoundedCachedCompressors
)
ReleaseGzipWriter
(
w
*
gzip
.
Writer
)
{
// forget the unmanaged ones
if
len
(
b
.
gzipWriters
)
<
b
.
writersCapacity
{
b
.
gzipWriters
<-
w
}
}
// AcquireGzipReader returns a *gzip.Reader. Needs to be released.
func
(
b
*
BoundedCachedCompressors
)
AcquireGzipReader
()
*
gzip
.
Reader
{
var
reader
*
gzip
.
Reader
select
{
case
reader
,
_
=
<-
b
.
gzipReaders
:
default
:
// return a new unmanaged one
reader
=
newGzipReader
()
}
return
reader
}
// ReleaseGzipReader accepts a reader (does not have to be one that was cached)
// only when the cache has room for it. It will ignore it otherwise.
func
(
b
*
BoundedCachedCompressors
)
ReleaseGzipReader
(
r
*
gzip
.
Reader
)
{
// forget the unmanaged ones
if
len
(
b
.
gzipReaders
)
<
b
.
readersCapacity
{
b
.
gzipReaders
<-
r
}
}
// AcquireZlibWriter returns an resettable *zlib.Writer. Needs to be released.
func
(
b
*
BoundedCachedCompressors
)
AcquireZlibWriter
()
*
zlib
.
Writer
{
var
writer
*
zlib
.
Writer
select
{
case
writer
,
_
=
<-
b
.
zlibWriters
:
default
:
// return a new unmanaged one
writer
=
newZlibWriter
()
}
return
writer
}
// ReleaseZlibWriter accepts a writer (does not have to be one that was cached)
// only when the cache has room for it. It will ignore it otherwise.
func
(
b
*
BoundedCachedCompressors
)
ReleaseZlibWriter
(
w
*
zlib
.
Writer
)
{
// forget the unmanaged ones
if
len
(
b
.
zlibWriters
)
<
b
.
writersCapacity
{
b
.
zlibWriters
<-
w
}
}
Godeps/_workspace/src/github.com/emicklei/go-restful/compressor_pools.go
View file @
2fcf1575
package
restful
package
restful
// Copyright 2015 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import
(
import
(
"bytes"
"bytes"
"compress/gzip"
"compress/gzip"
...
@@ -7,12 +11,50 @@ import (
...
@@ -7,12 +11,50 @@ import (
"sync"
"sync"
)
)
// GzipWriterPool is used to get reusable zippers.
// SyncPoolCompessors is a CompressorProvider that use the standard sync.Pool.
// The Get() result must be type asserted to *gzip.Writer.
type
SyncPoolCompessors
struct
{
var
GzipWriterPool
=
&
sync
.
Pool
{
GzipWriterPool
*
sync
.
Pool
New
:
func
()
interface
{}
{
GzipReaderPool
*
sync
.
Pool
return
newGzipWriter
()
ZlibWriterPool
*
sync
.
Pool
},
}
// NewSyncPoolCompessors returns a new ("empty") SyncPoolCompessors.
func
NewSyncPoolCompessors
()
*
SyncPoolCompessors
{
return
&
SyncPoolCompessors
{
GzipWriterPool
:
&
sync
.
Pool
{
New
:
func
()
interface
{}
{
return
newGzipWriter
()
},
},
GzipReaderPool
:
&
sync
.
Pool
{
New
:
func
()
interface
{}
{
return
newGzipReader
()
},
},
ZlibWriterPool
:
&
sync
.
Pool
{
New
:
func
()
interface
{}
{
return
newZlibWriter
()
},
},
}
}
func
(
s
*
SyncPoolCompessors
)
AcquireGzipWriter
()
*
gzip
.
Writer
{
return
s
.
GzipWriterPool
.
Get
()
.
(
*
gzip
.
Writer
)
}
func
(
s
*
SyncPoolCompessors
)
ReleaseGzipWriter
(
w
*
gzip
.
Writer
)
{
s
.
GzipWriterPool
.
Put
(
w
)
}
func
(
s
*
SyncPoolCompessors
)
AcquireGzipReader
()
*
gzip
.
Reader
{
return
s
.
GzipReaderPool
.
Get
()
.
(
*
gzip
.
Reader
)
}
func
(
s
*
SyncPoolCompessors
)
ReleaseGzipReader
(
r
*
gzip
.
Reader
)
{
s
.
GzipReaderPool
.
Put
(
r
)
}
func
(
s
*
SyncPoolCompessors
)
AcquireZlibWriter
()
*
zlib
.
Writer
{
return
s
.
ZlibWriterPool
.
Get
()
.
(
*
zlib
.
Writer
)
}
func
(
s
*
SyncPoolCompessors
)
ReleaseZlibWriter
(
w
*
zlib
.
Writer
)
{
s
.
ZlibWriterPool
.
Put
(
w
)
}
}
func
newGzipWriter
()
*
gzip
.
Writer
{
func
newGzipWriter
()
*
gzip
.
Writer
{
...
@@ -24,17 +66,11 @@ func newGzipWriter() *gzip.Writer {
...
@@ -24,17 +66,11 @@ func newGzipWriter() *gzip.Writer {
return
writer
return
writer
}
}
// GzipReaderPool is used to get reusable zippers.
// The Get() result must be type asserted to *gzip.Reader.
var
GzipReaderPool
=
&
sync
.
Pool
{
New
:
func
()
interface
{}
{
return
newGzipReader
()
},
}
func
newGzipReader
()
*
gzip
.
Reader
{
func
newGzipReader
()
*
gzip
.
Reader
{
// create with an empty reader (but with GZIP header); it will be replaced before using the gzipReader
// create with an empty reader (but with GZIP header); it will be replaced before using the gzipReader
w
:=
GzipWriterPool
.
Get
()
.
(
*
gzip
.
Writer
)
// we can safely use currentCompressProvider because it is set on package initialization.
w
:=
currentCompressorProvider
.
AcquireGzipWriter
()
defer
currentCompressorProvider
.
ReleaseGzipWriter
(
w
)
b
:=
new
(
bytes
.
Buffer
)
b
:=
new
(
bytes
.
Buffer
)
w
.
Reset
(
b
)
w
.
Reset
(
b
)
w
.
Flush
()
w
.
Flush
()
...
@@ -46,14 +82,6 @@ func newGzipReader() *gzip.Reader {
...
@@ -46,14 +82,6 @@ func newGzipReader() *gzip.Reader {
return
reader
return
reader
}
}
// ZlibWriterPool is used to get reusable zippers.
// The Get() result must be type asserted to *zlib.Writer.
var
ZlibWriterPool
=
&
sync
.
Pool
{
New
:
func
()
interface
{}
{
return
newZlibWriter
()
},
}
func
newZlibWriter
()
*
zlib
.
Writer
{
func
newZlibWriter
()
*
zlib
.
Writer
{
writer
,
err
:=
zlib
.
NewWriterLevel
(
new
(
bytes
.
Buffer
),
gzip
.
BestSpeed
)
writer
,
err
:=
zlib
.
NewWriterLevel
(
new
(
bytes
.
Buffer
),
gzip
.
BestSpeed
)
if
err
!=
nil
{
if
err
!=
nil
{
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/compressors.go
0 → 100644
View file @
2fcf1575
package
restful
// Copyright 2015 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import
(
"compress/gzip"
"compress/zlib"
)
type
CompressorProvider
interface
{
// Returns a *gzip.Writer which needs to be released later.
// Before using it, call Reset().
AcquireGzipWriter
()
*
gzip
.
Writer
// Releases an aqcuired *gzip.Writer.
ReleaseGzipWriter
(
w
*
gzip
.
Writer
)
// Returns a *gzip.Reader which needs to be released later.
AcquireGzipReader
()
*
gzip
.
Reader
// Releases an aqcuired *gzip.Reader.
ReleaseGzipReader
(
w
*
gzip
.
Reader
)
// Returns a *zlib.Writer which needs to be released later.
// Before using it, call Reset().
AcquireZlibWriter
()
*
zlib
.
Writer
// Releases an aqcuired *zlib.Writer.
ReleaseZlibWriter
(
w
*
zlib
.
Writer
)
}
// DefaultCompressorProvider is the actual provider of compressors (zlib or gzip).
var
currentCompressorProvider
CompressorProvider
func
init
()
{
currentCompressorProvider
=
NewSyncPoolCompessors
()
}
// CurrentCompressorProvider returns the current CompressorProvider.
// It is initialized using a SyncPoolCompessors.
func
CurrentCompressorProvider
()
CompressorProvider
{
return
currentCompressorProvider
}
// CompressorProvider sets the actual provider of compressors (zlib or gzip).
func
SetCompressorProvider
(
p
CompressorProvider
)
{
if
p
==
nil
{
panic
(
"cannot set compressor provider to nil"
)
}
currentCompressorProvider
=
p
}
Godeps/_workspace/src/github.com/emicklei/go-restful/container.go
View file @
2fcf1575
...
@@ -272,7 +272,7 @@ func (c Container) Handle(pattern string, handler http.Handler) {
...
@@ -272,7 +272,7 @@ func (c Container) Handle(pattern string, handler http.Handler) {
// HandleWithFilter registers the handler for the given pattern.
// HandleWithFilter registers the handler for the given pattern.
// Container's filter chain is applied for handler.
// Container's filter chain is applied for handler.
// If a handler already exists for pattern, HandleWithFilter panics.
// If a handler already exists for pattern, HandleWithFilter panics.
func
(
c
Container
)
HandleWithFilter
(
pattern
string
,
handler
http
.
Handler
)
{
func
(
c
*
Container
)
HandleWithFilter
(
pattern
string
,
handler
http
.
Handler
)
{
f
:=
func
(
httpResponse
http
.
ResponseWriter
,
httpRequest
*
http
.
Request
)
{
f
:=
func
(
httpResponse
http
.
ResponseWriter
,
httpRequest
*
http
.
Request
)
{
if
len
(
c
.
containerFilters
)
==
0
{
if
len
(
c
.
containerFilters
)
==
0
{
handler
.
ServeHTTP
(
httpResponse
,
httpRequest
)
handler
.
ServeHTTP
(
httpResponse
,
httpRequest
)
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/doc.go
View file @
2fcf1575
...
@@ -162,6 +162,11 @@ Default value is false; it will recover from panics. This has performance implic
...
@@ -162,6 +162,11 @@ Default value is false; it will recover from panics. This has performance implic
SetCacheReadEntity controls whether the response data ([]byte) is cached such that ReadEntity is repeatable.
SetCacheReadEntity controls whether the response data ([]byte) is cached such that ReadEntity is repeatable.
If you expect to read large amounts of payload data, and you do not use this feature, you should set it to false.
If you expect to read large amounts of payload data, and you do not use this feature, you should set it to false.
restful.SetCompressorProvider(NewBoundedCachedCompressors(20, 20))
If content encoding is enabled then the default strategy for getting new gzip/zlib writers and readers is to use a sync.Pool.
Because writers are expensive structures, performance is even more improved when using a preloaded cache. You can also inject your own implementation.
Trouble shooting
Trouble shooting
This package has the means to produce detail logging of the complete Http request matching process and filter invocation.
This package has the means to produce detail logging of the complete Http request matching process and filter invocation.
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/entity_accessors.go
0 → 100644
View file @
2fcf1575
package
restful
// Copyright 2015 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.
import
(
"encoding/json"
"encoding/xml"
"strings"
"sync"
)
// EntityReaderWriter can read and write values using an encoding such as JSON,XML.
type
EntityReaderWriter
interface
{
// Read a serialized version of the value from the request.
// The Request may have a decompressing reader. Depends on Content-Encoding.
Read
(
req
*
Request
,
v
interface
{})
error
// Write a serialized version of the value on the response.
// The Response may have a compressing writer. Depends on Accept-Encoding.
// status should be a valid Http Status code
Write
(
resp
*
Response
,
status
int
,
v
interface
{})
error
}
// entityAccessRegistry is a singleton
var
entityAccessRegistry
=
&
entityReaderWriters
{
protection
:
new
(
sync
.
RWMutex
),
accessors
:
map
[
string
]
EntityReaderWriter
{},
}
// entityReaderWriters associates MIME to an EntityReaderWriter
type
entityReaderWriters
struct
{
protection
*
sync
.
RWMutex
accessors
map
[
string
]
EntityReaderWriter
}
func
init
()
{
RegisterEntityAccessor
(
MIME_JSON
,
entityJSONAccess
{
ContentType
:
MIME_JSON
})
RegisterEntityAccessor
(
MIME_XML
,
entityXMLAccess
{
ContentType
:
MIME_XML
})
}
// RegisterEntityAccessor add/overrides the ReaderWriter for encoding content with this MIME type.
func
RegisterEntityAccessor
(
mime
string
,
erw
EntityReaderWriter
)
{
entityAccessRegistry
.
protection
.
Lock
()
defer
entityAccessRegistry
.
protection
.
Unlock
()
entityAccessRegistry
.
accessors
[
mime
]
=
erw
}
// AccessorAt returns the registered ReaderWriter for this MIME type.
func
(
r
*
entityReaderWriters
)
AccessorAt
(
mime
string
)
(
EntityReaderWriter
,
bool
)
{
r
.
protection
.
RLock
()
defer
r
.
protection
.
RUnlock
()
er
,
ok
:=
r
.
accessors
[
mime
]
if
!
ok
{
// retry with reverse lookup
// more expensive but we are in an exceptional situation anyway
for
k
,
v
:=
range
r
.
accessors
{
if
strings
.
Contains
(
mime
,
k
)
{
return
v
,
true
}
}
}
return
er
,
ok
}
// entityXMLAccess is a EntityReaderWriter for XML encoding
type
entityXMLAccess
struct
{
// This is used for setting the Content-Type header when writing
ContentType
string
}
// Read unmarshalls the value from XML
func
(
e
entityXMLAccess
)
Read
(
req
*
Request
,
v
interface
{})
error
{
return
xml
.
NewDecoder
(
req
.
Request
.
Body
)
.
Decode
(
v
)
}
// Write marshalls the value to JSON and set the Content-Type Header.
func
(
e
entityXMLAccess
)
Write
(
resp
*
Response
,
status
int
,
v
interface
{})
error
{
return
writeXML
(
resp
,
status
,
e
.
ContentType
,
v
)
}
// writeXML marshalls the value to JSON and set the Content-Type Header.
func
writeXML
(
resp
*
Response
,
status
int
,
contentType
string
,
v
interface
{})
error
{
if
v
==
nil
{
resp
.
WriteHeader
(
status
)
// do not write a nil representation
return
nil
}
if
resp
.
prettyPrint
{
// pretty output must be created and written explicitly
output
,
err
:=
xml
.
MarshalIndent
(
v
,
" "
,
" "
)
if
err
!=
nil
{
return
err
}
resp
.
Header
()
.
Set
(
HEADER_ContentType
,
contentType
)
resp
.
WriteHeader
(
status
)
_
,
err
=
resp
.
Write
([]
byte
(
xml
.
Header
))
if
err
!=
nil
{
return
err
}
_
,
err
=
resp
.
Write
(
output
)
return
err
}
// not-so-pretty
resp
.
Header
()
.
Set
(
HEADER_ContentType
,
contentType
)
resp
.
WriteHeader
(
status
)
return
xml
.
NewEncoder
(
resp
)
.
Encode
(
v
)
}
// entityJSONAccess is a EntityReaderWriter for JSON encoding
type
entityJSONAccess
struct
{
// This is used for setting the Content-Type header when writing
ContentType
string
}
// Read unmarshalls the value from JSON
func
(
e
entityJSONAccess
)
Read
(
req
*
Request
,
v
interface
{})
error
{
decoder
:=
json
.
NewDecoder
(
req
.
Request
.
Body
)
decoder
.
UseNumber
()
return
decoder
.
Decode
(
v
)
}
// Write marshalls the value to JSON and set the Content-Type Header.
func
(
e
entityJSONAccess
)
Write
(
resp
*
Response
,
status
int
,
v
interface
{})
error
{
return
writeJSON
(
resp
,
status
,
e
.
ContentType
,
v
)
}
// write marshalls the value to JSON and set the Content-Type Header.
func
writeJSON
(
resp
*
Response
,
status
int
,
contentType
string
,
v
interface
{})
error
{
if
v
==
nil
{
resp
.
WriteHeader
(
status
)
// do not write a nil representation
return
nil
}
if
resp
.
prettyPrint
{
// pretty output must be created and written explicitly
output
,
err
:=
json
.
MarshalIndent
(
v
,
" "
,
" "
)
if
err
!=
nil
{
return
err
}
resp
.
Header
()
.
Set
(
HEADER_ContentType
,
contentType
)
resp
.
WriteHeader
(
status
)
_
,
err
=
resp
.
Write
(
output
)
return
err
}
// not-so-pretty
resp
.
Header
()
.
Set
(
HEADER_ContentType
,
contentType
)
resp
.
WriteHeader
(
status
)
return
json
.
NewEncoder
(
resp
)
.
Encode
(
v
)
}
Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/app.yaml
View file @
2fcf1575
application
:
datastore-example
application
:
<your_app_id>
version
:
1
version
:
1
runtime
:
go
runtime
:
go
api_version
:
go1
api_version
:
go1
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/main.go
View file @
2fcf1575
package
main
package
main
import
(
import
(
"appengine"
"appengine/datastore"
"appengine/user"
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful/swagger"
"github.com/emicklei/go-restful/swagger"
"google.golang.com/appengine"
"google.golang.com/appengine/datastore"
"google.golang.com/appengine/user"
"net/http"
"net/http"
"time"
"time"
)
)
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/restful-user-service.go
View file @
2fcf1575
package
main
package
main
import
(
import
(
"appengine"
"appengine/memcache"
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful/swagger"
"github.com/emicklei/go-restful/swagger"
"google.golang.com/appengine"
"google.golang.com/appengine/memcache"
"net/http"
"net/http"
)
)
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-CORS-filter.go
View file @
2fcf1575
...
@@ -53,7 +53,7 @@ func main() {
...
@@ -53,7 +53,7 @@ func main() {
// Add container filter to enable CORS
// Add container filter to enable CORS
cors
:=
restful
.
CrossOriginResourceSharing
{
cors
:=
restful
.
CrossOriginResourceSharing
{
ExposeHeaders
:
[]
string
{
"X-My-Header"
},
ExposeHeaders
:
[]
string
{
"X-My-Header"
},
AllowedHeaders
:
[]
string
{
"Content-Type"
},
AllowedHeaders
:
[]
string
{
"Content-Type"
,
"Accept"
},
CookiesAllowed
:
false
,
CookiesAllowed
:
false
,
Container
:
wsContainer
}
Container
:
wsContainer
}
wsContainer
.
Filter
(
cors
.
Filter
)
wsContainer
.
Filter
(
cors
.
Filter
)
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-user-resource.go
View file @
2fcf1575
...
@@ -100,8 +100,7 @@ func (u *UserResource) createUser(request *restful.Request, response *restful.Re
...
@@ -100,8 +100,7 @@ func (u *UserResource) createUser(request *restful.Request, response *restful.Re
}
}
usr
.
Id
=
strconv
.
Itoa
(
len
(
u
.
users
)
+
1
)
// simple id generation
usr
.
Id
=
strconv
.
Itoa
(
len
(
u
.
users
)
+
1
)
// simple id generation
u
.
users
[
usr
.
Id
]
=
*
usr
u
.
users
[
usr
.
Id
]
=
*
usr
response
.
WriteHeader
(
http
.
StatusCreated
)
response
.
WriteHeaderAndEntity
(
http
.
StatusCreated
,
usr
)
response
.
WriteEntity
(
usr
)
}
}
// PUT http://localhost:8080/users/1
// PUT http://localhost:8080/users/1
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-user-service.go
View file @
2fcf1575
...
@@ -102,8 +102,7 @@ func (u *UserService) createUser(request *restful.Request, response *restful.Res
...
@@ -102,8 +102,7 @@ func (u *UserService) createUser(request *restful.Request, response *restful.Res
err
:=
request
.
ReadEntity
(
&
usr
)
err
:=
request
.
ReadEntity
(
&
usr
)
if
err
==
nil
{
if
err
==
nil
{
u
.
users
[
usr
.
Id
]
=
usr
u
.
users
[
usr
.
Id
]
=
usr
response
.
WriteHeader
(
http
.
StatusCreated
)
response
.
WriteHeaderAndEntity
(
http
.
StatusCreated
,
usr
)
response
.
WriteEntity
(
usr
)
}
else
{
}
else
{
response
.
WriteError
(
http
.
StatusInternalServerError
,
err
)
response
.
WriteError
(
http
.
StatusInternalServerError
,
err
)
}
}
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/log/log.go
View file @
2fcf1575
...
@@ -15,7 +15,7 @@ var Logger StdLogger
...
@@ -15,7 +15,7 @@ var Logger StdLogger
func
init
()
{
func
init
()
{
// default Logger
// default Logger
SetLogger
(
stdlog
.
New
(
os
.
Std
out
,
"[restful] "
,
stdlog
.
LstdFlags
|
stdlog
.
Lshortfile
))
SetLogger
(
stdlog
.
New
(
os
.
Std
err
,
"[restful] "
,
stdlog
.
LstdFlags
|
stdlog
.
Lshortfile
))
}
}
func
SetLogger
(
customLogger
StdLogger
)
{
func
SetLogger
(
customLogger
StdLogger
)
{
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/options_filter.go
View file @
2fcf1575
...
@@ -8,7 +8,8 @@ import "strings"
...
@@ -8,7 +8,8 @@ import "strings"
// OPTIONSFilter is a filter function that inspects the Http Request for the OPTIONS method
// OPTIONSFilter is a filter function that inspects the Http Request for the OPTIONS method
// and provides the response with a set of allowed methods for the request URL Path.
// and provides the response with a set of allowed methods for the request URL Path.
// As for any filter, you can also install it for a particular WebService within a Container
// As for any filter, you can also install it for a particular WebService within a Container.
// Note: this filter is not needed when using CrossOriginResourceSharing (for CORS).
func
(
c
*
Container
)
OPTIONSFilter
(
req
*
Request
,
resp
*
Response
,
chain
*
FilterChain
)
{
func
(
c
*
Container
)
OPTIONSFilter
(
req
*
Request
,
resp
*
Response
,
chain
*
FilterChain
)
{
if
"OPTIONS"
!=
req
.
Request
.
Method
{
if
"OPTIONS"
!=
req
.
Request
.
Method
{
chain
.
ProcessFilter
(
req
,
resp
)
chain
.
ProcessFilter
(
req
,
resp
)
...
@@ -19,6 +20,7 @@ func (c *Container) OPTIONSFilter(req *Request, resp *Response, chain *FilterCha
...
@@ -19,6 +20,7 @@ func (c *Container) OPTIONSFilter(req *Request, resp *Response, chain *FilterCha
// OPTIONSFilter is a filter function that inspects the Http Request for the OPTIONS method
// OPTIONSFilter is a filter function that inspects the Http Request for the OPTIONS method
// and provides the response with a set of allowed methods for the request URL Path.
// and provides the response with a set of allowed methods for the request URL Path.
// Note: this filter is not needed when using CrossOriginResourceSharing (for CORS).
func
OPTIONSFilter
()
FilterFunction
{
func
OPTIONSFilter
()
FilterFunction
{
return
DefaultContainer
.
OPTIONSFilter
return
DefaultContainer
.
OPTIONSFilter
}
}
Godeps/_workspace/src/github.com/emicklei/go-restful/parameter.go
View file @
2fcf1575
...
@@ -30,12 +30,12 @@ type Parameter struct {
...
@@ -30,12 +30,12 @@ type Parameter struct {
// ParameterData represents the state of a Parameter.
// ParameterData represents the state of a Parameter.
// It is made public to make it accessible to e.g. the Swagger package.
// It is made public to make it accessible to e.g. the Swagger package.
type
ParameterData
struct
{
type
ParameterData
struct
{
Name
,
Description
,
DataType
string
Name
,
Description
,
DataType
,
DataFormat
string
Kind
int
Kind
int
Required
bool
Required
bool
AllowableValues
map
[
string
]
string
AllowableValues
map
[
string
]
string
AllowMultiple
bool
AllowMultiple
bool
DefaultValue
string
DefaultValue
string
}
}
// Data returns the state of the Parameter
// Data returns the state of the Parameter
...
@@ -95,6 +95,12 @@ func (p *Parameter) DataType(typeName string) *Parameter {
...
@@ -95,6 +95,12 @@ func (p *Parameter) DataType(typeName string) *Parameter {
return
p
return
p
}
}
// DataFormat sets the dataFormat field for Swagger UI
func
(
p
*
Parameter
)
DataFormat
(
formatName
string
)
*
Parameter
{
p
.
data
.
DataFormat
=
formatName
return
p
}
// DefaultValue sets the default value field and returns the receiver
// DefaultValue sets the default value field and returns the receiver
func
(
p
*
Parameter
)
DefaultValue
(
stringRepresentation
string
)
*
Parameter
{
func
(
p
*
Parameter
)
DefaultValue
(
stringRepresentation
string
)
*
Parameter
{
p
.
data
.
DefaultValue
=
stringRepresentation
p
.
data
.
DefaultValue
=
stringRepresentation
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/request.go
View file @
2fcf1575
...
@@ -6,14 +6,9 @@ package restful
...
@@ -6,14 +6,9 @@ package restful
import
(
import
(
"bytes"
"bytes"
"compress/gzip"
"compress/zlib"
"compress/zlib"
"encoding/json"
"encoding/xml"
"io"
"io/ioutil"
"io/ioutil"
"net/http"
"net/http"
"strings"
)
)
var
defaultRequestContentType
string
var
defaultRequestContentType
string
...
@@ -81,62 +76,43 @@ func (r *Request) HeaderParameter(name string) string {
...
@@ -81,62 +76,43 @@ func (r *Request) HeaderParameter(name string) string {
return
r
.
Request
.
Header
.
Get
(
name
)
return
r
.
Request
.
Header
.
Get
(
name
)
}
}
// ReadEntity checks the Accept header and reads the content into the entityPointer
// ReadEntity checks the Accept header and reads the content into the entityPointer.
// May be called multiple times in the request-response flow
func
(
r
*
Request
)
ReadEntity
(
entityPointer
interface
{})
(
err
error
)
{
func
(
r
*
Request
)
ReadEntity
(
entityPointer
interface
{})
(
err
error
)
{
defer
r
.
Request
.
Body
.
Close
()
contentType
:=
r
.
Request
.
Header
.
Get
(
HEADER_ContentType
)
contentType
:=
r
.
Request
.
Header
.
Get
(
HEADER_ContentType
)
contentEncoding
:=
r
.
Request
.
Header
.
Get
(
HEADER_ContentEncoding
)
contentEncoding
:=
r
.
Request
.
Header
.
Get
(
HEADER_ContentEncoding
)
if
doCacheReadEntityBytes
{
return
r
.
cachingReadEntity
(
contentType
,
contentEncoding
,
entityPointer
)
}
// unmarshall directly from request Body
return
r
.
decodeEntity
(
r
.
Request
.
Body
,
contentType
,
contentEncoding
,
entityPointer
)
}
func
(
r
*
Request
)
cachingReadEntity
(
contentType
string
,
contentEncoding
string
,
entityPointer
interface
{})
(
err
error
)
{
// OLD feature, cache the body for reads
var
buffer
[]
byte
if
doCacheReadEntityBytes
{
if
r
.
bodyContent
!
=
nil
{
if
r
.
bodyContent
=
=
nil
{
buffer
=
*
r
.
bodyContent
data
,
err
:=
ioutil
.
ReadAll
(
r
.
Request
.
Body
)
}
else
{
if
err
!=
nil
{
buffer
,
err
=
ioutil
.
ReadAll
(
r
.
Request
.
Body
)
return
err
if
err
!=
nil
{
}
r
eturn
err
r
.
bodyContent
=
&
data
}
}
r
.
bodyContent
=
&
buffer
r
.
Request
.
Body
=
ioutil
.
NopCloser
(
bytes
.
NewReader
(
*
r
.
bodyContent
))
}
}
return
r
.
decodeEntity
(
bytes
.
NewReader
(
buffer
),
contentType
,
contentEncoding
,
entityPointer
)
}
func
(
r
*
Request
)
decodeEntity
(
reader
io
.
Reader
,
contentType
string
,
contentEncoding
string
,
entityPointer
interface
{})
(
err
error
)
{
entityReader
:=
reader
// check if the request body needs decompression
// check if the request body needs decompression
if
ENCODING_GZIP
==
contentEncoding
{
if
ENCODING_GZIP
==
contentEncoding
{
gzipReader
:=
GzipReaderPool
.
Get
()
.
(
*
gzip
.
Reader
)
gzipReader
:=
currentCompressorProvider
.
AcquireGzipReader
()
gzipReader
.
Reset
(
reader
)
defer
currentCompressorProvider
.
ReleaseGzipReader
(
gzipReader
)
entityReader
=
gzipReader
gzipReader
.
Reset
(
r
.
Request
.
Body
)
r
.
Request
.
Body
=
gzipReader
}
else
if
ENCODING_DEFLATE
==
contentEncoding
{
}
else
if
ENCODING_DEFLATE
==
contentEncoding
{
zlibReader
,
err
:=
zlib
.
NewReader
(
r
eader
)
zlibReader
,
err
:=
zlib
.
NewReader
(
r
.
Request
.
Body
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
entityReader
=
zlibReader
r
.
Request
.
Body
=
zlibReader
}
}
// decode JSON
// lookup the EntityReader
if
strings
.
Contains
(
contentType
,
MIME_JSON
)
||
MIME_JSON
==
defaultRequestContentType
{
entityReader
,
ok
:=
entityAccessRegistry
.
AccessorAt
(
contentType
)
decoder
:=
json
.
NewDecoder
(
entityReader
)
if
!
ok
{
decoder
.
UseNumber
()
return
NewError
(
http
.
StatusBadRequest
,
"Unable to unmarshal content of type:"
+
contentType
)
return
decoder
.
Decode
(
entityPointer
)
}
}
return
entityReader
.
Read
(
r
,
entityPointer
)
// decode XML
if
strings
.
Contains
(
contentType
,
MIME_XML
)
||
MIME_XML
==
defaultRequestContentType
{
return
xml
.
NewDecoder
(
entityReader
)
.
Decode
(
entityPointer
)
}
return
NewError
(
http
.
StatusBadRequest
,
"Unable to unmarshal content of type:"
+
contentType
)
}
}
// SetAttribute adds or replaces the attribute with the given value.
// SetAttribute adds or replaces the attribute with the given value.
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/response.go
View file @
2fcf1575
This diff is collapsed.
Click to expand it.
Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/CHANGES.md
View file @
2fcf1575
Change history of swagger
Change history of swagger
=
=
2015-10-16
-
add type override mechanism for swagger models (MR 254, nathanejohnson)
-
replace uses of wildcard in generated apidocs (issue 251)
2015-05-25
2015-05-25
-
(api break) changed the type of Properties in Model
-
(api break) changed the type of Properties in Model
-
(api break) changed the type of Models in ApiDeclaration
-
(api break) changed the type of Models in ApiDeclaration
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/config.go
View file @
2fcf1575
...
@@ -29,4 +29,6 @@ type Config struct {
...
@@ -29,4 +29,6 @@ type Config struct {
ApiVersion
string
ApiVersion
string
// If set then call this handler after building the complete ApiDeclaration Map
// If set then call this handler after building the complete ApiDeclaration Map
PostBuildHandler
PostBuildDeclarationMapFunc
PostBuildHandler
PostBuildDeclarationMapFunc
// Swagger global info struct
Info
Info
}
}
Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_builder.go
View file @
2fcf1575
...
@@ -132,9 +132,11 @@ func (b modelBuilder) buildProperty(field reflect.StructField, model *Model, mod
...
@@ -132,9 +132,11 @@ func (b modelBuilder) buildProperty(field reflect.StructField, model *Model, mod
modelDescription
=
tag
modelDescription
=
tag
}
}
fieldType
:=
field
.
Type
prop
.
setPropertyMetadata
(
field
)
prop
.
setPropertyMetadata
(
field
)
if
prop
.
Type
!=
nil
{
return
jsonName
,
modelDescription
,
prop
}
fieldType
:=
field
.
Type
// check if type is doing its own marshalling
// check if type is doing its own marshalling
marshalerType
:=
reflect
.
TypeOf
((
*
json
.
Marshaler
)(
nil
))
.
Elem
()
marshalerType
:=
reflect
.
TypeOf
((
*
json
.
Marshaler
)(
nil
))
.
Elem
()
...
@@ -212,8 +214,12 @@ func hasNamedJSONTag(field reflect.StructField) bool {
...
@@ -212,8 +214,12 @@ func hasNamedJSONTag(field reflect.StructField) bool {
}
}
func
(
b
modelBuilder
)
buildStructTypeProperty
(
field
reflect
.
StructField
,
jsonName
string
,
model
*
Model
)
(
nameJson
string
,
prop
ModelProperty
)
{
func
(
b
modelBuilder
)
buildStructTypeProperty
(
field
reflect
.
StructField
,
jsonName
string
,
model
*
Model
)
(
nameJson
string
,
prop
ModelProperty
)
{
fieldType
:=
field
.
Type
prop
.
setPropertyMetadata
(
field
)
prop
.
setPropertyMetadata
(
field
)
// Check for type override in tag
if
prop
.
Type
!=
nil
{
return
jsonName
,
prop
}
fieldType
:=
field
.
Type
// check for anonymous
// check for anonymous
if
len
(
fieldType
.
Name
())
==
0
{
if
len
(
fieldType
.
Name
())
==
0
{
// anonymous
// anonymous
...
@@ -263,8 +269,12 @@ func (b modelBuilder) buildStructTypeProperty(field reflect.StructField, jsonNam
...
@@ -263,8 +269,12 @@ func (b modelBuilder) buildStructTypeProperty(field reflect.StructField, jsonNam
}
}
func
(
b
modelBuilder
)
buildArrayTypeProperty
(
field
reflect
.
StructField
,
jsonName
,
modelName
string
)
(
nameJson
string
,
prop
ModelProperty
)
{
func
(
b
modelBuilder
)
buildArrayTypeProperty
(
field
reflect
.
StructField
,
jsonName
,
modelName
string
)
(
nameJson
string
,
prop
ModelProperty
)
{
fieldType
:=
field
.
Type
// check for type override in tags
prop
.
setPropertyMetadata
(
field
)
prop
.
setPropertyMetadata
(
field
)
if
prop
.
Type
!=
nil
{
return
jsonName
,
prop
}
fieldType
:=
field
.
Type
var
pType
=
"array"
var
pType
=
"array"
prop
.
Type
=
&
pType
prop
.
Type
=
&
pType
elemTypeName
:=
b
.
getElementTypeName
(
modelName
,
jsonName
,
fieldType
.
Elem
())
elemTypeName
:=
b
.
getElementTypeName
(
modelName
,
jsonName
,
fieldType
.
Elem
())
...
@@ -284,8 +294,12 @@ func (b modelBuilder) buildArrayTypeProperty(field reflect.StructField, jsonName
...
@@ -284,8 +294,12 @@ func (b modelBuilder) buildArrayTypeProperty(field reflect.StructField, jsonName
}
}
func
(
b
modelBuilder
)
buildPointerTypeProperty
(
field
reflect
.
StructField
,
jsonName
,
modelName
string
)
(
nameJson
string
,
prop
ModelProperty
)
{
func
(
b
modelBuilder
)
buildPointerTypeProperty
(
field
reflect
.
StructField
,
jsonName
,
modelName
string
)
(
nameJson
string
,
prop
ModelProperty
)
{
fieldType
:=
field
.
Type
prop
.
setPropertyMetadata
(
field
)
prop
.
setPropertyMetadata
(
field
)
// Check for type override in tags
if
prop
.
Type
!=
nil
{
return
jsonName
,
prop
}
fieldType
:=
field
.
Type
// override type of pointer to list-likes
// override type of pointer to list-likes
if
fieldType
.
Elem
()
.
Kind
()
==
reflect
.
Slice
||
fieldType
.
Elem
()
.
Kind
()
==
reflect
.
Array
{
if
fieldType
.
Elem
()
.
Kind
()
==
reflect
.
Slice
||
fieldType
.
Elem
()
.
Kind
()
==
reflect
.
Array
{
...
@@ -338,7 +352,7 @@ func (b modelBuilder) keyFrom(st reflect.Type) string {
...
@@ -338,7 +352,7 @@ func (b modelBuilder) keyFrom(st reflect.Type) string {
// see also https://golang.org/ref/spec#Numeric_types
// see also https://golang.org/ref/spec#Numeric_types
func
(
b
modelBuilder
)
isPrimitiveType
(
modelName
string
)
bool
{
func
(
b
modelBuilder
)
isPrimitiveType
(
modelName
string
)
bool
{
return
strings
.
Contains
(
"uint8 uint16 uint32 uint64 int int8 int16 int32 int64 float32 float64 bool string byte rune time.Time"
,
modelName
)
return
strings
.
Contains
(
"uint
uint
8 uint16 uint32 uint64 int int8 int16 int32 int64 float32 float64 bool string byte rune time.Time"
,
modelName
)
}
}
// jsonNameOfField returns the name of the field as it should appear in JSON format
// jsonNameOfField returns the name of the field as it should appear in JSON format
...
@@ -359,6 +373,7 @@ func (b modelBuilder) jsonNameOfField(field reflect.StructField) string {
...
@@ -359,6 +373,7 @@ func (b modelBuilder) jsonNameOfField(field reflect.StructField) string {
// see also http://json-schema.org/latest/json-schema-core.html#anchor8
// see also http://json-schema.org/latest/json-schema-core.html#anchor8
func
(
b
modelBuilder
)
jsonSchemaType
(
modelName
string
)
string
{
func
(
b
modelBuilder
)
jsonSchemaType
(
modelName
string
)
string
{
schemaMap
:=
map
[
string
]
string
{
schemaMap
:=
map
[
string
]
string
{
"uint"
:
"integer"
,
"uint8"
:
"integer"
,
"uint8"
:
"integer"
,
"uint16"
:
"integer"
,
"uint16"
:
"integer"
,
"uint32"
:
"integer"
,
"uint32"
:
"integer"
,
...
@@ -389,6 +404,7 @@ func (b modelBuilder) jsonSchemaFormat(modelName string) string {
...
@@ -389,6 +404,7 @@ func (b modelBuilder) jsonSchemaFormat(modelName string) string {
"int32"
:
"int32"
,
"int32"
:
"int32"
,
"int64"
:
"int64"
,
"int64"
:
"int64"
,
"byte"
:
"byte"
,
"byte"
:
"byte"
,
"uint"
:
"integer"
,
"uint8"
:
"byte"
,
"uint8"
:
"byte"
,
"float64"
:
"double"
,
"float64"
:
"double"
,
"float32"
:
"float"
,
"float32"
:
"float"
,
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/model_property_ext.go
View file @
2fcf1575
...
@@ -31,6 +31,12 @@ func (prop *ModelProperty) setMaximum(field reflect.StructField) {
...
@@ -31,6 +31,12 @@ func (prop *ModelProperty) setMaximum(field reflect.StructField) {
}
}
}
}
func
(
prop
*
ModelProperty
)
setType
(
field
reflect
.
StructField
)
{
if
tag
:=
field
.
Tag
.
Get
(
"type"
);
tag
!=
""
{
prop
.
Type
=
&
tag
}
}
func
(
prop
*
ModelProperty
)
setMinimum
(
field
reflect
.
StructField
)
{
func
(
prop
*
ModelProperty
)
setMinimum
(
field
reflect
.
StructField
)
{
if
tag
:=
field
.
Tag
.
Get
(
"minimum"
);
tag
!=
""
{
if
tag
:=
field
.
Tag
.
Get
(
"minimum"
);
tag
!=
""
{
prop
.
Minimum
=
tag
prop
.
Minimum
=
tag
...
@@ -56,4 +62,5 @@ func (prop *ModelProperty) setPropertyMetadata(field reflect.StructField) {
...
@@ -56,4 +62,5 @@ func (prop *ModelProperty) setPropertyMetadata(field reflect.StructField) {
prop
.
setMaximum
(
field
)
prop
.
setMaximum
(
field
)
prop
.
setUniqueItems
(
field
)
prop
.
setUniqueItems
(
field
)
prop
.
setDefaultValue
(
field
)
prop
.
setDefaultValue
(
field
)
prop
.
setType
(
field
)
}
}
Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger.go
View file @
2fcf1575
...
@@ -48,7 +48,7 @@ type Info struct {
...
@@ -48,7 +48,7 @@ type Info struct {
TermsOfServiceUrl
string
`json:"termsOfServiceUrl,omitempty"`
TermsOfServiceUrl
string
`json:"termsOfServiceUrl,omitempty"`
Contact
string
`json:"contact,omitempty"`
Contact
string
`json:"contact,omitempty"`
License
string
`json:"license,omitempty"`
License
string
`json:"license,omitempty"`
Licens
Url
string
`json:"licens
Url,omitempty"`
Licens
eUrl
string
`json:"license
Url,omitempty"`
}
}
// 5.1.5
// 5.1.5
...
@@ -134,7 +134,7 @@ type Api struct {
...
@@ -134,7 +134,7 @@ type Api struct {
// 5.2.3 Operation Object
// 5.2.3 Operation Object
type
Operation
struct
{
type
Operation
struct
{
Type
string
`json:"type"`
DataTypeFields
Method
string
`json:"method"`
Method
string
`json:"method"`
Summary
string
`json:"summary,omitempty"`
Summary
string
`json:"summary,omitempty"`
Notes
string
`json:"notes,omitempty"`
Notes
string
`json:"notes,omitempty"`
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_builder.go
0 → 100644
View file @
2fcf1575
package
swagger
type
SwaggerBuilder
struct
{
SwaggerService
}
func
NewSwaggerBuilder
(
config
Config
)
*
SwaggerBuilder
{
return
&
SwaggerBuilder
{
*
newSwaggerService
(
config
)}
}
func
(
sb
SwaggerBuilder
)
ProduceListing
()
ResourceListing
{
return
sb
.
SwaggerService
.
produceListing
()
}
func
(
sb
SwaggerBuilder
)
ProduceAllDeclarations
()
map
[
string
]
ApiDeclaration
{
return
sb
.
SwaggerService
.
produceAllDeclarations
()
}
func
(
sb
SwaggerBuilder
)
ProduceDeclarations
(
route
string
)
(
*
ApiDeclaration
,
bool
)
{
return
sb
.
SwaggerService
.
produceDeclarations
(
route
)
}
Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_webservice.go
View file @
2fcf1575
...
@@ -19,9 +19,35 @@ type SwaggerService struct {
...
@@ -19,9 +19,35 @@ type SwaggerService struct {
}
}
func
newSwaggerService
(
config
Config
)
*
SwaggerService
{
func
newSwaggerService
(
config
Config
)
*
SwaggerService
{
return
&
SwaggerService
{
sws
:=
&
SwaggerService
{
config
:
config
,
config
:
config
,
apiDeclarationMap
:
new
(
ApiDeclarationList
)}
apiDeclarationMap
:
new
(
ApiDeclarationList
)}
// Build all ApiDeclarations
for
_
,
each
:=
range
config
.
WebServices
{
rootPath
:=
each
.
RootPath
()
// skip the api service itself
if
rootPath
!=
config
.
ApiPath
{
if
rootPath
==
""
||
rootPath
==
"/"
{
// use routes
for
_
,
route
:=
range
each
.
Routes
()
{
entry
:=
staticPathFromRoute
(
route
)
_
,
exists
:=
sws
.
apiDeclarationMap
.
At
(
entry
)
if
!
exists
{
sws
.
apiDeclarationMap
.
Put
(
entry
,
sws
.
composeDeclaration
(
each
,
entry
))
}
}
}
else
{
// use root path
sws
.
apiDeclarationMap
.
Put
(
each
.
RootPath
(),
sws
.
composeDeclaration
(
each
,
each
.
RootPath
()))
}
}
}
// if specified then call the PostBuilderHandler
if
config
.
PostBuildHandler
!=
nil
{
config
.
PostBuildHandler
(
sws
.
apiDeclarationMap
)
}
return
sws
}
}
// LogInfo is the function that is called when this package needs to log. It defaults to log.Printf
// LogInfo is the function that is called when this package needs to log. It defaults to log.Printf
...
@@ -57,31 +83,6 @@ func RegisterSwaggerService(config Config, wsContainer *restful.Container) {
...
@@ -57,31 +83,6 @@ func RegisterSwaggerService(config Config, wsContainer *restful.Container) {
LogInfo
(
"[restful/swagger] listing is available at %v%v"
,
config
.
WebServicesUrl
,
config
.
ApiPath
)
LogInfo
(
"[restful/swagger] listing is available at %v%v"
,
config
.
WebServicesUrl
,
config
.
ApiPath
)
wsContainer
.
Add
(
ws
)
wsContainer
.
Add
(
ws
)
// Build all ApiDeclarations
for
_
,
each
:=
range
config
.
WebServices
{
rootPath
:=
each
.
RootPath
()
// skip the api service itself
if
rootPath
!=
config
.
ApiPath
{
if
rootPath
==
""
||
rootPath
==
"/"
{
// use routes
for
_
,
route
:=
range
each
.
Routes
()
{
entry
:=
staticPathFromRoute
(
route
)
_
,
exists
:=
sws
.
apiDeclarationMap
.
At
(
entry
)
if
!
exists
{
sws
.
apiDeclarationMap
.
Put
(
entry
,
sws
.
composeDeclaration
(
each
,
entry
))
}
}
}
else
{
// use root path
sws
.
apiDeclarationMap
.
Put
(
each
.
RootPath
(),
sws
.
composeDeclaration
(
each
,
each
.
RootPath
()))
}
}
}
// if specified then call the PostBuilderHandler
if
config
.
PostBuildHandler
!=
nil
{
config
.
PostBuildHandler
(
sws
.
apiDeclarationMap
)
}
// Check paths for UI serving
// Check paths for UI serving
if
config
.
StaticHandler
==
nil
&&
config
.
SwaggerFilePath
!=
""
&&
config
.
SwaggerPath
!=
""
{
if
config
.
StaticHandler
==
nil
&&
config
.
SwaggerFilePath
!=
""
&&
config
.
SwaggerPath
!=
""
{
swaggerPathSlash
:=
config
.
SwaggerPath
swaggerPathSlash
:=
config
.
SwaggerPath
...
@@ -138,7 +139,12 @@ func enableCORS(req *restful.Request, resp *restful.Response, chain *restful.Fil
...
@@ -138,7 +139,12 @@ func enableCORS(req *restful.Request, resp *restful.Response, chain *restful.Fil
}
}
func
(
sws
SwaggerService
)
getListing
(
req
*
restful
.
Request
,
resp
*
restful
.
Response
)
{
func
(
sws
SwaggerService
)
getListing
(
req
*
restful
.
Request
,
resp
*
restful
.
Response
)
{
listing
:=
ResourceListing
{
SwaggerVersion
:
swaggerVersion
,
ApiVersion
:
sws
.
config
.
ApiVersion
}
listing
:=
sws
.
produceListing
()
resp
.
WriteAsJson
(
listing
)
}
func
(
sws
SwaggerService
)
produceListing
()
ResourceListing
{
listing
:=
ResourceListing
{
SwaggerVersion
:
swaggerVersion
,
ApiVersion
:
sws
.
config
.
ApiVersion
,
Info
:
sws
.
config
.
Info
}
sws
.
apiDeclarationMap
.
Do
(
func
(
k
string
,
v
ApiDeclaration
)
{
sws
.
apiDeclarationMap
.
Do
(
func
(
k
string
,
v
ApiDeclaration
)
{
ref
:=
Resource
{
Path
:
k
}
ref
:=
Resource
{
Path
:
k
}
if
len
(
v
.
Apis
)
>
0
{
// use description of first (could still be empty)
if
len
(
v
.
Apis
)
>
0
{
// use description of first (could still be empty)
...
@@ -146,11 +152,11 @@ func (sws SwaggerService) getListing(req *restful.Request, resp *restful.Respons
...
@@ -146,11 +152,11 @@ func (sws SwaggerService) getListing(req *restful.Request, resp *restful.Respons
}
}
listing
.
Apis
=
append
(
listing
.
Apis
,
ref
)
listing
.
Apis
=
append
(
listing
.
Apis
,
ref
)
})
})
re
sp
.
WriteAsJson
(
listing
)
re
turn
listing
}
}
func
(
sws
SwaggerService
)
getDeclarations
(
req
*
restful
.
Request
,
resp
*
restful
.
Response
)
{
func
(
sws
SwaggerService
)
getDeclarations
(
req
*
restful
.
Request
,
resp
*
restful
.
Response
)
{
decl
,
ok
:=
sws
.
apiDeclarationMap
.
At
(
composeRootPath
(
req
))
decl
,
ok
:=
sws
.
produceDeclarations
(
composeRootPath
(
req
))
if
!
ok
{
if
!
ok
{
resp
.
WriteErrorString
(
http
.
StatusNotFound
,
"ApiDeclaration not found"
)
resp
.
WriteErrorString
(
http
.
StatusNotFound
,
"ApiDeclaration not found"
)
return
return
...
@@ -180,11 +186,28 @@ func (sws SwaggerService) getDeclarations(req *restful.Request, resp *restful.Re
...
@@ -180,11 +186,28 @@ func (sws SwaggerService) getDeclarations(req *restful.Request, resp *restful.Re
scheme
=
"https"
scheme
=
"https"
}
}
}
}
(
&
decl
)
.
BasePath
=
fmt
.
Sprintf
(
"%s://%s"
,
scheme
,
host
)
decl
.
BasePath
=
fmt
.
Sprintf
(
"%s://%s"
,
scheme
,
host
)
}
}
resp
.
WriteAsJson
(
decl
)
resp
.
WriteAsJson
(
decl
)
}
}
func
(
sws
SwaggerService
)
produceAllDeclarations
()
map
[
string
]
ApiDeclaration
{
decls
:=
map
[
string
]
ApiDeclaration
{}
sws
.
apiDeclarationMap
.
Do
(
func
(
k
string
,
v
ApiDeclaration
)
{
decls
[
k
]
=
v
})
return
decls
}
func
(
sws
SwaggerService
)
produceDeclarations
(
route
string
)
(
*
ApiDeclaration
,
bool
)
{
decl
,
ok
:=
sws
.
apiDeclarationMap
.
At
(
route
)
if
!
ok
{
return
nil
,
false
}
decl
.
BasePath
=
sws
.
config
.
WebServicesUrl
return
&
decl
,
true
}
// composeDeclaration uses all routes and parameters to create a ApiDeclaration
// composeDeclaration uses all routes and parameters to create a ApiDeclaration
func
(
sws
SwaggerService
)
composeDeclaration
(
ws
*
restful
.
WebService
,
pathPrefix
string
)
ApiDeclaration
{
func
(
sws
SwaggerService
)
composeDeclaration
(
ws
*
restful
.
WebService
,
pathPrefix
string
)
ApiDeclaration
{
decl
:=
ApiDeclaration
{
decl
:=
ApiDeclaration
{
...
@@ -207,13 +230,15 @@ func (sws SwaggerService) composeDeclaration(ws *restful.WebService, pathPrefix
...
@@ -207,13 +230,15 @@ func (sws SwaggerService) composeDeclaration(ws *restful.WebService, pathPrefix
}
}
}
}
pathToRoutes
.
Do
(
func
(
path
string
,
routes
[]
restful
.
Route
)
{
pathToRoutes
.
Do
(
func
(
path
string
,
routes
[]
restful
.
Route
)
{
api
:=
Api
{
Path
:
strings
.
TrimSuffix
(
path
,
"/"
),
Description
:
ws
.
Documentation
()}
api
:=
Api
{
Path
:
strings
.
TrimSuffix
(
withoutWildcard
(
path
),
"/"
),
Description
:
ws
.
Documentation
()}
voidString
:=
"void"
for
_
,
route
:=
range
routes
{
for
_
,
route
:=
range
routes
{
operation
:=
Operation
{
operation
:=
Operation
{
Method
:
route
.
Method
,
Method
:
route
.
Method
,
Summary
:
route
.
Doc
,
Summary
:
route
.
Doc
,
Notes
:
route
.
Notes
,
Notes
:
route
.
Notes
,
Type
:
asDataType
(
route
.
WriteSample
),
// Type gets overwritten if there is a write sample
DataTypeFields
:
DataTypeFields
{
Type
:
&
voidString
},
Parameters
:
[]
Parameter
{},
Parameters
:
[]
Parameter
{},
Nickname
:
route
.
Operation
,
Nickname
:
route
.
Operation
,
ResponseMessages
:
composeResponseMessages
(
route
,
&
decl
)}
ResponseMessages
:
composeResponseMessages
(
route
,
&
decl
)}
...
@@ -238,6 +263,13 @@ func (sws SwaggerService) composeDeclaration(ws *restful.WebService, pathPrefix
...
@@ -238,6 +263,13 @@ func (sws SwaggerService) composeDeclaration(ws *restful.WebService, pathPrefix
return
decl
return
decl
}
}
func
withoutWildcard
(
path
string
)
string
{
if
strings
.
HasSuffix
(
path
,
":*}"
)
{
return
path
[
0
:
len
(
path
)
-
3
]
+
"}"
}
return
path
}
// composeResponseMessages takes the ResponseErrors (if any) and creates ResponseMessages from them.
// composeResponseMessages takes the ResponseErrors (if any) and creates ResponseMessages from them.
func
composeResponseMessages
(
route
restful
.
Route
,
decl
*
ApiDeclaration
)
(
messages
[]
ResponseMessage
)
{
func
composeResponseMessages
(
route
restful
.
Route
,
decl
*
ApiDeclaration
)
(
messages
[]
ResponseMessage
)
{
if
route
.
ResponseErrors
==
nil
{
if
route
.
ResponseErrors
==
nil
{
...
@@ -299,14 +331,10 @@ func detectCollectionType(st reflect.Type) (bool, reflect.Type) {
...
@@ -299,14 +331,10 @@ func detectCollectionType(st reflect.Type) (bool, reflect.Type) {
// addModelFromSample creates and adds (or overwrites) a Model from a sample resource
// addModelFromSample creates and adds (or overwrites) a Model from a sample resource
func
(
sws
SwaggerService
)
addModelFromSampleTo
(
operation
*
Operation
,
isResponse
bool
,
sample
interface
{},
models
*
ModelList
)
{
func
(
sws
SwaggerService
)
addModelFromSampleTo
(
operation
*
Operation
,
isResponse
bool
,
sample
interface
{},
models
*
ModelList
)
{
st
:=
reflect
.
TypeOf
(
sample
)
isCollection
,
st
:=
detectCollectionType
(
st
)
modelName
:=
modelBuilder
{}
.
keyFrom
(
st
)
if
isResponse
{
if
isResponse
{
if
isCollection
{
type_
,
items
:=
asDataType
(
sample
)
modelName
=
"array["
+
modelName
+
"]"
operation
.
Type
=
type_
}
operation
.
Items
=
items
operation
.
Type
=
modelName
}
}
modelBuilder
{
models
}
.
addModelFrom
(
sample
)
modelBuilder
{
models
}
.
addModelFrom
(
sample
)
}
}
...
@@ -315,7 +343,7 @@ func asSwaggerParameter(param restful.ParameterData) Parameter {
...
@@ -315,7 +343,7 @@ func asSwaggerParameter(param restful.ParameterData) Parameter {
return
Parameter
{
return
Parameter
{
DataTypeFields
:
DataTypeFields
{
DataTypeFields
:
DataTypeFields
{
Type
:
&
param
.
DataType
,
Type
:
&
param
.
DataType
,
Format
:
asFormat
(
param
.
DataType
),
Format
:
asFormat
(
param
.
DataType
,
param
.
DataFormat
),
DefaultValue
:
Special
(
param
.
DefaultValue
),
DefaultValue
:
Special
(
param
.
DefaultValue
),
},
},
Name
:
param
.
Name
,
Name
:
param
.
Name
,
...
@@ -360,7 +388,10 @@ func composeRootPath(req *restful.Request) string {
...
@@ -360,7 +388,10 @@ func composeRootPath(req *restful.Request) string {
return
path
+
"/"
+
g
return
path
+
"/"
+
g
}
}
func
asFormat
(
name
string
)
string
{
func
asFormat
(
dataType
string
,
dataFormat
string
)
string
{
if
dataFormat
!=
""
{
return
dataFormat
}
return
""
// TODO
return
""
// TODO
}
}
...
@@ -380,9 +411,30 @@ func asParamType(kind int) string {
...
@@ -380,9 +411,30 @@ func asParamType(kind int) string {
return
""
return
""
}
}
func
asDataType
(
any
interface
{})
string
{
func
asDataType
(
any
interface
{})
(
*
string
,
*
Item
)
{
if
any
==
nil
{
// If it's not a collection, return the suggested model name
return
"void"
st
:=
reflect
.
TypeOf
(
any
)
isCollection
,
st
:=
detectCollectionType
(
st
)
modelName
:=
modelBuilder
{}
.
keyFrom
(
st
)
// if it's not a collection we are done
if
!
isCollection
{
return
&
modelName
,
nil
}
// XXX: This is not very elegant
// We create an Item object referring to the given model
models
:=
ModelList
{}
mb
:=
modelBuilder
{
&
models
}
mb
.
addModelFrom
(
any
)
elemTypeName
:=
mb
.
getElementTypeName
(
modelName
,
""
,
st
)
item
:=
new
(
Item
)
if
mb
.
isPrimitiveType
(
elemTypeName
)
{
mapped
:=
mb
.
jsonSchemaType
(
elemTypeName
)
item
.
Type
=
&
mapped
}
else
{
item
.
Ref
=
&
elemTypeName
}
}
return
reflect
.
TypeOf
(
any
)
.
Name
()
tmp
:=
"array"
return
&
tmp
,
item
}
}
Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/test_package/struct.go
0 → 100644
View file @
2fcf1575
package
test_package
type
TestStruct
struct
{
TestField
string
}
Godeps/_workspace/src/github.com/emicklei/go-restful/web_service.go
View file @
2fcf1575
package
restful
package
restful
import
(
import
(
"fmt"
"os"
"os"
"sync"
"github.com/emicklei/go-restful/log"
"github.com/emicklei/go-restful/log"
)
)
...
@@ -21,6 +23,15 @@ type WebService struct {
...
@@ -21,6 +23,15 @@ type WebService struct {
filters
[]
FilterFunction
filters
[]
FilterFunction
documentation
string
documentation
string
apiVersion
string
apiVersion
string
dynamicRoutes
bool
// protects 'routes' if dynamic routes are enabled
routesLock
sync
.
RWMutex
}
func
(
w
*
WebService
)
SetDynamicRoutes
(
enable
bool
)
{
w
.
dynamicRoutes
=
enable
}
}
// compilePathExpression ensures that the path is compiled into a RegEx for those routers that need it.
// compilePathExpression ensures that the path is compiled into a RegEx for those routers that need it.
...
@@ -134,11 +145,28 @@ func FormParameter(name, description string) *Parameter {
...
@@ -134,11 +145,28 @@ func FormParameter(name, description string) *Parameter {
// Route creates a new Route using the RouteBuilder and add to the ordered list of Routes.
// Route creates a new Route using the RouteBuilder and add to the ordered list of Routes.
func
(
w
*
WebService
)
Route
(
builder
*
RouteBuilder
)
*
WebService
{
func
(
w
*
WebService
)
Route
(
builder
*
RouteBuilder
)
*
WebService
{
w
.
routesLock
.
Lock
()
defer
w
.
routesLock
.
Unlock
()
builder
.
copyDefaults
(
w
.
produces
,
w
.
consumes
)
builder
.
copyDefaults
(
w
.
produces
,
w
.
consumes
)
w
.
routes
=
append
(
w
.
routes
,
builder
.
Build
())
w
.
routes
=
append
(
w
.
routes
,
builder
.
Build
())
return
w
return
w
}
}
// RemoveRoute removes the specified route, looks for something that matches 'path' and 'method'
func
(
w
*
WebService
)
RemoveRoute
(
path
,
method
string
)
error
{
if
!
w
.
dynamicRoutes
{
return
fmt
.
Errorf
(
"dynamic routes are not enabled."
)
}
w
.
routesLock
.
Lock
()
defer
w
.
routesLock
.
Unlock
()
for
ix
:=
range
w
.
routes
{
if
w
.
routes
[
ix
]
.
Method
==
method
&&
w
.
routes
[
ix
]
.
Path
==
path
{
w
.
routes
=
append
(
w
.
routes
[
:
ix
],
w
.
routes
[
ix
+
1
:
]
...
)
}
}
return
nil
}
// Method creates a new RouteBuilder and initialize its http method
// Method creates a new RouteBuilder and initialize its http method
func
(
w
*
WebService
)
Method
(
httpMethod
string
)
*
RouteBuilder
{
func
(
w
*
WebService
)
Method
(
httpMethod
string
)
*
RouteBuilder
{
return
new
(
RouteBuilder
)
.
servicePath
(
w
.
rootPath
)
.
Method
(
httpMethod
)
return
new
(
RouteBuilder
)
.
servicePath
(
w
.
rootPath
)
.
Method
(
httpMethod
)
...
@@ -160,7 +188,17 @@ func (w *WebService) Consumes(accepts ...string) *WebService {
...
@@ -160,7 +188,17 @@ func (w *WebService) Consumes(accepts ...string) *WebService {
// Routes returns the Routes associated with this WebService
// Routes returns the Routes associated with this WebService
func
(
w
WebService
)
Routes
()
[]
Route
{
func
(
w
WebService
)
Routes
()
[]
Route
{
return
w
.
routes
if
!
w
.
dynamicRoutes
{
return
w
.
routes
}
// Make a copy of the array to prevent concurrency problems
w
.
routesLock
.
RLock
()
defer
w
.
routesLock
.
RUnlock
()
result
:=
make
([]
Route
,
len
(
w
.
routes
))
for
ix
:=
range
w
.
routes
{
result
[
ix
]
=
w
.
routes
[
ix
]
}
return
result
}
}
// RootPath returns the RootPath associated with this WebService. Default "/"
// RootPath returns the RootPath associated with this WebService. Default "/"
...
...
api/swagger-spec/v1.json
View file @
2fcf1575
...
@@ -3168,7 +3168,7 @@
...
@@ -3168,7 +3168,7 @@
]
]
},
},
{
{
"path"
:
"/api/v1/proxy/nodes/{name}/{path
:*
}"
,
"path"
:
"/api/v1/proxy/nodes/{name}/{path}"
,
"description"
:
"API at /api/v1"
,
"description"
:
"API at /api/v1"
,
"operations"
:
[
"operations"
:
[
{
{
...
@@ -5281,7 +5281,7 @@
...
@@ -5281,7 +5281,7 @@
]
]
},
},
{
{
"path"
:
"/api/v1/proxy/namespaces/{namespace}/pods/{name}/{path
:*
}"
,
"path"
:
"/api/v1/proxy/namespaces/{namespace}/pods/{name}/{path}"
,
"description"
:
"API at /api/v1"
,
"description"
:
"API at /api/v1"
,
"operations"
:
[
"operations"
:
[
{
{
...
@@ -6633,7 +6633,7 @@
...
@@ -6633,7 +6633,7 @@
]
]
},
},
{
{
"path"
:
"/api/v1/namespaces/{namespace}/pods/{name}/proxy/{path
:*
}"
,
"path"
:
"/api/v1/namespaces/{namespace}/pods/{name}/proxy/{path}"
,
"description"
:
"API at /api/v1"
,
"description"
:
"API at /api/v1"
,
"operations"
:
[
"operations"
:
[
{
{
...
@@ -10918,7 +10918,7 @@
...
@@ -10918,7 +10918,7 @@
]
]
},
},
{
{
"path"
:
"/api/v1/proxy/namespaces/{namespace}/services/{name}/{path
:*
}"
,
"path"
:
"/api/v1/proxy/namespaces/{namespace}/services/{name}/{path}"
,
"description"
:
"API at /api/v1"
,
"description"
:
"API at /api/v1"
,
"operations"
:
[
"operations"
:
[
{
{
...
...
docs/api-reference/v1/operations.html
View file @
2fcf1575
...
@@ -6819,7 +6819,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -6819,7 +6819,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_connect_get_requests_to_proxy_of_pod_2"
>
connect GET requests to proxy of Pod
</h3>
<h3
id=
"_connect_get_requests_to_proxy_of_pod_2"
>
connect GET requests to proxy of Pod
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
GET /api/v1/namespaces/{namespace}/pods/{name}/proxy/{path
:*
}
</pre>
<pre>
GET /api/v1/namespaces/{namespace}/pods/{name}/proxy/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -6940,7 +6940,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -6940,7 +6940,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_connect_put_requests_to_proxy_of_pod_2"
>
connect PUT requests to proxy of Pod
</h3>
<h3
id=
"_connect_put_requests_to_proxy_of_pod_2"
>
connect PUT requests to proxy of Pod
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
PUT /api/v1/namespaces/{namespace}/pods/{name}/proxy/{path
:*
}
</pre>
<pre>
PUT /api/v1/namespaces/{namespace}/pods/{name}/proxy/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -7061,7 +7061,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -7061,7 +7061,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_connect_delete_requests_to_proxy_of_pod_2"
>
connect DELETE requests to proxy of Pod
</h3>
<h3
id=
"_connect_delete_requests_to_proxy_of_pod_2"
>
connect DELETE requests to proxy of Pod
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
DELETE /api/v1/namespaces/{namespace}/pods/{name}/proxy/{path
:*
}
</pre>
<pre>
DELETE /api/v1/namespaces/{namespace}/pods/{name}/proxy/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -7182,7 +7182,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -7182,7 +7182,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_connect_post_requests_to_proxy_of_pod_2"
>
connect POST requests to proxy of Pod
</h3>
<h3
id=
"_connect_post_requests_to_proxy_of_pod_2"
>
connect POST requests to proxy of Pod
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
POST /api/v1/namespaces/{namespace}/pods/{name}/proxy/{path
:*
}
</pre>
<pre>
POST /api/v1/namespaces/{namespace}/pods/{name}/proxy/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -15215,7 +15215,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -15215,7 +15215,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_proxy_get_requests_to_pod_2"
>
proxy GET requests to Pod
</h3>
<h3
id=
"_proxy_get_requests_to_pod_2"
>
proxy GET requests to Pod
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
GET /api/v1/proxy/namespaces/{namespace}/pods/{name}/{path
:*
}
</pre>
<pre>
GET /api/v1/proxy/namespaces/{namespace}/pods/{name}/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -15328,7 +15328,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -15328,7 +15328,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_proxy_put_requests_to_pod_2"
>
proxy PUT requests to Pod
</h3>
<h3
id=
"_proxy_put_requests_to_pod_2"
>
proxy PUT requests to Pod
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
PUT /api/v1/proxy/namespaces/{namespace}/pods/{name}/{path
:*
}
</pre>
<pre>
PUT /api/v1/proxy/namespaces/{namespace}/pods/{name}/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -15441,7 +15441,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -15441,7 +15441,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_proxy_delete_requests_to_pod_2"
>
proxy DELETE requests to Pod
</h3>
<h3
id=
"_proxy_delete_requests_to_pod_2"
>
proxy DELETE requests to Pod
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
DELETE /api/v1/proxy/namespaces/{namespace}/pods/{name}/{path
:*
}
</pre>
<pre>
DELETE /api/v1/proxy/namespaces/{namespace}/pods/{name}/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -15554,7 +15554,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -15554,7 +15554,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_proxy_post_requests_to_pod_2"
>
proxy POST requests to Pod
</h3>
<h3
id=
"_proxy_post_requests_to_pod_2"
>
proxy POST requests to Pod
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
POST /api/v1/proxy/namespaces/{namespace}/pods/{name}/{path
:*
}
</pre>
<pre>
POST /api/v1/proxy/namespaces/{namespace}/pods/{name}/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -16087,7 +16087,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -16087,7 +16087,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_proxy_get_requests_to_service_2"
>
proxy GET requests to Service
</h3>
<h3
id=
"_proxy_get_requests_to_service_2"
>
proxy GET requests to Service
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
GET /api/v1/proxy/namespaces/{namespace}/services/{name}/{path
:*
}
</pre>
<pre>
GET /api/v1/proxy/namespaces/{namespace}/services/{name}/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -16200,7 +16200,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -16200,7 +16200,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_proxy_put_requests_to_service_2"
>
proxy PUT requests to Service
</h3>
<h3
id=
"_proxy_put_requests_to_service_2"
>
proxy PUT requests to Service
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
PUT /api/v1/proxy/namespaces/{namespace}/services/{name}/{path
:*
}
</pre>
<pre>
PUT /api/v1/proxy/namespaces/{namespace}/services/{name}/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -16313,7 +16313,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -16313,7 +16313,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_proxy_delete_requests_to_service_2"
>
proxy DELETE requests to Service
</h3>
<h3
id=
"_proxy_delete_requests_to_service_2"
>
proxy DELETE requests to Service
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
DELETE /api/v1/proxy/namespaces/{namespace}/services/{name}/{path
:*
}
</pre>
<pre>
DELETE /api/v1/proxy/namespaces/{namespace}/services/{name}/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -16426,7 +16426,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -16426,7 +16426,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_proxy_post_requests_to_service_2"
>
proxy POST requests to Service
</h3>
<h3
id=
"_proxy_post_requests_to_service_2"
>
proxy POST requests to Service
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
POST /api/v1/proxy/namespaces/{namespace}/services/{name}/{path
:*
}
</pre>
<pre>
POST /api/v1/proxy/namespaces/{namespace}/services/{name}/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -16927,7 +16927,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -16927,7 +16927,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_proxy_get_requests_to_node_2"
>
proxy GET requests to Node
</h3>
<h3
id=
"_proxy_get_requests_to_node_2"
>
proxy GET requests to Node
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
GET /api/v1/proxy/nodes/{name}/{path
:*
}
</pre>
<pre>
GET /api/v1/proxy/nodes/{name}/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -17032,7 +17032,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -17032,7 +17032,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_proxy_put_requests_to_node_2"
>
proxy PUT requests to Node
</h3>
<h3
id=
"_proxy_put_requests_to_node_2"
>
proxy PUT requests to Node
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
PUT /api/v1/proxy/nodes/{name}/{path
:*
}
</pre>
<pre>
PUT /api/v1/proxy/nodes/{name}/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -17137,7 +17137,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -17137,7 +17137,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_proxy_delete_requests_to_node_2"
>
proxy DELETE requests to Node
</h3>
<h3
id=
"_proxy_delete_requests_to_node_2"
>
proxy DELETE requests to Node
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
DELETE /api/v1/proxy/nodes/{name}/{path
:*
}
</pre>
<pre>
DELETE /api/v1/proxy/nodes/{name}/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -17242,7 +17242,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -17242,7 +17242,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<h3
id=
"_proxy_post_requests_to_node_2"
>
proxy POST requests to Node
</h3>
<h3
id=
"_proxy_post_requests_to_node_2"
>
proxy POST requests to Node
</h3>
<div
class=
"listingblock"
>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<div
class=
"content"
>
<pre>
POST /api/v1/proxy/nodes/{name}/{path
:*
}
</pre>
<pre>
POST /api/v1/proxy/nodes/{name}/{path}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"sect3"
>
<div
class=
"sect3"
>
...
@@ -23664,7 +23664,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
...
@@ -23664,7 +23664,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
</div>
<div
id=
"footer"
>
<div
id=
"footer"
>
<div
id=
"footer-text"
>
<div
id=
"footer-text"
>
Last updated 2015-1
1-13 20:43:21
UTC
Last updated 2015-1
2-01 01:46:05
UTC
</div>
</div>
</div>
</div>
</body>
</body>
...
...
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