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 }