@@ -38,15 +38,24 @@ func TestHttpIpfsHandler(t *testing.T) {
3838 name string
3939 path string
4040 accept string
41+ method string
4142 expectedStatusCode int
4243 expectedBody string
44+ checkHeaders bool
4345 }{
4446 {
4547 name : "404" ,
4648 path : "/not here" ,
4749 expectedStatusCode : http .StatusNotFound ,
4850 expectedBody : "not found" ,
4951 },
52+ {
53+ name : "HEAD 404" ,
54+ path : "/not here" ,
55+ method : http .MethodHead ,
56+ expectedStatusCode : http .StatusNotFound ,
57+ expectedBody : "" , // HEAD should have no body
58+ },
5059 {
5160 name : "bad cid" ,
5261 path : "/ipfs/foobarbaz" ,
@@ -96,7 +105,11 @@ func TestHttpIpfsHandler(t *testing.T) {
96105 } {
97106 t .Run (testCase .name , func (t * testing.T ) {
98107 req := require .New (t )
99- request , err := http .NewRequest (http .MethodGet , testServer .URL + testCase .path , nil )
108+ method := testCase .method
109+ if method == "" {
110+ method = http .MethodGet
111+ }
112+ request , err := http .NewRequest (method , testServer .URL + testCase .path , nil )
100113 req .NoError (err )
101114 if testCase .accept != "" {
102115 request .Header .Set ("Accept" , testCase .accept )
@@ -107,6 +120,150 @@ func TestHttpIpfsHandler(t *testing.T) {
107120 body , err := io .ReadAll (res .Body )
108121 req .NoError (err )
109122 req .Equal (testCase .expectedBody , string (body ))
123+
124+ // For HEAD requests, verify headers are set but body is empty
125+ if method == http .MethodHead && testCase .checkHeaders {
126+ req .NotEmpty (res .Header .Get ("Content-Type" ))
127+ req .NotEmpty (res .Header .Get ("Etag" ))
128+ req .Empty (string (body ))
129+ }
130+ })
131+ }
132+ }
133+
134+ func TestProbePathAndHeadRequests (t * testing.T ) {
135+ req := require .New (t )
136+
137+ // Set up a basic link system with some test data
138+ lsys := cidlink .DefaultLinkSystem ()
139+ store := & memstore.Store {}
140+ lsys .SetReadStorage (& trustlesstestutil.CorrectedMemStore {ParentStore : store })
141+ lsys .SetWriteStorage (store )
142+
143+ // Create a simple test block
144+ testData := []byte ("test content" )
145+ testLink , err := lsys .Store (linking.LinkContext {}, cidlink.LinkPrototype {Prefix : cid.Prefix {
146+ Version : 1 ,
147+ Codec : cid .Raw ,
148+ MhType : multihash .SHA2_256 ,
149+ MhLength : - 1 ,
150+ }}, basicnode .NewBytes (testData ))
151+ req .NoError (err )
152+ testCid := testLink .(cidlink.Link ).Cid
153+
154+ handler := frisbii .NewHttpIpfs (context .Background (), lsys )
155+ testServer := httptest .NewServer (handler )
156+ defer testServer .Close ()
157+
158+ testCases := []struct {
159+ name string
160+ method string
161+ path string
162+ accept string
163+ expectedStatusCode int
164+ expectEmptyBody bool
165+ checkHeaders bool
166+ }{
167+ // Probe path tests - bafkqaaa is the special probe CID
168+ {
169+ name : "GET probe path with raw format" ,
170+ method : http .MethodGet ,
171+ path : "/ipfs/bafkqaaa" ,
172+ accept : trustlesshttp .MimeTypeRaw ,
173+ expectedStatusCode : http .StatusOK ,
174+ expectEmptyBody : true , // Identity CID has empty content
175+ checkHeaders : true ,
176+ },
177+ {
178+ name : "HEAD probe path with raw format" ,
179+ method : http .MethodHead ,
180+ path : "/ipfs/bafkqaaa" ,
181+ accept : trustlesshttp .MimeTypeRaw ,
182+ expectedStatusCode : http .StatusOK ,
183+ expectEmptyBody : true ,
184+ checkHeaders : true ,
185+ },
186+ {
187+ name : "GET probe path with CAR format" ,
188+ method : http .MethodGet ,
189+ path : "/ipfs/bafkqaaa" ,
190+ accept : trustlesshttp .MimeTypeCar ,
191+ expectedStatusCode : http .StatusOK ,
192+ expectEmptyBody : false , // CAR will have header
193+ checkHeaders : true ,
194+ },
195+ {
196+ name : "HEAD probe path with CAR format" ,
197+ method : http .MethodHead ,
198+ path : "/ipfs/bafkqaaa" ,
199+ accept : trustlesshttp .MimeTypeCar ,
200+ expectedStatusCode : http .StatusOK ,
201+ expectEmptyBody : true , // HEAD always has empty body
202+ checkHeaders : true ,
203+ },
204+ // Regular CID HEAD tests
205+ {
206+ name : "HEAD existing block with raw format" ,
207+ method : http .MethodHead ,
208+ path : "/ipfs/" + testCid .String (),
209+ accept : trustlesshttp .MimeTypeRaw ,
210+ expectedStatusCode : http .StatusOK ,
211+ expectEmptyBody : true ,
212+ checkHeaders : true ,
213+ },
214+ {
215+ name : "HEAD existing block with CAR format" ,
216+ method : http .MethodHead ,
217+ path : "/ipfs/" + testCid .String (),
218+ accept : trustlesshttp .MimeTypeCar ,
219+ expectedStatusCode : http .StatusOK ,
220+ expectEmptyBody : true ,
221+ checkHeaders : true ,
222+ },
223+ {
224+ name : "HEAD non-existing block" ,
225+ method : http .MethodHead ,
226+ path : "/ipfs/bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi" ,
227+ accept : trustlesshttp .MimeTypeRaw ,
228+ expectedStatusCode : http .StatusInternalServerError ,
229+ expectEmptyBody : true ,
230+ },
231+ }
232+
233+ for _ , tc := range testCases {
234+ t .Run (tc .name , func (t * testing.T ) {
235+ request , err := http .NewRequest (tc .method , testServer .URL + tc .path , nil )
236+ req .NoError (err )
237+ if tc .accept != "" {
238+ request .Header .Set ("Accept" , tc .accept )
239+ }
240+
241+ res , err := http .DefaultClient .Do (request )
242+ req .NoError (err )
243+ req .Equal (tc .expectedStatusCode , res .StatusCode )
244+
245+ body , err := io .ReadAll (res .Body )
246+ req .NoError (err )
247+
248+ if tc .expectEmptyBody {
249+ req .Empty (body , "Expected empty body but got: %s" , string (body ))
250+ }
251+
252+ if tc .checkHeaders && tc .expectedStatusCode == http .StatusOK {
253+ req .NotEmpty (res .Header .Get ("Content-Type" ), "Content-Type header should be set" )
254+ req .NotEmpty (res .Header .Get ("Etag" ), "Etag header should be set" )
255+ req .NotEmpty (res .Header .Get ("X-Ipfs-Path" ), "X-Ipfs-Path header should be set" )
256+ req .Equal ("Accept, Accept-Encoding" , res .Header .Get ("Vary" ), "Vary header should be set" )
257+ }
258+
259+ // Special check for probe CAR response
260+ if tc .path == "/ipfs/bafkqaaa" && tc .accept == trustlesshttp .MimeTypeCar && tc .method == http .MethodGet {
261+ // Parse the CAR to verify it's valid and has the probe CID as root
262+ reader , err := car .NewBlockReader (strings .NewReader (string (body )))
263+ req .NoError (err )
264+ req .Equal (1 , len (reader .Roots ))
265+ req .Equal ("bafkqaaa" , reader .Roots [0 ].String ())
266+ }
110267 })
111268 }
112269}
0 commit comments