Add support for sending a request body to http input (#5074)

This commit is contained in:
Daniel Nelson 2018-12-11 19:12:00 -08:00 committed by GitHub
parent cf2b85f383
commit 4d3519756c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 138 additions and 7 deletions

View File

@ -19,6 +19,13 @@ The HTTP input plugin collects metrics from one or more HTTP(S) endpoints. The
## Optional HTTP headers ## Optional HTTP headers
# headers = {"X-Special-Header" = "Special-Value"} # headers = {"X-Special-Header" = "Special-Value"}
## HTTP entity-body to send with POST/PUT requests.
# body = ""
## HTTP Content-Encoding for write request body, can be set to "gzip" to
## compress body or "identity" to apply no encoding.
# content_encoding = "identity"
## Optional HTTP Basic Auth Credentials ## Optional HTTP Basic Auth Credentials
# username = "username" # username = "username"
# password = "pa$$word" # password = "pa$$word"

View File

@ -3,6 +3,7 @@ package http
import ( import (
"errors" "errors"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings" "strings"
@ -18,16 +19,18 @@ import (
type HTTP struct { type HTTP struct {
URLs []string `toml:"urls"` URLs []string `toml:"urls"`
Method string Method string `toml:"method"`
Body string `toml:"body"`
ContentEncoding string `toml:"content_encoding"`
Headers map[string]string Headers map[string]string `toml:"headers"`
// HTTP Basic Auth Credentials // HTTP Basic Auth Credentials
Username string Username string `toml:"username"`
Password string Password string `toml:"password"`
tls.ClientConfig tls.ClientConfig
Timeout internal.Duration Timeout internal.Duration `toml:"timeout"`
client *http.Client client *http.Client
@ -52,6 +55,13 @@ var sampleConfig = `
# username = "username" # username = "username"
# password = "pa$$word" # password = "pa$$word"
## HTTP entity-body to send with POST/PUT requests.
# body = ""
## HTTP Content-Encoding for write request body, can be set to "gzip" to
## compress body or "identity" to apply no encoding.
# content_encoding = "identity"
## Optional TLS Config ## Optional TLS Config
# tls_ca = "/etc/telegraf/ca.pem" # tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem" # tls_cert = "/etc/telegraf/cert.pem"
@ -132,11 +142,20 @@ func (h *HTTP) gatherURL(
acc telegraf.Accumulator, acc telegraf.Accumulator,
url string, url string,
) error { ) error {
request, err := http.NewRequest(h.Method, url, nil) body, err := makeRequestBodyReader(h.ContentEncoding, h.Body)
if err != nil { if err != nil {
return err return err
} }
request, err := http.NewRequest(h.Method, url, body)
if err != nil {
return err
}
if h.ContentEncoding == "gzip" {
request.Header.Set("Content-Encoding", "gzip")
}
for k, v := range h.Headers { for k, v := range h.Headers {
if strings.ToLower(k) == "host" { if strings.ToLower(k) == "host" {
request.Host = v request.Host = v
@ -183,6 +202,18 @@ func (h *HTTP) gatherURL(
return nil return nil
} }
func makeRequestBodyReader(contentEncoding, body string) (io.Reader, error) {
var err error
var reader io.Reader = strings.NewReader(body)
if contentEncoding == "gzip" {
reader, err = internal.CompressWithGzip(reader)
if err != nil {
return nil, err
}
}
return reader, nil
}
func init() { func init() {
inputs.Add("http", func() telegraf.Input { inputs.Add("http", func() telegraf.Input {
return &HTTP{ return &HTTP{

View File

@ -1,6 +1,9 @@
package http_test package http_test
import ( import (
"compress/gzip"
"fmt"
"io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -149,3 +152,93 @@ const simpleJSON = `
"a": 1.2 "a": 1.2
} }
` `
func TestBodyAndContentEncoding(t *testing.T) {
ts := httptest.NewServer(http.NotFoundHandler())
defer ts.Close()
url := fmt.Sprintf("http://%s", ts.Listener.Addr().String())
tests := []struct {
name string
plugin *plugin.HTTP
queryHandlerFunc func(t *testing.T, w http.ResponseWriter, r *http.Request)
}{
{
name: "no body",
plugin: &plugin.HTTP{
Method: "POST",
URLs: []string{url},
},
queryHandlerFunc: func(t *testing.T, w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
require.NoError(t, err)
require.Equal(t, []byte(""), body)
w.WriteHeader(http.StatusOK)
},
},
{
name: "post body",
plugin: &plugin.HTTP{
URLs: []string{url},
Method: "POST",
Body: "test",
},
queryHandlerFunc: func(t *testing.T, w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
require.NoError(t, err)
require.Equal(t, []byte("test"), body)
w.WriteHeader(http.StatusOK)
},
},
{
name: "get method body is sent",
plugin: &plugin.HTTP{
URLs: []string{url},
Method: "GET",
Body: "test",
},
queryHandlerFunc: func(t *testing.T, w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
require.NoError(t, err)
require.Equal(t, []byte("test"), body)
w.WriteHeader(http.StatusOK)
},
},
{
name: "gzip encoding",
plugin: &plugin.HTTP{
URLs: []string{url},
Method: "GET",
Body: "test",
ContentEncoding: "gzip",
},
queryHandlerFunc: func(t *testing.T, w http.ResponseWriter, r *http.Request) {
require.Equal(t, r.Header.Get("Content-Encoding"), "gzip")
gr, err := gzip.NewReader(r.Body)
require.NoError(t, err)
body, err := ioutil.ReadAll(gr)
require.NoError(t, err)
require.Equal(t, []byte("test"), body)
w.WriteHeader(http.StatusOK)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ts.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tt.queryHandlerFunc(t, w, r)
})
parser, err := parsers.NewParser(&parsers.Config{DataFormat: "influx"})
require.NoError(t, err)
tt.plugin.SetParser(parser)
var acc testutil.Accumulator
err = tt.plugin.Gather(&acc)
require.NoError(t, err)
})
}
}