View Javadoc
1   /*
2    * Copyright (C) 2015, Google Inc. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
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 			// Explicitly invalid command, but sane enough capabilities.
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 			// Arbitrary non-sorted order.
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 }