@@ -492,8 +492,7 @@ static enum worker_result do__gvfs_config__get(struct req *req)
492492 * write_object_file_prepare()
493493 * write_loose_object()
494494 */
495- static enum worker_result send_loose_object (const struct object_info * oi ,
496- const struct object_id * oid ,
495+ static enum worker_result send_loose_object (const struct object_id * oid ,
497496 int fd )
498497{
499498#define MAX_HEADER_LEN 32
@@ -506,6 +505,42 @@ static enum worker_result send_loose_object(const struct object_info *oi,
506505 git_hash_ctx c ;
507506 int object_header_len ;
508507 int ret ;
508+ unsigned flags = 0 ;
509+ void * content ;
510+ unsigned long size ;
511+ enum object_type type ;
512+ struct object_info oi = OBJECT_INFO_INIT ;
513+
514+ /*
515+ * Since `test-gvfs-protocol` is mocking a real GVFS server (cache or
516+ * main), we don't want a request for a missing object to cause the
517+ * implicit dynamic fetch mechanism to try to fault-it-in (and cause
518+ * our call to oid_object_info_extended() to launch another instance
519+ * of `gvfs-helper` to magically fetch it (which would connect to a
520+ * new instance of `test-gvfs-protocol`)).
521+ *
522+ * Rather, we want a missing object to fail, so we can respond with
523+ * a 404, for example.
524+ */
525+ flags |= OBJECT_INFO_FOR_PREFETCH ;
526+ flags |= OBJECT_INFO_LOOKUP_REPLACE ;
527+
528+ oi .typep = & type ;
529+ oi .sizep = & size ;
530+ oi .contentp = & content ;
531+
532+ if (oid_object_info_extended (the_repository , oid , & oi , flags )) {
533+ logerror ("Could not find OID: '%s'" , oid_to_hex (oid ));
534+ return send_http_error (1 , 404 , "Not Found" , -1 , WR_OK );
535+ }
536+
537+ if (string_list_has_string (& mayhem_list , "http_404" )) {
538+ logmayhem ("http_404" );
539+ return send_http_error (1 , 404 , "Not Found" , -1 , WR_MAYHEM );
540+ }
541+
542+ trace2_printf ("%s: OBJECT type=%d len=%ld '%.40s'" , TR2_CAT ,
543+ type , size , (const char * )content );
509544
510545 /*
511546 * We are blending several somewhat independent concepts here:
@@ -560,13 +595,13 @@ static enum worker_result send_loose_object(const struct object_info *oi,
560595 /* [1a] */
561596 object_header_len = 1 + xsnprintf (object_header , MAX_HEADER_LEN ,
562597 "%s %" PRIuMAX ,
563- type_name (* oi -> typep ),
564- (uintmax_t )* oi -> sizep );
598+ type_name (* oi . typep ),
599+ (uintmax_t )* oi . sizep );
565600
566601 /* [2] */
567602 the_hash_algo -> init_fn (& c );
568603 the_hash_algo -> update_fn (& c , object_header , object_header_len );
569- the_hash_algo -> update_fn (& c , * oi -> contentp , * oi -> sizep );
604+ the_hash_algo -> update_fn (& c , * oi . contentp , * oi . sizep );
570605 the_hash_algo -> final_fn (oid_check .hash , & c );
571606 if (!oideq (oid , & oid_check ))
572607 BUG ("send_loose_object[2]: invalid construction '%s' '%s'" ,
@@ -586,8 +621,8 @@ static enum worker_result send_loose_object(const struct object_info *oi,
586621 the_hash_algo -> update_fn (& c , object_header , object_header_len );
587622
588623 /* [3, 1b, 5, 6] */
589- stream .next_in = * oi -> contentp ;
590- stream .avail_in = * oi -> sizep ;
624+ stream .next_in = * oi . contentp ;
625+ stream .avail_in = * oi . sizep ;
591626 do {
592627 enum worker_result wr ;
593628 unsigned char * in0 = stream .next_in ;
@@ -632,25 +667,6 @@ static enum worker_result send_loose_object(const struct object_info *oi,
632667static enum worker_result do__gvfs_objects__get (struct req * req )
633668{
634669 struct object_id oid ;
635- void * content ;
636- unsigned long size ;
637- enum object_type type ;
638- struct object_info oi = OBJECT_INFO_INIT ;
639- unsigned flags = 0 ;
640-
641- /*
642- * Since `test-gvfs-protocol` is mocking a real GVFS server (cache or
643- * main), we don't want a request for a missing object to cause the
644- * implicit dynamic fetch mechanism to try to fault-it-in (and cause
645- * our call to oid_object_info_extended() to launch another instance
646- * of `gvfs-helper` to magically fetch it (which would connect to a
647- * new instance of `test-gvfs-protocol`)).
648- *
649- * Rather, we want a missing object to fail, so we can respond with
650- * a 404, for example.
651- */
652- flags |= OBJECT_INFO_FOR_PREFETCH ;
653- flags |= OBJECT_INFO_LOOKUP_REPLACE ;
654670
655671 if (!req -> slash_args .len ||
656672 get_oid_hex (req -> slash_args .buf , & oid )) {
@@ -661,29 +677,13 @@ static enum worker_result do__gvfs_objects__get(struct req *req)
661677
662678 trace2_printf ("%s: GET %s" , TR2_CAT , oid_to_hex (& oid ));
663679
664- oi .typep = & type ;
665- oi .sizep = & size ;
666- oi .contentp = & content ;
667-
668- if (oid_object_info_extended (the_repository , & oid , & oi , flags )) {
669- logerror ("Could not find OID: '%s'" , oid_to_hex (& oid ));
670- return send_http_error (1 , 404 , "Not Found" , -1 , WR_OK );
671- }
672-
673- if (string_list_has_string (& mayhem_list , "http_404" )) {
674- logmayhem ("http_404" );
675- return send_http_error (1 , 404 , "Not Found" , -1 , WR_MAYHEM );
676- }
677-
678- trace2_printf ("%s: OBJECT type=%d len=%ld '%.40s'" , TR2_CAT ,
679- type , size , (const char * )content );
680-
681- return send_loose_object (& oi , & oid , 1 );
680+ return send_loose_object (& oid , 1 );
682681}
683682
684683static enum worker_result read_json_post_body (
685684 struct req * req ,
686- struct oidset * oids )
685+ struct oidset * oids ,
686+ int * nr_oids )
687687{
688688 struct object_id oid ;
689689 struct string_list_item * item ;
@@ -752,7 +752,8 @@ static enum worker_result read_json_post_body(
752752
753753 if (get_oid_hex (pstart , & oid ))
754754 goto could_not_parse_json ;
755- oidset_insert (oids , & oid );
755+ if (!oidset_insert (oids , & oid ))
756+ * nr_oids += 1 ;
756757 trace2_printf ("%s: POST %s" , TR2_CAT , oid_to_hex (& oid ));
757758
758759 /* Eat trailing whitespace after trailing DQUOTE */
@@ -796,16 +797,6 @@ static enum worker_result read_json_post_body(
796797 *
797798 * My assumption here is that we're not testing with GBs
798799 * of data....
799- *
800- * Note: The GVFS Protocol POST verb behaves like GET for
801- * Note: non-commit objects (in that it just returns the
802- * Note: requested object), but for commit objects POST
803- * Note: *also* returns all trees referenced by the commit.
804- * Note:
805- * Note: Since the goal of this test is to confirm that
806- * Note: gvfs-helper can request and receive a packfile
807- * Note: *at all*, I'm not going to blur the issue and
808- * Note: support the extra semantics for commit objects.
809800 */
810801static enum worker_result get_packfile_from_oids (
811802 struct oidset * oids ,
@@ -895,21 +886,99 @@ static enum worker_result send_packfile_from_buffer(const struct strbuf *packfil
895886 return wr ;
896887}
897888
889+ /*
890+ * The GVFS Protocol POST verb behaves like GET for non-commit objects
891+ * (in that it just returns the requested object), but for commit
892+ * objects POST *also* returns all trees referenced by the commit.
893+ *
894+ * The goal of this test is to confirm that:
895+ * [] `gvfs-helper post` can request and receive a packfile at all.
896+ * [] `gvfs-helper post` can handle getting either a packfile or a
897+ * loose object.
898+ *
899+ * Therefore, I'm not going to blur the issue and support the custom
900+ * semantics for commit objects.
901+ *
902+ * If one of the OIDs is a commit, `git pack-objects` will completely
903+ * walk the trees and blobs for it and we get that for free. This is
904+ * good enough for our testing.
905+ *
906+ * TODO A proper solution would separate the commit objects and do a
907+ * TODO `rev-list --filter=blobs:none` for them (or use the internal
908+ * TODO list-objects API) and a regular enumeration for the non-commit
909+ * TODO objects. And build an new oidset with union of those and then
910+ * TODO call pack-objects on it instead.
911+ * TODO
912+ * TODO But that's too much trouble for now.
913+ *
914+ * For now, we just need to know if the post asks for a single object,
915+ * is it a commit or non-commit. That is sufficient to know whether
916+ * we should send a packfile or loose object.
917+ */
918+ static enum worker_result classify_oids_in_post (
919+ struct oidset * oids , int nr_oids , int * need_packfile )
920+ {
921+ struct oidset_iter iter ;
922+ struct object_id * oid ;
923+ enum object_type type ;
924+ struct object_info oi = OBJECT_INFO_INIT ;
925+ unsigned flags = 0 ;
926+
927+ if (nr_oids > 1 ) {
928+ * need_packfile = 1 ;
929+ return WR_OK ;
930+ }
931+
932+ /* disable missing-object faulting */
933+ flags |= OBJECT_INFO_FOR_PREFETCH ;
934+ flags |= OBJECT_INFO_LOOKUP_REPLACE ;
935+
936+ oi .typep = & type ;
937+
938+ oidset_iter_init (oids , & iter );
939+ while ((oid = oidset_iter_next (& iter ))) {
940+ if (!oid_object_info_extended (the_repository , oid , & oi , flags ) &&
941+ type == OBJ_COMMIT ) {
942+ * need_packfile = 1 ;
943+ return WR_OK ;
944+ }
945+ }
946+
947+ * need_packfile = 0 ;
948+ return WR_OK ;
949+ }
950+
898951static enum worker_result do__gvfs_objects__post (struct req * req )
899952{
900953 struct oidset oids = OIDSET_INIT ;
901954 struct strbuf packfile = STRBUF_INIT ;
902955 enum worker_result wr ;
956+ int nr_oids = 0 ;
957+ int need_packfile = 0 ;
903958
904- wr = read_json_post_body (req , & oids );
959+ wr = read_json_post_body (req , & oids , & nr_oids );
905960 if (wr & WR_STOP_THE_MUSIC )
906961 goto done ;
907962
908- wr = get_packfile_from_oids (& oids , & packfile );
963+ wr = classify_oids_in_post (& oids , nr_oids , & need_packfile );
909964 if (wr & WR_STOP_THE_MUSIC )
910965 goto done ;
911966
912- wr = send_packfile_from_buffer (& packfile );
967+ if (!need_packfile ) {
968+ struct oidset_iter iter ;
969+ struct object_id * oid ;
970+
971+ oidset_iter_init (& oids , & iter );
972+ oid = oidset_iter_next (& iter );
973+
974+ wr = send_loose_object (oid , 1 );
975+ } else {
976+ wr = get_packfile_from_oids (& oids , & packfile );
977+ if (wr & WR_STOP_THE_MUSIC )
978+ goto done ;
979+
980+ wr = send_packfile_from_buffer (& packfile );
981+ }
913982
914983done :
915984 oidset_clear (& oids );
0 commit comments