You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PacketLineIn.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. /*
  2. * Copyright (C) 2008-2010, Google Inc.
  3. * Copyright (C) 2008-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
  4. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  5. * and other copyright owners as documented in the project's IP log.
  6. *
  7. * This program and the accompanying materials are made available
  8. * under the terms of the Eclipse Distribution License v1.0 which
  9. * accompanies this distribution, is reproduced below, and is
  10. * available at http://www.eclipse.org/org/documents/edl-v10.php
  11. *
  12. * All rights reserved.
  13. *
  14. * Redistribution and use in source and binary forms, with or
  15. * without modification, are permitted provided that the following
  16. * conditions are met:
  17. *
  18. * - Redistributions of source code must retain the above copyright
  19. * notice, this list of conditions and the following disclaimer.
  20. *
  21. * - Redistributions in binary form must reproduce the above
  22. * copyright notice, this list of conditions and the following
  23. * disclaimer in the documentation and/or other materials provided
  24. * with the distribution.
  25. *
  26. * - Neither the name of the Eclipse Foundation, Inc. nor the
  27. * names of its contributors may be used to endorse or promote
  28. * products derived from this software without specific prior
  29. * written permission.
  30. *
  31. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  32. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  33. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  34. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  35. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  36. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  37. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  38. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  39. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  40. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  41. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  42. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  43. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44. */
  45. package org.eclipse.jgit.transport;
  46. import static java.nio.charset.StandardCharsets.UTF_8;
  47. import java.io.IOException;
  48. import java.io.InputStream;
  49. import java.io.UncheckedIOException;
  50. import java.text.MessageFormat;
  51. import java.util.Iterator;
  52. import org.eclipse.jgit.errors.PackProtocolException;
  53. import org.eclipse.jgit.internal.JGitText;
  54. import org.eclipse.jgit.lib.MutableObjectId;
  55. import org.eclipse.jgit.util.IO;
  56. import org.eclipse.jgit.util.RawParseUtils;
  57. import org.slf4j.Logger;
  58. import org.slf4j.LoggerFactory;
  59. /**
  60. * Read Git style pkt-line formatting from an input stream.
  61. * <p>
  62. * This class is not thread safe and may issue multiple reads to the underlying
  63. * stream for each method call made.
  64. * <p>
  65. * This class performs no buffering on its own. This makes it suitable to
  66. * interleave reads performed by this class with reads performed directly
  67. * against the underlying InputStream.
  68. */
  69. public class PacketLineIn {
  70. private static final Logger log = LoggerFactory.getLogger(PacketLineIn.class);
  71. /**
  72. * Magic return from {@link #readString()} when a flush packet is found.
  73. *
  74. * @deprecated Callers should use {@link #isEnd(String)} to check if a
  75. * string is the end marker, or
  76. * {@link PacketLineIn#readStrings()} to iterate over all
  77. * strings in the input stream until the marker is reached.
  78. */
  79. @Deprecated
  80. public static final String END = new StringBuilder(0).toString(); /* must not string pool */
  81. /**
  82. * Magic return from {@link #readString()} when a delim packet is found.
  83. *
  84. * @since 5.0
  85. * @deprecated Callers should use {@link #isDelimiter(String)} to check if a
  86. * string is the delimiter.
  87. */
  88. @Deprecated
  89. public static final String DELIM = new StringBuilder(0).toString(); /* must not string pool */
  90. static enum AckNackResult {
  91. /** NAK */
  92. NAK,
  93. /** ACK */
  94. ACK,
  95. /** ACK + continue */
  96. ACK_CONTINUE,
  97. /** ACK + common */
  98. ACK_COMMON,
  99. /** ACK + ready */
  100. ACK_READY;
  101. }
  102. private final byte[] lineBuffer = new byte[SideBandOutputStream.SMALL_BUF];
  103. private final InputStream in;
  104. private long limit;
  105. /**
  106. * Create a new packet line reader.
  107. *
  108. * @param in
  109. * the input stream to consume.
  110. */
  111. public PacketLineIn(InputStream in) {
  112. this(in, 0);
  113. }
  114. /**
  115. * Create a new packet line reader.
  116. *
  117. * @param in
  118. * the input stream to consume.
  119. * @param limit
  120. * bytes to read from the input; unlimited if set to 0.
  121. * @since 4.7
  122. */
  123. public PacketLineIn(InputStream in, long limit) {
  124. this.in = in;
  125. this.limit = limit;
  126. }
  127. AckNackResult readACK(MutableObjectId returnedId) throws IOException {
  128. final String line = readString();
  129. if (line.length() == 0)
  130. throw new PackProtocolException(JGitText.get().expectedACKNAKFoundEOF);
  131. if ("NAK".equals(line)) //$NON-NLS-1$
  132. return AckNackResult.NAK;
  133. if (line.startsWith("ACK ")) { //$NON-NLS-1$
  134. returnedId.fromString(line.substring(4, 44));
  135. if (line.length() == 44)
  136. return AckNackResult.ACK;
  137. final String arg = line.substring(44);
  138. if (arg.equals(" continue")) //$NON-NLS-1$
  139. return AckNackResult.ACK_CONTINUE;
  140. else if (arg.equals(" common")) //$NON-NLS-1$
  141. return AckNackResult.ACK_COMMON;
  142. else if (arg.equals(" ready")) //$NON-NLS-1$
  143. return AckNackResult.ACK_READY;
  144. }
  145. if (line.startsWith("ERR ")) //$NON-NLS-1$
  146. throw new PackProtocolException(line.substring(4));
  147. throw new PackProtocolException(MessageFormat.format(JGitText.get().expectedACKNAKGot, line));
  148. }
  149. /**
  150. * Read a single UTF-8 encoded string packet from the input stream.
  151. * <p>
  152. * If the string ends with an LF, it will be removed before returning the
  153. * value to the caller. If this automatic trimming behavior is not desired,
  154. * use {@link #readStringRaw()} instead.
  155. *
  156. * @return the string. {@link #END} if the string was the magic flush
  157. * packet, {@link #DELIM} if the string was the magic DELIM
  158. * packet.
  159. * @throws java.io.IOException
  160. * the stream cannot be read.
  161. */
  162. public String readString() throws IOException {
  163. int len = readLength();
  164. if (len == 0) {
  165. log.debug("git< 0000"); //$NON-NLS-1$
  166. return END;
  167. }
  168. if (len == 1) {
  169. log.debug("git< 0001"); //$NON-NLS-1$
  170. return DELIM;
  171. }
  172. len -= 4; // length header (4 bytes)
  173. if (len == 0) {
  174. log.debug("git< "); //$NON-NLS-1$
  175. return ""; //$NON-NLS-1$
  176. }
  177. byte[] raw;
  178. if (len <= lineBuffer.length)
  179. raw = lineBuffer;
  180. else
  181. raw = new byte[len];
  182. IO.readFully(in, raw, 0, len);
  183. if (raw[len - 1] == '\n')
  184. len--;
  185. String s = RawParseUtils.decode(UTF_8, raw, 0, len);
  186. log.debug("git< " + s); //$NON-NLS-1$
  187. return s;
  188. }
  189. /**
  190. * Get an iterator to read strings from the input stream.
  191. *
  192. * @return an iterator that calls {@link #readString()} until {@link #END}
  193. * is encountered.
  194. *
  195. * @throws IOException
  196. * on failure to read the initial packet line.
  197. * @since 5.4
  198. */
  199. public PacketLineInIterator readStrings() throws IOException {
  200. return new PacketLineInIterator(this);
  201. }
  202. /**
  203. * Read a single UTF-8 encoded string packet from the input stream.
  204. * <p>
  205. * Unlike {@link #readString()} a trailing LF will be retained.
  206. *
  207. * @return the string. {@link #END} if the string was the magic flush
  208. * packet.
  209. * @throws java.io.IOException
  210. * the stream cannot be read.
  211. */
  212. public String readStringRaw() throws IOException {
  213. int len = readLength();
  214. if (len == 0) {
  215. log.debug("git< 0000"); //$NON-NLS-1$
  216. return END;
  217. }
  218. len -= 4; // length header (4 bytes)
  219. byte[] raw;
  220. if (len <= lineBuffer.length)
  221. raw = lineBuffer;
  222. else
  223. raw = new byte[len];
  224. IO.readFully(in, raw, 0, len);
  225. String s = RawParseUtils.decode(UTF_8, raw, 0, len);
  226. log.debug("git< " + s); //$NON-NLS-1$
  227. return s;
  228. }
  229. /**
  230. * Check if a string is the delimiter marker.
  231. *
  232. * @param s
  233. * the string to check
  234. * @return true if the given string is {@link #DELIM}, otherwise false.
  235. * @since 5.4
  236. */
  237. @SuppressWarnings({ "ReferenceEquality", "StringEquality" })
  238. public static boolean isDelimiter(String s) {
  239. return s == DELIM;
  240. }
  241. /**
  242. * Get the delimiter marker.
  243. * <p>
  244. * Intended for use only in tests.
  245. *
  246. * @return The delimiter marker.
  247. */
  248. static String delimiter() {
  249. return DELIM;
  250. }
  251. /**
  252. * Get the end marker.
  253. * <p>
  254. * Intended for use only in tests.
  255. *
  256. * @return The end marker.
  257. */
  258. static String end() {
  259. return END;
  260. }
  261. /**
  262. * Check if a string is the packet end marker.
  263. *
  264. * @param s
  265. * the string to check
  266. * @return true if the given string is {@link #END}, otherwise false.
  267. * @since 5.4
  268. */
  269. @SuppressWarnings({ "ReferenceEquality", "StringEquality" })
  270. public static boolean isEnd(String s) {
  271. return s == END;
  272. }
  273. void discardUntilEnd() throws IOException {
  274. for (;;) {
  275. int n = readLength();
  276. if (n == 0) {
  277. break;
  278. }
  279. IO.skipFully(in, n - 4);
  280. }
  281. }
  282. int readLength() throws IOException {
  283. IO.readFully(in, lineBuffer, 0, 4);
  284. int len;
  285. try {
  286. len = RawParseUtils.parseHexInt16(lineBuffer, 0);
  287. } catch (ArrayIndexOutOfBoundsException err) {
  288. throw invalidHeader();
  289. }
  290. if (len == 0) {
  291. return 0;
  292. } else if (len == 1) {
  293. return 1;
  294. } else if (len < 4) {
  295. throw invalidHeader();
  296. }
  297. if (limit != 0) {
  298. int n = len - 4;
  299. if (limit < n) {
  300. limit = -1;
  301. try {
  302. IO.skipFully(in, n);
  303. } catch (IOException e) {
  304. // Ignore failure discarding packet over limit.
  305. }
  306. throw new InputOverLimitIOException();
  307. }
  308. // if set limit must not be 0 (means unlimited).
  309. limit = n < limit ? limit - n : -1;
  310. }
  311. return len;
  312. }
  313. private IOException invalidHeader() {
  314. return new IOException(MessageFormat.format(JGitText.get().invalidPacketLineHeader,
  315. "" + (char) lineBuffer[0] + (char) lineBuffer[1] //$NON-NLS-1$
  316. + (char) lineBuffer[2] + (char) lineBuffer[3]));
  317. }
  318. /**
  319. * IOException thrown by read when the configured input limit is exceeded.
  320. *
  321. * @since 4.7
  322. */
  323. public static class InputOverLimitIOException extends IOException {
  324. private static final long serialVersionUID = 1L;
  325. }
  326. /**
  327. * Iterator over packet lines.
  328. * <p>
  329. * Calls {@link #readString()} on the {@link PacketLineIn} until
  330. * {@link #END} is encountered.
  331. *
  332. * @since 5.4
  333. *
  334. */
  335. public static class PacketLineInIterator implements Iterable<String> {
  336. private PacketLineIn in;
  337. private String current;
  338. PacketLineInIterator(PacketLineIn in) throws IOException {
  339. this.in = in;
  340. current = in.readString();
  341. }
  342. @Override
  343. public Iterator<String> iterator() {
  344. return new Iterator<String>() {
  345. @Override
  346. public boolean hasNext() {
  347. return !PacketLineIn.isEnd(current);
  348. }
  349. @Override
  350. public String next() {
  351. String next = current;
  352. try {
  353. current = in.readString();
  354. } catch (IOException e) {
  355. throw new UncheckedIOException(e);
  356. }
  357. return next;
  358. }
  359. };
  360. }
  361. }
  362. }