Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1d4cd78

Browse files
committed
Move file read buffering to toolsdk
1 parenta042936 commit1d4cd78

File tree

4 files changed

+75
-66
lines changed

4 files changed

+75
-66
lines changed

‎agent/files_test.go‎

Lines changed: 48 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package agent_test
22

33
import (
44
"context"
5+
"io"
56
"net/http"
67
"os"
78
"path/filepath"
@@ -125,53 +126,61 @@ func TestReadFile(t *testing.T) {
125126
error:"permission denied",
126127
},
127128
{
128-
name:"Defaults",
129-
path:filePath,
130-
bytes: []byte("content"),
129+
name:"Defaults",
130+
path:filePath,
131+
bytes: []byte("content"),
132+
mimeType:"application/octet-stream",
131133
},
132134
{
133-
name:"Limit1",
134-
path:filePath,
135-
limit:1,
136-
bytes: []byte("c"),
135+
name:"Limit1",
136+
path:filePath,
137+
limit:1,
138+
bytes: []byte("c"),
139+
mimeType:"application/octet-stream",
137140
},
138141
{
139-
name:"Offset1",
140-
path:filePath,
141-
offset:1,
142-
bytes: []byte("ontent"),
142+
name:"Offset1",
143+
path:filePath,
144+
offset:1,
145+
bytes: []byte("ontent"),
146+
mimeType:"application/octet-stream",
143147
},
144148
{
145-
name:"Limit1Offset2",
146-
path:filePath,
147-
limit:1,
148-
offset:2,
149-
bytes: []byte("n"),
149+
name:"Limit1Offset2",
150+
path:filePath,
151+
limit:1,
152+
offset:2,
153+
bytes: []byte("n"),
154+
mimeType:"application/octet-stream",
150155
},
151156
{
152-
name:"Limit7Offset0",
153-
path:filePath,
154-
limit:7,
155-
offset:0,
156-
bytes: []byte("content"),
157+
name:"Limit7Offset0",
158+
path:filePath,
159+
limit:7,
160+
offset:0,
161+
bytes: []byte("content"),
162+
mimeType:"application/octet-stream",
157163
},
158164
{
159-
name:"Limit100",
160-
path:filePath,
161-
limit:100,
162-
bytes: []byte("content"),
165+
name:"Limit100",
166+
path:filePath,
167+
limit:100,
168+
bytes: []byte("content"),
169+
mimeType:"application/octet-stream",
163170
},
164171
{
165-
name:"Offset7",
166-
path:filePath,
167-
offset:7,
168-
bytes: []byte{},
172+
name:"Offset7",
173+
path:filePath,
174+
offset:7,
175+
bytes: []byte{},
176+
mimeType:"application/octet-stream",
169177
},
170178
{
171-
name:"Offset100",
172-
path:filePath,
173-
offset:100,
174-
bytes: []byte{},
179+
name:"Offset100",
180+
path:filePath,
181+
offset:100,
182+
bytes: []byte{},
183+
mimeType:"application/octet-stream",
175184
},
176185
{
177186
name:"MimeTypePng",
@@ -188,20 +197,19 @@ func TestReadFile(t *testing.T) {
188197
ctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitLong)
189198
defercancel()
190199

191-
b,mimeType,err:=conn.ReadFile(ctx,tt.path,tt.offset,tt.limit)
200+
reader,mimeType,err:=conn.ReadFile(ctx,tt.path,tt.offset,tt.limit)
192201
iftt.errCode!=0 {
193202
require.Error(t,err)
194203
cerr:=coderdtest.SDKError(t,err)
195204
require.Contains(t,cerr.Error(),tt.error)
196205
require.Equal(t,tt.errCode,cerr.StatusCode())
197206
}else {
198207
require.NoError(t,err)
199-
require.Equal(t,tt.bytes,b)
200-
expectedMimeType:=tt.mimeType
201-
ifexpectedMimeType=="" {
202-
expectedMimeType="application/octet-stream"
203-
}
204-
require.Equal(t,expectedMimeType,mimeType)
208+
deferreader.Close()
209+
bytes,err:=io.ReadAll(reader)
210+
require.NoError(t,err)
211+
require.Equal(t,tt.bytes,bytes)
212+
require.Equal(t,tt.mimeType,mimeType)
205213
}
206214
})
207215
}

‎codersdk/toolsdk/toolsdk.go‎

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,8 @@ type WorkspaceReadFileResponse struct {
13801380
MimeTypestring`json:"mimeType"`
13811381
}
13821382

