github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/api/client/client.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 19:16:43</date>
    10  //</624450111354703872>
    11  
    12  
    13  package client
    14  
    15  import (
    16  	"archive/tar"
    17  	"bytes"
    18  	"context"
    19  	"encoding/json"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"io/ioutil"
    24  	"mime/multipart"
    25  	"net/http"
    26  	"net/http/httptrace"
    27  	"net/textproto"
    28  	"net/url"
    29  	"os"
    30  	"path/filepath"
    31  	"regexp"
    32  	"strconv"
    33  	"strings"
    34  	"time"
    35  
    36  	"github.com/ethereum/go-ethereum/log"
    37  	"github.com/ethereum/go-ethereum/metrics"
    38  	"github.com/ethereum/go-ethereum/swarm/api"
    39  	"github.com/ethereum/go-ethereum/swarm/spancontext"
    40  	"github.com/ethereum/go-ethereum/swarm/storage/feed"
    41  	"github.com/pborman/uuid"
    42  )
    43  
    44  var (
    45  	ErrUnauthorized = errors.New("unauthorized")
    46  )
    47  
    48  func NewClient(gateway string) *Client {
    49  	return &Client{
    50  		Gateway: gateway,
    51  	}
    52  }
    53  
    54  //客户端将与Swarm HTTP网关的交互进行包装。
    55  type Client struct {
    56  	Gateway string
    57  }
    58  
    59  //uploadraw将原始数据上载到swarm并返回结果哈希。如果加密是真的
    60  //上载加密数据
    61  func (c *Client) UploadRaw(r io.Reader, size int64, toEncrypt bool) (string, error) {
    62  	if size <= 0 {
    63  		return "", errors.New("data size must be greater than zero")
    64  	}
    65  	addr := ""
    66  	if toEncrypt {
    67  		addr = "encrypt"
    68  	}
    69  	req, err := http.NewRequest("POST", c.Gateway+"/bzz-raw:/"+addr, r)
    70  	if err != nil {
    71  		return "", err
    72  	}
    73  	req.ContentLength = size
    74  	res, err := http.DefaultClient.Do(req)
    75  	if err != nil {
    76  		return "", err
    77  	}
    78  	defer res.Body.Close()
    79  	if res.StatusCode != http.StatusOK {
    80  		return "", fmt.Errorf("unexpected HTTP status: %s", res.Status)
    81  	}
    82  	data, err := ioutil.ReadAll(res.Body)
    83  	if err != nil {
    84  		return "", err
    85  	}
    86  	return string(data), nil
    87  }
    88  
    89  //downloadraw从swarm下载原始数据,它返回readcloser和bool
    90  //内容已加密
    91  func (c *Client) DownloadRaw(hash string) (io.ReadCloser, bool, error) {
    92  	uri := c.Gateway + "/bzz-raw:/" + hash
    93  	res, err := http.DefaultClient.Get(uri)
    94  	if err != nil {
    95  		return nil, false, err
    96  	}
    97  	if res.StatusCode != http.StatusOK {
    98  		res.Body.Close()
    99  		return nil, false, fmt.Errorf("unexpected HTTP status: %s", res.Status)
   100  	}
   101  	isEncrypted := (res.Header.Get("X-Decrypted") == "true")
   102  	return res.Body, isEncrypted, nil
   103  }
   104  
   105  //文件表示群清单中的文件,用于上传和
   106  //从Swarm下载内容
   107  type File struct {
   108  	io.ReadCloser
   109  	api.ManifestEntry
   110  }
   111  
   112  //打开打开一个本地文件,然后可以将其传递到客户端。上载以上载
   113  //它蜂拥而至
   114  func Open(path string) (*File, error) {
   115  	f, err := os.Open(path)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  	stat, err := f.Stat()
   120  	if err != nil {
   121  		f.Close()
   122  		return nil, err
   123  	}
   124  
   125  	contentType, err := api.DetectContentType(f.Name(), f)
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	return &File{
   131  		ReadCloser: f,
   132  		ManifestEntry: api.ManifestEntry{
   133  			ContentType: contentType,
   134  			Mode:        int64(stat.Mode()),
   135  			Size:        stat.Size(),
   136  			ModTime:     stat.ModTime(),
   137  		},
   138  	}, nil
   139  }
   140  
   141  //上载将文件上载到Swarm,并将其添加到现有清单中
   142  //(如果manifest参数非空)或创建包含
   143  //文件,返回生成的清单哈希(然后该文件将
   144  //可在bzz:/<hash>/<path>)获取
   145  func (c *Client) Upload(file *File, manifest string, toEncrypt bool) (string, error) {
   146  	if file.Size <= 0 {
   147  		return "", errors.New("file size must be greater than zero")
   148  	}
   149  	return c.TarUpload(manifest, &FileUploader{file}, "", toEncrypt)
   150  }
   151  
   152  //下载从swarm manifest下载具有给定路径的文件
   153  //给定的哈希(即它得到bzz:/<hash>/<path>)
   154  func (c *Client) Download(hash, path string) (*File, error) {
   155  	uri := c.Gateway + "/bzz:/" + hash + "/" + path
   156  	res, err := http.DefaultClient.Get(uri)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  	if res.StatusCode != http.StatusOK {
   161  		res.Body.Close()
   162  		return nil, fmt.Errorf("unexpected HTTP status: %s", res.Status)
   163  	}
   164  	return &File{
   165  		ReadCloser: res.Body,
   166  		ManifestEntry: api.ManifestEntry{
   167  			ContentType: res.Header.Get("Content-Type"),
   168  			Size:        res.ContentLength,
   169  		},
   170  	}, nil
   171  }
   172  
   173  //uploadDirectory将目录树上载到swarm并添加文件
   174  //到现有清单(如果清单参数非空)或创建
   175  //新清单,返回生成的清单哈希(来自
   176  //目录将在bzz:/<hash>/path/to/file处可用,其中
   177  //默认路径中指定的文件正在上载到清单的根目录
   178  //(即bzz/<hash>/)
   179  func (c *Client) UploadDirectory(dir, defaultPath, manifest string, toEncrypt bool) (string, error) {
   180  	stat, err := os.Stat(dir)
   181  	if err != nil {
   182  		return "", err
   183  	} else if !stat.IsDir() {
   184  		return "", fmt.Errorf("not a directory: %s", dir)
   185  	}
   186  	if defaultPath != "" {
   187  		if _, err := os.Stat(filepath.Join(dir, defaultPath)); err != nil {
   188  			if os.IsNotExist(err) {
   189  				return "", fmt.Errorf("the default path %q was not found in the upload directory %q", defaultPath, dir)
   190  			}
   191  			return "", fmt.Errorf("default path: %v", err)
   192  		}
   193  	}
   194  	return c.TarUpload(manifest, &DirectoryUploader{dir}, defaultPath, toEncrypt)
   195  }
   196  
   197  //下载目录下载群清单中包含的文件
   198  //到本地目录的给定路径(现有文件将被覆盖)
   199  func (c *Client) DownloadDirectory(hash, path, destDir, credentials string) error {
   200  	stat, err := os.Stat(destDir)
   201  	if err != nil {
   202  		return err
   203  	} else if !stat.IsDir() {
   204  		return fmt.Errorf("not a directory: %s", destDir)
   205  	}
   206  
   207  	uri := c.Gateway + "/bzz:/" + hash + "/" + path
   208  	req, err := http.NewRequest("GET", uri, nil)
   209  	if err != nil {
   210  		return err
   211  	}
   212  	if credentials != "" {
   213  		req.SetBasicAuth("", credentials)
   214  	}
   215  	req.Header.Set("Accept", "application/x-tar")
   216  	res, err := http.DefaultClient.Do(req)
   217  	if err != nil {
   218  		return err
   219  	}
   220  	defer res.Body.Close()
   221  	switch res.StatusCode {
   222  	case http.StatusOK:
   223  	case http.StatusUnauthorized:
   224  		return ErrUnauthorized
   225  	default:
   226  		return fmt.Errorf("unexpected HTTP status: %s", res.Status)
   227  	}
   228  	tr := tar.NewReader(res.Body)
   229  	for {
   230  		hdr, err := tr.Next()
   231  		if err == io.EOF {
   232  			return nil
   233  		} else if err != nil {
   234  			return err
   235  		}
   236  //忽略默认路径文件
   237  		if hdr.Name == "" {
   238  			continue
   239  		}
   240  
   241  		dstPath := filepath.Join(destDir, filepath.Clean(strings.TrimPrefix(hdr.Name, path)))
   242  		if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil {
   243  			return err
   244  		}
   245  		var mode os.FileMode = 0644
   246  		if hdr.Mode > 0 {
   247  			mode = os.FileMode(hdr.Mode)
   248  		}
   249  		dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
   250  		if err != nil {
   251  			return err
   252  		}
   253  		n, err := io.Copy(dst, tr)
   254  		dst.Close()
   255  		if err != nil {
   256  			return err
   257  		} else if n != hdr.Size {
   258  			return fmt.Errorf("expected %s to be %d bytes but got %d", hdr.Name, hdr.Size, n)
   259  		}
   260  	}
   261  }
   262  
   263  //下载文件将单个文件下载到目标目录中
   264  //如果清单项未指定文件名-它将回退
   265  //以文件名的形式传递到文件的哈希
   266  func (c *Client) DownloadFile(hash, path, dest, credentials string) error {
   267  	hasDestinationFilename := false
   268  	if stat, err := os.Stat(dest); err == nil {
   269  		hasDestinationFilename = !stat.IsDir()
   270  	} else {
   271  		if os.IsNotExist(err) {
   272  //不存在-应创建
   273  			hasDestinationFilename = true
   274  		} else {
   275  			return fmt.Errorf("could not stat path: %v", err)
   276  		}
   277  	}
   278  
   279  	manifestList, err := c.List(hash, path, credentials)
   280  	if err != nil {
   281  		return err
   282  	}
   283  
   284  	switch len(manifestList.Entries) {
   285  	case 0:
   286  		return fmt.Errorf("could not find path requested at manifest address. make sure the path you've specified is correct")
   287  	case 1:
   288  //持续
   289  	default:
   290  		return fmt.Errorf("got too many matches for this path")
   291  	}
   292  
   293  	uri := c.Gateway + "/bzz:/" + hash + "/" + path
   294  	req, err := http.NewRequest("GET", uri, nil)
   295  	if err != nil {
   296  		return err
   297  	}
   298  	if credentials != "" {
   299  		req.SetBasicAuth("", credentials)
   300  	}
   301  	res, err := http.DefaultClient.Do(req)
   302  	if err != nil {
   303  		return err
   304  	}
   305  	defer res.Body.Close()
   306  	switch res.StatusCode {
   307  	case http.StatusOK:
   308  	case http.StatusUnauthorized:
   309  		return ErrUnauthorized
   310  	default:
   311  		return fmt.Errorf("unexpected HTTP status: expected 200 OK, got %d", res.StatusCode)
   312  	}
   313  	filename := ""
   314  	if hasDestinationFilename {
   315  		filename = dest
   316  	} else {
   317  //尝试断言
   318  re := regexp.MustCompile("[^/]+$") //最后一个斜线后的所有内容
   319  
   320  		if results := re.FindAllString(path, -1); len(results) > 0 {
   321  			filename = results[len(results)-1]
   322  		} else {
   323  			if entry := manifestList.Entries[0]; entry.Path != "" && entry.Path != "/" {
   324  				filename = entry.Path
   325  			} else {
   326  //如果命令行中没有任何内容,则假定hash为名称
   327  				filename = hash
   328  			}
   329  		}
   330  		filename = filepath.Join(dest, filename)
   331  	}
   332  	filePath, err := filepath.Abs(filename)
   333  	if err != nil {
   334  		return err
   335  	}
   336  
   337  	if err := os.MkdirAll(filepath.Dir(filePath), 0777); err != nil {
   338  		return err
   339  	}
   340  
   341  	dst, err := os.Create(filename)
   342  	if err != nil {
   343  		return err
   344  	}
   345  	defer dst.Close()
   346  
   347  	_, err = io.Copy(dst, res.Body)
   348  	return err
   349  }
   350  
   351  //上载清单将给定清单上载到Swarm
   352  func (c *Client) UploadManifest(m *api.Manifest, toEncrypt bool) (string, error) {
   353  	data, err := json.Marshal(m)
   354  	if err != nil {
   355  		return "", err
   356  	}
   357  	return c.UploadRaw(bytes.NewReader(data), int64(len(data)), toEncrypt)
   358  }
   359  
   360  //下载清单下载群清单
   361  func (c *Client) DownloadManifest(hash string) (*api.Manifest, bool, error) {
   362  	res, isEncrypted, err := c.DownloadRaw(hash)
   363  	if err != nil {
   364  		return nil, isEncrypted, err
   365  	}
   366  	defer res.Close()
   367  	var manifest api.Manifest
   368  	if err := json.NewDecoder(res).Decode(&manifest); err != nil {
   369  		return nil, isEncrypted, err
   370  	}
   371  	return &manifest, isEncrypted, nil
   372  }
   373  
   374  //列出具有给定前缀、分组的群清单中的列表文件
   375  //使用“/”作为分隔符的常见前缀。
   376  //
   377  //例如,如果清单表示以下目录结构:
   378  //
   379  //文件1.TXT
   380  //文件2.TXT
   381  //DRI1/FIL3.TXT
   382  //dir1/dir2/file4.txt文件
   383  //
   384  //然后:
   385  //
   386  //-前缀“”将返回[dir1/,file1.txt,file2.txt]
   387  //-前缀“file”将返回[file1.txt,file2.txt]
   388  //-前缀“dir1/”将返回[dir1/dir2/,dir1/file3.txt]
   389  //
   390  //其中以“/”结尾的条目是常见的前缀。
   391  func (c *Client) List(hash, prefix, credentials string) (*api.ManifestList, error) {
   392  	req, err := http.NewRequest(http.MethodGet, c.Gateway+"/bzz-list:/"+hash+"/"+prefix, nil)
   393  	if err != nil {
   394  		return nil, err
   395  	}
   396  	if credentials != "" {
   397  		req.SetBasicAuth("", credentials)
   398  	}
   399  	res, err := http.DefaultClient.Do(req)
   400  	if err != nil {
   401  		return nil, err
   402  	}
   403  	defer res.Body.Close()
   404  	switch res.StatusCode {
   405  	case http.StatusOK:
   406  	case http.StatusUnauthorized:
   407  		return nil, ErrUnauthorized
   408  	default:
   409  		return nil, fmt.Errorf("unexpected HTTP status: %s", res.Status)
   410  	}
   411  	var list api.ManifestList
   412  	if err := json.NewDecoder(res.Body).Decode(&list); err != nil {
   413  		return nil, err
   414  	}
   415  	return &list, nil
   416  }
   417  
   418  //上载程序使用提供的上载将文件上载到Swarm fn
   419  type Uploader interface {
   420  	Upload(UploadFn) error
   421  }
   422  
   423  type UploaderFunc func(UploadFn) error
   424  
   425  func (u UploaderFunc) Upload(upload UploadFn) error {
   426  	return u(upload)
   427  }
   428  
   429  //DirectoryUploader上载目录中的所有文件,可以选择上载
   430  //默认路径的文件
   431  type DirectoryUploader struct {
   432  	Dir string
   433  }
   434  
   435  //上载执行目录和默认路径的上载
   436  func (d *DirectoryUploader) Upload(upload UploadFn) error {
   437  	return filepath.Walk(d.Dir, func(path string, f os.FileInfo, err error) error {
   438  		if err != nil {
   439  			return err
   440  		}
   441  		if f.IsDir() {
   442  			return nil
   443  		}
   444  		file, err := Open(path)
   445  		if err != nil {
   446  			return err
   447  		}
   448  		relPath, err := filepath.Rel(d.Dir, path)
   449  		if err != nil {
   450  			return err
   451  		}
   452  		file.Path = filepath.ToSlash(relPath)
   453  		return upload(file)
   454  	})
   455  }
   456  
   457  //文件上载程序上载单个文件
   458  type FileUploader struct {
   459  	File *File
   460  }
   461  
   462  //上载执行文件上载
   463  func (f *FileUploader) Upload(upload UploadFn) error {
   464  	return upload(f.File)
   465  }
   466  
   467  //uploadfn是传递给上载程序以执行上载的函数类型。
   468  //对于单个文件(例如,目录上载程序将调用
   469  //目录树中每个文件的uploadfn)
   470  type UploadFn func(file *File) error
   471  
   472  //tar upload使用给定的上传器将文件作为tar流上传到swarm,
   473  //返回结果清单哈希
   474  func (c *Client) TarUpload(hash string, uploader Uploader, defaultPath string, toEncrypt bool) (string, error) {
   475  	ctx, sp := spancontext.StartSpan(context.Background(), "api.client.tarupload")
   476  	defer sp.Finish()
   477  
   478  	var tn time.Time
   479  
   480  	reqR, reqW := io.Pipe()
   481  	defer reqR.Close()
   482  	addr := hash
   483  
   484  //如果已经存在哈希(清单),那么该清单将确定上载是否
   485  //是否加密。如果没有清单,则toEncrypt参数决定
   486  //是否加密。
   487  	if hash == "" && toEncrypt {
   488  //这是加密上载端点的内置地址
   489  		addr = "encrypt"
   490  	}
   491  	req, err := http.NewRequest("POST", c.Gateway+"/bzz:/"+addr, reqR)
   492  	if err != nil {
   493  		return "", err
   494  	}
   495  
   496  	trace := GetClientTrace("swarm api client - upload tar", "api.client.uploadtar", uuid.New()[:8], &tn)
   497  
   498  	req = req.WithContext(httptrace.WithClientTrace(ctx, trace))
   499  	transport := http.DefaultTransport
   500  
   501  	req.Header.Set("Content-Type", "application/x-tar")
   502  	if defaultPath != "" {
   503  		q := req.URL.Query()
   504  		q.Set("defaultpath", defaultPath)
   505  		req.URL.RawQuery = q.Encode()
   506  	}
   507  
   508  //使用“expect:100 continue”,以便在以下情况下不发送请求正文:
   509  //服务器拒绝请求
   510  	req.Header.Set("Expect", "100-continue")
   511  
   512  	tw := tar.NewWriter(reqW)
   513  
   514  //定义将文件添加到tar流的uploadfn
   515  	uploadFn := func(file *File) error {
   516  		hdr := &tar.Header{
   517  			Name:    file.Path,
   518  			Mode:    file.Mode,
   519  			Size:    file.Size,
   520  			ModTime: file.ModTime,
   521  			Xattrs: map[string]string{
   522  				"user.swarm.content-type": file.ContentType,
   523  			},
   524  		}
   525  		if err := tw.WriteHeader(hdr); err != nil {
   526  			return err
   527  		}
   528  		_, err = io.Copy(tw, file)
   529  		return err
   530  	}
   531  
   532  //在Goroutine中运行上载,以便我们可以发送请求头和
   533  //在发送tar流之前,等待“100 continue”响应
   534  	go func() {
   535  		err := uploader.Upload(uploadFn)
   536  		if err == nil {
   537  			err = tw.Close()
   538  		}
   539  		reqW.CloseWithError(err)
   540  	}()
   541  	tn = time.Now()
   542  	res, err := transport.RoundTrip(req)
   543  	if err != nil {
   544  		return "", err
   545  	}
   546  	defer res.Body.Close()
   547  	if res.StatusCode != http.StatusOK {
   548  		return "", fmt.Errorf("unexpected HTTP status: %s", res.Status)
   549  	}
   550  	data, err := ioutil.ReadAll(res.Body)
   551  	if err != nil {
   552  		return "", err
   553  	}
   554  	return string(data), nil
   555  }
   556  
   557  //multipartupload使用给定的上载程序将文件作为
   558  //多部分表单,返回结果清单哈希
   559  func (c *Client) MultipartUpload(hash string, uploader Uploader) (string, error) {
   560  	reqR, reqW := io.Pipe()
   561  	defer reqR.Close()
   562  	req, err := http.NewRequest("POST", c.Gateway+"/bzz:/"+hash, reqR)
   563  	if err != nil {
   564  		return "", err
   565  	}
   566  
   567  //使用“expect:100 continue”,以便在以下情况下不发送请求正文:
   568  //服务器拒绝请求
   569  	req.Header.Set("Expect", "100-continue")
   570  
   571  	mw := multipart.NewWriter(reqW)
   572  	req.Header.Set("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%q", mw.Boundary()))
   573  
   574  //定义将文件添加到多部分表单的uploadfn
   575  	uploadFn := func(file *File) error {
   576  		hdr := make(textproto.MIMEHeader)
   577  		hdr.Set("Content-Disposition", fmt.Sprintf("form-data; name=%q", file.Path))
   578  		hdr.Set("Content-Type", file.ContentType)
   579  		hdr.Set("Content-Length", strconv.FormatInt(file.Size, 10))
   580  		w, err := mw.CreatePart(hdr)
   581  		if err != nil {
   582  			return err
   583  		}
   584  		_, err = io.Copy(w, file)
   585  		return err
   586  	}
   587  
   588  //在Goroutine中运行上载,以便我们可以发送请求头和
   589  //在发送多部分表单之前,请等待“100继续”响应
   590  	go func() {
   591  		err := uploader.Upload(uploadFn)
   592  		if err == nil {
   593  			err = mw.Close()
   594  		}
   595  		reqW.CloseWithError(err)
   596  	}()
   597  
   598  	res, err := http.DefaultClient.Do(req)
   599  	if err != nil {
   600  		return "", err
   601  	}
   602  	defer res.Body.Close()
   603  	if res.StatusCode != http.StatusOK {
   604  		return "", fmt.Errorf("unexpected HTTP status: %s", res.Status)
   605  	}
   606  	data, err := ioutil.ReadAll(res.Body)
   607  	if err != nil {
   608  		return "", err
   609  	}
   610  	return string(data), nil
   611  }
   612  
   613  //当Swarm找不到给定源的更新时,返回errnoFeedUpdatesFund。
   614  var ErrNoFeedUpdatesFound = errors.New("No updates found for this feed")
   615  
   616  //CreateFeedWithManifest创建源清单,并使用提供的
   617  //数据
   618  //返回可用于包含在ENS解析程序(setcontent)中的结果源清单地址。
   619  //或引用将来的更新(client.updatefeed)
   620  func (c *Client) CreateFeedWithManifest(request *feed.Request) (string, error) {
   621  	responseStream, err := c.updateFeed(request, true)
   622  	if err != nil {
   623  		return "", err
   624  	}
   625  	defer responseStream.Close()
   626  
   627  	body, err := ioutil.ReadAll(responseStream)
   628  	if err != nil {
   629  		return "", err
   630  	}
   631  
   632  	var manifestAddress string
   633  	if err = json.Unmarshal(body, &manifestAddress); err != nil {
   634  		return "", err
   635  	}
   636  	return manifestAddress, nil
   637  }
   638  
   639  //更新源允许您设置内容的新版本
   640  func (c *Client) UpdateFeed(request *feed.Request) error {
   641  	_, err := c.updateFeed(request, false)
   642  	return err
   643  }
   644  
   645  func (c *Client) updateFeed(request *feed.Request, createManifest bool) (io.ReadCloser, error) {
   646  	URL, err := url.Parse(c.Gateway)
   647  	if err != nil {
   648  		return nil, err
   649  	}
   650  	URL.Path = "/bzz-feed:/"
   651  	values := URL.Query()
   652  	body := request.AppendValues(values)
   653  	if createManifest {
   654  		values.Set("manifest", "1")
   655  	}
   656  	URL.RawQuery = values.Encode()
   657  
   658  	req, err := http.NewRequest("POST", URL.String(), bytes.NewBuffer(body))
   659  	if err != nil {
   660  		return nil, err
   661  	}
   662  
   663  	res, err := http.DefaultClient.Do(req)
   664  	if err != nil {
   665  		return nil, err
   666  	}
   667  
   668  	return res.Body, nil
   669  }
   670  
   671  //queryfeed返回具有源更新的原始内容的字节流
   672  //ManifestAddressOrDomain是您在CreateFeedWithManifest或其解析程序的ENS域中获得的地址。
   673  //指向那个地址
   674  func (c *Client) QueryFeed(query *feed.Query, manifestAddressOrDomain string) (io.ReadCloser, error) {
   675  	return c.queryFeed(query, manifestAddressOrDomain, false)
   676  }
   677  
   678  //queryfeed返回具有源更新的原始内容的字节流
   679  //ManifestAddressOrDomain是您在CreateFeedWithManifest或其解析程序的ENS域中获得的地址。
   680  //指向那个地址
   681  //meta设置为true将指示节点返回feed metainformation,而不是
   682  func (c *Client) queryFeed(query *feed.Query, manifestAddressOrDomain string, meta bool) (io.ReadCloser, error) {
   683  	URL, err := url.Parse(c.Gateway)
   684  	if err != nil {
   685  		return nil, err
   686  	}
   687  	URL.Path = "/bzz-feed:/" + manifestAddressOrDomain
   688  	values := URL.Query()
   689  	if query != nil {
   690  query.AppendValues(values) //添加查询参数
   691  	}
   692  	if meta {
   693  		values.Set("meta", "1")
   694  	}
   695  	URL.RawQuery = values.Encode()
   696  	res, err := http.Get(URL.String())
   697  	if err != nil {
   698  		return nil, err
   699  	}
   700  
   701  	if res.StatusCode != http.StatusOK {
   702  		if res.StatusCode == http.StatusNotFound {
   703  			return nil, ErrNoFeedUpdatesFound
   704  		}
   705  		errorMessageBytes, err := ioutil.ReadAll(res.Body)
   706  		var errorMessage string
   707  		if err != nil {
   708  			errorMessage = "cannot retrieve error message: " + err.Error()
   709  		} else {
   710  			errorMessage = string(errorMessageBytes)
   711  		}
   712  		return nil, fmt.Errorf("Error retrieving feed updates: %s", errorMessage)
   713  	}
   714  
   715  	return res.Body, nil
   716  }
   717  
   718  //GetFeedRequest返回一个描述引用的源状态的结构
   719  //ManifestAddressOrDomain是您在CreateFeedWithManifest或其解析程序的ENS域中获得的地址。
   720  //指向那个地址
   721  func (c *Client) GetFeedRequest(query *feed.Query, manifestAddressOrDomain string) (*feed.Request, error) {
   722  
   723  	responseStream, err := c.queryFeed(query, manifestAddressOrDomain, true)
   724  	if err != nil {
   725  		return nil, err
   726  	}
   727  	defer responseStream.Close()
   728  
   729  	body, err := ioutil.ReadAll(responseStream)
   730  	if err != nil {
   731  		return nil, err
   732  	}
   733  
   734  	var metadata feed.Request
   735  	if err := metadata.UnmarshalJSON(body); err != nil {
   736  		return nil, err
   737  	}
   738  	return &metadata, nil
   739  }
   740  
   741  func GetClientTrace(traceMsg, metricPrefix, ruid string, tn *time.Time) *httptrace.ClientTrace {
   742  	trace := &httptrace.ClientTrace{
   743  		GetConn: func(_ string) {
   744  			log.Trace(traceMsg+" - http get", "event", "GetConn", "ruid", ruid)
   745  			metrics.GetOrRegisterResettingTimer(metricPrefix+".getconn", nil).Update(time.Since(*tn))
   746  		},
   747  		GotConn: func(_ httptrace.GotConnInfo) {
   748  			log.Trace(traceMsg+" - http get", "event", "GotConn", "ruid", ruid)
   749  			metrics.GetOrRegisterResettingTimer(metricPrefix+".gotconn", nil).Update(time.Since(*tn))
   750  		},
   751  		PutIdleConn: func(err error) {
   752  			log.Trace(traceMsg+" - http get", "event", "PutIdleConn", "ruid", ruid, "err", err)
   753  			metrics.GetOrRegisterResettingTimer(metricPrefix+".putidle", nil).Update(time.Since(*tn))
   754  		},
   755  		GotFirstResponseByte: func() {
   756  			log.Trace(traceMsg+" - http get", "event", "GotFirstResponseByte", "ruid", ruid)
   757  			metrics.GetOrRegisterResettingTimer(metricPrefix+".firstbyte", nil).Update(time.Since(*tn))
   758  		},
   759  		Got100Continue: func() {
   760  			log.Trace(traceMsg, "event", "Got100Continue", "ruid", ruid)
   761  			metrics.GetOrRegisterResettingTimer(metricPrefix+".got100continue", nil).Update(time.Since(*tn))
   762  		},
   763  		DNSStart: func(_ httptrace.DNSStartInfo) {
   764  			log.Trace(traceMsg, "event", "DNSStart", "ruid", ruid)
   765  			metrics.GetOrRegisterResettingTimer(metricPrefix+".dnsstart", nil).Update(time.Since(*tn))
   766  		},
   767  		DNSDone: func(_ httptrace.DNSDoneInfo) {
   768  			log.Trace(traceMsg, "event", "DNSDone", "ruid", ruid)
   769  			metrics.GetOrRegisterResettingTimer(metricPrefix+".dnsdone", nil).Update(time.Since(*tn))
   770  		},
   771  		ConnectStart: func(network, addr string) {
   772  			log.Trace(traceMsg, "event", "ConnectStart", "ruid", ruid, "network", network, "addr", addr)
   773  			metrics.GetOrRegisterResettingTimer(metricPrefix+".connectstart", nil).Update(time.Since(*tn))
   774  		},
   775  		ConnectDone: func(network, addr string, err error) {
   776  			log.Trace(traceMsg, "event", "ConnectDone", "ruid", ruid, "network", network, "addr", addr, "err", err)
   777  			metrics.GetOrRegisterResettingTimer(metricPrefix+".connectdone", nil).Update(time.Since(*tn))
   778  		},
   779  		WroteHeaders: func() {
   780  			log.Trace(traceMsg, "event", "WroteHeaders(request)", "ruid", ruid)
   781  			metrics.GetOrRegisterResettingTimer(metricPrefix+".wroteheaders", nil).Update(time.Since(*tn))
   782  		},
   783  		Wait100Continue: func() {
   784  			log.Trace(traceMsg, "event", "Wait100Continue", "ruid", ruid)
   785  			metrics.GetOrRegisterResettingTimer(metricPrefix+".wait100continue", nil).Update(time.Since(*tn))
   786  		},
   787  		WroteRequest: func(_ httptrace.WroteRequestInfo) {
   788  			log.Trace(traceMsg, "event", "WroteRequest", "ruid", ruid)
   789  			metrics.GetOrRegisterResettingTimer(metricPrefix+".wroterequest", nil).Update(time.Since(*tn))
   790  		},
   791  	}
   792  	return trace
   793  }
   794