Skip to content

Commit 1d00c49

Browse files
jeffhostetlermjcheetham
authored andcommitted
t5799: add support for POST to return either a loose object or packfile
Earlier versions of the test always returned a packfile in response to a POST. Now we look at the number of objects in the POST request. If > 1, always send a packfile. If = 1 and it is a commit, send a packfile. Otherwise, send a loose object. This is to better model the behavior of the GVFS server/protocol which treats commits differently. Signed-off-by: Jeff Hostetler <[email protected]>
1 parent 81e1de7 commit 1d00c49

File tree

2 files changed

+209
-60
lines changed

2 files changed

+209
-60
lines changed

t/helper/test-gvfs-protocol.c

Lines changed: 134 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -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
struct 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,
642683
static 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

694699
static 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
*/
820817
static 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+
908967
static 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

924999
done:
9251000
oidset_clear(&oids);

0 commit comments

Comments
 (0)