Skip to content

Commit

Permalink
Add support for sending a request body to http input (influxdata#5074)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielnelson authored and Max Eshleman committed Feb 13, 2019
1 parent 66136b1 commit f893894
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 7 deletions.
7 changes: 7 additions & 0 deletions plugins/inputs/http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ The HTTP input plugin collects metrics from one or more HTTP(S) endpoints. The
## Optional HTTP headers
# 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
# username = "username"
# password = "pa$$word"
Expand Down
45 changes: 38 additions & 7 deletions plugins/inputs/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package http
import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"strings"
Expand All @@ -17,17 +18,19 @@ import (
)

type HTTP struct {
URLs []string `toml:"urls"`
Method string
URLs []string `toml:"urls"`
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
Username string
Password string
Username string `toml:"username"`
Password string `toml:"password"`
tls.ClientConfig

Timeout internal.Duration
Timeout internal.Duration `toml:"timeout"`

client *http.Client

Expand All @@ -52,6 +55,13 @@ var sampleConfig = `
# username = "username"
# 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
# tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem"
Expand Down Expand Up @@ -132,11 +142,20 @@ func (h *HTTP) gatherURL(
acc telegraf.Accumulator,
url string,
) error {
request, err := http.NewRequest(h.Method, url, nil)
body, err := makeRequestBodyReader(h.ContentEncoding, h.Body)
if err != nil {
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 {
if strings.ToLower(k) == "host" {
request.Host = v
Expand Down Expand Up @@ -183,6 +202,18 @@ func (h *HTTP) gatherURL(
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() {
inputs.Add("http", func() telegraf.Input {
return &HTTP{
Expand Down
93 changes: 93 additions & 0 deletions plugins/inputs/http/http_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package http_test

import (
"compress/gzip"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
Expand Down Expand Up @@ -149,3 +152,93 @@ const simpleJSON = `
"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)
})
}
}

0 comments on commit f893894

Please sign in to comment.