github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/swarm/api/http/server.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  /*
    13  A simple http server interface to Swarm
    14  */
    15  package http
    16  
    17  import (
    18  	"archive/tar"
    19  	"encoding/json"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"io/ioutil"
    24  	"mime"
    25  	"mime/multipart"
    26  	"net/http"
    27  	"os"
    28  	"path"
    29  	"strconv"
    30  	"strings"
    31  	"time"
    32  
    33  	"github.com/Sberex/go-sberex/common"
    34  	"github.com/Sberex/go-sberex/log"
    35  	"github.com/Sberex/go-sberex/metrics"
    36  	"github.com/Sberex/go-sberex/swarm/api"
    37  	"github.com/Sberex/go-sberex/swarm/storage"
    38  	"github.com/rs/cors"
    39  )
    40  
    41  //setup metrics
    42  var (
    43  	postRawCount     = metrics.NewRegisteredCounter("api.http.post.raw.count", nil)
    44  	postRawFail      = metrics.NewRegisteredCounter("api.http.post.raw.fail", nil)
    45  	postFilesCount   = metrics.NewRegisteredCounter("api.http.post.files.count", nil)
    46  	postFilesFail    = metrics.NewRegisteredCounter("api.http.post.files.fail", nil)
    47  	deleteCount      = metrics.NewRegisteredCounter("api.http.delete.count", nil)
    48  	deleteFail       = metrics.NewRegisteredCounter("api.http.delete.fail", nil)
    49  	getCount         = metrics.NewRegisteredCounter("api.http.get.count", nil)
    50  	getFail          = metrics.NewRegisteredCounter("api.http.get.fail", nil)
    51  	getFileCount     = metrics.NewRegisteredCounter("api.http.get.file.count", nil)
    52  	getFileNotFound  = metrics.NewRegisteredCounter("api.http.get.file.notfound", nil)
    53  	getFileFail      = metrics.NewRegisteredCounter("api.http.get.file.fail", nil)
    54  	getFilesCount    = metrics.NewRegisteredCounter("api.http.get.files.count", nil)
    55  	getFilesFail     = metrics.NewRegisteredCounter("api.http.get.files.fail", nil)
    56  	getListCount     = metrics.NewRegisteredCounter("api.http.get.list.count", nil)
    57  	getListFail      = metrics.NewRegisteredCounter("api.http.get.list.fail", nil)
    58  	requestCount     = metrics.NewRegisteredCounter("http.request.count", nil)
    59  	htmlRequestCount = metrics.NewRegisteredCounter("http.request.html.count", nil)
    60  	jsonRequestCount = metrics.NewRegisteredCounter("http.request.json.count", nil)
    61  	requestTimer     = metrics.NewRegisteredResettingTimer("http.request.time", nil)
    62  )
    63  
    64  // ServerConfig is the basic configuration needed for the HTTP server and also
    65  // includes CORS settings.
    66  type ServerConfig struct {
    67  	Addr       string
    68  	CorsString string
    69  }
    70  
    71  // browser API for registering bzz url scheme handlers:
    72  // https://developer.mozilla.org/en/docs/Web-based_protocol_handlers
    73  // electron (chromium) api for registering bzz url scheme handlers:
    74  // https://github.com/atom/electron/blob/master/docs/api/protocol.md
    75  
    76  // starts up http server
    77  func StartHttpServer(api *api.Api, config *ServerConfig) {
    78  	var allowedOrigins []string
    79  	for _, domain := range strings.Split(config.CorsString, ",") {
    80  		allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain))
    81  	}
    82  	c := cors.New(cors.Options{
    83  		AllowedOrigins: allowedOrigins,
    84  		AllowedMethods: []string{"POST", "GET", "DELETE", "PATCH", "PUT"},
    85  		MaxAge:         600,
    86  		AllowedHeaders: []string{"*"},
    87  	})
    88  	hdlr := c.Handler(NewServer(api))
    89  
    90  	go http.ListenAndServe(config.Addr, hdlr)
    91  }
    92  
    93  func NewServer(api *api.Api) *Server {
    94  	return &Server{api}
    95  }
    96  
    97  type Server struct {
    98  	api *api.Api
    99  }
   100  
   101  // Request wraps http.Request and also includes the parsed bzz URI
   102  type Request struct {
   103  	http.Request
   104  
   105  	uri *api.URI
   106  }
   107  
   108  // HandlePostRaw handles a POST request to a raw bzz-raw:/ URI, stores the request
   109  // body in swarm and returns the resulting storage key as a text/plain response
   110  func (s *Server) HandlePostRaw(w http.ResponseWriter, r *Request) {
   111  	postRawCount.Inc(1)
   112  	if r.uri.Path != "" {
   113  		postRawFail.Inc(1)
   114  		s.BadRequest(w, r, "raw POST request cannot contain a path")
   115  		return
   116  	}
   117  
   118  	if r.Header.Get("Content-Length") == "" {
   119  		postRawFail.Inc(1)
   120  		s.BadRequest(w, r, "missing Content-Length header in request")
   121  		return
   122  	}
   123  
   124  	key, err := s.api.Store(r.Body, r.ContentLength, nil)
   125  	if err != nil {
   126  		postRawFail.Inc(1)
   127  		s.Error(w, r, err)
   128  		return
   129  	}
   130  	s.logDebug("content for %s stored", key.Log())
   131  
   132  	w.Header().Set("Content-Type", "text/plain")
   133  	w.WriteHeader(http.StatusOK)
   134  	fmt.Fprint(w, key)
   135  }
   136  
   137  // HandlePostFiles handles a POST request (or deprecated PUT request) to
   138  // bzz:/<hash>/<path> which contains either a single file or multiple files
   139  // (either a tar archive or multipart form), adds those files either to an
   140  // existing manifest or to a new manifest under <path> and returns the
   141  // resulting manifest hash as a text/plain response
   142  func (s *Server) HandlePostFiles(w http.ResponseWriter, r *Request) {
   143  	postFilesCount.Inc(1)
   144  	contentType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
   145  	if err != nil {
   146  		postFilesFail.Inc(1)
   147  		s.BadRequest(w, r, err.Error())
   148  		return
   149  	}
   150  
   151  	var key storage.Key
   152  	if r.uri.Addr != "" {
   153  		key, err = s.api.Resolve(r.uri)
   154  		if err != nil {
   155  			postFilesFail.Inc(1)
   156  			s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
   157  			return
   158  		}
   159  	} else {
   160  		key, err = s.api.NewManifest()
   161  		if err != nil {
   162  			postFilesFail.Inc(1)
   163  			s.Error(w, r, err)
   164  			return
   165  		}
   166  	}
   167  
   168  	newKey, err := s.updateManifest(key, func(mw *api.ManifestWriter) error {
   169  		switch contentType {
   170  
   171  		case "application/x-tar":
   172  			return s.handleTarUpload(r, mw)
   173  
   174  		case "multipart/form-data":
   175  			return s.handleMultipartUpload(r, params["boundary"], mw)
   176  
   177  		default:
   178  			return s.handleDirectUpload(r, mw)
   179  		}
   180  	})
   181  	if err != nil {
   182  		postFilesFail.Inc(1)
   183  		s.Error(w, r, fmt.Errorf("error creating manifest: %s", err))
   184  		return
   185  	}
   186  
   187  	w.Header().Set("Content-Type", "text/plain")
   188  	w.WriteHeader(http.StatusOK)
   189  	fmt.Fprint(w, newKey)
   190  }
   191  
   192  func (s *Server) handleTarUpload(req *Request, mw *api.ManifestWriter) error {
   193  	tr := tar.NewReader(req.Body)
   194  	for {
   195  		hdr, err := tr.Next()
   196  		if err == io.EOF {
   197  			return nil
   198  		} else if err != nil {
   199  			return fmt.Errorf("error reading tar stream: %s", err)
   200  		}
   201  
   202  		// only store regular files
   203  		if !hdr.FileInfo().Mode().IsRegular() {
   204  			continue
   205  		}
   206  
   207  		// add the entry under the path from the request
   208  		path := path.Join(req.uri.Path, hdr.Name)
   209  		entry := &api.ManifestEntry{
   210  			Path:        path,
   211  			ContentType: hdr.Xattrs["user.swarm.content-type"],
   212  			Mode:        hdr.Mode,
   213  			Size:        hdr.Size,
   214  			ModTime:     hdr.ModTime,
   215  		}
   216  		s.logDebug("adding %s (%d bytes) to new manifest", entry.Path, entry.Size)
   217  		contentKey, err := mw.AddEntry(tr, entry)
   218  		if err != nil {
   219  			return fmt.Errorf("error adding manifest entry from tar stream: %s", err)
   220  		}
   221  		s.logDebug("content for %s stored", contentKey.Log())
   222  	}
   223  }
   224  
   225  func (s *Server) handleMultipartUpload(req *Request, boundary string, mw *api.ManifestWriter) error {
   226  	mr := multipart.NewReader(req.Body, boundary)
   227  	for {
   228  		part, err := mr.NextPart()
   229  		if err == io.EOF {
   230  			return nil
   231  		} else if err != nil {
   232  			return fmt.Errorf("error reading multipart form: %s", err)
   233  		}
   234  
   235  		var size int64
   236  		var reader io.Reader = part
   237  		if contentLength := part.Header.Get("Content-Length"); contentLength != "" {
   238  			size, err = strconv.ParseInt(contentLength, 10, 64)
   239  			if err != nil {
   240  				return fmt.Errorf("error parsing multipart content length: %s", err)
   241  			}
   242  			reader = part
   243  		} else {
   244  			// copy the part to a tmp file to get its size
   245  			tmp, err := ioutil.TempFile("", "swarm-multipart")
   246  			if err != nil {
   247  				return err
   248  			}
   249  			defer os.Remove(tmp.Name())
   250  			defer tmp.Close()
   251  			size, err = io.Copy(tmp, part)
   252  			if err != nil {
   253  				return fmt.Errorf("error copying multipart content: %s", err)
   254  			}
   255  			if _, err := tmp.Seek(0, io.SeekStart); err != nil {
   256  				return fmt.Errorf("error copying multipart content: %s", err)
   257  			}
   258  			reader = tmp
   259  		}
   260  
   261  		// add the entry under the path from the request
   262  		name := part.FileName()
   263  		if name == "" {
   264  			name = part.FormName()
   265  		}
   266  		path := path.Join(req.uri.Path, name)
   267  		entry := &api.ManifestEntry{
   268  			Path:        path,
   269  			ContentType: part.Header.Get("Content-Type"),
   270  			Size:        size,
   271  			ModTime:     time.Now(),
   272  		}
   273  		s.logDebug("adding %s (%d bytes) to new manifest", entry.Path, entry.Size)
   274  		contentKey, err := mw.AddEntry(reader, entry)
   275  		if err != nil {
   276  			return fmt.Errorf("error adding manifest entry from multipart form: %s", err)
   277  		}
   278  		s.logDebug("content for %s stored", contentKey.Log())
   279  	}
   280  }
   281  
   282  func (s *Server) handleDirectUpload(req *Request, mw *api.ManifestWriter) error {
   283  	key, err := mw.AddEntry(req.Body, &api.ManifestEntry{
   284  		Path:        req.uri.Path,
   285  		ContentType: req.Header.Get("Content-Type"),
   286  		Mode:        0644,
   287  		Size:        req.ContentLength,
   288  		ModTime:     time.Now(),
   289  	})
   290  	if err != nil {
   291  		return err
   292  	}
   293  	s.logDebug("content for %s stored", key.Log())
   294  	return nil
   295  }
   296  
   297  // HandleDelete handles a DELETE request to bzz:/<manifest>/<path>, removes
   298  // <path> from <manifest> and returns the resulting manifest hash as a
   299  // text/plain response
   300  func (s *Server) HandleDelete(w http.ResponseWriter, r *Request) {
   301  	deleteCount.Inc(1)
   302  	key, err := s.api.Resolve(r.uri)
   303  	if err != nil {
   304  		deleteFail.Inc(1)
   305  		s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
   306  		return
   307  	}
   308  
   309  	newKey, err := s.updateManifest(key, func(mw *api.ManifestWriter) error {
   310  		s.logDebug("removing %s from manifest %s", r.uri.Path, key.Log())
   311  		return mw.RemoveEntry(r.uri.Path)
   312  	})
   313  	if err != nil {
   314  		deleteFail.Inc(1)
   315  		s.Error(w, r, fmt.Errorf("error updating manifest: %s", err))
   316  		return
   317  	}
   318  
   319  	w.Header().Set("Content-Type", "text/plain")
   320  	w.WriteHeader(http.StatusOK)
   321  	fmt.Fprint(w, newKey)
   322  }
   323  
   324  // HandleGet handles a GET request to
   325  // - bzz-raw://<key> and responds with the raw content stored at the
   326  //   given storage key
   327  // - bzz-hash://<key> and responds with the hash of the content stored
   328  //   at the given storage key as a text/plain response
   329  func (s *Server) HandleGet(w http.ResponseWriter, r *Request) {
   330  	getCount.Inc(1)
   331  	key, err := s.api.Resolve(r.uri)
   332  	if err != nil {
   333  		getFail.Inc(1)
   334  		s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
   335  		return
   336  	}
   337  
   338  	// if path is set, interpret <key> as a manifest and return the
   339  	// raw entry at the given path
   340  	if r.uri.Path != "" {
   341  		walker, err := s.api.NewManifestWalker(key, nil)
   342  		if err != nil {
   343  			getFail.Inc(1)
   344  			s.BadRequest(w, r, fmt.Sprintf("%s is not a manifest", key))
   345  			return
   346  		}
   347  		var entry *api.ManifestEntry
   348  		walker.Walk(func(e *api.ManifestEntry) error {
   349  			// if the entry matches the path, set entry and stop
   350  			// the walk
   351  			if e.Path == r.uri.Path {
   352  				entry = e
   353  				// return an error to cancel the walk
   354  				return errors.New("found")
   355  			}
   356  
   357  			// ignore non-manifest files
   358  			if e.ContentType != api.ManifestType {
   359  				return nil
   360  			}
   361  
   362  			// if the manifest's path is a prefix of the
   363  			// requested path, recurse into it by returning
   364  			// nil and continuing the walk
   365  			if strings.HasPrefix(r.uri.Path, e.Path) {
   366  				return nil
   367  			}
   368  
   369  			return api.SkipManifest
   370  		})
   371  		if entry == nil {
   372  			getFail.Inc(1)
   373  			s.NotFound(w, r, fmt.Errorf("Manifest entry could not be loaded"))
   374  			return
   375  		}
   376  		key = storage.Key(common.Hex2Bytes(entry.Hash))
   377  	}
   378  
   379  	// check the root chunk exists by retrieving the file's size
   380  	reader := s.api.Retrieve(key)
   381  	if _, err := reader.Size(nil); err != nil {
   382  		getFail.Inc(1)
   383  		s.NotFound(w, r, fmt.Errorf("Root chunk not found %s: %s", key, err))
   384  		return
   385  	}
   386  
   387  	switch {
   388  	case r.uri.Raw() || r.uri.DeprecatedRaw():
   389  		// allow the request to overwrite the content type using a query
   390  		// parameter
   391  		contentType := "application/octet-stream"
   392  		if typ := r.URL.Query().Get("content_type"); typ != "" {
   393  			contentType = typ
   394  		}
   395  		w.Header().Set("Content-Type", contentType)
   396  
   397  		http.ServeContent(w, &r.Request, "", time.Now(), reader)
   398  	case r.uri.Hash():
   399  		w.Header().Set("Content-Type", "text/plain")
   400  		w.WriteHeader(http.StatusOK)
   401  		fmt.Fprint(w, key)
   402  	}
   403  }
   404  
   405  // HandleGetFiles handles a GET request to bzz:/<manifest> with an Accept
   406  // header of "application/x-tar" and returns a tar stream of all files
   407  // contained in the manifest
   408  func (s *Server) HandleGetFiles(w http.ResponseWriter, r *Request) {
   409  	getFilesCount.Inc(1)
   410  	if r.uri.Path != "" {
   411  		getFilesFail.Inc(1)
   412  		s.BadRequest(w, r, "files request cannot contain a path")
   413  		return
   414  	}
   415  
   416  	key, err := s.api.Resolve(r.uri)
   417  	if err != nil {
   418  		getFilesFail.Inc(1)
   419  		s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
   420  		return
   421  	}
   422  
   423  	walker, err := s.api.NewManifestWalker(key, nil)
   424  	if err != nil {
   425  		getFilesFail.Inc(1)
   426  		s.Error(w, r, err)
   427  		return
   428  	}
   429  
   430  	tw := tar.NewWriter(w)
   431  	defer tw.Close()
   432  	w.Header().Set("Content-Type", "application/x-tar")
   433  	w.WriteHeader(http.StatusOK)
   434  
   435  	err = walker.Walk(func(entry *api.ManifestEntry) error {
   436  		// ignore manifests (walk will recurse into them)
   437  		if entry.ContentType == api.ManifestType {
   438  			return nil
   439  		}
   440  
   441  		// retrieve the entry's key and size
   442  		reader := s.api.Retrieve(storage.Key(common.Hex2Bytes(entry.Hash)))
   443  		size, err := reader.Size(nil)
   444  		if err != nil {
   445  			return err
   446  		}
   447  
   448  		// write a tar header for the entry
   449  		hdr := &tar.Header{
   450  			Name:    entry.Path,
   451  			Mode:    entry.Mode,
   452  			Size:    size,
   453  			ModTime: entry.ModTime,
   454  			Xattrs: map[string]string{
   455  				"user.swarm.content-type": entry.ContentType,
   456  			},
   457  		}
   458  		if err := tw.WriteHeader(hdr); err != nil {
   459  			return err
   460  		}
   461  
   462  		// copy the file into the tar stream
   463  		n, err := io.Copy(tw, io.LimitReader(reader, hdr.Size))
   464  		if err != nil {
   465  			return err
   466  		} else if n != size {
   467  			return fmt.Errorf("error writing %s: expected %d bytes but sent %d", entry.Path, size, n)
   468  		}
   469  
   470  		return nil
   471  	})
   472  	if err != nil {
   473  		getFilesFail.Inc(1)
   474  		s.logError("error generating tar stream: %s", err)
   475  	}
   476  }
   477  
   478  // HandleGetList handles a GET request to bzz-list:/<manifest>/<path> and returns
   479  // a list of all files contained in <manifest> under <path> grouped into
   480  // common prefixes using "/" as a delimiter
   481  func (s *Server) HandleGetList(w http.ResponseWriter, r *Request) {
   482  	getListCount.Inc(1)
   483  	// ensure the root path has a trailing slash so that relative URLs work
   484  	if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
   485  		http.Redirect(w, &r.Request, r.URL.Path+"/", http.StatusMovedPermanently)
   486  		return
   487  	}
   488  
   489  	key, err := s.api.Resolve(r.uri)
   490  	if err != nil {
   491  		getListFail.Inc(1)
   492  		s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
   493  		return
   494  	}
   495  
   496  	list, err := s.getManifestList(key, r.uri.Path)
   497  
   498  	if err != nil {
   499  		getListFail.Inc(1)
   500  		s.Error(w, r, err)
   501  		return
   502  	}
   503  
   504  	// if the client wants HTML (e.g. a browser) then render the list as a
   505  	// HTML index with relative URLs
   506  	if strings.Contains(r.Header.Get("Accept"), "text/html") {
   507  		w.Header().Set("Content-Type", "text/html")
   508  		err := htmlListTemplate.Execute(w, &htmlListData{
   509  			URI: &api.URI{
   510  				Scheme: "bzz",
   511  				Addr:   r.uri.Addr,
   512  				Path:   r.uri.Path,
   513  			},
   514  			List: &list,
   515  		})
   516  		if err != nil {
   517  			getListFail.Inc(1)
   518  			s.logError("error rendering list HTML: %s", err)
   519  		}
   520  		return
   521  	}
   522  
   523  	w.Header().Set("Content-Type", "application/json")
   524  	json.NewEncoder(w).Encode(&list)
   525  }
   526  
   527  func (s *Server) getManifestList(key storage.Key, prefix string) (list api.ManifestList, err error) {
   528  	walker, err := s.api.NewManifestWalker(key, nil)
   529  	if err != nil {
   530  		return
   531  	}
   532  
   533  	err = walker.Walk(func(entry *api.ManifestEntry) error {
   534  		// handle non-manifest files
   535  		if entry.ContentType != api.ManifestType {
   536  			// ignore the file if it doesn't have the specified prefix
   537  			if !strings.HasPrefix(entry.Path, prefix) {
   538  				return nil
   539  			}
   540  
   541  			// if the path after the prefix contains a slash, add a
   542  			// common prefix to the list, otherwise add the entry
   543  			suffix := strings.TrimPrefix(entry.Path, prefix)
   544  			if index := strings.Index(suffix, "/"); index > -1 {
   545  				list.CommonPrefixes = append(list.CommonPrefixes, prefix+suffix[:index+1])
   546  				return nil
   547  			}
   548  			if entry.Path == "" {
   549  				entry.Path = "/"
   550  			}
   551  			list.Entries = append(list.Entries, entry)
   552  			return nil
   553  		}
   554  
   555  		// if the manifest's path is a prefix of the specified prefix
   556  		// then just recurse into the manifest by returning nil and
   557  		// continuing the walk
   558  		if strings.HasPrefix(prefix, entry.Path) {
   559  			return nil
   560  		}
   561  
   562  		// if the manifest's path has the specified prefix, then if the
   563  		// path after the prefix contains a slash, add a common prefix
   564  		// to the list and skip the manifest, otherwise recurse into
   565  		// the manifest by returning nil and continuing the walk
   566  		if strings.HasPrefix(entry.Path, prefix) {
   567  			suffix := strings.TrimPrefix(entry.Path, prefix)
   568  			if index := strings.Index(suffix, "/"); index > -1 {
   569  				list.CommonPrefixes = append(list.CommonPrefixes, prefix+suffix[:index+1])
   570  				return api.SkipManifest
   571  			}
   572  			return nil
   573  		}
   574  
   575  		// the manifest neither has the prefix or needs recursing in to
   576  		// so just skip it
   577  		return api.SkipManifest
   578  	})
   579  
   580  	return list, nil
   581  }
   582  
   583  // HandleGetFile handles a GET request to bzz://<manifest>/<path> and responds
   584  // with the content of the file at <path> from the given <manifest>
   585  func (s *Server) HandleGetFile(w http.ResponseWriter, r *Request) {
   586  	getFileCount.Inc(1)
   587  	// ensure the root path has a trailing slash so that relative URLs work
   588  	if r.uri.Path == "" && !strings.HasSuffix(r.URL.Path, "/") {
   589  		http.Redirect(w, &r.Request, r.URL.Path+"/", http.StatusMovedPermanently)
   590  		return
   591  	}
   592  
   593  	key, err := s.api.Resolve(r.uri)
   594  	if err != nil {
   595  		getFileFail.Inc(1)
   596  		s.Error(w, r, fmt.Errorf("error resolving %s: %s", r.uri.Addr, err))
   597  		return
   598  	}
   599  
   600  	reader, contentType, status, err := s.api.Get(key, r.uri.Path)
   601  	if err != nil {
   602  		switch status {
   603  		case http.StatusNotFound:
   604  			getFileNotFound.Inc(1)
   605  			s.NotFound(w, r, err)
   606  		default:
   607  			getFileFail.Inc(1)
   608  			s.Error(w, r, err)
   609  		}
   610  		return
   611  	}
   612  
   613  	//the request results in ambiguous files
   614  	//e.g. /read with readme.md and readinglist.txt available in manifest
   615  	if status == http.StatusMultipleChoices {
   616  		list, err := s.getManifestList(key, r.uri.Path)
   617  
   618  		if err != nil {
   619  			getFileFail.Inc(1)
   620  			s.Error(w, r, err)
   621  			return
   622  		}
   623  
   624  		s.logDebug(fmt.Sprintf("Multiple choices! -->  %v", list))
   625  		//show a nice page links to available entries
   626  		ShowMultipleChoices(w, &r.Request, list)
   627  		return
   628  	}
   629  
   630  	// check the root chunk exists by retrieving the file's size
   631  	if _, err := reader.Size(nil); err != nil {
   632  		getFileNotFound.Inc(1)
   633  		s.NotFound(w, r, fmt.Errorf("File not found %s: %s", r.uri, err))
   634  		return
   635  	}
   636  
   637  	w.Header().Set("Content-Type", contentType)
   638  
   639  	http.ServeContent(w, &r.Request, "", time.Now(), reader)
   640  }
   641  
   642  func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   643  	if metrics.Enabled {
   644  		//The increment for request count and request timer themselves have a flag check
   645  		//for metrics.Enabled. Nevertheless, we introduce the if here because we
   646  		//are looking into the header just to see what request type it is (json/html).
   647  		//So let's take advantage and add all metrics related stuff here
   648  		requestCount.Inc(1)
   649  		defer requestTimer.UpdateSince(time.Now())
   650  		if r.Header.Get("Accept") == "application/json" {
   651  			jsonRequestCount.Inc(1)
   652  		} else {
   653  			htmlRequestCount.Inc(1)
   654  		}
   655  	}
   656  	s.logDebug("HTTP %s request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.Method, r.RequestURI, r.URL.Host, r.URL.Path, r.Referer(), r.Header.Get("Accept"))
   657  
   658  	if r.RequestURI == "/" && strings.Contains(r.Header.Get("Accept"), "text/html") {
   659  
   660  		err := landingPageTemplate.Execute(w, nil)
   661  		if err != nil {
   662  			s.logError("error rendering landing page: %s", err)
   663  		}
   664  		return
   665  	}
   666  
   667  	uri, err := api.Parse(strings.TrimLeft(r.URL.Path, "/"))
   668  	req := &Request{Request: *r, uri: uri}
   669  	if err != nil {
   670  		s.logError("Invalid URI %q: %s", r.URL.Path, err)
   671  		s.BadRequest(w, req, fmt.Sprintf("Invalid URI %q: %s", r.URL.Path, err))
   672  		return
   673  	}
   674  	s.logDebug("%s request received for %s", r.Method, uri)
   675  
   676  	switch r.Method {
   677  	case "POST":
   678  		if uri.Raw() || uri.DeprecatedRaw() {
   679  			s.HandlePostRaw(w, req)
   680  		} else {
   681  			s.HandlePostFiles(w, req)
   682  		}
   683  
   684  	case "PUT":
   685  		// DEPRECATED:
   686  		//   clients should send a POST request (the request creates a
   687  		//   new manifest leaving the existing one intact, so it isn't
   688  		//   strictly a traditional PUT request which replaces content
   689  		//   at a URI, and POST is more ubiquitous)
   690  		if uri.Raw() || uri.DeprecatedRaw() {
   691  			ShowError(w, r, fmt.Sprintf("No PUT to %s allowed.", uri), http.StatusBadRequest)
   692  			return
   693  		} else {
   694  			s.HandlePostFiles(w, req)
   695  		}
   696  
   697  	case "DELETE":
   698  		if uri.Raw() || uri.DeprecatedRaw() {
   699  			ShowError(w, r, fmt.Sprintf("No DELETE to %s allowed.", uri), http.StatusBadRequest)
   700  			return
   701  		}
   702  		s.HandleDelete(w, req)
   703  
   704  	case "GET":
   705  		if uri.Raw() || uri.Hash() || uri.DeprecatedRaw() {
   706  			s.HandleGet(w, req)
   707  			return
   708  		}
   709  
   710  		if uri.List() {
   711  			s.HandleGetList(w, req)
   712  			return
   713  		}
   714  
   715  		if r.Header.Get("Accept") == "application/x-tar" {
   716  			s.HandleGetFiles(w, req)
   717  			return
   718  		}
   719  
   720  		s.HandleGetFile(w, req)
   721  
   722  	default:
   723  		ShowError(w, r, fmt.Sprintf("Method "+r.Method+" is not supported.", uri), http.StatusMethodNotAllowed)
   724  
   725  	}
   726  }
   727  
   728  func (s *Server) updateManifest(key storage.Key, update func(mw *api.ManifestWriter) error) (storage.Key, error) {
   729  	mw, err := s.api.NewManifestWriter(key, nil)
   730  	if err != nil {
   731  		return nil, err
   732  	}
   733  
   734  	if err := update(mw); err != nil {
   735  		return nil, err
   736  	}
   737  
   738  	key, err = mw.Store()
   739  	if err != nil {
   740  		return nil, err
   741  	}
   742  	s.logDebug("generated manifest %s", key)
   743  	return key, nil
   744  }
   745  
   746  func (s *Server) logDebug(format string, v ...interface{}) {
   747  	log.Debug(fmt.Sprintf("[BZZ] HTTP: "+format, v...))
   748  }
   749  
   750  func (s *Server) logError(format string, v ...interface{}) {
   751  	log.Error(fmt.Sprintf("[BZZ] HTTP: "+format, v...))
   752  }
   753  
   754  func (s *Server) BadRequest(w http.ResponseWriter, r *Request, reason string) {
   755  	ShowError(w, &r.Request, fmt.Sprintf("Bad request %s %s: %s", r.Method, r.uri, reason), http.StatusBadRequest)
   756  }
   757  
   758  func (s *Server) Error(w http.ResponseWriter, r *Request, err error) {
   759  	ShowError(w, &r.Request, fmt.Sprintf("Error serving %s %s: %s", r.Method, r.uri, err), http.StatusInternalServerError)
   760  }
   761  
   762  func (s *Server) NotFound(w http.ResponseWriter, r *Request, err error) {
   763  	ShowError(w, &r.Request, fmt.Sprintf("NOT FOUND error serving %s %s: %s", r.Method, r.uri, err), http.StatusNotFound)
   764  }