github.com/devfans/go-ethereum@v1.5.10-0.20170326212234-7419d0c38291/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  	"bytes"
    24  	"fmt"
    25  	"io"
    26  	"net/http"
    27  	"regexp"
    28  	"strings"
    29  	"sync"
    30  	"time"
    31  
    32  	"github.com/ethereum/go-ethereum/common"
    33  	"github.com/ethereum/go-ethereum/log"
    34  	"github.com/ethereum/go-ethereum/swarm/api"
    35  	"github.com/ethereum/go-ethereum/swarm/storage"
    36  	"github.com/rs/cors"
    37  )
    38  
    39  const (
    40  	rawType = "application/octet-stream"
    41  )
    42  
    43  var (
    44  	// accepted protocols: bzz (traditional), bzzi (immutable) and bzzr (raw)
    45  	bzzPrefix       = regexp.MustCompile("^/+bzz[ir]?:/+")
    46  	trailingSlashes = regexp.MustCompile("/+$")
    47  	rootDocumentUri = regexp.MustCompile("^/+bzz[i]?:/+[^/]+$")
    48  	// forever         = func() time.Time { return time.Unix(0, 0) }
    49  	forever = time.Now
    50  )
    51  
    52  type sequentialReader struct {
    53  	reader io.Reader
    54  	pos    int64
    55  	ahead  map[int64](chan bool)
    56  	lock   sync.Mutex
    57  }
    58  
    59  // Server is the basic configuration needs for the HTTP server and also
    60  // includes CORS settings.
    61  type Server struct {
    62  	Addr       string
    63  	CorsString string
    64  }
    65  
    66  // browser API for registering bzz url scheme handlers:
    67  // https://developer.mozilla.org/en/docs/Web-based_protocol_handlers
    68  // electron (chromium) api for registering bzz url scheme handlers:
    69  // https://github.com/atom/electron/blob/master/docs/api/protocol.md
    70  
    71  // starts up http server
    72  func StartHttpServer(api *api.Api, server *Server) {
    73  	serveMux := http.NewServeMux()
    74  	serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    75  		handler(w, r, api)
    76  	})
    77  	var allowedOrigins []string
    78  	for _, domain := range strings.Split(server.CorsString, ",") {
    79  		allowedOrigins = append(allowedOrigins, strings.TrimSpace(domain))
    80  	}
    81  	c := cors.New(cors.Options{
    82  		AllowedOrigins: allowedOrigins,
    83  		AllowedMethods: []string{"POST", "GET", "DELETE", "PATCH", "PUT"},
    84  		MaxAge:         600,
    85  		AllowedHeaders: []string{"*"},
    86  	})
    87  	hdlr := c.Handler(serveMux)
    88  
    89  	go http.ListenAndServe(server.Addr, hdlr)
    90  	log.Info(fmt.Sprintf("Swarm HTTP proxy started on localhost:%s", server.Addr))
    91  }
    92  
    93  func handler(w http.ResponseWriter, r *http.Request, a *api.Api) {
    94  	requestURL := r.URL
    95  	// This is wrong
    96  	//	if requestURL.Host == "" {
    97  	//		var err error
    98  	//		requestURL, err = url.Parse(r.Referer() + requestURL.String())
    99  	//		if err != nil {
   100  	//			http.Error(w, err.Error(), http.StatusBadRequest)
   101  	//			return
   102  	//		}
   103  	//	}
   104  	log.Debug(fmt.Sprintf("HTTP %s request URL: '%s', Host: '%s', Path: '%s', Referer: '%s', Accept: '%s'", r.Method, r.RequestURI, requestURL.Host, requestURL.Path, r.Referer(), r.Header.Get("Accept")))
   105  	uri := requestURL.Path
   106  	var raw, nameresolver bool
   107  	var proto string
   108  
   109  	// HTTP-based URL protocol handler
   110  	log.Debug(fmt.Sprintf("BZZ request URI: '%s'", uri))
   111  
   112  	path := bzzPrefix.ReplaceAllStringFunc(uri, func(p string) string {
   113  		proto = p
   114  		return ""
   115  	})
   116  
   117  	// protocol identification (ugly)
   118  	if proto == "" {
   119  		log.Error(fmt.Sprintf("[BZZ] Swarm: Protocol error in request `%s`.", uri))
   120  		http.Error(w, "Invalid request URL: need access protocol (bzz:/, bzzr:/, bzzi:/) as first element in path.", http.StatusBadRequest)
   121  		return
   122  	}
   123  	if len(proto) > 4 {
   124  		raw = proto[1:5] == "bzzr"
   125  		nameresolver = proto[1:5] != "bzzi"
   126  	}
   127  
   128  	log.Debug("", "msg", log.Lazy{Fn: func() string {
   129  		return fmt.Sprintf("[BZZ] Swarm: %s request over protocol %s '%s' received.", r.Method, proto, path)
   130  	}})
   131  
   132  	switch {
   133  	case r.Method == "POST" || r.Method == "PUT":
   134  		if r.Header.Get("content-length") == "" {
   135  			http.Error(w, "Missing Content-Length header in request.", http.StatusBadRequest)
   136  			return
   137  		}
   138  		key, err := a.Store(io.LimitReader(r.Body, r.ContentLength), r.ContentLength, nil)
   139  		if err == nil {
   140  			log.Debug(fmt.Sprintf("Content for %v stored", key.Log()))
   141  		} else {
   142  			http.Error(w, err.Error(), http.StatusBadRequest)
   143  			return
   144  		}
   145  		if r.Method == "POST" {
   146  			if raw {
   147  				w.Header().Set("Content-Type", "text/plain")
   148  				http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(common.Bytes2Hex(key))))
   149  			} else {
   150  				http.Error(w, "No POST to "+uri+" allowed.", http.StatusBadRequest)
   151  				return
   152  			}
   153  		} else {
   154  			// PUT
   155  			if raw {
   156  				http.Error(w, "No PUT to /raw allowed.", http.StatusBadRequest)
   157  				return
   158  			} else {
   159  				path = api.RegularSlashes(path)
   160  				mime := r.Header.Get("Content-Type")
   161  				// TODO proper root hash separation
   162  				log.Debug(fmt.Sprintf("Modify '%s' to store %v as '%s'.", path, key.Log(), mime))
   163  				newKey, err := a.Modify(path, common.Bytes2Hex(key), mime, nameresolver)
   164  				if err == nil {
   165  					log.Debug(fmt.Sprintf("Swarm replaced manifest by '%s'", newKey))
   166  					w.Header().Set("Content-Type", "text/plain")
   167  					http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(newKey)))
   168  				} else {
   169  					http.Error(w, "PUT to "+path+"failed.", http.StatusBadRequest)
   170  					return
   171  				}
   172  			}
   173  		}
   174  	case r.Method == "DELETE":
   175  		if raw {
   176  			http.Error(w, "No DELETE to /raw allowed.", http.StatusBadRequest)
   177  			return
   178  		} else {
   179  			path = api.RegularSlashes(path)
   180  			log.Debug(fmt.Sprintf("Delete '%s'.", path))
   181  			newKey, err := a.Modify(path, "", "", nameresolver)
   182  			if err == nil {
   183  				log.Debug(fmt.Sprintf("Swarm replaced manifest by '%s'", newKey))
   184  				w.Header().Set("Content-Type", "text/plain")
   185  				http.ServeContent(w, r, "", time.Now(), bytes.NewReader([]byte(newKey)))
   186  			} else {
   187  				http.Error(w, "DELETE to "+path+"failed.", http.StatusBadRequest)
   188  				return
   189  			}
   190  		}
   191  	case r.Method == "GET" || r.Method == "HEAD":
   192  		path = trailingSlashes.ReplaceAllString(path, "")
   193  		if path == "" {
   194  			http.Error(w, "Empty path not allowed", http.StatusBadRequest)
   195  			return
   196  		}
   197  		if raw {
   198  			var reader storage.LazySectionReader
   199  			parsedurl, _ := api.Parse(path)
   200  
   201  			if parsedurl == path {
   202  				key, err := a.Resolve(parsedurl, nameresolver)
   203  				if err != nil {
   204  					log.Error(fmt.Sprintf("%v", err))
   205  					http.Error(w, err.Error(), http.StatusBadRequest)
   206  					return
   207  				}
   208  				reader = a.Retrieve(key)
   209  			} else {
   210  				var status int
   211  				readertmp, _, status, err := a.Get(path, nameresolver)
   212  				if err != nil {
   213  					http.Error(w, err.Error(), status)
   214  					return
   215  				}
   216  				reader = readertmp
   217  			}
   218  
   219  			// retrieving content
   220  
   221  			quitC := make(chan bool)
   222  			size, err := reader.Size(quitC)
   223  			if err != nil {
   224  				log.Debug(fmt.Sprintf("Could not determine size: %v", err.Error()))
   225  				//An error on call to Size means we don't have the root chunk
   226  				http.Error(w, err.Error(), http.StatusNotFound)
   227  				return
   228  			}
   229  			log.Debug(fmt.Sprintf("Reading %d bytes.", size))
   230  
   231  			// setting mime type
   232  			qv := requestURL.Query()
   233  			mimeType := qv.Get("content_type")
   234  			if mimeType == "" {
   235  				mimeType = rawType
   236  			}
   237  
   238  			w.Header().Set("Content-Type", mimeType)
   239  			http.ServeContent(w, r, uri, forever(), reader)
   240  			log.Debug(fmt.Sprintf("Serve raw content '%s' (%d bytes) as '%s'", uri, size, mimeType))
   241  
   242  			// retrieve path via manifest
   243  		} else {
   244  			log.Debug(fmt.Sprintf("Structured GET request '%s' received.", uri))
   245  			// add trailing slash, if missing
   246  			if rootDocumentUri.MatchString(uri) {
   247  				http.Redirect(w, r, path+"/", http.StatusFound)
   248  				return
   249  			}
   250  			reader, mimeType, status, err := a.Get(path, nameresolver)
   251  			if err != nil {
   252  				if _, ok := err.(api.ErrResolve); ok {
   253  					log.Debug(fmt.Sprintf("%v", err))
   254  					status = http.StatusBadRequest
   255  				} else {
   256  					log.Debug(fmt.Sprintf("error retrieving '%s': %v", uri, err))
   257  					status = http.StatusNotFound
   258  				}
   259  				http.Error(w, err.Error(), status)
   260  				return
   261  			}
   262  			// set mime type and status headers
   263  			w.Header().Set("Content-Type", mimeType)
   264  			if status > 0 {
   265  				w.WriteHeader(status)
   266  			} else {
   267  				status = 200
   268  			}
   269  			quitC := make(chan bool)
   270  			size, err := reader.Size(quitC)
   271  			if err != nil {
   272  				log.Debug(fmt.Sprintf("Could not determine size: %v", err.Error()))
   273  				//An error on call to Size means we don't have the root chunk
   274  				http.Error(w, err.Error(), http.StatusNotFound)
   275  				return
   276  			}
   277  			log.Debug(fmt.Sprintf("Served '%s' (%d bytes) as '%s' (status code: %v)", uri, size, mimeType, status))
   278  
   279  			http.ServeContent(w, r, path, forever(), reader)
   280  
   281  		}
   282  	default:
   283  		http.Error(w, "Method "+r.Method+" is not supported.", http.StatusMethodNotAllowed)
   284  	}
   285  }
   286  
   287  func (self *sequentialReader) ReadAt(target []byte, off int64) (n int, err error) {
   288  	self.lock.Lock()
   289  	// assert self.pos <= off
   290  	if self.pos > off {
   291  		log.Error(fmt.Sprintf("non-sequential read attempted from sequentialReader; %d > %d", self.pos, off))
   292  		panic("Non-sequential read attempt")
   293  	}
   294  	if self.pos != off {
   295  		log.Debug(fmt.Sprintf("deferred read in POST at position %d, offset %d.", self.pos, off))
   296  		wait := make(chan bool)
   297  		self.ahead[off] = wait
   298  		self.lock.Unlock()
   299  		if <-wait {
   300  			// failed read behind
   301  			n = 0
   302  			err = io.ErrUnexpectedEOF
   303  			return
   304  		}
   305  		self.lock.Lock()
   306  	}
   307  	localPos := 0
   308  	for localPos < len(target) {
   309  		n, err = self.reader.Read(target[localPos:])
   310  		localPos += n
   311  		log.Debug(fmt.Sprintf("Read %d bytes into buffer size %d from POST, error %v.", n, len(target), err))
   312  		if err != nil {
   313  			log.Debug(fmt.Sprintf("POST stream's reading terminated with %v.", err))
   314  			for i := range self.ahead {
   315  				self.ahead[i] <- true
   316  				delete(self.ahead, i)
   317  			}
   318  			self.lock.Unlock()
   319  			return localPos, err
   320  		}
   321  		self.pos += int64(n)
   322  	}
   323  	wait := self.ahead[self.pos]
   324  	if wait != nil {
   325  		log.Debug(fmt.Sprintf("deferred read in POST at position %d triggered.", self.pos))
   326  		delete(self.ahead, self.pos)
   327  		close(wait)
   328  	}
   329  	self.lock.Unlock()
   330  	return localPos, err
   331  }