Updating go-restful dependency to fix thirdparty

This commit is contained in:
Ruddarraju, Uday Kumar Raju 2016-04-11 19:51:45 -07:00
parent 31de62216d
commit 6df648d018
14 changed files with 180 additions and 45 deletions

4
Godeps/Godeps.json generated
View File

@ -492,8 +492,8 @@
},
{
"ImportPath": "github.com/emicklei/go-restful",
"Comment": "v1.2",
"Rev": "777bb3f19bcafe2575ffb2a3e46af92509ae9594"
"Comment": "v1.2-34-g496d495",
"Rev": "496d495156da218b9912f03dfa7df7f80fbd8cc3"
},
{
"ImportPath": "github.com/evanphx/json-patch",

View File

@ -1,5 +1,9 @@
Change history of go-restful
=
2016-02-14
- take the qualify factor of the Accept header mediatype into account when deciding the contentype of the response
- add constructors for custom entity accessors for xml and json
2015-09-27
- rename new WriteStatusAnd... to WriteHeaderAnd... for consistency

View File

@ -5,10 +5,12 @@ package restful
// that can be found in the LICENSE file.
import (
"bufio"
"compress/gzip"
"compress/zlib"
"errors"
"io"
"net"
"net/http"
"strings"
)
@ -69,6 +71,17 @@ func (c *CompressingResponseWriter) isCompressorClosed() bool {
return nil == c.compressor
}
// Hijack implements the Hijacker interface
// This is especially useful when combining Container.EnabledContentEncoding
// in combination with websockets (for instance gorilla/websocket)
func (c *CompressingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
hijacker, ok := c.writer.(http.Hijacker)
if !ok {
return nil, nil, errors.New("ResponseWriter doesn't support Hijacker interface")
}
return hijacker.Hijack()
}
// WantsCompressedResponse reads the Accept-Encoding header to see if and which encoding is requested.
func wantsCompressedResponse(httpRequest *http.Request) (bool, string) {
header := httpRequest.Header.Get(HEADER_AcceptEncoding)

View File

@ -74,7 +74,11 @@ func (c CrossOriginResourceSharing) doActualRequest(req *Request, resp *Response
func (c *CrossOriginResourceSharing) doPreflightRequest(req *Request, resp *Response) {
if len(c.AllowedMethods) == 0 {
c.AllowedMethods = c.Container.computeAllowedMethods(req)
if c.Container == nil {
c.AllowedMethods = DefaultContainer.computeAllowedMethods(req)
} else {
c.AllowedMethods = c.Container.computeAllowedMethods(req)
}
}
acrm := req.Request.Header.Get(HEADER_AccessControlRequestMethod)

View File

@ -36,8 +36,8 @@ type entityReaderWriters struct {
}
func init() {
RegisterEntityAccessor(MIME_JSON, entityJSONAccess{ContentType: MIME_JSON})
RegisterEntityAccessor(MIME_XML, entityXMLAccess{ContentType: MIME_XML})
RegisterEntityAccessor(MIME_JSON, NewEntityAccessorJSON(MIME_JSON))
RegisterEntityAccessor(MIME_XML, NewEntityAccessorXML(MIME_XML))
}
// RegisterEntityAccessor add/overrides the ReaderWriter for encoding content with this MIME type.
@ -47,8 +47,20 @@ func RegisterEntityAccessor(mime string, erw EntityReaderWriter) {
entityAccessRegistry.accessors[mime] = erw
}
// AccessorAt returns the registered ReaderWriter for this MIME type.
func (r *entityReaderWriters) AccessorAt(mime string) (EntityReaderWriter, bool) {
// NewEntityAccessorJSON returns a new EntityReaderWriter for accessing JSON content.
// This package is already initialized with such an accessor using the MIME_JSON contentType.
func NewEntityAccessorJSON(contentType string) EntityReaderWriter {
return entityJSONAccess{ContentType: contentType}
}
// NewEntityAccessorXML returns a new EntityReaderWriter for accessing XML content.
// This package is already initialized with such an accessor using the MIME_XML contentType.
func NewEntityAccessorXML(contentType string) EntityReaderWriter {
return entityXMLAccess{ContentType: contentType}
}
// 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]

View File

@ -3,9 +3,9 @@ package main
import (
"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"
"google.golang.org/appengine"
"google.golang.org/appengine/datastore"
"google.golang.org/appengine/user"
"net/http"
"time"
)

View File

@ -3,8 +3,8 @@ package main
import (
"github.com/emicklei/go-restful"
"github.com/emicklei/go-restful/swagger"
"google.golang.com/appengine"
"google.golang.com/appengine/memcache"
"google.golang.org/appengine"
"google.golang.org/appengine/memcache"
"net/http"
)

View File

@ -54,6 +54,7 @@ func main() {
cors := restful.CrossOriginResourceSharing{
ExposeHeaders: []string{"X-My-Header"},
AllowedHeaders: []string{"Content-Type", "Accept"},
AllowedMethods: []string{"GET", "POST"},
CookiesAllowed: false,
Container: wsContainer}
wsContainer.Filter(cors.Filter)

View File

@ -0,0 +1,31 @@
package main
import (
"io"
"net/http"
"github.com/emicklei/go-restful"
)
func NoBrowserCacheFilter(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
resp.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") // HTTP 1.1.
resp.Header().Set("Pragma", "no-cache") // HTTP 1.0.
resp.Header().Set("Expires", "0") // Proxies.
chain.ProcessFilter(req, resp)
}
// This example shows how to use a WebService filter that passed the Http headers to disable browser cacheing.
//
// GET http://localhost:8080/hello
func main() {
ws := new(restful.WebService)
ws.Filter(NoBrowserCacheFilter)
ws.Route(ws.GET("/hello").To(hello))
restful.Add(ws)
http.ListenAndServe(":8080", nil)
}
func hello(req *restful.Request, resp *restful.Response) {
io.WriteString(resp, "world")
}

View File

@ -0,0 +1,45 @@
package restful
import (
"strconv"
"strings"
)
type mime struct {
media string
quality float64
}
// insertMime adds a mime to a list and keeps it sorted by quality.
func insertMime(l []mime, e mime) []mime {
for i, each := range l {
// if current mime has lower quality then insert before
if e.quality > each.quality {
left := append([]mime{}, l[0:i]...)
return append(append(left, e), l[i:]...)
}
}
return append(l, e)
}
// sortedMimes returns a list of mime sorted (desc) by its specified quality.
func sortedMimes(accept string) (sorted []mime) {
for _, each := range strings.Split(accept, ",") {
typeAndQuality := strings.Split(strings.Trim(each, " "), ";")
if len(typeAndQuality) == 1 {
sorted = insertMime(sorted, mime{typeAndQuality[0], 1.0})
} else {
// take factor
parts := strings.Split(typeAndQuality[1], "=")
if len(parts) == 2 {
f, err := strconv.ParseFloat(parts[1], 64)
if err != nil {
traceLogger.Printf("unable to parse quality in %s, %v", each, err)
} else {
sorted = insertMime(sorted, mime{typeAndQuality[0], f})
}
}
}
}
return
}

View File

@ -108,7 +108,7 @@ func (r *Request) ReadEntity(entityPointer interface{}) (err error) {
}
// lookup the EntityReader
entityReader, ok := entityAccessRegistry.AccessorAt(contentType)
entityReader, ok := entityAccessRegistry.accessorAt(contentType)
if !ok {
return NewError(http.StatusBadRequest, "Unable to unmarshal content of type:"+contentType)
}

View File

@ -7,7 +7,6 @@ package restful
import (
"errors"
"net/http"
"strings"
)
// DEPRECATED, use DefaultResponseContentType(mime)
@ -68,38 +67,39 @@ func (r *Response) SetRequestAccepts(mime string) {
// can write according to what the request wants (Accept) and what the Route can produce or what the restful defaults say.
// If called before WriteEntity and WriteHeader then a false return value can be used to write a 406: Not Acceptable.
func (r *Response) EntityWriter() (EntityReaderWriter, bool) {
for _, qualifiedMime := range strings.Split(r.requestAccept, ",") {
mime := strings.Trim(strings.Split(qualifiedMime, ";")[0], " ")
if 0 == len(mime) || mime == "*/*" {
for _, each := range r.routeProduces {
if MIME_JSON == each {
return entityAccessRegistry.AccessorAt(MIME_JSON)
}
if MIME_XML == each {
return entityAccessRegistry.AccessorAt(MIME_XML)
sorted := sortedMimes(r.requestAccept)
for _, eachAccept := range sorted {
for _, eachProduce := range r.routeProduces {
if eachProduce == eachAccept.media {
if w, ok := entityAccessRegistry.accessorAt(eachAccept.media); ok {
return w, true
}
}
} else { // mime is not blank; see if we have a match in Produces
}
if eachAccept.media == "*/*" {
for _, each := range r.routeProduces {
if mime == each {
if MIME_JSON == each {
return entityAccessRegistry.AccessorAt(MIME_JSON)
}
if MIME_XML == each {
return entityAccessRegistry.AccessorAt(MIME_XML)
}
if w, ok := entityAccessRegistry.accessorAt(each); ok {
return w, true
}
}
}
}
writer, ok := entityAccessRegistry.AccessorAt(r.requestAccept)
// if requestAccept is empty
writer, ok := entityAccessRegistry.accessorAt(r.requestAccept)
if !ok {
// if not registered then fallback to the defaults (if set)
if DefaultResponseMimeType == MIME_JSON {
return entityAccessRegistry.AccessorAt(MIME_JSON)
return entityAccessRegistry.accessorAt(MIME_JSON)
}
if DefaultResponseMimeType == MIME_XML {
return entityAccessRegistry.AccessorAt(MIME_XML)
return entityAccessRegistry.accessorAt(MIME_XML)
}
// Fallback to whatever the route says it can produce.
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
for _, each := range r.routeProduces {
if w, ok := entityAccessRegistry.accessorAt(each); ok {
return w, true
}
}
if trace {
traceLogger.Printf("no registered EntityReaderWriter found for %s", r.requestAccept)
@ -184,6 +184,15 @@ func (r *Response) WriteErrorString(httpStatus int, errorReason string) error {
return nil
}
// Flush implements http.Flusher interface, which sends any buffered data to the client.
func (r *Response) Flush() {
if f, ok := r.ResponseWriter.(http.Flusher); ok {
f.Flush()
} else if trace {
traceLogger.Printf("ResponseWriter %v doesn't support Flush", r)
}
}
// WriteHeader is overridden to remember the Status Code that has been written.
// Changes to the Header of the response have no effect after this.
func (r *Response) WriteHeader(httpStatus int) {

View File

@ -178,8 +178,8 @@ func (b modelBuilder) buildProperty(field reflect.StructField, model *Model, mod
return jsonName, modelDescription, prop
case fieldKind == reflect.Map:
// if it's a map, it's unstructured, and swagger 1.2 can't handle it
anyt := "any"
prop.Type = &anyt
objectType := "object"
prop.Type = &objectType
return jsonName, modelDescription, prop
}
@ -277,9 +277,10 @@ func (b modelBuilder) buildArrayTypeProperty(field reflect.StructField, jsonName
fieldType := field.Type
var pType = "array"
prop.Type = &pType
isPrimitive := b.isPrimitiveType(fieldType.Elem().Name())
elemTypeName := b.getElementTypeName(modelName, jsonName, fieldType.Elem())
prop.Items = new(Item)
if b.isPrimitiveType(elemTypeName) {
if isPrimitive {
mapped := b.jsonSchemaType(elemTypeName)
prop.Items.Type = &mapped
} else {
@ -289,7 +290,9 @@ func (b modelBuilder) buildArrayTypeProperty(field reflect.StructField, jsonName
if fieldType.Elem().Kind() == reflect.Ptr {
fieldType = fieldType.Elem()
}
b.addModel(fieldType.Elem(), elemTypeName)
if !isPrimitive {
b.addModel(fieldType.Elem(), elemTypeName)
}
return jsonName, prop
}
@ -305,10 +308,18 @@ func (b modelBuilder) buildPointerTypeProperty(field reflect.StructField, jsonNa
if fieldType.Elem().Kind() == reflect.Slice || fieldType.Elem().Kind() == reflect.Array {
var pType = "array"
prop.Type = &pType
isPrimitive := b.isPrimitiveType(fieldType.Elem().Elem().Name())
elemName := b.getElementTypeName(modelName, jsonName, fieldType.Elem().Elem())
prop.Items = &Item{Ref: &elemName}
// add|overwrite model for element type
b.addModel(fieldType.Elem().Elem(), elemName)
if isPrimitive {
primName := b.jsonSchemaType(elemName)
prop.Items = &Item{Ref: &primName}
} else {
prop.Items = &Item{Ref: &elemName}
}
if !isPrimitive {
// add|overwrite model for element type
b.addModel(fieldType.Elem().Elem(), elemName)
}
} else {
// non-array, pointer type
var pType = b.jsonSchemaType(fieldType.String()[1:]) // no star, include pkg path
@ -335,9 +346,6 @@ func (b modelBuilder) getElementTypeName(modelName, jsonName string, t reflect.T
if t.Name() == "" {
return modelName + "." + jsonName
}
if b.isPrimitiveType(t.Name()) {
return b.jsonSchemaType(t.Name())
}
return b.keyFrom(t)
}
@ -352,6 +360,9 @@ func (b modelBuilder) keyFrom(st reflect.Type) string {
// see also https://golang.org/ref/spec#Numeric_types
func (b modelBuilder) isPrimitiveType(modelName string) bool {
if len(modelName) == 0 {
return false
}
return strings.Contains("uint uint8 uint16 uint32 uint64 int int8 int16 int32 int64 float32 float64 bool string byte rune time.Time", modelName)
}

View File

@ -159,11 +159,16 @@ func (w *WebService) RemoveRoute(path, method string) error {
}
w.routesLock.Lock()
defer w.routesLock.Unlock()
newRoutes := make([]Route, (len(w.routes) - 1))
current := 0
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:]...)
continue
}
newRoutes[current] = w.routes[ix]
current = current + 1
}
w.routes = newRoutes
return nil
}