@@ -500,8 +500,7 @@ static enum worker_result do__gvfs_config__get(struct req *req)
500500 * write_object_file_prepare()
501501 * write_loose_object()
502502 */
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 ,
505504 int fd )
506505{
507506#define MAX_HEADER_LEN 32
@@ -514,6 +513,44 @@ static enum worker_result send_loose_object(const struct object_info *oi,
514513 git_hash_ctx c ;
515514 int object_header_len ;
516515 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 );
517554
518555 /*
519556 * We are blending several somewhat independent concepts here:
@@ -554,6 +591,7 @@ static enum worker_result send_loose_object(const struct object_info *oi,
554591
555592 if (write_in_full (fd , response_header .buf , response_header .len ) < 0 ) {
556593 logerror ("unable to write response header" );
594+ free (content );
557595 return WR_IO_ERROR ;
558596 }
559597
@@ -562,20 +600,21 @@ static enum worker_result send_loose_object(const struct object_info *oi,
562600
563601 if (string_list_has_string (& mayhem_list , "close_write" )) {
564602 logmayhem ("close_write" );
603+ free (content );
565604 return WR_MAYHEM | WR_HANGUP ;
566605 }
567606
568607 /* [1a] */
569608 object_header_len = 1 + xsnprintf (object_header , MAX_HEADER_LEN ,
570609 "%s %" PRIuMAX ,
571- type_name (* oi -> typep ),
572- (uintmax_t )* oi -> sizep );
610+ type_name (* oi . typep ),
611+ (uintmax_t )* oi . sizep );
573612
574613 /* [2] */
575614 memset (& oid_check , 0 , sizeof (oid_check ));
576615 the_hash_algo -> init_fn (& c );
577616 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 );
579618 the_hash_algo -> final_fn (oid_check .hash , & c );
580619 if (!oideq (oid , & oid_check ))
581620 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,
595634 the_hash_algo -> update_fn (& c , object_header , object_header_len );
596635
597636 /* [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 ;
600639 do {
601640 enum worker_result wr ;
602641 unsigned char * in0 = stream .next_in ;
@@ -606,6 +645,7 @@ static enum worker_result send_loose_object(const struct object_info *oi,
606645 /* [5] */
607646 wr = send_chunk (fd , compressed , stream .next_out - compressed );
608647 if (wr & WR_STOP_THE_MUSIC ) {
648+ free (content );
609649 return wr ;
610650 }
611651
@@ -628,6 +668,7 @@ static enum worker_result send_loose_object(const struct object_info *oi,
628668 oid_to_hex (oid ), oid_to_hex (& oid_check ));
629669
630670 /* [5] */
671+ free (content );
631672 return send_final_chunk (fd );
632673}
633674
@@ -642,25 +683,6 @@ static enum worker_result send_loose_object(const struct object_info *oi,
642683static enum worker_result do__gvfs_objects__get (struct req * req )
643684{
644685 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 ;
664686
665687 if (!req -> slash_args .len ||
666688 get_oid_hex (req -> slash_args .buf , & oid )) {
@@ -671,29 +693,13 @@ static enum worker_result do__gvfs_objects__get(struct req *req)
671693
672694 trace2_printf ("%s: GET %s" , TR2_CAT , oid_to_hex (& oid ));
673695
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 );
692697}
693698
694699static enum worker_result read_json_post_body (
695700 struct req * req ,
696- struct oidset * oids )
701+ struct oidset * oids ,
702+ int * nr_oids )
697703{
698704 struct object_id oid ;
699705 struct string_list_item * item ;
@@ -762,7 +768,8 @@ static enum worker_result read_json_post_body(
762768
763769 if (get_oid_hex (pstart , & oid ))
764770 goto could_not_parse_json ;
765- oidset_insert (oids , & oid );
771+ if (!oidset_insert (oids , & oid ))
772+ * nr_oids += 1 ;
766773 trace2_printf ("%s: POST %s" , TR2_CAT , oid_to_hex (& oid ));
767774
768775 /* Eat trailing whitespace after trailing DQUOTE */
@@ -806,16 +813,6 @@ static enum worker_result read_json_post_body(
806813 *
807814 * My assumption here is that we're not testing with GBs
808815 * 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.
819816 */
820817static enum worker_result get_packfile_from_oids (
821818 struct oidset * oids ,
@@ -905,21 +902,99 @@ static enum worker_result send_packfile_from_buffer(const struct strbuf *packfil
905902 return wr ;
906903}
907904
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+
908967static enum worker_result do__gvfs_objects__post (struct req * req )
909968{
910969 struct oidset oids = OIDSET_INIT ;
911970 struct strbuf packfile = STRBUF_INIT ;
912971 enum worker_result wr ;
972+ int nr_oids = 0 ;
973+ int need_packfile = 0 ;
913974
914- wr = read_json_post_body (req , & oids );
975+ wr = read_json_post_body (req , & oids , & nr_oids );
915976 if (wr & WR_STOP_THE_MUSIC )
916977 goto done ;
917978
918- wr = get_packfile_from_oids (& oids , & packfile );
979+ wr = classify_oids_in_post (& oids , nr_oids , & need_packfile );
919980 if (wr & WR_STOP_THE_MUSIC )
920981 goto done ;
921982
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+ }
923998
924999done :
9251000 oidset_clear (& oids );
0 commit comments