/* Copyright 2022 The Kubernetes Authors. 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 httpexpect import ( "fmt" "io" "net/http" "net/url" "path" ) type HTTPRequest struct { chain chain reporter Reporter baseURL string client *http.Client query url.Values Request *http.Request HTTPResponse *HTTPResponse } // NewRequest returns an HTTPRequest object. func NewRequest(baseURL string, client *http.Client, reporter Reporter) *HTTPRequest { response := NewResponse(reporter) return &HTTPRequest{ baseURL: baseURL, client: client, reporter: reporter, chain: makeChain(reporter), HTTPResponse: response, } } // GET creates a new HTTP request with GET method. func (h *HTTPRequest) GET(rpath string) *HTTPRequest { if h.chain.failed() { return h } return h.DoRequest("GET", rpath) } // DoRequest creates a new HTTP request object. func (h *HTTPRequest) DoRequest(method, rpath string) *HTTPRequest { uri, err := url.Parse(h.baseURL) if err != nil { h.chain.fail(err.Error()) } var request *http.Request uri.Path = path.Join(uri.Path, rpath) if request, err = http.NewRequest(method, uri.String(), nil); err != nil { h.chain.fail(err.Error()) } h.Request = request return h } // Expect executes the request and returns an HTTP response. func (h *HTTPRequest) Expect() *HTTPResponse { if h.query != nil { h.Request.URL.RawQuery = h.query.Encode() } response, err := h.client.Do(h.Request) if err != nil { h.chain.fail(err.Error()) } h.HTTPResponse.Response = response // set the HTTP response var content []byte if content, err = getContent(response); err != nil { h.chain.fail(err.Error()) } // set content and cookies from HTTPResponse h.HTTPResponse.content = content h.HTTPResponse.cookies = h.HTTPResponse.Response.Cookies() return h.HTTPResponse } // WithURL sets the request URL appending paths when already exist. func (h *HTTPRequest) WithURL(urlStr string) *HTTPRequest { if h.chain.failed() { return h } if u, err := url.Parse(urlStr); err != nil { h.chain.fail(err.Error()) } else { u.Path = path.Join(h.Request.URL.Path, u.Path) h.Request.URL = u } return h } // WithHeader adds given header to request. func (h *HTTPRequest) WithHeader(key, value string) *HTTPRequest { if h.chain.failed() { return h } switch http.CanonicalHeaderKey(key) { case "Host": h.Request.Host = value default: h.Request.Header.Add(key, value) } return h } // WithCookies adds given cookies to request. func (h *HTTPRequest) WithCookies(cookies map[string]string) *HTTPRequest { if h.chain.failed() { return h } for k, v := range cookies { h.WithCookie(k, v) } return h } // WithCookie adds given single cookie to request. func (h *HTTPRequest) WithCookie(k, v string) *HTTPRequest { if h.chain.failed() { return h } h.Request.AddCookie(&http.Cookie{Name: k, Value: v}) return h } // WithBasicAuth sets the request's Authorization header to use HTTP // Basic Authentication with the provided username and password. // // With HTTP Basic Authentication the provided username and password // are not encrypted. func (h *HTTPRequest) WithBasicAuth(username, password string) *HTTPRequest { if h.chain.failed() { return h } h.Request.SetBasicAuth(username, password) return h } // WithQuery adds query parameter to request URL. func (h *HTTPRequest) WithQuery(key string, value interface{}) *HTTPRequest { if h.chain.failed() { return h } if h.query == nil { h.query = make(url.Values) } h.query.Add(key, fmt.Sprint(value)) return h } // getContent returns the content from the body response. func getContent(resp *http.Response) ([]byte, error) { if resp.Body == nil { return []byte{}, nil } return io.ReadAll(resp.Body) }