Mercurial > 510Connectbot
annotate src/ch/ethz/ssh2/channel/Channel.java @ 392:7ac1a0b02f44
add test buffer changed when input stream runs dry, similar to async test
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Tue, 16 Sep 2014 16:22:41 -0700 |
parents | 071eccdff8ea |
children |
rev | line source |
---|---|
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
1 /* |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
2 * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
3 * Please refer to the LICENSE.txt for licensing details. |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
4 */ |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
5 package ch.ethz.ssh2.channel; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
6 |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
7 import java.io.IOException; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
8 |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
9 import ch.ethz.ssh2.transport.TransportManager; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
10 |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
11 /** |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
12 * Channel. |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
13 * |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
14 * @author Christian Plattner |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
15 * @version $Id: Channel.java 123 2014-04-12 21:11:47Z dkocher@sudo.ch $ |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
16 */ |
307 | 17 public class Channel { |
18 /* | |
19 * OK. Here is an important part of the JVM Specification: | |
20 * (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214) | |
21 * | |
22 * Any association between locks and variables is purely conventional. | |
23 * Locking any lock conceptually flushes all variables from a thread's | |
24 * working memory, and unlocking any lock forces the writing out to main | |
25 * memory of all variables that the thread has assigned. That a lock may be | |
26 * associated with a particular object or a class is purely a convention. | |
27 * (...) | |
28 * | |
29 * If a thread uses a particular shared variable only after locking a | |
30 * particular lock and before the corresponding unlocking of that same lock, | |
31 * then the thread will read the shared value of that variable from main | |
32 * memory after the lock operation, if necessary, and will copy back to main | |
33 * memory the value most recently assigned to that variable before the | |
34 * unlock operation. | |
35 * | |
36 * This, in conjunction with the mutual exclusion rules for locks, suffices | |
37 * to guarantee that values are correctly transmitted from one thread to | |
38 * another through shared variables. | |
39 * | |
40 * ====> Always keep that in mind when modifying the Channel/ChannelManger | |
41 * code. | |
42 * | |
43 */ | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
44 |
307 | 45 public static final int STATE_OPENING = 1; |
46 public static final int STATE_OPEN = 2; | |
47 public static final int STATE_CLOSED = 4; | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
48 |
307 | 49 static final int CHANNEL_BUFFER_SIZE = 32 * 1024 * 3 * 2; |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
50 |
307 | 51 /* |
52 * To achieve correctness, the following rules have to be respected when | |
53 * accessing this object: | |
54 */ | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
55 |
307 | 56 // These fields can always be read |
57 final ChannelManager cm; | |
58 final ChannelOutputStream stdinStream; | |
59 final ChannelInputStream stdoutStream; | |
60 final ChannelInputStream stderrStream; | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
61 |
307 | 62 // In case this channel belongs to a server-side session. |
63 ServerSessionImpl ss; | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
64 |
307 | 65 // These two fields will only be written while the Channel is in state |
66 // STATE_OPENING. | |
67 // The code makes sure that the two fields are written out when the state is | |
68 // changing to STATE_OPEN. | |
69 // Therefore, if you know that the Channel is in state STATE_OPEN, then you | |
70 // can read these two fields without synchronizing on the Channel. However, make | |
71 // sure that you get the latest values (e.g., flush caches by synchronizing on any | |
72 // object). However, to be on the safe side, you can lock the channel. | |
73 | |
74 int localID = -1; | |
75 int remoteID = -1; | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
76 |
307 | 77 /* |
78 * Make sure that we never send a data/EOF/WindowChange msg after a CLOSE | |
79 * msg. | |
80 * | |
81 * This is a little bit complicated, but we have to do it in that way, since | |
82 * we cannot keep a lock on the Channel during the send operation (this | |
83 * would block sometimes the receiver thread, and, in extreme cases, can | |
84 * lead to a deadlock on both sides of the connection (senders are blocked | |
85 * since the receive buffers on the other side are full, and receiver | |
86 * threads wait for the senders to finish). It all depends on the | |
87 * implementation on the other side. But we cannot make any assumptions, we | |
88 * have to assume the worst case. Confused? Just believe me. | |
89 */ | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
90 |
307 | 91 /* |
92 * If you send a message on a channel, then you have to aquire the | |
93 * "channelSendLock" and check the "closeMessageSent" flag (this variable | |
94 * may only be accessed while holding the "channelSendLock" !!! | |
95 * | |
96 * BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation | |
97 * above. | |
98 */ | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
99 |
307 | 100 final Object channelSendLock = new Object(); |
101 boolean closeMessageSent = false; | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
102 |
307 | 103 /* |
104 * Stop memory fragmentation by allocating this often used buffer. | |
105 * May only be used while holding the channelSendLock | |
106 */ | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
107 |
307 | 108 final byte[] msgWindowAdjust = new byte[9]; |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
109 |
307 | 110 // If you access (read or write) any of the following fields, then you have |
111 // to synchronize on the channel. | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
112 |
307 | 113 int state = STATE_OPENING; |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
114 |
307 | 115 boolean closeMessageRecv = false; |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
116 |
307 | 117 /* This is a stupid implementation. At the moment we can only wait |
118 * for one pending request per channel. | |
119 */ | |
120 int successCounter = 0; | |
121 int failedCounter = 0; | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
122 |
307 | 123 int localWindow = 0; /* locally, we use a small window, < 2^31 */ |
124 long remoteWindow = 0; /* long for readable 2^32 - 1 window support */ | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
125 |
307 | 126 int localMaxPacketSize = -1; |
127 int remoteMaxPacketSize = -1; | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
128 |
307 | 129 final byte[] stdoutBuffer = new byte[CHANNEL_BUFFER_SIZE]; |
130 final byte[] stderrBuffer = new byte[CHANNEL_BUFFER_SIZE]; | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
131 |
307 | 132 int stdoutReadpos = 0; |
133 int stdoutWritepos = 0; | |
134 int stderrReadpos = 0; | |
135 int stderrWritepos = 0; | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
136 |
307 | 137 boolean EOF = false; |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
138 |
307 | 139 Integer exit_status; |
140 | |
141 String exit_signal; | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
142 |
307 | 143 // we keep the x11 cookie so that this channel can be closed when this |
144 // specific x11 forwarding gets stopped | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
145 |
307 | 146 String hexX11FakeCookie; |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
147 |
307 | 148 // reasonClosed is special, since we sometimes need to access it |
149 // while holding the channelSendLock. | |
150 // We protect it with a private short term lock. | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
151 |
307 | 152 private final Object reasonClosedLock = new Object(); |
153 private IOException reasonClosed = null; | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
154 |
307 | 155 public Channel(ChannelManager cm) { |
156 this.cm = cm; | |
157 this.localWindow = CHANNEL_BUFFER_SIZE; | |
158 this.localMaxPacketSize = TransportManager.MAX_PACKET_SIZE; | |
159 this.stdinStream = new ChannelOutputStream(this); | |
160 this.stdoutStream = new ChannelInputStream(this, false); | |
161 this.stderrStream = new ChannelInputStream(this, true); | |
162 } | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
163 |
307 | 164 /* Methods to allow access from classes outside of this package */ |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
165 |
307 | 166 public ChannelInputStream getStderrStream() { |
167 return stderrStream; | |
168 } | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
169 |
307 | 170 public ChannelOutputStream getStdinStream() { |
171 return stdinStream; | |
172 } | |
173 | |
174 public ChannelInputStream getStdoutStream() { | |
175 return stdoutStream; | |
176 } | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
177 |
307 | 178 public String getExitSignal() { |
179 synchronized (this) { | |
180 return exit_signal; | |
181 } | |
182 } | |
183 | |
184 public Integer getExitStatus() { | |
185 synchronized (this) { | |
186 return exit_status; | |
187 } | |
188 } | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
189 |
307 | 190 public IOException getReasonClosed() { |
191 synchronized (reasonClosedLock) { | |
192 return reasonClosed; | |
193 } | |
194 } | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
195 |
307 | 196 public void setReasonClosed(IOException e) { |
197 synchronized (reasonClosedLock) { | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
198 this.reasonClosed = e; |
307 | 199 } |
200 } | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
201 |
307 | 202 public int getState() { |
203 return this.state; | |
204 } | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
diff
changeset
|
205 } |