@@ -45,8 +45,8 @@ func (rc *requestContext) ObjectLink(oid string) string {
45
45
}
46
46
47
47
// VerifyLink builds a URL for verifying the object.
48
- func (rc * requestContext ) VerifyLink () string {
49
- return setting .AppURL + path .Join (rc .User , rc .Repo + ".git" , "info/lfs/verify" )
48
+ func (rc * requestContext ) VerifyLink (oid string ) string {
49
+ return setting .AppURL + path .Join (rc .User , rc .Repo + ".git" , "info/lfs/verify" , oid )
50
50
}
51
51
52
52
// ObjectOidHandler is the main request routing entry point into LFS server functions
@@ -195,7 +195,7 @@ func getMetaHandler(ctx *context.Context) {
195
195
if ctx .Req .Method == "GET" {
196
196
json := jsoniter .ConfigCompatibleWithStandardLibrary
197
197
enc := json .NewEncoder (ctx .Resp )
198
- if err := enc .Encode (represent (rc , meta .Pointer , true , false )); err != nil {
198
+ if err := enc .Encode (buildObjectResponse (rc , meta .Pointer , true , false , 0 )); err != nil {
199
199
log .Error ("Failed to encode representation as json. Error: %v" , err )
200
200
}
201
201
}
@@ -267,7 +267,7 @@ func PostHandler(ctx *context.Context) {
267
267
268
268
json := jsoniter .ConfigCompatibleWithStandardLibrary
269
269
enc := json .NewEncoder (ctx .Resp )
270
- if err := enc .Encode (represent (rc , meta .Pointer , meta .Existing , true )); err != nil {
270
+ if err := enc .Encode (buildObjectResponse (rc , meta .Pointer , meta .Existing , true , 0 )); err != nil {
271
271
log .Error ("Failed to encode representation as json. Error: %v" , err )
272
272
}
273
273
logRequest (ctx .Req , sentStatus )
@@ -289,6 +289,17 @@ func BatchHandler(ctx *context.Context) {
289
289
290
290
bv := unpackbatch (ctx )
291
291
292
+ var isUpload bool
293
+ if bv .Operation == "upload" {
294
+ isUpload = true
295
+ } else if bv .Operation == "download" {
296
+ isUpload = false
297
+ } else {
298
+ log .Info ("Attempt to BATCH with invalid operation: %s" , bv .Operation )
299
+ writeStatus (ctx , http .StatusBadRequest )
300
+ return
301
+ }
302
+
292
303
reqCtx := & requestContext {
293
304
User : ctx .Params ("username" ),
294
305
Repo : strings .TrimSuffix (ctx .Params ("reponame" ), ".git" ),
@@ -302,12 +313,7 @@ func BatchHandler(ctx *context.Context) {
302
313
return
303
314
}
304
315
305
- requireWrite := false
306
- if bv .Operation == "upload" {
307
- requireWrite = true
308
- }
309
-
310
- if ! authenticate (ctx , repository , reqCtx .Authorization , requireWrite ) {
316
+ if ! authenticate (ctx , repository , reqCtx .Authorization , isUpload ) {
311
317
requireAuth (ctx )
312
318
return
313
319
}
@@ -316,41 +322,57 @@ func BatchHandler(ctx *context.Context) {
316
322
317
323
var responseObjects []* lfs_module.ObjectResponse
318
324
319
- // Create a response object
320
325
for _ , object := range bv .Objects {
321
326
if ! object .IsValid () {
322
- log . Info ( "Invalid LFS OID[%s] attempt to BATCH in %s/%s" , object . Oid , reqCtx . User , reqCtx . Repo )
327
+ responseObjects = append ( responseObjects , buildDownloadObjectResponse ( reqCtx , object , http . StatusUnprocessableEntity ) )
323
328
continue
324
329
}
325
330
326
- if requireWrite && setting .LFS .MaxFileSize > 0 && object .Size > setting .LFS .MaxFileSize {
327
- log .Info ("Denied LFS OID[%s] upload of size %d to %s/%s because of LFS_MAX_FILE_SIZE=%d" , object .Oid , object .Size , reqCtx .User , reqCtx .Repo , setting .LFS .MaxFileSize )
328
- writeStatus (ctx , http .StatusRequestEntityTooLarge )
331
+ exist , err := contentStore .Exists (object )
332
+ if err != nil {
333
+ log .Error ("Unable to check if LFS OID[%s] exist on %s/%s. Error: %v" , object .Oid , reqCtx .User , reqCtx .Repo , err )
334
+ writeStatus (ctx , http .StatusInternalServerError )
329
335
return
330
336
}
331
337
332
- exist , err := contentStore . Exists (object )
333
- if err != nil {
334
- log .Error ("Unable to check if LFS OID [%s] exist on %s / %s. Error: %v" , object .Oid , reqCtx .User , reqCtx .Repo , err )
338
+ meta , metaErr := repository . GetLFSMetaObjectByOid (object . Oid )
339
+ if metaErr != nil && metaErr != models . ErrLFSObjectNotExist {
340
+ log .Error ("Unable to get LFS MetaObject [%s] for %s/ %s. Error: %v" , object .Oid , reqCtx .User , reqCtx .Repo , metaErr )
335
341
writeStatus (ctx , http .StatusInternalServerError )
336
342
return
337
343
}
338
344
339
- meta , err := repository .GetLFSMetaObjectByOid (object .Oid )
340
- if err == nil { // Object is found and exists
341
- if exist {
342
- responseObjects = append (responseObjects , represent (reqCtx , meta .Pointer , true , false ))
343
- continue
345
+ var responseObject * lfs_module.ObjectResponse
346
+ if isUpload {
347
+ if ! exists && setting .LFS .MaxFileSize > 0 && object .Size > setting .LFS .MaxFileSize {
348
+ log .Info ("Denied LFS OID[%s] upload of size %d to %s/%s because of LFS_MAX_FILE_SIZE=%d" , object .Oid , object .Size , reqCtx .User , reqCtx .Repo , setting .LFS .MaxFileSize )
349
+ writeStatus (ctx , http .StatusRequestEntityTooLarge )
350
+ return
344
351
}
345
- }
346
352
347
- // Object is not found
348
- meta , err = models .NewLFSMetaObject (& models.LFSMetaObject {Pointer : object , RepositoryID : repository .ID })
349
- if err == nil {
350
- responseObjects = append (responseObjects , represent (reqCtx , meta .Pointer , meta .Existing , ! exist ))
353
+ if exists {
354
+ if meta == nil {
355
+ _ , err := models .NewLFSMetaObject (& models.LFSMetaObject {Pointer : object , RepositoryID : repository .ID })
356
+ if err != nil {
357
+ log .Error ("Unable to create LFS MetaObject [%s] for %s/%s. Error: %v" , object .Oid , reqCtx .User , reqCtx .Repo , metaErr )
358
+ writeStatus (ctx , http .StatusInternalServerError )
359
+ return
360
+ }
361
+ }
362
+ }
363
+
364
+ responseObject = buildObjectResponse (reqCtx , object , false , ! exists , 0 )
351
365
} else {
352
- log .Error ("Unable to write LFS OID[%s] size %d meta object in %v/%v to database. Error: %v" , object .Oid , object .Size , reqCtx .User , reqCtx .Repo , err )
366
+ errorCode := 0
367
+ if ! exist || meta == nil {
368
+ errorCode = http .StatusNotFound
369
+ } else if meta .Size != object .Size {
370
+ errorCode = http .StatusUnprocessableEntity
371
+ }
372
+
373
+ responseObject = buildObjectResponse (reqCtx , object , true , false , errorCode )
353
374
}
375
+ responseObjects = append (responseObjects , responseObject )
354
376
}
355
377
356
378
ctx .Resp .Header ().Set ("Content-Type" , lfs_module .MediaType )
@@ -432,44 +454,32 @@ func VerifyHandler(ctx *context.Context) {
432
454
logRequest (ctx .Req , http .StatusOK )
433
455
}
434
456
435
- // represent takes a requestContext and Meta and turns it into a ObjectResponse suitable
436
- // for json encoding
437
- func represent (rc * requestContext , pointer lfs_module.Pointer , download , upload bool ) * lfs_module.ObjectResponse {
438
- rep := & lfs_module.ObjectResponse {
439
- Pointer : pointer ,
440
- Actions : make (map [string ]* lfs_module.Link ),
441
- }
442
-
443
- header := make (map [string ]string )
444
-
445
- if rc .Authorization == "" {
446
- //https://github.com/github/git-lfs/issues/1088
447
- header ["Authorization" ] = "Authorization: Basic dummy"
457
+ func buildObjectResponse (rc * requestContext , pointer lfs_module.Pointer , download , upload bool , errorCode int ) * lfs_module.ObjectResponse {
458
+ rep := & lfs_module.ObjectResponse {Pointer : pointer }
459
+ if errorCode > 0 {
460
+ rep .Error = & ObjectError {
461
+ Code : errorCode ,
462
+ Message : http .StatusText (errorCode ),
463
+ }
448
464
} else {
449
- header ["Authorization" ] = rc .Authorization
450
- }
465
+ rep .Actions = make (map [string ]* lfs_module.Link )
451
466
452
- if download {
453
- rep .Actions ["download" ] = & lfs_module.Link {Href : rc .ObjectLink (pointer .Oid ), Header : header }
454
- }
455
-
456
- if upload {
457
- rep .Actions ["upload" ] = & lfs_module.Link {Href : rc .ObjectLink (pointer .Oid ), Header : header }
458
- }
459
-
460
- if upload && ! download {
461
- // Force client side verify action while gitea lacks proper server side verification
467
+ header := make (map [string ]string )
462
468
verifyHeader := make (map [string ]string )
463
- for k , v := range header {
464
- verifyHeader [k ] = v
469
+
470
+ if len (rc .Authorization ) > 0 {
471
+ header ["Authorization" ] = rc .Authorization
472
+ verifyHeader ["Authorization" ] = rc .Authorization
465
473
}
466
474
467
- // This is only needed to workaround https://github.com/git-lfs/git-lfs/issues/3662
468
- verifyHeader ["Accept" ] = lfs_module .MediaType
469
-
470
- rep .Actions ["verify" ] = & lfs_module.Link {Href : rc .VerifyLink (), Header : verifyHeader }
475
+ if download {
476
+ rep .Actions ["download" ] = & lfs_module.Link {Href : rc .ObjectLink (pointer .Oid ), Header : header }
477
+ }
478
+ if upload {
479
+ rep .Actions ["upload" ] = & lfs_module.Link {Href : rc .ObjectLink (pointer .Oid ), Header : header }
480
+ rep .Actions ["verify" ] = & lfs_module.Link {Href : rc .VerifyLink (pointer .Oid ), Header : verifyHeader }
481
+ }
471
482
}
472
-
473
483
return rep
474
484
}
475
485
0 commit comments