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> and others
5 *
6 * This program and the accompanying materials are made available under the
7 * terms of the Eclipse Distribution License v. 1.0 which is available at
8 * https://www.eclipse.org/org/documents/edl-v10.php.
9 *
10 * SPDX-License-Identifier: BSD-3-Clause
11 */
12
13 package org.eclipse.jgit.transport;
14
15 import static java.nio.charset.StandardCharsets.UTF_8;
16
17 import java.io.IOException;
18 import java.io.OutputStream;
19
20 import org.eclipse.jgit.lib.Constants;
21 import org.eclipse.jgit.util.RawParseUtils;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 /**
26 * Write Git style pkt-line formatting to an output stream.
27 * <p>
28 * This class is not thread safe and may issue multiple writes to the underlying
29 * stream for each method call made.
30 * <p>
31 * This class performs no buffering on its own. This makes it suitable to
32 * interleave writes performed by this class with writes performed directly
33 * against the underlying OutputStream.
34 */
35 public class PacketLineOut {
36 private static final Logger log = LoggerFactory.getLogger(PacketLineOut.class);
37
38 private final OutputStream out;
39
40 private final byte[] lenbuffer;
41
42 private boolean flushOnEnd;
43
44 private boolean usingSideband;
45
46 /**
47 * Create a new packet line writer.
48 *
49 * @param outputStream
50 * stream.
51 */
52 public PacketLineOut(OutputStream outputStream) {
53 out = outputStream;
54 lenbuffer = new byte[5];
55 flushOnEnd = true;
56 }
57
58 /**
59 * Set the flush behavior during {@link #end()}.
60 *
61 * @param flushOnEnd
62 * if true, a flush-pkt written during {@link #end()} also
63 * flushes the underlying stream.
64 */
65 public void setFlushOnEnd(boolean flushOnEnd) {
66 this.flushOnEnd = flushOnEnd;
67 }
68
69 /**
70 * @return whether to add a sideband designator to each non-flush and
71 * non-delim packet
72 * @see #setUsingSideband
73 * @since 5.5
74 */
75 public boolean isUsingSideband() {
76 return usingSideband;
77 }
78
79 /**
80 * @param value If true, when writing packet lines, add, as the first
81 * byte, a sideband designator to each non-flush and non-delim
82 * packet. See pack-protocol.txt and protocol-v2.txt from the Git
83 * project for more information, specifically the "side-band" and
84 * "sideband-all" sections.
85 * @since 5.5
86 */
87 public void setUsingSideband(boolean value) {
88 this.usingSideband = value;
89 }
90
91 /**
92 * Write a UTF-8 encoded string as a single length-delimited packet.
93 *
94 * @param s
95 * string to write.
96 * @throws java.io.IOException
97 * the packet could not be written, the stream is corrupted as
98 * the packet may have been only partially written.
99 */
100 public void writeString(String s) throws IOException {
101 writePacket(Constants.encode(s));
102 }
103
104 /**
105 * Write a binary packet to the stream.
106 *
107 * @param packet
108 * the packet to write; the length of the packet is equal to the
109 * size of the byte array.
110 * @throws java.io.IOException
111 * the packet could not be written, the stream is corrupted as
112 * the packet may have been only partially written.
113 */
114 public void writePacket(byte[] packet) throws IOException {
115 writePacket(packet, 0, packet.length);
116 }
117
118 /**
119 * Write a binary packet to the stream.
120 *
121 * @param buf
122 * the packet to write
123 * @param pos
124 * first index within {@code buf}.
125 * @param len
126 * number of bytes to write.
127 * @throws java.io.IOException
128 * the packet could not be written, the stream is corrupted as
129 * the packet may have been only partially written.
130 * @since 4.5
131 */
132 public void writePacket(byte[] buf, int pos, int len) throws IOException {
133 if (usingSideband) {
134 formatLength(len + 5);
135 out.write(lenbuffer, 0, 4);
136 out.write(1);
137 } else {
138 formatLength(len + 4);
139 out.write(lenbuffer, 0, 4);
140 }
141 out.write(buf, pos, len);
142 if (log.isDebugEnabled()) {
143 String s = RawParseUtils.decode(UTF_8, buf, pos, len);
144 log.debug("git> " + s); //$NON-NLS-1$
145 }
146 }
147
148 /**
149 * Write a packet delim marker (0001).
150 *
151 * @throws java.io.IOException
152 * the marker could not be written, the stream is corrupted
153 * as the marker may have been only partially written.
154 * @since 5.0
155 */
156 public void writeDelim() throws IOException {
157 formatLength(1);
158 out.write(lenbuffer, 0, 4);
159 log.debug("git> 0001"); //$NON-NLS-1$
160 }
161
162 /**
163 * Write a packet end marker, sometimes referred to as a flush command.
164 * <p>
165 * Technically this is a magical packet type which can be detected
166 * separately from an empty string or an empty packet.
167 * <p>
168 * Implicitly performs a flush on the underlying OutputStream to ensure the
169 * peer will receive all data written thus far.
170 *
171 * @throws java.io.IOException
172 * the end marker could not be written, the stream is corrupted
173 * as the end marker may have been only partially written.
174 */
175 public void end() throws IOException {
176 formatLength(0);
177 out.write(lenbuffer, 0, 4);
178 log.debug("git> 0000"); //$NON-NLS-1$
179 if (flushOnEnd)
180 flush();
181 }
182
183 /**
184 * Flush the underlying OutputStream.
185 * <p>
186 * Performs a flush on the underlying OutputStream to ensure the peer will
187 * receive all data written thus far.
188 *
189 * @throws java.io.IOException
190 * the underlying stream failed to flush.
191 */
192 public void flush() throws IOException {
193 out.flush();
194 }
195
196 private static final byte[] hexchar = { '0', '1', '2', '3', '4', '5', '6',
197 '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
198
199 private void formatLength(int w) {
200 formatLength(lenbuffer, w);
201 }
202
203 static void formatLength(byte[] lenbuffer, int w) {
204 int o = 3;
205 while (o >= 0 && w != 0) {
206 lenbuffer[o--] = hexchar[w & 0xf];
207 w >>>= 4;
208 }
209 while (o >= 0)
210 lenbuffer[o--] = '0';
211 }
212 }