657caea2d3
Signed-off-by: Antonio Navarro Perez <antnavper@gmail.com>
179 lines
3.6 KiB
Go
179 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"math"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"path"
|
|
"strconv"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/hanwen/go-fuse/v2/fs"
|
|
"github.com/hanwen/go-fuse/v2/fuse"
|
|
)
|
|
|
|
var _ fs.NodeOnAdder = &HttpRoot{}
|
|
var _ fs.NodeGetattrer = &HttpRoot{}
|
|
|
|
type HttpRoot struct {
|
|
fs.Inode
|
|
|
|
URLs []string
|
|
|
|
m sync.Mutex
|
|
loaded bool
|
|
}
|
|
|
|
func (r *HttpRoot) OnAdd(ctx context.Context) {
|
|
r.m.Lock()
|
|
defer r.m.Unlock()
|
|
if !r.loaded {
|
|
for _, u := range r.URLs {
|
|
fu, err := url.Parse(u)
|
|
if err != nil {
|
|
log.Println("ERROR FORMATTING URL", u)
|
|
panic("BUH")
|
|
}
|
|
|
|
ok := r.AddChild(path.Base(fu.Path), r.NewPersistentInode(ctx, NewHttpFile(u), fs.StableAttr{
|
|
Mode: syscall.S_IFREG & 07777,
|
|
}), true)
|
|
if !ok {
|
|
log.Println("Problem adding node child with name", u)
|
|
}
|
|
|
|
}
|
|
log.Println("ALL LOADED")
|
|
r.loaded = true
|
|
}
|
|
}
|
|
|
|
func (r *HttpRoot) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
|
|
log.Println("GET ATTR FOLDER")
|
|
out.Mode = syscall.S_IFDIR & 07777
|
|
|
|
return fs.OK
|
|
}
|
|
|
|
var _ fs.NodeGetattrer = &HttpFile{}
|
|
var _ fs.NodeOpener = &HttpFile{}
|
|
var _ fs.NodeReader = &HttpFile{}
|
|
|
|
type HttpFile struct {
|
|
fs.Inode
|
|
|
|
len uint64
|
|
url string
|
|
|
|
c *http.Client
|
|
}
|
|
|
|
func NewHttpFile(url string) *HttpFile {
|
|
roundTripper := &http.Transport{
|
|
Proxy: http.ProxyFromEnvironment,
|
|
DialContext: (&net.Dialer{
|
|
Timeout: 60 * time.Second,
|
|
KeepAlive: 60 * time.Second,
|
|
}).DialContext,
|
|
MaxConnsPerHost: 1,
|
|
}
|
|
httpClient := &http.Client{
|
|
Transport: roundTripper,
|
|
Timeout: 60 * time.Second,
|
|
}
|
|
|
|
return &HttpFile{c: httpClient, url: url}
|
|
}
|
|
|
|
func (f *HttpFile) getLen() (uint64, error) {
|
|
if f.len != 0 {
|
|
return f.len, nil
|
|
}
|
|
|
|
res, err := f.c.Head(f.url)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
lStr := res.Header.Get("content-length")
|
|
len, err := strconv.Atoi(lStr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
f.len = uint64(len)
|
|
|
|
return f.len, nil
|
|
}
|
|
|
|
func (f *HttpFile) Getattr(ctx context.Context, fi fs.FileHandle, out *fuse.AttrOut) syscall.Errno {
|
|
|
|
len, err := f.getLen()
|
|
if err != nil {
|
|
log.Println("error getting len", err)
|
|
return syscall.EIO
|
|
}
|
|
|
|
out.Mode = syscall.S_IFREG & 07777
|
|
out.Nlink = 1
|
|
out.Size = len
|
|
// out.Blksize
|
|
// out.Blocks
|
|
|
|
return fs.OK
|
|
}
|
|
|
|
func (f *HttpFile) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
|
|
log.Println("OPEN FILE", f.url)
|
|
|
|
return nil, fuse.FOPEN_KEEP_CACHE, fs.OK
|
|
//return nil, fuse.FOPEN_DIRECT_IO, fs.OK
|
|
}
|
|
|
|
func (f *HttpFile) Read(ctx context.Context, fh fs.FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
|
|
log.Println("READDDD FROM", off, "TO", int64(len(dest))+off, "TOTAL", len(dest))
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, f.url, nil)
|
|
if err != nil {
|
|
log.Println("error generating request from url", err, f.url)
|
|
return nil, syscall.EIO
|
|
}
|
|
|
|
l, err := f.getLen()
|
|
if err != nil {
|
|
log.Println("error getting length", err, f.url)
|
|
return nil, syscall.EIO
|
|
}
|
|
|
|
req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", off, int64(len(dest))+off))
|
|
|
|
res, err := f.c.Do(req)
|
|
if err != nil {
|
|
log.Println("error sending request", err, f.url)
|
|
return nil, syscall.EIO
|
|
}
|
|
if res.StatusCode != 200 && res.StatusCode != 206 {
|
|
log.Println("ERROR GETTING RESPONSE FROM SERVER", res.StatusCode)
|
|
return nil, syscall.EIO
|
|
}
|
|
|
|
defer res.Body.Close()
|
|
|
|
buf := dest[:int(math.Min(float64(len(dest)), float64(int64(l)-off)))]
|
|
n, err := io.ReadFull(res.Body, buf)
|
|
if err != nil && err != io.EOF {
|
|
log.Println("error readd fully data", err)
|
|
|
|
return nil, syscall.EIO
|
|
}
|
|
buf = buf[:n]
|
|
|
|
return fuse.ReadResultData(buf), fs.OK
|
|
}
|