2015-12-21 05:15:35 +00:00
|
|
|
/*
|
2016-06-03 00:25:58 +00:00
|
|
|
Copyright 2015 The Kubernetes Authors.
|
2015-12-21 05:15:35 +00:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package apiserver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"mime"
|
|
|
|
"net/http"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"bitbucket.org/ww/goautoneg"
|
|
|
|
|
|
|
|
"k8s.io/kubernetes/pkg/runtime"
|
|
|
|
)
|
|
|
|
|
2016-04-20 17:35:09 +00:00
|
|
|
func negotiateOutput(req *http.Request, supported []string) (string, map[string]string, error) {
|
2015-12-21 05:15:35 +00:00
|
|
|
acceptHeader := req.Header.Get("Accept")
|
|
|
|
if len(acceptHeader) == 0 && len(supported) > 0 {
|
|
|
|
acceptHeader = supported[0]
|
|
|
|
}
|
|
|
|
accept, ok := negotiate(acceptHeader, supported)
|
|
|
|
if !ok {
|
2016-04-20 17:35:09 +00:00
|
|
|
return "", nil, errNotAcceptable{supported}
|
2015-12-21 05:15:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pretty := isPrettyPrint(req)
|
|
|
|
if _, ok := accept.Params["pretty"]; !ok && pretty {
|
|
|
|
accept.Params["pretty"] = "1"
|
|
|
|
}
|
2016-04-20 17:35:09 +00:00
|
|
|
|
2015-12-21 05:15:35 +00:00
|
|
|
mediaType := accept.Type
|
|
|
|
if len(accept.SubType) > 0 {
|
|
|
|
mediaType += "/" + accept.SubType
|
|
|
|
}
|
2016-04-20 17:35:09 +00:00
|
|
|
|
|
|
|
return mediaType, accept.Params, nil
|
|
|
|
}
|
|
|
|
|
2016-04-23 19:00:28 +00:00
|
|
|
func negotiateOutputSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
|
2016-04-20 17:35:09 +00:00
|
|
|
supported := ns.SupportedMediaTypes()
|
|
|
|
mediaType, params, err := negotiateOutput(req, supported)
|
|
|
|
if err != nil {
|
2016-04-23 19:00:28 +00:00
|
|
|
return runtime.SerializerInfo{}, err
|
2016-04-20 17:35:09 +00:00
|
|
|
}
|
|
|
|
if s, ok := ns.SerializerForMediaType(mediaType, params); ok {
|
2016-04-23 19:00:28 +00:00
|
|
|
return s, nil
|
2015-12-21 05:15:35 +00:00
|
|
|
}
|
2016-04-23 19:00:28 +00:00
|
|
|
return runtime.SerializerInfo{}, errNotAcceptable{supported}
|
2015-12-21 05:15:35 +00:00
|
|
|
}
|
|
|
|
|
2016-04-23 19:00:28 +00:00
|
|
|
func negotiateOutputStreamSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.StreamSerializerInfo, error) {
|
2016-04-20 17:35:09 +00:00
|
|
|
supported := ns.SupportedMediaTypes()
|
|
|
|
mediaType, params, err := negotiateOutput(req, supported)
|
|
|
|
if err != nil {
|
2016-04-23 19:00:28 +00:00
|
|
|
return runtime.StreamSerializerInfo{}, err
|
2016-04-20 17:35:09 +00:00
|
|
|
}
|
2016-04-23 19:00:28 +00:00
|
|
|
if s, ok := ns.StreamingSerializerForMediaType(mediaType, params); ok {
|
|
|
|
return s, nil
|
2016-04-20 17:35:09 +00:00
|
|
|
}
|
2016-04-23 19:00:28 +00:00
|
|
|
return runtime.StreamSerializerInfo{}, errNotAcceptable{supported}
|
2016-04-20 17:35:09 +00:00
|
|
|
}
|
|
|
|
|
2016-04-23 19:00:28 +00:00
|
|
|
func negotiateInputSerializer(req *http.Request, s runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
|
2015-12-21 05:15:35 +00:00
|
|
|
supported := s.SupportedMediaTypes()
|
|
|
|
mediaType := req.Header.Get("Content-Type")
|
|
|
|
if len(mediaType) == 0 {
|
|
|
|
mediaType = supported[0]
|
|
|
|
}
|
|
|
|
mediaType, options, err := mime.ParseMediaType(mediaType)
|
|
|
|
if err != nil {
|
2016-04-23 19:00:28 +00:00
|
|
|
return runtime.SerializerInfo{}, errUnsupportedMediaType{supported}
|
2015-12-21 05:15:35 +00:00
|
|
|
}
|
|
|
|
out, ok := s.SerializerForMediaType(mediaType, options)
|
|
|
|
if !ok {
|
2016-04-23 19:00:28 +00:00
|
|
|
return runtime.SerializerInfo{}, errUnsupportedMediaType{supported}
|
2015-12-21 05:15:35 +00:00
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// isPrettyPrint returns true if the "pretty" query parameter is true or if the User-Agent
|
|
|
|
// matches known "human" clients.
|
|
|
|
func isPrettyPrint(req *http.Request) bool {
|
|
|
|
// DEPRECATED: should be part of the content type
|
|
|
|
if req.URL != nil {
|
|
|
|
pp := req.URL.Query().Get("pretty")
|
|
|
|
if len(pp) > 0 {
|
|
|
|
pretty, _ := strconv.ParseBool(pp)
|
|
|
|
return pretty
|
|
|
|
}
|
|
|
|
}
|
|
|
|
userAgent := req.UserAgent()
|
|
|
|
// This covers basic all browers and cli http tools
|
|
|
|
if strings.HasPrefix(userAgent, "curl") || strings.HasPrefix(userAgent, "Wget") || strings.HasPrefix(userAgent, "Mozilla/5.0") {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// negotiate the most appropriate content type given the accept header and a list of
|
|
|
|
// alternatives.
|
|
|
|
func negotiate(header string, alternatives []string) (goautoneg.Accept, bool) {
|
|
|
|
alternates := make([][]string, 0, len(alternatives))
|
|
|
|
for _, alternate := range alternatives {
|
|
|
|
alternates = append(alternates, strings.SplitN(alternate, "/", 2))
|
|
|
|
}
|
|
|
|
for _, clause := range goautoneg.ParseAccept(header) {
|
|
|
|
for _, alternate := range alternates {
|
|
|
|
if clause.Type == alternate[0] && clause.SubType == alternate[1] {
|
|
|
|
return clause, true
|
|
|
|
}
|
|
|
|
if clause.Type == alternate[0] && clause.SubType == "*" {
|
|
|
|
clause.SubType = alternate[1]
|
|
|
|
return clause, true
|
|
|
|
}
|
|
|
|
if clause.Type == "*" && clause.SubType == "*" {
|
|
|
|
clause.Type = alternate[0]
|
|
|
|
clause.SubType = alternate[1]
|
|
|
|
return clause, true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return goautoneg.Accept{}, false
|
|
|
|
}
|