swarm/api: integrate tags to count chunks being split and stored swarm/api/http: integrate tags in middleware for HTTP `POST` calls and assert chunks being calculated and counted correctly swarm: remove deprecated and unused code, add swarm hash to DoneSplit signature, remove calls to the api client from the http package
		
			
				
	
	
		
			163 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package http
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"runtime/debug"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/ethereum/go-ethereum/metrics"
 | |
| 	"github.com/ethereum/go-ethereum/swarm/api"
 | |
| 	"github.com/ethereum/go-ethereum/swarm/chunk"
 | |
| 	"github.com/ethereum/go-ethereum/swarm/log"
 | |
| 	"github.com/ethereum/go-ethereum/swarm/sctx"
 | |
| 	"github.com/ethereum/go-ethereum/swarm/spancontext"
 | |
| 	"github.com/pborman/uuid"
 | |
| )
 | |
| 
 | |
| // Adapt chains h (main request handler) main handler to adapters (middleware handlers)
 | |
| // Please note that the order of execution for `adapters` is FIFO (adapters[0] will be executed first)
 | |
| func Adapt(h http.Handler, adapters ...Adapter) http.Handler {
 | |
| 	for i := range adapters {
 | |
| 		adapter := adapters[len(adapters)-1-i]
 | |
| 		h = adapter(h)
 | |
| 	}
 | |
| 	return h
 | |
| }
 | |
| 
 | |
| type Adapter func(http.Handler) http.Handler
 | |
| 
 | |
