@@ -500,8 +500,7 @@ static enum worker_result do__gvfs_config__get(struct req *req)
500
500
* write_object_file_prepare()
501
501
* write_loose_object()
502
502
*/
503
- static enum worker_result send_loose_object (const struct object_info * oi ,
504
- const struct object_id * oid ,
503
+ static enum worker_result send_loose_object (const struct object_id * oid ,
505
504
int fd )
506
505
{
507
506
#define MAX_HEADER_LEN 32
@@ -514,6 +513,44 @@ static enum worker_result send_loose_object(const struct object_info *oi,
514
513
struct git_hash_ctx c ;
515
514
int object_header_len ;
516
515
int ret ;
516
+ unsigned flags = 0 ;
517
+ void * content ;
518
+ unsigned long size ;
519
+ enum object_type type ;
520
+ struct object_info oi = OBJECT_INFO_INIT ;
521
+
522
+ /*
523
+ * Since `test-gvfs-protocol` is mocking a real GVFS server (cache or
524
+ * main), we don't want a request for a missing object to cause the
525
+ * implicit dynamic fetch mechanism to try to fault-it-in (and cause
526
+ * our call to oid_object_info_extended() to launch another instance
527
+ * of `gvfs-helper` to magically fetch it (which would connect to a
528
+ * new instance of `test-gvfs-protocol`)).
529
+ *
530
+ * Rather, we want a missing object to fail, so we can respond with
531
+ * a 404, for example.
532
+ */
533
+ flags |= OBJECT_INFO_FOR_PREFETCH ;
534
+ flags |= OBJECT_INFO_LOOKUP_REPLACE ;
535
+
536
+ oi .typep = & type ;
537
+ oi .sizep = & size ;
538
+ oi .contentp = & content ;
539
+
540
+ if (oid_object_info_extended (the_repository , oid , & oi , flags )) {
541
+ logerror ("Could not find OID: '%s'" , oid_to_hex (oid ));
542
+ free (content );
543
+ return send_http_error (1 , 404 , "Not Found" , -1 , WR_OK );
544
+ }
545
+
546
+ if (string_list_has_string (& mayhem_list , "http_404" )) {
547
+ logmayhem ("http_404" );
548
+ free (content );
549
+ return send_http_error (1 , 404 , "Not Found" , -1 , WR_MAYHEM );
550
+ }
551
+
552
+ trace2_printf ("%s: OBJECT type=%d len=%ld '%.40s'" , TR2_CAT ,
553
+ type , size , (const char * )content );
517
554
518
555
/*
519
556
* We are blending several somewhat independent concepts here:
@@ -554,6 +591,7 @@ static enum worker_result send_loose_object(const struct object_info *oi,
554
591
555
592
if (write_in_full (fd , response_header .buf , response_header .len ) < 0 ) {
556
593
logerror ("unable to write response header" );
594
+ free (content );
557
595
return WR_IO_ERROR ;
558
596
}
559
597
@@ -562,20 +600,21 @@ static enum worker_result send_loose_object(const struct object_info *oi,
562
600
563
601
if (string_list_has_string (& mayhem_list , "close_write" )) {
564
602
logmayhem ("close_write" );
603
+ free (content );
565
604
return WR_MAYHEM | WR_HANGUP ;
566
605
}
567
606
568
607
/* [1a] */
569
608
object_header_len = 1 + xsnprintf (object_header , MAX_HEADER_LEN ,
570
609
"%s %" PRIuMAX ,
571
- type_name (* oi -> typep ),
572
- (uintmax_t )* oi -> sizep );
610
+ type_name (* oi . typep ),
611
+ (uintmax_t )* oi . sizep );
573
612
574
613
/* [2] */
575
614
memset (& oid_check , 0 , sizeof (oid_check ));
576
615
the_hash_algo -> init_fn (& c );
577
616
the_hash_algo -> update_fn (& c , object_header , object_header_len );
578
- the_hash_algo -> update_fn (& c , * oi -> contentp , * oi -> sizep );
617
+ the_hash_algo -> update_fn (& c , * oi . contentp , * oi . sizep );
579
618
the_hash_algo -> final_fn (oid_check .hash , & c );
580
619
if (!oideq (oid , & oid_check ))
581
620
BUG ("send_loose_object[2]: invalid construction '%s' '%s'" ,
@@ -595,8 +634,8 @@ static enum worker_result send_loose_object(const struct object_info *oi,
595
634
the_hash_algo -> update_fn (& c , object_header , object_header_len );
596
635
597
636
/* [3, 1b, 5, 6] */
598
- stream .next_in = * oi -> contentp ;
599
- stream .avail_in = * oi -> sizep ;
637
+ stream .next_in = * oi . contentp ;
638
+ stream .avail_in = * oi . sizep ;
600
639
do {
601
640
enum worker_result wr ;
602
641
unsigned char * in0 = stream .next_in ;
@@ -606,6 +645,7 @@ static enum worker_result send_loose_object(const struct object_info *oi,
606
645
/* [5] */
607
646
wr = send_chunk (fd , compressed , stream .next_out - compressed );
608
647
if (wr & WR_STOP_THE_MUSIC ) {
648
+ free (content );
609
649
return wr ;
610
650
}
611
651
@@ -628,6 +668,7 @@ static enum worker_result send_loose_object(const struct object_info *oi,
628
668
oid_to_hex (oid ), oid_to_hex (& oid_check ));
629
669
630
670
/* [5] */
671
+ free (content );
631
672
return send_final_chunk (fd );
632
673
}
633
674
@@ -642,25 +683,6 @@ static enum worker_result send_loose_object(const struct object_info *oi,
642
683
static enum worker_result do__gvfs_objects__get (struct req * req )
643
684
{
644
685
struct object_id oid ;
645
- void * content ;
646
- unsigned long size ;
647
- enum object_type type ;
648
- struct object_info oi = OBJECT_INFO_INIT ;
649
- unsigned flags = 0 ;
650
-
651
- /*
652
- * Since `test-gvfs-protocol` is mocking a real GVFS server (cache or
653
- * main), we don't want a request for a missing object to cause the
654
- * implicit dynamic fetch mechanism to try to fault-it-in (and cause
655
- * our call to oid_object_info_extended() to launch another instance
656
- * of `gvfs-helper` to magically fetch it (which would connect to a
657
- * new instance of `test-gvfs-protocol`)).
658
- *
659
- * Rather, we want a missing object to fail, so we can respond with
660
- * a 404, for example.
661
- */
662
- flags |= OBJECT_INFO_FOR_PREFETCH ;
663
- flags |= OBJECT_INFO_LOOKUP_REPLACE ;
664
686
665
687
if (!req -> slash_args .len ||
666
688
get_oid_hex (req -> slash_args .buf , & oid )) {
@@ -671,29 +693,13 @@ static enum worker_result do__gvfs_objects__get(struct req *req)
671
693
672
694
trace2_printf ("%s: GET %s" , TR2_CAT , oid_to_hex (& oid ));
673
695
674
- oi .typep = & type ;
675
- oi .sizep = & size ;
676
- oi .contentp = & content ;
677
-
678
- if (oid_object_info_extended (the_repository , & oid , & oi , flags )) {
679
- logerror ("Could not find OID: '%s'" , oid_to_hex (& oid ));
680
- return send_http_error (1 , 404 , "Not Found" , -1 , WR_OK );
681
- }
682
-
683
- if (string_list_has_string (& mayhem_list , "http_404" )) {
684
- logmayhem ("http_404" );
685
- return send_http_error (1 , 404 , "Not Found" , -1 , WR_MAYHEM );
686
- }
687
-
688
- trace2_printf ("%s: OBJECT type=%d len=%ld '%.40s'" , TR2_CAT ,
689
- type , size , (const char * )content );
690
-
691
- return send_loose_object (& oi , & oid , 1 );
696
+ return send_loose_object (& oid , 1 );
692
697
}
693
698
694
699
static enum worker_result read_json_post_body (
695
700
struct req * req ,
696
- struct oidset * oids )
701
+ struct oidset * oids ,
702
+ int * nr_oids )
697
703
{
698
704
struct object_id oid ;
699
705
struct string_list_item * item ;
@@ -762,7 +768,8 @@ static enum worker_result read_json_post_body(
762
768
763
769
if (get_oid_hex (pstart , & oid ))
764
770
goto could_not_parse_json ;
765
- oidset_insert (oids , & oid );
771
+ if (!oidset_insert (oids , & oid ))
772
+ * nr_oids += 1 ;
766
773
trace2_printf ("%s: POST %s" , TR2_CAT , oid_to_hex (& oid ));
767
774
768
775
/* Eat trailing whitespace after trailing DQUOTE */
@@ -806,16 +813,6 @@ static enum worker_result read_json_post_body(
806
813
*
807
814
* My assumption here is that we're not testing with GBs
808
815
* of data....
809
- *
810
- * Note: The GVFS Protocol POST verb behaves like GET for
811
- * Note: non-commit objects (in that it just returns the
812
- * Note: requested object), but for commit objects POST
813
- * Note: *also* returns all trees referenced by the commit.
814
- * Note:
815
- * Note: Since the goal of this test is to confirm that
816
- * Note: gvfs-helper can request and receive a packfile
817
- * Note: *at all*, I'm not going to blur the issue and
818
- * Note: support the extra semantics for commit objects.
819
816
*/
820
817
static enum worker_result get_packfile_from_oids (
821
818
struct oidset * oids ,
@@ -905,21 +902,99 @@ static enum worker_result send_packfile_from_buffer(const struct strbuf *packfil
905
902
return wr ;
906
903
}
907
904
905
+ /*
906
+ * The GVFS Protocol POST verb behaves like GET for non-commit objects
907
+ * (in that it just returns the requested object), but for commit
908
+ * objects POST *also* returns all trees referenced by the commit.
909
+ *
910
+ * The goal of this test is to confirm that:
911
+ * [] `gvfs-helper post` can request and receive a packfile at all.
912
+ * [] `gvfs-helper post` can handle getting either a packfile or a
913
+ * loose object.
914
+ *
915
+ * Therefore, I'm not going to blur the issue and support the custom
916
+ * semantics for commit objects.
917
+ *
918
+ * If one of the OIDs is a commit, `git pack-objects` will completely
919
+ * walk the trees and blobs for it and we get that for free. This is
920
+ * good enough for our testing.
921
+ *
922
+ * TODO A proper solution would separate the commit objects and do a
923
+ * TODO `rev-list --filter=blobs:none` for them (or use the internal
924
+ * TODO list-objects API) and a regular enumeration for the non-commit
925
+ * TODO objects. And build an new oidset with union of those and then
926
+ * TODO call pack-objects on it instead.
927
+ * TODO
928
+ * TODO But that's too much trouble for now.
929
+ *
930
+ * For now, we just need to know if the post asks for a single object,
931
+ * is it a commit or non-commit. That is sufficient to know whether
932
+ * we should send a packfile or loose object.
933
+ */
934
+ static enum worker_result classify_oids_in_post (
935
+ struct oidset * oids , int nr_oids , int * need_packfile )
936
+ {
937
+ struct oidset_iter iter ;
938
+ struct object_id * oid ;
939
+ enum object_type type ;
940
+ struct object_info oi = OBJECT_INFO_INIT ;
941
+ unsigned flags = 0 ;
942
+
943
+ if (nr_oids > 1 ) {
944
+ * need_packfile = 1 ;
945
+ return WR_OK ;
946
+ }
947
+
948
+ /* disable missing-object faulting */
949
+ flags |= OBJECT_INFO_FOR_PREFETCH ;
950
+ flags |= OBJECT_INFO_LOOKUP_REPLACE ;
951
+
952
+ oi .typep = & type ;
953
+
954
+ oidset_iter_init (oids , & iter );
955
+ while ((oid = oidset_iter_next (& iter ))) {
956
+ if (!oid_object_info_extended (the_repository , oid , & oi , flags ) &&
957
+ type == OBJ_COMMIT ) {
958
+ * need_packfile = 1 ;
959
+ return WR_OK ;
960
+ }
961
+ }
962
+
963
+ * need_packfile = 0 ;
964
+ return WR_OK ;
965
+ }
966
+
908
967
static enum worker_result do__gvfs_objects__post (struct req * req )
909
968
{
910
969
struct oidset oids = OIDSET_INIT ;
911
970
struct strbuf packfile = STRBUF_INIT ;
912
971
enum worker_result wr ;
972
+ int nr_oids = 0 ;
973
+ int need_packfile = 0 ;
913
974
914
- wr = read_json_post_body (req , & oids );
975
+ wr = read_json_post_body (req , & oids , & nr_oids );
915
976
if (wr & WR_STOP_THE_MUSIC )
916
977
goto done ;
917
978
918
- wr = get_packfile_from_oids (& oids , & packfile );
979
+ wr = classify_oids_in_post (& oids , nr_oids , & need_packfile );
919
980
if (wr & WR_STOP_THE_MUSIC )
920
981
goto done ;
921
982
922
- wr = send_packfile_from_buffer (& packfile );
983
+ if (!need_packfile ) {
984
+ struct oidset_iter iter ;
985
+ struct object_id * oid ;
986
+
987
+ oidset_iter_init (& oids , & iter );
988
+ oid = oidset_iter_next (& iter );
989
+
990
+ wr = send_loose_object (oid , 1 );
991
+ } else {
992
+ wr = get_packfile_from_oids (& oids , & packfile );
993
+ if (wr & WR_STOP_THE_MUSIC )
994
+ goto done ;
995
+
996
+ wr = send_packfile_from_buffer (& packfile );
997
+ }
923
998
924
999
done :
925
1000
oidset_clear (& oids );
0 commit comments