github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/api/http/server.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  //</624450112046764032>
    11  
    12  
    13  /*
    14  Swarm的简单HTTP服务器接口
    15  **/
    16  
    17  package http
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"encoding/json"
    23  	"fmt"
    24  	"io"
    25  	"io/ioutil"
    26  	"mime"
    27  	"mime/multipart"
    28  	"net/http"
    29  	"os"
    30  	"path"
    31  	"strconv"
    32  	"strings"
    33  	"time"
    34  
    35  	"github.com/ethereum/go-ethereum/common"
    36  	"github.com/ethereum/go-ethereum/metrics"
    37  	"github.com/ethereum/go-ethereum/swarm/api"
    38  	"github.com/ethereum/go-ethereum/swarm/log"
    39  	"github.com/ethereum/go-ethereum/swarm/storage"
    40  	"github.com/ethereum/go-ethereum/swarm/storage/feed"
    41  	"github.com/rs/cors"
    42  )
    43  
    44  var (
    45  	postRawCount    = metrics.NewRegisteredCounter("api.http.post.raw.count", nil)
    46  	postRawFail     = metrics.NewRegisteredCounter("api.http.post.raw.fail", nil)
    47  	postFilesCount  = metrics.NewRegisteredCounter("api.http.post.files.count", nil)
    48  	postFilesFail   = metrics.NewRegisteredCounter("api.http.post.files.fail", nil)
    49  	deleteCount     = metrics.NewRegisteredCounter("api.http.delete.count", nil)
    50  	deleteFail      = metrics.NewRegisteredCounter("api.http.delete.fail", nil)
    51  	getCount        = metrics.NewRegisteredCounter("api.http.get.count", nil)
    52  	getFail         = metrics.NewRegisteredCounter("api.http.get.fail", nil)
    53  	getFileCount    = metrics.NewRegisteredCounter("api.http.get.file.count", nil)
    54  	getFileNotFound = metrics.NewRegisteredCounter("api.http.get.file.notfound", nil)
    55  	getFileFail     = metrics.NewRegisteredCounter("api.http.get.file.fail", nil)
    56  	getListCount    = metrics.NewRegisteredCounter("api.http.get.list.count", nil)
    57  	getListFail     = metrics.NewRegisteredCounter("api.http.get.list.fail", nil)
    58  )
    59  
    60  type methodHandler map[string]http.Handler
    61  
    62  func (m methodHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
    63  	v, ok := m[r.Method]
    64  	if ok {
    65  		v.ServeHTTP(rw, r)
    66  		return
    67  	}
    68  	rw.WriteHeader(http.StatusMethodNotAllowed)
    69  }
    70  
    71  func NewServer(api *api.API, corsString string) *Server {
    72  	var allowedOrigins []string
    73  	for _, domain := range strings.Split(corsString, ",") {
    74  		allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain))
    75  	}
    76  	c := cors.New(cors.Options{
    77  		AllowedOrigins: allowedOrigins,
    78  		AllowedMethods: []string{http.MethodPost, http.MethodGet, http.MethodDelete, http.MethodPatch, http.MethodPut},
    79  		MaxAge:         600,
    80  		AllowedHeaders: []string{"*"},
    81  	})
    82  
    83  	server := &Server{api: api}
    84  
    85  	defaultMiddlewares := []Adapter{
    86  		RecoverPanic,
    87  		SetRequestID,
    88  		SetRequestHost,
    89  		InitLoggingResponseWriter,
    90  		ParseURI,
    91  		InstrumentOpenTracing,
    92  	}
    93  
    94  	mux := http.NewServeMux()
    95  	mux.Handle("/bzz:/", methodHandler{
    96  		"GET": Adapt(
    97  			http.HandlerFunc(server.HandleBzzGet),
    98  			defaultMiddlewares...,
    99  		),
   100  		"POST": Adapt(
   101  			http.HandlerFunc(server.HandlePostFiles),
   102  			defaultMiddlewares...,
   103  		),
   104  		"DELETE": Adapt(
   105  			http.HandlerFunc(server.HandleDelete),
   106  			defaultMiddlewares...,
   107  		),
   108  	})
   109  	mux.Handle("/bzz-raw:/", methodHandler{
   110  		"GET": Adapt(
   111  			http.HandlerFunc(server.HandleGet),
   112  			defaultMiddlewares...,
   113  		),
   114  		"POST": Adapt(
   115  			http.HandlerFunc(server.HandlePostRaw),
   116  			defaultMiddlewares...,
   117  		),
   118  	})
   119  	mux.Handle("/bzz-immutable:/", methodHandler{
   120  		"GET": Adapt(
   121  			http.HandlerFunc(server.HandleBzzGet),
   122  			defaultMiddlewares...,
   123  		),
   124  	})
   125  	mux.Handle("/bzz-hash:/", methodHandler{
   126  		"GET": Adapt(
   127  			http.HandlerFunc(server.HandleGet),
   128  			defaultMiddlewares...,
   129  		),
   130  	})
   131  	mux.Handle("/bzz-list:/", methodHandler{
   132  		"GET": Adapt(
   133  			http.HandlerFunc(server.HandleGetList),
   134  			defaultMiddlewares...,
   135  		),
   136  	})
   137  	mux.Handle("/bzz-feed:/", methodHandler{
   138  		"GET": Adapt(
   139  			http.HandlerFunc(server.HandleGetFeed),
   140  			defaultMiddlewares...,
   141  		),
   142  		"POST": Adapt(
   143  			http.HandlerFunc(server.HandlePostFeed),
   144  			defaultMiddlewares...,
   145  		),
   146  	})
   147  
   148  	mux.Handle("/", methodHandler{
   149  		"GET": Adapt(
   150  			http.HandlerFunc(server.HandleRootPaths),
   151  			SetRequestID,
   152  			InitLoggingResponseWriter,
   153  		),
   154  	})
   155  	server.Handler = c.Handler(mux)
   156  
   157  	return server
   158  }
   159  
   160  func (s *Server) ListenAndServe(addr string) error {
   161  	s.listenAddr = addr
   162  	return http.ListenAndServe(addr, s)
   163  }
   164  
   165  //用于注册BZZ URL方案处理程序的浏览器API:
   166  //https://developer.mozilla.org/en/docs/web-based_协议处理程序
   167  //用于注册BZZ URL方案处理程序的电子(铬)API:
   168  //https://github.com/atom/electron/blob/master/docs/api/protocol.md网站
   169  type Server struct {
   170  	http.Handler
   171  	api        *api.API
   172  	listenAddr string
   173  }
   174  
   175  func (s *Server) HandleBzzGet(w http.ResponseWriter, r *http.Request) {
   176  	log.Debug("handleBzzGet", "ruid", GetRUID(r.Context()), "uri", r.RequestURI)
   177  	if r.Header.Get("Accept") == "application/x-tar" {
   178  		uri := GetURI(r.Context())
   179  		_, credentials, _ := r.BasicAuth()
   180  		reader, err := s.api.GetDirectoryTar(r.Context(), s.api.Decryptor(r.Context(), credentials), uri)
   181  		if err != nil {
   182  			if isDecryptError(err) {
   183  				w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", uri.Address().String()))
   184  				respondError(w, r, err.Error(), http.StatusUnauthorized)
   185  				return
   186  			}
   187  			respondError(w, r, fmt.Sprintf("Had an error building the tarball: %v", err), http.StatusInternalServerError)
   188  			return
   189  		}
   190  		defer reader.Close()
   191  
   192  		w.Header().Set("Content-Type", "application/x-tar")
   193  
   194  		fileName := uri.Addr
   195  		if found := path.Base(uri.Path); found != "" && found != "." && found != "/" {
   196  			fileName = found
   197  		}
   198  		w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s.tar\"", fileName))
   199  
   200  		w.WriteHeader(http.StatusOK)
   201  		io.Copy(w, reader)
   202  		return
   203  	}
   204  
   205  	s.HandleGetFile(w, r)
   206  }
   207  
   208  func (s *Server) HandleRootPaths(w http.ResponseWriter, r *http.Request) {
   209  	switch r.RequestURI {
   210  	case "/":
   211  		respondTemplate(w, r, "landing-page", "Swarm: Please request a valid ENS or swarm hash with the appropriate bzz scheme", 200)
   212  		return
   213  	case "/robots.txt":
   214  		w.Header().Set("Last-Modified", time.Now().Format(http.TimeFormat))
   215  		fmt.Fprintf(w, "User-agent: *\nDisallow: /")
   216  	case "/favicon.ico":
   217  		w.WriteHeader(http.StatusOK)
   218  		w.Write(faviconBytes)
   219  	default:
   220  		respondError(w, r, "Not Found", http.StatusNotFound)
   221  	}
   222  }
   223  
   224  //handlePostraw处理对raw bzz raw的post请求:/uri,存储该请求
   225  //swarm中的body并返回结果存储地址作为文本/纯响应
   226  func (s *Server) HandlePostRaw(w http.ResponseWriter, r *http.Request) {
   227  	ruid := GetRUID(r.Context())
   228  	log.Debug("handle.post.raw", "ruid", ruid)
   229  
   230  	postRawCount.Inc(1)
   231  
   232  	toEncrypt := false
   233  	uri := GetURI(r.Context())
   234  	if uri.Addr == "encrypt" {
   235  		toEncrypt = true
   236  	}
   237  
   238  	if uri.Path != "" {
   239  		postRawFail.Inc(1)
   240  		respondError(w, r, "raw POST request cannot contain a path", http.StatusBadRequest)
   241  		return
   242  	}
   243  
   244  	if uri.Addr != "" && uri.Addr != "encrypt" {
   245  		postRawFail.Inc(1)
   246  		respondError(w, r, "raw POST request addr can only be empty or \"encrypt\"", http.StatusBadRequest)
   247  		return
   248  	}
   249  
   250  	if r.Header.Get("Content-Length") == "" {
   251  		postRawFail.Inc(1)
   252  		respondError(w, r, "missing Content-Length header in request", http.StatusBadRequest)
   253  		return
   254  	}
   255  
   256  	addr, _, err := s.api.Store(r.Context(), r.Body, r.ContentLength, toEncrypt)
   257  	if err != nil {
   258  		postRawFail.Inc(1)
   259  		respondError(w, r, err.Error(), http.StatusInternalServerError)
   260  		return
   261  	}
   262  
   263  	log.Debug("stored content", "ruid", ruid, "key", addr)
   264  
   265  	w.Header().Set("Content-Type", "text/plain")
   266  	w.WriteHeader(http.StatusOK)
   267  	fmt.Fprint(w, addr)
   268  }
   269  
   270  //handlePostfiles处理对
   271  //bzz:/<hash>/<path>其中包含单个文件或多个文件
   272  //(tar存档或多部分表单),将这些文件添加到
   273  //现有清单或<path>下的新清单,并返回
   274  //作为文本/纯响应的结果清单哈希
   275  func (s *Server) HandlePostFiles(w http.ResponseWriter, r *http.Request) {
   276  	ruid := GetRUID(r.Context())
   277  	log.Debug("handle.post.files", "ruid", ruid)
   278  	postFilesCount.Inc(1)
   279  
   280  	contentType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
   281  	if err != nil {
   282  		postFilesFail.Inc(1)
   283  		respondError(w, r, err.Error(), http.StatusBadRequest)
   284  		return
   285  	}
   286  
   287  	toEncrypt := false
   288  	uri := GetURI(r.Context())
   289  	if uri.Addr == "encrypt" {
   290  		toEncrypt = true
   291  	}
   292  
   293  	var addr storage.Address
   294  	if uri.Addr != "" && uri.Addr != "encrypt" {
   295  		addr, err = s.api.Resolve(r.Context(), uri.Addr)
   296  		if err != nil {
   297  			postFilesFail.Inc(1)
   298  			respondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusInternalServerError)
   299  			return
   300  		}
   301  		log.Debug("resolved key", "ruid", ruid, "key", addr)
   302  	} else {
   303  		addr, err = s.api.NewManifest(r.Context(), toEncrypt)
   304  		if err != nil {
   305  			postFilesFail.Inc(1)
   306  			respondError(w, r, err.Error(), http.StatusInternalServerError)
   307  			return
   308  		}
   309  		log.Debug("new manifest", "ruid", ruid, "key", addr)
   310  	}
   311  
   312  	newAddr, err := s.api.UpdateManifest(r.Context(), addr, func(mw *api.ManifestWriter) error {
   313  		switch contentType {
   314  		case "application/x-tar":
   315  			_, err := s.handleTarUpload(r, mw)
   316  			if err != nil {
   317  				respondError(w, r, fmt.Sprintf("error uploading tarball: %v", err), http.StatusInternalServerError)
   318  				return err
   319  			}
   320  			return nil
   321  		case "multipart/form-data":
   322  			return s.handleMultipartUpload(r, params["boundary"], mw)
   323  
   324  		default:
   325  			return s.handleDirectUpload(r, mw)
   326  		}
   327  	})
   328  	if err != nil {
   329  		postFilesFail.Inc(1)
   330  		respondError(w, r, fmt.Sprintf("cannot create manifest: %s", err), http.StatusInternalServerError)
   331  		return
   332  	}
   333  
   334  	log.Debug("stored content", "ruid", ruid, "key", newAddr)
   335  
   336  	w.Header().Set("Content-Type", "text/plain")
   337  	w.WriteHeader(http.StatusOK)
   338  	fmt.Fprint(w, newAddr)
   339  }
   340  
   341  func (s *Server) handleTarUpload(r *http.Request, mw *api.ManifestWriter) (storage.Address, error) {
   342  	log.Debug("handle.tar.upload", "ruid", GetRUID(r.Context()))
   343  
   344  	defaultPath := r.URL.Query().Get("defaultpath")
   345  
   346  	key, err := s.api.UploadTar(r.Context(), r.Body, GetURI(r.Context()).Path, defaultPath, mw)
   347  	if err != nil {
   348  		return nil, err
   349  	}
   350  	return key, nil
   351  }
   352  
   353  func (s *Server) handleMultipartUpload(r *http.Request, boundary string, mw *api.ManifestWriter) error {
   354  	ruid := GetRUID(r.Context())
   355  	log.Debug("handle.multipart.upload", "ruid", ruid)
   356  	mr := multipart.NewReader(r.Body, boundary)
   357  	for {
   358  		part, err := mr.NextPart()
   359  		if err == io.EOF {
   360  			return nil
   361  		} else if err != nil {
   362  			return fmt.Errorf("error reading multipart form: %s", err)
   363  		}
   364  
   365  		var size int64
   366  		var reader io.Reader
   367  		if contentLength := part.Header.Get("Content-Length"); contentLength != "" {
   368  			size, err = strconv.ParseInt(contentLength, 10, 64)
   369  			if err != nil {
   370  				return fmt.Errorf("error parsing multipart content length: %s", err)
   371  			}
   372  			reader = part
   373  		} else {
   374  //将部件复制到tmp文件以获取其大小
   375  			tmp, err := ioutil.TempFile("", "swarm-multipart")
   376  			if err != nil {
   377  				return err
   378  			}
   379  			defer os.Remove(tmp.Name())
   380  			defer tmp.Close()
   381  			size, err = io.Copy(tmp, part)
   382  			if err != nil {
   383  				return fmt.Errorf("error copying multipart content: %s", err)
   384  			}
   385  			if _, err := tmp.Seek(0, io.SeekStart); err != nil {
   386  				return fmt.Errorf("error copying multipart content: %s", err)
   387  			}
   388  			reader = tmp
   389  		}
   390  
   391  //在请求的路径下添加条目
   392  		name := part.FileName()
   393  		if name == "" {
   394  			name = part.FormName()
   395  		}
   396  		uri := GetURI(r.Context())
   397  		path := path.Join(uri.Path, name)
   398  		entry := &api.ManifestEntry{
   399  			Path:        path,
   400  			ContentType: part.Header.Get("Content-Type"),
   401  			Size:        size,
   402  		}
   403  		log.Debug("adding path to new manifest", "ruid", ruid, "bytes", entry.Size, "path", entry.Path)
   404  		contentKey, err := mw.AddEntry(r.Context(), reader, entry)
   405  		if err != nil {
   406  			return fmt.Errorf("error adding manifest entry from multipart form: %s", err)
   407  		}
   408  		log.Debug("stored content", "ruid", ruid, "key", contentKey)
   409  	}
   410  }
   411  
   412  func (s *Server) handleDirectUpload(r *http.Request, mw *api.ManifestWriter) error {
   413  	ruid := GetRUID(r.Context())
   414  	log.Debug("handle.direct.upload", "ruid", ruid)
   415  	key, err := mw.AddEntry(r.Context(), r.Body, &api.ManifestEntry{
   416  		Path:        GetURI(r.Context()).Path,
   417  		ContentType: r.Header.Get("Content-Type"),
   418  		Mode:        0644,
   419  		Size:        r.ContentLength,
   420  	})
   421  	if err != nil {
   422  		return err
   423  	}
   424  	log.Debug("stored content", "ruid", ruid, "key", key)
   425  	return nil
   426  }
   427  
   428  //handleDelete处理对bzz:/<manifest>/<path>的删除请求,删除
   429  //<path>from<manifest>并返回结果清单哈希作为
   430  //文本/纯响应
   431  func (s *Server) HandleDelete(w http.ResponseWriter, r *http.Request) {
   432  	ruid := GetRUID(r.Context())
   433  	uri := GetURI(r.Context())
   434  	log.Debug("handle.delete", "ruid", ruid)
   435  	deleteCount.Inc(1)
   436  	newKey, err := s.api.Delete(r.Context(), uri.Addr, uri.Path)
   437  	if err != nil {
   438  		deleteFail.Inc(1)
   439  		respondError(w, r, fmt.Sprintf("could not delete from manifest: %v", err), http.StatusInternalServerError)
   440  		return
   441  	}
   442  
   443  	w.Header().Set("Content-Type", "text/plain")
   444  	w.WriteHeader(http.StatusOK)
   445  	fmt.Fprint(w, newKey)
   446  }
   447  
   448  //处理源清单创建和源更新
   449  //post请求允许feeds包中定义的JSON结构:`feed.updateRequestJSON`
   450  //请求可以是a)创建源清单,b)更新源,或c)a+b:创建源清单并发布第一个更新
   451  func (s *Server) HandlePostFeed(w http.ResponseWriter, r *http.Request) {
   452  	ruid := GetRUID(r.Context())
   453  	uri := GetURI(r.Context())
   454  	log.Debug("handle.post.feed", "ruid", ruid)
   455  	var err error
   456  
   457  //创建和更新必须发送feed.updateRequestJSON JSON结构
   458  	body, err := ioutil.ReadAll(r.Body)
   459  	if err != nil {
   460  		respondError(w, r, err.Error(), http.StatusInternalServerError)
   461  		return
   462  	}
   463  
   464  	fd, err := s.api.ResolveFeed(r.Context(), uri, r.URL.Query())
   465  if err != nil { //无法分析查询字符串或检索清单
   466  		getFail.Inc(1)
   467  		httpStatus := http.StatusBadRequest
   468  		if err == api.ErrCannotLoadFeedManifest || err == api.ErrCannotResolveFeedURI {
   469  			httpStatus = http.StatusNotFound
   470  		}
   471  		respondError(w, r, fmt.Sprintf("cannot retrieve feed from manifest: %s", err), httpStatus)
   472  		return
   473  	}
   474  
   475  	var updateRequest feed.Request
   476  	updateRequest.Feed = *fd
   477  	query := r.URL.Query()
   478  
   479  if err := updateRequest.FromValues(query, body); err != nil { //解码来自查询参数的请求
   480  		respondError(w, r, err.Error(), http.StatusBadRequest)
   481  		return
   482  	}
   483  
   484  	switch {
   485  	case updateRequest.IsUpdate():
   486  //验证签名是否完整,以及签名者是否获得授权。
   487  //更新此源
   488  //请尽早检查,以避免创建源,然后无法设置其第一次更新。
   489  		if err = updateRequest.Verify(); err != nil {
   490  			respondError(w, r, err.Error(), http.StatusForbidden)
   491  			return
   492  		}
   493  		_, err = s.api.FeedsUpdate(r.Context(), &updateRequest)
   494  		if err != nil {
   495  			respondError(w, r, err.Error(), http.StatusInternalServerError)
   496  			return
   497  		}
   498  		fallthrough
   499  	case query.Get("manifest") == "1":
   500  //我们创建一个清单,以便稍后使用bzz://later检索源更新
   501  //此清单具有特殊的“源类型”清单,并保存
   502  //用于稍后检索源更新的源标识
   503  		m, err := s.api.NewFeedManifest(r.Context(), &updateRequest.Feed)
   504  		if err != nil {
   505  			respondError(w, r, fmt.Sprintf("failed to create feed manifest: %v", err), http.StatusInternalServerError)
   506  			return
   507  		}
   508  //清单的密钥将传回客户端
   509  //客户端可以通过其feed成员直接访问feed
   510  //清单键可以设置为ENS名称的冲突解决程序中的内容
   511  		outdata, err := json.Marshal(m)
   512  		if err != nil {
   513  			respondError(w, r, fmt.Sprintf("failed to create json response: %s", err), http.StatusInternalServerError)
   514  			return
   515  		}
   516  		fmt.Fprint(w, string(outdata))
   517  
   518  		w.Header().Add("Content-type", "application/json")
   519  	default:
   520  		respondError(w, r, "Missing signature in feed update request", http.StatusBadRequest)
   521  	}
   522  }
   523  
   524  //handlegetfeed检索swarm feed更新:
   525  //bzz feed://<manifest address or ens name>-获取最新的源更新,给定清单地址
   526  //-或
   527  //直接指定用户+主题(可选)、子主题名称(可选),不带清单:
   528  //BZZ进料://?user=0x…&topic=0x…&name=subtopic名称
   529  //主题默认为0x000…如果未指定。
   530  //如果未指定名称,则默认为空字符串。
   531  //因此,空名称和主题引用用户的默认提要。
   532  //
   533  //可选参数:
   534  //time=xx-在time之前获取最新更新(以epoch秒为单位)
   535  //hint.time=xx-提示查找算法在该时间前后查找更新
   536  //hint.level=xx-提示查找算法查找此频率级别附近的更新
   537  //meta=1-获取源元数据和状态信息,而不是执行源查询
   538  //注意:meta=1将在不久的将来被弃用
   539  func (s *Server) HandleGetFeed(w http.ResponseWriter, r *http.Request) {
   540  	ruid := GetRUID(r.Context())
   541  	uri := GetURI(r.Context())
   542  	log.Debug("handle.get.feed", "ruid", ruid)
   543  	var err error
   544  
   545  	fd, err := s.api.ResolveFeed(r.Context(), uri, r.URL.Query())
   546  if err != nil { //无法分析查询字符串或检索清单
   547  		getFail.Inc(1)
   548  		httpStatus := http.StatusBadRequest
   549  		if err == api.ErrCannotLoadFeedManifest || err == api.ErrCannotResolveFeedURI {
   550  			httpStatus = http.StatusNotFound
   551  		}
   552  		respondError(w, r, fmt.Sprintf("cannot retrieve feed information from manifest: %s", err), httpStatus)
   553  		return
   554  	}
   555  
   556  //确定查询是指定了期间和版本,还是指定了元数据查询
   557  	if r.URL.Query().Get("meta") == "1" {
   558  		unsignedUpdateRequest, err := s.api.FeedsNewRequest(r.Context(), fd)
   559  		if err != nil {
   560  			getFail.Inc(1)
   561  			respondError(w, r, fmt.Sprintf("cannot retrieve feed metadata for feed=%s: %s", fd.Hex(), err), http.StatusNotFound)
   562  			return
   563  		}
   564  		rawResponse, err := unsignedUpdateRequest.MarshalJSON()
   565  		if err != nil {
   566  			respondError(w, r, fmt.Sprintf("cannot encode unsigned feed update request: %v", err), http.StatusInternalServerError)
   567  			return
   568  		}
   569  		w.Header().Add("Content-type", "application/json")
   570  		w.WriteHeader(http.StatusOK)
   571  		fmt.Fprint(w, string(rawResponse))
   572  		return
   573  	}
   574  
   575  	lookupParams := &feed.Query{Feed: *fd}
   576  if err = lookupParams.FromValues(r.URL.Query()); err != nil { //分析期间,版本
   577  		respondError(w, r, fmt.Sprintf("invalid feed update request:%s", err), http.StatusBadRequest)
   578  		return
   579  	}
   580  
   581  	data, err := s.api.FeedsLookup(r.Context(), lookupParams)
   582  
   583  //switch语句中的任何错误都将在此处结束
   584  	if err != nil {
   585  		code, err2 := s.translateFeedError(w, r, "feed lookup fail", err)
   586  		respondError(w, r, err2.Error(), code)
   587  		return
   588  	}
   589  
   590  //一切正常,提供检索到的更新
   591  	log.Debug("Found update", "feed", fd.Hex(), "ruid", ruid)
   592  	w.Header().Set("Content-Type", api.MimeOctetStream)
   593  	http.ServeContent(w, r, "", time.Now(), bytes.NewReader(data))
   594  }
   595  
   596  func (s *Server) translateFeedError(w http.ResponseWriter, r *http.Request, supErr string, err error) (int, error) {
   597  	code := 0
   598  	defaultErr := fmt.Errorf("%s: %v", supErr, err)
   599  	rsrcErr, ok := err.(*feed.Error)
   600  	if !ok && rsrcErr != nil {
   601  		code = rsrcErr.Code()
   602  	}
   603  	switch code {
   604  	case storage.ErrInvalidValue:
   605  		return http.StatusBadRequest, defaultErr
   606  	case storage.ErrNotFound, storage.ErrNotSynced, storage.ErrNothingToReturn, storage.ErrInit:
   607  		return http.StatusNotFound, defaultErr
   608  	case storage.ErrUnauthorized, storage.ErrInvalidSignature:
   609  		return http.StatusUnauthorized, defaultErr
   610  	case storage.ErrDataOverflow:
   611  		return http.StatusRequestEntityTooLarge, defaultErr
   612  	}
   613  
   614  	return http.StatusInternalServerError, defaultErr
   615  }
   616  
   617  //handleget处理get请求
   618  //-bzz raw://<key>并用存储在
   619  //给定的存储密钥
   620  //-bzz hash://<key>并用存储内容的哈希进行响应
   621  //在给定的存储键上作为文本/纯响应
   622  func (s *Server) HandleGet(w http.ResponseWriter, r *http.Request) {
   623  	ruid := GetRUID(r.Context())
   624  	uri := GetURI(r.Context())
   625  	log.Debug("handle.get", "ruid", ruid, "uri", uri)
   626  	getCount.Inc(1)
   627  	_, pass, _ := r.BasicAuth()
   628  
   629  	addr, err := s.api.ResolveURI(r.Context(), uri, pass)
   630  	if err != nil {
   631  		getFail.Inc(1)
   632  		respondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
   633  		return
   634  	}
   635  w.Header().Set("Cache-Control", "max-age=2147483648, immutable") //URL的类型为bzz://<hex key>/path,因此我们确信它是不可变的。
   636  
   637  	log.Debug("handle.get: resolved", "ruid", ruid, "key", addr)
   638  
   639  //如果设置了path,则将<key>解释为清单并返回
   640  //给定路径的原始条目
   641  
   642  	etag := common.Bytes2Hex(addr)
   643  	noneMatchEtag := r.Header.Get("If-None-Match")
   644  w.Header().Set("ETag", fmt.Sprintf("%q", etag)) //将etag设置为manifest key或raw entry key。
   645  	if noneMatchEtag != "" {
   646  		if bytes.Equal(storage.Address(common.Hex2Bytes(noneMatchEtag)), addr) {
   647  			w.WriteHeader(http.StatusNotModified)
   648  			return
   649  		}
   650  	}
   651  
   652  //通过检索文件的大小检查根块是否存在
   653  	reader, isEncrypted := s.api.Retrieve(r.Context(), addr)
   654  	if _, err := reader.Size(r.Context(), nil); err != nil {
   655  		getFail.Inc(1)
   656  		respondError(w, r, fmt.Sprintf("root chunk not found %s: %s", addr, err), http.StatusNotFound)
   657  		return
   658  	}
   659  
   660  	w.Header().Set("X-Decrypted", fmt.Sprintf("%v", isEncrypted))
   661  
   662  	switch {
   663  	case uri.Raw():
   664  //允许请求使用查询覆盖内容类型
   665  //参数
   666  		if typ := r.URL.Query().Get("content_type"); typ != "" {
   667  			w.Header().Set("Content-Type", typ)
   668  		}
   669  		http.ServeContent(w, r, "", time.Now(), reader)
   670  	case uri.Hash():
   671  		w.Header().Set("Content-Type", "text/plain")
   672  		w.WriteHeader(http.StatusOK)
   673  		fmt.Fprint(w, addr)
   674  	}
   675  }
   676  
   677  //handleGetList处理对bzz list的get请求:/<manifest>/<path>并返回
   678  //<manifest>中<path>下包含的所有文件的列表,分组为
   679  //使用“/”作为分隔符的常见前缀
   680  func (s *Server) HandleGetList(w http.ResponseWriter, r *http.Request) {
   681  	ruid := GetRUID(r.Context())
   682  	uri := GetURI(r.Context())
   683  	_, credentials, _ := r.BasicAuth()
   684  	log.Debug("handle.get.list", "ruid", ruid, "uri", uri)
   685  	getListCount.Inc(1)
   686  
   687  //确保根路径有一个尾随斜杠,以便相对URL工作
   688  	if uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
   689  		http.Redirect(w, r, r.URL.Path+"/", http.StatusMovedPermanently)
   690  		return
   691  	}
   692  
   693  	addr, err := s.api.Resolve(r.Context(), uri.Addr)
   694  	if err != nil {
   695  		getListFail.Inc(1)
   696  		respondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
   697  		return
   698  	}
   699  	log.Debug("handle.get.list: resolved", "ruid", ruid, "key", addr)
   700  
   701  	list, err := s.api.GetManifestList(r.Context(), s.api.Decryptor(r.Context(), credentials), addr, uri.Path)
   702  	if err != nil {
   703  		getListFail.Inc(1)
   704  		if isDecryptError(err) {
   705  			w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", addr.String()))
   706  			respondError(w, r, err.Error(), http.StatusUnauthorized)
   707  			return
   708  		}
   709  		respondError(w, r, err.Error(), http.StatusInternalServerError)
   710  		return
   711  	}
   712  
   713  //如果客户端需要HTML(例如浏览器),则将列表呈现为
   714  //带相对URL的HTML索引
   715  	if strings.Contains(r.Header.Get("Accept"), "text/html") {
   716  		w.Header().Set("Content-Type", "text/html")
   717  		err := TemplatesMap["bzz-list"].Execute(w, &htmlListData{
   718  			URI: &api.URI{
   719  				Scheme: "bzz",
   720  				Addr:   uri.Addr,
   721  				Path:   uri.Path,
   722  			},
   723  			List: &list,
   724  		})
   725  		if err != nil {
   726  			getListFail.Inc(1)
   727  			log.Error(fmt.Sprintf("error rendering list HTML: %s", err))
   728  		}
   729  		return
   730  	}
   731  
   732  	w.Header().Set("Content-Type", "application/json")
   733  	json.NewEncoder(w).Encode(&list)
   734  }
   735  
   736  //handlegetfile处理对bzz://<manifest>><path>的get请求并响应
   737  //文件内容位于给定的<manifest>
   738  func (s *Server) HandleGetFile(w http.ResponseWriter, r *http.Request) {
   739  	ruid := GetRUID(r.Context())
   740  	uri := GetURI(r.Context())
   741  	_, credentials, _ := r.BasicAuth()
   742  	log.Debug("handle.get.file", "ruid", ruid, "uri", r.RequestURI)
   743  	getFileCount.Inc(1)
   744  
   745  //确保根路径有一个尾随斜杠,以便相对URL工作
   746  	if uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
   747  		http.Redirect(w, r, r.URL.Path+"/", http.StatusMovedPermanently)
   748  		return
   749  	}
   750  	var err error
   751  	manifestAddr := uri.Address()
   752  
   753  	if manifestAddr == nil {
   754  		manifestAddr, err = s.api.Resolve(r.Context(), uri.Addr)
   755  		if err != nil {
   756  			getFileFail.Inc(1)
   757  			respondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
   758  			return
   759  		}
   760  	} else {
   761  w.Header().Set("Cache-Control", "max-age=2147483648, immutable") //URL的类型为bzz://<hex key>/path,因此我们确信它是不可变的。
   762  	}
   763  
   764  	log.Debug("handle.get.file: resolved", "ruid", ruid, "key", manifestAddr)
   765  
   766  	reader, contentType, status, contentKey, err := s.api.Get(r.Context(), s.api.Decryptor(r.Context(), credentials), manifestAddr, uri.Path)
   767  
   768  	etag := common.Bytes2Hex(contentKey)
   769  	noneMatchEtag := r.Header.Get("If-None-Match")
   770  w.Header().Set("ETag", fmt.Sprintf("%q", etag)) //将etag设置为实际内容键。
   771  	if noneMatchEtag != "" {
   772  		if bytes.Equal(storage.Address(common.Hex2Bytes(noneMatchEtag)), contentKey) {
   773  			w.WriteHeader(http.StatusNotModified)
   774  			return
   775  		}
   776  	}
   777  
   778  	if err != nil {
   779  		if isDecryptError(err) {
   780  			w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", manifestAddr))
   781  			respondError(w, r, err.Error(), http.StatusUnauthorized)
   782  			return
   783  		}
   784  
   785  		switch status {
   786  		case http.StatusNotFound:
   787  			getFileNotFound.Inc(1)
   788  			respondError(w, r, err.Error(), http.StatusNotFound)
   789  		default:
   790  			getFileFail.Inc(1)
   791  			respondError(w, r, err.Error(), http.StatusInternalServerError)
   792  		}
   793  		return
   794  	}
   795  
   796  //请求导致文件不明确
   797  //例如,清单中提供/readme.md和readinglist.txt。
   798  	if status == http.StatusMultipleChoices {
   799  		list, err := s.api.GetManifestList(r.Context(), s.api.Decryptor(r.Context(), credentials), manifestAddr, uri.Path)
   800  		if err != nil {
   801  			getFileFail.Inc(1)
   802  			if isDecryptError(err) {
   803  				w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", manifestAddr))
   804  				respondError(w, r, err.Error(), http.StatusUnauthorized)
   805  				return
   806  			}
   807  			respondError(w, r, err.Error(), http.StatusInternalServerError)
   808  			return
   809  		}
   810  
   811  		log.Debug(fmt.Sprintf("Multiple choices! --> %v", list), "ruid", ruid)
   812  //显示指向可用条目的良好页面链接
   813  		ShowMultipleChoices(w, r, list)
   814  		return
   815  	}
   816  
   817  //通过检索文件的大小检查根块是否存在
   818  	if _, err := reader.Size(r.Context(), nil); err != nil {
   819  		getFileNotFound.Inc(1)
   820  		respondError(w, r, fmt.Sprintf("file not found %s: %s", uri, err), http.StatusNotFound)
   821  		return
   822  	}
   823  
   824  	if contentType != "" {
   825  		w.Header().Set("Content-Type", contentType)
   826  	}
   827  
   828  	fileName := uri.Addr
   829  	if found := path.Base(uri.Path); found != "" && found != "." && found != "/" {
   830  		fileName = found
   831  	}
   832  	w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s\"", fileName))
   833  
   834  	http.ServeContent(w, r, fileName, time.Now(), newBufferedReadSeeker(reader, getFileBufferSize))
   835  }
   836  
   837  //在Lazychunkreader上用于bufio.reader的缓冲区大小传递给
   838  //handlegetfile中的http.serveContent。
   839  //警告:此值影响块请求和chunker join goroutine的数量
   840  //按文件请求。
   841  //建议值是IO的4倍。复制默认缓冲区值32KB。
   842  const getFileBufferSize = 4 * 32 * 1024
   843  
   844  //BufferedReadSeeker将Bufio.Reader包装为公开Seek方法
   845  //来自NewBufferedReadSeeker中提供的IO.readSeeker。
   846  type bufferedReadSeeker struct {
   847  	r io.Reader
   848  	s io.Seeker
   849  }
   850  
   851  //NewBufferedReadSeeker创建BufferedReadSeeker的新实例,
   852  //输出IO.readseeker。参数“size”是读取缓冲区的大小。
   853  func newBufferedReadSeeker(readSeeker io.ReadSeeker, size int) bufferedReadSeeker {
   854  	return bufferedReadSeeker{
   855  		r: bufio.NewReaderSize(readSeeker, size),
   856  		s: readSeeker,
   857  	}
   858  }
   859  
   860  func (b bufferedReadSeeker) Read(p []byte) (n int, err error) {
   861  	return b.r.Read(p)
   862  }
   863  
   864  func (b bufferedReadSeeker) Seek(offset int64, whence int) (int64, error) {
   865  	return b.s.Seek(offset, whence)
   866  }
   867  
   868  type loggingResponseWriter struct {
   869  	http.ResponseWriter
   870  	statusCode int
   871  }
   872  
   873  func newLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter {
   874  	return &loggingResponseWriter{w, http.StatusOK}
   875  }
   876  
   877  func (lrw *loggingResponseWriter) WriteHeader(code int) {
   878  	lrw.statusCode = code
   879  	lrw.ResponseWriter.WriteHeader(code)
   880  }
   881  
   882  func isDecryptError(err error) bool {
   883  	return strings.Contains(err.Error(), api.ErrDecrypt.Error())
   884  }
   885