1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
* $RCSfile$
* $Revision: $
* $Date: $
*
* Copyright (C) 2005 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.openfire.net;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;
/**
* A <code>TLSStreamWriter</code> that returns a special OutputStream that hides the ByteBuffers
* used by the underlying Channels.
*
* @author Hao Chen
*
*/
public class TLSStreamWriter {
/**
* <code>TLSWrapper</code> is a TLS wrapper for connections requiring TLS protocol.
*/
private TLSWrapper wrapper;
private WritableByteChannel wbc;
private ByteBuffer outAppData;
public TLSStreamWriter(TLSWrapper tlsWrapper, Socket socket) throws IOException {
wrapper = tlsWrapper;
// DANIELE: Add code to use directly the socket channel
if (socket.getChannel() != null) {
wbc = ServerTrafficCounter.wrapWritableChannel(socket.getChannel());
}
else {
wbc = Channels.newChannel(
ServerTrafficCounter.wrapOutputStream(socket.getOutputStream()));
}
outAppData = ByteBuffer.allocate(tlsWrapper.getAppBuffSize());
}
private void doWrite(ByteBuffer buff) throws IOException {
if (buff == null) {
// Possibly handshaking process
buff = ByteBuffer.allocate(0);
}
if (wrapper == null) {
writeToSocket(buff);
} else {
tlsWrite(buff);
}
}
private void tlsWrite(ByteBuffer buf) throws IOException {
ByteBuffer tlsBuffer;
ByteBuffer tlsOutput;
do {
// TODO Consider optimizing by not creating new instances each time
tlsBuffer = ByteBuffer.allocate(Math.min(buf.remaining(), wrapper.getAppBuffSize()));
tlsOutput = ByteBuffer.allocate(wrapper.getNetBuffSize());
while (tlsBuffer.hasRemaining() && buf.hasRemaining()) {
tlsBuffer.put(buf.get());
}
tlsBuffer.flip();
wrapper.wrap(tlsBuffer, tlsOutput);
tlsOutput.flip();
writeToSocket(tlsOutput);
tlsOutput.clear();
} while (buf.hasRemaining());
}
/*
* Writes outNetData to the SocketChannel. <P> Returns true when the ByteBuffer has no remaining
* data.
*/
private boolean writeToSocket(ByteBuffer outNetData) throws IOException {
wbc.write(outNetData);
return !outNetData.hasRemaining();
}
public OutputStream getOutputStream() {
return createOutputStream();
}
/*
* Returns an output stream for a ByteBuffer. The write() methods use the relative ByteBuffer
* put() methods.
*/
private OutputStream createOutputStream() {
return new OutputStream() {
public synchronized void write(int b) throws IOException {
outAppData.put((byte) b);
outAppData.flip();
doWrite(outAppData);
outAppData.clear();
}
public synchronized void write(byte[] bytes, int off, int len) throws IOException {
outAppData = resizeApplicationBuffer(bytes.length);
outAppData.put(bytes, off, len);
outAppData.flip();
doWrite(outAppData);
outAppData.clear();
}
};
}
private ByteBuffer resizeApplicationBuffer(int increment) {
// TODO Creating new buffers and copying over old one may not scale. Consider using views. Thanks to Noah for the tip.
if (outAppData.remaining() < increment) {
ByteBuffer bb = ByteBuffer.allocate(outAppData.capacity() + wrapper.getAppBuffSize());
outAppData.flip();
bb.put(outAppData);
return bb;
} else {
return outAppData;
}
}
}