| func SetRequestID(h http.Handler) http.Handler {
 | |
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | |
| 		r = r.WithContext(SetRUID(r.Context(), uuid.New()[:8]))
 | |
| 		metrics.GetOrRegisterCounter(fmt.Sprintf("http.request.%s", r.Method), nil).Inc(1)
 | |
| 		log.Info("created ruid for request", "ruid", GetRUID(r.Context()), "method", r.Method, "url", r.RequestURI)
 | |
| 
 | |
| 		h.ServeHTTP(w, r)
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func SetRequestHost(h http.Handler) http.Handler {
 | |
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | |
| 		r = r.WithContext(sctx.SetHost(r.Context(), r.Host))
 | |
| 		log.Info("setting request host", "ruid", GetRUID(r.Context()), "host", sctx.GetHost(r.Context()))
 | |
| 
 | |
| 		h.ServeHTTP(w, r)
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func ParseURI(h http.Handler) http.Handler {
 | |
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | |
| 		uri, err := api.Parse(strings.TrimLeft(r.URL.Path, "/"))
 | |
| 		if err != nil {
 | |
| 			w.WriteHeader(http.StatusBadRequest)
 | |
| 			respondError(w, r, fmt.Sprintf("invalid URI %q", r.URL.Path), http.StatusBadRequest)
 | |
| 			return
 | |
| 		}
 | |
| 		if uri.Addr != "" && strings.HasPrefix(uri.Addr, "0x") {
 | |
| 			uri.Addr = strings.TrimPrefix(uri.Addr, "0x")
 | |
| 
 | |
| 			msg := fmt.Sprintf(`The requested hash seems to be prefixed with '0x'. You will be redirected to the correct URL within 5 seconds.<br/>
 | |
| 			Please click <a href='%[1]s'>here</a> if your browser does not redirect you within 5 seconds.<script>setTimeout("location.href='%[1]s';",5000);</script>`, "/"+uri.String())
 | |
| 			w.WriteHeader(http.StatusNotFound)
 | |
| 			w.Write([]byte(msg))
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		ctx := r.Context()
 | |
| 		r = r.WithContext(SetURI(ctx, uri))
 | |
| 		log.Debug("parsed request path", "ruid", GetRUID(r.Context()), "method", r.Method, "uri.Addr", uri.Addr, "uri.Path", uri.Path, "uri.Scheme", uri.Scheme)
 | |
| 
 | |
| 		h.ServeHTTP(w, r)
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func InitLoggingResponseWriter(h http.Handler) http.Handler {
 | |
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | |
| 		tn := time.Now()
 | |
| 
 | |
| 		writer := newLoggingResponseWriter(w)
 | |
| 		h.ServeHTTP(writer, r)
 | |
| 
 | |
| 		ts := time.Since(tn)
 | |
| 		log.Info("request served", "ruid", GetRUID(r.Context()), "code", writer.statusCode, "time", ts)
 | |
| 		metrics.GetOrRegisterResettingTimer(fmt.Sprintf("http.request.%s.time", r.Method), nil).Update(ts)
 | |
| 		metrics.GetOrRegisterResettingTimer(fmt.Sprintf("http.request.%s.%d.time", r.Method, writer.statusCode), nil).Update(ts)
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // InitUploadTag creates a new tag for an upload to the local HTTP proxy
 | |
| // if a tag is not named using the SwarmTagHeaderName, a fallback name will be used
 | |
| // when the Content-Length header is set, an ETA on chunking will be available since the
 | |
| // number of chunks to be split is known in advance (not including enclosing manifest chunks)
 | |
| // the tag can later be accessed using the appropriate identifier in the request context
 | |
| func InitUploadTag(h http.Handler, tags *chunk.Tags) http.Handler {
 | |
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | |
| 		var (
 | |
| 			tagName        string
 | |
| 			err            error
 | |
| 			estimatedTotal int64 = 0
 | |
| 			contentType          = r.Header.Get("Content-Type")
 | |
| 			headerTag            = r.Header.Get(SwarmTagHeaderName)
 | |
| 		)
 | |
| 		if headerTag != "" {
 | |
| 			tagName = headerTag
 | |
| 			log.Trace("got tag name from http header", "tagName", tagName)
 | |
| 		} else {
 | |
| 			tagName = fmt.Sprintf("unnamed_tag_%d", time.Now().Unix())
 | |
| 		}
 | |
| 
 | |
| 		if !strings.Contains(contentType, "multipart") && r.ContentLength > 0 {
 | |
| 			log.Trace("calculating tag size", "contentType", contentType, "contentLength", r.ContentLength)
 | |
| 			uri := GetURI(r.Context())
 | |
| 			if uri != nil {
 | |
| 				log.Debug("got uri from context")
 | |
| 				if uri.Addr == "encrypt" {
 | |
| 					estimatedTotal = calculateNumberOfChunks(r.ContentLength, true)
 | |
| 				} else {
 | |
| 					estimatedTotal = calculateNumberOfChunks(r.ContentLength, false)
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		log.Trace("creating tag", "tagName", tagName, "estimatedTotal", estimatedTotal)
 | |
| 
 | |
| 		t, err := tags.New(tagName, estimatedTotal)
 | |
| 		if err != nil {
 | |
| 			log.Error("error creating tag", "err", err, "tagName", tagName)
 | |
| 		}
 | |
| 
 | |
| 		log.Trace("setting tag id to context", "uid", t.Uid)
 | |
| 		ctx := sctx.SetTag(r.Context(), t.Uid)
 | |
| 
 | |
| 		h.ServeHTTP(w, r.WithContext(ctx))
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func InstrumentOpenTracing(h http.Handler) http.Handler {
 | |
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | |
| 		uri := GetURI(r.Context())
 | |
| 		if uri == nil || r.Method == "" || (uri != nil && uri.Scheme == "") {
 | |
| 			h.ServeHTTP(w, r) // soft fail
 | |
| 			return
 | |
| 		}
 | |
| 		spanName := fmt.Sprintf("http.%s.%s", r.Method, uri.Scheme)
 | |
| 		ctx, sp := spancontext.StartSpan(r.Context(), spanName)
 | |
| 
 | |
| 		defer sp.Finish()
 | |
| 		h.ServeHTTP(w, r.WithContext(ctx))
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func RecoverPanic(h http.Handler) http.Handler {
 | |
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | |
| 		defer func() {
 | |
| 			if err := recover(); err != nil {
 | |
| 				log.Error("panic recovery!", "stack trace", string(debug.Stack()), "url", r.URL.String(), "headers", r.Header)
 | |
| 			}
 | |
| 		}()
 | |
| 		h.ServeHTTP(w, r)
 | |
| 	})
 | |
| }
 |