Skip to content

Commit

Permalink
Merge 104264a into c0559d4
Browse files Browse the repository at this point in the history
  • Loading branch information
astj committed Feb 21, 2019
2 parents c0559d4 + 104264a commit 0555753
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 8 deletions.
23 changes: 15 additions & 8 deletions check-http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,15 @@ command = ["check-http", "-u", "http://example.com"]
### Options

```
-u, --url= A URL to connect to
-s, --status= mapping of HTTP status
--no-check-certificate Do not check certificate
-i, --source-ip= source IP address
-H= HTTP request headers
-p, --pattern= Expected pattern in the content
--max-redirects= Maximum number of redirects followed (default: 10)
--connect-to=HOST1:PORT1:HOST2:PORT2 Request to HOST2:PORT2 instead of HOST1:PORT1
-u, --url= A URL to connect to
-s, --status= mapping of HTTP status
--no-check-certificate Do not check certificate
-i, --source-ip= source IP address
-H= HTTP request headers
-p, --pattern= Expected pattern in the content
--max-redirects= Maximum number of redirects followed (default: 10)
--connect-to=HOST1:PORT1:HOST2:PORT2 Request to HOST2:PORT2 instead of HOST1:PORT1
-x, --proxy=[PROTOCOL://][USER:PASS@]HOST[:PORT] Use the specified proxy. PROTOCOL's default is http, and PORT's default is 1080.
```


