Home | History | Annotate | Download | only in fetch
      1 // Copyright 2014 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Package fetch provides an extensible mechanism to fetch a profile
      6 // from a data source.
      7 package fetch
      8 
      9 import (
     10 	"fmt"
     11 	"io"
     12 	"io/ioutil"
     13 	"net/http"
     14 	"net/url"
     15 	"os"
     16 	"strings"
     17 	"time"
     18 
     19 	"cmd/pprof/internal/plugin"
     20 	"cmd/pprof/internal/profile"
     21 )
     22 
     23 // FetchProfile reads from a data source (network, file) and generates a
     24 // profile.
     25 func FetchProfile(source string, timeout time.Duration) (*profile.Profile, error) {
     26 	return Fetcher(source, timeout, plugin.StandardUI())
     27 }
     28 
     29 // Fetcher is the plugin.Fetcher version of FetchProfile.
     30 func Fetcher(source string, timeout time.Duration, ui plugin.UI) (*profile.Profile, error) {
     31 	var f io.ReadCloser
     32 	var err error
     33 
     34 	url, err := url.Parse(source)
     35 	if err == nil && url.Host != "" {
     36 		f, err = FetchURL(source, timeout)
     37 	} else {
     38 		f, err = os.Open(source)
     39 	}
     40 	if err != nil {
     41 		return nil, err
     42 	}
     43 	defer f.Close()
     44 	return profile.Parse(f)
     45 }
     46 
     47 // FetchURL fetches a profile from a URL using HTTP.
     48 func FetchURL(source string, timeout time.Duration) (io.ReadCloser, error) {
     49 	resp, err := httpGet(source, timeout)
     50 	if err != nil {
     51 		return nil, fmt.Errorf("http fetch %s: %v", source, err)
     52 	}
     53 	if resp.StatusCode != http.StatusOK {
     54 		return nil, fmt.Errorf("server response: %s", resp.Status)
     55 	}
     56 
     57 	return resp.Body, nil
     58 }
     59 
     60 // PostURL issues a POST to a URL over HTTP.
     61 func PostURL(source, post string) ([]byte, error) {
     62 	resp, err := http.Post(source, "application/octet-stream", strings.NewReader(post))
     63 	if err != nil {
     64 		return nil, fmt.Errorf("http post %s: %v", source, err)
     65 	}
     66 	if resp.StatusCode != http.StatusOK {
     67 		return nil, fmt.Errorf("server response: %s", resp.Status)
     68 	}
     69 	defer resp.Body.Close()
     70 	return ioutil.ReadAll(resp.Body)
     71 }
     72 
     73 // httpGet is a wrapper around http.Get; it is defined as a variable
     74 // so it can be redefined during for testing.
     75 var httpGet = func(url string, timeout time.Duration) (*http.Response, error) {
     76 	client := &http.Client{
     77 		Transport: &http.Transport{
     78 			ResponseHeaderTimeout: timeout + 5*time.Second,
     79 		},
     80 	}
     81 	return client.Get(url)
     82 }
     83