github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/common/httpclient/httpclient.go (about)

     1  // Copyright 2015 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package httpclient
    18  
    19  import (
    20  	"fmt"
    21  	"io/ioutil"
    22  	"net/http"
    23  	"path/filepath"
    24  
    25  	"github.com/ethereumproject/go-ethereum/common"
    26  	"github.com/ethereumproject/go-ethereum/crypto"
    27  )
    28  
    29  type HTTPClient struct {
    30  	*http.Transport
    31  	DocRoot string
    32  	schemes []string
    33  }
    34  
    35  func New(docRoot string) (self *HTTPClient) {
    36  	self = &HTTPClient{
    37  		Transport: &http.Transport{},
    38  		DocRoot:   docRoot,
    39  		schemes:   []string{"file"},
    40  	}
    41  	self.RegisterProtocol("file", http.NewFileTransport(http.Dir(self.DocRoot)))
    42  	return
    43  }
    44  
    45  // Clients should be reused instead of created as needed. Clients are safe for concurrent use by multiple goroutines.
    46  
    47  // A Client is higher-level than a RoundTripper (such as Transport) and additionally handles HTTP details such as cookies and redirects.
    48  
    49  func (self *HTTPClient) Client() *http.Client {
    50  	return &http.Client{
    51  		Transport: self,
    52  	}
    53  }
    54  
    55  func (self *HTTPClient) RegisterScheme(scheme string, rt http.RoundTripper) {
    56  	self.schemes = append(self.schemes, scheme)
    57  	self.RegisterProtocol(scheme, rt)
    58  }
    59  
    60  func (self *HTTPClient) HasScheme(scheme string) bool {
    61  	for _, s := range self.schemes {
    62  		if s == scheme {
    63  			return true
    64  		}
    65  	}
    66  	return false
    67  }
    68  
    69  func (self *HTTPClient) GetAuthContent(uri string, hash common.Hash) ([]byte, error) {
    70  	// retrieve content
    71  	content, err := self.Get(uri, "")
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  
    76  	// check hash to authenticate content
    77  	chash := crypto.Keccak256Hash(content)
    78  	if chash != hash {
    79  		return nil, fmt.Errorf("content hash mismatch %x != %x (exp)", hash[:], chash[:])
    80  	}
    81  
    82  	return content, nil
    83  
    84  }
    85  
    86  // Get(uri, path) downloads the document at uri, if path is non-empty it
    87  // is interpreted as a filepath to which the contents are saved
    88  func (self *HTTPClient) Get(uri, path string) ([]byte, error) {
    89  	// retrieve content
    90  	resp, err := self.Client().Get(uri)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	defer func() {
    95  		if resp != nil {
    96  			resp.Body.Close()
    97  		}
    98  	}()
    99  
   100  	var content []byte
   101  	content, err = ioutil.ReadAll(resp.Body)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  
   106  	if resp.StatusCode/100 != 2 {
   107  		return content, fmt.Errorf("HTTP error: %s", resp.Status)
   108  	}
   109  
   110  	if path != "" {
   111  		var abspath string
   112  		abspath, err = filepath.Abs(path)
   113  		if err != nil {
   114  			return nil, err
   115  		}
   116  		err = ioutil.WriteFile(abspath, content, 0600)
   117  		if err != nil {
   118  			return nil, err
   119  		}
   120  	}
   121  
   122  	return content, nil
   123  
   124  }