Skip to content

Commit 5ffc1d1

Browse files
committed
Add X-Repo-Commit header from cache or fetched data for HuggingFace requests
1 parent 499628b commit 5ffc1d1

1 file changed

Lines changed: 165 additions & 23 deletions

File tree

mirror.go

Lines changed: 165 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"crypto/md5"
66
"encoding/hex"
7+
"encoding/json"
78
"errors"
89
"fmt"
910
"io"
@@ -209,24 +210,157 @@ func (m *MirrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
209210
}
210211

211212
func (m *MirrorHandler) responseCache(rw http.ResponseWriter, r *http.Request, file string, info sss.FileInfo) {
212-
m.setHuggingFaceHeaders(rw, r)
213+
if err := m.setHuggingFaceHeaders(rw, r); err != nil {
214+
m.errorResponse(rw, r, err)
215+
return
216+
}
213217
if m.NoRedirect {
214218
m.serveFromCache(rw, r, file, info)
215219
} else {
216220
m.redirect(rw, r, file, info)
217221
}
218222
}
219223

220-
func (m *MirrorHandler) setHuggingFaceHeaders(rw http.ResponseWriter, r *http.Request) {
224+
func (m *MirrorHandler) setHuggingFaceHeaders(rw http.ResponseWriter, r *http.Request) error {
221225
// Special handling for huggingface.co to add X-Repo-Commit header with HF_ENDPOINT
222-
if r.Host == "huggingface.co" {
223-
sl := strings.SplitN(r.URL.Path, "/", 6)
224-
if len(sl) == 6 &&
225-
sl[3] == "resolve" &&
226-
len(sl[4]) == 40 {
227-
rw.Header().Set("X-Repo-Commit", sl[4])
226+
if m.RemoteCache == nil {
227+
return nil
228+
}
229+
230+
if r.Host != "huggingface.co" {
231+
return nil
232+
}
233+
234+
rIndex := strings.Index(r.URL.Path, "/resolve/")
235+
if rIndex < 0 {
236+
return nil
237+
}
238+
239+
repoRef := r.URL.Path[rIndex+9:]
240+
slashIndex := strings.Index(repoRef, "/")
241+
if slashIndex >= 0 {
242+
repoRef = repoRef[:slashIndex]
243+
}
244+
245+
if len(repoRef) == 40 {
246+
rw.Header().Set("X-Repo-Commit", repoRef)
247+
return nil
248+
}
249+
250+
repoName := r.URL.Path[1:rIndex]
251+
repoType := "models"
252+
if strings.HasPrefix(repoName, "datasets/") {
253+
repoType = "datasets"
254+
repoName = strings.TrimPrefix(repoName, "datasets/")
255+
} else if strings.HasPrefix(repoName, "spaces/") {
256+
repoType = "spaces"
257+
repoName = strings.TrimPrefix(repoName, "spaces/")
258+
}
259+
260+
file := fmt.Sprintf("huggingface.co/api/%s/%s/revision/%s", repoType, repoName, repoRef)
261+
if m.Logger != nil {
262+
m.Logger.Println("HF Repo Info", file)
263+
}
264+
265+
ctx := r.Context()
266+
doneCache, err := m.uniqueLock(ctx, file)
267+
if err != nil {
268+
return err
269+
}
270+
271+
setFromCache := func() {
272+
fr, err := m.RemoteCache.Reader(ctx, file)
273+
if err != nil {
274+
if m.Logger != nil {
275+
m.Logger.Println("HF Repo Reader error", file, err)
276+
}
277+
return
278+
}
279+
defer fr.Close()
280+
281+
var sha struct {
282+
Sha string `json:"sha"`
283+
}
284+
285+
_ = json.NewDecoder(fr).Decode(&sha)
286+
if sha.Sha != "" {
287+
rw.Header().Set("X-Repo-Commit", sha.Sha)
228288
}
229289
}
290+
291+
cacheInfo, err := m.RemoteCache.Stat(ctx, file)
292+
if err != nil {
293+
if errors.Is(err, context.Canceled) {
294+
doneCache()
295+
return err
296+
}
297+
if m.Logger != nil {
298+
m.Logger.Println("HF Cache Miss", file, err)
299+
}
300+
} else {
301+
if m.Logger != nil {
302+
m.Logger.Println("HF Cache Hit", file)
303+
}
304+
305+
if m.CIDNClient == nil {
306+
sourceCtx, sourceCancel := context.WithTimeout(ctx, m.CheckSyncTimeout)
307+
sourceInfo, err := httpHead(sourceCtx, m.client(), r.URL.String())
308+
if err != nil {
309+
sourceCancel()
310+
if m.Logger != nil {
311+
m.Logger.Println("HF Source Miss", file, err)
312+
}
313+
setFromCache()
314+
doneCache()
315+
return nil
316+
}
317+
sourceCancel()
318+
319+
sourceSize := sourceInfo.Size()
320+
cacheSize := cacheInfo.Size()
321+
if cacheSize != 0 && (sourceSize <= 0 || sourceSize == cacheSize) {
322+
setFromCache()
323+
doneCache()
324+
return nil
325+
}
326+
327+
if m.Logger != nil {
328+
m.Logger.Println("HF Source change", file, sourceSize, cacheSize)
329+
}
330+
}
331+
332+
}
333+
334+
errCh := make(chan error, 1)
335+
336+
go func() {
337+
defer doneCache()
338+
url := "https://" + file
339+
errCh <- m.cacheFile(context.Background(), url, file)
340+
}()
341+
342+
select {
343+
case <-ctx.Done():
344+
return err
345+
case err := <-errCh:
346+
if err != nil {
347+
if cacheInfo != nil {
348+
if m.Logger != nil {
349+
m.Logger.Println("HF Recache error", file, err)
350+
}
351+
setFromCache()
352+
return nil
353+
}
354+
355+
if errors.Is(err, ErrNotOK) {
356+
return nil
357+
}
358+
return err
359+
}
360+
setFromCache()
361+
}
362+
363+
return nil
230364
}
231365

232366
func (m *MirrorHandler) setHeaders(rw http.ResponseWriter, info sss.FileInfo) {
@@ -335,26 +469,33 @@ func (m *MirrorHandler) serveFromCache(rw http.ResponseWriter, r *http.Request,
335469
}
336470
}
337471

338-
func (m *MirrorHandler) cacheResponse(w http.ResponseWriter, r *http.Request) {
339-
ctx := r.Context()
340-
file := path.Join(r.Host, r.URL.EscapedPath())
341-
472+
func (m *MirrorHandler) uniqueLock(ctx context.Context, file string) (func(), error) {
342473
closeValue, loaded := m.mut.LoadOrStore(file, make(chan struct{}))
343474
closeCh := closeValue.(chan struct{})
344475
for loaded {
345476
select {
346477
case <-ctx.Done():
347-
m.errorResponse(w, r, ctx.Err())
348-
return
478+
return nil, ctx.Err()
349479
case <-closeCh:
350480
}
351481
closeValue, loaded = m.mut.LoadOrStore(file, make(chan struct{}))
352482
closeCh = closeValue.(chan struct{})
353483
}
354484

355-
doneCache := func() {
485+
return func() {
356486
m.mut.Delete(file)
357487
close(closeCh)
488+
}, nil
489+
}
490+
491+
func (m *MirrorHandler) cacheResponse(w http.ResponseWriter, r *http.Request) {
492+
ctx := r.Context()
493+
file := path.Join(r.Host, r.URL.EscapedPath())
494+
495+
doneCache, err := m.uniqueLock(ctx, file)
496+
if err != nil {
497+
m.errorResponse(w, r, err)
498+
return
358499
}
359500

360501
cacheInfo, err := m.RemoteCache.Stat(ctx, file)
@@ -410,13 +551,7 @@ func (m *MirrorHandler) cacheResponse(w http.ResponseWriter, r *http.Request) {
410551

411552
go func() {
412553
defer doneCache()
413-
var err error
414-
if m.CIDNClient != nil {
415-
err = m.cacheFileWithCIDN(context.Background(), r.URL.String(), file)
416-
} else {
417-
err = m.cacheFile(context.Background(), r.URL.String(), file)
418-
}
419-
errCh <- err
554+
errCh <- m.cacheFile(context.Background(), r.URL.String(), file)
420555
}()
421556

422557
select {
@@ -465,6 +600,13 @@ func formatGroup(s string) string {
465600
return u.String()
466601
}
467602

603+
func (m *MirrorHandler) cacheFile(ctx context.Context, sourceFile, cacheFile string) error {
604+
if m.CIDNClient != nil {
605+
return m.cacheFileWithCIDN(context.Background(), sourceFile, cacheFile)
606+
}
607+
return m.cacheFileDirect(context.Background(), sourceFile, cacheFile)
608+
}
609+
468610
func (m *MirrorHandler) cacheFileWithCIDN(ctx context.Context, sourceFile, cacheFile string) error {
469611
blobs := m.CIDNClient.TaskV1alpha1().Blobs()
470612
name := getBlobName(cacheFile)
@@ -601,7 +743,7 @@ func (m *MirrorHandler) cacheFileWithCIDN(ctx context.Context, sourceFile, cache
601743
}
602744
}
603745

604-
func (m *MirrorHandler) cacheFile(ctx context.Context, sourceFile, cacheFile string) error {
746+
func (m *MirrorHandler) cacheFileDirect(ctx context.Context, sourceFile, cacheFile string) error {
605747
resp, info, err := httpGet(ctx, m.client(), sourceFile)
606748
if err != nil {
607749
return err

0 commit comments

Comments
 (0)