1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 package org.eclipse.jgit.transport;
45
46 import static java.util.function.Function.identity;
47 import static java.util.stream.Collectors.toMap;
48 import static org.eclipse.jgit.lib.Constants.R_TAGS;
49 import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REF_IN_WANT;
50 import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_FETCH;
51 import static org.eclipse.jgit.transport.GitProtocolConstants.COMMAND_LS_REFS;
52 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
53 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_REACHABLE_SHA1_IN_WANT;
54 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_ALLOW_TIP_SHA1_IN_WANT;
55 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_DEEPEN_RELATIVE;
56 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_FILTER;
57 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_INCLUDE_TAG;
58 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK;
59 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_MULTI_ACK_DETAILED;
60 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_DONE;
61 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS;
62 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA;
63 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SHALLOW;
64 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND;
65 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K;
66 import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK;
67
68 import java.io.ByteArrayOutputStream;
69 import java.io.EOFException;
70 import java.io.IOException;
71 import java.io.InputStream;
72 import java.io.OutputStream;
73 import java.text.MessageFormat;
74 import java.util.ArrayList;
75 import java.util.Collection;
76 import java.util.Collections;
77 import java.util.HashMap;
78 import java.util.HashSet;
79 import java.util.List;
80 import java.util.Map;
81 import java.util.Set;
82 import java.util.TreeMap;
83
84 import org.eclipse.jgit.annotations.Nullable;
85 import org.eclipse.jgit.errors.CorruptObjectException;
86 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
87 import org.eclipse.jgit.errors.MissingObjectException;
88 import org.eclipse.jgit.errors.PackProtocolException;
89 import org.eclipse.jgit.internal.JGitText;
90 import org.eclipse.jgit.internal.storage.pack.PackWriter;
91 import org.eclipse.jgit.lib.BitmapIndex;
92 import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
93 import org.eclipse.jgit.lib.Constants;
94 import org.eclipse.jgit.lib.NullProgressMonitor;
95 import org.eclipse.jgit.lib.ObjectId;
96 import org.eclipse.jgit.lib.ObjectReader;
97 import org.eclipse.jgit.lib.ProgressMonitor;
98 import org.eclipse.jgit.lib.Ref;
99 import org.eclipse.jgit.lib.Repository;
100 import org.eclipse.jgit.revwalk.AsyncRevObjectQueue;
101 import org.eclipse.jgit.revwalk.BitmapWalker;
102 import org.eclipse.jgit.revwalk.DepthWalk;
103 import org.eclipse.jgit.revwalk.ObjectWalk;
104 import org.eclipse.jgit.revwalk.RevCommit;
105 import org.eclipse.jgit.revwalk.RevFlag;
106 import org.eclipse.jgit.revwalk.RevFlagSet;
107 import org.eclipse.jgit.revwalk.RevObject;
108 import org.eclipse.jgit.revwalk.RevTag;
109 import org.eclipse.jgit.revwalk.RevWalk;
110 import org.eclipse.jgit.revwalk.filter.CommitTimeRevFilter;
111 import org.eclipse.jgit.storage.pack.PackConfig;
112 import org.eclipse.jgit.storage.pack.PackStatistics;
113 import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck;
114 import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser;
115 import org.eclipse.jgit.transport.TransferConfig.ProtocolVersion;
116 import org.eclipse.jgit.util.io.InterruptTimer;
117 import org.eclipse.jgit.util.io.NullOutputStream;
118 import org.eclipse.jgit.util.io.TimeoutInputStream;
119 import org.eclipse.jgit.util.io.TimeoutOutputStream;
120
121
122
123
124 public class UploadPack {
125
126 public static enum RequestPolicy {
127
128 ADVERTISED,
129
130
131
132
133
134 REACHABLE_COMMIT,
135
136
137
138
139
140
141
142
143
144 TIP,
145
146
147
148
149
150
151
152 REACHABLE_COMMIT_TIP,
153
154
155 ANY;
156 }
157
158
159
160
161
162
163 public interface RequestValidator {
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178 void checkWants(UploadPack up, List<ObjectId> wants)
179 throws PackProtocolException, IOException;
180 }
181
182
183 public static class FirstLine {
184 private final String line;
185 private final Set<String> options;
186
187
188
189
190
191
192
193 public FirstLine(String line) {
194 if (line.length() > 45) {
195 final HashSet<String> opts = new HashSet<>();
196 String opt = line.substring(45);
197 if (opt.startsWith(" "))
198 opt = opt.substring(1);
199 for (String c : opt.split(" "))
200 opts.add(c);
201 this.line = line.substring(0, 45);
202 this.options = Collections.unmodifiableSet(opts);
203 } else {
204 this.line = line;
205 this.options = Collections.emptySet();
206 }
207 }
208
209
210 public String getLine() {
211 return line;
212 }
213
214
215 public Set<String> getOptions() {
216 return options;
217 }
218 }
219
220
221 private final Repository db;
222
223
224 private final RevWalk walk;
225
226
227 private PackConfig packConfig;
228
229
230 private TransferConfig transferConfig;
231
232
233 private int timeout;
234
235
236
237
238
239
240
241
242
243
244
245
246 private boolean biDirectionalPipe = true;
247
248
249 private InterruptTimer timer;
250
251
252
253
254
255 private boolean clientRequestedV2;
256
257 private InputStream rawIn;
258
259 private ResponseBufferedOutputStream rawOut;
260
261 private PacketLineIn pckIn;
262
263 private PacketLineOut pckOut;
264
265 private OutputStream msgOut = NullOutputStream.INSTANCE;
266
267
268
269
270
271 private Map<String, Ref> refs;
272
273
274 private ProtocolV2Hook protocolV2Hook = ProtocolV2Hook.DEFAULT;
275
276
277 private AdvertiseRefsHook advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
278
279
280 private boolean advertiseRefsHookCalled;
281
282
283 private RefFilter refFilter = RefFilter.DEFAULT;
284
285
286 private PreUploadHook preUploadHook = PreUploadHook.NULL;
287
288
289 private PostUploadHook postUploadHook = PostUploadHook.NULL;
290
291
292 private Set<String> options;
293 String userAgent;
294
295
296 private final Set<ObjectId> wantIds = new HashSet<>();
297
298
299 private final Set<RevObject> wantAll = new HashSet<>();
300
301
302 private final Set<RevObject> commonBase = new HashSet<>();
303
304
305 private Set<ObjectId> clientShallowCommits = new HashSet<>();
306
307
308 private int depth;
309
310
311
312
313
314 private int shallowSince;
315
316
317
318
319
320
321 private List<String> deepenNotRefs = new ArrayList<>();
322
323
324 private int oldestTime;
325
326
327 private Boolean okToGiveUp;
328
329 private boolean sentReady;
330
331
332 private Set<ObjectId> advertised;
333
334
335 private final RevFlag WANT;
336
337
338 private final RevFlag PEER_HAS;
339
340
341 private final RevFlag COMMON;
342
343
344 private final RevFlag SATISFIED;
345
346 private final RevFlagSet SAVE;
347
348 private RequestValidator requestValidator = new AdvertisedRequestValidator();
349
350 private MultiAck multiAck = MultiAck.OFF;
351
352 private boolean noDone;
353
354 private PackStatistics statistics;
355
356 private long filterBlobLimit = -1;
357
358
359
360
361
362
363
364 public UploadPack(Repository copyFrom) {
365 db = copyFrom;
366 walk = new RevWalk(db);
367 walk.setRetainBody(false);
368
369 WANT = walk.newFlag("WANT");
370 PEER_HAS = walk.newFlag("PEER_HAS");
371 COMMON = walk.newFlag("COMMON");
372 SATISFIED = walk.newFlag("SATISFIED");
373 walk.carry(PEER_HAS);
374
375 SAVE = new RevFlagSet();
376 SAVE.add(WANT);
377 SAVE.add(PEER_HAS);
378 SAVE.add(COMMON);
379 SAVE.add(SATISFIED);
380
381 setTransferConfig(null);
382 }
383
384
385
386
387
388
389 public final Repository getRepository() {
390 return db;
391 }
392
393
394
395
396
397
398 public final RevWalk getRevWalk() {
399 return walk;
400 }
401
402
403
404
405
406
407
408 public final Map<String, Ref> getAdvertisedRefs() {
409 return refs;
410 }
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425 public void setAdvertisedRefs(Map<String, Ref> allRefs) {
426 if (allRefs != null)
427 refs = allRefs;
428 else
429 refs = db.getAllRefs();
430 if (refFilter == RefFilter.DEFAULT)
431 refs = transferConfig.getRefFilter().filter(refs);
432 else
433 refs = refFilter.filter(refs);
434 }
435
436
437
438
439
440
441 public int getTimeout() {
442 return timeout;
443 }
444
445
446
447
448
449
450
451
452
453 public void setTimeout(int seconds) {
454 timeout = seconds;
455 }
456
457
458
459
460
461
462
463
464 public boolean isBiDirectionalPipe() {
465 return biDirectionalPipe;
466 }
467
468
469
470
471
472
473
474
475
476
477
478
479
480 public void setBiDirectionalPipe(boolean twoWay) {
481 biDirectionalPipe = twoWay;
482 }
483
484
485
486
487
488
489
490 public RequestPolicy getRequestPolicy() {
491 if (requestValidator instanceof AdvertisedRequestValidator)
492 return RequestPolicy.ADVERTISED;
493 if (requestValidator instanceof ReachableCommitRequestValidator)
494 return RequestPolicy.REACHABLE_COMMIT;
495 if (requestValidator instanceof TipRequestValidator)
496 return RequestPolicy.TIP;
497 if (requestValidator instanceof ReachableCommitTipRequestValidator)
498 return RequestPolicy.REACHABLE_COMMIT_TIP;
499 if (requestValidator instanceof AnyRequestValidator)
500 return RequestPolicy.ANY;
501 return null;
502 }
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521 public void setRequestPolicy(RequestPolicy policy) {
522 switch (policy) {
523 case ADVERTISED:
524 default:
525 requestValidator = new AdvertisedRequestValidator();
526 break;
527 case REACHABLE_COMMIT:
528 requestValidator = new ReachableCommitRequestValidator();
529 break;
530 case TIP:
531 requestValidator = new TipRequestValidator();
532 break;
533 case REACHABLE_COMMIT_TIP:
534 requestValidator = new ReachableCommitTipRequestValidator();
535 break;
536 case ANY:
537 requestValidator = new AnyRequestValidator();
538 break;
539 }
540 }
541
542
543
544
545
546
547
548
549 public void setRequestValidator(RequestValidator validator) {
550 requestValidator = validator != null ? validator
551 : new AdvertisedRequestValidator();
552 }
553
554
555
556
557
558
559 public AdvertiseRefsHook getAdvertiseRefsHook() {
560 return advertiseRefsHook;
561 }
562
563
564
565
566
567
568 public RefFilter getRefFilter() {
569 return refFilter;
570 }
571
572
573
574
575
576
577
578
579
580
581
582
583 public void setAdvertiseRefsHook(AdvertiseRefsHook advertiseRefsHook) {
584 if (advertiseRefsHook != null)
585 this.advertiseRefsHook = advertiseRefsHook;
586 else
587 this.advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
588 }
589
590
591
592
593
594
595
596 public void setProtocolV2Hook(ProtocolV2Hook hook) {
597 this.protocolV2Hook = hook;
598 }
599
600
601
602
603
604
605
606
607
608
609
610
611
612 public void setRefFilter(RefFilter refFilter) {
613 this.refFilter = refFilter != null ? refFilter : RefFilter.DEFAULT;
614 }
615
616
617
618
619
620
621 public PreUploadHook getPreUploadHook() {
622 return preUploadHook;
623 }
624
625
626
627
628
629
630
631 public void setPreUploadHook(PreUploadHook hook) {
632 preUploadHook = hook != null ? hook : PreUploadHook.NULL;
633 }
634
635
636
637
638
639
640
641 public PostUploadHook getPostUploadHook() {
642 return postUploadHook;
643 }
644
645
646
647
648
649
650
651
652 public void setPostUploadHook(PostUploadHook hook) {
653 postUploadHook = hook != null ? hook : PostUploadHook.NULL;
654 }
655
656
657
658
659
660
661
662
663 public void setPackConfig(PackConfig pc) {
664 this.packConfig = pc;
665 }
666
667
668
669
670
671
672
673
674
675 public void setTransferConfig(TransferConfig tc) {
676 this.transferConfig = tc != null ? tc : new TransferConfig(db);
677 if (transferConfig.isAllowTipSha1InWant()) {
678 setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
679 ? RequestPolicy.REACHABLE_COMMIT_TIP : RequestPolicy.TIP);
680 } else {
681 setRequestPolicy(transferConfig.isAllowReachableSha1InWant()
682 ? RequestPolicy.REACHABLE_COMMIT : RequestPolicy.ADVERTISED);
683 }
684 }
685
686
687
688
689
690
691
692
693
694
695
696
697 public boolean isSideBand() throws RequestNotYetReadException {
698 if (options == null)
699 throw new RequestNotYetReadException();
700 return (options.contains(OPTION_SIDE_BAND)
701 || options.contains(OPTION_SIDE_BAND_64K));
702 }
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717 public void setExtraParameters(Collection<String> params) {
718 this.clientRequestedV2 = params.contains("version=2");
719 }
720
721 private boolean useProtocolV2() {
722 return ProtocolVersion.V2.equals(transferConfig.protocolVersion)
723 && clientRequestedV2;
724 }
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747 public void upload(final InputStream input, OutputStream output,
748 final OutputStream messages) throws IOException {
749 try {
750 rawIn = input;
751 if (messages != null)
752 msgOut = messages;
753
754 if (timeout > 0) {
755 final Thread caller = Thread.currentThread();
756 timer = new InterruptTimer(caller.getName() + "-Timer");
757 TimeoutInputStream i = new TimeoutInputStream(rawIn, timer);
758 @SuppressWarnings("resource")
759 TimeoutOutputStream o = new TimeoutOutputStream(output, timer);
760 i.setTimeout(timeout * 1000);
761 o.setTimeout(timeout * 1000);
762 rawIn = i;
763 output = o;
764 }
765
766 rawOut = new ResponseBufferedOutputStream(output);
767 if (biDirectionalPipe) {
768 rawOut.stopBuffering();
769 }
770
771 pckIn = new PacketLineIn(rawIn);
772 pckOut = new PacketLineOut(rawOut);
773 if (useProtocolV2()) {
774 serviceV2();
775 } else {
776 service();
777 }
778 } finally {
779 msgOut = NullOutputStream.INSTANCE;
780 walk.close();
781 if (timer != null) {
782 try {
783 timer.terminate();
784 } finally {
785 timer = null;
786 }
787 }
788 }
789 }
790
791
792
793
794
795
796
797
798
799 public PackStatistics getStatistics() {
800 return statistics;
801 }
802
803 private Map<String, Ref> getAdvertisedOrDefaultRefs() throws IOException {
804 if (refs != null) {
805 return refs;
806 }
807
808 if (!advertiseRefsHookCalled) {
809 advertiseRefsHook.advertiseRefs(this);
810 advertiseRefsHookCalled = true;
811 }
812 if (refs == null) {
813
814 setAdvertisedRefs(
815 db.getRefDatabase().getRefs().stream()
816 .collect(toMap(Ref::getName, identity())));
817 }
818 return refs;
819 }
820
821 private Map<String, Ref> getFilteredRefs(Collection<String> refPrefixes)
822 throws IOException {
823 if (refPrefixes.isEmpty()) {
824 return getAdvertisedOrDefaultRefs();
825 }
826 if (refs == null && !advertiseRefsHookCalled) {
827 advertiseRefsHook.advertiseRefs(this);
828 advertiseRefsHookCalled = true;
829 }
830 if (refs == null) {
831
832 Map<String, Ref> rs = new HashMap<>();
833 for (String p : refPrefixes) {
834 for (Ref r : db.getRefDatabase().getRefsByPrefix(p)) {
835 rs.put(r.getName(), r);
836 }
837 }
838 if (refFilter != RefFilter.DEFAULT) {
839 return refFilter.filter(rs);
840 }
841 return transferConfig.getRefFilter().filter(rs);
842 }
843
844
845
846 return refs.values().stream()
847 .filter(ref -> refPrefixes.stream()
848 .anyMatch(ref.getName()::startsWith))
849 .collect(toMap(Ref::getName, identity()));
850 }
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865 @Nullable
866 private Ref getRef(String name) throws IOException {
867 if (refs != null) {
868 return refs.get(name);
869 }
870 if (!advertiseRefsHookCalled) {
871 advertiseRefsHook.advertiseRefs(this);
872 advertiseRefsHookCalled = true;
873 }
874 if (refs == null &&
875 refFilter == RefFilter.DEFAULT &&
876 transferConfig.hasDefaultRefFilter()) {
877
878 return db.getRefDatabase().exactRef(name);
879 }
880 return getAdvertisedOrDefaultRefs().get(name);
881 }
882
883 private void service() throws IOException {
884 boolean sendPack = false;
885
886
887 PackStatistics.Accumulator accumulator = new PackStatistics.Accumulator();
888 List<ObjectId> unshallowCommits = new ArrayList<>();
889 try {
890 if (biDirectionalPipe)
891 sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
892 else if (requestValidator instanceof AnyRequestValidator)
893 advertised = Collections.emptySet();
894 else
895 advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
896
897 long negotiateStart = System.currentTimeMillis();
898 accumulator.advertised = advertised.size();
899 recvWants();
900 if (wantIds.isEmpty()) {
901 preUploadHook.onBeginNegotiateRound(this, wantIds, 0);
902 preUploadHook.onEndNegotiateRound(this, wantIds, 0, 0, false);
903 return;
904 }
905 accumulator.wants = wantIds.size();
906
907 if (options.contains(OPTION_MULTI_ACK_DETAILED)) {
908 multiAck = MultiAck.DETAILED;
909 noDone = options.contains(OPTION_NO_DONE);
910 } else if (options.contains(OPTION_MULTI_ACK))
911 multiAck = MultiAck.CONTINUE;
912 else
913 multiAck = MultiAck.OFF;
914
915 if (!clientShallowCommits.isEmpty())
916 verifyClientShallow(clientShallowCommits);
917 if (depth != 0)
918 processShallow(null, unshallowCommits, true);
919 if (!clientShallowCommits.isEmpty())
920 walk.assumeShallow(clientShallowCommits);
921 sendPack = negotiate(accumulator);
922 accumulator.timeNegotiating += System.currentTimeMillis()
923 - negotiateStart;
924
925 if (sendPack && !biDirectionalPipe) {
926
927
928 int eof = rawIn.read();
929 if (0 <= eof) {
930 sendPack = false;
931 throw new CorruptObjectException(MessageFormat.format(
932 JGitText.get().expectedEOFReceived,
933 "\\x" + Integer.toHexString(eof)));
934 }
935 }
936 } catch (ServiceMayNotContinueException err) {
937 if (!err.isOutput() && err.getMessage() != null) {
938 try {
939 pckOut.writeString("ERR " + err.getMessage() + "\n");
940 err.setOutput();
941 } catch (Throwable err2) {
942
943 }
944 }
945 throw err;
946 } catch (IOException | RuntimeException | Error err) {
947 boolean output = false;
948 try {
949 String msg = err instanceof PackProtocolException
950 ? err.getMessage()
951 : JGitText.get().internalServerError;
952 pckOut.writeString("ERR " + msg + "\n");
953 output = true;
954 } catch (Throwable err2) {
955
956 }
957 if (output) {
958 throw new UploadPackInternalServerErrorException(err);
959 }
960 throw err;
961 } finally {
962 if (!sendPack && !biDirectionalPipe) {
963 while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) {
964
965 }
966 }
967 rawOut.stopBuffering();
968 }
969
970 if (sendPack) {
971 sendPack(accumulator, refs == null ? null : refs.values(), unshallowCommits);
972 }
973 }
974
975 private void lsRefsV2() throws IOException {
976 LsRefsV2Request.Builder builder = LsRefsV2Request.builder();
977 List<String> prefixes = new ArrayList<>();
978 String line = pckIn.readString();
979
980
981 if (line == PacketLineIn.DELIM) {
982 while ((line = pckIn.readString()) != PacketLineIn.END) {
983 if (line.equals("peel")) {
984 builder.setPeel(true);
985 } else if (line.equals("symrefs")) {
986 builder.setSymrefs(true);
987 } else if (line.startsWith("ref-prefix ")) {
988 prefixes.add(line.substring("ref-prefix ".length()));
989 } else {
990 throw new PackProtocolException(MessageFormat
991 .format(JGitText.get().unexpectedPacketLine, line));
992 }
993 }
994 } else if (line != PacketLineIn.END) {
995 throw new PackProtocolException(MessageFormat
996 .format(JGitText.get().unexpectedPacketLine, line));
997 }
998 LsRefsV2Request req = builder.setRefPrefixes(prefixes).build();
999
1000 protocolV2Hook.onLsRefs(req);
1001
1002 rawOut.stopBuffering();
1003 PacketLineOutRefAdvertiser adv = new PacketLineOutRefAdvertiser(pckOut);
1004 adv.setUseProtocolV2(true);
1005 if (req.getPeel()) {
1006 adv.setDerefTags(true);
1007 }
1008 Map<String, Ref> refsToSend = getFilteredRefs(req.getRefPrefixes());
1009 if (req.getSymrefs()) {
1010 findSymrefs(adv, refsToSend);
1011 }
1012
1013 adv.send(refsToSend);
1014 adv.end();
1015 }
1016
1017 private void fetchV2() throws IOException {
1018
1019
1020
1021
1022 if (requestValidator instanceof TipRequestValidator ||
1023 requestValidator instanceof ReachableCommitTipRequestValidator ||
1024 requestValidator instanceof AnyRequestValidator) {
1025 advertised = Collections.emptySet();
1026 } else {
1027 advertised = refIdSet(getAdvertisedOrDefaultRefs().values());
1028 }
1029
1030 ProtocolV2Parser parser = new ProtocolV2Parser(transferConfig);
1031 FetchV2Request req = parser.parseFetchRequest(pckIn);
1032 rawOut.stopBuffering();
1033
1034 protocolV2Hook.onFetch(req);
1035
1036
1037
1038 options = req.getOptions();
1039 clientShallowCommits = req.getClientShallowCommits();
1040 depth = req.getDepth();
1041 shallowSince = req.getDeepenSince();
1042 filterBlobLimit = req.getFilterBlobLimit();
1043 deepenNotRefs = req.getDeepenNotRefs();
1044
1045 wantIds.addAll(req.getWantsIds());
1046 Map<String, ObjectId> wantedRefs = new TreeMap<>();
1047 for (String refName : req.getWantedRefs()) {
1048 Ref ref = getRef(refName);
1049 if (ref == null) {
1050 throw new PackProtocolException(MessageFormat
1051 .format(JGitText.get().invalidRefName, refName));
1052 }
1053 ObjectId oid = ref.getObjectId();
1054 if (oid == null) {
1055 throw new PackProtocolException(MessageFormat
1056 .format(JGitText.get().invalidRefName, refName));
1057 }
1058 wantIds.add(oid);
1059 wantedRefs.put(refName, oid);
1060 }
1061
1062 boolean sectionSent = false;
1063 @Nullable List<ObjectId> shallowCommits = null;
1064 List<ObjectId> unshallowCommits = new ArrayList<>();
1065
1066 if (!req.getClientShallowCommits().isEmpty()) {
1067 verifyClientShallow(req.getClientShallowCommits());
1068 }
1069 if (req.getDepth() != 0 || req.getDeepenSince() != 0
1070 || !req.getDeepenNotRefs().isEmpty()) {
1071 shallowCommits = new ArrayList<>();
1072 processShallow(shallowCommits, unshallowCommits, false);
1073 }
1074 if (!req.getClientShallowCommits().isEmpty())
1075 walk.assumeShallow(req.getClientShallowCommits());
1076
1077 if (req.wasDoneReceived()) {
1078 processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
1079 new PacketLineOut(NullOutputStream.INSTANCE));
1080 } else {
1081 pckOut.writeString("acknowledgments\n");
1082 for (ObjectId id : req.getPeerHas()) {
1083 if (walk.getObjectReader().has(id)) {
1084 pckOut.writeString("ACK " + id.getName() + "\n");
1085 }
1086 }
1087 processHaveLines(req.getPeerHas(), ObjectId.zeroId(),
1088 new PacketLineOut(NullOutputStream.INSTANCE));
1089 if (okToGiveUp()) {
1090 pckOut.writeString("ready\n");
1091 } else if (commonBase.isEmpty()) {
1092 pckOut.writeString("NAK\n");
1093 }
1094 sectionSent = true;
1095 }
1096
1097 if (req.wasDoneReceived() || okToGiveUp()) {
1098 if (shallowCommits != null) {
1099 if (sectionSent)
1100 pckOut.writeDelim();
1101 pckOut.writeString("shallow-info\n");
1102 for (ObjectId o : shallowCommits) {
1103 pckOut.writeString("shallow " + o.getName() + '\n');
1104 }
1105 for (ObjectId o : unshallowCommits) {
1106 pckOut.writeString("unshallow " + o.getName() + '\n');
1107 }
1108 sectionSent = true;
1109 }
1110
1111 if (!wantedRefs.isEmpty()) {
1112 if (sectionSent) {
1113 pckOut.writeDelim();
1114 }
1115 pckOut.writeString("wanted-refs\n");
1116 for (Map.Entry<String, ObjectId> entry :
1117 wantedRefs.entrySet()) {
1118 pckOut.writeString(entry.getValue().getName() + ' ' +
1119 entry.getKey() + '\n');
1120 }
1121 sectionSent = true;
1122 }
1123
1124 if (sectionSent)
1125 pckOut.writeDelim();
1126 pckOut.writeString("packfile\n");
1127 sendPack(new PackStatistics.Accumulator(),
1128 req.getOptions().contains(OPTION_INCLUDE_TAG)
1129 ? db.getRefDatabase().getRefsByPrefix(R_TAGS)
1130 : null,
1131 unshallowCommits);
1132
1133
1134 } else {
1135
1136 pckOut.end();
1137 }
1138 }
1139
1140
1141
1142
1143
1144 private boolean serveOneCommandV2() throws IOException {
1145 String command;
1146 try {
1147 command = pckIn.readString();
1148 } catch (EOFException eof) {
1149
1150 return true;
1151 }
1152 if (command == PacketLineIn.END) {
1153
1154
1155
1156 return true;
1157 }
1158 if (command.equals("command=" + COMMAND_LS_REFS)) {
1159 lsRefsV2();
1160 return false;
1161 }
1162 if (command.equals("command=" + COMMAND_FETCH)) {
1163 fetchV2();
1164 return false;
1165 }
1166 throw new PackProtocolException(MessageFormat
1167 .format(JGitText.get().unknownTransportCommand, command));
1168 }
1169
1170 private List<String> getV2CapabilityAdvertisement() {
1171 ArrayList<String> caps = new ArrayList<>();
1172 caps.add("version 2");
1173 caps.add(COMMAND_LS_REFS);
1174 boolean advertiseRefInWant = transferConfig.isAllowRefInWant() &&
1175 db.getConfig().getBoolean("uploadpack", null,
1176 "advertiserefinwant", true);
1177 caps.add(
1178 COMMAND_FETCH + '=' +
1179 (transferConfig.isAllowFilter() ? OPTION_FILTER + ' ' : "") +
1180 (advertiseRefInWant ? CAPABILITY_REF_IN_WANT + ' ' : "") +
1181 OPTION_SHALLOW);
1182 return caps;
1183 }
1184
1185 private void serviceV2() throws IOException {
1186 if (biDirectionalPipe) {
1187
1188
1189
1190
1191 protocolV2Hook
1192 .onCapabilities(CapabilitiesV2Request.builder().build());
1193 for (String s : getV2CapabilityAdvertisement()) {
1194 pckOut.writeString(s + "\n");
1195 }
1196 pckOut.end();
1197
1198 while (!serveOneCommandV2()) {
1199
1200 }
1201 return;
1202 }
1203
1204 try {
1205 serveOneCommandV2();
1206 } finally {
1207 while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) {
1208
1209 }
1210 rawOut.stopBuffering();
1211 }
1212 }
1213
1214 private static Set<ObjectId> refIdSet(Collection<Ref> refs) {
1215 Set<ObjectId> ids = new HashSet<>(refs.size());
1216 for (Ref ref : refs) {
1217 ObjectId id = ref.getObjectId();
1218 if (id != null) {
1219 ids.add(id);
1220 }
1221 id = ref.getPeeledObjectId();
1222 if (id != null) {
1223 ids.add(id);
1224 }
1225 }
1226 return ids;
1227 }
1228
1229
1230
1231
1232
1233
1234
1235 private void processShallow(@Nullable List<ObjectId> shallowCommits,
1236 List<ObjectId> unshallowCommits,
1237 boolean writeToPckOut) throws IOException {
1238 if (options.contains(OPTION_DEEPEN_RELATIVE) ||
1239 shallowSince != 0 ||
1240 !deepenNotRefs.isEmpty()) {
1241
1242
1243 throw new UnsupportedOperationException();
1244 }
1245
1246 int walkDepth = depth - 1;
1247 try (DepthWalk.RevWalk depthWalk = new DepthWalk.RevWalk(
1248 walk.getObjectReader(), walkDepth)) {
1249
1250
1251 for (ObjectId o : wantIds) {
1252 try {
1253 depthWalk.markRoot(depthWalk.parseCommit(o));
1254 } catch (IncorrectObjectTypeException notCommit) {
1255
1256 }
1257 }
1258
1259 RevCommit o;
1260 while ((o = depthWalk.next()) != null) {
1261 DepthWalk.Commit c = (DepthWalk.Commit) o;
1262
1263
1264
1265 if (c.getDepth() == walkDepth
1266 && !clientShallowCommits.contains(c)) {
1267 if (shallowCommits != null) {
1268 shallowCommits.add(c.copy());
1269 }
1270 if (writeToPckOut) {
1271 pckOut.writeString("shallow " + o.name());
1272 }
1273 }
1274
1275
1276
1277 if (c.getDepth() < walkDepth
1278 && clientShallowCommits.remove(c)) {
1279 unshallowCommits.add(c.copy());
1280 if (writeToPckOut) {
1281 pckOut.writeString("unshallow " + c.name());
1282 }
1283 }
1284 }
1285 }
1286 if (writeToPckOut) {
1287 pckOut.end();
1288 }
1289 }
1290
1291
1292
1293
1294
1295
1296 private void verifyClientShallow(Set<ObjectId> shallowCommits)
1297 throws IOException, PackProtocolException {
1298 AsyncRevObjectQueue q = walk.parseAny(shallowCommits, true);
1299 try {
1300 for (;;) {
1301 try {
1302
1303 RevObject o = q.next();
1304 if (o == null) {
1305 break;
1306 }
1307 if (!(o instanceof RevCommit)) {
1308 throw new PackProtocolException(
1309 MessageFormat.format(
1310 JGitText.get().invalidShallowObject,
1311 o.name()));
1312 }
1313 } catch (MissingObjectException notCommit) {
1314
1315
1316 shallowCommits.remove(notCommit.getObjectId());
1317 continue;
1318 }
1319 }
1320 } finally {
1321 q.release();
1322 }
1323 }
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335 public void sendAdvertisedRefs(RefAdvertiser adv) throws IOException,
1336 ServiceMayNotContinueException {
1337 sendAdvertisedRefs(adv, null);
1338 }
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356 public void sendAdvertisedRefs(RefAdvertiser adv,
1357 @Nullable String serviceName) throws IOException,
1358 ServiceMayNotContinueException {
1359 if (useProtocolV2()) {
1360
1361
1362 protocolV2Hook
1363 .onCapabilities(CapabilitiesV2Request.builder().build());
1364 for (String s : getV2CapabilityAdvertisement()) {
1365 adv.writeOne(s);
1366 }
1367 adv.end();
1368 return;
1369 }
1370
1371 Map<String, Ref> advertisedOrDefaultRefs = getAdvertisedOrDefaultRefs();
1372
1373 if (serviceName != null) {
1374 adv.writeOne("# service=" + serviceName + '\n');
1375 adv.end();
1376 }
1377 adv.init(db);
1378 adv.advertiseCapability(OPTION_INCLUDE_TAG);
1379 adv.advertiseCapability(OPTION_MULTI_ACK_DETAILED);
1380 adv.advertiseCapability(OPTION_MULTI_ACK);
1381 adv.advertiseCapability(OPTION_OFS_DELTA);
1382 adv.advertiseCapability(OPTION_SIDE_BAND);
1383 adv.advertiseCapability(OPTION_SIDE_BAND_64K);
1384 adv.advertiseCapability(OPTION_THIN_PACK);
1385 adv.advertiseCapability(OPTION_NO_PROGRESS);
1386 adv.advertiseCapability(OPTION_SHALLOW);
1387 if (!biDirectionalPipe)
1388 adv.advertiseCapability(OPTION_NO_DONE);
1389 RequestPolicy policy = getRequestPolicy();
1390 if (policy == RequestPolicy.TIP
1391 || policy == RequestPolicy.REACHABLE_COMMIT_TIP
1392 || policy == null)
1393 adv.advertiseCapability(OPTION_ALLOW_TIP_SHA1_IN_WANT);
1394 if (policy == RequestPolicy.REACHABLE_COMMIT
1395 || policy == RequestPolicy.REACHABLE_COMMIT_TIP
1396 || policy == null)
1397 adv.advertiseCapability(OPTION_ALLOW_REACHABLE_SHA1_IN_WANT);
1398 adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
1399 if (transferConfig.isAllowFilter()) {
1400 adv.advertiseCapability(OPTION_FILTER);
1401 }
1402 adv.setDerefTags(true);
1403 findSymrefs(adv, advertisedOrDefaultRefs);
1404 advertised = adv.send(advertisedOrDefaultRefs);
1405 if (adv.isEmpty())
1406 adv.advertiseId(ObjectId.zeroId(), "capabilities^{}");
1407 adv.end();
1408 }
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421 public void sendMessage(String what) {
1422 try {
1423 msgOut.write(Constants.encode(what + "\n"));
1424 } catch (IOException e) {
1425
1426 }
1427 }
1428
1429
1430
1431
1432
1433
1434
1435 public OutputStream getMessageOutputStream() {
1436 return msgOut;
1437 }
1438
1439 private void recvWants() throws IOException {
1440 boolean isFirst = true;
1441 boolean filterReceived = false;
1442 for (;;) {
1443 String line;
1444 try {
1445 line = pckIn.readString();
1446 } catch (EOFException eof) {
1447 if (isFirst)
1448 break;
1449 throw eof;
1450 }
1451
1452 if (line == PacketLineIn.END)
1453 break;
1454
1455 if (line.startsWith("deepen ")) {
1456 depth = Integer.parseInt(line.substring(7));
1457 if (depth <= 0) {
1458 throw new PackProtocolException(
1459 MessageFormat.format(JGitText.get().invalidDepth,
1460 Integer.valueOf(depth)));
1461 }
1462 continue;
1463 }
1464
1465 if (line.startsWith("shallow ")) {
1466 clientShallowCommits.add(ObjectId.fromString(line.substring(8)));
1467 continue;
1468 }
1469
1470 if (transferConfig.isAllowFilter()
1471 && line.startsWith(OPTION_FILTER + " ")) {
1472 String arg = line.substring(OPTION_FILTER.length() + 1);
1473
1474 if (filterReceived) {
1475 throw new PackProtocolException(JGitText.get().tooManyFilters);
1476 }
1477 filterReceived = true;
1478
1479 filterBlobLimit = ProtocolV2Parser.filterLine(arg);
1480 continue;
1481 }
1482
1483 if (!line.startsWith("want ") || line.length() < 45)
1484 throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "want", line));
1485
1486 if (isFirst) {
1487 if (line.length() > 45) {
1488 FirstLine firstLine = new FirstLine(line);
1489 options = firstLine.getOptions();
1490 line = firstLine.getLine();
1491 } else
1492 options = Collections.emptySet();
1493 }
1494
1495 wantIds.add(ObjectId.fromString(line.substring(5)));
1496 isFirst = false;
1497 }
1498 }
1499
1500
1501
1502
1503
1504
1505
1506
1507 public int getDepth() {
1508 if (options == null)
1509 throw new RequestNotYetReadException();
1510 return depth;
1511 }
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528 public String getPeerUserAgent() {
1529 return UserAgent.getAgent(options, userAgent);
1530 }
1531
1532 private boolean negotiate(PackStatistics.Accumulator accumulator)
1533 throws IOException {
1534 okToGiveUp = Boolean.FALSE;
1535
1536 ObjectId last = ObjectId.zeroId();
1537 List<ObjectId> peerHas = new ArrayList<>(64);
1538 for (;;) {
1539 String line;
1540 try {
1541 line = pckIn.readString();
1542 } catch (EOFException eof) {
1543
1544
1545
1546
1547
1548 if (!biDirectionalPipe && depth > 0)
1549 return false;
1550 throw eof;
1551 }
1552
1553 if (line == PacketLineIn.END) {
1554 last = processHaveLines(peerHas, last, pckOut);
1555 if (commonBase.isEmpty() || multiAck != MultiAck.OFF)
1556 pckOut.writeString("NAK\n");
1557 if (noDone && sentReady) {
1558 pckOut.writeString("ACK " + last.name() + "\n");
1559 return true;
1560 }
1561 if (!biDirectionalPipe)
1562 return false;
1563 pckOut.flush();
1564
1565 } else if (line.startsWith("have ") && line.length() == 45) {
1566 peerHas.add(ObjectId.fromString(line.substring(5)));
1567 accumulator.haves++;
1568 } else if (line.equals("done")) {
1569 last = processHaveLines(peerHas, last, pckOut);
1570
1571 if (commonBase.isEmpty())
1572 pckOut.writeString("NAK\n");
1573
1574 else if (multiAck != MultiAck.OFF)
1575 pckOut.writeString("ACK " + last.name() + "\n");
1576
1577 return true;
1578
1579 } else {
1580 throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedGot, "have", line));
1581 }
1582 }
1583 }
1584
1585 private ObjectId processHaveLines(List<ObjectId> peerHas, ObjectId last, PacketLineOut out)
1586 throws IOException {
1587 preUploadHook.onBeginNegotiateRound(this, wantIds, peerHas.size());
1588 if (wantAll.isEmpty() && !wantIds.isEmpty())
1589 parseWants();
1590 if (peerHas.isEmpty())
1591 return last;
1592
1593 sentReady = false;
1594 int haveCnt = 0;
1595 walk.getObjectReader().setAvoidUnreachableObjects(true);
1596 AsyncRevObjectQueue q = walk.parseAny(peerHas, false);
1597 try {
1598 for (;;) {
1599 RevObject obj;
1600 try {
1601 obj = q.next();
1602 } catch (MissingObjectException notFound) {
1603 continue;
1604 }
1605 if (obj == null)
1606 break;
1607
1608 last = obj;
1609 haveCnt++;
1610
1611 if (obj instanceof RevCommit) {
1612 RevCommit c = (RevCommit) obj;
1613 if (oldestTime == 0 || c.getCommitTime() < oldestTime)
1614 oldestTime = c.getCommitTime();
1615 }
1616
1617 if (obj.has(PEER_HAS))
1618 continue;
1619
1620 obj.add(PEER_HAS);
1621 if (obj instanceof RevCommit)
1622 ((RevCommit) obj).carry(PEER_HAS);
1623 addCommonBase(obj);
1624
1625
1626
1627 switch (multiAck) {
1628 case OFF:
1629 if (commonBase.size() == 1)
1630 out.writeString("ACK " + obj.name() + "\n");
1631 break;
1632 case CONTINUE:
1633 out.writeString("ACK " + obj.name() + " continue\n");
1634 break;
1635 case DETAILED:
1636 out.writeString("ACK " + obj.name() + " common\n");
1637 break;
1638 }
1639 }
1640 } finally {
1641 q.release();
1642 walk.getObjectReader().setAvoidUnreachableObjects(false);
1643 }
1644
1645 int missCnt = peerHas.size() - haveCnt;
1646
1647
1648
1649
1650
1651 boolean didOkToGiveUp = false;
1652 if (0 < missCnt) {
1653 for (int i = peerHas.size() - 1; i >= 0; i--) {
1654 ObjectId id = peerHas.get(i);
1655 if (walk.lookupOrNull(id) == null) {
1656 didOkToGiveUp = true;
1657 if (okToGiveUp()) {
1658 switch (multiAck) {
1659 case OFF:
1660 break;
1661 case CONTINUE:
1662 out.writeString("ACK " + id.name() + " continue\n");
1663 break;
1664 case DETAILED:
1665 out.writeString("ACK " + id.name() + " ready\n");
1666 sentReady = true;
1667 break;
1668 }
1669 }
1670 break;
1671 }
1672 }
1673 }
1674
1675 if (multiAck == MultiAck.DETAILED && !didOkToGiveUp && okToGiveUp()) {
1676 ObjectId id = peerHas.get(peerHas.size() - 1);
1677 out.writeString("ACK " + id.name() + " ready\n");
1678 sentReady = true;
1679 }
1680
1681 preUploadHook.onEndNegotiateRound(this, wantAll, haveCnt, missCnt, sentReady);
1682 peerHas.clear();
1683 return last;
1684 }
1685
1686 private void parseWants() throws IOException {
1687 List<ObjectId> notAdvertisedWants = null;
1688 for (ObjectId obj : wantIds) {
1689 if (!advertised.contains(obj)) {
1690 if (notAdvertisedWants == null)
1691 notAdvertisedWants = new ArrayList<>();
1692 notAdvertisedWants.add(obj);
1693 }
1694 }
1695 if (notAdvertisedWants != null)
1696 requestValidator.checkWants(this, notAdvertisedWants);
1697
1698 AsyncRevObjectQueue q = walk.parseAny(wantIds, true);
1699 try {
1700 RevObject obj;
1701 while ((obj = q.next()) != null) {
1702 want(obj);
1703
1704 if (!(obj instanceof RevCommit))
1705 obj.add(SATISFIED);
1706 if (obj instanceof RevTag) {
1707 obj = walk.peel(obj);
1708 if (obj instanceof RevCommit)
1709 want(obj);
1710 }
1711 }
1712 wantIds.clear();
1713 } catch (MissingObjectException notFound) {
1714 throw new WantNotValidException(notFound.getObjectId(), notFound);
1715 } finally {
1716 q.release();
1717 }
1718 }
1719
1720 private void want(RevObject obj) {
1721 if (!obj.has(WANT)) {
1722 obj.add(WANT);
1723 wantAll.add(obj);
1724 }
1725 }
1726
1727
1728
1729
1730
1731
1732 public static final class AdvertisedRequestValidator
1733 implements RequestValidator {
1734 @Override
1735 public void checkWants(UploadPack up, List<ObjectId> wants)
1736 throws PackProtocolException, IOException {
1737 if (!up.isBiDirectionalPipe())
1738 new ReachableCommitRequestValidator().checkWants(up, wants);
1739 else if (!wants.isEmpty())
1740 throw new WantNotValidException(wants.iterator().next());
1741 }
1742 }
1743
1744
1745
1746
1747
1748
1749 public static final class ReachableCommitRequestValidator
1750 implements RequestValidator {
1751 @Override
1752 public void checkWants(UploadPack up, List<ObjectId> wants)
1753 throws PackProtocolException, IOException {
1754 checkNotAdvertisedWants(up, wants,
1755 refIdSet(up.getAdvertisedRefs().values()));
1756 }
1757 }
1758
1759
1760
1761
1762
1763
1764 public static final class TipRequestValidator implements RequestValidator {
1765 @Override
1766 public void checkWants(UploadPack up, List<ObjectId> wants)
1767 throws PackProtocolException, IOException {
1768 if (!up.isBiDirectionalPipe())
1769 new ReachableCommitTipRequestValidator().checkWants(up, wants);
1770 else if (!wants.isEmpty()) {
1771 Set<ObjectId> refIds =
1772 refIdSet(up.getRepository().getRefDatabase().getRefs());
1773 for (ObjectId obj : wants) {
1774 if (!refIds.contains(obj))
1775 throw new WantNotValidException(obj);
1776 }
1777 }
1778 }
1779 }
1780
1781
1782
1783
1784
1785
1786 public static final class ReachableCommitTipRequestValidator
1787 implements RequestValidator {
1788 @Override
1789 public void checkWants(UploadPack up, List<ObjectId> wants)
1790 throws PackProtocolException, IOException {
1791 checkNotAdvertisedWants(up, wants,
1792 refIdSet(up.getRepository().getRefDatabase().getRefs()));
1793 }
1794 }
1795
1796
1797
1798
1799
1800
1801 public static final class AnyRequestValidator implements RequestValidator {
1802 @Override
1803 public void checkWants(UploadPack up, List<ObjectId> wants)
1804 throws PackProtocolException, IOException {
1805
1806 }
1807 }
1808
1809 private static void checkNotAdvertisedWantsUsingBitmap(ObjectReader reader,
1810 BitmapIndex bitmapIndex, List<ObjectId> notAdvertisedWants,
1811 Set<ObjectId> reachableFrom) throws IOException {
1812 BitmapWalker bitmapWalker = new BitmapWalker(new ObjectWalk(reader), bitmapIndex, null);
1813 BitmapBuilder reachables = bitmapWalker.findObjects(reachableFrom, null, false);
1814 for (ObjectId oid : notAdvertisedWants) {
1815 if (!reachables.contains(oid)) {
1816 throw new WantNotValidException(oid);
1817 }
1818 }
1819 }
1820
1821 private static void checkNotAdvertisedWants(UploadPack up,
1822 List<ObjectId> notAdvertisedWants, Set<ObjectId> reachableFrom)
1823 throws MissingObjectException, IncorrectObjectTypeException, IOException {
1824
1825
1826
1827
1828
1829
1830 ObjectReader reader = up.getRevWalk().getObjectReader();
1831 try (RevWalk walk = new RevWalk(reader)) {
1832 AsyncRevObjectQueue q = walk.parseAny(notAdvertisedWants, true);
1833 try {
1834 RevObject obj;
1835 while ((obj = q.next()) != null) {
1836 if (!(obj instanceof RevCommit)) {
1837
1838
1839
1840
1841 BitmapIndex bitmapIndex = reader.getBitmapIndex();
1842 if (bitmapIndex != null) {
1843 checkNotAdvertisedWantsUsingBitmap(
1844 reader,
1845 bitmapIndex,
1846 notAdvertisedWants,
1847 reachableFrom);
1848 return;
1849 }
1850 throw new WantNotValidException(obj);
1851 }
1852 walk.markStart((RevCommit) obj);
1853 }
1854 } catch (MissingObjectException notFound) {
1855 throw new WantNotValidException(notFound.getObjectId(),
1856 notFound);
1857 } finally {
1858 q.release();
1859 }
1860 for (ObjectId id : reachableFrom) {
1861 try {
1862 walk.markUninteresting(walk.parseCommit(id));
1863 } catch (IncorrectObjectTypeException notCommit) {
1864 continue;
1865 }
1866 }
1867
1868 RevCommit bad = walk.next();
1869 if (bad != null) {
1870 throw new WantNotValidException(bad);
1871 }
1872 }
1873 }
1874
1875 private void addCommonBase(RevObject o) {
1876 if (!o.has(COMMON)) {
1877 o.add(COMMON);
1878 commonBase.add(o);
1879 okToGiveUp = null;
1880 }
1881 }
1882
1883 private boolean okToGiveUp() throws PackProtocolException {
1884 if (okToGiveUp == null)
1885 okToGiveUp = Boolean.valueOf(okToGiveUpImp());
1886 return okToGiveUp.booleanValue();
1887 }
1888
1889 private boolean okToGiveUpImp() throws PackProtocolException {
1890 if (commonBase.isEmpty())
1891 return false;
1892
1893 try {
1894 for (RevObject obj : wantAll) {
1895 if (!wantSatisfied(obj))
1896 return false;
1897 }
1898 return true;
1899 } catch (IOException e) {
1900 throw new PackProtocolException(JGitText.get().internalRevisionError, e);
1901 }
1902 }
1903
1904 private boolean wantSatisfied(RevObject want) throws IOException {
1905 if (want.has(SATISFIED))
1906 return true;
1907
1908 walk.resetRetain(SAVE);
1909 walk.markStart((RevCommit) want);
1910 if (oldestTime != 0)
1911 walk.setRevFilter(CommitTimeRevFilter.after(oldestTime * 1000L));
1912 for (;;) {
1913 final RevCommit c = walk.next();
1914 if (c == null)
1915 break;
1916 if (c.has(PEER_HAS)) {
1917 addCommonBase(c);
1918 want.add(SATISFIED);
1919 return true;
1920 }
1921 }
1922 return false;
1923 }
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940 private void sendPack(PackStatistics.Accumulator accumulator,
1941 @Nullable Collection<Ref> allTags,
1942 List<ObjectId> unshallowCommits) throws IOException {
1943 final boolean sideband = options.contains(OPTION_SIDE_BAND)
1944 || options.contains(OPTION_SIDE_BAND_64K);
1945 if (sideband) {
1946 try {
1947 sendPack(true, accumulator, allTags, unshallowCommits);
1948 } catch (ServiceMayNotContinueException noPack) {
1949
1950 throw noPack;
1951 } catch (IOException err) {
1952 if (reportInternalServerErrorOverSideband())
1953 throw new UploadPackInternalServerErrorException(err);
1954 else
1955 throw err;
1956 } catch (RuntimeException err) {
1957 if (reportInternalServerErrorOverSideband())
1958 throw new UploadPackInternalServerErrorException(err);
1959 else
1960 throw err;
1961 } catch (Error err) {
1962 if (reportInternalServerErrorOverSideband())
1963 throw new UploadPackInternalServerErrorException(err);
1964 else
1965 throw err;
1966 }
1967 } else {
1968 sendPack(false, accumulator, allTags, unshallowCommits);
1969 }
1970 }
1971
1972 private boolean reportInternalServerErrorOverSideband() {
1973 try {
1974 @SuppressWarnings("resource" )
1975 SideBandOutputStream err = new SideBandOutputStream(
1976 SideBandOutputStream.CH_ERROR,
1977 SideBandOutputStream.SMALL_BUF,
1978 rawOut);
1979 err.write(Constants.encode(JGitText.get().internalServerError));
1980 err.flush();
1981 return true;
1982 } catch (Throwable cannotReport) {
1983
1984 return false;
1985 }
1986 }
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006 private void sendPack(final boolean sideband,
2007 PackStatistics.Accumulator accumulator,
2008 @Nullable Collection<Ref> allTags,
2009 List<ObjectId> unshallowCommits) throws IOException {
2010 ProgressMonitor pm = NullProgressMonitor.INSTANCE;
2011 OutputStream packOut = rawOut;
2012
2013 if (sideband) {
2014 int bufsz = SideBandOutputStream.SMALL_BUF;
2015 if (options.contains(OPTION_SIDE_BAND_64K))
2016 bufsz = SideBandOutputStream.MAX_BUF;
2017
2018 packOut = new SideBandOutputStream(SideBandOutputStream.CH_DATA,
2019 bufsz, rawOut);
2020 if (!options.contains(OPTION_NO_PROGRESS)) {
2021 msgOut = new SideBandOutputStream(
2022 SideBandOutputStream.CH_PROGRESS, bufsz, rawOut);
2023 pm = new SideBandProgressMonitor(msgOut);
2024 }
2025 }
2026
2027 try {
2028 if (wantAll.isEmpty()) {
2029 preUploadHook.onSendPack(this, wantIds, commonBase);
2030 } else {
2031 preUploadHook.onSendPack(this, wantAll, commonBase);
2032 }
2033 msgOut.flush();
2034 } catch (ServiceMayNotContinueException noPack) {
2035 if (sideband && noPack.getMessage() != null) {
2036 noPack.setOutput();
2037 @SuppressWarnings("resource" )
2038 SideBandOutputStream err = new SideBandOutputStream(
2039 SideBandOutputStream.CH_ERROR,
2040 SideBandOutputStream.SMALL_BUF, rawOut);
2041 err.write(Constants.encode(noPack.getMessage()));
2042 err.flush();
2043 }
2044 throw noPack;
2045 }
2046
2047 PackConfig cfg = packConfig;
2048 if (cfg == null)
2049 cfg = new PackConfig(db);
2050 @SuppressWarnings("resource")
2051
2052 final PackWriter pw = new PackWriter(cfg, walk.getObjectReader(),
2053 accumulator);
2054 try {
2055 pw.setIndexDisabled(true);
2056 if (filterBlobLimit >= 0) {
2057 pw.setFilterBlobLimit(filterBlobLimit);
2058 pw.setUseCachedPacks(false);
2059 } else {
2060 pw.setUseCachedPacks(true);
2061 }
2062 pw.setUseBitmaps(depth == 0 && clientShallowCommits.isEmpty());
2063 pw.setClientShallowCommits(clientShallowCommits);
2064 pw.setReuseDeltaCommits(true);
2065 pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
2066 pw.setThin(options.contains(OPTION_THIN_PACK));
2067 pw.setReuseValidatingObjects(false);
2068
2069
2070
2071 if (commonBase.isEmpty() && refs != null) {
2072 Set<ObjectId> tagTargets = new HashSet<>();
2073 for (Ref ref : refs.values()) {
2074 if (ref.getPeeledObjectId() != null)
2075 tagTargets.add(ref.getPeeledObjectId());
2076 else if (ref.getObjectId() == null)
2077 continue;
2078 else if (ref.getName().startsWith(Constants.R_HEADS))
2079 tagTargets.add(ref.getObjectId());
2080 }
2081 pw.setTagTargets(tagTargets);
2082 }
2083
2084 RevWalk rw = walk;
2085 if (depth > 0) {
2086 pw.setShallowPack(depth, unshallowCommits);
2087 rw = new DepthWalk.RevWalk(walk.getObjectReader(), depth - 1);
2088 rw.assumeShallow(clientShallowCommits);
2089 }
2090
2091 if (wantAll.isEmpty()) {
2092 pw.preparePack(pm, wantIds, commonBase, clientShallowCommits);
2093 } else {
2094 walk.reset();
2095
2096 ObjectWalk ow = rw.toObjectWalkWithSameObjects();
2097 pw.preparePack(pm, ow, wantAll, commonBase, PackWriter.NONE);
2098 rw = ow;
2099 }
2100
2101 if (options.contains(OPTION_INCLUDE_TAG) && allTags != null) {
2102 for (Ref ref : allTags) {
2103 ObjectId objectId = ref.getObjectId();
2104 if (objectId == null) {
2105
2106 continue;
2107 }
2108
2109
2110 if (wantAll.isEmpty()) {
2111 if (wantIds.contains(objectId))
2112 continue;
2113 } else {
2114 RevObject obj = rw.lookupOrNull(objectId);
2115 if (obj != null && obj.has(WANT))
2116 continue;
2117 }
2118
2119 if (!ref.isPeeled())
2120 ref = db.getRefDatabase().peel(ref);
2121
2122 ObjectId peeledId = ref.getPeeledObjectId();
2123 objectId = ref.getObjectId();
2124 if (peeledId == null || objectId == null)
2125 continue;
2126
2127 if (pw.willInclude(peeledId) && !pw.willInclude(objectId)) {
2128 pw.addObject(rw.parseAny(objectId));
2129 }
2130 }
2131 }
2132
2133 pw.writePack(pm, NullProgressMonitor.INSTANCE, packOut);
2134
2135 if (msgOut != NullOutputStream.INSTANCE) {
2136 String msg = pw.getStatistics().getMessage() + '\n';
2137 msgOut.write(Constants.encode(msg));
2138 msgOut.flush();
2139 }
2140
2141 } finally {
2142 statistics = pw.getStatistics();
2143 if (statistics != null) {
2144 postUploadHook.onPostUpload(statistics);
2145 }
2146 pw.close();
2147 }
2148
2149 if (sideband)
2150 pckOut.end();
2151 }
2152
2153 private static void findSymrefs(
2154 final RefAdvertiser adv, final Map<String, Ref> refs) {
2155 Ref head = refs.get(Constants.HEAD);
2156 if (head != null && head.isSymbolic()) {
2157 adv.addSymref(Constants.HEAD, head.getLeaf().getName());
2158 }
2159 }
2160
2161 private static class ResponseBufferedOutputStream extends OutputStream {
2162 private final OutputStream rawOut;
2163
2164 private OutputStream out;
2165
2166 ResponseBufferedOutputStream(OutputStream rawOut) {
2167 this.rawOut = rawOut;
2168 this.out = new ByteArrayOutputStream();
2169 }
2170
2171 @Override
2172 public void write(int b) throws IOException {
2173 out.write(b);
2174 }
2175
2176 @Override
2177 public void write(byte b[]) throws IOException {
2178 out.write(b);
2179 }
2180
2181 @Override
2182 public void write(byte b[], int off, int len) throws IOException {
2183 out.write(b, off, len);
2184 }
2185
2186 @Override
2187 public void flush() throws IOException {
2188 out.flush();
2189 }
2190
2191 @Override
2192 public void close() throws IOException {
2193 out.close();
2194 }
2195
2196 void stopBuffering() throws IOException {
2197 if (out != rawOut) {
2198 ((ByteArrayOutputStream) out).writeTo(rawOut);
2199 out = rawOut;
2200 }
2201 }
2202 }
2203 }