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 @@
},
{
"ImportPath"
:
"github.com/emicklei/go-restful"
,
"Comment"
:
"v1.
1.3-98-g1f9a0ee
"
,
"Rev"
:
"
1f9a0ee00ff93717a275e15b30cf7df356255877
"
"Comment"
:
"v1.
2
"
,
"Rev"
:
"
777bb3f19bcafe2575ffb2a3e46af92509ae9594
"
},
{
"ImportPath"
:
"github.com/evanphx/json-patch"
,
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/CHANGES.md
View file @
2fcf1575
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
-
add support for reading entities from compressed request content
-
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
-
Panic recovery to produce HTTP 500, customizable using RecoverHandler(...)
-
Route errors produce HTTP 404/405/406/415 errors, customizable using ServiceErrorHandler(...)
-
Configurable (trace) logging
-
Customizable encoding using EntityReaderWriter registration
-
Customizable gzip/deflate readers and writers using CompressorProvider registration
### Resources
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/compress.go
View file @
2fcf1575
...
...
@@ -20,6 +20,7 @@ var EnableContentEncoding = false
type
CompressingResponseWriter
struct
{
writer
http
.
ResponseWriter
compressor
io
.
WriteCloser
encoding
string
}
// Header is part of http.ResponseWriter interface
...
...
@@ -35,6 +36,9 @@ func (c *CompressingResponseWriter) WriteHeader(status int) {
// Write is part of http.ResponseWriter interface
// It is passed through the compressor
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
)
}
...
...
@@ -44,8 +48,25 @@ func (c *CompressingResponseWriter) CloseNotify() <-chan bool {
}
// 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
()
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.
...
...
@@ -73,13 +94,15 @@ func NewCompressingResponseWriter(httpWriter http.ResponseWriter, encoding strin
c
.
writer
=
httpWriter
var
err
error
if
ENCODING_GZIP
==
encoding
{
w
:=
GzipWriterPool
.
Get
()
.
(
*
gzip
.
Writer
)
w
:=
currentCompressorProvider
.
AcquireGzipWriter
(
)
w
.
Reset
(
httpWriter
)
c
.
compressor
=
w
c
.
encoding
=
ENCODING_GZIP
}
else
if
ENCODING_DEFLATE
==
encoding
{
w
:=
ZlibWriterPool
.
Get
()
.
(
*
zlib
.
Writer
)
w
:=
currentCompressorProvider
.
AcquireZlibWriter
(
)
w
.
Reset
(
httpWriter
)
c
.
compressor
=
w
c
.
encoding
=
ENCODING_DEFLATE
}
else
{
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
// 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
(
"bytes"
"compress/gzip"
...
...
@@ -7,12 +11,50 @@ import (
"sync"
)
// GzipWriterPool is used to get reusable zippers.
// The Get() result must be type asserted to *gzip.Writer.
var
GzipWriterPool
=
&
sync
.
Pool
{
New
:
func
()
interface
{}
{
return
newGzipWriter
()
},
// SyncPoolCompessors is a CompressorProvider that use the standard sync.Pool.
type
SyncPoolCompessors
struct
{
GzipWriterPool
*
sync
.
Pool
GzipReaderPool
*
sync
.
Pool
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
{
...
...
@@ -24,17 +66,11 @@ func newGzipWriter() *gzip.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
{
// 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
)
w
.
Reset
(
b
)
w
.
Flush
()
...
...
@@ -46,14 +82,6 @@ func newGzipReader() *gzip.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
{
writer
,
err
:=
zlib
.
NewWriterLevel
(
new
(
bytes
.
Buffer
),
gzip
.
BestSpeed
)
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) {
// HandleWithFilter registers the handler for the given pattern.
// Container's filter chain is applied for handler.
// 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
)
{
if
len
(
c
.
containerFilters
)
==
0
{
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
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.
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
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
runtime
:
go
api_version
:
go1
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/datastore/main.go
View file @
2fcf1575
package
main
import
(
"appengine"
"appengine/datastore"
"appengine/user"
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful/swagger"
"google.golang.com/appengine"
"google.golang.com/appengine/datastore"
"google.golang.com/appengine/user"
"net/http"
"time"
)
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/examples/google_app_engine/restful-user-service.go
View file @
2fcf1575
package
main
import
(
"appengine"
"appengine/memcache"
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful/swagger"
"google.golang.com/appengine"
"google.golang.com/appengine/memcache"
"net/http"
)
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/examples/restful-CORS-filter.go
View file @
2fcf1575
...
...
@@ -53,7 +53,7 @@ func main() {
// Add container filter to enable CORS
cors
:=
restful
.
CrossOriginResourceSharing
{
ExposeHeaders
:
[]
string
{
"X-My-Header"
},
AllowedHeaders
:
[]
string
{
"Content-Type"
},
AllowedHeaders
:
[]
string
{
"Content-Type"
,
"Accept"
},
CookiesAllowed
:
false
,
Container
:
wsContainer
}
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
}
usr
.
Id
=
strconv
.
Itoa
(
len
(
u
.
users
)
+
1
)
// simple id generation
u
.
users
[
usr
.
Id
]
=
*
usr
response
.
WriteHeader
(
http
.
StatusCreated
)
response
.
WriteEntity
(
usr
)
response
.
WriteHeaderAndEntity
(
http
.
StatusCreated
,
usr
)
}
// 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
err
:=
request
.
ReadEntity
(
&
usr
)
if
err
==
nil
{
u
.
users
[
usr
.
Id
]
=
usr
response
.
WriteHeader
(
http
.
StatusCreated
)
response
.
WriteEntity
(
usr
)
response
.
WriteHeaderAndEntity
(
http
.
StatusCreated
,
usr
)
}
else
{
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
func
init
()
{
// 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
)
{
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/options_filter.go
View file @
2fcf1575
...
...
@@ -8,7 +8,8 @@ import "strings"
// 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.
// 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
)
{
if
"OPTIONS"
!=
req
.
Request
.
Method
{
chain
.
ProcessFilter
(
req
,
resp
)
...
...
@@ -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
// 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
{
return
DefaultContainer
.
OPTIONSFilter
}
Godeps/_workspace/src/github.com/emicklei/go-restful/parameter.go
View file @
2fcf1575
...
...
@@ -30,12 +30,12 @@ type Parameter struct {
// ParameterData represents the state of a Parameter.
// It is made public to make it accessible to e.g. the Swagger package.
type
ParameterData
struct
{
Name
,
Description
,
DataType
string
Kind
int
Required
bool
AllowableValues
map
[
string
]
string
AllowMultiple
bool
DefaultValue
string
Name
,
Description
,
DataType
,
DataFormat
string
Kind
int
Required
bool
AllowableValues
map
[
string
]
string
AllowMultiple
bool
DefaultValue
string
}
// Data returns the state of the Parameter
...
...
@@ -95,6 +95,12 @@ func (p *Parameter) DataType(typeName string) *Parameter {
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
func
(
p
*
Parameter
)
DefaultValue
(
stringRepresentation
string
)
*
Parameter
{
p
.
data
.
DefaultValue
=
stringRepresentation
...
...
Godeps/_workspace/src/github.com/emicklei/go-restful/request.go
View file @
2fcf1575
...
...
@@ -6,14 +6,9 @@ package restful
import
(
"bytes"
"compress/gzip"
"compress/zlib"
"encoding/json"
"encoding/xml"
"io"
"io/ioutil"
"net/http"
"strings"
)
var
defaultRequestContentType
string
...
...
@@ -81,62 +76,43 @@ func (r *Request) HeaderParameter(name string) string {
return
r
.
Request
.
Header
.
Get
(
name
)
}
// ReadEntity checks the Accept header and reads the content into the entityPointer
// May be called multiple times in the request-response flow
// ReadEntity checks the Accept header and reads the content into the entityPointer.
func
(
r
*
Request
)
ReadEntity
(
entityPointer
interface
{})
(
err
error
)
{
defer
r
.
Request
.
Body
.
Close
()
contentType
:=
r
.
Request
.
Header
.
Get
(
HEADER_ContentType
)
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
)
{
var
buffer
[]
byte
if
r
.
bodyContent
!
=
nil
{
buffer
=
*
r
.
bodyContent
}
else
{
buffer
,
err
=
ioutil
.
ReadAll
(
r
.
Request
.
Body
)
if
err
!=
nil
{
r
eturn
err
// OLD feature, cache the body for reads
if
doCacheReadEntityBytes
{
if
r
.
bodyContent
=
=
nil
{
data
,
err
:=
ioutil
.
ReadAll
(
r
.
Request
.
Body
)
if
err
!=
nil
{
return
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
if
ENCODING_GZIP
==
contentEncoding
{
gzipReader
:=
GzipReaderPool
.
Get
()
.
(
*
gzip
.
Reader
)
gzipReader
.
Reset
(
reader
)
entityReader
=
gzipReader
gzipReader
:=
currentCompressorProvider
.
AcquireGzipReader
()
defer
currentCompressorProvider
.
ReleaseGzipReader
(
gzipReader
)
gzipReader
.
Reset
(
r
.
Request
.
Body
)
r
.
Request
.
Body
=
gzipReader
}
else
if
ENCODING_DEFLATE
==
contentEncoding
{
zlibReader
,
err
:=
zlib
.
NewReader
(
r
eader
)
zlibReader
,
err
:=
zlib
.
NewReader
(
r
.
Request
.
Body
)
if
err
!=
nil
{
return
err
}
entityReader
=
zlibReader
r
.
Request
.
Body
=
zlibReader
}
// decode JSON
if
strings
.
Contains
(
contentType
,
MIME_JSON
)
||
MIME_JSON
==
defaultRequestContentType
{
decoder
:=
json
.
NewDecoder
(
entityReader
)
decoder
.
UseNumber
()
return
decoder
.
Decode
(
entityPointer
)
// lookup the EntityReader
entityReader
,
ok
:=
entityAccessRegistry
.
AccessorAt
(
contentType
)
if
!
ok
{
return
NewError
(
http
.
StatusBadRequest
,
"Unable to unmarshal content of type:"
+
contentType
)
}
// 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
)
return
entityReader
.
Read
(
r
,
entityPointer
)
}
// 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
=
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
-
(api break) changed the type of Properties in Model
-
(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 {
ApiVersion
string
// If set then call this handler after building the complete ApiDeclaration Map
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
modelDescription
=
tag
}
fieldType
:=
field
.
Type
prop
.
setPropertyMetadata
(
field
)
if
prop
.
Type
!=
nil
{
return
jsonName
,
modelDescription
,
prop
}
fieldType
:=
field
.
Type
// check if type is doing its own marshalling
marshalerType
:=
reflect
.
TypeOf
((
*
json
.
Marshaler
)(
nil
))
.
Elem
()
...
...
@@ -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
)
{
fieldType
:=
field
.
Type
prop
.
setPropertyMetadata
(
field
)
// Check for type override in tag
if
prop
.
Type
!=
nil
{
return
jsonName
,
prop
}
fieldType
:=
field
.
Type
// check for anonymous
if
len
(
fieldType
.
Name
())
==
0
{
// anonymous
...
...
@@ -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
)
{
fieldType
:=
field
.
Type
// check for type override in tags
prop
.
setPropertyMetadata
(
field
)
if
prop
.
Type
!=
nil
{
return
jsonName
,
prop
}
fieldType
:=
field
.
Type
var
pType
=
"array"
prop
.
Type
=
&
pType
elemTypeName
:=
b
.
getElementTypeName
(
modelName
,
jsonName
,
fieldType
.
Elem
())
...
...
@@ -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
)
{
fieldType
:=
field
.
Type
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
if
fieldType
.
Elem
()
.
Kind
()
==
reflect
.
Slice
||
fieldType
.
Elem
()
.
Kind
()
==
reflect
.
Array
{
...
...
@@ -338,7 +352,7 @@ func (b modelBuilder) keyFrom(st reflect.Type) string {
// see also https://golang.org/ref/spec#Numeric_types
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
...
...
@@ -359,6 +373,7 @@ func (b modelBuilder) jsonNameOfField(field reflect.StructField) string {
// see also http://json-schema.org/latest/json-schema-core.html#anchor8
func
(
b
modelBuilder
)
jsonSchemaType
(
modelName
string
)
string
{
schemaMap
:=
map
[
string
]
string
{
"uint"
:
"integer"
,
"uint8"
:
"integer"
,
"uint16"
:
"integer"
,
"uint32"
:
"integer"
,
...
...
@@ -389,6 +404,7 @@ func (b modelBuilder) jsonSchemaFormat(modelName string) string {
"int32"
:
"int32"
,
"int64"
:
"int64"
,
"byte"
:
"byte"
,
"uint"
:
"integer"
,
"uint8"
:
"byte"
,
"float64"
:
"double"
,
"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) {
}
}
func
(
prop
*
ModelProperty
)
setType
(
field
reflect
.
StructField
)
{
if
tag
:=
field
.
Tag
.
Get
(
"type"
);
tag
!=
""
{
prop
.
Type
=
&
tag
}
}
func
(
prop
*
ModelProperty
)
setMinimum
(
field
reflect
.
StructField
)
{
if
tag
:=
field
.
Tag
.
Get
(
"minimum"
);
tag
!=
""
{
prop
.
Minimum
=
tag
...
...
@@ -56,4 +62,5 @@ func (prop *ModelProperty) setPropertyMetadata(field reflect.StructField) {
prop
.
setMaximum
(
field
)
prop
.
setUniqueItems
(
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 {
TermsOfServiceUrl
string
`json:"termsOfServiceUrl,omitempty"`
Contact
string
`json:"contact,omitempty"`
License
string
`json:"license,omitempty"`
Licens
Url
string
`json:"licens
Url,omitempty"`
Licens
eUrl
string
`json:"license
Url,omitempty"`
}
// 5.1.5
...
...
@@ -134,7 +134,7 @@ type Api struct {
// 5.2.3 Operation Object
type
Operation
struct
{
Type
string
`json:"type"`
DataTypeFields
Method
string
`json:"method"`
Summary
string
`json:"summary,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 {
}
func
newSwaggerService
(
config
Config
)
*
SwaggerService
{
return
&
SwaggerService
{
sws
:=
&
SwaggerService
{
config
:
config
,
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
...
...
@@ -57,31 +83,6 @@ func RegisterSwaggerService(config Config, wsContainer *restful.Container) {
LogInfo
(
"[restful/swagger] listing is available at %v%v"
,
config
.
WebServicesUrl
,
config
.
ApiPath
)
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
if
config
.
StaticHandler
==
nil
&&
config
.
SwaggerFilePath
!=
""
&&
config
.
SwaggerPath
!=
""
{
swaggerPathSlash
:=
config
.
SwaggerPath
...
...
@@ -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
)
{
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
)
{
ref
:=
Resource
{
Path
:
k
}
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
}
listing
.
Apis
=
append
(
listing
.
Apis
,
ref
)
})
re
sp
.
WriteAsJson
(
listing
)
re
turn
listing
}
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
{
resp
.
WriteErrorString
(
http
.
StatusNotFound
,
"ApiDeclaration not found"
)
return
...
...
@@ -180,11 +186,28 @@ func (sws SwaggerService) getDeclarations(req *restful.Request, resp *restful.Re
scheme
=
"https"
}
}
(
&
decl
)
.
BasePath
=
fmt
.
Sprintf
(
"%s://%s"
,
scheme
,
host
)
decl
.
BasePath
=
fmt
.
Sprintf
(
"%s://%s"
,
scheme
,
host
)
}
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
func
(
sws
SwaggerService
)
composeDeclaration
(
ws
*
restful
.
WebService
,
pathPrefix
string
)
ApiDeclaration
{
decl
:=
ApiDeclaration
{
...
...
@@ -207,13 +230,15 @@ func (sws SwaggerService) composeDeclaration(ws *restful.WebService, pathPrefix
}
}
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
{
operation
:=
Operation
{
Method
:
route
.
Method
,
Summary
:
route
.
Doc
,
Notes
:
route
.
Notes
,
Type
:
asDataType
(
route
.
WriteSample
),
Method
:
route
.
Method
,
Summary
:
route
.
Doc
,
Notes
:
route
.
Notes
,
// Type gets overwritten if there is a write sample
DataTypeFields
:
DataTypeFields
{
Type
:
&
voidString
},
Parameters
:
[]
Parameter
{},
Nickname
:
route
.
Operation
,
ResponseMessages
:
composeResponseMessages
(
route
,
&
decl
)}
...
...
@@ -238,6 +263,13 @@ func (sws SwaggerService) composeDeclaration(ws *restful.WebService, pathPrefix
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.
func
composeResponseMessages
(
route
restful
.
Route
,
decl
*
ApiDeclaration
)
(
messages
[]
ResponseMessage
)
{
if
route
.
ResponseErrors
==
nil
{
...
...
@@ -299,14 +331,10 @@ func detectCollectionType(st reflect.Type) (bool, reflect.Type) {
// addModelFromSample creates and adds (or overwrites) a Model from a sample resource
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
isCollection
{
modelName
=
"array["
+
modelName
+
"]"
}
operation
.
Type
=
modelName
type_
,
items
:=
asDataType
(
sample
)
operation
.
Type
=
type_
operation
.
Items
=
items
}
modelBuilder
{
models
}
.
addModelFrom
(
sample
)
}
...
...
@@ -315,7 +343,7 @@ func asSwaggerParameter(param restful.ParameterData) Parameter {
return
Parameter
{
DataTypeFields
:
DataTypeFields
{
Type
:
&
param
.
DataType
,
Format
:
asFormat
(
param
.
DataType
),
Format
:
asFormat
(
param
.
DataType
,
param
.
DataFormat
),
DefaultValue
:
Special
(
param
.
DefaultValue
),
},
Name
:
param
.
Name
,
...
...
@@ -360,7 +388,10 @@ func composeRootPath(req *restful.Request) string {
return
path
+
"/"
+
g
}
func
asFormat
(
name
string
)
string
{
func
asFormat
(
dataType
string
,
dataFormat
string
)
string
{
if
dataFormat
!=
""
{
return
dataFormat
}
return
""
// TODO
}
...
...
@@ -380,9 +411,30 @@ func asParamType(kind int) string {
return
""
}
func
asDataType
(
any
interface
{})
string
{
if
any
==
nil
{
return
"void"
func
asDataType
(
any
interface
{})
(
*
string
,
*
Item
)
{
// If it's not a collection, return the suggested model name
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
import
(
"fmt"
"os"
"sync"
"github.com/emicklei/go-restful/log"
)
...
...
@@ -21,6 +23,15 @@ type WebService struct {
filters
[]
FilterFunction
documentation
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.
...
...
@@ -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.
func
(
w
*
WebService
)
Route
(
builder
*
RouteBuilder
)
*
WebService
{
w
.
routesLock
.
Lock
()
defer
w
.
routesLock
.
Unlock
()
builder
.
copyDefaults
(
w
.
produces
,
w
.
consumes
)
w
.
routes
=
append
(
w
.
routes
,
builder
.
Build
())
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
func
(
w
*
WebService
)
Method
(
httpMethod
string
)
*
RouteBuilder
{
return
new
(
RouteBuilder
)
.
servicePath
(
w
.
rootPath
)
.
Method
(
httpMethod
)
...
...
@@ -160,7 +188,17 @@ func (w *WebService) Consumes(accepts ...string) *WebService {
// Routes returns the Routes associated with this WebService
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 "/"
...
...
api/swagger-spec/v1.json
View file @
2fcf1575
...
...
@@ -3168,7 +3168,7 @@
]
},
{
"path"
:
"/api/v1/proxy/nodes/{name}/{path
:*
}"
,
"path"
:
"/api/v1/proxy/nodes/{name}/{path}"
,
"description"
:
"API at /api/v1"
,
"operations"
:
[
{
...
...
@@ -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"
,
"operations"
:
[
{
...
...
@@ -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"
,
"operations"
:
[
{
...
...
@@ -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"
,
"operations"
:
[
{
...
...
docs/api-reference/v1/operations.html
View file @
2fcf1575
...
...
@@ -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>
<div
class=
"listingblock"
>
<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
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<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
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<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
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<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
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<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
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<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
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<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
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<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
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<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
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<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
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<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
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<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
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<pre>
GET /api/v1/proxy/nodes/{name}/{path
:*
}
</pre>
<pre>
GET /api/v1/proxy/nodes/{name}/{path}
</pre>
</div>
</div>
<div
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<pre>
PUT /api/v1/proxy/nodes/{name}/{path
:*
}
</pre>
<pre>
PUT /api/v1/proxy/nodes/{name}/{path}
</pre>
</div>
</div>
<div
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<pre>
DELETE /api/v1/proxy/nodes/{name}/{path
:*
}
</pre>
<pre>
DELETE /api/v1/proxy/nodes/{name}/{path}
</pre>
</div>
</div>
<div
class=
"sect3"
>
...
...
@@ -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>
<div
class=
"listingblock"
>
<div
class=
"content"
>
<pre>
POST /api/v1/proxy/nodes/{name}/{path
:*
}
</pre>
<pre>
POST /api/v1/proxy/nodes/{name}/{path}
</pre>
</div>
</div>
<div
class=
"sect3"
>
...
...
@@ -23664,7 +23664,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div>
<div
id=
"footer"
>
<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>
</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