1383+
constmaxFileLimit=1<<20// 1MiB
1384+
13831385
varWorkspaceReadFile=Tool[WorkspaceReadFileArgs,WorkspaceReadFileResponse]{
13841386
Tool: aisdk.Tool{
13851387
Name:ToolNameWorkspaceReadFile,
@@ -1414,12 +1416,28 @@ var WorkspaceReadFile = Tool[WorkspaceReadFileArgs, WorkspaceReadFileResponse]{
14141416
}
14151417
deferconn.Close()
14161418

1417-
bytes,mimeType,err:=conn.ReadFile(ctx,args.Path,args.Offset,args.Limit)
1419+
// Ideally we could stream this all the way back, but it looks like the MCP
1420+
// interfaces only allow returning full responses which means the whole
1421+
// thing has to be read into memory. So, add a maximum to compensate.
1422+
limit:=args.Limit
1423+
iflimit==0 {
1424+
limit=maxFileLimit
1425+
}elseiflimit>maxFileLimit {
1426+
returnWorkspaceReadFileResponse{},xerrors.Errorf("limit must be %d or less, got %d",maxFileLimit,limit)
1427+
}
1428+
1429+
reader,mimeType,err:=conn.ReadFile(ctx,args.Path,args.Offset,args.Limit)
14181430
iferr!=nil {
14191431
returnWorkspaceReadFileResponse{},err
14201432
}
1433+
deferreader.Close()
1434+
1435+
bs,err:=io.ReadAll(reader)
1436+
iferr!=nil {
1437+
returnWorkspaceReadFileResponse{},xerrors.Errorf("read response body: %w",err)
1438+
}
14211439

1422-
returnWorkspaceReadFileResponse{Content:bytes,MimeType:mimeType},nil
1440+
returnWorkspaceReadFileResponse{Content:bs,MimeType:mimeType},nil
14231441
},
14241442
}
14251443

‎codersdk/workspacesdk/agentconn.go‎

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ type AgentConn interface {
6060
PrometheusMetrics(ctx context.Context) ([]byte,error)
6161
ReconnectingPTY(ctx context.Context,id uuid.UUID,heightuint16,widthuint16,commandstring,initOpts...AgentReconnectingPTYInitOption) (net.Conn,error)
6262
RecreateDevcontainer(ctx context.Context,devcontainerIDstring) (codersdk.Response,error)
63-
ReadFile(ctx context.Context,pathstring,offset,limitint64) ([]byte,string,error)
63+
ReadFile(ctx context.Context,pathstring,offset,limitint64) (io.ReadCloser,string,error)
6464
SSH(ctx context.Context) (*gonet.TCPConn,error)
6565
SSHClient(ctx context.Context) (*ssh.Client,error)
6666
SSHClientOnPort(ctx context.Context,portuint16) (*ssh.Client,error)
@@ -477,43 +477,26 @@ func (c *agentConn) RecreateDevcontainer(ctx context.Context, devcontainerID str
477477
returnm,nil
478478
}
479479

480-
constmaxFileLimit=1<<20// 1MiB
481-
482-
// ReadFile reads from a file from the workspace, returning the file's
483-
// (potentially partial) bytes and the mime type.
484-
func (c*agentConn)ReadFile(ctx context.Context,pathstring,offset,limitint64) ([]byte,string,error) {
480+
// ReadFile reads from a file from the workspace, returning a file reader and
481+
// the mime type.
482+
func (c*agentConn)ReadFile(ctx context.Context,pathstring,offset,limitint64) (io.ReadCloser,string,error) {
485483
ctx,span:=tracing.StartSpan(ctx)
486484
deferspan.End()
487485

488-
// Ideally we could stream this all the way back, but it looks like the MCP
489-
// interfaces only allow returning full responses which means the whole thing
490-
// has to be read into memory. So, add a maximum to compensate.
491-
iflimit==0 {
492-
limit=maxFileLimit
493-
}elseiflimit>maxFileLimit {
494-
returnnil,"",xerrors.Errorf("limit must be %d or less, got %d",maxFileLimit,limit)
495-
}
496-
497486
res,err:=c.apiRequest(ctx,http.MethodGet,fmt.Sprintf("/api/v0/read-file?path=%s&offset=%d&limit=%d",path,offset,limit),nil)
498487
iferr!=nil {
499488
returnnil,"",xerrors.Errorf("do request: %w",err)
500489
}
501-
deferres.Body.Close()
502490
ifres.StatusCode!=http.StatusOK {
503491
returnnil,"",codersdk.ReadBodyAsError(res)
504492
}
505493

506-
bs,err:=io.ReadAll(res.Body)
507-
iferr!=nil {
508-
returnnil,"",xerrors.Errorf("read response body: %w",err)
509-
}
510-
511494
mimeType:=res.Header.Get("Content-Type")
512495
ifmimeType=="" {
513496
mimeType="application/octet-stream"
514497
}
515498

516-
returnbs,mimeType,nil
499+
returnres.Body,mimeType,nil
517500
}
518501

519502
// apiRequest makes a request to the workspace agent's HTTP API server.

‎codersdk/workspacesdk/agentconnmock/agentconnmock.go‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp