// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package fetch provides an extensible mechanism to fetch a profile
// from a data source.
package fetch
import (
"crypto/tls"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"time"
"cmd/pprof/internal/plugin"
"internal/pprof/profile"
)
// FetchProfile reads from a data source (network, file) and generates a
// profile.
func FetchProfile(source string, timeout time.Duration) (*profile.Profile, error) {
return Fetcher(source, timeout, plugin.StandardUI())
}
// Fetcher is the plugin.Fetcher version of FetchProfile.
func Fetcher(source string, timeout time.Duration, ui plugin.UI) (*profile.Profile, error) {
var f io.ReadCloser
var err error
url, err := url.Parse(source)
if err == nil && url.Host != "" {
f, err = FetchURL(source, timeout)
} else {
f, err = os.Open(source)
}
if err != nil {
return nil, err
}
defer f.Close()
return profile.Parse(f)
}
// FetchURL fetches a profile from a URL using HTTP.
func FetchURL(source string, timeout time.Duration) (io.ReadCloser, error) {
resp, err := httpGet(source, timeout)
if err != nil {
return nil, fmt.Errorf("http fetch %s: %v", source, err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("server response: %s", resp.Status)
}
return resp.Body, nil
}
// PostURL issues a POST to a URL over HTTP.
func PostURL(source, post string) ([]byte, error) {
resp, err := http.Post(source, "application/octet-stream", strings.NewReader(post))
if err != nil {
return nil, fmt.Errorf("http post %s: %v", source, err)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("server response: %s", resp.Status)
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}
// httpGet is a wrapper around http.Get; it is defined as a variable
// so it can be redefined during for testing.
var httpGet = func(source string, timeout time.Duration) (*http.Response, error) {
url, err := url.Parse(source)
if err != nil {
return nil, err
}
var tlsConfig *tls.Config
if url.Scheme == "https+insecure" {
tlsConfig = &tls.Config{
InsecureSkipVerify: true,
}
url.Scheme = "https"
source = url.String()
}
client := &http.Client{
Transport: &http.Transport{
ResponseHeaderTimeout: timeout + 5*time.Second,
TLSClientConfig: tlsConfig,
},
}
return client.Get(source)
}