github.com/vantum/vantum@v0.0.0-20180815184342-fe37d5f7a990/swarm/api/http/server.go (about)

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