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