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