github.com/yinchengtsinghua/golang-Eos-dpos-Ethereum@v0.0.0-20190121132951-92cc4225ed8e/swarm/api/http/server.go (about)

     1  
     2  //此源码被清华学神尹成大魔王专业翻译分析并修改
     3  //尹成QQ77025077
     4  //尹成微信18510341407
     5  //尹成所在QQ群721929980
     6  //尹成邮箱 yinc13@mails.tsinghua.edu.cn
     7  //尹成毕业于清华大学,微软区块链领域全球最有价值专家
     8  //https://mvp.microsoft.com/zh-cn/PublicProfile/4033620
     9  //
    10  //
    11  //
    12  //
    13  //
    14  //
    15  //
    16  //
    17  //
    18  //
    19  //
    20  //
    21  //
    22  //
    23  //
    24  
    25  /*
    26  
    27  */
    28  
    29  package http
    30  
    31  import (
    32  	"bufio"
    33  	"bytes"
    34  	"encoding/json"
    35  	"fmt"
    36  	"io"
    37  	"io/ioutil"
    38  	"mime"
    39  	"mime/multipart"
    40  	"net/http"
    41  	"os"
    42  	"path"
    43  	"regexp"
    44  	"strconv"
    45  	"strings"
    46  	"time"
    47  
    48  	"github.com/ethereum/go-ethereum/common"
    49  	"github.com/ethereum/go-ethereum/metrics"
    50  	"github.com/ethereum/go-ethereum/swarm/api"
    51  	"github.com/ethereum/go-ethereum/swarm/log"
    52  	"github.com/ethereum/go-ethereum/swarm/storage"
    53  	"github.com/ethereum/go-ethereum/swarm/storage/mru"
    54  
    55  	"github.com/rs/cors"
    56  )
    57  
    58  type resourceResponse struct {
    59  	Manifest storage.Address `json:"manifest"`
    60  	Resource string          `json:"resource"`
    61  	Update   storage.Address `json:"update"`
    62  }
    63  
    64  var (
    65  	postRawCount    = metrics.NewRegisteredCounter("api.http.post.raw.count", nil)
    66  	postRawFail     = metrics.NewRegisteredCounter("api.http.post.raw.fail", nil)
    67  	postFilesCount  = metrics.NewRegisteredCounter("api.http.post.files.count", nil)
    68  	postFilesFail   = metrics.NewRegisteredCounter("api.http.post.files.fail", nil)
    69  	deleteCount     = metrics.NewRegisteredCounter("api.http.delete.count", nil)
    70  	deleteFail      = metrics.NewRegisteredCounter("api.http.delete.fail", nil)
    71  	getCount        = metrics.NewRegisteredCounter("api.http.get.count", nil)
    72  	getFail         = metrics.NewRegisteredCounter("api.http.get.fail", nil)
    73  	getFileCount    = metrics.NewRegisteredCounter("api.http.get.file.count", nil)
    74  	getFileNotFound = metrics.NewRegisteredCounter("api.http.get.file.notfound", nil)
    75  	getFileFail     = metrics.NewRegisteredCounter("api.http.get.file.fail", nil)
    76  	getListCount    = metrics.NewRegisteredCounter("api.http.get.list.count", nil)
    77  	getListFail     = metrics.NewRegisteredCounter("api.http.get.list.fail", nil)
    78  )
    79  
    80  type methodHandler map[string]http.Handler
    81  
    82  func (m methodHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
    83  	v, ok := m[r.Method]
    84  	if ok {
    85  		v.ServeHTTP(rw, r)
    86  		return
    87  	}
    88  	rw.WriteHeader(http.StatusMethodNotAllowed)
    89  }
    90  
    91  func NewServer(api *api.API, corsString string) *Server {
    92  	var allowedOrigins []string
    93  	for _, domain := range strings.Split(corsString, ",") {
    94  		allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain))
    95  	}
    96  	c := cors.New(cors.Options{
    97  		AllowedOrigins: allowedOrigins,
    98  		AllowedMethods: []string{http.MethodPost, http.MethodGet, http.MethodDelete, http.MethodPatch, http.MethodPut},
    99  		MaxAge:         600,
   100  		AllowedHeaders: []string{"*"},
   101  	})
   102  
   103  	server := &Server{api: api}
   104  
   105  	defaultMiddlewares := []Adapter{
   106  		RecoverPanic,
   107  		SetRequestID,
   108  		SetRequestHost,
   109  		InitLoggingResponseWriter,
   110  		ParseURI,
   111  		InstrumentOpenTracing,
   112  	}
   113  
   114  	mux := http.NewServeMux()
   115  	mux.Handle("/bzz:/", methodHandler{
   116  		"GET": Adapt(
   117  			http.HandlerFunc(server.HandleBzzGet),
   118  			defaultMiddlewares...,
   119  		),
   120  		"POST": Adapt(
   121  			http.HandlerFunc(server.HandlePostFiles),
   122  			defaultMiddlewares...,
   123  		),
   124  		"DELETE": Adapt(
   125  			http.HandlerFunc(server.HandleDelete),
   126  			defaultMiddlewares...,
   127  		),
   128  	})
   129  	mux.Handle("/bzz-raw:/", methodHandler{
   130  		"GET": Adapt(
   131  			http.HandlerFunc(server.HandleGet),
   132  			defaultMiddlewares...,
   133  		),
   134  		"POST": Adapt(
   135  			http.HandlerFunc(server.HandlePostRaw),
   136  			defaultMiddlewares...,
   137  		),
   138  	})
   139  	mux.Handle("/bzz-immutable:/", methodHandler{
   140  		"GET": Adapt(
   141  			http.HandlerFunc(server.HandleGet),
   142  			defaultMiddlewares...,
   143  		),
   144  	})
   145  	mux.Handle("/bzz-hash:/", methodHandler{
   146  		"GET": Adapt(
   147  			http.HandlerFunc(server.HandleGet),
   148  			defaultMiddlewares...,
   149  		),
   150  	})
   151  	mux.Handle("/bzz-list:/", methodHandler{
   152  		"GET": Adapt(
   153  			http.HandlerFunc(server.HandleGetList),
   154  			defaultMiddlewares...,
   155  		),
   156  	})
   157  	mux.Handle("/bzz-resource:/", methodHandler{
   158  		"GET": Adapt(
   159  			http.HandlerFunc(server.HandleGetResource),
   160  			defaultMiddlewares...,
   161  		),
   162  		"POST": Adapt(
   163  			http.HandlerFunc(server.HandlePostResource),
   164  			defaultMiddlewares...,
   165  		),
   166  	})
   167  
   168  	mux.Handle("/", methodHandler{
   169  		"GET": Adapt(
   170  			http.HandlerFunc(server.HandleRootPaths),
   171  			SetRequestID,
   172  			InitLoggingResponseWriter,
   173  		),
   174  	})
   175  	server.Handler = c.Handler(mux)
   176  
   177  	return server
   178  }
   179  
   180  func (s *Server) ListenAndServe(addr string) error {
   181  	s.listenAddr = addr
   182  	return http.ListenAndServe(addr, s)
   183  }
   184  
   185  //
   186  //
   187  //
   188  //
   189  type Server struct {
   190  	http.Handler
   191  	api        *api.API
   192  	listenAddr string
   193  }
   194  
   195  func (s *Server) HandleBzzGet(w http.ResponseWriter, r *http.Request) {
   196  	log.Debug("handleBzzGet", "ruid", GetRUID(r.Context()), "uri", r.RequestURI)
   197  	if r.Header.Get("Accept") == "application/x-tar" {
   198  		uri := GetURI(r.Context())
   199  		_, credentials, _ := r.BasicAuth()
   200  		reader, err := s.api.GetDirectoryTar(r.Context(), s.api.Decryptor(r.Context(), credentials), uri)
   201  		if err != nil {
   202  			if isDecryptError(err) {
   203  				w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", uri.Address().String()))
   204  				RespondError(w, r, err.Error(), http.StatusUnauthorized)
   205  				return
   206  			}
   207  			RespondError(w, r, fmt.Sprintf("Had an error building the tarball: %v", err), http.StatusInternalServerError)
   208  			return
   209  		}
   210  		defer reader.Close()
   211  
   212  		w.Header().Set("Content-Type", "application/x-tar")
   213  		w.WriteHeader(http.StatusOK)
   214  		io.Copy(w, reader)
   215  		return
   216  	}
   217  
   218  	s.HandleGetFile(w, r)
   219  }
   220  
   221  func (s *Server) HandleRootPaths(w http.ResponseWriter, r *http.Request) {
   222  	switch r.RequestURI {
   223  	case "/":
   224  		RespondTemplate(w, r, "landing-page", "Swarm: Please request a valid ENS or swarm hash with the appropriate bzz scheme", 200)
   225  		return
   226  	case "/robots.txt":
   227  		w.Header().Set("Last-Modified", time.Now().Format(http.TimeFormat))
   228  		fmt.Fprintf(w, "User-agent: *\nDisallow: /")
   229  	case "/favicon.ico":
   230  		w.WriteHeader(http.StatusOK)
   231  		w.Write(faviconBytes)
   232  	default:
   233  		RespondError(w, r, "Not Found", http.StatusNotFound)
   234  	}
   235  }
   236  
   237  //
   238  //
   239  func (s *Server) HandlePostRaw(w http.ResponseWriter, r *http.Request) {
   240  	ruid := GetRUID(r.Context())
   241  	log.Debug("handle.post.raw", "ruid", ruid)
   242  
   243  	postRawCount.Inc(1)
   244  
   245  	toEncrypt := false
   246  	uri := GetURI(r.Context())
   247  	if uri.Addr == "encrypt" {
   248  		toEncrypt = true
   249  	}
   250  
   251  	if uri.Path != "" {
   252  		postRawFail.Inc(1)
   253  		RespondError(w, r, "raw POST request cannot contain a path", http.StatusBadRequest)
   254  		return
   255  	}
   256  
   257  	if uri.Addr != "" && uri.Addr != "encrypt" {
   258  		postRawFail.Inc(1)
   259  		RespondError(w, r, "raw POST request addr can only be empty or \"encrypt\"", http.StatusBadRequest)
   260  		return
   261  	}
   262  
   263  	if r.Header.Get("Content-Length") == "" {
   264  		postRawFail.Inc(1)
   265  		RespondError(w, r, "missing Content-Length header in request", http.StatusBadRequest)
   266  		return
   267  	}
   268  
   269  	addr, _, err := s.api.Store(r.Context(), r.Body, r.ContentLength, toEncrypt)
   270  	if err != nil {
   271  		postRawFail.Inc(1)
   272  		RespondError(w, r, err.Error(), http.StatusInternalServerError)
   273  		return
   274  	}
   275  
   276  	log.Debug("stored content", "ruid", ruid, "key", addr)
   277  
   278  	w.Header().Set("Content-Type", "text/plain")
   279  	w.WriteHeader(http.StatusOK)
   280  	fmt.Fprint(w, addr)
   281  }
   282  
   283  //
   284  //
   285  //
   286  //
   287  //
   288  func (s *Server) HandlePostFiles(w http.ResponseWriter, r *http.Request) {
   289  	ruid := GetRUID(r.Context())
   290  	log.Debug("handle.post.files", "ruid", ruid)
   291  	postFilesCount.Inc(1)
   292  
   293  	contentType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
   294  	if err != nil {
   295  		postFilesFail.Inc(1)
   296  		RespondError(w, r, err.Error(), http.StatusBadRequest)
   297  		return
   298  	}
   299  
   300  	toEncrypt := false
   301  	uri := GetURI(r.Context())
   302  	if uri.Addr == "encrypt" {
   303  		toEncrypt = true
   304  	}
   305  
   306  	var addr storage.Address
   307  	if uri.Addr != "" && uri.Addr != "encrypt" {
   308  		addr, err = s.api.Resolve(r.Context(), uri.Addr)
   309  		if err != nil {
   310  			postFilesFail.Inc(1)
   311  			RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusInternalServerError)
   312  			return
   313  		}
   314  		log.Debug("resolved key", "ruid", ruid, "key", addr)
   315  	} else {
   316  		addr, err = s.api.NewManifest(r.Context(), toEncrypt)
   317  		if err != nil {
   318  			postFilesFail.Inc(1)
   319  			RespondError(w, r, err.Error(), http.StatusInternalServerError)
   320  			return
   321  		}
   322  		log.Debug("new manifest", "ruid", ruid, "key", addr)
   323  	}
   324  
   325  	newAddr, err := s.api.UpdateManifest(r.Context(), addr, func(mw *api.ManifestWriter) error {
   326  		switch contentType {
   327  		case "application/x-tar":
   328  			_, err := s.handleTarUpload(r, mw)
   329  			if err != nil {
   330  				RespondError(w, r, fmt.Sprintf("error uploading tarball: %v", err), http.StatusInternalServerError)
   331  				return err
   332  			}
   333  			return nil
   334  		case "multipart/form-data":
   335  			return s.handleMultipartUpload(r, params["boundary"], mw)
   336  
   337  		default:
   338  			return s.handleDirectUpload(r, mw)
   339  		}
   340  	})
   341  	if err != nil {
   342  		postFilesFail.Inc(1)
   343  		RespondError(w, r, fmt.Sprintf("cannot create manifest: %s", err), http.StatusInternalServerError)
   344  		return
   345  	}
   346  
   347  	log.Debug("stored content", "ruid", ruid, "key", newAddr)
   348  
   349  	w.Header().Set("Content-Type", "text/plain")
   350  	w.WriteHeader(http.StatusOK)
   351  	fmt.Fprint(w, newAddr)
   352  }
   353  
   354  func (s *Server) handleTarUpload(r *http.Request, mw *api.ManifestWriter) (storage.Address, error) {
   355  	log.Debug("handle.tar.upload", "ruid", GetRUID(r.Context()))
   356  
   357  	defaultPath := r.URL.Query().Get("defaultpath")
   358  
   359  	key, err := s.api.UploadTar(r.Context(), r.Body, GetURI(r.Context()).Path, defaultPath, mw)
   360  	if err != nil {
   361  		return nil, err
   362  	}
   363  	return key, nil
   364  }
   365  
   366  func (s *Server) handleMultipartUpload(r *http.Request, boundary string, mw *api.ManifestWriter) error {
   367  	ruid := GetRUID(r.Context())
   368  	log.Debug("handle.multipart.upload", "ruid", ruid)
   369  	mr := multipart.NewReader(r.Body, boundary)
   370  	for {
   371  		part, err := mr.NextPart()
   372  		if err == io.EOF {
   373  			return nil
   374  		} else if err != nil {
   375  			return fmt.Errorf("error reading multipart form: %s", err)
   376  		}
   377  
   378  		var size int64
   379  		var reader io.Reader = part
   380  		if contentLength := part.Header.Get("Content-Length"); contentLength != "" {
   381  			size, err = strconv.ParseInt(contentLength, 10, 64)
   382  			if err != nil {
   383  				return fmt.Errorf("error parsing multipart content length: %s", err)
   384  			}
   385  			reader = part
   386  		} else {
   387  //
   388  			tmp, err := ioutil.TempFile("", "swarm-multipart")
   389  			if err != nil {
   390  				return err
   391  			}
   392  			defer os.Remove(tmp.Name())
   393  			defer tmp.Close()
   394  			size, err = io.Copy(tmp, part)
   395  			if err != nil {
   396  				return fmt.Errorf("error copying multipart content: %s", err)
   397  			}
   398  			if _, err := tmp.Seek(0, io.SeekStart); err != nil {
   399  				return fmt.Errorf("error copying multipart content: %s", err)
   400  			}
   401  			reader = tmp
   402  		}
   403  
   404  //
   405  		name := part.FileName()
   406  		if name == "" {
   407  			name = part.FormName()
   408  		}
   409  		uri := GetURI(r.Context())
   410  		path := path.Join(uri.Path, name)
   411  		entry := &api.ManifestEntry{
   412  			Path:        path,
   413  			ContentType: part.Header.Get("Content-Type"),
   414  			Size:        size,
   415  			ModTime:     time.Now(),
   416  		}
   417  		log.Debug("adding path to new manifest", "ruid", ruid, "bytes", entry.Size, "path", entry.Path)
   418  		contentKey, err := mw.AddEntry(r.Context(), reader, entry)
   419  		if err != nil {
   420  			return fmt.Errorf("error adding manifest entry from multipart form: %s", err)
   421  		}
   422  		log.Debug("stored content", "ruid", ruid, "key", contentKey)
   423  	}
   424  }
   425  
   426  func (s *Server) handleDirectUpload(r *http.Request, mw *api.ManifestWriter) error {
   427  	ruid := GetRUID(r.Context())
   428  	log.Debug("handle.direct.upload", "ruid", ruid)
   429  	key, err := mw.AddEntry(r.Context(), r.Body, &api.ManifestEntry{
   430  		Path:        GetURI(r.Context()).Path,
   431  		ContentType: r.Header.Get("Content-Type"),
   432  		Mode:        0644,
   433  		Size:        r.ContentLength,
   434  		ModTime:     time.Now(),
   435  	})
   436  	if err != nil {
   437  		return err
   438  	}
   439  	log.Debug("stored content", "ruid", ruid, "key", key)
   440  	return nil
   441  }
   442  
   443  //
   444  //
   445  //
   446  func (s *Server) HandleDelete(w http.ResponseWriter, r *http.Request) {
   447  	ruid := GetRUID(r.Context())
   448  	uri := GetURI(r.Context())
   449  	log.Debug("handle.delete", "ruid", ruid)
   450  	deleteCount.Inc(1)
   451  	newKey, err := s.api.Delete(r.Context(), uri.Addr, uri.Path)
   452  	if err != nil {
   453  		deleteFail.Inc(1)
   454  		RespondError(w, r, fmt.Sprintf("could not delete from manifest: %v", err), http.StatusInternalServerError)
   455  		return
   456  	}
   457  
   458  	w.Header().Set("Content-Type", "text/plain")
   459  	w.WriteHeader(http.StatusOK)
   460  	fmt.Fprint(w, newKey)
   461  }
   462  
   463  //
   464  //
   465  //
   466  //
   467  //
   468  //
   469  func resourcePostMode(path string) (isRaw bool, frequency uint64, err error) {
   470  	re, err := regexp.Compile("^(raw)?/?([0-9]+)?$")
   471  	if err != nil {
   472  		return isRaw, frequency, err
   473  	}
   474  	m := re.FindAllStringSubmatch(path, 2)
   475  	var freqstr = "0"
   476  	if len(m) > 0 {
   477  		if m[0][1] != "" {
   478  			isRaw = true
   479  		}
   480  		if m[0][2] != "" {
   481  			freqstr = m[0][2]
   482  		}
   483  	} else if len(path) > 0 {
   484  		return isRaw, frequency, fmt.Errorf("invalid path")
   485  	}
   486  	frequency, err = strconv.ParseUint(freqstr, 10, 64)
   487  	return isRaw, frequency, err
   488  }
   489  
   490  //
   491  //
   492  //
   493  //
   494  //
   495  //
   496  //
   497  func (s *Server) HandlePostResource(w http.ResponseWriter, r *http.Request) {
   498  	ruid := GetRUID(r.Context())
   499  	log.Debug("handle.post.resource", "ruid", ruid)
   500  	var err error
   501  
   502  //
   503  	body, err := ioutil.ReadAll(r.Body)
   504  	if err != nil {
   505  		RespondError(w, r, err.Error(), http.StatusInternalServerError)
   506  		return
   507  	}
   508  	var updateRequest mru.Request
   509  if err := updateRequest.UnmarshalJSON(body); err != nil { //
   510  RespondError(w, r, err.Error(), http.StatusBadRequest) //
   511  		return
   512  	}
   513  
   514  	if updateRequest.IsUpdate() {
   515  //
   516  //
   517  //
   518  		if err = updateRequest.Verify(); err != nil {
   519  			RespondError(w, r, err.Error(), http.StatusForbidden)
   520  			return
   521  		}
   522  	}
   523  
   524  	if updateRequest.IsNew() {
   525  		err = s.api.ResourceCreate(r.Context(), &updateRequest)
   526  		if err != nil {
   527  			code, err2 := s.translateResourceError(w, r, "resource creation fail", err)
   528  			RespondError(w, r, err2.Error(), code)
   529  			return
   530  		}
   531  	}
   532  
   533  	if updateRequest.IsUpdate() {
   534  		_, err = s.api.ResourceUpdate(r.Context(), &updateRequest.SignedResourceUpdate)
   535  		if err != nil {
   536  			RespondError(w, r, err.Error(), http.StatusInternalServerError)
   537  			return
   538  		}
   539  	}
   540  
   541  //
   542  //
   543  
   544  	if updateRequest.IsNew() {
   545  //
   546  //
   547  //
   548  		m, err := s.api.NewResourceManifest(r.Context(), updateRequest.RootAddr().Hex())
   549  		if err != nil {
   550  			RespondError(w, r, fmt.Sprintf("failed to create resource manifest: %v", err), http.StatusInternalServerError)
   551  			return
   552  		}
   553  
   554  //
   555  //
   556  //
   557  //
   558  		outdata, err := json.Marshal(m)
   559  		if err != nil {
   560  			RespondError(w, r, fmt.Sprintf("failed to create json response: %s", err), http.StatusInternalServerError)
   561  			return
   562  		}
   563  		fmt.Fprint(w, string(outdata))
   564  	}
   565  	w.Header().Add("Content-type", "application/json")
   566  }
   567  
   568  //
   569  //
   570  //
   571  //
   572  //
   573  //
   574  //
   575  func (s *Server) HandleGetResource(w http.ResponseWriter, r *http.Request) {
   576  	ruid := GetRUID(r.Context())
   577  	uri := GetURI(r.Context())
   578  	log.Debug("handle.get.resource", "ruid", ruid)
   579  	var err error
   580  
   581  //
   582  	manifestAddr := uri.Address()
   583  	if manifestAddr == nil {
   584  		manifestAddr, err = s.api.Resolve(r.Context(), uri.Addr)
   585  		if err != nil {
   586  			getFail.Inc(1)
   587  			RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
   588  			return
   589  		}
   590  	} else {
   591  		w.Header().Set("Cache-Control", "max-age=2147483648")
   592  	}
   593  
   594  //
   595  	rootAddr, err := s.api.ResolveResourceManifest(r.Context(), manifestAddr)
   596  	if err != nil {
   597  		getFail.Inc(1)
   598  		RespondError(w, r, fmt.Sprintf("error resolving resource root chunk for %s: %s", uri.Addr, err), http.StatusNotFound)
   599  		return
   600  	}
   601  
   602  	log.Debug("handle.get.resource: resolved", "ruid", ruid, "manifestkey", manifestAddr, "rootchunk addr", rootAddr)
   603  
   604  //
   605  	var params []string
   606  	if len(uri.Path) > 0 {
   607  		if uri.Path == "meta" {
   608  			unsignedUpdateRequest, err := s.api.ResourceNewRequest(r.Context(), rootAddr)
   609  			if err != nil {
   610  				getFail.Inc(1)
   611  				RespondError(w, r, fmt.Sprintf("cannot retrieve resource metadata for rootAddr=%s: %s", rootAddr.Hex(), err), http.StatusNotFound)
   612  				return
   613  			}
   614  			rawResponse, err := unsignedUpdateRequest.MarshalJSON()
   615  			if err != nil {
   616  				RespondError(w, r, fmt.Sprintf("cannot encode unsigned UpdateRequest: %v", err), http.StatusInternalServerError)
   617  				return
   618  			}
   619  			w.Header().Add("Content-type", "application/json")
   620  			w.WriteHeader(http.StatusOK)
   621  			fmt.Fprint(w, string(rawResponse))
   622  			return
   623  
   624  		}
   625  
   626  		params = strings.Split(uri.Path, "/")
   627  
   628  	}
   629  	var name string
   630  	var data []byte
   631  	now := time.Now()
   632  
   633  	switch len(params) {
   634  case 0: //
   635  		name, data, err = s.api.ResourceLookup(r.Context(), mru.LookupLatest(rootAddr))
   636  case 2: //
   637  		var version uint64
   638  		var period uint64
   639  		version, err = strconv.ParseUint(params[1], 10, 32)
   640  		if err != nil {
   641  			break
   642  		}
   643  		period, err = strconv.ParseUint(params[0], 10, 32)
   644  		if err != nil {
   645  			break
   646  		}
   647  		name, data, err = s.api.ResourceLookup(r.Context(), mru.LookupVersion(rootAddr, uint32(period), uint32(version)))
   648  case 1: //
   649  		var period uint64
   650  		period, err = strconv.ParseUint(params[0], 10, 32)
   651  		if err != nil {
   652  			break
   653  		}
   654  		name, data, err = s.api.ResourceLookup(r.Context(), mru.LookupLatestVersionInPeriod(rootAddr, uint32(period)))
   655  default: //
   656  		err = mru.NewError(storage.ErrInvalidValue, "invalid mutable resource request")
   657  	}
   658  
   659  //
   660  	if err != nil {
   661  		code, err2 := s.translateResourceError(w, r, "mutable resource lookup fail", err)
   662  		RespondError(w, r, err2.Error(), code)
   663  		return
   664  	}
   665  
   666  //
   667  	log.Debug("Found update", "name", name, "ruid", ruid)
   668  	w.Header().Set("Content-Type", "application/octet-stream")
   669  	http.ServeContent(w, r, "", now, bytes.NewReader(data))
   670  }
   671  
   672  func (s *Server) translateResourceError(w http.ResponseWriter, r *http.Request, supErr string, err error) (int, error) {
   673  	code := 0
   674  	defaultErr := fmt.Errorf("%s: %v", supErr, err)
   675  	rsrcErr, ok := err.(*mru.Error)
   676  	if !ok && rsrcErr != nil {
   677  		code = rsrcErr.Code()
   678  	}
   679  	switch code {
   680  	case storage.ErrInvalidValue:
   681  		return http.StatusBadRequest, defaultErr
   682  	case storage.ErrNotFound, storage.ErrNotSynced, storage.ErrNothingToReturn, storage.ErrInit:
   683  		return http.StatusNotFound, defaultErr
   684  	case storage.ErrUnauthorized, storage.ErrInvalidSignature:
   685  		return http.StatusUnauthorized, defaultErr
   686  	case storage.ErrDataOverflow:
   687  		return http.StatusRequestEntityTooLarge, defaultErr
   688  	}
   689  
   690  	return http.StatusInternalServerError, defaultErr
   691  }
   692  
   693  //
   694  //
   695  //
   696  //
   697  //
   698  func (s *Server) HandleGet(w http.ResponseWriter, r *http.Request) {
   699  	ruid := GetRUID(r.Context())
   700  	uri := GetURI(r.Context())
   701  	log.Debug("handle.get", "ruid", ruid, "uri", uri)
   702  	getCount.Inc(1)
   703  	_, pass, _ := r.BasicAuth()
   704  
   705  	addr, err := s.api.ResolveURI(r.Context(), uri, pass)
   706  	if err != nil {
   707  		getFail.Inc(1)
   708  		RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
   709  		return
   710  	}
   711  w.Header().Set("Cache-Control", "max-age=2147483648, immutable") //
   712  
   713  	log.Debug("handle.get: resolved", "ruid", ruid, "key", addr)
   714  
   715  //
   716  //
   717  
   718  	etag := common.Bytes2Hex(addr)
   719  	noneMatchEtag := r.Header.Get("If-None-Match")
   720  w.Header().Set("ETag", fmt.Sprintf("%q", etag)) //
   721  	if noneMatchEtag != "" {
   722  		if bytes.Equal(storage.Address(common.Hex2Bytes(noneMatchEtag)), addr) {
   723  			w.WriteHeader(http.StatusNotModified)
   724  			return
   725  		}
   726  	}
   727  
   728  //
   729  	reader, isEncrypted := s.api.Retrieve(r.Context(), addr)
   730  	if _, err := reader.Size(r.Context(), nil); err != nil {
   731  		getFail.Inc(1)
   732  		RespondError(w, r, fmt.Sprintf("root chunk not found %s: %s", addr, err), http.StatusNotFound)
   733  		return
   734  	}
   735  
   736  	w.Header().Set("X-Decrypted", fmt.Sprintf("%v", isEncrypted))
   737  
   738  	switch {
   739  	case uri.Raw():
   740  //
   741  //
   742  		contentType := "application/octet-stream"
   743  		if typ := r.URL.Query().Get("content_type"); typ != "" {
   744  			contentType = typ
   745  		}
   746  		w.Header().Set("Content-Type", contentType)
   747  		http.ServeContent(w, r, "", time.Now(), reader)
   748  	case uri.Hash():
   749  		w.Header().Set("Content-Type", "text/plain")
   750  		w.WriteHeader(http.StatusOK)
   751  		fmt.Fprint(w, addr)
   752  	}
   753  }
   754  
   755  //
   756  //
   757  //
   758  func (s *Server) HandleGetList(w http.ResponseWriter, r *http.Request) {
   759  	ruid := GetRUID(r.Context())
   760  	uri := GetURI(r.Context())
   761  	_, credentials, _ := r.BasicAuth()
   762  	log.Debug("handle.get.list", "ruid", ruid, "uri", uri)
   763  	getListCount.Inc(1)
   764  
   765  //
   766  	if uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
   767  		http.Redirect(w, r, r.URL.Path+"/", http.StatusMovedPermanently)
   768  		return
   769  	}
   770  
   771  	addr, err := s.api.Resolve(r.Context(), uri.Addr)
   772  	if err != nil {
   773  		getListFail.Inc(1)
   774  		RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
   775  		return
   776  	}
   777  	log.Debug("handle.get.list: resolved", "ruid", ruid, "key", addr)
   778  
   779  	list, err := s.api.GetManifestList(r.Context(), s.api.Decryptor(r.Context(), credentials), addr, uri.Path)
   780  	if err != nil {
   781  		getListFail.Inc(1)
   782  		if isDecryptError(err) {
   783  			w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", addr.String()))
   784  			RespondError(w, r, err.Error(), http.StatusUnauthorized)
   785  			return
   786  		}
   787  		RespondError(w, r, err.Error(), http.StatusInternalServerError)
   788  		return
   789  	}
   790  
   791  //
   792  //
   793  	if strings.Contains(r.Header.Get("Accept"), "text/html") {
   794  		w.Header().Set("Content-Type", "text/html")
   795  		err := TemplatesMap["bzz-list"].Execute(w, &htmlListData{
   796  			URI: &api.URI{
   797  				Scheme: "bzz",
   798  				Addr:   uri.Addr,
   799  				Path:   uri.Path,
   800  			},
   801  			List: &list,
   802  		})
   803  		if err != nil {
   804  			getListFail.Inc(1)
   805  			log.Error(fmt.Sprintf("error rendering list HTML: %s", err))
   806  		}
   807  		return
   808  	}
   809  
   810  	w.Header().Set("Content-Type", "application/json")
   811  	json.NewEncoder(w).Encode(&list)
   812  }
   813  
   814  //
   815  //
   816  func (s *Server) HandleGetFile(w http.ResponseWriter, r *http.Request) {
   817  	ruid := GetRUID(r.Context())
   818  	uri := GetURI(r.Context())
   819  	_, credentials, _ := r.BasicAuth()
   820  	log.Debug("handle.get.file", "ruid", ruid, "uri", r.RequestURI)
   821  	getFileCount.Inc(1)
   822  
   823  //
   824  	if uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
   825  		http.Redirect(w, r, r.URL.Path+"/", http.StatusMovedPermanently)
   826  		return
   827  	}
   828  	var err error
   829  	manifestAddr := uri.Address()
   830  
   831  	if manifestAddr == nil {
   832  		manifestAddr, err = s.api.ResolveURI(r.Context(), uri, credentials)
   833  		if err != nil {
   834  			getFileFail.Inc(1)
   835  			RespondError(w, r, fmt.Sprintf("cannot resolve %s: %s", uri.Addr, err), http.StatusNotFound)
   836  			return
   837  		}
   838  	} else {
   839  w.Header().Set("Cache-Control", "max-age=2147483648, immutable") //
   840  	}
   841  
   842  	log.Debug("handle.get.file: resolved", "ruid", ruid, "key", manifestAddr)
   843  
   844  	reader, contentType, status, contentKey, err := s.api.Get(r.Context(), s.api.Decryptor(r.Context(), credentials), manifestAddr, uri.Path)
   845  
   846  	etag := common.Bytes2Hex(contentKey)
   847  	noneMatchEtag := r.Header.Get("If-None-Match")
   848  w.Header().Set("ETag", fmt.Sprintf("%q", etag)) //
   849  	if noneMatchEtag != "" {
   850  		if bytes.Equal(storage.Address(common.Hex2Bytes(noneMatchEtag)), contentKey) {
   851  			w.WriteHeader(http.StatusNotModified)
   852  			return
   853  		}
   854  	}
   855  
   856  	if err != nil {
   857  		if isDecryptError(err) {
   858  			w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", manifestAddr))
   859  			RespondError(w, r, err.Error(), http.StatusUnauthorized)
   860  			return
   861  		}
   862  
   863  		switch status {
   864  		case http.StatusNotFound:
   865  			getFileNotFound.Inc(1)
   866  			RespondError(w, r, err.Error(), http.StatusNotFound)
   867  		default:
   868  			getFileFail.Inc(1)
   869  			RespondError(w, r, err.Error(), http.StatusInternalServerError)
   870  		}
   871  		return
   872  	}
   873  
   874  //
   875  //
   876  	if status == http.StatusMultipleChoices {
   877  		list, err := s.api.GetManifestList(r.Context(), s.api.Decryptor(r.Context(), credentials), manifestAddr, uri.Path)
   878  		if err != nil {
   879  			getFileFail.Inc(1)
   880  			if isDecryptError(err) {
   881  				w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=%q", manifestAddr))
   882  				RespondError(w, r, err.Error(), http.StatusUnauthorized)
   883  				return
   884  			}
   885  			RespondError(w, r, err.Error(), http.StatusInternalServerError)
   886  			return
   887  		}
   888  
   889  		log.Debug(fmt.Sprintf("Multiple choices! --> %v", list), "ruid", ruid)
   890  //
   891  		ShowMultipleChoices(w, r, list)
   892  		return
   893  	}
   894  
   895  //
   896  	if _, err := reader.Size(r.Context(), nil); err != nil {
   897  		getFileNotFound.Inc(1)
   898  		RespondError(w, r, fmt.Sprintf("file not found %s: %s", uri, err), http.StatusNotFound)
   899  		return
   900  	}
   901  
   902  	w.Header().Set("Content-Type", contentType)
   903  	http.ServeContent(w, r, "", time.Now(), newBufferedReadSeeker(reader, getFileBufferSize))
   904  }
   905  
   906  //
   907  //
   908  //
   909  //
   910  //
   911  const getFileBufferSize = 4 * 32 * 1024
   912  
   913  //
   914  //
   915  type bufferedReadSeeker struct {
   916  	r io.Reader
   917  	s io.Seeker
   918  }
   919  
   920  //
   921  //
   922  func newBufferedReadSeeker(readSeeker io.ReadSeeker, size int) bufferedReadSeeker {
   923  	return bufferedReadSeeker{
   924  		r: bufio.NewReaderSize(readSeeker, size),
   925  		s: readSeeker,
   926  	}
   927  }
   928  
   929  func (b bufferedReadSeeker) Read(p []byte) (n int, err error) {
   930  	return b.r.Read(p)
   931  }
   932  
   933  func (b bufferedReadSeeker) Seek(offset int64, whence int) (int64, error) {
   934  	return b.s.Seek(offset, whence)
   935  }
   936  
   937  type loggingResponseWriter struct {
   938  	http.ResponseWriter
   939  	statusCode int
   940  }
   941  
   942  func newLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter {
   943  	return &loggingResponseWriter{w, http.StatusOK}
   944  }
   945  
   946  func (lrw *loggingResponseWriter) WriteHeader(code int) {
   947  	lrw.statusCode = code
   948  	lrw.ResponseWriter.WriteHeader(code)
   949  }
   950  
   951  func isDecryptError(err error) bool {
   952  	return strings.Contains(err.Error(), api.ErrDecrypt.Error())
   953  }