com.mysql.cj.protocol.a.CompressedInputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mysql-connector-java Show documentation
Show all versions of mysql-connector-java Show documentation
JDBC Type 4 driver for MySQL
The newest version!
/*
* Copyright (c) 2002, 2020, Oracle and/or its affiliates.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, version 2.0, as published by the
* Free Software Foundation.
*
* This program is also distributed with certain software (including but not
* limited to OpenSSL) that is licensed under separate terms, as designated in a
* particular file or component or in included license documentation. The
* authors of MySQL hereby grant you an additional permission to link the
* program and your derivative works with the separately licensed software that
* they have included with MySQL.
*
* Without limiting anything contained in the foregoing, this file, which is
* part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
* version 1.0, a copy of which can be found at
* http://oss.oracle.com/licenses/universal-foss-exception.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
* for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.mysql.cj.protocol.a;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import com.mysql.cj.conf.RuntimeProperty;
import com.mysql.cj.log.Log;
import com.mysql.cj.util.StringUtils;
/**
* Used to de-compress packets from the MySQL server when protocol-level compression is turned on.
*/
public class CompressedInputStream extends InputStream {
/** The packet data after it has been un-compressed */
private byte[] buffer;
/** The stream we are reading from the server */
private InputStream in;
/** The ZIP inflater used to un-compress packets */
private Inflater inflater;
/** Connection property reference */
private RuntimeProperty traceProtocol;
/** Connection logger */
private Log log;
/**
* The buffer to read packet headers into
*/
private byte[] packetHeaderBuffer = new byte[7];
/** The position we are reading from */
private int pos = 0;
/**
* Creates a new CompressedInputStream that reads the given stream from the
* server.
*
* @param streamFromServer
* original server InputStream
* @param traceProtocol
* "traceProtocol" property
* @param log
* logger
*/
public CompressedInputStream(InputStream streamFromServer, RuntimeProperty traceProtocol, Log log) {
this.traceProtocol = traceProtocol;
this.log = log;
this.in = streamFromServer;
this.inflater = new Inflater();
}
@Override
public int available() throws IOException {
if (this.buffer == null) {
return this.in.available();
}
return this.buffer.length - this.pos + this.in.available();
}
@Override
public void close() throws IOException {
this.in.close();
this.buffer = null;
this.inflater.end();
this.inflater = null;
this.traceProtocol = null;
this.log = null;
}
/**
* Retrieves and un-compressed (if necessary) the next packet from the
* server.
*
* @throws IOException
* if an I/O error occurs
*/
private void getNextPacketFromServer() throws IOException {
byte[] uncompressedData = null;
int lengthRead = readFully(this.packetHeaderBuffer, 0, 7);
if (lengthRead < 7) {
throw new IOException("Unexpected end of input stream");
}
int compressedPacketLength = ((this.packetHeaderBuffer[0] & 0xff)) + (((this.packetHeaderBuffer[1] & 0xff)) << 8)
+ (((this.packetHeaderBuffer[2] & 0xff)) << 16);
int uncompressedLength = ((this.packetHeaderBuffer[4] & 0xff)) + (((this.packetHeaderBuffer[5] & 0xff)) << 8)
+ (((this.packetHeaderBuffer[6] & 0xff)) << 16);
boolean doTrace = this.traceProtocol.getValue();
if (doTrace) {
this.log.logTrace("Reading compressed packet of length " + compressedPacketLength + " uncompressed to " + uncompressedLength);
}
if (uncompressedLength > 0) {
uncompressedData = new byte[uncompressedLength];
byte[] compressedBuffer = new byte[compressedPacketLength];
readFully(compressedBuffer, 0, compressedPacketLength);
this.inflater.reset();
this.inflater.setInput(compressedBuffer);
try {
this.inflater.inflate(uncompressedData);
} catch (DataFormatException dfe) {
throw new IOException("Error while uncompressing packet from server.");
}
} else {
if (doTrace) {
this.log.logTrace("Packet didn't meet compression threshold, not uncompressing...");
}
//
// Read data, note this this code is reached when using compressed packets that have not been compressed, as well
//
uncompressedLength = compressedPacketLength;
uncompressedData = new byte[uncompressedLength];
readFully(uncompressedData, 0, uncompressedLength);
}
if (doTrace) {
if (uncompressedLength > 1024) {
this.log.logTrace("Uncompressed packet: \n" + StringUtils.dumpAsHex(uncompressedData, 256));
byte[] tempData = new byte[256];
System.arraycopy(uncompressedData, uncompressedLength - 256, tempData, 0, 256);
this.log.logTrace("Uncompressed packet: \n" + StringUtils.dumpAsHex(tempData, 256));
this.log.logTrace("Large packet dump truncated. Showing first and last 256 bytes.");
} else {
this.log.logTrace("Uncompressed packet: \n" + StringUtils.dumpAsHex(uncompressedData, uncompressedLength));
}
}
if ((this.buffer != null) && (this.pos < this.buffer.length)) {
if (doTrace) {
this.log.logTrace("Combining remaining packet with new: ");
}
int remaining = this.buffer.length - this.pos;
byte[] newBuffer = new byte[remaining + uncompressedData.length];
System.arraycopy(this.buffer, this.pos, newBuffer, 0, remaining);
System.arraycopy(uncompressedData, 0, newBuffer, remaining, uncompressedData.length);
uncompressedData = newBuffer;
}
this.pos = 0;
this.buffer = uncompressedData;
return;
}
/**
* Determines if another packet needs to be read from the server to be able
* to read numBytes from the stream.
*
* @param numBytes
* the number of bytes to be read
*
* @throws IOException
* if an I/O error occors.
*/
private void getNextPacketIfRequired(int numBytes) throws IOException {
if ((this.buffer == null) || ((this.pos + numBytes) > this.buffer.length)) {
getNextPacketFromServer();
}
}
@Override
public int read() throws IOException {
try {
getNextPacketIfRequired(1);
} catch (IOException ioEx) {
return -1;
}
return this.buffer[this.pos++] & 0xff;
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
}
if (len <= 0) {
return 0;
}
try {
getNextPacketIfRequired(len);
} catch (IOException ioEx) {
return -1;
}
int remainingBufferLength = this.buffer.length - this.pos;
int consummedBytesLength = Math.min(remainingBufferLength, len);
System.arraycopy(this.buffer, this.pos, b, off, consummedBytesLength);
this.pos += consummedBytesLength;
return consummedBytesLength;
}
private final int readFully(byte[] b, int off, int len) throws IOException {
if (len < 0) {
throw new IndexOutOfBoundsException();
}
int n = 0;
while (n < len) {
int count = this.in.read(b, off + n, len - n);
if (count < 0) {
throw new EOFException();
}
n += count;
}
return n;
}
@Override
public long skip(long n) throws IOException {
long count = 0;
for (long i = 0; i < n; i++) {
int bytesRead = read();
if (bytesRead == -1) {
break;
}
count++;
}
return count;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy