/** * Read data from the network. The method will return immediately, if there is still some data * left in the buffer, or block until some application data has been read from the network. * * @param buf The buffer where the data will be copied to. * @param offset The position where the data will be placed in the buffer. * @param len The maximum number of bytes to read. * @return The number of bytes read. * @throws IOException If something goes wrong during reading data. */ protected int readApplicationData(byte[] buf, int offset, int len) throws IOException { while (applicationDataQueue.size() == 0) { /* * We need to read some data. */ if (this.closed) { if (this.failedWithError) { /* * Something went terribly wrong, we should throw an IOException */ throw new IOException(TLS_ERROR_MESSAGE); } /* * Connection has been closed, there is no more data to read. */ return -1; } safeReadData(); } len = Math.min(len, applicationDataQueue.size()); applicationDataQueue.read(buf, offset, len, 0); applicationDataQueue.removeData(len); return len; }
protected void processData(short protocol, byte[] buf, int offset, int len) throws IOException { /* * Have a look at the protocol type, and add it to the correct queue. */ switch (protocol) { case ContentType.change_cipher_spec: changeCipherSpecQueue.addData(buf, offset, len); processChangeCipherSpec(); break; case ContentType.alert: alertQueue.addData(buf, offset, len); processAlert(); break; case ContentType.handshake: handshakeQueue.addData(buf, offset, len); processHandshake(); break; case ContentType.application_data: if (!appDataReady) { this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); } applicationDataQueue.addData(buf, offset, len); processApplicationData(); break; default: /* * Uh, we don't know this protocol. * * RFC2246 defines on page 13, that we should ignore this. */ } }
/** * This method is called, when a change cipher spec message is received. * * @throws IOException If the message has an invalid content or the handshake is not in the * correct state. */ private void processChangeCipherSpec() throws IOException { while (changeCipherSpecQueue.size() > 0) { /* * A change cipher spec message is only one byte with the value 1. */ byte[] b = new byte[1]; changeCipherSpecQueue.read(b, 0, 1, 0); changeCipherSpecQueue.removeData(1); if (b[0] != 1) { /* * This should never happen. */ this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); } /* * Check if we are in the correct connection state. */ if (this.connection_state != CS_CLIENT_FINISHED_SEND) { this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure); } rs.serverClientSpecReceived(); this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED; } }
public void testMultipleWords() { String[] words = new String[5]; for (int i = 0; i < words.length; i++) { words[i] = "Word " + i; } OutputStream output = byteQueue.getByteOutputStream(); String[] processedWords = new String[words.length]; try { OutputStreamWriter writer = new OutputStreamWriter(output); for (String w : words) { writer.write(w); writer.write("\n"); } writer.close(); BufferedReader reader = new BufferedReader(new InputStreamReader(byteQueue.getByteInputStream())); String line = null; int index = 0; while ((line = reader.readLine()) != null) { processedWords[index++] = line; } } catch (Exception ex) { ex.printStackTrace(); fail("Error pushing bytes around: " + ex.getMessage()); } for (int i = 0; i < words.length; i++) { String original = words[i]; String processed = processedWords[i]; assertEquals("Word was different after pushed through queue!", original, processed); } }
public int read() throws IOException { if (byteq.count() == 0) { fillBuffer(); if (byteq.count() == 0) { return -1; } } byte val = byteq.dequeue(); if (val >= 0) return val; else return val & 0xFF; }
/** Look for new input from the ptty, send it to the terminal emulator. */ private void readFromProcess() { int bytesAvailable = mByteQueue.getBytesAvailable(); int bytesToRead = Math.min(bytesAvailable, mReceiveBuffer.length); int bytesRead = 0; try { bytesRead = mByteQueue.read(mReceiveBuffer, 0, bytesToRead); } catch (InterruptedException e) { return; } // Give subclasses a chance to process the read data processInput(mReceiveBuffer, 0, bytesRead); notifyUpdate(); }
private void processHandshake() throws IOException { boolean read; do { read = false; /* * We need the first 4 bytes, they contain type and length of the message. */ if (handshakeQueue.size() >= 4) { byte[] beginning = new byte[4]; handshakeQueue.read(beginning, 0, 4, 0); ByteArrayInputStream bis = new ByteArrayInputStream(beginning); short type = TlsUtils.readUint8(bis); int len = TlsUtils.readUint24(bis); /* * Check if we have enough bytes in the buffer to read the full message. */ if (handshakeQueue.size() >= (len + 4)) { /* * Read the message. */ byte[] buf = new byte[len]; handshakeQueue.read(buf, 0, len, 4); handshakeQueue.removeData(len + 4); /* * RFC 2246 7.4.9. The value handshake_messages includes all handshake * messages starting at client hello up to, but not including, this * finished message. [..] Note: [Also,] Hello Request messages are * omitted from handshake hashes. */ switch (type) { case HandshakeType.hello_request: case HandshakeType.finished: break; default: rs.updateHandshakeData(beginning, 0, 4); rs.updateHandshakeData(buf, 0, len); break; } /* * Now, parse the message. */ processHandshakeMessage(type, buf); read = true; } } } while (read); }
public void testHugeWord() { String word = generateRandomWord(8192); String processedWord = null; OutputStream output = byteQueue.getByteOutputStream(); try { OutputStreamWriter writer = new OutputStreamWriter(output); writer.write(word); writer.close(); BufferedReader reader = new BufferedReader(new InputStreamReader(byteQueue.getByteInputStream())); processedWord = reader.readLine(); } catch (IOException ex) { ex.printStackTrace(); fail("Error pushing bytes around: " + ex.getMessage()); } assertEquals("Word was different after pushed through queue!", word, processedWord); }
/** * Add some data to our buffer. * * @param data A byte-array to read data from. * @param offset How many bytes to skip at the beginning of the array. * @param len How many bytes to read from the array. */ public void addData(byte[] data, int offset, int len) { if ((skipped + available + len) > databuf.length) { byte[] tmp = new byte[ByteQueue.nextTwoPow(data.length)]; System.arraycopy(databuf, skipped, tmp, 0, available); skipped = 0; databuf = tmp; } System.arraycopy(data, offset, databuf, skipped + available, len); available += len; }
/** * Write data to the terminal output. The written data will be consumed by the emulation client as * input. * * <p><code>write</code> itself runs on the main thread. The default implementation writes the * data into a circular buffer and signals the writer thread to copy it from there to the {@link * OutputStream}. * * <p>Subclasses may override this method to modify the output before writing it to the stream, * but implementations in derived classes should call through to this method to do the actual * writing. * * @param data An array of bytes to write to the terminal. * @param offset The offset into the array at which the data starts. * @param count The number of bytes to be written. */ public void write(byte[] data, int offset, int count) { try { while (count > 0) { int written = mWriteQueue.write(data, offset, count); offset += written; count -= written; notifyNewOutput(); } } catch (InterruptedException e) { } }
private void decodeAndEnqueue(byte[] data, int len) { int accum = 0; accum |= data[0] << 18; accum |= data[1] << 12; accum |= data[2] << 6; accum |= data[3]; byte b1 = (byte) (accum >>> 16); byteq.enqueue(b1); if (len > 2) { byte b2 = (byte) ((accum >>> 8) & 0xFF); byteq.enqueue(b2); if (len > 3) { byte b3 = (byte) (accum & 0xFF); byteq.enqueue(b3); } } }
private void processAlert() throws IOException { while (alertQueue.size() >= 2) { /* * An alert is always 2 bytes. Read the alert. */ byte[] tmp = new byte[2]; alertQueue.read(tmp, 0, 2, 0); alertQueue.removeData(2); short level = tmp[0]; short description = tmp[1]; if (level == AlertLevel.fatal) { /* * This is a fatal error. */ this.failedWithError = true; this.closed = true; /* * Now try to close the stream, ignore errors. */ try { rs.close(); } catch (Exception e) { } throw new IOException(TLS_ERROR_MESSAGE); } else { /* * This is just a warning. */ if (description == AlertDescription.close_notify) { /* * Close notify */ this.failWithError(AlertLevel.warning, AlertDescription.close_notify); } /* * If it is just a warning, we continue. */ } } }
public void testHugeWordRetrievedInPieces() { String word = generateRandomWord(8192); String processedWord = null; OutputStream output = byteQueue.getByteOutputStream(); try { OutputStreamWriter writer = new OutputStreamWriter(output); writer.write(word); writer.close(); InputStreamReader reader = new InputStreamReader(byteQueue.getByteInputStream()); StringBuffer processed = new StringBuffer(); Random rand = new Random(System.currentTimeMillis()); char[] charBuff = new char[rand.nextInt(32) + 16]; int charsRead = -1; while ((charsRead = reader.read(charBuff)) != -1) { processed.append(new String(charBuff, 0, charsRead)); charBuff = new char[rand.nextInt(32) + 16]; } processedWord = processed.toString(); } catch (IOException ex) { ex.printStackTrace(); fail("Error pushing bytes around: " + ex.getMessage()); } assertEquals("Word was different after pushed through queue!", word, processedWord); }
public void testConcurrentReadWrite() { // generate a bunch of garbage to test final List<String> originalWords = new ArrayList<String>(); for (int i = 0; i < 4; i++) { // originalWords.add(generateRandomWord(8192)); originalWords.add("Word " + i); } // writer and re-reader final OutputStream output = byteQueue.getByteOutputStream(); final InputStream input = byteQueue.getByteInputStream(); // write words in to the buffer every 1000ms Runnable writeRunner = new Runnable() { public void run() { OutputStreamWriter writer = new OutputStreamWriter(output); Iterator<String> wordIter = originalWords.iterator(); while (wordIter.hasNext()) { try { writer.write(wordIter.next()); writer.write('\n'); Thread.sleep(1000); } catch (Exception ex) { ex.printStackTrace(); } } try { writer.close(); } catch (Exception ex) { ex.printStackTrace(); } } }; final List<String> processedWords = new ArrayList<String>(); // read bytes out as fast as possible Runnable readRunner = new Runnable() { public void run() { BufferedReader reader = new BufferedReader(new InputStreamReader(input)); String line = null; try { while ((line = reader.readLine()) != null) { processedWords.add(line); } } catch (Exception ex) { ex.printStackTrace(); } } }; // start up the processes Thread writeThread = new Thread(writeRunner); Thread readThread = new Thread(readRunner); writeThread.start(); readThread.start(); try { writeThread.join(); readThread.join(); } catch (Exception ex) { ex.printStackTrace(); fail("Error waiting for worker threads: " + ex.getMessage()); } assertEquals( "Count of words after processing was not as expected", originalWords.size(), processedWords.size()); for (int i = 0; i < originalWords.size(); i++) { String original = originalWords.get(i); String processed = processedWords.get(i); assertEquals("Processed word was different from original", original, processed); } }