|  | package ghttp | 
|  |  | 
|  | import ( | 
|  | "encoding/base64" | 
|  | "encoding/json" | 
|  | "fmt" | 
|  | "io/ioutil" | 
|  | "net/http" | 
|  | "net/url" | 
|  | "reflect" | 
|  |  | 
|  | "github.com/golang/protobuf/proto" | 
|  | . "github.com/onsi/gomega" | 
|  | "github.com/onsi/gomega/types" | 
|  | ) | 
|  |  | 
|  | //CombineHandler takes variadic list of handlers and produces one handler | 
|  | //that calls each handler in order. | 
|  | func CombineHandlers(handlers ...http.HandlerFunc) http.HandlerFunc { | 
|  | return func(w http.ResponseWriter, req *http.Request) { | 
|  | for _, handler := range handlers { | 
|  | handler(w, req) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | //VerifyRequest returns a handler that verifies that a request uses the specified method to connect to the specified path | 
|  | //You may also pass in an optional rawQuery string which is tested against the request's `req.URL.RawQuery` | 
|  | // | 
|  | //For path, you may pass in a string, in which case strict equality will be applied | 
|  | //Alternatively you can pass in a matcher (ContainSubstring("/foo") and MatchRegexp("/foo/[a-f0-9]+") for example) | 
|  | func VerifyRequest(method string, path interface{}, rawQuery ...string) http.HandlerFunc { | 
|  | return func(w http.ResponseWriter, req *http.Request) { | 
|  | Ω(req.Method).Should(Equal(method), "Method mismatch") | 
|  | switch p := path.(type) { | 
|  | case types.GomegaMatcher: | 
|  | Ω(req.URL.Path).Should(p, "Path mismatch") | 
|  | default: | 
|  | Ω(req.URL.Path).Should(Equal(path), "Path mismatch") | 
|  | } | 
|  | if len(rawQuery) > 0 { | 
|  | values, err := url.ParseQuery(rawQuery[0]) | 
|  | Ω(err).ShouldNot(HaveOccurred(), "Expected RawQuery is malformed") | 
|  |  | 
|  | Ω(req.URL.Query()).Should(Equal(values), "RawQuery mismatch") | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | //VerifyContentType returns a handler that verifies that a request has a Content-Type header set to the | 
|  | //specified value | 
|  | func VerifyContentType(contentType string) http.HandlerFunc { | 
|  | return func(w http.ResponseWriter, req *http.Request) { | 
|  | Ω(req.Header.Get("Content-Type")).Should(Equal(contentType)) | 
|  | } | 
|  | } | 
|  |  | 
|  | //VerifyBasicAuth returns a handler that verifies the request contains a BasicAuth Authorization header | 
|  | //matching the passed in username and password | 
|  | func VerifyBasicAuth(username string, password string) http.HandlerFunc { | 
|  | return func(w http.ResponseWriter, req *http.Request) { | 
|  | auth := req.Header.Get("Authorization") | 
|  | Ω(auth).ShouldNot(Equal(""), "Authorization header must be specified") | 
|  |  | 
|  | decoded, err := base64.StdEncoding.DecodeString(auth[6:]) | 
|  | Ω(err).ShouldNot(HaveOccurred()) | 
|  |  | 
|  | Ω(string(decoded)).Should(Equal(fmt.Sprintf("%s:%s", username, password)), "Authorization mismatch") | 
|  | } | 
|  | } | 
|  |  | 
|  | //VerifyHeader returns a handler that verifies the request contains the passed in headers. | 
|  | //The passed in header keys are first canonicalized via http.CanonicalHeaderKey. | 
|  | // | 
|  | //The request must contain *all* the passed in headers, but it is allowed to have additional headers | 
|  | //beyond the passed in set. | 
|  | func VerifyHeader(header http.Header) http.HandlerFunc { | 
|  | return func(w http.ResponseWriter, req *http.Request) { | 
|  | for key, values := range header { | 
|  | key = http.CanonicalHeaderKey(key) | 
|  | Ω(req.Header[key]).Should(Equal(values), "Header mismatch for key: %s", key) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | //VerifyHeaderKV returns a handler that verifies the request contains a header matching the passed in key and values | 
|  | //(recall that a `http.Header` is a mapping from string (key) to []string (values)) | 
|  | //It is a convenience wrapper around `VerifyHeader` that allows you to avoid having to create an `http.Header` object. | 
|  | func VerifyHeaderKV(key string, values ...string) http.HandlerFunc { | 
|  | return VerifyHeader(http.Header{key: values}) | 
|  | } | 
|  |  | 
|  | //VerifyJSON returns a handler that verifies that the body of the request is a valid JSON representation | 
|  | //matching the passed in JSON string.  It does this using Gomega's MatchJSON method | 
|  | // | 
|  | //VerifyJSON also verifies that the request's content type is application/json | 
|  | func VerifyJSON(expectedJSON string) http.HandlerFunc { | 
|  | return CombineHandlers( | 
|  | VerifyContentType("application/json"), | 
|  | func(w http.ResponseWriter, req *http.Request) { | 
|  | body, err := ioutil.ReadAll(req.Body) | 
|  | req.Body.Close() | 
|  | Ω(err).ShouldNot(HaveOccurred()) | 
|  | Ω(body).Should(MatchJSON(expectedJSON), "JSON Mismatch") | 
|  | }, | 
|  | ) | 
|  | } | 
|  |  | 
|  | //VerifyJSONRepresenting is similar to VerifyJSON.  Instead of taking a JSON string, however, it | 
|  | //takes an arbitrary JSON-encodable object and verifies that the requests's body is a JSON representation | 
|  | //that matches the object | 
|  | func VerifyJSONRepresenting(object interface{}) http.HandlerFunc { | 
|  | data, err := json.Marshal(object) | 
|  | Ω(err).ShouldNot(HaveOccurred()) | 
|  | return CombineHandlers( | 
|  | VerifyContentType("application/json"), | 
|  | VerifyJSON(string(data)), | 
|  | ) | 
|  | } | 
|  |  | 
|  | //VerifyForm returns a handler that verifies a request contains the specified form values. | 
|  | // | 
|  | //The request must contain *all* of the specified values, but it is allowed to have additional | 
|  | //form values beyond the passed in set. | 
|  | func VerifyForm(values url.Values) http.HandlerFunc { | 
|  | return func(w http.ResponseWriter, r *http.Request) { | 
|  | err := r.ParseForm() | 
|  | Ω(err).ShouldNot(HaveOccurred()) | 
|  | for key, vals := range values { | 
|  | Ω(r.Form[key]).Should(Equal(vals), "Form mismatch for key: %s", key) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | //VerifyFormKV returns a handler that verifies a request contains a form key with the specified values. | 
|  | // | 
|  | //It is a convenience wrapper around `VerifyForm` that lets you avoid having to create a `url.Values` object. | 
|  | func VerifyFormKV(key string, values ...string) http.HandlerFunc { | 
|  | return VerifyForm(url.Values{key: values}) | 
|  | } | 
|  |  | 
|  | //VerifyProtoRepresenting returns a handler that verifies that the body of the request is a valid protobuf | 
|  | //representation of the passed message. | 
|  | // | 
|  | //VerifyProtoRepresenting also verifies that the request's content type is application/x-protobuf | 
|  | func VerifyProtoRepresenting(expected proto.Message) http.HandlerFunc { | 
|  | return CombineHandlers( | 
|  | VerifyContentType("application/x-protobuf"), | 
|  | func(w http.ResponseWriter, req *http.Request) { | 
|  | body, err := ioutil.ReadAll(req.Body) | 
|  | Ω(err).ShouldNot(HaveOccurred()) | 
|  | req.Body.Close() | 
|  |  | 
|  | expectedType := reflect.TypeOf(expected) | 
|  | actualValuePtr := reflect.New(expectedType.Elem()) | 
|  |  | 
|  | actual, ok := actualValuePtr.Interface().(proto.Message) | 
|  | Ω(ok).Should(BeTrue(), "Message value is not a proto.Message") | 
|  |  | 
|  | err = proto.Unmarshal(body, actual) | 
|  | Ω(err).ShouldNot(HaveOccurred(), "Failed to unmarshal protobuf") | 
|  |  | 
|  | Ω(actual).Should(Equal(expected), "ProtoBuf Mismatch") | 
|  | }, | 
|  | ) | 
|  | } | 
|  |  | 
|  | func copyHeader(src http.Header, dst http.Header) { | 
|  | for key, value := range src { | 
|  | dst[key] = value | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | RespondWith returns a handler that responds to a request with the specified status code and body | 
|  |  | 
|  | Body may be a string or []byte | 
|  |  | 
|  | Also, RespondWith can be given an optional http.Header.  The headers defined therein will be added to the response headers. | 
|  | */ | 
|  | func RespondWith(statusCode int, body interface{}, optionalHeader ...http.Header) http.HandlerFunc { | 
|  | return func(w http.ResponseWriter, req *http.Request) { | 
|  | if len(optionalHeader) == 1 { | 
|  | copyHeader(optionalHeader[0], w.Header()) | 
|  | } | 
|  | w.WriteHeader(statusCode) | 
|  | switch x := body.(type) { | 
|  | case string: | 
|  | w.Write([]byte(x)) | 
|  | case []byte: | 
|  | w.Write(x) | 
|  | default: | 
|  | Ω(body).Should(BeNil(), "Invalid type for body.  Should be string or []byte.") | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | RespondWithPtr returns a handler that responds to a request with the specified status code and body | 
|  |  | 
|  | Unlike RespondWith, you pass RepondWithPtr a pointer to the status code and body allowing different tests | 
|  | to share the same setup but specify different status codes and bodies. | 
|  |  | 
|  | Also, RespondWithPtr can be given an optional http.Header.  The headers defined therein will be added to the response headers. | 
|  | Since the http.Header can be mutated after the fact you don't need to pass in a pointer. | 
|  | */ | 
|  | func RespondWithPtr(statusCode *int, body interface{}, optionalHeader ...http.Header) http.HandlerFunc { | 
|  | return func(w http.ResponseWriter, req *http.Request) { | 
|  | if len(optionalHeader) == 1 { | 
|  | copyHeader(optionalHeader[0], w.Header()) | 
|  | } | 
|  | w.WriteHeader(*statusCode) | 
|  | if body != nil { | 
|  | switch x := (body).(type) { | 
|  | case *string: | 
|  | w.Write([]byte(*x)) | 
|  | case *[]byte: | 
|  | w.Write(*x) | 
|  | default: | 
|  | Ω(body).Should(BeNil(), "Invalid type for body.  Should be string or []byte.") | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | RespondWithJSONEncoded returns a handler that responds to a request with the specified status code and a body | 
|  | containing the JSON-encoding of the passed in object | 
|  |  | 
|  | Also, RespondWithJSONEncoded can be given an optional http.Header.  The headers defined therein will be added to the response headers. | 
|  | */ | 
|  | func RespondWithJSONEncoded(statusCode int, object interface{}, optionalHeader ...http.Header) http.HandlerFunc { | 
|  | data, err := json.Marshal(object) | 
|  | Ω(err).ShouldNot(HaveOccurred()) | 
|  |  | 
|  | var headers http.Header | 
|  | if len(optionalHeader) == 1 { | 
|  | headers = optionalHeader[0] | 
|  | } else { | 
|  | headers = make(http.Header) | 
|  | } | 
|  | if _, found := headers["Content-Type"]; !found { | 
|  | headers["Content-Type"] = []string{"application/json"} | 
|  | } | 
|  | return RespondWith(statusCode, string(data), headers) | 
|  | } | 
|  |  | 
|  | /* | 
|  | RespondWithJSONEncodedPtr behaves like RespondWithJSONEncoded but takes a pointer | 
|  | to a status code and object. | 
|  |  | 
|  | This allows different tests to share the same setup but specify different status codes and JSON-encoded | 
|  | objects. | 
|  |  | 
|  | Also, RespondWithJSONEncodedPtr can be given an optional http.Header.  The headers defined therein will be added to the response headers. | 
|  | Since the http.Header can be mutated after the fact you don't need to pass in a pointer. | 
|  | */ | 
|  | func RespondWithJSONEncodedPtr(statusCode *int, object interface{}, optionalHeader ...http.Header) http.HandlerFunc { | 
|  | return func(w http.ResponseWriter, req *http.Request) { | 
|  | data, err := json.Marshal(object) | 
|  | Ω(err).ShouldNot(HaveOccurred()) | 
|  | var headers http.Header | 
|  | if len(optionalHeader) == 1 { | 
|  | headers = optionalHeader[0] | 
|  | } else { | 
|  | headers = make(http.Header) | 
|  | } | 
|  | if _, found := headers["Content-Type"]; !found { | 
|  | headers["Content-Type"] = []string{"application/json"} | 
|  | } | 
|  | copyHeader(headers, w.Header()) | 
|  | w.WriteHeader(*statusCode) | 
|  | w.Write(data) | 
|  | } | 
|  | } | 
|  |  | 
|  | //RespondWithProto returns a handler that responds to a request with the specified status code and a body | 
|  | //containing the protobuf serialization of the provided message. | 
|  | // | 
|  | //Also, RespondWithProto can be given an optional http.Header.  The headers defined therein will be added to the response headers. | 
|  | func RespondWithProto(statusCode int, message proto.Message, optionalHeader ...http.Header) http.HandlerFunc { | 
|  | return func(w http.ResponseWriter, req *http.Request) { | 
|  | data, err := proto.Marshal(message) | 
|  | Ω(err).ShouldNot(HaveOccurred()) | 
|  |  | 
|  | var headers http.Header | 
|  | if len(optionalHeader) == 1 { | 
|  | headers = optionalHeader[0] | 
|  | } else { | 
|  | headers = make(http.Header) | 
|  | } | 
|  | if _, found := headers["Content-Type"]; !found { | 
|  | headers["Content-Type"] = []string{"application/x-protobuf"} | 
|  | } | 
|  | copyHeader(headers, w.Header()) | 
|  |  | 
|  | w.WriteHeader(statusCode) | 
|  | w.Write(data) | 
|  | } | 
|  | } |