cmd/http/http.go (105 lines of code) (raw):

/* Copyright (c) Facebook, Inc. and its affiliates. All rights reserved. This source code is licensed under the BSD-style license found in the LICENSE file in the root directory of this source tree. */ package http import ( "fmt" "net/http" "net/url" "strconv" "strings" "github.com/facebookincubator/fbender/cmd/core/errors" "github.com/facebookincubator/fbender/cmd/core/input" "github.com/facebookincubator/fbender/cmd/core/options" "github.com/facebookincubator/fbender/cmd/core/runner" tester "github.com/facebookincubator/fbender/tester/http" "github.com/spf13/cobra" ) const formats = "'GET RelativeURL' or 'POST RelativeURL FormData'" func params(cmd *cobra.Command, o *options.Options) (*runner.Params, error) { ssl, err := cmd.Flags().GetBool("ssl") if err != nil { //nolint:wrapcheck return nil, err } r, err := input.NewRequestGenerator(o.Input, inputTransformer(ssl, o.Target), requestCreator) if err != nil { //nolint:wrapcheck return nil, err } t := &tester.Tester{ Timeout: o.Timeout, } return &runner.Params{Tester: t, RequestGenerator: r}, nil } func inputTransformer(ssl bool, target string) input.Transformer { protocol := "http" if ssl { protocol = "https" } return func(input string) (interface{}, error) { i := strings.Index(input, " ") if i < 0 { return nil, fmt.Errorf("%w, want: %s, got: %q", errors.ErrInvalidFormat, formats, input) } method, data := input[:i], input[i+1:] switch method { case "GET": return parseGetRequest(protocol, target, data) case "POST": return parsePostRequest(protocol, target, data) } return nil, fmt.Errorf("%w, want: (GET|POST), got: %q", errors.ErrInvalidFormat, method) } } type request interface { Create() (*http.Request, error) } type getRequest struct { url string } func (r *getRequest) Create() (*http.Request, error) { //nolint:noctx return http.NewRequest("GET", r.url, nil) } func parseGetRequest(protocol, target, data string) (interface{}, error) { rawurl, err := joinURL(protocol, target, data) if err != nil { return nil, err } return &getRequest{url: rawurl}, nil } type postRequest struct { url string body string } func (r *postRequest) Create() (*http.Request, error) { //nolint:noctx req, err := http.NewRequest("POST", r.url, strings.NewReader(r.body)) if err != nil { //nolint:wrapcheck return nil, err } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Content-Length", strconv.Itoa(len(r.body))) return req, nil } func parsePostRequest(protocol, target, data string) (interface{}, error) { i := strings.Index(data, " ") if i < 0 { return nil, fmt.Errorf("%w, want: %s, got: \"POST %s\"", errors.ErrInvalidFormat, formats, data) } form, err := url.ParseQuery(data[i+1:]) if err != nil { //nolint:wrapcheck return nil, err } rawurl, err := joinURL(protocol, target, data[:i]) if err != nil { return nil, err } return &postRequest{url: rawurl, body: form.Encode()}, nil } // NewRequest uses reader interface for message body, which is being used up. // Therefore we cannot reuse a once created request and need to invoke // NewRequest everytime before sending it. func requestCreator(r interface{}) (interface{}, error) { if r, ok := r.(request); ok { return r.Create() } return nil, fmt.Errorf("%w, want: request, got: %T", errors.ErrInvalidType, r) } func joinURL(protocol, target, path string) (string, error) { path = strings.TrimPrefix(path, "/") rawurl := fmt.Sprintf("%s://%s/%s", protocol, target, path) _, err := url.Parse(rawurl) //nolint:wrapcheck return rawurl, err }