Expand All @@ -68,6 +69,12 @@ check-http --connect-to=localhost:443::8080 https://localhost # empty host2 mean
check-http --connect-to=example.com:443:127.0.0.1: https://example.com # empty port2 means unchanged, therefore will request to 127.0.0.1:443
```

To request via proxy (http/https/socks5)
```shell
check-http --proxy=http://localhost:8080 -u http://example.com # request via http://localhost:8080
HTTP_PROXY=http://localhost:8080 check-http -u http://example.com # Same. you can set proxy via environment variable
check-http --proxy=http://user:pass@localhost:8080 -u http://example.com # basic authentication is also supported
```
## For more information

Please execute `check-http -h` and you can get command line options.
30 changes: 30 additions & 0 deletions check-http/lib/check_http.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net"
"net/http"
"net/textproto"
"net/url"
"os"
"regexp"
"strconv"
Expand All @@ -30,6 +31,7 @@ type checkHTTPOpts struct {
Regexp string `short:"p" long:"pattern" description:"Expected pattern in the content"`
MaxRedirects int `long:"max-redirects" description:"Maximum number of redirects followed" default:"10"`
ConnectTos []string `long:"connect-to" value-name:"HOST1:PORT1:HOST2:PORT2" description:"Request to HOST2:PORT2 instead of HOST1:PORT1"`
Proxy string `short:"x" long:"proxy" value-name:"[PROTOCOL://][USER:PASS@]HOST[:PORT]" description:"Use the specified proxy. PROTOCOL's default is http, and PORT's default is 1080."`
}

// Do the plugin
Expand Down Expand Up @@ -174,6 +176,26 @@ func parseConnectTo(opts *checkHTTPOpts) ([]resolveMapping, error) {
return mappings, nil
}

func parseProxy(opts *checkHTTPOpts) (*url.URL, error) {
if opts.Proxy == "" {
return nil, nil
}
// Append protocol if absent.
// Overwriting u.Scheme is not enough, since url.Parse cannot parse "HOST:PORT" since it's ambiguous
proxy := opts.Proxy
if !strings.Contains(proxy, "://") {
proxy = "http://" + proxy
}
u, err := url.Parse(proxy)
if err != nil {
return nil, err
}
if u.Port() == "" {
u.Host = u.Hostname() + ":1080"
}
return u, nil
}

// Run do external monitoring via HTTP
func Run(args []string) *checkers.Checker {
opts := checkHTTPOpts{}
Expand Down Expand Up @@ -207,6 +229,14 @@ func Run(args []string) *checkers.Checker {
dialer.LocalAddr = &net.TCPAddr{IP: ip}
}

proxyURL, err := parseProxy(&opts)
if err != nil {
return checkers.Unknown(err.Error())
}
if proxyURL != nil {
tr.Proxy = http.ProxyURL(proxyURL)
}

if len(opts.ConnectTos) != 0 {
resolves, err := parseConnectTo(&opts)
if err != nil {
Expand Down
103 changes: 103 additions & 0 deletions check-http/lib/check_http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"strings"
"testing"

"github.com/elazarl/goproxy"
"github.com/elazarl/goproxy/ext/auth"
"github.com/mackerelio/checkers"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -283,3 +285,104 @@ func TestConnectTos(t *testing.T) {
assert.Equal(t, ckr.Status, tc.want, "#%d: Status should be %s, %s", i, tc.want, ckr.Message)
}
}

func TestProxy(t *testing.T) {
// target server
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "I am target")
}))
defer ts.Close()

// proxy server, which intercepts request
proxy := goproxy.NewProxyHttpServer()
proxy.OnRequest().DoFunc(
func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
return r, goproxy.NewResponse(r,
goproxy.ContentTypeText, http.StatusOK,
fmt.Sprintf("intercept a req to %s", r.URL))
})
tsP := httptest.NewServer(proxy)
defer tsP.Close()
// extract host and port
s2 := strings.SplitN(tsP.URL, ":", 3)
hostP := strings.TrimPrefix(s2[1], "//")
portP := s2[2]

testCases := []struct {
args []string
want checkers.Status
}{
{
// direct target
args: []string{"--pattern", "I am target",
"-u", ts.URL},
want: checkers.OK,
},
{
// proxy with full url
args: []string{"--proxy", tsP.URL, "--pattern", fmt.Sprintf("intercept a req to %s", ts.URL),
"-u", ts.URL},
want: checkers.OK,
},
{
// proxy without scheme
args: []string{"--proxy", hostP + ":" + portP, "--pattern", fmt.Sprintf("intercept a req to %s", ts.URL),
"-u", ts.URL},
want: checkers.OK,
},
}

for i, tc := range testCases {
ckr := Run(tc.args)
assert.Equal(t, ckr.Status, tc.want, "#%d: Status should be %s, %s", i, tc.want, ckr.Message)
}
}

func TestProxy_Auth(t *testing.T) {
// target server
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "I am target")
}))
defer ts.Close()

// proxy server, which forces basic authentication and intercepts
proxy := goproxy.NewProxyHttpServer()
auth.ProxyBasic(proxy, "basic!", func(user, password string) bool {
return user == "somename" && password == "somepassword"
})
proxy.OnRequest().DoFunc(
func(r *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
return r, goproxy.NewResponse(r,
goproxy.ContentTypeText, http.StatusOK,
fmt.Sprintf("intercept a req to %s", r.URL))
})
tsP := httptest.NewServer(proxy)
defer tsP.Close()
// extract host and port
s2 := strings.SplitN(tsP.URL, ":", 2)
hostPort := strings.TrimPrefix(s2[1], "//")

testCases := []struct {
args []string
want checkers.Status
}{
{
// without basic => 407
args: []string{"--proxy", tsP.URL, "-s", "200=warning", "-s", "407=ok",
"-u", ts.URL},
want: checkers.OK,
},
{
// with basic => pass
args: []string{"--proxy", "http://somename:somepassword@" + hostPort,
"--pattern", fmt.Sprintf("intercept a req to %s", ts.URL),
"-u", ts.URL},
want: checkers.OK,
},
}

for i, tc := range testCases {
ckr := Run(tc.args)
assert.Equal(t, ckr.Status, tc.want, "#%d: Status should be %s, %s", i, tc.want, ckr.Message)
}
}

0 comments on commit 0555753

Please sign in to comment.