func requestModePut(r *http.Request) storage.ModePut {
	if h := strings.ToLower(r.Header.Get(SwarmPinHeader)); h == "true" {
		return storage.ModePutUploadPin
	}
	return storage.ModePutUpload
}
requestModePut returns the desired storage.ModePut for this request based on the request headers.
package api
import (
	"context"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"math"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"
	"unicode/utf8"
	"github.com/ethsana/sana/pkg/crypto"
	"github.com/ethsana/sana/pkg/feeds"
	"github.com/ethsana/sana/pkg/file/pipeline/builder"
	"github.com/ethsana/sana/pkg/logging"
	m "github.com/ethsana/sana/pkg/metrics"
	"github.com/ethsana/sana/pkg/pinning"
	"github.com/ethsana/sana/pkg/postage"
	"github.com/ethsana/sana/pkg/postage/postagecontract"
	"github.com/ethsana/sana/pkg/pss"
	"github.com/ethsana/sana/pkg/resolver"
	"github.com/ethsana/sana/pkg/steward"
	"github.com/ethsana/sana/pkg/storage"
	"github.com/ethsana/sana/pkg/swarm"
	"github.com/ethsana/sana/pkg/tags"
	"github.com/ethsana/sana/pkg/tracing"
	"github.com/ethsana/sana/pkg/traversal"
)
const (
	SwarmPinHeader            = "Sana-Pin"
	SwarmTagHeader            = "Sana-Tag"
	SwarmEncryptHeader        = "Sana-Encrypt"
	SwarmIndexDocumentHeader  = "Sana-Index-Document"
	SwarmErrorDocumentHeader  = "Sana-Error-Document"
	SwarmFeedIndexHeader      = "Sana-Feed-Index"
	SwarmFeedIndexNextHeader  = "Sana-Feed-Index-Next"
	SwarmCollectionHeader     = "Sana-Collection"
	SwarmPostageBatchIdHeader = "Sana-Postage-Batch-Id"
)
const (
	smallFileBufferSize = 8 * 32 * 1024
	largeFileBufferSize = 16 * 32 * 1024
	largeBufferFilesizeThreshold = 10 * 1000000  
)
const (
	contentTypeHeader = "Content-Type"
	multiPartFormData = "multipart/form-data"
	contentTypeTar    = "application/x-tar"
	contentTypeZip    = "application/zip"
	contentTypeZipX   = "application/x-zip-compressed"
)
var (
	errInvalidNameOrAddress = errors.New("invalid name or sana address")
	errNoResolver           = errors.New("no resolver connected")
	errInvalidRequest       = errors.New("could not validate request")
	errInvalidContentType   = errors.New("invalid content-type")
	errDirectoryStore       = errors.New("could not store directory")
	errFileStore            = errors.New("could not store file")
	errInvalidPostageBatch  = errors.New("invalid postage batch id")
	errCreateTempFile       = errors.New("could not create temp file")
	errOpenZipFile          = errors.New("could not open zip file")
)
type Service interface {
	http.Handler
	m.Collector
	io.Closer
	GatewayResolverHandler() http.Handler
}
type server struct {
	tags            *tags.Tags
	storer          storage.Storer
	resolver        resolver.Interface
	pss             pss.Interface
	traversal       traversal.Traverser
	pinning         pinning.Interface
	steward         steward.Reuploader
	logger          logging.Logger
	tracer          *tracing.Tracer
	feedFactory     feeds.Factory
	signer          crypto.Signer
	post            postage.Service
	postageContract postagecontract.Interface
	Options
	http.Handler
	metrics metrics
	wsWg sync.WaitGroup  
	quit chan struct{}
}
type Options struct {
	CORSAllowedOrigins []string
	GatewayMode        bool
	WsPingPeriod       time.Duration
}
const (
	TargetsRecoveryHeader = "swarm-recovery-targets"
)
func New(tags *tags.Tags, storer storage.Storer, resolver resolver.Interface, pss pss.Interface, traversalService traversal.Traverser, pinning pinning.Interface, feedFactory feeds.Factory, post postage.Service, postageContract postagecontract.Interface, steward steward.Reuploader, signer crypto.Signer, logger logging.Logger, tracer *tracing.Tracer, o Options) Service {
	s := &server{
		tags:            tags,
		storer:          storer,
		resolver:        resolver,
		pss:             pss,
		traversal:       traversalService,
		pinning:         pinning,
		feedFactory:     feedFactory,
		post:            post,
		postageContract: postageContract,
		steward:         steward,
		signer:          signer,
		Options:         o,
		logger:          logger,
		tracer:          tracer,
		metrics:         newMetrics(),
		quit:            make(chan struct{}),
	}
	s.setupRouting()
	return s
}
func (s *server) Close() error {
	s.logger.Info("api shutting down")
	close(s.quit)
	done := make(chan struct{})
	go func() {
		defer close(done)
		s.wsWg.Wait()
	}()
	select {
	case <-done:
	case <-time.After(1 * time.Second):
		return errors.New("api shutting down with open websockets")
	}
	return nil
}
func (s *server) getOrCreateTag(tagUid string) (*tags.Tag, bool, error) {
	if tagUid == "" {
		tag, err := s.tags.Create(0)
		if err != nil {
			return nil, false, fmt.Errorf("cannot create tag: %w", err)
		}
		return tag, true, nil
	}
	t, err := s.getTag(tagUid)
	return t, false, err
}
func (s *server) getTag(tagUid string) (*tags.Tag, error) {
	uid, err := strconv.Atoi(tagUid)
	if err != nil {
		return nil, fmt.Errorf("cannot parse taguid: %w", err)
	}
	return s.tags.Get(uint32(uid))
}
func (s *server) resolveNameOrAddress(str string) (swarm.Address, error) {
	log := s.logger
	addr, err := swarm.ParseHexAddress(str)
	if err == nil {
		log.Tracef("name resolve: valid sana address %q", str)
		return addr, nil
	}
	if s.resolver == nil {
		return swarm.ZeroAddress, errNoResolver
	}
	log.Debugf("name resolve: attempting to resolve %s to sana address", str)
	addr, err = s.resolver.Resolve(str)
	if err == nil {
		log.Tracef("name resolve: resolved name %s to %s", str, addr)
		return addr, nil
	}
	return swarm.ZeroAddress, fmt.Errorf("%w: %v", errInvalidNameOrAddress, err)
}