1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.transport;
12
13 import static org.eclipse.jgit.transport.BasePackPushConnection.CAPABILITY_REPORT_STATUS;
14 import static org.eclipse.jgit.transport.BasePackPushConnection.CAPABILITY_SIDE_BAND_64K;
15 import static org.eclipse.jgit.transport.RemoteRefUpdate.Status.REJECTED_OTHER_REASON;
16 import static org.junit.Assert.assertEquals;
17 import static org.junit.Assert.fail;
18
19 import java.io.IOException;
20 import java.io.StringWriter;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26
27 import org.eclipse.jgit.errors.TransportException;
28 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
29 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
30 import org.eclipse.jgit.junit.TestRepository;
31 import org.eclipse.jgit.lib.Constants;
32 import org.eclipse.jgit.lib.NullProgressMonitor;
33 import org.eclipse.jgit.lib.ObjectId;
34 import org.eclipse.jgit.lib.ObjectInserter;
35 import org.eclipse.jgit.lib.RefUpdate;
36 import org.eclipse.jgit.lib.Repository;
37 import org.junit.After;
38 import org.junit.Before;
39 import org.junit.Test;
40
41 public class PushConnectionTest {
42 private URIish uri;
43 private TestProtocol<Object> testProtocol;
44 private Object ctx = new Object();
45 private InMemoryRepository server;
46 private InMemoryRepository client;
47 private List<String> processedRefs;
48 private ObjectId obj1;
49 private ObjectId obj2;
50 private ObjectId obj3;
51 private String refName = "refs/tags/blob";
52
53 @Before
54 public void setUp() throws Exception {
55 server = newRepo("server");
56 client = newRepo("client");
57 processedRefs = new ArrayList<>();
58 testProtocol = new TestProtocol<>(null, (Object req, Repository db) -> {
59 ReceivePack rp = new ReceivePack(db);
60 rp.setPreReceiveHook((ReceivePack receivePack,
61 Collection<ReceiveCommand> cmds) -> {
62 for (ReceiveCommand cmd : cmds) {
63 processedRefs.add(cmd.getRefName());
64 }
65 });
66 return rp;
67 });
68 uri = testProtocol.register(ctx, server);
69
70 try (ObjectInserter ins = server.newObjectInserter()) {
71 obj1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("test"));
72 obj3 = ins.insert(Constants.OBJ_BLOB, Constants.encode("not"));
73 ins.flush();
74
75 RefUpdate u = server.updateRef(refName);
76 u.setNewObjectId(obj1);
77 assertEquals(RefUpdate.Result.NEW, u.update());
78 }
79
80 try (ObjectInserter ins = client.newObjectInserter()) {
81 obj2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("file"));
82 ins.flush();
83 }
84 }
85
86 @After
87 public void tearDown() {
88 Transport.unregister(testProtocol);
89 }
90
91 private static InMemoryRepository newRepo(String name) {
92 return new InMemoryRepository(new DfsRepositoryDescription(name));
93 }
94
95 @Test
96 public void testWrongOldIdDoesNotReplace() throws IOException {
97 RemoteRefUpdate rru = new RemoteRefUpdate(null, null, obj2, refName,
98 false, null, obj3);
99
100 Map<String, RemoteRefUpdate> updates = new HashMap<>();
101 updates.put(rru.getRemoteName(), rru);
102
103 try (Transport tn = testProtocol.open(uri, client, "server");
104 PushConnection connection = tn.openPush()) {
105 connection.push(NullProgressMonitor.INSTANCE, updates);
106 }
107
108 assertEquals(REJECTED_OTHER_REASON, rru.getStatus());
109 assertEquals("invalid old id sent", rru.getMessage());
110 }
111
112 @Test
113 public void invalidCommand() throws IOException {
114 try (Transport tn = testProtocol.open(uri, client, "server");
115 InternalPushConnection c = (InternalPushConnection) tn.openPush()) {
116 StringWriter msgs = new StringWriter();
117 PacketLineOut pckOut = c.pckOut;
118
119 @SuppressWarnings("resource")
120 SideBandInputStream in = new SideBandInputStream(c.in,
121 NullProgressMonitor.INSTANCE, msgs, null);
122
123
124 StringBuilder buf = new StringBuilder();
125 buf.append("42");
126 buf.append(' ');
127 buf.append(obj2.name());
128 buf.append(' ');
129 buf.append("refs/heads/A" + obj2.name());
130 buf.append('\0').append(CAPABILITY_SIDE_BAND_64K);
131 buf.append(' ').append(CAPABILITY_REPORT_STATUS);
132 buf.append('\n');
133 pckOut.writeString(buf.toString());
134 pckOut.end();
135
136 try {
137 in.read();
138 fail("expected TransportException");
139 } catch (TransportException e) {
140 assertEquals(
141 "remote: error: invalid protocol: wanted 'old new ref'",
142 e.getMessage());
143 }
144 }
145 }
146
147 @Test
148 public void limitCommandBytes() throws IOException {
149 Map<String, RemoteRefUpdate> updates = new HashMap<>();
150 for (int i = 0; i < 4; i++) {
151 RemoteRefUpdate rru = new RemoteRefUpdate(
152 null, null, obj2, "refs/test/T" + i,
153 false, null, ObjectId.zeroId());
154 updates.put(rru.getRemoteName(), rru);
155 }
156
157 server.getConfig().setInt("receive", null, "maxCommandBytes", 195);
158 try (Transport tn = testProtocol.open(uri, client, "server");
159 PushConnection connection = tn.openPush()) {
160 try {
161 connection.push(NullProgressMonitor.INSTANCE, updates);
162 fail("server did not abort");
163 } catch (TransportException e) {
164 String msg = e.getMessage();
165 assertEquals(
166 "remote: Commands size exceeds limit defined in receive.maxCommandBytes",
167 msg);
168 }
169 }
170 }
171
172 @Test
173 public void commandOrder() throws Exception {
174 List<RemoteRefUpdate> updates = new ArrayList<>();
175 try (TestRepository<?> tr = new TestRepository<>(client)) {
176
177 for (int i = 9; i >= 0; i--) {
178 String name = "refs/heads/b" + i;
179 tr.branch(name).commit().create();
180 RemoteRefUpdate rru = new RemoteRefUpdate(client, name, name,
181 false, null, ObjectId.zeroId());
182 updates.add(rru);
183 }
184 }
185
186 PushResult result;
187 try (Transport tn = testProtocol.open(uri, client, "server")) {
188 result = tn.push(NullProgressMonitor.INSTANCE, updates);
189 }
190
191 for (RemoteRefUpdate remoteUpdate : result.getRemoteUpdates()) {
192 assertEquals(
193 "update should succeed on " + remoteUpdate.getRemoteName(),
194 RemoteRefUpdate.Status.OK, remoteUpdate.getStatus());
195 }
196
197 List<String> expected = remoteRefNames(updates);
198 assertEquals(
199 "ref names processed by ReceivePack should match input ref names in order",
200 expected, processedRefs);
201 assertEquals(
202 "remote ref names should match input ref names in order",
203 expected, remoteRefNames(result.getRemoteUpdates()));
204 }
205
206 private static List<String> remoteRefNames(Collection<RemoteRefUpdate> updates) {
207 List<String> result = new ArrayList<>();
208 for (RemoteRefUpdate u : updates) {
209 result.add(u.getRemoteName());
210 }
211 return result;
212 }
213 }