Skip to content

Support tee response #16

@wzshiming

Description

@wzshiming

Refer to the following to integrate into the project

Use "github.com/wzshiming/ioswmr"

type teeResponse struct {
	fileInfo fs.FileInfo
	swmr     ioswmr.SWMR
	tmp      *os.File
}

func (t *teeResponse) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	name := path.Base(r.URL.Path)
	http.ServeContent(w, r, name, t.fileInfo.ModTime(), t.swmr.NewReadSeeker(0, int(t.fileInfo.Size())))
}

func (t *teeResponse) Close() error {
	err := t.swmr.Close()
	if err != nil {
		return err
	}
	err = t.tmp.Close()
	if err != nil {
		return err
	}

	os.Remove(t.tmp.Name())
	return nil
}

func (m *MirrorHandler) cacheFileTee(ctx context.Context, sourceFile, cacheFile string) (*teeResponse, error) {
	resp, info, err := httpGet(ctx, m.client(), sourceFile)
	if err != nil {
		return nil, err
	}
	defer resp.Close()

	var body io.Reader = resp

	contentLength := info.Size()
	if contentLength == 0 {
		return nil, ErrNotOK
	}

	if m.Logger != nil {
		m.Logger.Println("Tee Cache", cacheFile, contentLength)
	}

	tmp, err := os.CreateTemp("", "mirror-tee-*")
	if err != nil {
		return nil, err
	}
	fw, err := m.RemoteCache.Writer(ctx, cacheFile)
	if err != nil {
		if m.Logger != nil {
			m.Logger.Println("Cache writer error", cacheFile, contentLength, err)
		}
		return nil, err
	}

	swmr := ioswmr.NewSWMR(tmp)

	go func() {
		defer fw.Close()

		w := io.MultiWriter(swmr, fw)
		n, err := io.Copy(w, body)
		if err != nil && !errors.Is(err, io.EOF) {
			if m.Logger != nil {
				m.Logger.Println("SWMR copy error", cacheFile, contentLength, err)
			}
			_ = fw.Cancel(context.Background())
			return
		}

		if contentLength > 0 && n != contentLength {
			err = fmt.Errorf("copied %d bytes, expected %d", n, contentLength)
			if m.Logger != nil {
				m.Logger.Println("Cache copy error", cacheFile, err)
			}
			_ = fw.Cancel(context.Background())
			return
		}

		err = fw.Commit(context.Background())
		if err != nil {
			if m.Logger != nil {
				m.Logger.Println("Cache Commit error", cacheFile, err)
			}
			return
		}
	}()

	return &teeResponse{
		fileInfo: info,
		swmr:     swmr,
		tmp:      tmp,
	}, nil
}

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions