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