diff --git a/cmd/olliesrv/server.go b/cmd/olliesrv/server.go index e11fd9c..14a99d8 100644 --- a/cmd/olliesrv/server.go +++ b/cmd/olliesrv/server.go @@ -468,11 +468,15 @@ func read(root *fs.Tree, cs *connState, fc *plan9.Fcall, ctx context.Context) *p return errFcall(fc, "file not opened") } - if entry.BlockingReadMode() && fc.Offset > 0 { - return &plan9.Fcall{Type: plan9.Rread, Tag: fc.Tag, Count: 0} - } - - if entry.BlockingReadMode() || entry.StreamMode() { + if entry.BlockingReadMode() { + // Serve from cache if we already have content (multi-Tread paging). + if f.readCache != nil { + if int(fc.Offset) >= len(f.readCache) { + return &plan9.Fcall{Type: plan9.Rread, Tag: fc.Tag, Count: 0} + } + return readSlice(fc, f.readCache) + } + // First Tread: block for content. cs.mu.RLock() f, fidOK := cs.fids[fc.Fid] var base string @@ -480,13 +484,7 @@ func read(root *fs.Tree, cs *connState, fc *plan9.Fcall, ctx context.Context) *p base = f.waitBase } cs.mu.RUnlock() - var waitCtx context.Context - var waitCancel context.CancelFunc - if entry.StreamMode() { - waitCtx, waitCancel = context.WithCancel(ctx) - } else { - waitCtx, waitCancel = context.WithTimeout(ctx, 5*time.Second) - } + waitCtx, waitCancel := context.WithTimeout(ctx, 5*time.Second) defer waitCancel() content, nextBase, err := entry.BlockingRead(waitCtx, base) if err != nil { @@ -499,16 +497,38 @@ func read(root *fs.Tree, cs *connState, fc *plan9.Fcall, ctx context.Context) *p } cs.mu.Unlock() } - if entry.StreamMode() { - count := int(fc.Count) - if count > len(content) { - count = len(content) - } - return &plan9.Fcall{Type: plan9.Rread, Tag: fc.Tag, Count: uint32(count), Data: content[:count]} - } + f.readCache = content return readSlice(fc, content) } + if entry.StreamMode() { + cs.mu.RLock() + f, fidOK := cs.fids[fc.Fid] + var base string + if fidOK { + base = f.waitBase + } + cs.mu.RUnlock() + waitCtx, waitCancel := context.WithCancel(ctx) + defer waitCancel() + content, nextBase, err := entry.BlockingRead(waitCtx, base) + if err != nil { + return errFcall(fc, err.Error()) + } + if nextBase != "" { + cs.mu.Lock() + if f, ok := cs.fids[fc.Fid]; ok { + f.waitBase = nextBase + } + cs.mu.Unlock() + } + count := int(fc.Count) + if count > len(content) { + count = len(content) + } + return &plan9.Fcall{Type: plan9.Rread, Tag: fc.Tag, Count: uint32(count), Data: content[:count]} + } + // Normal file read: cache content on first Tread, serve via readSlice. // This handles the case where io.ReadAll issues multiple Treads at // increasing offsets (buffer starts at 512 bytes and grows).