blob: bb8df3cb3deff609b5e88a94829c5e98daff3353 [file] [log] [blame]
Serge Bazanskicc25bdf2018-10-25 14:02:58 +02001// Copyright 2015 go-swagger maintainers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package middleware
16
17import (
18 "mime"
19 "net/http"
20 "strings"
21
22 "github.com/go-openapi/errors"
23 "github.com/go-openapi/runtime"
24 "github.com/go-openapi/swag"
25)
26
27type validation struct {
28 context *Context
29 result []error
30 request *http.Request
31 route *MatchedRoute
32 bound map[string]interface{}
33}
34
35// ContentType validates the content type of a request
36func validateContentType(allowed []string, actual string) error {
37 debugLog("validating content type for %q against [%s]", actual, strings.Join(allowed, ", "))
38 if len(allowed) == 0 {
39 return nil
40 }
41 mt, _, err := mime.ParseMediaType(actual)
42 if err != nil {
43 return errors.InvalidContentType(actual, allowed)
44 }
45 if swag.ContainsStringsCI(allowed, mt) {
46 return nil
47 }
48 if swag.ContainsStringsCI(allowed, "*/*") {
49 return nil
50 }
51 parts := strings.Split(actual, "/")
52 if len(parts) == 2 && swag.ContainsStringsCI(allowed, parts[0]+"/*") {
53 return nil
54 }
55 return errors.InvalidContentType(actual, allowed)
56}
57
58func validateRequest(ctx *Context, request *http.Request, route *MatchedRoute) *validation {
59 debugLog("validating request %s %s", request.Method, request.URL.EscapedPath())
60 validate := &validation{
61 context: ctx,
62 request: request,
63 route: route,
64 bound: make(map[string]interface{}),
65 }
66
67 validate.contentType()
68 if len(validate.result) == 0 {
69 validate.responseFormat()
70 }
71 if len(validate.result) == 0 {
72 validate.parameters()
73 }
74
75 return validate
76}
77
78func (v *validation) parameters() {
79 debugLog("validating request parameters for %s %s", v.request.Method, v.request.URL.EscapedPath())
80 if result := v.route.Binder.Bind(v.request, v.route.Params, v.route.Consumer, v.bound); result != nil {
81 if result.Error() == "validation failure list" {
82 for _, e := range result.(*errors.Validation).Value.([]interface{}) {
83 v.result = append(v.result, e.(error))
84 }
85 return
86 }
87 v.result = append(v.result, result)
88 }
89}
90
91func (v *validation) contentType() {
92 if len(v.result) == 0 && runtime.HasBody(v.request) {
93 debugLog("validating body content type for %s %s", v.request.Method, v.request.URL.EscapedPath())
94 ct, _, req, err := v.context.ContentType(v.request)
95 if err != nil {
96 v.result = append(v.result, err)
97 } else {
98 v.request = req
99 }
100
101 if len(v.result) == 0 {
102 if err := validateContentType(v.route.Consumes, ct); err != nil {
103 v.result = append(v.result, err)
104 }
105 }
106 if ct != "" && v.route.Consumer == nil {
107 cons, ok := v.route.Consumers[ct]
108 if !ok {
109 v.result = append(v.result, errors.New(500, "no consumer registered for %s", ct))
110 } else {
111 v.route.Consumer = cons
112 }
113 }
114 }
115}
116
117func (v *validation) responseFormat() {
118 if str, rCtx := v.context.ResponseFormat(v.request, v.route.Produces); str == "" && runtime.HasBody(v.request) {
119 v.request = rCtx
120 v.result = append(v.result, errors.InvalidResponseFormat(v.request.Header.Get(runtime.HeaderAccept), v.route.Produces))
121 }
122}