mirror of https://github.com/k3s-io/k3s
Update go-restful
parent
b6f2f396ba
commit
535c509dd3
|
@ -215,8 +215,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/emicklei/go-restful",
|
"ImportPath": "github.com/emicklei/go-restful",
|
||||||
"Comment": "v1.1.3-76-gbfd6ff2",
|
"Comment": "v1.1.3-98-g1f9a0ee",
|
||||||
"Rev": "bfd6ff29d2961031cec64346a92bae4cde96c868"
|
"Rev": "1f9a0ee00ff93717a275e15b30cf7df356255877"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/evanphx/json-patch",
|
"ImportPath": "github.com/evanphx/json-patch",
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
Change history of go-restful
|
Change history of go-restful
|
||||||
=
|
=
|
||||||
|
2015-08-06
|
||||||
|
- add support for reading entities from compressed request content
|
||||||
|
- use sync.Pool for compressors of http response and request body
|
||||||
|
- add Description to Parameter for documentation in Swagger UI
|
||||||
|
|
||||||
2015-03-20
|
2015-03-20
|
||||||
- add configurable logging
|
- add configurable logging
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ func (u UserResource) findUser(request *restful.Request, response *restful.Respo
|
||||||
- Filters for intercepting the request → response flow on Service or Route level
|
- Filters for intercepting the request → response flow on Service or Route level
|
||||||
- Request-scoped variables using attributes
|
- Request-scoped variables using attributes
|
||||||
- Containers for WebServices on different HTTP endpoints
|
- Containers for WebServices on different HTTP endpoints
|
||||||
- Content encoding (gzip,deflate) of responses
|
- Content encoding (gzip,deflate) of request and response payloads
|
||||||
- Automatic responses on OPTIONS (using a filter)
|
- Automatic responses on OPTIONS (using a filter)
|
||||||
- Automatic CORS request handling (using a filter)
|
- Automatic CORS request handling (using a filter)
|
||||||
- API declaration for Swagger UI (see swagger package)
|
- API declaration for Swagger UI (see swagger package)
|
||||||
|
|
|
@ -73,15 +73,13 @@ 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 {
|
||||||
c.compressor, err = gzip.NewWriterLevel(httpWriter, gzip.BestSpeed)
|
w := GzipWriterPool.Get().(*gzip.Writer)
|
||||||
if err != nil {
|
w.Reset(httpWriter)
|
||||||
return nil, err
|
c.compressor = w
|
||||||
}
|
|
||||||
} else if ENCODING_DEFLATE == encoding {
|
} else if ENCODING_DEFLATE == encoding {
|
||||||
c.compressor, err = zlib.NewWriterLevel(httpWriter, zlib.BestSpeed)
|
w := ZlibWriterPool.Get().(*zlib.Writer)
|
||||||
if err != nil {
|
w.Reset(httpWriter)
|
||||||
return nil, err
|
c.compressor = w
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("Unknown encoding:" + encoding)
|
return nil, errors.New("Unknown encoding:" + encoding)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
package restful
|
package restful
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"compress/zlib"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// go test -v -test.run TestGzip ...restful
|
||||||
func TestGzip(t *testing.T) {
|
func TestGzip(t *testing.T) {
|
||||||
EnableContentEncoding = true
|
EnableContentEncoding = true
|
||||||
httpRequest, _ := http.NewRequest("GET", "/test", nil)
|
httpRequest, _ := http.NewRequest("GET", "/test", nil)
|
||||||
|
@ -27,6 +33,17 @@ func TestGzip(t *testing.T) {
|
||||||
if httpWriter.Header().Get("Content-Encoding") != "gzip" {
|
if httpWriter.Header().Get("Content-Encoding") != "gzip" {
|
||||||
t.Fatal("Missing gzip header")
|
t.Fatal("Missing gzip header")
|
||||||
}
|
}
|
||||||
|
reader, err := gzip.NewReader(httpWriter.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
if got, want := string(data), "Hello World"; got != want {
|
||||||
|
t.Errorf("got %v want %v", got, want)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeflate(t *testing.T) {
|
func TestDeflate(t *testing.T) {
|
||||||
|
@ -50,4 +67,61 @@ func TestDeflate(t *testing.T) {
|
||||||
if httpWriter.Header().Get("Content-Encoding") != "deflate" {
|
if httpWriter.Header().Get("Content-Encoding") != "deflate" {
|
||||||
t.Fatal("Missing deflate header")
|
t.Fatal("Missing deflate header")
|
||||||
}
|
}
|
||||||
|
reader, err := zlib.NewReader(httpWriter.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
if got, want := string(data), "Hello World"; got != want {
|
||||||
|
t.Errorf("got %v want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGzipDecompressRequestBody(t *testing.T) {
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
w := newGzipWriter()
|
||||||
|
w.Reset(b)
|
||||||
|
io.WriteString(w, `{"msg":"hi"}`)
|
||||||
|
w.Flush()
|
||||||
|
w.Close()
|
||||||
|
|
||||||
|
req := new(Request)
|
||||||
|
httpRequest, _ := http.NewRequest("GET", "/", bytes.NewReader(b.Bytes()))
|
||||||
|
httpRequest.Header.Set("Content-Type", "application/json")
|
||||||
|
httpRequest.Header.Set("Content-Encoding", "gzip")
|
||||||
|
req.Request = httpRequest
|
||||||
|
|
||||||
|
doCacheReadEntityBytes = false
|
||||||
|
doc := make(map[string]interface{})
|
||||||
|
req.ReadEntity(&doc)
|
||||||
|
|
||||||
|
if got, want := doc["msg"], "hi"; got != want {
|
||||||
|
t.Errorf("got %v want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestZlibDecompressRequestBody(t *testing.T) {
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
w := newZlibWriter()
|
||||||
|
w.Reset(b)
|
||||||
|
io.WriteString(w, `{"msg":"hi"}`)
|
||||||
|
w.Flush()
|
||||||
|
w.Close()
|
||||||
|
|
||||||
|
req := new(Request)
|
||||||
|
httpRequest, _ := http.NewRequest("GET", "/", bytes.NewReader(b.Bytes()))
|
||||||
|
httpRequest.Header.Set("Content-Type", "application/json")
|
||||||
|
httpRequest.Header.Set("Content-Encoding", "deflate")
|
||||||
|
req.Request = httpRequest
|
||||||
|
|
||||||
|
doCacheReadEntityBytes = false
|
||||||
|
doc := make(map[string]interface{})
|
||||||
|
req.ReadEntity(&doc)
|
||||||
|
|
||||||
|
if got, want := doc["msg"], "hi"; got != want {
|
||||||
|
t.Errorf("got %v want %v", got, want)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
63
Godeps/_workspace/src/github.com/emicklei/go-restful/compressor_pools.go
generated
vendored
Normal file
63
Godeps/_workspace/src/github.com/emicklei/go-restful/compressor_pools.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package restful
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"compress/zlib"
|
||||||
|
"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()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGzipWriter() *gzip.Writer {
|
||||||
|
// create with an empty bytes writer; it will be replaced before using the gzipWriter
|
||||||
|
writer, err := gzip.NewWriterLevel(new(bytes.Buffer), gzip.BestSpeed)
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
w.Reset(b)
|
||||||
|
w.Flush()
|
||||||
|
w.Close()
|
||||||
|
reader, err := gzip.NewReader(bytes.NewReader(b.Bytes()))
|
||||||
|
if err != nil {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
panic(err.Error())
|
||||||
|
}
|
||||||
|
return writer
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/emicklei/go-restful/log"
|
"github.com/emicklei/go-restful/log"
|
||||||
)
|
)
|
||||||
|
@ -18,6 +19,7 @@ import (
|
||||||
// Container holds a collection of WebServices and a http.ServeMux to dispatch http requests.
|
// Container holds a collection of WebServices and a http.ServeMux to dispatch http requests.
|
||||||
// The requests are further dispatched to routes of WebServices using a RouteSelector
|
// The requests are further dispatched to routes of WebServices using a RouteSelector
|
||||||
type Container struct {
|
type Container struct {
|
||||||
|
webServicesLock sync.RWMutex
|
||||||
webServices []*WebService
|
webServices []*WebService
|
||||||
ServeMux *http.ServeMux
|
ServeMux *http.ServeMux
|
||||||
isRegisteredOnRoot bool
|
isRegisteredOnRoot bool
|
||||||
|
@ -83,6 +85,8 @@ func (c *Container) EnableContentEncoding(enabled bool) {
|
||||||
|
|
||||||
// Add a WebService to the Container. It will detect duplicate root paths and panic in that case.
|
// Add a WebService to the Container. It will detect duplicate root paths and panic in that case.
|
||||||
func (c *Container) Add(service *WebService) *Container {
|
func (c *Container) Add(service *WebService) *Container {
|
||||||
|
c.webServicesLock.Lock()
|
||||||
|
defer c.webServicesLock.Unlock()
|
||||||
// If registered on root then no additional specific mapping is needed
|
// If registered on root then no additional specific mapping is needed
|
||||||
if !c.isRegisteredOnRoot {
|
if !c.isRegisteredOnRoot {
|
||||||
pattern := c.fixedPrefixPath(service.RootPath())
|
pattern := c.fixedPrefixPath(service.RootPath())
|
||||||
|
@ -122,6 +126,19 @@ func (c *Container) Add(service *WebService) *Container {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Container) Remove(ws *WebService) error {
|
||||||
|
c.webServicesLock.Lock()
|
||||||
|
defer c.webServicesLock.Unlock()
|
||||||
|
newServices := []*WebService{}
|
||||||
|
for ix := range c.webServices {
|
||||||
|
if c.webServices[ix].rootPath != ws.rootPath {
|
||||||
|
newServices = append(newServices, c.webServices[ix])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.webServices = newServices
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// logStackOnRecover is the default RecoverHandleFunction and is called
|
// logStackOnRecover is the default RecoverHandleFunction and is called
|
||||||
// when DoNotRecover is false and the recoverHandleFunc is not set for the container.
|
// when DoNotRecover is false and the recoverHandleFunc is not set for the container.
|
||||||
// Default implementation logs the stacktrace and writes the stacktrace on the response.
|
// Default implementation logs the stacktrace and writes the stacktrace on the response.
|
||||||
|
@ -190,9 +207,16 @@ func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.R
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Find best match Route ; err is non nil if no match was found
|
// Find best match Route ; err is non nil if no match was found
|
||||||
webService, route, err := c.router.SelectRoute(
|
var webService *WebService
|
||||||
|
var route *Route
|
||||||
|
var err error
|
||||||
|
func() {
|
||||||
|
c.webServicesLock.RLock()
|
||||||
|
defer c.webServicesLock.RUnlock()
|
||||||
|
webService, route, err = c.router.SelectRoute(
|
||||||
c.webServices,
|
c.webServices,
|
||||||
httpRequest)
|
httpRequest)
|
||||||
|
}()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// a non-200 response has already been written
|
// a non-200 response has already been written
|
||||||
// run container filters anyway ; they should not touch the response...
|
// run container filters anyway ; they should not touch the response...
|
||||||
|
@ -272,7 +296,13 @@ func (c *Container) Filter(filter FilterFunction) {
|
||||||
|
|
||||||
// RegisteredWebServices returns the collections of added WebServices
|
// RegisteredWebServices returns the collections of added WebServices
|
||||||
func (c Container) RegisteredWebServices() []*WebService {
|
func (c Container) RegisteredWebServices() []*WebService {
|
||||||
return c.webServices
|
c.webServicesLock.RLock()
|
||||||
|
defer c.webServicesLock.RUnlock()
|
||||||
|
result := make([]*WebService, len(c.webServices))
|
||||||
|
for ix := range c.webServices {
|
||||||
|
result[ix] = c.webServices[ix]
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// computeAllowedMethods returns a list of HTTP methods that are valid for a Request
|
// computeAllowedMethods returns a list of HTTP methods that are valid for a Request
|
||||||
|
|
|
@ -95,8 +95,14 @@ func (p *Parameter) DataType(typeName string) *Parameter {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultValue sets the default value field and returnw 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
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Description sets the description value field and returns the receiver
|
||||||
|
func (p *Parameter) Description(doc string) *Parameter {
|
||||||
|
p.data.Description = doc
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ package restful
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"compress/zlib"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"io"
|
"io"
|
||||||
|
@ -82,15 +84,17 @@ func (r *Request) HeaderParameter(name string) string {
|
||||||
// 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
|
// 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)
|
||||||
if doCacheReadEntityBytes {
|
if doCacheReadEntityBytes {
|
||||||
return r.cachingReadEntity(contentType, entityPointer)
|
return r.cachingReadEntity(contentType, contentEncoding, entityPointer)
|
||||||
}
|
}
|
||||||
// unmarshall directly from request Body
|
// unmarshall directly from request Body
|
||||||
return r.decodeEntity(r.Request.Body, contentType, entityPointer)
|
return r.decodeEntity(r.Request.Body, contentType, contentEncoding, entityPointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Request) cachingReadEntity(contentType string, entityPointer interface{}) (err error) {
|
func (r *Request) cachingReadEntity(contentType string, contentEncoding string, entityPointer interface{}) (err error) {
|
||||||
var buffer []byte
|
var buffer []byte
|
||||||
if r.bodyContent != nil {
|
if r.bodyContent != nil {
|
||||||
buffer = *r.bodyContent
|
buffer = *r.bodyContent
|
||||||
|
@ -101,22 +105,38 @@ func (r *Request) cachingReadEntity(contentType string, entityPointer interface{
|
||||||
}
|
}
|
||||||
r.bodyContent = &buffer
|
r.bodyContent = &buffer
|
||||||
}
|
}
|
||||||
return r.decodeEntity(bytes.NewReader(buffer), contentType, entityPointer)
|
return r.decodeEntity(bytes.NewReader(buffer), contentType, contentEncoding, entityPointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Request) decodeEntity(reader io.Reader, contentType string, entityPointer interface{}) (err error) {
|
func (r *Request) decodeEntity(reader io.Reader, contentType string, contentEncoding string, entityPointer interface{}) (err error) {
|
||||||
if strings.Contains(contentType, MIME_XML) {
|
entityReader := reader
|
||||||
return xml.NewDecoder(reader).Decode(entityPointer)
|
|
||||||
|
// check if the request body needs decompression
|
||||||
|
if ENCODING_GZIP == contentEncoding {
|
||||||
|
gzipReader := GzipReaderPool.Get().(*gzip.Reader)
|
||||||
|
gzipReader.Reset(reader)
|
||||||
|
entityReader = gzipReader
|
||||||
|
} else if ENCODING_DEFLATE == contentEncoding {
|
||||||
|
zlibReader, err := zlib.NewReader(reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
entityReader = zlibReader
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode JSON
|
||||||
if strings.Contains(contentType, MIME_JSON) || MIME_JSON == defaultRequestContentType {
|
if strings.Contains(contentType, MIME_JSON) || MIME_JSON == defaultRequestContentType {
|
||||||
decoder := json.NewDecoder(reader)
|
decoder := json.NewDecoder(entityReader)
|
||||||
decoder.UseNumber()
|
decoder.UseNumber()
|
||||||
return decoder.Decode(entityPointer)
|
return decoder.Decode(entityPointer)
|
||||||
}
|
}
|
||||||
if MIME_XML == defaultRequestContentType {
|
|
||||||
return xml.NewDecoder(reader).Decode(entityPointer)
|
// decode XML
|
||||||
|
if strings.Contains(contentType, MIME_XML) || MIME_XML == defaultRequestContentType {
|
||||||
|
return xml.NewDecoder(entityReader).Decode(entityPointer)
|
||||||
}
|
}
|
||||||
return NewError(400, "Unable to unmarshal content of type:"+contentType)
|
|
||||||
|
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.
|
||||||
|
|
|
@ -28,11 +28,12 @@ type Response struct {
|
||||||
statusCode int // HTTP status code that has been written explicity (if zero then net/http has written 200)
|
statusCode int // HTTP status code that has been written explicity (if zero then net/http has written 200)
|
||||||
contentLength int // number of bytes written for the response body
|
contentLength int // number of bytes written for the response body
|
||||||
prettyPrint bool // controls the indentation feature of XML and JSON serialization. It is initialized using var PrettyPrintResponses.
|
prettyPrint bool // controls the indentation feature of XML and JSON serialization. It is initialized using var PrettyPrintResponses.
|
||||||
|
err error // err property is kept when WriteError is called
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new response based on a http ResponseWriter.
|
// Creates a new response based on a http ResponseWriter.
|
||||||
func NewResponse(httpWriter http.ResponseWriter) *Response {
|
func NewResponse(httpWriter http.ResponseWriter) *Response {
|
||||||
return &Response{httpWriter, "", []string{}, http.StatusOK, 0, PrettyPrintResponses} // empty content-types
|
return &Response{httpWriter, "", []string{}, http.StatusOK, 0, PrettyPrintResponses, nil} // empty content-types
|
||||||
}
|
}
|
||||||
|
|
||||||
// If Accept header matching fails, fall back to this type, otherwise
|
// If Accept header matching fails, fall back to this type, otherwise
|
||||||
|
@ -182,6 +183,7 @@ func (r *Response) WriteJson(value interface{}, contentType string) error {
|
||||||
|
|
||||||
// WriteError write the http status and the error string on the response.
|
// WriteError write the http status and the error string on the response.
|
||||||
func (r *Response) WriteError(httpStatus int, err error) error {
|
func (r *Response) WriteError(httpStatus int, err error) error {
|
||||||
|
r.err = err
|
||||||
return r.WriteErrorString(httpStatus, err.Error())
|
return r.WriteErrorString(httpStatus, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,21 +205,30 @@ func (r *Response) WriteErrorString(status int, errorReason string) error {
|
||||||
|
|
||||||
// WriteHeader is overridden to remember the Status Code that has been written.
|
// WriteHeader is overridden to remember the Status Code that has been written.
|
||||||
// Note that using this method, the status value is only written when
|
// Note that using this method, the status value is only written when
|
||||||
// - calling WriteEntity,
|
// calling WriteEntity,
|
||||||
// - or directly calling WriteAsXml or WriteAsJson,
|
// or directly calling WriteAsXml or WriteAsJson,
|
||||||
// - or if the status is one for which no response is allowed (i.e.,
|
// or if the status is one for which no response is allowed:
|
||||||
// 204 (http.StatusNoContent) or 304 (http.StatusNotModified))
|
//
|
||||||
|
// 202 = http.StatusAccepted
|
||||||
|
// 204 = http.StatusNoContent
|
||||||
|
// 206 = http.StatusPartialContent
|
||||||
|
// 304 = http.StatusNotModified
|
||||||
|
//
|
||||||
|
// If this behavior does not fit your need then you can write to the underlying response, such as:
|
||||||
|
// response.ResponseWriter.WriteHeader(http.StatusAccepted)
|
||||||
func (r *Response) WriteHeader(httpStatus int) {
|
func (r *Response) WriteHeader(httpStatus int) {
|
||||||
r.statusCode = httpStatus
|
r.statusCode = httpStatus
|
||||||
// if 201,204,304 then WriteEntity will not be called so we need to pass this code
|
// if 202,204,206,304 then WriteEntity will not be called so we need to pass this code
|
||||||
if http.StatusNoContent == httpStatus ||
|
if http.StatusNoContent == httpStatus ||
|
||||||
http.StatusNotModified == httpStatus ||
|
http.StatusNotModified == httpStatus ||
|
||||||
http.StatusPartialContent == httpStatus {
|
http.StatusPartialContent == httpStatus ||
|
||||||
|
http.StatusAccepted == httpStatus {
|
||||||
r.ResponseWriter.WriteHeader(httpStatus)
|
r.ResponseWriter.WriteHeader(httpStatus)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatusCode returns the code that has been written using WriteHeader.
|
// StatusCode returns the code that has been written using WriteHeader.
|
||||||
|
// If WriteHeader, WriteEntity or WriteAsXml has not been called (yet) then return 200 OK.
|
||||||
func (r Response) StatusCode() int {
|
func (r Response) StatusCode() int {
|
||||||
if 0 == r.statusCode {
|
if 0 == r.statusCode {
|
||||||
// no status code has been written yet; assume OK
|
// no status code has been written yet; assume OK
|
||||||
|
@ -245,3 +256,8 @@ func (r Response) ContentLength() int {
|
||||||
func (r Response) CloseNotify() <-chan bool {
|
func (r Response) CloseNotify() <-chan bool {
|
||||||
return r.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
return r.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error returns the err created by WriteError
|
||||||
|
func (r Response) Error() error {
|
||||||
|
return r.err
|
||||||
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
func TestWriteHeader(t *testing.T) {
|
func TestWriteHeader(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||||
resp.WriteHeader(123)
|
resp.WriteHeader(123)
|
||||||
if resp.StatusCode() != 123 {
|
if resp.StatusCode() != 123 {
|
||||||
t.Errorf("Unexpected status code:%d", resp.StatusCode())
|
t.Errorf("Unexpected status code:%d", resp.StatusCode())
|
||||||
|
@ -18,7 +18,7 @@ func TestWriteHeader(t *testing.T) {
|
||||||
|
|
||||||
func TestNoWriteHeader(t *testing.T) {
|
func TestNoWriteHeader(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||||
if resp.StatusCode() != http.StatusOK {
|
if resp.StatusCode() != http.StatusOK {
|
||||||
t.Errorf("Unexpected status code:%d", resp.StatusCode())
|
t.Errorf("Unexpected status code:%d", resp.StatusCode())
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ type food struct {
|
||||||
// go test -v -test.run TestMeasureContentLengthXml ...restful
|
// go test -v -test.run TestMeasureContentLengthXml ...restful
|
||||||
func TestMeasureContentLengthXml(t *testing.T) {
|
func TestMeasureContentLengthXml(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||||
resp.WriteAsXml(food{"apple"})
|
resp.WriteAsXml(food{"apple"})
|
||||||
if resp.ContentLength() != 76 {
|
if resp.ContentLength() != 76 {
|
||||||
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
||||||
|
@ -41,7 +41,7 @@ func TestMeasureContentLengthXml(t *testing.T) {
|
||||||
// go test -v -test.run TestMeasureContentLengthJson ...restful
|
// go test -v -test.run TestMeasureContentLengthJson ...restful
|
||||||
func TestMeasureContentLengthJson(t *testing.T) {
|
func TestMeasureContentLengthJson(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||||
resp.WriteAsJson(food{"apple"})
|
resp.WriteAsJson(food{"apple"})
|
||||||
if resp.ContentLength() != 22 {
|
if resp.ContentLength() != 22 {
|
||||||
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
||||||
|
@ -51,7 +51,7 @@ func TestMeasureContentLengthJson(t *testing.T) {
|
||||||
// go test -v -test.run TestMeasureContentLengthJsonNotPretty ...restful
|
// go test -v -test.run TestMeasureContentLengthJsonNotPretty ...restful
|
||||||
func TestMeasureContentLengthJsonNotPretty(t *testing.T) {
|
func TestMeasureContentLengthJsonNotPretty(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, false}
|
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, false, nil}
|
||||||
resp.WriteAsJson(food{"apple"})
|
resp.WriteAsJson(food{"apple"})
|
||||||
if resp.ContentLength() != 16 {
|
if resp.ContentLength() != 16 {
|
||||||
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
||||||
|
@ -61,7 +61,7 @@ func TestMeasureContentLengthJsonNotPretty(t *testing.T) {
|
||||||
// go test -v -test.run TestMeasureContentLengthWriteErrorString ...restful
|
// go test -v -test.run TestMeasureContentLengthWriteErrorString ...restful
|
||||||
func TestMeasureContentLengthWriteErrorString(t *testing.T) {
|
func TestMeasureContentLengthWriteErrorString(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||||
resp.WriteErrorString(404, "Invalid")
|
resp.WriteErrorString(404, "Invalid")
|
||||||
if resp.ContentLength() != len("Invalid") {
|
if resp.ContentLength() != len("Invalid") {
|
||||||
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
t.Errorf("Incorrect measured length:%d", resp.ContentLength())
|
||||||
|
@ -79,7 +79,7 @@ func TestStatusIsPassedToResponse(t *testing.T) {
|
||||||
{write: 400, read: 200},
|
{write: 400, read: 200},
|
||||||
} {
|
} {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true}
|
resp := Response{httpWriter, "*/*", []string{"*/*"}, 0, 0, true, nil}
|
||||||
resp.WriteHeader(each.write)
|
resp.WriteHeader(each.write)
|
||||||
if got, want := httpWriter.Code, each.read; got != want {
|
if got, want := httpWriter.Code, each.read; got != want {
|
||||||
t.Error("got %v want %v", got, want)
|
t.Error("got %v want %v", got, want)
|
||||||
|
@ -90,7 +90,7 @@ func TestStatusIsPassedToResponse(t *testing.T) {
|
||||||
// go test -v -test.run TestStatusCreatedAndContentTypeJson_Issue54 ...restful
|
// go test -v -test.run TestStatusCreatedAndContentTypeJson_Issue54 ...restful
|
||||||
func TestStatusCreatedAndContentTypeJson_Issue54(t *testing.T) {
|
func TestStatusCreatedAndContentTypeJson_Issue54(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true}
|
resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true, nil}
|
||||||
resp.WriteHeader(201)
|
resp.WriteHeader(201)
|
||||||
resp.WriteAsJson(food{"Juicy"})
|
resp.WriteAsJson(food{"Juicy"})
|
||||||
if httpWriter.HeaderMap.Get("Content-Type") != "application/json" {
|
if httpWriter.HeaderMap.Get("Content-Type") != "application/json" {
|
||||||
|
@ -112,7 +112,7 @@ func (e errorOnWriteRecorder) Write(bytes []byte) (int, error) {
|
||||||
// go test -v -test.run TestLastWriteErrorCaught ...restful
|
// go test -v -test.run TestLastWriteErrorCaught ...restful
|
||||||
func TestLastWriteErrorCaught(t *testing.T) {
|
func TestLastWriteErrorCaught(t *testing.T) {
|
||||||
httpWriter := errorOnWriteRecorder{httptest.NewRecorder()}
|
httpWriter := errorOnWriteRecorder{httptest.NewRecorder()}
|
||||||
resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true}
|
resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true, nil}
|
||||||
err := resp.WriteAsJson(food{"Juicy"})
|
err := resp.WriteAsJson(food{"Juicy"})
|
||||||
if err.Error() != "fail" {
|
if err.Error() != "fail" {
|
||||||
t.Errorf("Unexpected error message:%v", err)
|
t.Errorf("Unexpected error message:%v", err)
|
||||||
|
@ -123,7 +123,7 @@ func TestLastWriteErrorCaught(t *testing.T) {
|
||||||
func TestAcceptStarStar_Issue83(t *testing.T) {
|
func TestAcceptStarStar_Issue83(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
// Accept Produces
|
// Accept Produces
|
||||||
resp := Response{httpWriter, "application/bogus,*/*;q=0.8", []string{"application/json"}, 0, 0, true}
|
resp := Response{httpWriter, "application/bogus,*/*;q=0.8", []string{"application/json"}, 0, 0, true, nil}
|
||||||
resp.WriteEntity(food{"Juicy"})
|
resp.WriteEntity(food{"Juicy"})
|
||||||
ct := httpWriter.Header().Get("Content-Type")
|
ct := httpWriter.Header().Get("Content-Type")
|
||||||
if "application/json" != ct {
|
if "application/json" != ct {
|
||||||
|
@ -135,7 +135,7 @@ func TestAcceptStarStar_Issue83(t *testing.T) {
|
||||||
func TestAcceptSkipStarStar_Issue83(t *testing.T) {
|
func TestAcceptSkipStarStar_Issue83(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
// Accept Produces
|
// Accept Produces
|
||||||
resp := Response{httpWriter, " application/xml ,*/* ; q=0.8", []string{"application/json", "application/xml"}, 0, 0, true}
|
resp := Response{httpWriter, " application/xml ,*/* ; q=0.8", []string{"application/json", "application/xml"}, 0, 0, true, nil}
|
||||||
resp.WriteEntity(food{"Juicy"})
|
resp.WriteEntity(food{"Juicy"})
|
||||||
ct := httpWriter.Header().Get("Content-Type")
|
ct := httpWriter.Header().Get("Content-Type")
|
||||||
if "application/xml" != ct {
|
if "application/xml" != ct {
|
||||||
|
@ -147,7 +147,7 @@ func TestAcceptSkipStarStar_Issue83(t *testing.T) {
|
||||||
func TestAcceptXmlBeforeStarStar_Issue83(t *testing.T) {
|
func TestAcceptXmlBeforeStarStar_Issue83(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
// Accept Produces
|
// Accept Produces
|
||||||
resp := Response{httpWriter, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", []string{"application/json"}, 0, 0, true}
|
resp := Response{httpWriter, "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", []string{"application/json"}, 0, 0, true, nil}
|
||||||
resp.WriteEntity(food{"Juicy"})
|
resp.WriteEntity(food{"Juicy"})
|
||||||
ct := httpWriter.Header().Get("Content-Type")
|
ct := httpWriter.Header().Get("Content-Type")
|
||||||
if "application/json" != ct {
|
if "application/json" != ct {
|
||||||
|
@ -158,7 +158,7 @@ func TestAcceptXmlBeforeStarStar_Issue83(t *testing.T) {
|
||||||
// go test -v -test.run TestWriteHeaderNoContent_Issue124 ...restful
|
// go test -v -test.run TestWriteHeaderNoContent_Issue124 ...restful
|
||||||
func TestWriteHeaderNoContent_Issue124(t *testing.T) {
|
func TestWriteHeaderNoContent_Issue124(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "text/plain", []string{"text/plain"}, 0, 0, true}
|
resp := Response{httpWriter, "text/plain", []string{"text/plain"}, 0, 0, true, nil}
|
||||||
resp.WriteHeader(http.StatusNoContent)
|
resp.WriteHeader(http.StatusNoContent)
|
||||||
if httpWriter.Code != http.StatusNoContent {
|
if httpWriter.Code != http.StatusNoContent {
|
||||||
t.Errorf("got %d want %d", httpWriter.Code, http.StatusNoContent)
|
t.Errorf("got %d want %d", httpWriter.Code, http.StatusNoContent)
|
||||||
|
@ -168,7 +168,7 @@ func TestWriteHeaderNoContent_Issue124(t *testing.T) {
|
||||||
// go test -v -test.run TestStatusCreatedAndContentTypeJson_Issue163 ...restful
|
// go test -v -test.run TestStatusCreatedAndContentTypeJson_Issue163 ...restful
|
||||||
func TestStatusCreatedAndContentTypeJson_Issue163(t *testing.T) {
|
func TestStatusCreatedAndContentTypeJson_Issue163(t *testing.T) {
|
||||||
httpWriter := httptest.NewRecorder()
|
httpWriter := httptest.NewRecorder()
|
||||||
resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true}
|
resp := Response{httpWriter, "application/json", []string{"application/json"}, 0, 0, true, nil}
|
||||||
resp.WriteHeader(http.StatusNotModified)
|
resp.WriteHeader(http.StatusNotModified)
|
||||||
if httpWriter.Code != http.StatusNotModified {
|
if httpWriter.Code != http.StatusNotModified {
|
||||||
t.Errorf("Got %d want %d", httpWriter.Code, http.StatusNotModified)
|
t.Errorf("Got %d want %d", httpWriter.Code, http.StatusNotModified)
|
||||||
|
|
9
Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_webservice.go
generated
vendored
9
Godeps/_workspace/src/github.com/emicklei/go-restful/swagger/swagger_webservice.go
generated
vendored
|
@ -173,7 +173,14 @@ func (sws SwaggerService) getDeclarations(req *restful.Request, resp *restful.Re
|
||||||
} else {
|
} else {
|
||||||
host = hostvalues[0]
|
host = hostvalues[0]
|
||||||
}
|
}
|
||||||
(&decl).BasePath = fmt.Sprintf("http://%s", host)
|
// inspect Referer for the scheme (http vs https)
|
||||||
|
scheme := "http"
|
||||||
|
if referer := req.Request.Header["Referer"]; len(referer) > 0 {
|
||||||
|
if strings.HasPrefix(referer[0], "https") {
|
||||||
|
scheme = "https"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(&decl).BasePath = fmt.Sprintf("%s://%s", scheme, host)
|
||||||
}
|
}
|
||||||
resp.WriteAsJson(decl)
|
resp.WriteAsJson(decl)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue