mirror of
https://github.com/apache/cloudstack.git
synced 2025-12-16 02:22:52 +01:00
CLOUDSTACK-61 Console proxy has plenty of files with CRLF line ending.
This commit is contained in:
parent
f03d438c4c
commit
0bf8c5a18f
@ -24,42 +24,42 @@ import java.util.Map;
|
|||||||
import com.cloud.consoleproxy.util.Logger;
|
import com.cloud.consoleproxy.util.Logger;
|
||||||
|
|
||||||
public class AjaxFIFOImageCache {
|
public class AjaxFIFOImageCache {
|
||||||
private static final Logger s_logger = Logger.getLogger(AjaxFIFOImageCache.class);
|
private static final Logger s_logger = Logger.getLogger(AjaxFIFOImageCache.class);
|
||||||
|
|
||||||
private List<Integer> fifoQueue;
|
private List<Integer> fifoQueue;
|
||||||
private Map<Integer, byte[]> cache;
|
private Map<Integer, byte[]> cache;
|
||||||
private int cacheSize;
|
private int cacheSize;
|
||||||
private int nextKey = 0;
|
private int nextKey = 0;
|
||||||
|
|
||||||
public AjaxFIFOImageCache(int cacheSize) {
|
public AjaxFIFOImageCache(int cacheSize) {
|
||||||
this.cacheSize = cacheSize;
|
this.cacheSize = cacheSize;
|
||||||
fifoQueue = new ArrayList<Integer>();
|
fifoQueue = new ArrayList<Integer>();
|
||||||
cache = new HashMap<Integer, byte[]>();
|
cache = new HashMap<Integer, byte[]>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void clear() {
|
public synchronized void clear() {
|
||||||
fifoQueue.clear();
|
fifoQueue.clear();
|
||||||
cache.clear();
|
cache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized int putImage(byte[] image) {
|
public synchronized int putImage(byte[] image) {
|
||||||
while(cache.size() >= cacheSize) {
|
while(cache.size() >= cacheSize) {
|
||||||
Integer keyToRemove = fifoQueue.remove(0);
|
Integer keyToRemove = fifoQueue.remove(0);
|
||||||
cache.remove(keyToRemove);
|
cache.remove(keyToRemove);
|
||||||
|
|
||||||
if(s_logger.isTraceEnabled())
|
if(s_logger.isTraceEnabled())
|
||||||
s_logger.trace("Remove image from cache, key: " + keyToRemove);
|
s_logger.trace("Remove image from cache, key: " + keyToRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
int key = getNextKey();
|
int key = getNextKey();
|
||||||
|
|
||||||
if(s_logger.isTraceEnabled())
|
if(s_logger.isTraceEnabled())
|
||||||
s_logger.trace("Add image to cache, key: " + key);
|
s_logger.trace("Add image to cache, key: " + key);
|
||||||
|
|
||||||
cache.put(key, image);
|
cache.put(key, image);
|
||||||
fifoQueue.add(key);
|
fifoQueue.add(key);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized byte[] getImage(int key) {
|
public synchronized byte[] getImage(int key) {
|
||||||
if (key == 0) {
|
if (key == 0) {
|
||||||
|
|||||||
@ -17,17 +17,17 @@
|
|||||||
package com.cloud.consoleproxy;
|
package com.cloud.consoleproxy;
|
||||||
|
|
||||||
public class AuthenticationException extends Exception {
|
public class AuthenticationException extends Exception {
|
||||||
private static final long serialVersionUID = -393139302884898842L;
|
private static final long serialVersionUID = -393139302884898842L;
|
||||||
public AuthenticationException() {
|
public AuthenticationException() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
public AuthenticationException(String s) {
|
public AuthenticationException(String s) {
|
||||||
super(s);
|
super(s);
|
||||||
}
|
}
|
||||||
public AuthenticationException(String message, Throwable cause) {
|
public AuthenticationException(String message, Throwable cause) {
|
||||||
super(message, cause);
|
super(message, cause);
|
||||||
}
|
}
|
||||||
public AuthenticationException(Throwable cause) {
|
public AuthenticationException(Throwable cause) {
|
||||||
super(cause);
|
super(cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,375 +32,375 @@ import com.sun.net.httpserver.HttpExchange;
|
|||||||
import com.sun.net.httpserver.HttpHandler;
|
import com.sun.net.httpserver.HttpHandler;
|
||||||
|
|
||||||
public class ConsoleProxyAjaxHandler implements HttpHandler {
|
public class ConsoleProxyAjaxHandler implements HttpHandler {
|
||||||
private static final Logger s_logger = Logger.getLogger(ConsoleProxyAjaxHandler.class);
|
private static final Logger s_logger = Logger.getLogger(ConsoleProxyAjaxHandler.class);
|
||||||
|
|
||||||
public ConsoleProxyAjaxHandler() {
|
public ConsoleProxyAjaxHandler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handle(HttpExchange t) throws IOException {
|
public void handle(HttpExchange t) throws IOException {
|
||||||
try {
|
try {
|
||||||
if(s_logger.isTraceEnabled())
|
if(s_logger.isTraceEnabled())
|
||||||
s_logger.trace("AjaxHandler " + t.getRequestURI());
|
s_logger.trace("AjaxHandler " + t.getRequestURI());
|
||||||
|
|
||||||
long startTick = System.currentTimeMillis();
|
long startTick = System.currentTimeMillis();
|
||||||
|
|
||||||
doHandle(t);
|
doHandle(t);
|
||||||
|
|
||||||
if(s_logger.isTraceEnabled())
|
if(s_logger.isTraceEnabled())
|
||||||
s_logger.trace(t.getRequestURI() + " process time " + (System.currentTimeMillis() - startTick) + " ms");
|
s_logger.trace(t.getRequestURI() + " process time " + (System.currentTimeMillis() - startTick) + " ms");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
s_logger.warn("Exception, ", e);
|
s_logger.warn("Exception, ", e);
|
||||||
t.sendResponseHeaders(400, -1); // bad request
|
t.sendResponseHeaders(400, -1); // bad request
|
||||||
} catch(Throwable e) {
|
} catch(Throwable e) {
|
||||||
s_logger.error("Unexpected exception, ", e);
|
s_logger.error("Unexpected exception, ", e);
|
||||||
t.sendResponseHeaders(500, -1); // server error
|
t.sendResponseHeaders(500, -1); // server error
|
||||||
} finally {
|
} finally {
|
||||||
t.close();
|
t.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException {
|
private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException {
|
||||||
String queries = t.getRequestURI().getQuery();
|
String queries = t.getRequestURI().getQuery();
|
||||||
if(s_logger.isTraceEnabled())
|
if(s_logger.isTraceEnabled())
|
||||||
s_logger.trace("Handle AJAX request: " + queries);
|
s_logger.trace("Handle AJAX request: " + queries);
|
||||||
|
|
||||||
Map<String, String> queryMap = ConsoleProxyHttpHandlerHelper.getQueryMap(queries);
|
Map<String, String> queryMap = ConsoleProxyHttpHandlerHelper.getQueryMap(queries);
|
||||||
|
|
||||||
String host = queryMap.get("host");
|
String host = queryMap.get("host");
|
||||||
String portStr = queryMap.get("port");
|
String portStr = queryMap.get("port");
|
||||||
String sid = queryMap.get("sid");
|
String sid = queryMap.get("sid");
|
||||||
String tag = queryMap.get("tag");
|
String tag = queryMap.get("tag");
|
||||||
String ticket = queryMap.get("ticket");
|
String ticket = queryMap.get("ticket");
|
||||||
String ajaxSessionIdStr = queryMap.get("sess");
|
String ajaxSessionIdStr = queryMap.get("sess");
|
||||||
String eventStr = queryMap.get("event");
|
String eventStr = queryMap.get("event");
|
||||||
String console_url = queryMap.get("consoleurl");
|
String console_url = queryMap.get("consoleurl");
|
||||||
String console_host_session = queryMap.get("sessionref");
|
String console_host_session = queryMap.get("sessionref");
|
||||||
|
|
||||||
if(tag == null)
|
if(tag == null)
|
||||||
tag = "";
|
tag = "";
|
||||||
|
|
||||||
long ajaxSessionId = 0;
|
long ajaxSessionId = 0;
|
||||||
int event = 0;
|
int event = 0;
|
||||||
|
|
||||||
int port;
|
int port;
|
||||||
|
|
||||||
if(host == null || portStr == null || sid == null)
|
if(host == null || portStr == null || sid == null)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
port = Integer.parseInt(portStr);
|
port = Integer.parseInt(portStr);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
s_logger.warn("Invalid number parameter in query string: " + portStr);
|
s_logger.warn("Invalid number parameter in query string: " + portStr);
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ajaxSessionIdStr != null) {
|
if(ajaxSessionIdStr != null) {
|
||||||
try {
|
try {
|
||||||
ajaxSessionId = Long.parseLong(ajaxSessionIdStr);
|
ajaxSessionId = Long.parseLong(ajaxSessionIdStr);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
s_logger.warn("Invalid number parameter in query string: " + ajaxSessionIdStr);
|
s_logger.warn("Invalid number parameter in query string: " + ajaxSessionIdStr);
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(eventStr != null) {
|
if(eventStr != null) {
|
||||||
try {
|
try {
|
||||||
event = Integer.parseInt(eventStr);
|
event = Integer.parseInt(eventStr);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
s_logger.warn("Invalid number parameter in query string: " + eventStr);
|
s_logger.warn("Invalid number parameter in query string: " + eventStr);
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleProxyClient viewer = null;
|
ConsoleProxyClient viewer = null;
|
||||||
try {
|
try {
|
||||||
ConsoleProxyClientParam param = new ConsoleProxyClientParam();
|
ConsoleProxyClientParam param = new ConsoleProxyClientParam();
|
||||||
param.setClientHostAddress(host);
|
param.setClientHostAddress(host);
|
||||||
param.setClientHostPort(port);
|
param.setClientHostPort(port);
|
||||||
param.setClientHostPassword(sid);
|
param.setClientHostPassword(sid);
|
||||||
param.setClientTag(tag);
|
param.setClientTag(tag);
|
||||||
param.setTicket(ticket);
|
param.setTicket(ticket);
|
||||||
param.setClientTunnelUrl(console_url);
|
param.setClientTunnelUrl(console_url);
|
||||||
param.setClientTunnelSession(console_host_session);
|
param.setClientTunnelSession(console_host_session);
|
||||||
|
|
||||||
viewer = ConsoleProxy.getAjaxVncViewer(param, ajaxSessionIdStr);
|
viewer = ConsoleProxy.getAjaxVncViewer(param, ajaxSessionIdStr);
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
|
|
||||||
s_logger.warn("Failed to create viewer due to " + e.getMessage(), e);
|
s_logger.warn("Failed to create viewer due to " + e.getMessage(), e);
|
||||||
|
|
||||||
String[] content = new String[] {
|
String[] content = new String[] {
|
||||||
"<html><head></head><body>",
|
"<html><head></head><body>",
|
||||||
"<div id=\"main_panel\" tabindex=\"1\">",
|
"<div id=\"main_panel\" tabindex=\"1\">",
|
||||||
"<p>Access is denied for the console session. Please close the window and retry again</p>",
|
"<p>Access is denied for the console session. Please close the window and retry again</p>",
|
||||||
"</div></body></html>"
|
"</div></body></html>"
|
||||||
};
|
};
|
||||||
|
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
for(int i = 0; i < content.length; i++)
|
for(int i = 0; i < content.length; i++)
|
||||||
sb.append(content[i]);
|
sb.append(content[i]);
|
||||||
|
|
||||||
sendResponse(t, "text/html", sb.toString());
|
sendResponse(t, "text/html", sb.toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event != 0) {
|
if(event != 0) {
|
||||||
if(ajaxSessionId != 0 && ajaxSessionId == viewer.getAjaxSessionId()) {
|
if(ajaxSessionId != 0 && ajaxSessionId == viewer.getAjaxSessionId()) {
|
||||||
if(event == 7) {
|
if(event == 7) {
|
||||||
// client send over an event bag
|
// client send over an event bag
|
||||||
InputStream is = t.getRequestBody();
|
InputStream is = t.getRequestBody();
|
||||||
handleClientEventBag(viewer, convertStreamToString(is, true));
|
handleClientEventBag(viewer, convertStreamToString(is, true));
|
||||||
} else {
|
} else {
|
||||||
handleClientEvent(viewer, event, queryMap);
|
handleClientEvent(viewer, event, queryMap);
|
||||||
}
|
}
|
||||||
sendResponse(t, "text/html", "OK");
|
sendResponse(t, "text/html", "OK");
|
||||||
} else {
|
} else {
|
||||||
if(s_logger.isDebugEnabled())
|
if(s_logger.isDebugEnabled())
|
||||||
s_logger.debug("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId());
|
s_logger.debug("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId());
|
||||||
|
|
||||||
sendResponse(t, "text/html", "Invalid ajax client session id");
|
sendResponse(t, "text/html", "Invalid ajax client session id");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(ajaxSessionId != 0 && ajaxSessionId != viewer.getAjaxSessionId()) {
|
if(ajaxSessionId != 0 && ajaxSessionId != viewer.getAjaxSessionId()) {
|
||||||
s_logger.info("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId());
|
s_logger.info("Ajax request comes from a different session, id in request: " + ajaxSessionId + ", id in viewer: " + viewer.getAjaxSessionId());
|
||||||
handleClientKickoff(t, viewer);
|
handleClientKickoff(t, viewer);
|
||||||
} else if(ajaxSessionId == 0) {
|
} else if(ajaxSessionId == 0) {
|
||||||
if(s_logger.isDebugEnabled())
|
if(s_logger.isDebugEnabled())
|
||||||
s_logger.debug("Ajax request indicates a fresh client start");
|
s_logger.debug("Ajax request indicates a fresh client start");
|
||||||
|
|
||||||
String title = queryMap.get("t");
|
String title = queryMap.get("t");
|
||||||
String guest = queryMap.get("guest");
|
String guest = queryMap.get("guest");
|
||||||
handleClientStart(t, viewer, title != null ? title : "", guest);
|
handleClientStart(t, viewer, title != null ? title : "", guest);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if(s_logger.isTraceEnabled())
|
if(s_logger.isTraceEnabled())
|
||||||
s_logger.trace("Ajax request indicates client update");
|
s_logger.trace("Ajax request indicates client update");
|
||||||
|
|
||||||
handleClientUpdate(t, viewer);
|
handleClientUpdate(t, viewer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String convertStreamToString(InputStream is, boolean closeStreamAfterRead) {
|
private static String convertStreamToString(InputStream is, boolean closeStreamAfterRead) {
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
String line = null;
|
String line = null;
|
||||||
try {
|
try {
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
sb.append(line + "\n");
|
sb.append(line + "\n");
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
s_logger.warn("Exception while reading request body: ", e);
|
s_logger.warn("Exception while reading request body: ", e);
|
||||||
} finally {
|
} finally {
|
||||||
if(closeStreamAfterRead) {
|
if(closeStreamAfterRead) {
|
||||||
try {
|
try {
|
||||||
is.close();
|
is.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendResponse(HttpExchange t, String contentType, String response) throws IOException {
|
private void sendResponse(HttpExchange t, String contentType, String response) throws IOException {
|
||||||
Headers hds = t.getResponseHeaders();
|
Headers hds = t.getResponseHeaders();
|
||||||
hds.set("Content-Type", contentType);
|
hds.set("Content-Type", contentType);
|
||||||
|
|
||||||
t.sendResponseHeaders(200, response.length());
|
t.sendResponseHeaders(200, response.length());
|
||||||
OutputStream os = t.getResponseBody();
|
OutputStream os = t.getResponseBody();
|
||||||
try {
|
try {
|
||||||
os.write(response.getBytes());
|
os.write(response.getBytes());
|
||||||
} finally {
|
} finally {
|
||||||
os.close();
|
os.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private void handleClientEventBag(ConsoleProxyClient viewer, String requestData) {
|
private void handleClientEventBag(ConsoleProxyClient viewer, String requestData) {
|
||||||
if(s_logger.isTraceEnabled())
|
if(s_logger.isTraceEnabled())
|
||||||
s_logger.trace("Handle event bag, event bag: " + requestData);
|
s_logger.trace("Handle event bag, event bag: " + requestData);
|
||||||
|
|
||||||
int start = requestData.indexOf("=");
|
int start = requestData.indexOf("=");
|
||||||
if(start < 0)
|
if(start < 0)
|
||||||
start = 0;
|
start = 0;
|
||||||
else if(start > 0)
|
else if(start > 0)
|
||||||
start++;
|
start++;
|
||||||
String data = URLDecoder.decode(requestData.substring(start));
|
String data = URLDecoder.decode(requestData.substring(start));
|
||||||
String[] tokens = data.split("\\|");
|
String[] tokens = data.split("\\|");
|
||||||
if(tokens != null && tokens.length > 0) {
|
if(tokens != null && tokens.length > 0) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
try {
|
try {
|
||||||
count = Integer.parseInt(tokens[0]);
|
count = Integer.parseInt(tokens[0]);
|
||||||
int parsePos = 1;
|
int parsePos = 1;
|
||||||
int type, event, x, y, code, modifiers;
|
int type, event, x, y, code, modifiers;
|
||||||
for(int i = 0; i < count; i++) {
|
for(int i = 0; i < count; i++) {
|
||||||
type = Integer.parseInt(tokens[parsePos++]);
|
type = Integer.parseInt(tokens[parsePos++]);
|
||||||
if(type == 1) {
|
if(type == 1) {
|
||||||
// mouse event
|
// mouse event
|
||||||
event = Integer.parseInt(tokens[parsePos++]);
|
event = Integer.parseInt(tokens[parsePos++]);
|
||||||
x = Integer.parseInt(tokens[parsePos++]);
|
x = Integer.parseInt(tokens[parsePos++]);
|
||||||
y = Integer.parseInt(tokens[parsePos++]);
|
y = Integer.parseInt(tokens[parsePos++]);
|
||||||
code = Integer.parseInt(tokens[parsePos++]);
|
code = Integer.parseInt(tokens[parsePos++]);
|
||||||
modifiers = Integer.parseInt(tokens[parsePos++]);
|
modifiers = Integer.parseInt(tokens[parsePos++]);
|
||||||
|
|
||||||
Map<String, String> queryMap = new HashMap<String, String>();
|
Map<String, String> queryMap = new HashMap<String, String>();
|
||||||
queryMap.put("event", String.valueOf(event));
|
queryMap.put("event", String.valueOf(event));
|
||||||
queryMap.put("x", String.valueOf(x));
|
queryMap.put("x", String.valueOf(x));
|
||||||
queryMap.put("y", String.valueOf(y));
|
queryMap.put("y", String.valueOf(y));
|
||||||
queryMap.put("code", String.valueOf(code));
|
queryMap.put("code", String.valueOf(code));
|
||||||
queryMap.put("modifier", String.valueOf(modifiers));
|
queryMap.put("modifier", String.valueOf(modifiers));
|
||||||
handleClientEvent(viewer, event, queryMap);
|
handleClientEvent(viewer, event, queryMap);
|
||||||
} else {
|
} else {
|
||||||
// keyboard event
|
// keyboard event
|
||||||
event = Integer.parseInt(tokens[parsePos++]);
|
event = Integer.parseInt(tokens[parsePos++]);
|
||||||
code = Integer.parseInt(tokens[parsePos++]);
|
code = Integer.parseInt(tokens[parsePos++]);
|
||||||
modifiers = Integer.parseInt(tokens[parsePos++]);
|
modifiers = Integer.parseInt(tokens[parsePos++]);
|
||||||
|
|
||||||
Map<String, String> queryMap = new HashMap<String, String>();
|
Map<String, String> queryMap = new HashMap<String, String>();
|
||||||
queryMap.put("event", String.valueOf(event));
|
queryMap.put("event", String.valueOf(event));
|
||||||
queryMap.put("code", String.valueOf(code));
|
queryMap.put("code", String.valueOf(code));
|
||||||
queryMap.put("modifier", String.valueOf(modifiers));
|
queryMap.put("modifier", String.valueOf(modifiers));
|
||||||
handleClientEvent(viewer, event, queryMap);
|
handleClientEvent(viewer, event, queryMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(NumberFormatException e) {
|
} catch(NumberFormatException e) {
|
||||||
s_logger.warn("Exception in handle client event bag: " + data + ", ", e);
|
s_logger.warn("Exception in handle client event bag: " + data + ", ", e);
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
s_logger.warn("Exception in handle client event bag: " + data + ", ", e);
|
s_logger.warn("Exception in handle client event bag: " + data + ", ", e);
|
||||||
} catch(OutOfMemoryError e) {
|
} catch(OutOfMemoryError e) {
|
||||||
s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched");
|
s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleClientEvent(ConsoleProxyClient viewer, int event, Map<String, String> queryMap) {
|
private void handleClientEvent(ConsoleProxyClient viewer, int event, Map<String, String> queryMap) {
|
||||||
int code = 0;
|
int code = 0;
|
||||||
int x = 0, y = 0;
|
int x = 0, y = 0;
|
||||||
int modifiers = 0;
|
int modifiers = 0;
|
||||||
|
|
||||||
String str;
|
String str;
|
||||||
switch(event) {
|
switch(event) {
|
||||||
case 1: // mouse move
|
case 1: // mouse move
|
||||||
case 2: // mouse down
|
case 2: // mouse down
|
||||||
case 3: // mouse up
|
case 3: // mouse up
|
||||||
case 8: // mouse double click
|
case 8: // mouse double click
|
||||||
str = queryMap.get("x");
|
str = queryMap.get("x");
|
||||||
if(str != null) {
|
if(str != null) {
|
||||||
try {
|
try {
|
||||||
x = Integer.parseInt(str);
|
x = Integer.parseInt(str);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
s_logger.warn("Invalid number parameter in query string: " + str);
|
s_logger.warn("Invalid number parameter in query string: " + str);
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
str = queryMap.get("y");
|
str = queryMap.get("y");
|
||||||
if(str != null) {
|
if(str != null) {
|
||||||
try {
|
try {
|
||||||
y = Integer.parseInt(str);
|
y = Integer.parseInt(str);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
s_logger.warn("Invalid number parameter in query string: " + str);
|
s_logger.warn("Invalid number parameter in query string: " + str);
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(event != 1) {
|
if(event != 1) {
|
||||||
str = queryMap.get("code");
|
str = queryMap.get("code");
|
||||||
try {
|
try {
|
||||||
code = Integer.parseInt(str);
|
code = Integer.parseInt(str);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
s_logger.warn("Invalid number parameter in query string: " + str);
|
s_logger.warn("Invalid number parameter in query string: " + str);
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
str = queryMap.get("modifier");
|
str = queryMap.get("modifier");
|
||||||
try {
|
try {
|
||||||
modifiers = Integer.parseInt(str);
|
modifiers = Integer.parseInt(str);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
s_logger.warn("Invalid number parameter in query string: " + str);
|
s_logger.warn("Invalid number parameter in query string: " + str);
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(s_logger.isTraceEnabled())
|
if(s_logger.isTraceEnabled())
|
||||||
s_logger.trace("Handle client mouse event. event: " + event + ", x: " + x + ", y: " + y + ", button: " + code + ", modifier: " + modifiers);
|
s_logger.trace("Handle client mouse event. event: " + event + ", x: " + x + ", y: " + y + ", button: " + code + ", modifier: " + modifiers);
|
||||||
} else {
|
} else {
|
||||||
if(s_logger.isTraceEnabled())
|
if(s_logger.isTraceEnabled())
|
||||||
s_logger.trace("Handle client mouse move event. x: " + x + ", y: " + y);
|
s_logger.trace("Handle client mouse move event. x: " + x + ", y: " + y);
|
||||||
}
|
}
|
||||||
viewer.sendClientMouseEvent(InputEventType.fromEventCode(event), x, y, code, modifiers);
|
viewer.sendClientMouseEvent(InputEventType.fromEventCode(event), x, y, code, modifiers);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4: // key press
|
case 4: // key press
|
||||||
case 5: // key down
|
case 5: // key down
|
||||||
case 6: // key up
|
case 6: // key up
|
||||||
str = queryMap.get("code");
|
str = queryMap.get("code");
|
||||||
try {
|
try {
|
||||||
code = Integer.parseInt(str);
|
code = Integer.parseInt(str);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
s_logger.warn("Invalid number parameter in query string: " + str);
|
s_logger.warn("Invalid number parameter in query string: " + str);
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
str = queryMap.get("modifier");
|
str = queryMap.get("modifier");
|
||||||
try {
|
try {
|
||||||
modifiers = Integer.parseInt(str);
|
modifiers = Integer.parseInt(str);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
s_logger.warn("Invalid number parameter in query string: " + str);
|
s_logger.warn("Invalid number parameter in query string: " + str);
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(s_logger.isDebugEnabled())
|
if(s_logger.isDebugEnabled())
|
||||||
s_logger.debug("Handle client keyboard event. event: " + event + ", code: " + code + ", modifier: " + modifiers);
|
s_logger.debug("Handle client keyboard event. event: " + event + ", code: " + code + ", modifier: " + modifiers);
|
||||||
viewer.sendClientRawKeyboardEvent(InputEventType.fromEventCode(event), code, modifiers);
|
viewer.sendClientRawKeyboardEvent(InputEventType.fromEventCode(event), code, modifiers);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default :
|
default :
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleClientKickoff(HttpExchange t, ConsoleProxyClient viewer) throws IOException {
|
private void handleClientKickoff(HttpExchange t, ConsoleProxyClient viewer) throws IOException {
|
||||||
String response = viewer.onAjaxClientKickoff();
|
String response = viewer.onAjaxClientKickoff();
|
||||||
t.sendResponseHeaders(200, response.length());
|
t.sendResponseHeaders(200, response.length());
|
||||||
OutputStream os = t.getResponseBody();
|
OutputStream os = t.getResponseBody();
|
||||||
try {
|
try {
|
||||||
os.write(response.getBytes());
|
os.write(response.getBytes());
|
||||||
} finally {
|
} finally {
|
||||||
os.close();
|
os.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleClientStart(HttpExchange t, ConsoleProxyClient viewer, String title, String guest) throws IOException {
|
private void handleClientStart(HttpExchange t, ConsoleProxyClient viewer, String title, String guest) throws IOException {
|
||||||
List<String> languages = t.getRequestHeaders().get("Accept-Language");
|
List<String> languages = t.getRequestHeaders().get("Accept-Language");
|
||||||
String response = viewer.onAjaxClientStart(title, languages, guest);
|
String response = viewer.onAjaxClientStart(title, languages, guest);
|
||||||
|
|
||||||
Headers hds = t.getResponseHeaders();
|
Headers hds = t.getResponseHeaders();
|
||||||
hds.set("Content-Type", "text/html");
|
hds.set("Content-Type", "text/html");
|
||||||
hds.set("Cache-Control", "no-cache");
|
hds.set("Cache-Control", "no-cache");
|
||||||
hds.set("Cache-Control", "no-store");
|
hds.set("Cache-Control", "no-store");
|
||||||
t.sendResponseHeaders(200, response.length());
|
t.sendResponseHeaders(200, response.length());
|
||||||
|
|
||||||
OutputStream os = t.getResponseBody();
|
OutputStream os = t.getResponseBody();
|
||||||
try {
|
try {
|
||||||
os.write(response.getBytes());
|
os.write(response.getBytes());
|
||||||
} finally {
|
} finally {
|
||||||
os.close();
|
os.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleClientUpdate(HttpExchange t, ConsoleProxyClient viewer) throws IOException {
|
private void handleClientUpdate(HttpExchange t, ConsoleProxyClient viewer) throws IOException {
|
||||||
String response = viewer.onAjaxClientUpdate();
|
String response = viewer.onAjaxClientUpdate();
|
||||||
|
|
||||||
Headers hds = t.getResponseHeaders();
|
Headers hds = t.getResponseHeaders();
|
||||||
hds.set("Content-Type", "text/javascript");
|
hds.set("Content-Type", "text/javascript");
|
||||||
t.sendResponseHeaders(200, response.length());
|
t.sendResponseHeaders(200, response.length());
|
||||||
|
|
||||||
OutputStream os = t.getResponseBody();
|
OutputStream os = t.getResponseBody();
|
||||||
try {
|
try {
|
||||||
os.write(response.getBytes());
|
os.write(response.getBytes());
|
||||||
} finally {
|
} finally {
|
||||||
os.close();
|
os.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,47 +30,47 @@ import com.sun.net.httpserver.HttpExchange;
|
|||||||
import com.sun.net.httpserver.HttpHandler;
|
import com.sun.net.httpserver.HttpHandler;
|
||||||
|
|
||||||
public class ConsoleProxyAjaxImageHandler implements HttpHandler {
|
public class ConsoleProxyAjaxImageHandler implements HttpHandler {
|
||||||
private static final Logger s_logger = Logger.getLogger(ConsoleProxyAjaxImageHandler.class);
|
private static final Logger s_logger = Logger.getLogger(ConsoleProxyAjaxImageHandler.class);
|
||||||
|
|
||||||
public void handle(HttpExchange t) throws IOException {
|
public void handle(HttpExchange t) throws IOException {
|
||||||
try {
|
try {
|
||||||
if(s_logger.isDebugEnabled())
|
if(s_logger.isDebugEnabled())
|
||||||
s_logger.debug("AjaxImageHandler " + t.getRequestURI());
|
s_logger.debug("AjaxImageHandler " + t.getRequestURI());
|
||||||
|
|
||||||
long startTick = System.currentTimeMillis();
|
long startTick = System.currentTimeMillis();
|
||||||
|
|
||||||
doHandle(t);
|
doHandle(t);
|
||||||
|
|
||||||
if(s_logger.isDebugEnabled())
|
if(s_logger.isDebugEnabled())
|
||||||
s_logger.debug(t.getRequestURI() + "Process time " + (System.currentTimeMillis() - startTick) + " ms");
|
s_logger.debug(t.getRequestURI() + "Process time " + (System.currentTimeMillis() - startTick) + " ms");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
s_logger.warn("Exception, ", e);
|
s_logger.warn("Exception, ", e);
|
||||||
t.sendResponseHeaders(400, -1); // bad request
|
t.sendResponseHeaders(400, -1); // bad request
|
||||||
} catch(OutOfMemoryError e) {
|
} catch(OutOfMemoryError e) {
|
||||||
s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched");
|
s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
} catch(Throwable e) {
|
} catch(Throwable e) {
|
||||||
s_logger.error("Unexpected exception, ", e);
|
s_logger.error("Unexpected exception, ", e);
|
||||||
t.sendResponseHeaders(500, -1); // server error
|
t.sendResponseHeaders(500, -1); // server error
|
||||||
} finally {
|
} finally {
|
||||||
t.close();
|
t.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException {
|
private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException {
|
||||||
String queries = t.getRequestURI().getQuery();
|
String queries = t.getRequestURI().getQuery();
|
||||||
Map<String, String> queryMap = ConsoleProxyHttpHandlerHelper.getQueryMap(queries);
|
Map<String, String> queryMap = ConsoleProxyHttpHandlerHelper.getQueryMap(queries);
|
||||||
|
|
||||||
String host = queryMap.get("host");
|
String host = queryMap.get("host");
|
||||||
String portStr = queryMap.get("port");
|
String portStr = queryMap.get("port");
|
||||||
String sid = queryMap.get("sid");
|
String sid = queryMap.get("sid");
|
||||||
String tag = queryMap.get("tag");
|
String tag = queryMap.get("tag");
|
||||||
String ticket = queryMap.get("ticket");
|
String ticket = queryMap.get("ticket");
|
||||||
String keyStr = queryMap.get("key");
|
String keyStr = queryMap.get("key");
|
||||||
String console_url = queryMap.get("consoleurl");
|
String console_url = queryMap.get("consoleurl");
|
||||||
String console_host_session = queryMap.get("sessionref");
|
String console_host_session = queryMap.get("sessionref");
|
||||||
String w = queryMap.get("w");
|
String w = queryMap.get("w");
|
||||||
String h = queryMap.get("h");
|
String h = queryMap.get("h");
|
||||||
|
|
||||||
@ -78,23 +78,23 @@ public class ConsoleProxyAjaxImageHandler implements HttpHandler {
|
|||||||
int width = 144;
|
int width = 144;
|
||||||
int height = 110;
|
int height = 110;
|
||||||
|
|
||||||
if(tag == null)
|
if(tag == null)
|
||||||
tag = "";
|
tag = "";
|
||||||
|
|
||||||
int port;
|
int port;
|
||||||
if(host == null || portStr == null || sid == null)
|
if(host == null || portStr == null || sid == null)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
port = Integer.parseInt(portStr);
|
port = Integer.parseInt(portStr);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
s_logger.warn("Invalid numeric parameter in query string: " + portStr);
|
s_logger.warn("Invalid numeric parameter in query string: " + portStr);
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (keyStr != null)
|
if (keyStr != null)
|
||||||
key = Integer.parseInt(keyStr);
|
key = Integer.parseInt(keyStr);
|
||||||
if(null != w)
|
if(null != w)
|
||||||
width = Integer.parseInt(w);
|
width = Integer.parseInt(w);
|
||||||
|
|
||||||
@ -102,58 +102,58 @@ public class ConsoleProxyAjaxImageHandler implements HttpHandler {
|
|||||||
height = Integer.parseInt(h);
|
height = Integer.parseInt(h);
|
||||||
|
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
s_logger.warn("Invalid numeric parameter in query string: " + keyStr);
|
s_logger.warn("Invalid numeric parameter in query string: " + keyStr);
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleProxyClientParam param = new ConsoleProxyClientParam();
|
ConsoleProxyClientParam param = new ConsoleProxyClientParam();
|
||||||
param.setClientHostAddress(host);
|
param.setClientHostAddress(host);
|
||||||
param.setClientHostPort(port);
|
param.setClientHostPort(port);
|
||||||
param.setClientHostPassword(sid);
|
param.setClientHostPassword(sid);
|
||||||
param.setClientTag(tag);
|
param.setClientTag(tag);
|
||||||
param.setTicket(ticket);
|
param.setTicket(ticket);
|
||||||
param.setClientTunnelUrl(console_url);
|
param.setClientTunnelUrl(console_url);
|
||||||
param.setClientTunnelSession(console_host_session);
|
param.setClientTunnelSession(console_host_session);
|
||||||
|
|
||||||
ConsoleProxyClient viewer = ConsoleProxy.getVncViewer(param);
|
ConsoleProxyClient viewer = ConsoleProxy.getVncViewer(param);
|
||||||
|
|
||||||
if (key == 0) {
|
if (key == 0) {
|
||||||
Image scaledImage = viewer.getClientScaledImage(width, height);
|
Image scaledImage = viewer.getClientScaledImage(width, height);
|
||||||
BufferedImage bufferedImage = new BufferedImage(width, height,
|
BufferedImage bufferedImage = new BufferedImage(width, height,
|
||||||
BufferedImage.TYPE_3BYTE_BGR);
|
BufferedImage.TYPE_3BYTE_BGR);
|
||||||
Graphics2D bufImageGraphics = bufferedImage.createGraphics();
|
Graphics2D bufImageGraphics = bufferedImage.createGraphics();
|
||||||
bufImageGraphics.drawImage(scaledImage, 0, 0, null);
|
bufImageGraphics.drawImage(scaledImage, 0, 0, null);
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(8196);
|
ByteArrayOutputStream bos = new ByteArrayOutputStream(8196);
|
||||||
javax.imageio.ImageIO.write(bufferedImage, "jpg", bos);
|
javax.imageio.ImageIO.write(bufferedImage, "jpg", bos);
|
||||||
byte[] bs = bos.toByteArray();
|
byte[] bs = bos.toByteArray();
|
||||||
Headers hds = t.getResponseHeaders();
|
Headers hds = t.getResponseHeaders();
|
||||||
hds.set("Content-Type", "image/jpeg");
|
hds.set("Content-Type", "image/jpeg");
|
||||||
hds.set("Cache-Control", "no-cache");
|
hds.set("Cache-Control", "no-cache");
|
||||||
hds.set("Cache-Control", "no-store");
|
hds.set("Cache-Control", "no-store");
|
||||||
t.sendResponseHeaders(200, bs.length);
|
t.sendResponseHeaders(200, bs.length);
|
||||||
OutputStream os = t.getResponseBody();
|
OutputStream os = t.getResponseBody();
|
||||||
os.write(bs);
|
os.write(bs);
|
||||||
os.close();
|
os.close();
|
||||||
} else {
|
} else {
|
||||||
AjaxFIFOImageCache imageCache = viewer.getAjaxImageCache();
|
AjaxFIFOImageCache imageCache = viewer.getAjaxImageCache();
|
||||||
byte[] img = imageCache.getImage(key);
|
byte[] img = imageCache.getImage(key);
|
||||||
|
|
||||||
if(img != null) {
|
if(img != null) {
|
||||||
Headers hds = t.getResponseHeaders();
|
Headers hds = t.getResponseHeaders();
|
||||||
hds.set("Content-Type", "image/jpeg");
|
hds.set("Content-Type", "image/jpeg");
|
||||||
t.sendResponseHeaders(200, img.length);
|
t.sendResponseHeaders(200, img.length);
|
||||||
|
|
||||||
OutputStream os = t.getResponseBody();
|
OutputStream os = t.getResponseBody();
|
||||||
try {
|
try {
|
||||||
os.write(img, 0, img.length);
|
os.write(img, 0, img.length);
|
||||||
} finally {
|
} finally {
|
||||||
os.close();
|
os.close();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(s_logger.isInfoEnabled())
|
if(s_logger.isInfoEnabled())
|
||||||
s_logger.info("Image has already been swept out, key: " + key);
|
s_logger.info("Image has already been swept out, key: " + key);
|
||||||
t.sendResponseHeaders(404, -1);
|
t.sendResponseHeaders(404, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,64 +18,64 @@ package com.cloud.consoleproxy;
|
|||||||
|
|
||||||
// duplicated class
|
// duplicated class
|
||||||
public class ConsoleProxyAuthenticationResult {
|
public class ConsoleProxyAuthenticationResult {
|
||||||
private boolean success;
|
private boolean success;
|
||||||
private boolean isReauthentication;
|
private boolean isReauthentication;
|
||||||
private String host;
|
private String host;
|
||||||
private int port;
|
private int port;
|
||||||
private String tunnelUrl;
|
private String tunnelUrl;
|
||||||
private String tunnelSession;
|
private String tunnelSession;
|
||||||
|
|
||||||
public ConsoleProxyAuthenticationResult() {
|
public ConsoleProxyAuthenticationResult() {
|
||||||
success = false;
|
success = false;
|
||||||
isReauthentication = false;
|
isReauthentication = false;
|
||||||
port = 0;
|
port = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSuccess() {
|
public boolean isSuccess() {
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSuccess(boolean success) {
|
public void setSuccess(boolean success) {
|
||||||
this.success = success;
|
this.success = success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isReauthentication() {
|
public boolean isReauthentication() {
|
||||||
return isReauthentication;
|
return isReauthentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReauthentication(boolean isReauthentication) {
|
public void setReauthentication(boolean isReauthentication) {
|
||||||
this.isReauthentication = isReauthentication;
|
this.isReauthentication = isReauthentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getHost() {
|
public String getHost() {
|
||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHost(String host) {
|
public void setHost(String host) {
|
||||||
this.host = host;
|
this.host = host;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPort() {
|
public int getPort() {
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPort(int port) {
|
public void setPort(int port) {
|
||||||
this.port = port;
|
this.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTunnelUrl() {
|
public String getTunnelUrl() {
|
||||||
return tunnelUrl;
|
return tunnelUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTunnelUrl(String tunnelUrl) {
|
public void setTunnelUrl(String tunnelUrl) {
|
||||||
this.tunnelUrl = tunnelUrl;
|
this.tunnelUrl = tunnelUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTunnelSession() {
|
public String getTunnelSession() {
|
||||||
return tunnelSession;
|
return tunnelSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTunnelSession(String tunnelSession) {
|
public void setTunnelSession(String tunnelSession) {
|
||||||
this.tunnelSession = tunnelSession;
|
this.tunnelSession = tunnelSession;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,24 +25,24 @@ import com.cloud.consoleproxy.util.Logger;
|
|||||||
import com.sun.net.httpserver.HttpServer;
|
import com.sun.net.httpserver.HttpServer;
|
||||||
|
|
||||||
public class ConsoleProxyBaseServerFactoryImpl implements ConsoleProxyServerFactory {
|
public class ConsoleProxyBaseServerFactoryImpl implements ConsoleProxyServerFactory {
|
||||||
private static final Logger s_logger = Logger.getLogger(ConsoleProxyBaseServerFactoryImpl.class);
|
private static final Logger s_logger = Logger.getLogger(ConsoleProxyBaseServerFactoryImpl.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(byte[] ksBits, String ksPassword) {
|
public void init(byte[] ksBits, String ksPassword) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpServer createHttpServerInstance(int port) throws IOException {
|
public HttpServer createHttpServerInstance(int port) throws IOException {
|
||||||
if(s_logger.isInfoEnabled())
|
if(s_logger.isInfoEnabled())
|
||||||
s_logger.info("create HTTP server instance at port: " + port);
|
s_logger.info("create HTTP server instance at port: " + port);
|
||||||
return HttpServer.create(new InetSocketAddress(port), 5);
|
return HttpServer.create(new InetSocketAddress(port), 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SSLServerSocket createSSLServerSocket(int port) throws IOException {
|
public SSLServerSocket createSSLServerSocket(int port) throws IOException {
|
||||||
if(s_logger.isInfoEnabled())
|
if(s_logger.isInfoEnabled())
|
||||||
s_logger.info("SSL server socket is not supported in ConsoleProxyBaseServerFactoryImpl");
|
s_logger.info("SSL server socket is not supported in ConsoleProxyBaseServerFactoryImpl");
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,9 +17,9 @@
|
|||||||
package com.cloud.consoleproxy;
|
package com.cloud.consoleproxy;
|
||||||
|
|
||||||
public interface ConsoleProxyClientListener {
|
public interface ConsoleProxyClientListener {
|
||||||
void onFramebufferSizeChange(int w, int h);
|
void onFramebufferSizeChange(int w, int h);
|
||||||
void onFramebufferUpdate(int x, int y, int w, int h);
|
void onFramebufferUpdate(int x, int y, int w, int h);
|
||||||
|
|
||||||
void onClientConnected();
|
void onClientConnected();
|
||||||
void onClientClose();
|
void onClientClose();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,45 +26,45 @@ import com.sun.net.httpserver.HttpExchange;
|
|||||||
import com.sun.net.httpserver.HttpHandler;
|
import com.sun.net.httpserver.HttpHandler;
|
||||||
|
|
||||||
public class ConsoleProxyCmdHandler implements HttpHandler {
|
public class ConsoleProxyCmdHandler implements HttpHandler {
|
||||||
private static final Logger s_logger = Logger.getLogger(ConsoleProxyCmdHandler.class);
|
private static final Logger s_logger = Logger.getLogger(ConsoleProxyCmdHandler.class);
|
||||||
|
|
||||||
public void handle(HttpExchange t) throws IOException {
|
public void handle(HttpExchange t) throws IOException {
|
||||||
try {
|
try {
|
||||||
Thread.currentThread().setName("Cmd Thread " +
|
Thread.currentThread().setName("Cmd Thread " +
|
||||||
Thread.currentThread().getId() + " " + t.getRemoteAddress());
|
Thread.currentThread().getId() + " " + t.getRemoteAddress());
|
||||||
s_logger.info("CmdHandler " + t.getRequestURI());
|
s_logger.info("CmdHandler " + t.getRequestURI());
|
||||||
doHandle(t);
|
doHandle(t);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
s_logger.error(e.toString(), e);
|
s_logger.error(e.toString(), e);
|
||||||
String response = "Not found";
|
String response = "Not found";
|
||||||
t.sendResponseHeaders(404, response.length());
|
t.sendResponseHeaders(404, response.length());
|
||||||
OutputStream os = t.getResponseBody();
|
OutputStream os = t.getResponseBody();
|
||||||
os.write(response.getBytes());
|
os.write(response.getBytes());
|
||||||
os.close();
|
os.close();
|
||||||
} catch(OutOfMemoryError e) {
|
} catch(OutOfMemoryError e) {
|
||||||
s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched");
|
s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
s_logger.error(e.toString(), e);
|
s_logger.error(e.toString(), e);
|
||||||
} finally {
|
} finally {
|
||||||
t.close();
|
t.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void doHandle(HttpExchange t) throws Exception {
|
public void doHandle(HttpExchange t) throws Exception {
|
||||||
String path = t.getRequestURI().getPath();
|
String path = t.getRequestURI().getPath();
|
||||||
int i = path.indexOf("/", 1);
|
int i = path.indexOf("/", 1);
|
||||||
String cmd = path.substring(i + 1);
|
String cmd = path.substring(i + 1);
|
||||||
s_logger.info("Get CMD request for " + cmd);
|
s_logger.info("Get CMD request for " + cmd);
|
||||||
if (cmd.equals("getstatus")) {
|
if (cmd.equals("getstatus")) {
|
||||||
ConsoleProxyClientStatsCollector statsCollector = ConsoleProxy.getStatsCollector();
|
ConsoleProxyClientStatsCollector statsCollector = ConsoleProxy.getStatsCollector();
|
||||||
|
|
||||||
Headers hds = t.getResponseHeaders();
|
Headers hds = t.getResponseHeaders();
|
||||||
hds.set("Content-Type", "text/plain");
|
hds.set("Content-Type", "text/plain");
|
||||||
t.sendResponseHeaders(200, 0);
|
t.sendResponseHeaders(200, 0);
|
||||||
OutputStreamWriter os = new OutputStreamWriter(t.getResponseBody());
|
OutputStreamWriter os = new OutputStreamWriter(t.getResponseBody());
|
||||||
statsCollector.getStatsReport(os);
|
statsCollector.getStatsReport(os);
|
||||||
os.close();
|
os.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,53 +22,53 @@ import java.util.Map;
|
|||||||
import com.cloud.consoleproxy.util.Logger;
|
import com.cloud.consoleproxy.util.Logger;
|
||||||
|
|
||||||
public class ConsoleProxyHttpHandlerHelper {
|
public class ConsoleProxyHttpHandlerHelper {
|
||||||
private static final Logger s_logger = Logger.getLogger(ConsoleProxyHttpHandlerHelper.class);
|
private static final Logger s_logger = Logger.getLogger(ConsoleProxyHttpHandlerHelper.class);
|
||||||
|
|
||||||
public static Map<String, String> getQueryMap(String query) {
|
public static Map<String, String> getQueryMap(String query) {
|
||||||
String[] params = query.split("&");
|
String[] params = query.split("&");
|
||||||
Map<String, String> map = new HashMap<String, String>();
|
Map<String, String> map = new HashMap<String, String>();
|
||||||
for (String param : params) {
|
for (String param : params) {
|
||||||
String[] paramTokens = param.split("=");
|
String[] paramTokens = param.split("=");
|
||||||
if(paramTokens != null && paramTokens.length == 2) {
|
if(paramTokens != null && paramTokens.length == 2) {
|
||||||
String name = param.split("=")[0];
|
String name = param.split("=")[0];
|
||||||
String value = param.split("=")[1];
|
String value = param.split("=")[1];
|
||||||
map.put(name, value);
|
map.put(name, value);
|
||||||
} else if (paramTokens.length == 3) {
|
} else if (paramTokens.length == 3) {
|
||||||
// very ugly, added for Xen tunneling url
|
// very ugly, added for Xen tunneling url
|
||||||
String name = paramTokens[0];
|
String name = paramTokens[0];
|
||||||
String value = paramTokens[1] + "=" + paramTokens[2];
|
String value = paramTokens[1] + "=" + paramTokens[2];
|
||||||
map.put(name, value);
|
map.put(name, value);
|
||||||
} else {
|
} else {
|
||||||
if(s_logger.isDebugEnabled())
|
if(s_logger.isDebugEnabled())
|
||||||
s_logger.debug("Invalid paramemter in URL found. param: " + param);
|
s_logger.debug("Invalid paramemter in URL found. param: " + param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a ugly solution for now. We will do encryption/decryption translation
|
// This is a ugly solution for now. We will do encryption/decryption translation
|
||||||
// here to make it transparent to rest of the code.
|
// here to make it transparent to rest of the code.
|
||||||
if(map.get("token") != null) {
|
if(map.get("token") != null) {
|
||||||
ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor(
|
ConsoleProxyPasswordBasedEncryptor encryptor = new ConsoleProxyPasswordBasedEncryptor(
|
||||||
ConsoleProxy.getEncryptorPassword());
|
ConsoleProxy.getEncryptorPassword());
|
||||||
|
|
||||||
ConsoleProxyClientParam param = encryptor.decryptObject(ConsoleProxyClientParam.class, map.get("token"));
|
ConsoleProxyClientParam param = encryptor.decryptObject(ConsoleProxyClientParam.class, map.get("token"));
|
||||||
if(param != null) {
|
if(param != null) {
|
||||||
if(param.getClientHostAddress() != null)
|
if(param.getClientHostAddress() != null)
|
||||||
map.put("host", param.getClientHostAddress());
|
map.put("host", param.getClientHostAddress());
|
||||||
if(param.getClientHostPort() != 0)
|
if(param.getClientHostPort() != 0)
|
||||||
map.put("port", String.valueOf(param.getClientHostPort()));
|
map.put("port", String.valueOf(param.getClientHostPort()));
|
||||||
if(param.getClientTag() != null)
|
if(param.getClientTag() != null)
|
||||||
map.put("tag", param.getClientTag());
|
map.put("tag", param.getClientTag());
|
||||||
if(param.getClientHostPassword() != null)
|
if(param.getClientHostPassword() != null)
|
||||||
map.put("sid", param.getClientHostPassword());
|
map.put("sid", param.getClientHostPassword());
|
||||||
if(param.getClientTunnelUrl() != null)
|
if(param.getClientTunnelUrl() != null)
|
||||||
map.put("consoleurl", param.getClientTunnelUrl());
|
map.put("consoleurl", param.getClientTunnelUrl());
|
||||||
if(param.getClientTunnelSession() != null)
|
if(param.getClientTunnelSession() != null)
|
||||||
map.put("sessionref", param.getClientTunnelSession());
|
map.put("sessionref", param.getClientTunnelSession());
|
||||||
if(param.getTicket() != null)
|
if(param.getTicket() != null)
|
||||||
map.put("ticket", param.getTicket());
|
map.put("ticket", param.getTicket());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,70 +20,70 @@ import com.cloud.consoleproxy.util.Logger;
|
|||||||
import com.cloud.consoleproxy.util.LoggerFactory;
|
import com.cloud.consoleproxy.util.LoggerFactory;
|
||||||
|
|
||||||
public class ConsoleProxyLoggerFactory implements LoggerFactory {
|
public class ConsoleProxyLoggerFactory implements LoggerFactory {
|
||||||
public ConsoleProxyLoggerFactory() {
|
public ConsoleProxyLoggerFactory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Logger getLogger(Class<?> clazz) {
|
public Logger getLogger(Class<?> clazz) {
|
||||||
return new Log4jLogger(org.apache.log4j.Logger.getLogger(clazz));
|
return new Log4jLogger(org.apache.log4j.Logger.getLogger(clazz));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Log4jLogger extends Logger {
|
public static class Log4jLogger extends Logger {
|
||||||
private org.apache.log4j.Logger logger;
|
private org.apache.log4j.Logger logger;
|
||||||
|
|
||||||
public Log4jLogger(org.apache.log4j.Logger logger) {
|
public Log4jLogger(org.apache.log4j.Logger logger) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTraceEnabled() {
|
public boolean isTraceEnabled() {
|
||||||
return logger.isTraceEnabled();
|
return logger.isTraceEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDebugEnabled() {
|
public boolean isDebugEnabled() {
|
||||||
return logger.isDebugEnabled();
|
return logger.isDebugEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInfoEnabled() {
|
public boolean isInfoEnabled() {
|
||||||
return logger.isInfoEnabled();
|
return logger.isInfoEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void trace(Object message) {
|
public void trace(Object message) {
|
||||||
logger.trace(message);
|
logger.trace(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void trace(Object message, Throwable exception) {
|
public void trace(Object message, Throwable exception) {
|
||||||
logger.trace(message, exception);
|
logger.trace(message, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void info(Object message) {
|
public void info(Object message) {
|
||||||
logger.info(message);
|
logger.info(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void info(Object message, Throwable exception) {
|
public void info(Object message, Throwable exception) {
|
||||||
logger.info(message, exception);
|
logger.info(message, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void debug(Object message) {
|
public void debug(Object message) {
|
||||||
logger.debug(message);
|
logger.debug(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void debug(Object message, Throwable exception) {
|
public void debug(Object message, Throwable exception) {
|
||||||
logger.debug(message, exception);
|
logger.debug(message, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void warn(Object message) {
|
public void warn(Object message) {
|
||||||
logger.warn(message);
|
logger.warn(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void warn(Object message, Throwable exception) {
|
public void warn(Object message, Throwable exception) {
|
||||||
logger.warn(message, exception);
|
logger.warn(message, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void error(Object message) {
|
public void error(Object message) {
|
||||||
logger.error(message);
|
logger.error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void error(Object message, Throwable exception) {
|
public void error(Object message, Throwable exception) {
|
||||||
logger.error(message, exception);
|
logger.error(message, exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,120 +34,120 @@ import com.cloud.consoleproxy.util.Logger;
|
|||||||
// itself and the shell script will re-launch console proxy
|
// itself and the shell script will re-launch console proxy
|
||||||
//
|
//
|
||||||
public class ConsoleProxyMonitor {
|
public class ConsoleProxyMonitor {
|
||||||
private static final Logger s_logger = Logger.getLogger(ConsoleProxyMonitor.class);
|
private static final Logger s_logger = Logger.getLogger(ConsoleProxyMonitor.class);
|
||||||
|
|
||||||
private String[] _argv;
|
private String[] _argv;
|
||||||
private Map<String, String> _argMap = new HashMap<String, String>();
|
private Map<String, String> _argMap = new HashMap<String, String>();
|
||||||
|
|
||||||
private volatile Process _process;
|
private volatile Process _process;
|
||||||
private boolean _quit = false;
|
private boolean _quit = false;
|
||||||
|
|
||||||
public ConsoleProxyMonitor(String[] argv) {
|
public ConsoleProxyMonitor(String[] argv) {
|
||||||
_argv = argv;
|
_argv = argv;
|
||||||
|
|
||||||
for(String arg : _argv) {
|
for(String arg : _argv) {
|
||||||
String[] tokens = arg.split("=");
|
String[] tokens = arg.split("=");
|
||||||
if(tokens.length == 2) {
|
if(tokens.length == 2) {
|
||||||
s_logger.info("Add argument " + tokens[0] + "=" + tokens[1] + " to the argument map");
|
s_logger.info("Add argument " + tokens[0] + "=" + tokens[1] + " to the argument map");
|
||||||
|
|
||||||
_argMap.put(tokens[0].trim(), tokens[1].trim());
|
_argMap.put(tokens[0].trim(), tokens[1].trim());
|
||||||
} else {
|
} else {
|
||||||
s_logger.warn("unrecognized argument, skip adding it to argument map");
|
s_logger.warn("unrecognized argument, skip adding it to argument map");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void run() {
|
private void run() {
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
_quit = true;
|
_quit = true;
|
||||||
onShutdown();
|
onShutdown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
while(!_quit) {
|
while(!_quit) {
|
||||||
String cmdLine = getLaunchCommandLine();
|
String cmdLine = getLaunchCommandLine();
|
||||||
|
|
||||||
s_logger.info("Launch console proxy process with command line: " + cmdLine);
|
s_logger.info("Launch console proxy process with command line: " + cmdLine);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
_process = Runtime.getRuntime().exec(cmdLine);
|
_process = Runtime.getRuntime().exec(cmdLine);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
s_logger.error("Unexpected exception ", e);
|
s_logger.error("Unexpected exception ", e);
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean waitSucceeded = false;
|
boolean waitSucceeded = false;
|
||||||
int exitCode = 0;
|
int exitCode = 0;
|
||||||
while(!waitSucceeded) {
|
while(!waitSucceeded) {
|
||||||
try {
|
try {
|
||||||
exitCode = _process.waitFor();
|
exitCode = _process.waitFor();
|
||||||
waitSucceeded = true;
|
waitSucceeded = true;
|
||||||
|
|
||||||
if(s_logger.isInfoEnabled())
|
if(s_logger.isInfoEnabled())
|
||||||
s_logger.info("Console proxy process exits with code: " + exitCode);
|
s_logger.info("Console proxy process exits with code: " + exitCode);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
if(s_logger.isInfoEnabled())
|
if(s_logger.isInfoEnabled())
|
||||||
s_logger.info("InterruptedException while waiting for termination of console proxy, will retry");
|
s_logger.info("InterruptedException while waiting for termination of console proxy, will retry");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getLaunchCommandLine() {
|
private String getLaunchCommandLine() {
|
||||||
StringBuffer sb = new StringBuffer("java ");
|
StringBuffer sb = new StringBuffer("java ");
|
||||||
String jvmOptions = _argMap.get("jvmoptions");
|
String jvmOptions = _argMap.get("jvmoptions");
|
||||||
|
|
||||||
if(jvmOptions != null)
|
if(jvmOptions != null)
|
||||||
sb.append(jvmOptions);
|
sb.append(jvmOptions);
|
||||||
|
|
||||||
for(Map.Entry<String, String> entry : _argMap.entrySet()) {
|
for(Map.Entry<String, String> entry : _argMap.entrySet()) {
|
||||||
if(!"jvmoptions".equalsIgnoreCase(entry.getKey()))
|
if(!"jvmoptions".equalsIgnoreCase(entry.getKey()))
|
||||||
sb.append(" ").append(entry.getKey()).append("=").append(entry.getValue());
|
sb.append(" ").append(entry.getKey()).append("=").append(entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onShutdown() {
|
private void onShutdown() {
|
||||||
if(_process != null) {
|
if(_process != null) {
|
||||||
if(s_logger.isInfoEnabled())
|
if(s_logger.isInfoEnabled())
|
||||||
s_logger.info("Console proxy monitor shuts dwon, terminate console proxy process");
|
s_logger.info("Console proxy monitor shuts dwon, terminate console proxy process");
|
||||||
_process.destroy();
|
_process.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void configLog4j() {
|
private static void configLog4j() {
|
||||||
URL configUrl = System.class.getResource("/conf/log4j-cloud.xml");
|
URL configUrl = System.class.getResource("/conf/log4j-cloud.xml");
|
||||||
if(configUrl == null)
|
if(configUrl == null)
|
||||||
configUrl = ClassLoader.getSystemResource("log4j-cloud.xml");
|
configUrl = ClassLoader.getSystemResource("log4j-cloud.xml");
|
||||||
|
|
||||||
if(configUrl == null)
|
if(configUrl == null)
|
||||||
configUrl = ClassLoader.getSystemResource("conf/log4j-cloud.xml");
|
configUrl = ClassLoader.getSystemResource("conf/log4j-cloud.xml");
|
||||||
|
|
||||||
if(configUrl != null) {
|
if(configUrl != null) {
|
||||||
try {
|
try {
|
||||||
System.out.println("Configure log4j using " + configUrl.toURI().toString());
|
System.out.println("Configure log4j using " + configUrl.toURI().toString());
|
||||||
} catch (URISyntaxException e1) {
|
} catch (URISyntaxException e1) {
|
||||||
e1.printStackTrace();
|
e1.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
File file = new File(configUrl.toURI());
|
File file = new File(configUrl.toURI());
|
||||||
|
|
||||||
System.out.println("Log4j configuration from : " + file.getAbsolutePath());
|
System.out.println("Log4j configuration from : " + file.getAbsolutePath());
|
||||||
DOMConfigurator.configureAndWatch(file.getAbsolutePath(), 10000);
|
DOMConfigurator.configureAndWatch(file.getAbsolutePath(), 10000);
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
System.out.println("Unable to convert log4j configuration Url to URI");
|
System.out.println("Unable to convert log4j configuration Url to URI");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
System.out.println("Configure log4j with default properties");
|
System.out.println("Configure log4j with default properties");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] argv) {
|
public static void main(String[] argv) {
|
||||||
configLog4j();
|
configLog4j();
|
||||||
(new ConsoleProxyMonitor(argv)).run();
|
(new ConsoleProxyMonitor(argv)).run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,152 +30,152 @@ import com.sun.net.httpserver.HttpExchange;
|
|||||||
import com.sun.net.httpserver.HttpHandler;
|
import com.sun.net.httpserver.HttpHandler;
|
||||||
|
|
||||||
public class ConsoleProxyResourceHandler implements HttpHandler {
|
public class ConsoleProxyResourceHandler implements HttpHandler {
|
||||||
private static final Logger s_logger = Logger.getLogger(ConsoleProxyResourceHandler.class);
|
private static final Logger s_logger = Logger.getLogger(ConsoleProxyResourceHandler.class);
|
||||||
|
|
||||||
static Map<String, String> s_mimeTypes;
|
static Map<String, String> s_mimeTypes;
|
||||||
static {
|
static {
|
||||||
s_mimeTypes = new HashMap<String, String>();
|
s_mimeTypes = new HashMap<String, String>();
|
||||||
s_mimeTypes.put("jar", "application/java-archive");
|
s_mimeTypes.put("jar", "application/java-archive");
|
||||||
s_mimeTypes.put("js", "text/javascript");
|
s_mimeTypes.put("js", "text/javascript");
|
||||||
s_mimeTypes.put("css", "text/css");
|
s_mimeTypes.put("css", "text/css");
|
||||||
s_mimeTypes.put("jpg", "image/jpeg");
|
s_mimeTypes.put("jpg", "image/jpeg");
|
||||||
s_mimeTypes.put("html", "text/html");
|
s_mimeTypes.put("html", "text/html");
|
||||||
s_mimeTypes.put("htm", "text/html");
|
s_mimeTypes.put("htm", "text/html");
|
||||||
s_mimeTypes.put("log", "text/plain");
|
s_mimeTypes.put("log", "text/plain");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Map<String, String> s_validResourceFolders;
|
static Map<String, String> s_validResourceFolders;
|
||||||
static {
|
static {
|
||||||
s_validResourceFolders = new HashMap<String, String>();
|
s_validResourceFolders = new HashMap<String, String>();
|
||||||
s_validResourceFolders.put("applet", "");
|
s_validResourceFolders.put("applet", "");
|
||||||
s_validResourceFolders.put("logs", "");
|
s_validResourceFolders.put("logs", "");
|
||||||
s_validResourceFolders.put("images", "");
|
s_validResourceFolders.put("images", "");
|
||||||
s_validResourceFolders.put("js", "");
|
s_validResourceFolders.put("js", "");
|
||||||
s_validResourceFolders.put("css", "");
|
s_validResourceFolders.put("css", "");
|
||||||
s_validResourceFolders.put("html", "");
|
s_validResourceFolders.put("html", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConsoleProxyResourceHandler() {
|
public ConsoleProxyResourceHandler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handle(HttpExchange t) throws IOException {
|
public void handle(HttpExchange t) throws IOException {
|
||||||
try {
|
try {
|
||||||
if(s_logger.isDebugEnabled())
|
if(s_logger.isDebugEnabled())
|
||||||
s_logger.debug("Resource Handler " + t.getRequestURI());
|
s_logger.debug("Resource Handler " + t.getRequestURI());
|
||||||
|
|
||||||
long startTick = System.currentTimeMillis();
|
long startTick = System.currentTimeMillis();
|
||||||
|
|
||||||
doHandle(t);
|
doHandle(t);
|
||||||
|
|
||||||
if(s_logger.isDebugEnabled())
|
if(s_logger.isDebugEnabled())
|
||||||
s_logger.debug(t.getRequestURI() + " Process time " + (System.currentTimeMillis() - startTick) + " ms");
|
s_logger.debug(t.getRequestURI() + " Process time " + (System.currentTimeMillis() - startTick) + " ms");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch(Throwable e) {
|
} catch(Throwable e) {
|
||||||
s_logger.error("Unexpected exception, ", e);
|
s_logger.error("Unexpected exception, ", e);
|
||||||
t.sendResponseHeaders(500, -1); // server error
|
t.sendResponseHeaders(500, -1); // server error
|
||||||
} finally {
|
} finally {
|
||||||
t.close();
|
t.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private void doHandle(HttpExchange t) throws Exception {
|
private void doHandle(HttpExchange t) throws Exception {
|
||||||
String path = t.getRequestURI().getPath();
|
String path = t.getRequestURI().getPath();
|
||||||
|
|
||||||
if(s_logger.isInfoEnabled())
|
if(s_logger.isInfoEnabled())
|
||||||
s_logger.info("Get resource request for " + path);
|
s_logger.info("Get resource request for " + path);
|
||||||
|
|
||||||
int i = path.indexOf("/", 1);
|
int i = path.indexOf("/", 1);
|
||||||
String filepath = path.substring(i + 1);
|
String filepath = path.substring(i + 1);
|
||||||
i = path.lastIndexOf(".");
|
i = path.lastIndexOf(".");
|
||||||
String extension = (i == -1) ? "" : path.substring(i + 1);
|
String extension = (i == -1) ? "" : path.substring(i + 1);
|
||||||
String contentType = getContentType(extension);
|
String contentType = getContentType(extension);
|
||||||
|
|
||||||
if(!validatePath(filepath)) {
|
if(!validatePath(filepath)) {
|
||||||
if(s_logger.isInfoEnabled())
|
if(s_logger.isInfoEnabled())
|
||||||
s_logger.info("Resource access is forbidden, uri: " + path);
|
s_logger.info("Resource access is forbidden, uri: " + path);
|
||||||
|
|
||||||
t.sendResponseHeaders(403, -1); // forbidden
|
t.sendResponseHeaders(403, -1); // forbidden
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File f = new File ("./" + filepath);
|
File f = new File ("./" + filepath);
|
||||||
if(f.exists()) {
|
if(f.exists()) {
|
||||||
long lastModified = f.lastModified();
|
long lastModified = f.lastModified();
|
||||||
String ifModifiedSince = t.getRequestHeaders().getFirst("If-Modified-Since");
|
String ifModifiedSince = t.getRequestHeaders().getFirst("If-Modified-Since");
|
||||||
if (ifModifiedSince != null) {
|
if (ifModifiedSince != null) {
|
||||||
long d = Date.parse(ifModifiedSince);
|
long d = Date.parse(ifModifiedSince);
|
||||||
if (d + 1000 >= lastModified) {
|
if (d + 1000 >= lastModified) {
|
||||||
Headers hds = t.getResponseHeaders();
|
Headers hds = t.getResponseHeaders();
|
||||||
hds.set("Content-Type", contentType);
|
hds.set("Content-Type", contentType);
|
||||||
t.sendResponseHeaders(304, -1);
|
t.sendResponseHeaders(304, -1);
|
||||||
|
|
||||||
if(s_logger.isInfoEnabled())
|
if(s_logger.isInfoEnabled())
|
||||||
s_logger.info("Sent 304 file has not been " +
|
s_logger.info("Sent 304 file has not been " +
|
||||||
"modified since " + ifModifiedSince);
|
"modified since " + ifModifiedSince);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long length = f.length();
|
long length = f.length();
|
||||||
Headers hds = t.getResponseHeaders();
|
Headers hds = t.getResponseHeaders();
|
||||||
hds.set("Content-Type", contentType);
|
hds.set("Content-Type", contentType);
|
||||||
hds.set("Last-Modified", new Date(lastModified).toGMTString());
|
hds.set("Last-Modified", new Date(lastModified).toGMTString());
|
||||||
t.sendResponseHeaders(200, length);
|
t.sendResponseHeaders(200, length);
|
||||||
responseFileContent(t, f);
|
responseFileContent(t, f);
|
||||||
|
|
||||||
if(s_logger.isInfoEnabled())
|
if(s_logger.isInfoEnabled())
|
||||||
s_logger.info("Sent file " + path + " with content type " + contentType);
|
s_logger.info("Sent file " + path + " with content type " + contentType);
|
||||||
} else {
|
} else {
|
||||||
if(s_logger.isInfoEnabled())
|
if(s_logger.isInfoEnabled())
|
||||||
s_logger.info("file does not exist" + path);
|
s_logger.info("file does not exist" + path);
|
||||||
t.sendResponseHeaders(404, -1);
|
t.sendResponseHeaders(404, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getContentType(String extension) {
|
private static String getContentType(String extension) {
|
||||||
String key = extension.toLowerCase();
|
String key = extension.toLowerCase();
|
||||||
if(s_mimeTypes.containsKey(key)) {
|
if(s_mimeTypes.containsKey(key)) {
|
||||||
return s_mimeTypes.get(key);
|
return s_mimeTypes.get(key);
|
||||||
}
|
}
|
||||||
return "application/octet-stream";
|
return "application/octet-stream";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void responseFileContent(HttpExchange t, File f) throws Exception {
|
private static void responseFileContent(HttpExchange t, File f) throws Exception {
|
||||||
OutputStream os = t.getResponseBody();
|
OutputStream os = t.getResponseBody();
|
||||||
FileInputStream fis = new FileInputStream(f);
|
FileInputStream fis = new FileInputStream(f);
|
||||||
while (true) {
|
while (true) {
|
||||||
byte[] b = new byte[8192];
|
byte[] b = new byte[8192];
|
||||||
int n = fis.read(b);
|
int n = fis.read(b);
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
os.write(b, 0, n);
|
os.write(b, 0, n);
|
||||||
}
|
}
|
||||||
fis.close();
|
fis.close();
|
||||||
os.close();
|
os.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean validatePath(String path) {
|
private static boolean validatePath(String path) {
|
||||||
int i = path.indexOf("/");
|
int i = path.indexOf("/");
|
||||||
if(i == -1) {
|
if(i == -1) {
|
||||||
if(s_logger.isInfoEnabled())
|
if(s_logger.isInfoEnabled())
|
||||||
s_logger.info("Invalid resource path: can not start at resource root");
|
s_logger.info("Invalid resource path: can not start at resource root");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(path.contains("..")) {
|
if(path.contains("..")) {
|
||||||
if(s_logger.isInfoEnabled())
|
if(s_logger.isInfoEnabled())
|
||||||
s_logger.info("Invalid resource path: contains relative up-level navigation");
|
s_logger.info("Invalid resource path: contains relative up-level navigation");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return isValidResourceFolder(path.substring(0, i));
|
return isValidResourceFolder(path.substring(0, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isValidResourceFolder(String name) {
|
private static boolean isValidResourceFolder(String name) {
|
||||||
return s_validResourceFolders.containsKey(name);
|
return s_validResourceFolders.containsKey(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,109 +37,109 @@ import com.sun.net.httpserver.HttpsParameters;
|
|||||||
import com.sun.net.httpserver.HttpsServer;
|
import com.sun.net.httpserver.HttpsServer;
|
||||||
|
|
||||||
public class ConsoleProxySecureServerFactoryImpl implements ConsoleProxyServerFactory {
|
public class ConsoleProxySecureServerFactoryImpl implements ConsoleProxyServerFactory {
|
||||||
private static final Logger s_logger = Logger.getLogger(ConsoleProxySecureServerFactoryImpl.class);
|
private static final Logger s_logger = Logger.getLogger(ConsoleProxySecureServerFactoryImpl.class);
|
||||||
|
|
||||||
private SSLContext sslContext = null;
|
private SSLContext sslContext = null;
|
||||||
|
|
||||||
public ConsoleProxySecureServerFactoryImpl() {
|
public ConsoleProxySecureServerFactoryImpl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(byte[] ksBits, String ksPassword) {
|
public void init(byte[] ksBits, String ksPassword) {
|
||||||
s_logger.info("Start initializing SSL");
|
s_logger.info("Start initializing SSL");
|
||||||
|
|
||||||
if(ksBits == null) {
|
if(ksBits == null) {
|
||||||
try {
|
try {
|
||||||
s_logger.info("Initializing SSL from built-in default certificate");
|
s_logger.info("Initializing SSL from built-in default certificate");
|
||||||
|
|
||||||
char[] passphrase = "vmops.com".toCharArray();
|
char[] passphrase = "vmops.com".toCharArray();
|
||||||
KeyStore ks = KeyStore.getInstance("JKS");
|
KeyStore ks = KeyStore.getInstance("JKS");
|
||||||
|
|
||||||
ks.load(new FileInputStream("certs/realhostip.keystore"), passphrase);
|
ks.load(new FileInputStream("certs/realhostip.keystore"), passphrase);
|
||||||
// ks.load(ConsoleProxy.class.getResourceAsStream("/realhostip.keystore"), passphrase);
|
// ks.load(ConsoleProxy.class.getResourceAsStream("/realhostip.keystore"), passphrase);
|
||||||
|
|
||||||
s_logger.info("SSL certificate loaded");
|
s_logger.info("SSL certificate loaded");
|
||||||
|
|
||||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
||||||
kmf.init(ks, passphrase);
|
kmf.init(ks, passphrase);
|
||||||
s_logger.info("Key manager factory is initialized");
|
s_logger.info("Key manager factory is initialized");
|
||||||
|
|
||||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
|
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
|
||||||
tmf.init(ks);
|
tmf.init(ks);
|
||||||
s_logger.info("Trust manager factory is initialized");
|
s_logger.info("Trust manager factory is initialized");
|
||||||
|
|
||||||
sslContext = SSLContext.getInstance("TLS");
|
sslContext = SSLContext.getInstance("TLS");
|
||||||
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||||
s_logger.info("SSL context is initialized");
|
s_logger.info("SSL context is initialized");
|
||||||
} catch (Exception ioe) {
|
} catch (Exception ioe) {
|
||||||
s_logger.error(ioe.toString(), ioe);
|
s_logger.error(ioe.toString(), ioe);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
char[] passphrase = ksPassword != null ? ksPassword.toCharArray() : null;
|
char[] passphrase = ksPassword != null ? ksPassword.toCharArray() : null;
|
||||||
try {
|
try {
|
||||||
s_logger.info("Initializing SSL from passed-in certificate");
|
s_logger.info("Initializing SSL from passed-in certificate");
|
||||||
|
|
||||||
KeyStore ks = KeyStore.getInstance("JKS");
|
KeyStore ks = KeyStore.getInstance("JKS");
|
||||||
ks.load(new ByteArrayInputStream(ksBits), passphrase);
|
ks.load(new ByteArrayInputStream(ksBits), passphrase);
|
||||||
|
|
||||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
||||||
kmf.init(ks, passphrase);
|
kmf.init(ks, passphrase);
|
||||||
s_logger.info("Key manager factory is initialized");
|
s_logger.info("Key manager factory is initialized");
|
||||||
|
|
||||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
|
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
|
||||||
tmf.init(ks);
|
tmf.init(ks);
|
||||||
s_logger.info("Trust manager factory is initialized");
|
s_logger.info("Trust manager factory is initialized");
|
||||||
|
|
||||||
sslContext = SSLContext.getInstance("TLS");
|
sslContext = SSLContext.getInstance("TLS");
|
||||||
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||||
s_logger.info("SSL context is initialized");
|
s_logger.info("SSL context is initialized");
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
s_logger.error("Unable to init factory due to exception ", e);
|
s_logger.error("Unable to init factory due to exception ", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpServer createHttpServerInstance(int port) throws IOException {
|
public HttpServer createHttpServerInstance(int port) throws IOException {
|
||||||
try {
|
try {
|
||||||
HttpsServer server = HttpsServer.create(new InetSocketAddress(port), 5);
|
HttpsServer server = HttpsServer.create(new InetSocketAddress(port), 5);
|
||||||
server.setHttpsConfigurator (new HttpsConfigurator(sslContext) {
|
server.setHttpsConfigurator (new HttpsConfigurator(sslContext) {
|
||||||
@Override
|
@Override
|
||||||
public void configure (HttpsParameters params) {
|
public void configure (HttpsParameters params) {
|
||||||
|
|
||||||
// get the remote address if needed
|
// get the remote address if needed
|
||||||
InetSocketAddress remote = params.getClientAddress();
|
InetSocketAddress remote = params.getClientAddress();
|
||||||
SSLContext c = getSSLContext();
|
SSLContext c = getSSLContext();
|
||||||
|
|
||||||
// get the default parameters
|
// get the default parameters
|
||||||
SSLParameters sslparams = c.getDefaultSSLParameters();
|
SSLParameters sslparams = c.getDefaultSSLParameters();
|
||||||
|
|
||||||
params.setSSLParameters(sslparams);
|
params.setSSLParameters(sslparams);
|
||||||
// statement above could throw IAE if any params invalid.
|
// statement above could throw IAE if any params invalid.
|
||||||
// eg. if app has a UI and parameters supplied by a user.
|
// eg. if app has a UI and parameters supplied by a user.
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
s_logger.info("create HTTPS server instance on port: " + port);
|
s_logger.info("create HTTPS server instance on port: " + port);
|
||||||
return server;
|
return server;
|
||||||
} catch (Exception ioe) {
|
} catch (Exception ioe) {
|
||||||
s_logger.error(ioe.toString(), ioe);
|
s_logger.error(ioe.toString(), ioe);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SSLServerSocket createSSLServerSocket(int port) throws IOException {
|
public SSLServerSocket createSSLServerSocket(int port) throws IOException {
|
||||||
try {
|
try {
|
||||||
SSLServerSocket srvSock = null;
|
SSLServerSocket srvSock = null;
|
||||||
SSLServerSocketFactory ssf = sslContext.getServerSocketFactory();
|
SSLServerSocketFactory ssf = sslContext.getServerSocketFactory();
|
||||||
srvSock = (SSLServerSocket) ssf.createServerSocket(port);
|
srvSock = (SSLServerSocket) ssf.createServerSocket(port);
|
||||||
|
|
||||||
s_logger.info("create SSL server socket on port: " + port);
|
s_logger.info("create SSL server socket on port: " + port);
|
||||||
return srvSock;
|
return srvSock;
|
||||||
} catch (Exception ioe) {
|
} catch (Exception ioe) {
|
||||||
s_logger.error(ioe.toString(), ioe);
|
s_logger.error(ioe.toString(), ioe);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import javax.net.ssl.SSLServerSocket;
|
|||||||
import com.sun.net.httpserver.HttpServer;
|
import com.sun.net.httpserver.HttpServer;
|
||||||
|
|
||||||
public interface ConsoleProxyServerFactory {
|
public interface ConsoleProxyServerFactory {
|
||||||
void init(byte[] ksBits, String ksPassword);
|
void init(byte[] ksBits, String ksPassword);
|
||||||
HttpServer createHttpServerInstance(int port) throws IOException;
|
HttpServer createHttpServerInstance(int port) throws IOException;
|
||||||
SSLServerSocket createSSLServerSocket(int port) throws IOException;
|
SSLServerSocket createSSLServerSocket(int port) throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,179 +34,179 @@ import com.sun.net.httpserver.HttpExchange;
|
|||||||
import com.sun.net.httpserver.HttpHandler;
|
import com.sun.net.httpserver.HttpHandler;
|
||||||
|
|
||||||
public class ConsoleProxyThumbnailHandler implements HttpHandler {
|
public class ConsoleProxyThumbnailHandler implements HttpHandler {
|
||||||
private static final Logger s_logger = Logger.getLogger(ConsoleProxyThumbnailHandler.class);
|
private static final Logger s_logger = Logger.getLogger(ConsoleProxyThumbnailHandler.class);
|
||||||
|
|
||||||
public ConsoleProxyThumbnailHandler() {
|
public ConsoleProxyThumbnailHandler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handle(HttpExchange t) throws IOException {
|
public void handle(HttpExchange t) throws IOException {
|
||||||
try {
|
try {
|
||||||
Thread.currentThread().setName("JPG Thread " +
|
Thread.currentThread().setName("JPG Thread " +
|
||||||
Thread.currentThread().getId() + " " + t.getRemoteAddress());
|
Thread.currentThread().getId() + " " + t.getRemoteAddress());
|
||||||
|
|
||||||
if(s_logger.isDebugEnabled())
|
if(s_logger.isDebugEnabled())
|
||||||
s_logger.debug("ScreenHandler " + t.getRequestURI());
|
s_logger.debug("ScreenHandler " + t.getRequestURI());
|
||||||
|
|
||||||
long startTick = System.currentTimeMillis();
|
long startTick = System.currentTimeMillis();
|
||||||
doHandle(t);
|
doHandle(t);
|
||||||
|
|
||||||
if(s_logger.isDebugEnabled())
|
if(s_logger.isDebugEnabled())
|
||||||
s_logger.debug(t.getRequestURI() + "Process time " + (System.currentTimeMillis() - startTick) + " ms");
|
s_logger.debug(t.getRequestURI() + "Process time " + (System.currentTimeMillis() - startTick) + " ms");
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
String response = "Bad query string";
|
String response = "Bad query string";
|
||||||
s_logger.error(response + ", request URI : " + t.getRequestURI());
|
s_logger.error(response + ", request URI : " + t.getRequestURI());
|
||||||
t.sendResponseHeaders(200, response.length());
|
t.sendResponseHeaders(200, response.length());
|
||||||
OutputStream os = t.getResponseBody();
|
OutputStream os = t.getResponseBody();
|
||||||
os.write(response.getBytes());
|
os.write(response.getBytes());
|
||||||
os.close();
|
os.close();
|
||||||
} catch(OutOfMemoryError e) {
|
} catch(OutOfMemoryError e) {
|
||||||
s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched");
|
s_logger.error("Unrecoverable OutOfMemory Error, exit and let it be re-launched");
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
s_logger.error("Unexpected exception while handing thumbnail request, ", e);
|
s_logger.error("Unexpected exception while handing thumbnail request, ", e);
|
||||||
|
|
||||||
String queries = t.getRequestURI().getQuery();
|
String queries = t.getRequestURI().getQuery();
|
||||||
Map<String, String> queryMap = getQueryMap(queries);
|
Map<String, String> queryMap = getQueryMap(queries);
|
||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
String ws = queryMap.get("w");
|
String ws = queryMap.get("w");
|
||||||
String hs = queryMap.get("h");
|
String hs = queryMap.get("h");
|
||||||
try {
|
try {
|
||||||
width = Integer.parseInt(ws);
|
width = Integer.parseInt(ws);
|
||||||
height = Integer.parseInt(hs);
|
height = Integer.parseInt(hs);
|
||||||
} catch (NumberFormatException ex) {
|
} catch (NumberFormatException ex) {
|
||||||
}
|
}
|
||||||
width = Math.min(width, 800);
|
width = Math.min(width, 800);
|
||||||
height = Math.min(height, 600);
|
height = Math.min(height, 600);
|
||||||
|
|
||||||
BufferedImage img = generateTextImage(width, height, "Cannot Connect");
|
BufferedImage img = generateTextImage(width, height, "Cannot Connect");
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(8196);
|
ByteArrayOutputStream bos = new ByteArrayOutputStream(8196);
|
||||||
javax.imageio.ImageIO.write(img, "jpg", bos);
|
javax.imageio.ImageIO.write(img, "jpg", bos);
|
||||||
byte[] bs = bos.toByteArray();
|
byte[] bs = bos.toByteArray();
|
||||||
Headers hds = t.getResponseHeaders();
|
Headers hds = t.getResponseHeaders();
|
||||||
hds.set("Content-Type", "image/jpeg");
|
hds.set("Content-Type", "image/jpeg");
|
||||||
hds.set("Cache-Control", "no-cache");
|
hds.set("Cache-Control", "no-cache");
|
||||||
hds.set("Cache-Control", "no-store");
|
hds.set("Cache-Control", "no-store");
|
||||||
t.sendResponseHeaders(200, bs.length);
|
t.sendResponseHeaders(200, bs.length);
|
||||||
OutputStream os = t.getResponseBody();
|
OutputStream os = t.getResponseBody();
|
||||||
os.write(bs);
|
os.write(bs);
|
||||||
os.close();
|
os.close();
|
||||||
s_logger.error("Cannot get console, sent error JPG response for " + t.getRequestURI());
|
s_logger.error("Cannot get console, sent error JPG response for " + t.getRequestURI());
|
||||||
return;
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
t.close();
|
t.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException {
|
private void doHandle(HttpExchange t) throws Exception, IllegalArgumentException {
|
||||||
String queries = t.getRequestURI().getQuery();
|
String queries = t.getRequestURI().getQuery();
|
||||||
Map<String, String> queryMap = getQueryMap(queries);
|
Map<String, String> queryMap = getQueryMap(queries);
|
||||||
int width = 0;
|
int width = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
int port = 0;
|
int port = 0;
|
||||||
String ws = queryMap.get("w");
|
String ws = queryMap.get("w");
|
||||||
String hs = queryMap.get("h");
|
String hs = queryMap.get("h");
|
||||||
String host = queryMap.get("host");
|
String host = queryMap.get("host");
|
||||||
String portStr = queryMap.get("port");
|
String portStr = queryMap.get("port");
|
||||||
String sid = queryMap.get("sid");
|
String sid = queryMap.get("sid");
|
||||||
String tag = queryMap.get("tag");
|
String tag = queryMap.get("tag");
|
||||||
String ticket = queryMap.get("ticket");
|
String ticket = queryMap.get("ticket");
|
||||||
String console_url = queryMap.get("consoleurl");
|
String console_url = queryMap.get("consoleurl");
|
||||||
String console_host_session = queryMap.get("sessionref");
|
String console_host_session = queryMap.get("sessionref");
|
||||||
|
|
||||||
if(tag == null)
|
if(tag == null)
|
||||||
tag = "";
|
tag = "";
|
||||||
|
|
||||||
if (ws == null || hs == null || host == null || portStr == null || sid == null ) {
|
if (ws == null || hs == null || host == null || portStr == null || sid == null ) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
width = Integer.parseInt(ws);
|
width = Integer.parseInt(ws);
|
||||||
height = Integer.parseInt(hs);
|
height = Integer.parseInt(hs);
|
||||||
port = Integer.parseInt(portStr);
|
port = Integer.parseInt(portStr);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleProxyClientParam param = new ConsoleProxyClientParam();
|
ConsoleProxyClientParam param = new ConsoleProxyClientParam();
|
||||||
param.setClientHostAddress(host);
|
param.setClientHostAddress(host);
|
||||||
param.setClientHostPort(port);
|
param.setClientHostPort(port);
|
||||||
param.setClientHostPassword(sid);
|
param.setClientHostPassword(sid);
|
||||||
param.setClientTag(tag);
|
param.setClientTag(tag);
|
||||||
param.setTicket(ticket);
|
param.setTicket(ticket);
|
||||||
param.setClientTunnelUrl(console_url);
|
param.setClientTunnelUrl(console_url);
|
||||||
param.setClientTunnelSession(console_host_session);
|
param.setClientTunnelSession(console_host_session);
|
||||||
|
|
||||||
ConsoleProxyClient viewer = ConsoleProxy.getVncViewer(param);
|
ConsoleProxyClient viewer = ConsoleProxy.getVncViewer(param);
|
||||||
|
|
||||||
if (!viewer.isHostConnected()) {
|
if (!viewer.isHostConnected()) {
|
||||||
// use generated image instead of static
|
// use generated image instead of static
|
||||||
BufferedImage img = generateTextImage(width, height, "Connecting");
|
BufferedImage img = generateTextImage(width, height, "Connecting");
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(8196);
|
ByteArrayOutputStream bos = new ByteArrayOutputStream(8196);
|
||||||
javax.imageio.ImageIO.write(img, "jpg", bos);
|
javax.imageio.ImageIO.write(img, "jpg", bos);
|
||||||
byte[] bs = bos.toByteArray();
|
byte[] bs = bos.toByteArray();
|
||||||
Headers hds = t.getResponseHeaders();
|
Headers hds = t.getResponseHeaders();
|
||||||
hds.set("Content-Type", "image/jpeg");
|
hds.set("Content-Type", "image/jpeg");
|
||||||
hds.set("Cache-Control", "no-cache");
|
hds.set("Cache-Control", "no-cache");
|
||||||
hds.set("Cache-Control", "no-store");
|
hds.set("Cache-Control", "no-store");
|
||||||
t.sendResponseHeaders(200, bs.length);
|
t.sendResponseHeaders(200, bs.length);
|
||||||
OutputStream os = t.getResponseBody();
|
OutputStream os = t.getResponseBody();
|
||||||
os.write(bs);
|
os.write(bs);
|
||||||
os.close();
|
os.close();
|
||||||
|
|
||||||
if(s_logger.isInfoEnabled())
|
if(s_logger.isInfoEnabled())
|
||||||
s_logger.info("Console not ready, sent dummy JPG response");
|
s_logger.info("Console not ready, sent dummy JPG response");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Image scaledImage = viewer.getClientScaledImage(width, height);
|
Image scaledImage = viewer.getClientScaledImage(width, height);
|
||||||
BufferedImage bufferedImage = new BufferedImage(width, height,
|
BufferedImage bufferedImage = new BufferedImage(width, height,
|
||||||
BufferedImage.TYPE_3BYTE_BGR);
|
BufferedImage.TYPE_3BYTE_BGR);
|
||||||
Graphics2D bufImageGraphics = bufferedImage.createGraphics();
|
Graphics2D bufImageGraphics = bufferedImage.createGraphics();
|
||||||
bufImageGraphics.drawImage(scaledImage, 0, 0, null);
|
bufImageGraphics.drawImage(scaledImage, 0, 0, null);
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(8196);
|
ByteArrayOutputStream bos = new ByteArrayOutputStream(8196);
|
||||||
javax.imageio.ImageIO.write(bufferedImage, "jpg", bos);
|
javax.imageio.ImageIO.write(bufferedImage, "jpg", bos);
|
||||||
byte[] bs = bos.toByteArray();
|
byte[] bs = bos.toByteArray();
|
||||||
Headers hds = t.getResponseHeaders();
|
Headers hds = t.getResponseHeaders();
|
||||||
hds.set("Content-Type", "image/jpeg");
|
hds.set("Content-Type", "image/jpeg");
|
||||||
hds.set("Cache-Control", "no-cache");
|
hds.set("Cache-Control", "no-cache");
|
||||||
hds.set("Cache-Control", "no-store");
|
hds.set("Cache-Control", "no-store");
|
||||||
t.sendResponseHeaders(200, bs.length);
|
t.sendResponseHeaders(200, bs.length);
|
||||||
OutputStream os = t.getResponseBody();
|
OutputStream os = t.getResponseBody();
|
||||||
os.write(bs);
|
os.write(bs);
|
||||||
os.close();
|
os.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BufferedImage generateTextImage(int w, int h, String text) {
|
public static BufferedImage generateTextImage(int w, int h, String text) {
|
||||||
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);
|
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_3BYTE_BGR);
|
||||||
Graphics2D g = img.createGraphics();
|
Graphics2D g = img.createGraphics();
|
||||||
g.setColor(Color.BLACK);
|
g.setColor(Color.BLACK);
|
||||||
g.fillRect(0, 0, w, h);
|
g.fillRect(0, 0, w, h);
|
||||||
g.setColor(Color.WHITE);
|
g.setColor(Color.WHITE);
|
||||||
try {
|
try {
|
||||||
g.setFont(new Font(null, Font.PLAIN, 12));
|
g.setFont(new Font(null, Font.PLAIN, 12));
|
||||||
FontMetrics fm = g.getFontMetrics();
|
FontMetrics fm = g.getFontMetrics();
|
||||||
int textWidth = fm.stringWidth(text);
|
int textWidth = fm.stringWidth(text);
|
||||||
int startx = (w-textWidth) / 2;
|
int startx = (w-textWidth) / 2;
|
||||||
if(startx < 0)
|
if(startx < 0)
|
||||||
startx = 0;
|
startx = 0;
|
||||||
g.drawString(text, startx, h/2);
|
g.drawString(text, startx, h/2);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
s_logger.warn("Problem in generating text to thumnail image, return blank image");
|
s_logger.warn("Problem in generating text to thumnail image, return blank image");
|
||||||
}
|
}
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Map<String, String> getQueryMap(String query) {
|
public static Map<String, String> getQueryMap(String query) {
|
||||||
String[] params = query.split("&");
|
String[] params = query.split("&");
|
||||||
Map<String, String> map = new HashMap<String, String>();
|
Map<String, String> map = new HashMap<String, String>();
|
||||||
for (String param : params) {
|
for (String param : params) {
|
||||||
String name = param.split("=")[0];
|
String name = param.split("=")[0];
|
||||||
String value = param.split("=")[1];
|
String value = param.split("=")[1];
|
||||||
map.put(name, value);
|
map.put(name, value);
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,42 +17,42 @@
|
|||||||
package com.cloud.consoleproxy;
|
package com.cloud.consoleproxy;
|
||||||
|
|
||||||
public enum InputEventType {
|
public enum InputEventType {
|
||||||
MOUSE_MOVE(1),
|
MOUSE_MOVE(1),
|
||||||
MOUSE_DOWN(2),
|
MOUSE_DOWN(2),
|
||||||
MOUSE_UP(3),
|
MOUSE_UP(3),
|
||||||
KEY_PRESS(4),
|
KEY_PRESS(4),
|
||||||
KEY_DOWN(5),
|
KEY_DOWN(5),
|
||||||
KEY_UP(6),
|
KEY_UP(6),
|
||||||
MOUSE_DBLCLICK(8);
|
MOUSE_DBLCLICK(8);
|
||||||
|
|
||||||
int eventCode;
|
int eventCode;
|
||||||
private InputEventType(int eventCode) {
|
private InputEventType(int eventCode) {
|
||||||
this.eventCode = eventCode;
|
this.eventCode = eventCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getEventCode() {
|
public int getEventCode() {
|
||||||
return eventCode;
|
return eventCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InputEventType fromEventCode(int eventCode) {
|
public static InputEventType fromEventCode(int eventCode) {
|
||||||
switch(eventCode) {
|
switch(eventCode) {
|
||||||
case 1 :
|
case 1 :
|
||||||
return MOUSE_MOVE;
|
return MOUSE_MOVE;
|
||||||
case 2 :
|
case 2 :
|
||||||
return MOUSE_DOWN;
|
return MOUSE_DOWN;
|
||||||
case 3 :
|
case 3 :
|
||||||
return MOUSE_UP;
|
return MOUSE_UP;
|
||||||
case 4 :
|
case 4 :
|
||||||
return KEY_PRESS;
|
return KEY_PRESS;
|
||||||
case 5 :
|
case 5 :
|
||||||
return KEY_DOWN;
|
return KEY_DOWN;
|
||||||
case 6 :
|
case 6 :
|
||||||
return KEY_UP;
|
return KEY_UP;
|
||||||
case 8 :
|
case 8 :
|
||||||
return MOUSE_DBLCLICK;
|
return MOUSE_DBLCLICK;
|
||||||
default :
|
default :
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("Unsupport event code: " + eventCode);
|
throw new IllegalArgumentException("Unsupport event code: " + eventCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,6 @@ import java.awt.Rectangle;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface ITileScanListener {
|
public interface ITileScanListener {
|
||||||
boolean onTileChange(Rectangle rowMergedRect, int row, int col);
|
boolean onTileChange(Rectangle rowMergedRect, int row, int col);
|
||||||
void onRegionChange(List<Region> regionList);
|
void onRegionChange(List<Region> regionList);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,12 +21,12 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class ImageHelper {
|
public class ImageHelper {
|
||||||
public static byte[] jpegFromImage(BufferedImage image) throws IOException {
|
public static byte[] jpegFromImage(BufferedImage image) throws IOException {
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(128000);
|
ByteArrayOutputStream bos = new ByteArrayOutputStream(128000);
|
||||||
javax.imageio.ImageIO.write(image, "jpg", bos);
|
javax.imageio.ImageIO.write(image, "jpg", bos);
|
||||||
|
|
||||||
byte[] jpegBits = bos.toByteArray();
|
byte[] jpegBits = bos.toByteArray();
|
||||||
bos.close();
|
bos.close();
|
||||||
return jpegBits;
|
return jpegBits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,206 +18,206 @@ package com.cloud.consoleproxy.util;
|
|||||||
|
|
||||||
// logger facility for dynamic switch between console logger used in Applet and log4j based logger
|
// logger facility for dynamic switch between console logger used in Applet and log4j based logger
|
||||||
public class Logger {
|
public class Logger {
|
||||||
private static LoggerFactory factory = null;
|
private static LoggerFactory factory = null;
|
||||||
|
|
||||||
public static final int LEVEL_TRACE = 1;
|
public static final int LEVEL_TRACE = 1;
|
||||||
public static final int LEVEL_DEBUG = 2;
|
public static final int LEVEL_DEBUG = 2;
|
||||||
public static final int LEVEL_INFO = 3;
|
public static final int LEVEL_INFO = 3;
|
||||||
public static final int LEVEL_WARN = 4;
|
public static final int LEVEL_WARN = 4;
|
||||||
public static final int LEVEL_ERROR = 5;
|
public static final int LEVEL_ERROR = 5;
|
||||||
|
|
||||||
private Class<?> clazz;
|
private Class<?> clazz;
|
||||||
private Logger logger;
|
private Logger logger;
|
||||||
|
|
||||||
private static int level = LEVEL_INFO;
|
private static int level = LEVEL_INFO;
|
||||||
|
|
||||||
public static Logger getLogger(Class<?> clazz) {
|
public static Logger getLogger(Class<?> clazz) {
|
||||||
return new Logger(clazz);
|
return new Logger(clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setFactory(LoggerFactory f) {
|
public static void setFactory(LoggerFactory f) {
|
||||||
factory = f;
|
factory = f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setLevel(int l) {
|
public static void setLevel(int l) {
|
||||||
level = l;
|
level = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Logger(Class<?> clazz) {
|
public Logger(Class<?> clazz) {
|
||||||
this.clazz = clazz;
|
this.clazz = clazz;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Logger() {
|
protected Logger() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTraceEnabled() {
|
public boolean isTraceEnabled() {
|
||||||
if(factory != null) {
|
if(factory != null) {
|
||||||
if(logger == null)
|
if(logger == null)
|
||||||
logger = factory.getLogger(clazz);
|
logger = factory.getLogger(clazz);
|
||||||
|
|
||||||
return logger.isTraceEnabled();
|
return logger.isTraceEnabled();
|
||||||
}
|
}
|
||||||
return level <= LEVEL_TRACE;
|
return level <= LEVEL_TRACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDebugEnabled() {
|
public boolean isDebugEnabled() {
|
||||||
if(factory != null) {
|
if(factory != null) {
|
||||||
if(logger == null)
|
if(logger == null)
|
||||||
logger = factory.getLogger(clazz);
|
logger = factory.getLogger(clazz);
|
||||||
|
|
||||||
return logger.isDebugEnabled();
|
return logger.isDebugEnabled();
|
||||||
}
|
}
|
||||||
return level <= LEVEL_DEBUG;
|
return level <= LEVEL_DEBUG;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInfoEnabled() {
|
public boolean isInfoEnabled() {
|
||||||
if(factory != null) {
|
if(factory != null) {
|
||||||
if(logger == null)
|
if(logger == null)
|
||||||
logger = factory.getLogger(clazz);
|
logger = factory.getLogger(clazz);
|
||||||
|
|
||||||
return logger.isInfoEnabled();
|
return logger.isInfoEnabled();
|
||||||
}
|
}
|
||||||
return level <= LEVEL_INFO;
|
return level <= LEVEL_INFO;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void trace(Object message) {
|
public void trace(Object message) {
|
||||||
|
|
||||||
if(factory != null) {
|
if(factory != null) {
|
||||||
if(logger == null)
|
if(logger == null)
|
||||||
logger = factory.getLogger(clazz);
|
logger = factory.getLogger(clazz);
|
||||||
|
|
||||||
logger.trace(message);
|
logger.trace(message);
|
||||||
} else {
|
} else {
|
||||||
if(level <= LEVEL_TRACE)
|
if(level <= LEVEL_TRACE)
|
||||||
System.out.println(message);
|
System.out.println(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void trace(Object message, Throwable exception) {
|
public void trace(Object message, Throwable exception) {
|
||||||
if(factory != null) {
|
if(factory != null) {
|
||||||
if(logger == null)
|
if(logger == null)
|
||||||
logger = factory.getLogger(clazz);
|
logger = factory.getLogger(clazz);
|
||||||
|
|
||||||
logger.trace(message, exception);
|
logger.trace(message, exception);
|
||||||
} else {
|
} else {
|
||||||
if(level <= LEVEL_TRACE) {
|
if(level <= LEVEL_TRACE) {
|
||||||
System.out.println(message);
|
System.out.println(message);
|
||||||
if (exception != null) {
|
if (exception != null) {
|
||||||
exception.printStackTrace(System.out);
|
exception.printStackTrace(System.out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void info(Object message) {
|
public void info(Object message) {
|
||||||
if(factory != null) {
|
if(factory != null) {
|
||||||
if(logger == null)
|
if(logger == null)
|
||||||
logger = factory.getLogger(clazz);
|
logger = factory.getLogger(clazz);
|
||||||
|
|
||||||
logger.info(message);
|
logger.info(message);
|
||||||
} else {
|
} else {
|
||||||
if(level <= LEVEL_INFO)
|
if(level <= LEVEL_INFO)
|
||||||
System.out.println(message);
|
System.out.println(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void info(Object message, Throwable exception) {
|
public void info(Object message, Throwable exception) {
|
||||||
if(factory != null) {
|
if(factory != null) {
|
||||||
if(logger == null)
|
if(logger == null)
|
||||||
logger = factory.getLogger(clazz);
|
logger = factory.getLogger(clazz);
|
||||||
|
|
||||||
logger.info(message, exception);
|
logger.info(message, exception);
|
||||||
} else {
|
} else {
|
||||||
if(level <= LEVEL_INFO) {
|
if(level <= LEVEL_INFO) {
|
||||||
System.out.println(message);
|
System.out.println(message);
|
||||||
if (exception != null) {
|
if (exception != null) {
|
||||||
exception.printStackTrace(System.out);
|
exception.printStackTrace(System.out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void debug(Object message) {
|
public void debug(Object message) {
|
||||||
if(factory != null) {
|
if(factory != null) {
|
||||||
if(logger == null)
|
if(logger == null)
|
||||||
logger = factory.getLogger(clazz);
|
logger = factory.getLogger(clazz);
|
||||||
|
|
||||||
logger.debug(message);
|
logger.debug(message);
|
||||||
} else {
|
} else {
|
||||||
if(level <= LEVEL_DEBUG)
|
if(level <= LEVEL_DEBUG)
|
||||||
System.out.println(message);
|
System.out.println(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void debug(Object message, Throwable exception) {
|
public void debug(Object message, Throwable exception) {
|
||||||
if(factory != null) {
|
if(factory != null) {
|
||||||
if(logger == null)
|
if(logger == null)
|
||||||
logger = factory.getLogger(clazz);
|
logger = factory.getLogger(clazz);
|
||||||
|
|
||||||
logger.debug(message, exception);
|
logger.debug(message, exception);
|
||||||
} else {
|
} else {
|
||||||
if(level <= LEVEL_DEBUG) {
|
if(level <= LEVEL_DEBUG) {
|
||||||
System.out.println(message);
|
System.out.println(message);
|
||||||
if (exception != null) {
|
if (exception != null) {
|
||||||
exception.printStackTrace(System.out);
|
exception.printStackTrace(System.out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void warn(Object message) {
|
public void warn(Object message) {
|
||||||
if(factory != null) {
|
if(factory != null) {
|
||||||
if(logger == null)
|
if(logger == null)
|
||||||
logger = factory.getLogger(clazz);
|
logger = factory.getLogger(clazz);
|
||||||
|
|
||||||
logger.warn(message);
|
logger.warn(message);
|
||||||
} else {
|
} else {
|
||||||
if(level <= LEVEL_WARN)
|
if(level <= LEVEL_WARN)
|
||||||
System.out.println(message);
|
System.out.println(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void warn(Object message, Throwable exception) {
|
public void warn(Object message, Throwable exception) {
|
||||||
if(factory != null) {
|
if(factory != null) {
|
||||||
if(logger == null)
|
if(logger == null)
|
||||||
logger = factory.getLogger(clazz);
|
logger = factory.getLogger(clazz);
|
||||||
|
|
||||||
logger.warn(message, exception);
|
logger.warn(message, exception);
|
||||||
} else {
|
} else {
|
||||||
if(level <= LEVEL_WARN) {
|
if(level <= LEVEL_WARN) {
|
||||||
System.out.println(message);
|
System.out.println(message);
|
||||||
if (exception != null) {
|
if (exception != null) {
|
||||||
exception.printStackTrace(System.out);
|
exception.printStackTrace(System.out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void error(Object message) {
|
public void error(Object message) {
|
||||||
if(factory != null) {
|
if(factory != null) {
|
||||||
if(logger == null)
|
if(logger == null)
|
||||||
logger = factory.getLogger(clazz);
|
logger = factory.getLogger(clazz);
|
||||||
|
|
||||||
logger.error(message);
|
logger.error(message);
|
||||||
} else {
|
} else {
|
||||||
if(level <= LEVEL_ERROR)
|
if(level <= LEVEL_ERROR)
|
||||||
System.out.println(message);
|
System.out.println(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void error(Object message, Throwable exception) {
|
public void error(Object message, Throwable exception) {
|
||||||
if(factory != null) {
|
if(factory != null) {
|
||||||
if(logger == null)
|
if(logger == null)
|
||||||
logger = factory.getLogger(clazz);
|
logger = factory.getLogger(clazz);
|
||||||
|
|
||||||
logger.error(message, exception);
|
logger.error(message, exception);
|
||||||
} else {
|
} else {
|
||||||
if(level <= LEVEL_ERROR) {
|
if(level <= LEVEL_ERROR) {
|
||||||
System.out.println(message);
|
System.out.println(message);
|
||||||
if (exception != null) {
|
if (exception != null) {
|
||||||
exception.printStackTrace(System.out);
|
exception.printStackTrace(System.out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,5 +17,5 @@
|
|||||||
package com.cloud.consoleproxy.util;
|
package com.cloud.consoleproxy.util;
|
||||||
|
|
||||||
public interface LoggerFactory {
|
public interface LoggerFactory {
|
||||||
Logger getLogger(Class<?> clazz);
|
Logger getLogger(Class<?> clazz);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,7 +46,7 @@ import javax.net.ssl.X509TrustManager;
|
|||||||
* connections and import/export operations.
|
* connections and import/export operations.
|
||||||
*/
|
*/
|
||||||
public final class RawHTTP {
|
public final class RawHTTP {
|
||||||
private static final Logger s_logger = Logger.getLogger(RawHTTP.class);
|
private static final Logger s_logger = Logger.getLogger(RawHTTP.class);
|
||||||
|
|
||||||
private static final Pattern END_PATTERN = Pattern.compile("^\r\n$");
|
private static final Pattern END_PATTERN = Pattern.compile("^\r\n$");
|
||||||
private static final Pattern HEADER_PATTERN = Pattern
|
private static final Pattern HEADER_PATTERN = Pattern
|
||||||
@ -55,47 +55,47 @@ public final class RawHTTP {
|
|||||||
.compile("^HTTP/\\d+\\.\\d+ (\\d*) (.*)\r\n$");
|
.compile("^HTTP/\\d+\\.\\d+ (\\d*) (.*)\r\n$");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @uml.property name="command"
|
* @uml.property name="command"
|
||||||
*/
|
*/
|
||||||
private final String command;
|
private final String command;
|
||||||
/**
|
/**
|
||||||
* @uml.property name="host"
|
* @uml.property name="host"
|
||||||
*/
|
*/
|
||||||
private final String host;
|
private final String host;
|
||||||
/**
|
/**
|
||||||
* @uml.property name="port"
|
* @uml.property name="port"
|
||||||
*/
|
*/
|
||||||
private final int port;
|
private final int port;
|
||||||
/**
|
/**
|
||||||
* @uml.property name="path"
|
* @uml.property name="path"
|
||||||
*/
|
*/
|
||||||
private final String path;
|
private final String path;
|
||||||
/**
|
/**
|
||||||
* @uml.property name="session"
|
* @uml.property name="session"
|
||||||
*/
|
*/
|
||||||
private final String session;
|
private final String session;
|
||||||
/**
|
/**
|
||||||
* @uml.property name="useSSL"
|
* @uml.property name="useSSL"
|
||||||
*/
|
*/
|
||||||
private final boolean useSSL;
|
private final boolean useSSL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @uml.property name="responseHeaders"
|
* @uml.property name="responseHeaders"
|
||||||
* @uml.associationEnd qualifier="group:java.lang.String java.lang.String"
|
* @uml.associationEnd qualifier="group:java.lang.String java.lang.String"
|
||||||
*/
|
*/
|
||||||
private final Map<String, String> responseHeaders = new HashMap<String, String>();
|
private final Map<String, String> responseHeaders = new HashMap<String, String>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @uml.property name="ic"
|
* @uml.property name="ic"
|
||||||
*/
|
*/
|
||||||
private InputStream ic;
|
private InputStream ic;
|
||||||
/**
|
/**
|
||||||
* @uml.property name="oc"
|
* @uml.property name="oc"
|
||||||
*/
|
*/
|
||||||
private OutputStream oc;
|
private OutputStream oc;
|
||||||
/**
|
/**
|
||||||
* @uml.property name="s"
|
* @uml.property name="s"
|
||||||
*/
|
*/
|
||||||
private Socket s;
|
private Socket s;
|
||||||
|
|
||||||
public InputStream getInputStream() {
|
public InputStream getInputStream() {
|
||||||
@ -138,7 +138,7 @@ public final class RawHTTP {
|
|||||||
if (useSSL) {
|
if (useSSL) {
|
||||||
SSLContext context = getClientSSLContext();
|
SSLContext context = getClientSSLContext();
|
||||||
if(context == null)
|
if(context == null)
|
||||||
throw new IOException("Unable to setup SSL context");
|
throw new IOException("Unable to setup SSL context");
|
||||||
|
|
||||||
SSLSocket ssl = null;
|
SSLSocket ssl = null;
|
||||||
try {
|
try {
|
||||||
@ -237,13 +237,13 @@ public final class RawHTTP {
|
|||||||
|
|
||||||
private SSLContext getClientSSLContext() {
|
private SSLContext getClientSSLContext() {
|
||||||
SSLContext sslContext = null;
|
SSLContext sslContext = null;
|
||||||
try {
|
try {
|
||||||
sslContext = SSLContext.getInstance("SSL", "SunJSSE");
|
sslContext = SSLContext.getInstance("SSL", "SunJSSE");
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
s_logger.error("Unexpected exception ", e);
|
s_logger.error("Unexpected exception ", e);
|
||||||
} catch (NoSuchProviderException e) {
|
} catch (NoSuchProviderException e) {
|
||||||
s_logger.error("Unexpected exception ", e);
|
s_logger.error("Unexpected exception ", e);
|
||||||
}
|
}
|
||||||
return sslContext;
|
return sslContext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,70 +21,70 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Region {
|
public class Region {
|
||||||
private Rectangle bound;
|
private Rectangle bound;
|
||||||
private List<Rectangle> rectList;
|
private List<Rectangle> rectList;
|
||||||
|
|
||||||
public Region() {
|
public Region() {
|
||||||
bound = new Rectangle(0, 0, 0, 0);
|
bound = new Rectangle(0, 0, 0, 0);
|
||||||
rectList = new ArrayList<Rectangle>();
|
rectList = new ArrayList<Rectangle>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Region(Rectangle rect) {
|
public Region(Rectangle rect) {
|
||||||
bound = new Rectangle(rect.x, rect.y, rect.width, rect.height);
|
bound = new Rectangle(rect.x, rect.y, rect.width, rect.height);
|
||||||
rectList = new ArrayList<Rectangle>();
|
rectList = new ArrayList<Rectangle>();
|
||||||
rectList.add(rect);
|
rectList.add(rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rectangle getBound() {
|
public Rectangle getBound() {
|
||||||
return bound;
|
return bound;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearBound() {
|
public void clearBound() {
|
||||||
assert(rectList.size() == 0);
|
assert(rectList.size() == 0);
|
||||||
bound.x = bound.y = bound.width = bound.height = 0;
|
bound.x = bound.y = bound.width = bound.height = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Rectangle> getRectangles() {
|
public List<Rectangle> getRectangles() {
|
||||||
return rectList;
|
return rectList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean add(Rectangle rect) {
|
public boolean add(Rectangle rect) {
|
||||||
if(bound.isEmpty()) {
|
if(bound.isEmpty()) {
|
||||||
assert(rectList.size() == 0);
|
assert(rectList.size() == 0);
|
||||||
bound.x = rect.x;
|
bound.x = rect.x;
|
||||||
bound.y = rect.y;
|
bound.y = rect.y;
|
||||||
bound.width = rect.width;
|
bound.width = rect.width;
|
||||||
bound.height = rect.height;
|
bound.height = rect.height;
|
||||||
|
|
||||||
rectList.add(rect);
|
rectList.add(rect);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle rcInflated = new Rectangle(rect.x - 1, rect.y- 1, rect.width + 2, rect.height + 2);
|
Rectangle rcInflated = new Rectangle(rect.x - 1, rect.y- 1, rect.width + 2, rect.height + 2);
|
||||||
if(!bound.intersects(rcInflated))
|
if(!bound.intersects(rcInflated))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for(Rectangle r : rectList) {
|
for(Rectangle r : rectList) {
|
||||||
if(r.intersects(rcInflated)) {
|
if(r.intersects(rcInflated)) {
|
||||||
if(!r.contains(rect)) {
|
if(!r.contains(rect)) {
|
||||||
enlargeBound(rect);
|
enlargeBound(rect);
|
||||||
rectList.add(rect);
|
rectList.add(rect);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enlargeBound(Rectangle rect) {
|
private void enlargeBound(Rectangle rect) {
|
||||||
int boundLeft = Math.min(bound.x, rect.x);
|
int boundLeft = Math.min(bound.x, rect.x);
|
||||||
int boundTop = Math.min(bound.y, rect.y);
|
int boundTop = Math.min(bound.y, rect.y);
|
||||||
int boundRight = Math.max(bound.x + bound.width, rect.x + rect.width);
|
int boundRight = Math.max(bound.x + bound.width, rect.x + rect.width);
|
||||||
int boundBottom = Math.max(bound.y + bound.height, rect.y + rect.height);
|
int boundBottom = Math.max(bound.y + bound.height, rect.y + rect.height);
|
||||||
|
|
||||||
bound.x = boundLeft;
|
bound.x = boundLeft;
|
||||||
bound.y = boundTop;
|
bound.y = boundTop;
|
||||||
bound.width = boundRight - boundLeft;
|
bound.width = boundRight - boundLeft;
|
||||||
bound.height = boundBottom - boundTop;
|
bound.height = boundBottom - boundTop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,38 +21,38 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class RegionClassifier {
|
public class RegionClassifier {
|
||||||
private List<Region> regionList;
|
private List<Region> regionList;
|
||||||
|
|
||||||
public RegionClassifier() {
|
public RegionClassifier() {
|
||||||
regionList = new ArrayList<Region>();
|
regionList = new ArrayList<Region>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(Rectangle rect) {
|
public void add(Rectangle rect) {
|
||||||
boolean newRegion = true;
|
boolean newRegion = true;
|
||||||
Rectangle rcInflated = new Rectangle(rect.x - 1, rect.y - 1, rect.width + 2, rect.height + 2);
|
Rectangle rcInflated = new Rectangle(rect.x - 1, rect.y - 1, rect.width + 2, rect.height + 2);
|
||||||
for(Region region : regionList) {
|
for(Region region : regionList) {
|
||||||
if(region.getBound().intersects(rcInflated)) {
|
if(region.getBound().intersects(rcInflated)) {
|
||||||
newRegion = false;
|
newRegion = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(newRegion) {
|
if(newRegion) {
|
||||||
regionList.add(new Region(rect));
|
regionList.add(new Region(rect));
|
||||||
} else {
|
} else {
|
||||||
for(Region region : regionList) {
|
for(Region region : regionList) {
|
||||||
if(region.add(rect))
|
if(region.add(rect))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
regionList.add(new Region(rect));
|
regionList.add(new Region(rect));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Region> getRegionList() {
|
public List<Region> getRegionList() {
|
||||||
return regionList;
|
return regionList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
regionList.clear();
|
regionList.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,37 +19,37 @@ package com.cloud.consoleproxy.util;
|
|||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
|
||||||
public class TileInfo {
|
public class TileInfo {
|
||||||
private int row;
|
private int row;
|
||||||
private int col;
|
private int col;
|
||||||
private Rectangle tileRect;
|
private Rectangle tileRect;
|
||||||
|
|
||||||
public TileInfo(int row, int col, Rectangle tileRect) {
|
public TileInfo(int row, int col, Rectangle tileRect) {
|
||||||
this.row = row;
|
this.row = row;
|
||||||
this.col = col;
|
this.col = col;
|
||||||
this.tileRect = tileRect;
|
this.tileRect = tileRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getRow() {
|
public int getRow() {
|
||||||
return row;
|
return row;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRow(int row) {
|
public void setRow(int row) {
|
||||||
this.row = row;
|
this.row = row;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCol() {
|
public int getCol() {
|
||||||
return col;
|
return col;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCol(int col) {
|
public void setCol(int col) {
|
||||||
this.col = col;
|
this.col = col;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rectangle getTileRect() {
|
public Rectangle getTileRect() {
|
||||||
return tileRect;
|
return tileRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTileRect(Rectangle tileRect) {
|
public void setTileRect(Rectangle tileRect) {
|
||||||
this.tileRect = tileRect;
|
this.tileRect = tileRect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,248 +22,248 @@ import java.util.List;
|
|||||||
|
|
||||||
public class TileTracker {
|
public class TileTracker {
|
||||||
|
|
||||||
// 2 dimension tile status snapshot, a true value means the corresponding tile has been invalidated
|
// 2 dimension tile status snapshot, a true value means the corresponding tile has been invalidated
|
||||||
private boolean[][] snapshot;
|
private boolean[][] snapshot;
|
||||||
|
|
||||||
private int tileWidth = 0;
|
private int tileWidth = 0;
|
||||||
private int tileHeight = 0;
|
private int tileHeight = 0;
|
||||||
private int trackWidth = 0;
|
private int trackWidth = 0;
|
||||||
private int trackHeight = 0;
|
private int trackHeight = 0;
|
||||||
|
|
||||||
public TileTracker() {
|
public TileTracker() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTileWidth() {
|
public int getTileWidth() {
|
||||||
return tileWidth;
|
return tileWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTileWidth(int tileWidth) {
|
public void setTileWidth(int tileWidth) {
|
||||||
this.tileWidth = tileWidth;
|
this.tileWidth = tileWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTileHeight() {
|
public int getTileHeight() {
|
||||||
return tileHeight;
|
return tileHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTileHeight(int tileHeight) {
|
public void setTileHeight(int tileHeight) {
|
||||||
this.tileHeight = tileHeight;
|
this.tileHeight = tileHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTrackWidth() {
|
public int getTrackWidth() {
|
||||||
return trackWidth;
|
return trackWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTrackWidth(int trackWidth) {
|
public void setTrackWidth(int trackWidth) {
|
||||||
this.trackWidth = trackWidth;
|
this.trackWidth = trackWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTrackHeight() {
|
public int getTrackHeight() {
|
||||||
return trackHeight;
|
return trackHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTrackHeight(int trackHeight) {
|
public void setTrackHeight(int trackHeight) {
|
||||||
this.trackHeight = trackHeight;
|
this.trackHeight = trackHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initTracking(int tileWidth, int tileHeight, int trackWidth, int trackHeight) {
|
public void initTracking(int tileWidth, int tileHeight, int trackWidth, int trackHeight) {
|
||||||
assert(tileWidth > 0);
|
assert(tileWidth > 0);
|
||||||
assert(tileHeight > 0);
|
assert(tileHeight > 0);
|
||||||
assert(trackWidth > 0);
|
assert(trackWidth > 0);
|
||||||
assert(trackHeight > 0);
|
assert(trackHeight > 0);
|
||||||
assert(tileWidth <= trackWidth);
|
assert(tileWidth <= trackWidth);
|
||||||
assert(tileHeight <= trackHeight);
|
assert(tileHeight <= trackHeight);
|
||||||
|
|
||||||
this.tileWidth = tileWidth;
|
this.tileWidth = tileWidth;
|
||||||
this.tileHeight = tileHeight;
|
this.tileHeight = tileHeight;
|
||||||
this.trackWidth = trackWidth;
|
this.trackWidth = trackWidth;
|
||||||
this.trackHeight = trackHeight;
|
this.trackHeight = trackHeight;
|
||||||
|
|
||||||
int cols = getTileCols();
|
int cols = getTileCols();
|
||||||
int rows = getTileRows();
|
int rows = getTileRows();
|
||||||
snapshot = new boolean[rows][cols];
|
snapshot = new boolean[rows][cols];
|
||||||
for(int i = 0; i < rows; i++)
|
for(int i = 0; i < rows; i++)
|
||||||
for(int j = 0; j < cols; j++)
|
for(int j = 0; j < cols; j++)
|
||||||
snapshot[i][j] = false;
|
snapshot[i][j] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void resize(int trackWidth, int trackHeight) {
|
public synchronized void resize(int trackWidth, int trackHeight) {
|
||||||
assert(tileWidth > 0);
|
assert(tileWidth > 0);
|
||||||
assert(tileHeight > 0);
|
assert(tileHeight > 0);
|
||||||
assert(trackWidth > 0);
|
assert(trackWidth > 0);
|
||||||
assert(trackHeight > 0);
|
assert(trackHeight > 0);
|
||||||
|
|
||||||
this.trackWidth = trackWidth;
|
this.trackWidth = trackWidth;
|
||||||
this.trackHeight = trackHeight;
|
this.trackHeight = trackHeight;
|
||||||
|
|
||||||
int cols = getTileCols();
|
int cols = getTileCols();
|
||||||
int rows = getTileRows();
|
int rows = getTileRows();
|
||||||
snapshot = new boolean[rows][cols];
|
snapshot = new boolean[rows][cols];
|
||||||
for(int i = 0; i < rows; i++)
|
for(int i = 0; i < rows; i++)
|
||||||
for(int j = 0; j < cols; j++)
|
for(int j = 0; j < cols; j++)
|
||||||
snapshot[i][j] = true;
|
snapshot[i][j] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invalidate(Rectangle rect) {
|
public void invalidate(Rectangle rect) {
|
||||||
setTileFlag(rect, true);
|
setTileFlag(rect, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validate(Rectangle rect) {
|
public void validate(Rectangle rect) {
|
||||||
setTileFlag(rect, false);
|
setTileFlag(rect, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<TileInfo> scan(boolean init) {
|
public List<TileInfo> scan(boolean init) {
|
||||||
List<TileInfo> l = new ArrayList<TileInfo>();
|
List<TileInfo> l = new ArrayList<TileInfo>();
|
||||||
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
for(int i = 0; i < getTileRows(); i++) {
|
for(int i = 0; i < getTileRows(); i++) {
|
||||||
for(int j = 0; j < getTileCols(); j++) {
|
for(int j = 0; j < getTileCols(); j++) {
|
||||||
if(init || snapshot[i][j]) {
|
if(init || snapshot[i][j]) {
|
||||||
Rectangle rect = new Rectangle();
|
Rectangle rect = new Rectangle();
|
||||||
rect.y = i*tileHeight;
|
rect.y = i*tileHeight;
|
||||||
rect.x = j*tileWidth;
|
rect.x = j*tileWidth;
|
||||||
rect.width = Math.min(trackWidth - rect.x, tileWidth);
|
rect.width = Math.min(trackWidth - rect.x, tileWidth);
|
||||||
rect.height = Math.min(trackHeight - rect.y, tileHeight);
|
rect.height = Math.min(trackHeight - rect.y, tileHeight);
|
||||||
|
|
||||||
l.add(new TileInfo(i, j, rect));
|
l.add(new TileInfo(i, j, rect));
|
||||||
snapshot[i][j] = false;
|
snapshot[i][j] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasFullCoverage() {
|
public boolean hasFullCoverage() {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
for(int i = 0; i < getTileRows(); i++) {
|
for(int i = 0; i < getTileRows(); i++) {
|
||||||
for(int j = 0; j < getTileCols(); j++) {
|
for(int j = 0; j < getTileCols(); j++) {
|
||||||
if(!snapshot[i][j])
|
if(!snapshot[i][j])
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void initCoverageTest() {
|
public void initCoverageTest() {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
for(int i = 0; i < getTileRows(); i++) {
|
for(int i = 0; i < getTileRows(); i++) {
|
||||||
for(int j = 0; j < getTileCols(); j++) {
|
for(int j = 0; j < getTileCols(); j++) {
|
||||||
snapshot[i][j] = false;
|
snapshot[i][j] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// listener will be called while holding the object lock, use it
|
// listener will be called while holding the object lock, use it
|
||||||
// with care to avoid deadlock condition being formed
|
// with care to avoid deadlock condition being formed
|
||||||
public synchronized void scan(int nStartRow, int nStartCol, ITileScanListener listener) {
|
public synchronized void scan(int nStartRow, int nStartCol, ITileScanListener listener) {
|
||||||
assert(listener != null);
|
assert(listener != null);
|
||||||
|
|
||||||
int cols = getTileCols();
|
int cols = getTileCols();
|
||||||
int rows = getTileRows();
|
int rows = getTileRows();
|
||||||
|
|
||||||
nStartRow = nStartRow % rows;
|
nStartRow = nStartRow % rows;
|
||||||
nStartCol = nStartCol % cols;
|
nStartCol = nStartCol % cols;
|
||||||
|
|
||||||
int nPos = nStartRow*cols + nStartCol;
|
int nPos = nStartRow*cols + nStartCol;
|
||||||
int nUnits = rows*cols;
|
int nUnits = rows*cols;
|
||||||
int nStartPos = nPos;
|
int nStartPos = nPos;
|
||||||
int nRow;
|
int nRow;
|
||||||
int nCol;
|
int nCol;
|
||||||
do {
|
do {
|
||||||
nRow = nPos / cols;
|
nRow = nPos / cols;
|
||||||
nCol = nPos % cols;
|
nCol = nPos % cols;
|
||||||
|
|
||||||
if(snapshot[nRow][nCol]) {
|
if(snapshot[nRow][nCol]) {
|
||||||
int nEndCol = nCol;
|
int nEndCol = nCol;
|
||||||
for(; nEndCol < cols && snapshot[nRow][nEndCol]; nEndCol++) {
|
for(; nEndCol < cols && snapshot[nRow][nEndCol]; nEndCol++) {
|
||||||
snapshot[nRow][nEndCol] = false;
|
snapshot[nRow][nEndCol] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle rect = new Rectangle();
|
Rectangle rect = new Rectangle();
|
||||||
rect.y = nRow*tileHeight;
|
rect.y = nRow*tileHeight;
|
||||||
rect.height = tileHeight;
|
rect.height = tileHeight;
|
||||||
rect.x = nCol*tileWidth;
|
rect.x = nCol*tileWidth;
|
||||||
rect.width = (nEndCol - nCol)*tileWidth;
|
rect.width = (nEndCol - nCol)*tileWidth;
|
||||||
|
|
||||||
if(!listener.onTileChange(rect, nRow, nEndCol))
|
if(!listener.onTileChange(rect, nRow, nEndCol))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
nPos = (nPos + 1) % nUnits;
|
nPos = (nPos + 1) % nUnits;
|
||||||
} while(nPos != nStartPos);
|
} while(nPos != nStartPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void capture(ITileScanListener listener) {
|
public void capture(ITileScanListener listener) {
|
||||||
assert(listener != null);
|
assert(listener != null);
|
||||||
|
|
||||||
int cols = getTileCols();
|
int cols = getTileCols();
|
||||||
int rows = getTileRows();
|
int rows = getTileRows();
|
||||||
|
|
||||||
RegionClassifier classifier = new RegionClassifier();
|
RegionClassifier classifier = new RegionClassifier();
|
||||||
int left, top, right, bottom;
|
int left, top, right, bottom;
|
||||||
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
for(int i = 0; i < rows; i++) {
|
for(int i = 0; i < rows; i++) {
|
||||||
top = i*tileHeight;
|
top = i*tileHeight;
|
||||||
bottom = Math.min(top + tileHeight, trackHeight);
|
bottom = Math.min(top + tileHeight, trackHeight);
|
||||||
for(int j = 0; j < cols; j++) {
|
for(int j = 0; j < cols; j++) {
|
||||||
left = j*tileWidth;
|
left = j*tileWidth;
|
||||||
right = Math.min(left + tileWidth, trackWidth);
|
right = Math.min(left + tileWidth, trackWidth);
|
||||||
|
|
||||||
if(snapshot[i][j]) {
|
if(snapshot[i][j]) {
|
||||||
snapshot[i][j] = false;
|
snapshot[i][j] = false;
|
||||||
classifier.add(new Rectangle(left, top, right - left, bottom - top));
|
classifier.add(new Rectangle(left, top, right - left, bottom - top));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
listener.onRegionChange(classifier.getRegionList());
|
listener.onRegionChange(classifier.getRegionList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void setTileFlag(Rectangle rect, boolean flag) {
|
private synchronized void setTileFlag(Rectangle rect, boolean flag) {
|
||||||
int nStartTileRow;
|
int nStartTileRow;
|
||||||
int nStartTileCol;
|
int nStartTileCol;
|
||||||
int nEndTileRow;
|
int nEndTileRow;
|
||||||
int nEndTileCol;
|
int nEndTileCol;
|
||||||
|
|
||||||
int cols = getTileCols();
|
int cols = getTileCols();
|
||||||
int rows = getTileRows();
|
int rows = getTileRows();
|
||||||
|
|
||||||
if(rect != null) {
|
if(rect != null) {
|
||||||
nStartTileRow = Math.min(getTileYPos(rect.y), rows - 1);
|
nStartTileRow = Math.min(getTileYPos(rect.y), rows - 1);
|
||||||
nStartTileCol = Math.min(getTileXPos(rect.x), cols - 1);
|
nStartTileCol = Math.min(getTileXPos(rect.x), cols - 1);
|
||||||
nEndTileRow = Math.min(getTileYPos(rect.y + rect.height - 1), rows -1);
|
nEndTileRow = Math.min(getTileYPos(rect.y + rect.height - 1), rows -1);
|
||||||
nEndTileCol = Math.min(getTileXPos(rect.x + rect.width - 1), cols -1);
|
nEndTileCol = Math.min(getTileXPos(rect.x + rect.width - 1), cols -1);
|
||||||
} else {
|
} else {
|
||||||
nStartTileRow = 0;
|
nStartTileRow = 0;
|
||||||
nStartTileCol = 0;
|
nStartTileCol = 0;
|
||||||
nEndTileRow = rows - 1;
|
nEndTileRow = rows - 1;
|
||||||
nEndTileCol = cols - 1;
|
nEndTileCol = cols - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = nStartTileRow; i <= nEndTileRow; i++)
|
for(int i = nStartTileRow; i <= nEndTileRow; i++)
|
||||||
for(int j = nStartTileCol; j <= nEndTileCol; j++)
|
for(int j = nStartTileCol; j <= nEndTileCol; j++)
|
||||||
snapshot[i][j] = flag;
|
snapshot[i][j] = flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getTileRows() {
|
private int getTileRows() {
|
||||||
return (trackHeight + tileHeight - 1) / tileHeight;
|
return (trackHeight + tileHeight - 1) / tileHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getTileCols() {
|
private int getTileCols() {
|
||||||
return (trackWidth + tileWidth - 1) / tileWidth;
|
return (trackWidth + tileWidth - 1) / tileWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getTileXPos(int x) {
|
private int getTileXPos(int x) {
|
||||||
return x / tileWidth;
|
return x / tileWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTileYPos(int y) {
|
public int getTileYPos(int y) {
|
||||||
return y / tileHeight;
|
return y / tileHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,125 +30,121 @@ import com.cloud.consoleproxy.util.ImageHelper;
|
|||||||
import com.cloud.consoleproxy.util.TileInfo;
|
import com.cloud.consoleproxy.util.TileInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A <code>BuffereImageCanvas</code> component represents frame buffer image on the
|
* A <code>BuffereImageCanvas</code> component represents frame buffer image on
|
||||||
* screen. It also notifies its subscribers when screen is repainted.
|
* the screen. It also notifies its subscribers when screen is repainted.
|
||||||
*/
|
*/
|
||||||
public class BufferedImageCanvas extends Canvas implements FrameBufferCanvas {
|
public class BufferedImageCanvas extends Canvas implements FrameBufferCanvas {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
// Offline screen buffer
|
// Offline screen buffer
|
||||||
private BufferedImage offlineImage;
|
private BufferedImage offlineImage;
|
||||||
|
|
||||||
// Cached Graphics2D object for offline screen buffer
|
// Cached Graphics2D object for offline screen buffer
|
||||||
private Graphics2D graphics;
|
private Graphics2D graphics;
|
||||||
|
|
||||||
private PaintNotificationListener listener;
|
private PaintNotificationListener listener;
|
||||||
|
|
||||||
public BufferedImageCanvas(PaintNotificationListener listener, int width, int height) {
|
public BufferedImageCanvas(PaintNotificationListener listener, int width, int height) {
|
||||||
super();
|
super();
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
|
||||||
setBackground(Color.black);
|
setBackground(Color.black);
|
||||||
|
|
||||||
setFocusable(true);
|
setFocusable(true);
|
||||||
|
|
||||||
// Don't intercept TAB key
|
// Don't intercept TAB key
|
||||||
setFocusTraversalKeysEnabled(false);
|
setFocusTraversalKeysEnabled(false);
|
||||||
|
|
||||||
setCanvasSize(width, height);
|
setCanvasSize(width, height);
|
||||||
}
|
|
||||||
|
|
||||||
public void setCanvasSize(int width, int height) {
|
|
||||||
this.offlineImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
|
||||||
graphics = offlineImage.createGraphics();
|
|
||||||
|
|
||||||
setSize(offlineImage.getWidth(), offlineImage.getHeight());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(Graphics g) {
|
|
||||||
// Call paint() directly, without clearing screen first
|
|
||||||
paint(g);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void paint(Graphics g) {
|
|
||||||
// Only part of image, requested with repaint(Rectangle), will be
|
|
||||||
// painted on screen.
|
|
||||||
synchronized(offlineImage) {
|
|
||||||
g.drawImage(offlineImage, 0, 0, this);
|
|
||||||
}
|
}
|
||||||
// Notify server that update is painted on screen
|
|
||||||
listener.imagePaintedOnScreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BufferedImage getOfflineImage() {
|
public void setCanvasSize(int width, int height) {
|
||||||
return offlineImage;
|
this.offlineImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||||
}
|
graphics = offlineImage.createGraphics();
|
||||||
|
|
||||||
public Graphics2D getOfflineGraphics() {
|
setSize(offlineImage.getWidth(), offlineImage.getHeight());
|
||||||
return graphics;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void copyTile(Graphics2D g, int x, int y, Rectangle rc) {
|
@Override
|
||||||
synchronized(offlineImage) {
|
public void update(Graphics g) {
|
||||||
g.drawImage(offlineImage, x, y, x + rc.width, y + rc.height,
|
// Call paint() directly, without clearing screen first
|
||||||
rc.x, rc.y, rc.x + rc.width, rc.y + rc.height, null);
|
paint(g);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Image getFrameBufferScaledImage(int width, int height) {
|
public void paint(Graphics g) {
|
||||||
if(offlineImage != null)
|
// Only part of image, requested with repaint(Rectangle), will be
|
||||||
return offlineImage.getScaledInstance(width, height, Image.SCALE_DEFAULT);
|
// painted on screen.
|
||||||
return null;
|
synchronized (offlineImage) {
|
||||||
}
|
g.drawImage(offlineImage, 0, 0, this);
|
||||||
|
}
|
||||||
|
// Notify server that update is painted on screen
|
||||||
|
listener.imagePaintedOnScreen();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
public BufferedImage getOfflineImage() {
|
||||||
public byte[] getFrameBufferJpeg() {
|
return offlineImage;
|
||||||
int width = 800;
|
}
|
||||||
int height = 600;
|
|
||||||
|
|
||||||
width = offlineImage.getWidth();
|
public Graphics2D getOfflineGraphics() {
|
||||||
height = offlineImage.getHeight();
|
return graphics;
|
||||||
|
}
|
||||||
|
|
||||||
BufferedImage bufferedImage = new BufferedImage(width, height,
|
public void copyTile(Graphics2D g, int x, int y, Rectangle rc) {
|
||||||
BufferedImage.TYPE_3BYTE_BGR);
|
synchronized (offlineImage) {
|
||||||
Graphics2D g = bufferedImage.createGraphics();
|
g.drawImage(offlineImage, x, y, x + rc.width, y + rc.height, rc.x, rc.y, rc.x + rc.width, rc.y + rc.height, null);
|
||||||
synchronized(offlineImage) {
|
}
|
||||||
g.drawImage(offlineImage, 0, 0, width, height, 0, 0, width, height, null);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
byte[] imgBits = null;
|
@Override
|
||||||
try {
|
public Image getFrameBufferScaledImage(int width, int height) {
|
||||||
imgBits = ImageHelper.jpegFromImage(bufferedImage);
|
if (offlineImage != null)
|
||||||
} catch (IOException e) {
|
return offlineImage.getScaledInstance(width, height, Image.SCALE_DEFAULT);
|
||||||
}
|
return null;
|
||||||
return imgBits;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getTilesMergedJpeg(List<TileInfo> tileList, int tileWidth, int tileHeight) {
|
public byte[] getFrameBufferJpeg() {
|
||||||
int width = Math.max(tileWidth, tileWidth*tileList.size());
|
int width = 800;
|
||||||
BufferedImage bufferedImage = new BufferedImage(width, tileHeight,
|
int height = 600;
|
||||||
BufferedImage.TYPE_3BYTE_BGR);
|
|
||||||
Graphics2D g = bufferedImage.createGraphics();
|
|
||||||
|
|
||||||
synchronized(offlineImage) {
|
width = offlineImage.getWidth();
|
||||||
int i = 0;
|
height = offlineImage.getHeight();
|
||||||
for(TileInfo tile : tileList) {
|
|
||||||
Rectangle rc = tile.getTileRect();
|
|
||||||
g.drawImage(offlineImage, i*tileWidth, 0, i*tileWidth + rc.width, rc.height,
|
|
||||||
rc.x, rc.y, rc.x + rc.width, rc.y + rc.height, null);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] imgBits = null;
|
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
|
||||||
try {
|
Graphics2D g = bufferedImage.createGraphics();
|
||||||
imgBits = ImageHelper.jpegFromImage(bufferedImage);
|
synchronized (offlineImage) {
|
||||||
} catch (IOException e) {
|
g.drawImage(offlineImage, 0, 0, width, height, 0, 0, width, height, null);
|
||||||
}
|
}
|
||||||
return imgBits;
|
|
||||||
}
|
byte[] imgBits = null;
|
||||||
|
try {
|
||||||
|
imgBits = ImageHelper.jpegFromImage(bufferedImage);
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
return imgBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getTilesMergedJpeg(List<TileInfo> tileList, int tileWidth, int tileHeight) {
|
||||||
|
int width = Math.max(tileWidth, tileWidth * tileList.size());
|
||||||
|
BufferedImage bufferedImage = new BufferedImage(width, tileHeight, BufferedImage.TYPE_3BYTE_BGR);
|
||||||
|
Graphics2D g = bufferedImage.createGraphics();
|
||||||
|
|
||||||
|
synchronized (offlineImage) {
|
||||||
|
int i = 0;
|
||||||
|
for (TileInfo tile : tileList) {
|
||||||
|
Rectangle rc = tile.getTileRect();
|
||||||
|
g.drawImage(offlineImage, i * tileWidth, 0, i * tileWidth + rc.width, rc.height, rc.x, rc.y, rc.x + rc.width, rc.y + rc.height, null);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] imgBits = null;
|
||||||
|
try {
|
||||||
|
imgBits = ImageHelper.jpegFromImage(bufferedImage);
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
return imgBits;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -22,7 +22,9 @@ import java.util.List;
|
|||||||
import com.cloud.consoleproxy.util.TileInfo;
|
import com.cloud.consoleproxy.util.TileInfo;
|
||||||
|
|
||||||
public interface FrameBufferCanvas {
|
public interface FrameBufferCanvas {
|
||||||
Image getFrameBufferScaledImage(int width, int height);
|
Image getFrameBufferScaledImage(int width, int height);
|
||||||
public byte[] getFrameBufferJpeg();
|
|
||||||
public byte[] getTilesMergedJpeg(List<TileInfo> tileList, int tileWidth, int tileHeight);
|
public byte[] getFrameBufferJpeg();
|
||||||
|
|
||||||
|
public byte[] getTilesMergedJpeg(List<TileInfo> tileList, int tileWidth, int tileHeight);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,9 +18,9 @@ package com.cloud.consoleproxy.vnc;
|
|||||||
|
|
||||||
public interface FrameBufferUpdateListener {
|
public interface FrameBufferUpdateListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify listener, that frame buffer update packet is received, so client is
|
* Notify listener, that frame buffer update packet is received, so client
|
||||||
* permitted (but not obligated) to ask server to send another update.
|
* is permitted (but not obligated) to ask server to send another update.
|
||||||
*/
|
*/
|
||||||
void frameBufferPacketReceived();
|
void frameBufferPacketReceived();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,10 +18,10 @@ package com.cloud.consoleproxy.vnc;
|
|||||||
|
|
||||||
public interface PaintNotificationListener {
|
public interface PaintNotificationListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify subscriber that screen is updated, so client can send another frame
|
* Notify subscriber that screen is updated, so client can send another
|
||||||
* buffer update request to server.
|
* frame buffer update request to server.
|
||||||
*/
|
*/
|
||||||
void imagePaintedOnScreen();
|
void imagePaintedOnScreen();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,62 +20,63 @@ import java.nio.charset.Charset;
|
|||||||
|
|
||||||
public interface RfbConstants {
|
public interface RfbConstants {
|
||||||
|
|
||||||
public static final String RFB_PROTOCOL_VERSION_MAJOR = "RFB 003.";
|
public static final String RFB_PROTOCOL_VERSION_MAJOR = "RFB 003.";
|
||||||
// public static final String VNC_PROTOCOL_VERSION_MINOR = "003";
|
// public static final String VNC_PROTOCOL_VERSION_MINOR = "003";
|
||||||
public static final String VNC_PROTOCOL_VERSION_MINOR = "003";
|
public static final String VNC_PROTOCOL_VERSION_MINOR = "003";
|
||||||
public static final String RFB_PROTOCOL_VERSION = RFB_PROTOCOL_VERSION_MAJOR + VNC_PROTOCOL_VERSION_MINOR;
|
public static final String RFB_PROTOCOL_VERSION = RFB_PROTOCOL_VERSION_MAJOR + VNC_PROTOCOL_VERSION_MINOR;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server message types.
|
* Server message types.
|
||||||
*/
|
*/
|
||||||
final static int SERVER_FRAMEBUFFER_UPDATE = 0, SERVER_SET_COLOURMAP_ENTRIES = 1, SERVER_BELL = 2, SERVER_CUT_TEXT = 3;
|
final static int SERVER_FRAMEBUFFER_UPDATE = 0, SERVER_SET_COLOURMAP_ENTRIES = 1, SERVER_BELL = 2, SERVER_CUT_TEXT = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client message types.
|
* Client message types.
|
||||||
*/
|
*/
|
||||||
public static final int CLIENT_SET_PIXEL_FORMAT = 0, CLIENT_FIX_COLOURMAP_ENTRIES = 1, CLIENT_SET_ENCODINGS = 2, CLIENT_FRAMEBUFFER_UPDATE_REQUEST = 3,
|
public static final int CLIENT_SET_PIXEL_FORMAT = 0, CLIENT_FIX_COLOURMAP_ENTRIES = 1, CLIENT_SET_ENCODINGS = 2, CLIENT_FRAMEBUFFER_UPDATE_REQUEST = 3, CLIENT_KEYBOARD_EVENT = 4,
|
||||||
CLIENT_KEYBOARD_EVENT = 4, CLIENT_POINTER_EVENT = 5, CLIENT_CUT_TEXT = 6;
|
CLIENT_POINTER_EVENT = 5, CLIENT_CUT_TEXT = 6;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server authorization type
|
* Server authorization type
|
||||||
*/
|
*/
|
||||||
public final static int CONNECTION_FAILED = 0, NO_AUTH = 1, VNC_AUTH = 2;
|
public final static int CONNECTION_FAILED = 0, NO_AUTH = 1, VNC_AUTH = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server authorization reply.
|
* Server authorization reply.
|
||||||
*/
|
*/
|
||||||
public final static int VNC_AUTH_OK = 0, VNC_AUTH_FAILED = 1, VNC_AUTH_TOO_MANY = 2;
|
public final static int VNC_AUTH_OK = 0, VNC_AUTH_FAILED = 1, VNC_AUTH_TOO_MANY = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodings.
|
* Encodings.
|
||||||
*/
|
*/
|
||||||
public final static int ENCODING_RAW = 0, ENCODING_COPY_RECT = 1, ENCODING_RRE = 2, ENCODING_CO_RRE = 4, ENCODING_HEXTILE = 5, ENCODING_ZRLE = 16;
|
public final static int ENCODING_RAW = 0, ENCODING_COPY_RECT = 1, ENCODING_RRE = 2, ENCODING_CO_RRE = 4, ENCODING_HEXTILE = 5, ENCODING_ZRLE = 16;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pseudo-encodings.
|
* Pseudo-encodings.
|
||||||
*/
|
*/
|
||||||
public final static int ENCODING_CURSOR = -239 /*0xFFFFFF11*/, ENCODING_DESKTOP_SIZE = -223 /*0xFFFFFF21*/;
|
public final static int ENCODING_CURSOR = -239 /* 0xFFFFFF11 */, ENCODING_DESKTOP_SIZE = -223 /* 0xFFFFFF21 */;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encodings, which we support.
|
* Encodings, which we support.
|
||||||
*/
|
*/
|
||||||
public final static int[] SUPPORTED_ENCODINGS_ARRAY = { ENCODING_RAW, ENCODING_COPY_RECT, ENCODING_DESKTOP_SIZE };
|
public final static int[] SUPPORTED_ENCODINGS_ARRAY = { ENCODING_RAW, ENCODING_COPY_RECT, ENCODING_DESKTOP_SIZE };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frame buffer update request type: update of whole screen or partial update.
|
* Frame buffer update request type: update of whole screen or partial
|
||||||
*/
|
* update.
|
||||||
public static final int FRAMEBUFFER_FULL_UPDATE_REQUEST = 0, FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST = 1;
|
*/
|
||||||
|
public static final int FRAMEBUFFER_FULL_UPDATE_REQUEST = 0, FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST = 1;
|
||||||
|
|
||||||
public static final int KEY_UP = 0, KEY_DOWN = 1;
|
public static final int KEY_UP = 0, KEY_DOWN = 1;
|
||||||
|
|
||||||
public static final int LITTLE_ENDIAN = 0, BIG_ENDIAN = 1;
|
public static final int LITTLE_ENDIAN = 0, BIG_ENDIAN = 1;
|
||||||
|
|
||||||
public static final int EXCLUSIVE_ACCESS = 0, SHARED_ACCESS = 1;
|
public static final int EXCLUSIVE_ACCESS = 0, SHARED_ACCESS = 1;
|
||||||
|
|
||||||
public static final int PALETTE = 0, TRUE_COLOR = 1;
|
public static final int PALETTE = 0, TRUE_COLOR = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default charset to use when communicating with server.
|
* Default charset to use when communicating with server.
|
||||||
*/
|
*/
|
||||||
public static final Charset CHARSET = Charset.availableCharsets().get("US-ASCII");
|
public static final Charset CHARSET = Charset.availableCharsets().get("US-ASCII");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,414 +39,413 @@ import com.cloud.consoleproxy.vnc.packet.client.KeyboardEventPacket;
|
|||||||
import com.cloud.consoleproxy.vnc.packet.client.MouseEventPacket;
|
import com.cloud.consoleproxy.vnc.packet.client.MouseEventPacket;
|
||||||
|
|
||||||
public class VncClient {
|
public class VncClient {
|
||||||
private static final Logger s_logger = Logger.getLogger(VncClient.class);
|
private static final Logger s_logger = Logger.getLogger(VncClient.class);
|
||||||
|
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
private DataInputStream is;
|
private DataInputStream is;
|
||||||
private DataOutputStream os;
|
private DataOutputStream os;
|
||||||
|
|
||||||
private VncScreenDescription screen = new VncScreenDescription();
|
private VncScreenDescription screen = new VncScreenDescription();
|
||||||
|
|
||||||
private VncClientPacketSender sender;
|
private VncClientPacketSender sender;
|
||||||
private VncServerPacketReceiver receiver;
|
private VncServerPacketReceiver receiver;
|
||||||
|
|
||||||
private boolean noUI = false;
|
private boolean noUI = false;
|
||||||
private ConsoleProxyClientListener clientListener = null;
|
private ConsoleProxyClientListener clientListener = null;
|
||||||
|
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
if (args.length < 3) {
|
if (args.length < 3) {
|
||||||
printHelpMessage();
|
printHelpMessage();
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
String host = args[0];
|
||||||
|
String port = args[1];
|
||||||
|
String password = args[2];
|
||||||
|
|
||||||
|
try {
|
||||||
|
new VncClient(host, Integer.parseInt(port), password, false, null);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
s_logger.error("Incorrect VNC server port number: " + port + ".");
|
||||||
|
System.exit(1);
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
s_logger.error("Incorrect VNC server host name: " + host + ".");
|
||||||
|
System.exit(1);
|
||||||
|
} catch (IOException e) {
|
||||||
|
s_logger.error("Cannot communicate with VNC server: " + e.getMessage());
|
||||||
|
System.exit(1);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
s_logger.error("An error happened: " + e.getMessage());
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
System.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
String host = args[0];
|
private static void printHelpMessage() {
|
||||||
String port = args[1];
|
/* LOG */s_logger.info("Usage: HOST PORT PASSWORD.");
|
||||||
String password = args[2];
|
|
||||||
|
|
||||||
try {
|
|
||||||
new VncClient(host, Integer.parseInt(port), password, false, null);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
s_logger.error("Incorrect VNC server port number: " + port + ".");
|
|
||||||
System.exit(1);
|
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
s_logger.error("Incorrect VNC server host name: " + host + ".");
|
|
||||||
System.exit(1);
|
|
||||||
} catch (IOException e) {
|
|
||||||
s_logger.error("Cannot communicate with VNC server: " + e.getMessage());
|
|
||||||
System.exit(1);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
s_logger.error("An error happened: " + e.getMessage());
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void printHelpMessage() {
|
|
||||||
/* LOG */s_logger.info("Usage: HOST PORT PASSWORD.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public VncClient(ConsoleProxyClientListener clientListener) {
|
|
||||||
this.noUI = true;
|
|
||||||
this.clientListener = clientListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VncClient(String host, int port, String password, boolean noUI, ConsoleProxyClientListener clientListener)
|
|
||||||
throws UnknownHostException, IOException {
|
|
||||||
|
|
||||||
this.noUI = noUI;
|
|
||||||
this.clientListener = clientListener;
|
|
||||||
connectTo(host, port, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void shutdown() {
|
|
||||||
if(sender != null)
|
|
||||||
sender.closeConnection();
|
|
||||||
|
|
||||||
if(receiver != null)
|
|
||||||
receiver.closeConnection();
|
|
||||||
|
|
||||||
if(is != null) {
|
|
||||||
try {
|
|
||||||
is.close();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(os != null) {
|
|
||||||
try {
|
|
||||||
os.close();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(socket != null) {
|
|
||||||
try {
|
|
||||||
socket.close();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConsoleProxyClientListener getClientListener() {
|
|
||||||
return clientListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connectTo(String host, int port, String path,
|
|
||||||
String session, boolean useSSL, String sid) throws UnknownHostException, IOException {
|
|
||||||
if(port < 0) {
|
|
||||||
if(useSSL)
|
|
||||||
port = 443;
|
|
||||||
else
|
|
||||||
port = 80;
|
|
||||||
}
|
|
||||||
|
|
||||||
RawHTTP tunnel = new RawHTTP("CONNECT", host, port, path, session, useSSL);
|
|
||||||
this.socket = tunnel.connect();
|
|
||||||
doConnect(sid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connectTo(String host, int port, String password) throws UnknownHostException, IOException {
|
|
||||||
// Connect to server
|
|
||||||
s_logger.info("Connecting to VNC server " + host + ":" + port + "...");
|
|
||||||
this.socket = new Socket(host, port);
|
|
||||||
doConnect(password);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doConnect(String password) throws IOException {
|
|
||||||
is = new DataInputStream(socket.getInputStream());
|
|
||||||
os = new DataOutputStream(socket.getOutputStream());
|
|
||||||
|
|
||||||
// Initialize connection
|
|
||||||
handshake();
|
|
||||||
authenticate(password);
|
|
||||||
initialize();
|
|
||||||
|
|
||||||
s_logger.info("Connecting to VNC server succeeded, start session");
|
|
||||||
|
|
||||||
// Run client-to-server packet sender
|
|
||||||
sender = new VncClientPacketSender(os, screen, this);
|
|
||||||
|
|
||||||
// Create buffered image canvas
|
|
||||||
BufferedImageCanvas canvas = new BufferedImageCanvas(sender, screen.getFramebufferWidth(), screen.getFramebufferHeight());
|
|
||||||
|
|
||||||
// Subscribe packet sender to various events
|
|
||||||
canvas.addMouseListener(sender);
|
|
||||||
canvas.addMouseMotionListener(sender);
|
|
||||||
canvas.addKeyListener(sender);
|
|
||||||
|
|
||||||
Frame frame = null;
|
|
||||||
if(!noUI)
|
|
||||||
frame = createVncClientMainWindow(canvas, screen.getDesktopName());
|
|
||||||
|
|
||||||
new Thread(sender).start();
|
|
||||||
|
|
||||||
// Run server-to-client packet receiver
|
|
||||||
receiver = new VncServerPacketReceiver(is, canvas, screen, this, sender, clientListener);
|
|
||||||
try {
|
|
||||||
receiver.run();
|
|
||||||
} finally {
|
|
||||||
if(frame != null) {
|
|
||||||
frame.setVisible(false);
|
|
||||||
frame.dispose();
|
|
||||||
}
|
|
||||||
this.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Frame createVncClientMainWindow(BufferedImageCanvas canvas, String title) {
|
|
||||||
// Create AWT windows
|
|
||||||
final Frame frame = new Frame(title + " - VNCle");
|
|
||||||
|
|
||||||
// Use scrolling pane to support screens, which are larger than ours
|
|
||||||
ScrollPane scroller = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
|
|
||||||
scroller.add(canvas);
|
|
||||||
scroller.setSize(screen.getFramebufferWidth(), screen.getFramebufferHeight());
|
|
||||||
|
|
||||||
frame.add(scroller);
|
|
||||||
frame.pack();
|
|
||||||
frame.setVisible(true);
|
|
||||||
|
|
||||||
frame.addWindowListener(new WindowAdapter() {
|
|
||||||
public void windowClosing(WindowEvent evt) {
|
|
||||||
frame.setVisible(false);
|
|
||||||
shutdown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handshake with VNC server.
|
|
||||||
*/
|
|
||||||
private void handshake() throws IOException {
|
|
||||||
|
|
||||||
// Read protocol version
|
|
||||||
byte[] buf = new byte[12];
|
|
||||||
is.readFully(buf);
|
|
||||||
String rfbProtocol = new String(buf);
|
|
||||||
|
|
||||||
// Server should use RFB protocol 3.x
|
|
||||||
if (!rfbProtocol.contains(RfbConstants.RFB_PROTOCOL_VERSION_MAJOR)) {
|
|
||||||
s_logger.error("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\".");
|
|
||||||
throw new RuntimeException("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\".");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send response: we support RFB 3.3 only
|
public VncClient(ConsoleProxyClientListener clientListener) {
|
||||||
String ourProtocolString = RfbConstants.RFB_PROTOCOL_VERSION + "\n";
|
this.noUI = true;
|
||||||
os.write(ourProtocolString.getBytes());
|
this.clientListener = clientListener;
|
||||||
os.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VNC authentication.
|
|
||||||
*/
|
|
||||||
private void authenticate(String password) throws IOException {
|
|
||||||
// Read security type
|
|
||||||
int authType = is.readInt();
|
|
||||||
|
|
||||||
switch (authType) {
|
|
||||||
case RfbConstants.CONNECTION_FAILED: {
|
|
||||||
// Server forbids to connect. Read reason and throw exception
|
|
||||||
|
|
||||||
int length = is.readInt();
|
|
||||||
byte[] buf = new byte[length];
|
|
||||||
is.readFully(buf);
|
|
||||||
String reason = new String(buf, RfbConstants.CHARSET);
|
|
||||||
|
|
||||||
s_logger.error("Authentication to VNC server is failed. Reason: " + reason);
|
|
||||||
throw new RuntimeException("Authentication to VNC server is failed. Reason: " + reason);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case RfbConstants.NO_AUTH: {
|
public VncClient(String host, int port, String password, boolean noUI, ConsoleProxyClientListener clientListener) throws UnknownHostException, IOException {
|
||||||
// Client can connect without authorization. Nothing to do.
|
|
||||||
break;
|
this.noUI = noUI;
|
||||||
|
this.clientListener = clientListener;
|
||||||
|
connectTo(host, port, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
case RfbConstants.VNC_AUTH: {
|
public void shutdown() {
|
||||||
s_logger.info("VNC server requires password authentication");
|
if (sender != null)
|
||||||
doVncAuth(password);
|
sender.closeConnection();
|
||||||
break;
|
|
||||||
|
if (receiver != null)
|
||||||
|
receiver.closeConnection();
|
||||||
|
|
||||||
|
if (is != null) {
|
||||||
|
try {
|
||||||
|
is.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (os != null) {
|
||||||
|
try {
|
||||||
|
os.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socket != null) {
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
public ConsoleProxyClientListener getClientListener() {
|
||||||
s_logger.error("Unsupported VNC protocol authorization scheme, scheme code: " + authType + ".");
|
return clientListener;
|
||||||
throw new RuntimeException("Unsupported VNC protocol authorization scheme, scheme code: " + authType + ".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode client password and send it to server.
|
|
||||||
*/
|
|
||||||
private void doVncAuth(String password) throws IOException {
|
|
||||||
|
|
||||||
// Read challenge
|
|
||||||
byte[] challenge = new byte[16];
|
|
||||||
is.readFully(challenge);
|
|
||||||
|
|
||||||
// Encode challenge with password
|
|
||||||
byte[] response;
|
|
||||||
try {
|
|
||||||
response = encodePassword(challenge, password);
|
|
||||||
} catch (Exception e) {
|
|
||||||
s_logger.error("Cannot encrypt client password to send to server: " + e.getMessage());
|
|
||||||
throw new RuntimeException("Cannot encrypt client password to send to server: " + e.getMessage());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send encoded challenge
|
public void connectTo(String host, int port, String path, String session, boolean useSSL, String sid) throws UnknownHostException, IOException {
|
||||||
os.write(response);
|
if (port < 0) {
|
||||||
os.flush();
|
if (useSSL)
|
||||||
|
port = 443;
|
||||||
|
else
|
||||||
|
port = 80;
|
||||||
|
}
|
||||||
|
|
||||||
// Read security result
|
RawHTTP tunnel = new RawHTTP("CONNECT", host, port, path, session, useSSL);
|
||||||
int authResult = is.readInt();
|
this.socket = tunnel.connect();
|
||||||
|
doConnect(sid);
|
||||||
switch (authResult) {
|
|
||||||
case RfbConstants.VNC_AUTH_OK: {
|
|
||||||
// Nothing to do
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case RfbConstants.VNC_AUTH_TOO_MANY:
|
public void connectTo(String host, int port, String password) throws UnknownHostException, IOException {
|
||||||
s_logger.error("Connection to VNC server failed: too many wrong attempts.");
|
// Connect to server
|
||||||
throw new RuntimeException("Connection to VNC server failed: too many wrong attempts.");
|
s_logger.info("Connecting to VNC server " + host + ":" + port + "...");
|
||||||
|
this.socket = new Socket(host, port);
|
||||||
case RfbConstants.VNC_AUTH_FAILED:
|
doConnect(password);
|
||||||
s_logger.error("Connection to VNC server failed: wrong password.");
|
|
||||||
throw new RuntimeException("Connection to VNC server failed: wrong password.");
|
|
||||||
|
|
||||||
default:
|
|
||||||
s_logger.error("Connection to VNC server failed, reason code: " + authResult);
|
|
||||||
throw new RuntimeException("Connection to VNC server failed, reason code: " + authResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode password using DES encryption with given challenge.
|
|
||||||
*
|
|
||||||
* @param challenge
|
|
||||||
* a random set of bytes.
|
|
||||||
* @param password
|
|
||||||
* a password
|
|
||||||
* @return DES hash of password and challenge
|
|
||||||
*/
|
|
||||||
public byte[] encodePassword(byte[] challenge, String password) throws Exception {
|
|
||||||
// VNC password consist of up to eight ASCII characters.
|
|
||||||
byte[] key = { 0, 0, 0, 0, 0, 0, 0, 0 }; // Padding
|
|
||||||
byte[] passwordAsciiBytes = password.getBytes(RfbConstants.CHARSET);
|
|
||||||
System.arraycopy(passwordAsciiBytes, 0, key, 0, Math.min(password.length(), 8));
|
|
||||||
|
|
||||||
// Flip bytes (reverse bits) in key
|
|
||||||
for (int i = 0; i < key.length; i++) {
|
|
||||||
key[i] = flipByte(key[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KeySpec desKeySpec = new DESKeySpec(key);
|
private void doConnect(String password) throws IOException {
|
||||||
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
|
is = new DataInputStream(socket.getInputStream());
|
||||||
SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);
|
os = new DataOutputStream(socket.getOutputStream());
|
||||||
Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
|
||||||
|
|
||||||
byte[] response = cipher.doFinal(challenge);
|
// Initialize connection
|
||||||
return response;
|
handshake();
|
||||||
}
|
authenticate(password);
|
||||||
|
initialize();
|
||||||
|
|
||||||
/**
|
s_logger.info("Connecting to VNC server succeeded, start session");
|
||||||
* Reverse bits in byte, so least significant bit will be most significant
|
|
||||||
* bit. E.g. 01001100 will become 00110010.
|
|
||||||
*
|
|
||||||
* See also: http://www.vidarholen.net/contents/junk/vnc.html ,
|
|
||||||
* http://bytecrafter .blogspot.com/2010/09/des-encryption-as-used-in-vnc.html
|
|
||||||
*
|
|
||||||
* @param b
|
|
||||||
* a byte
|
|
||||||
* @return byte in reverse order
|
|
||||||
*/
|
|
||||||
private static byte flipByte(byte b) {
|
|
||||||
int b1_8 = (b & 0x1) << 7;
|
|
||||||
int b2_7 = (b & 0x2) << 5;
|
|
||||||
int b3_6 = (b & 0x4) << 3;
|
|
||||||
int b4_5 = (b & 0x8) << 1;
|
|
||||||
int b5_4 = (b & 0x10) >>> 1;
|
|
||||||
int b6_3 = (b & 0x20) >>> 3;
|
|
||||||
int b7_2 = (b & 0x40) >>> 5;
|
|
||||||
int b8_1 = (b & 0x80) >>> 7;
|
|
||||||
byte c = (byte) (b1_8 | b2_7 | b3_6 | b4_5 | b5_4 | b6_3 | b7_2 | b8_1);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initialize() throws IOException {
|
// Run client-to-server packet sender
|
||||||
// Send client initialization message
|
sender = new VncClientPacketSender(os, screen, this);
|
||||||
{
|
|
||||||
// Send shared flag
|
// Create buffered image canvas
|
||||||
os.writeByte(RfbConstants.EXCLUSIVE_ACCESS);
|
BufferedImageCanvas canvas = new BufferedImageCanvas(sender, screen.getFramebufferWidth(), screen.getFramebufferHeight());
|
||||||
os.flush();
|
|
||||||
|
// Subscribe packet sender to various events
|
||||||
|
canvas.addMouseListener(sender);
|
||||||
|
canvas.addMouseMotionListener(sender);
|
||||||
|
canvas.addKeyListener(sender);
|
||||||
|
|
||||||
|
Frame frame = null;
|
||||||
|
if (!noUI)
|
||||||
|
frame = createVncClientMainWindow(canvas, screen.getDesktopName());
|
||||||
|
|
||||||
|
new Thread(sender).start();
|
||||||
|
|
||||||
|
// Run server-to-client packet receiver
|
||||||
|
receiver = new VncServerPacketReceiver(is, canvas, screen, this, sender, clientListener);
|
||||||
|
try {
|
||||||
|
receiver.run();
|
||||||
|
} finally {
|
||||||
|
if (frame != null) {
|
||||||
|
frame.setVisible(false);
|
||||||
|
frame.dispose();
|
||||||
|
}
|
||||||
|
this.shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read server initialization message
|
private Frame createVncClientMainWindow(BufferedImageCanvas canvas, String title) {
|
||||||
{
|
// Create AWT windows
|
||||||
// Read frame buffer size
|
final Frame frame = new Frame(title + " - VNCle");
|
||||||
int framebufferWidth = is.readUnsignedShort();
|
|
||||||
int framebufferHeight = is.readUnsignedShort();
|
// Use scrolling pane to support screens, which are larger than ours
|
||||||
screen.setFramebufferSize(framebufferWidth, framebufferHeight);
|
ScrollPane scroller = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
|
||||||
if(clientListener != null)
|
scroller.add(canvas);
|
||||||
clientListener.onFramebufferSizeChange(framebufferWidth, framebufferHeight);
|
scroller.setSize(screen.getFramebufferWidth(), screen.getFramebufferHeight());
|
||||||
|
|
||||||
|
frame.add(scroller);
|
||||||
|
frame.pack();
|
||||||
|
frame.setVisible(true);
|
||||||
|
|
||||||
|
frame.addWindowListener(new WindowAdapter() {
|
||||||
|
public void windowClosing(WindowEvent evt) {
|
||||||
|
frame.setVisible(false);
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read pixel format
|
/**
|
||||||
{
|
* Handshake with VNC server.
|
||||||
int bitsPerPixel = is.readUnsignedByte();
|
*/
|
||||||
int depth = is.readUnsignedByte();
|
private void handshake() throws IOException {
|
||||||
|
|
||||||
int bigEndianFlag = is.readUnsignedByte();
|
// Read protocol version
|
||||||
int trueColorFlag = is.readUnsignedByte();
|
byte[] buf = new byte[12];
|
||||||
|
is.readFully(buf);
|
||||||
|
String rfbProtocol = new String(buf);
|
||||||
|
|
||||||
int redMax = is.readUnsignedShort();
|
// Server should use RFB protocol 3.x
|
||||||
int greenMax = is.readUnsignedShort();
|
if (!rfbProtocol.contains(RfbConstants.RFB_PROTOCOL_VERSION_MAJOR)) {
|
||||||
int blueMax = is.readUnsignedShort();
|
s_logger.error("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\".");
|
||||||
|
throw new RuntimeException("Cannot handshake with VNC server. Unsupported protocol version: \"" + rfbProtocol + "\".");
|
||||||
|
}
|
||||||
|
|
||||||
int redShift = is.readUnsignedByte();
|
// Send response: we support RFB 3.3 only
|
||||||
int greenShift = is.readUnsignedByte();
|
String ourProtocolString = RfbConstants.RFB_PROTOCOL_VERSION + "\n";
|
||||||
int blueShift = is.readUnsignedByte();
|
os.write(ourProtocolString.getBytes());
|
||||||
|
os.flush();
|
||||||
// Skip padding
|
|
||||||
is.skipBytes(3);
|
|
||||||
|
|
||||||
screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag, trueColorFlag, redMax, greenMax, blueMax, redShift, greenShift, blueShift);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read desktop name
|
/**
|
||||||
{
|
* VNC authentication.
|
||||||
int length = is.readInt();
|
*/
|
||||||
byte buf[] = new byte[length];
|
private void authenticate(String password) throws IOException {
|
||||||
is.readFully(buf);
|
// Read security type
|
||||||
String desktopName = new String(buf, RfbConstants.CHARSET);
|
int authType = is.readInt();
|
||||||
screen.setDesktopName(desktopName);
|
|
||||||
|
switch (authType) {
|
||||||
|
case RfbConstants.CONNECTION_FAILED: {
|
||||||
|
// Server forbids to connect. Read reason and throw exception
|
||||||
|
|
||||||
|
int length = is.readInt();
|
||||||
|
byte[] buf = new byte[length];
|
||||||
|
is.readFully(buf);
|
||||||
|
String reason = new String(buf, RfbConstants.CHARSET);
|
||||||
|
|
||||||
|
s_logger.error("Authentication to VNC server is failed. Reason: " + reason);
|
||||||
|
throw new RuntimeException("Authentication to VNC server is failed. Reason: " + reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
case RfbConstants.NO_AUTH: {
|
||||||
|
// Client can connect without authorization. Nothing to do.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case RfbConstants.VNC_AUTH: {
|
||||||
|
s_logger.info("VNC server requires password authentication");
|
||||||
|
doVncAuth(password);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
s_logger.error("Unsupported VNC protocol authorization scheme, scheme code: " + authType + ".");
|
||||||
|
throw new RuntimeException("Unsupported VNC protocol authorization scheme, scheme code: " + authType + ".");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public FrameBufferCanvas getFrameBufferCanvas() {
|
/**
|
||||||
if(receiver != null)
|
* Encode client password and send it to server.
|
||||||
return receiver.getCanvas();
|
*/
|
||||||
|
private void doVncAuth(String password) throws IOException {
|
||||||
|
|
||||||
return null;
|
// Read challenge
|
||||||
}
|
byte[] challenge = new byte[16];
|
||||||
|
is.readFully(challenge);
|
||||||
|
|
||||||
public void requestUpdate(boolean fullUpdate) {
|
// Encode challenge with password
|
||||||
if(fullUpdate)
|
byte[] response;
|
||||||
sender.requestFullScreenUpdate();
|
try {
|
||||||
else
|
response = encodePassword(challenge, password);
|
||||||
sender.imagePaintedOnScreen();
|
} catch (Exception e) {
|
||||||
}
|
s_logger.error("Cannot encrypt client password to send to server: " + e.getMessage());
|
||||||
|
throw new RuntimeException("Cannot encrypt client password to send to server: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
public void sendClientKeyboardEvent(int event, int code, int modifiers) {
|
// Send encoded challenge
|
||||||
sender.sendClientPacket(new KeyboardEventPacket(event, code));
|
os.write(response);
|
||||||
}
|
os.flush();
|
||||||
|
|
||||||
public void sendClientMouseEvent(int event, int x, int y, int code, int modifiers) {
|
// Read security result
|
||||||
sender.sendClientPacket(new MouseEventPacket(event, x, y));
|
int authResult = is.readInt();
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isHostConnected() {
|
switch (authResult) {
|
||||||
return receiver != null && receiver.isConnectionAlive();
|
case RfbConstants.VNC_AUTH_OK: {
|
||||||
}
|
// Nothing to do
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case RfbConstants.VNC_AUTH_TOO_MANY:
|
||||||
|
s_logger.error("Connection to VNC server failed: too many wrong attempts.");
|
||||||
|
throw new RuntimeException("Connection to VNC server failed: too many wrong attempts.");
|
||||||
|
|
||||||
|
case RfbConstants.VNC_AUTH_FAILED:
|
||||||
|
s_logger.error("Connection to VNC server failed: wrong password.");
|
||||||
|
throw new RuntimeException("Connection to VNC server failed: wrong password.");
|
||||||
|
|
||||||
|
default:
|
||||||
|
s_logger.error("Connection to VNC server failed, reason code: " + authResult);
|
||||||
|
throw new RuntimeException("Connection to VNC server failed, reason code: " + authResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode password using DES encryption with given challenge.
|
||||||
|
*
|
||||||
|
* @param challenge
|
||||||
|
* a random set of bytes.
|
||||||
|
* @param password
|
||||||
|
* a password
|
||||||
|
* @return DES hash of password and challenge
|
||||||
|
*/
|
||||||
|
public byte[] encodePassword(byte[] challenge, String password) throws Exception {
|
||||||
|
// VNC password consist of up to eight ASCII characters.
|
||||||
|
byte[] key = { 0, 0, 0, 0, 0, 0, 0, 0 }; // Padding
|
||||||
|
byte[] passwordAsciiBytes = password.getBytes(RfbConstants.CHARSET);
|
||||||
|
System.arraycopy(passwordAsciiBytes, 0, key, 0, Math.min(password.length(), 8));
|
||||||
|
|
||||||
|
// Flip bytes (reverse bits) in key
|
||||||
|
for (int i = 0; i < key.length; i++) {
|
||||||
|
key[i] = flipByte(key[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
KeySpec desKeySpec = new DESKeySpec(key);
|
||||||
|
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DES");
|
||||||
|
SecretKey secretKey = secretKeyFactory.generateSecret(desKeySpec);
|
||||||
|
Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||||
|
|
||||||
|
byte[] response = cipher.doFinal(challenge);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse bits in byte, so least significant bit will be most significant
|
||||||
|
* bit. E.g. 01001100 will become 00110010.
|
||||||
|
*
|
||||||
|
* See also: http://www.vidarholen.net/contents/junk/vnc.html ,
|
||||||
|
* http://bytecrafter
|
||||||
|
* .blogspot.com/2010/09/des-encryption-as-used-in-vnc.html
|
||||||
|
*
|
||||||
|
* @param b
|
||||||
|
* a byte
|
||||||
|
* @return byte in reverse order
|
||||||
|
*/
|
||||||
|
private static byte flipByte(byte b) {
|
||||||
|
int b1_8 = (b & 0x1) << 7;
|
||||||
|
int b2_7 = (b & 0x2) << 5;
|
||||||
|
int b3_6 = (b & 0x4) << 3;
|
||||||
|
int b4_5 = (b & 0x8) << 1;
|
||||||
|
int b5_4 = (b & 0x10) >>> 1;
|
||||||
|
int b6_3 = (b & 0x20) >>> 3;
|
||||||
|
int b7_2 = (b & 0x40) >>> 5;
|
||||||
|
int b8_1 = (b & 0x80) >>> 7;
|
||||||
|
byte c = (byte) (b1_8 | b2_7 | b3_6 | b4_5 | b5_4 | b6_3 | b7_2 | b8_1);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initialize() throws IOException {
|
||||||
|
// Send client initialization message
|
||||||
|
{
|
||||||
|
// Send shared flag
|
||||||
|
os.writeByte(RfbConstants.EXCLUSIVE_ACCESS);
|
||||||
|
os.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read server initialization message
|
||||||
|
{
|
||||||
|
// Read frame buffer size
|
||||||
|
int framebufferWidth = is.readUnsignedShort();
|
||||||
|
int framebufferHeight = is.readUnsignedShort();
|
||||||
|
screen.setFramebufferSize(framebufferWidth, framebufferHeight);
|
||||||
|
if (clientListener != null)
|
||||||
|
clientListener.onFramebufferSizeChange(framebufferWidth, framebufferHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read pixel format
|
||||||
|
{
|
||||||
|
int bitsPerPixel = is.readUnsignedByte();
|
||||||
|
int depth = is.readUnsignedByte();
|
||||||
|
|
||||||
|
int bigEndianFlag = is.readUnsignedByte();
|
||||||
|
int trueColorFlag = is.readUnsignedByte();
|
||||||
|
|
||||||
|
int redMax = is.readUnsignedShort();
|
||||||
|
int greenMax = is.readUnsignedShort();
|
||||||
|
int blueMax = is.readUnsignedShort();
|
||||||
|
|
||||||
|
int redShift = is.readUnsignedByte();
|
||||||
|
int greenShift = is.readUnsignedByte();
|
||||||
|
int blueShift = is.readUnsignedByte();
|
||||||
|
|
||||||
|
// Skip padding
|
||||||
|
is.skipBytes(3);
|
||||||
|
|
||||||
|
screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag, trueColorFlag, redMax, greenMax, blueMax, redShift, greenShift, blueShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read desktop name
|
||||||
|
{
|
||||||
|
int length = is.readInt();
|
||||||
|
byte buf[] = new byte[length];
|
||||||
|
is.readFully(buf);
|
||||||
|
String desktopName = new String(buf, RfbConstants.CHARSET);
|
||||||
|
screen.setDesktopName(desktopName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrameBufferCanvas getFrameBufferCanvas() {
|
||||||
|
if (receiver != null)
|
||||||
|
return receiver.getCanvas();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestUpdate(boolean fullUpdate) {
|
||||||
|
if (fullUpdate)
|
||||||
|
sender.requestFullScreenUpdate();
|
||||||
|
else
|
||||||
|
sender.imagePaintedOnScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendClientKeyboardEvent(int event, int code, int modifiers) {
|
||||||
|
sender.sendClientPacket(new KeyboardEventPacket(event, code));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendClientMouseEvent(int event, int x, int y, int code, int modifiers) {
|
||||||
|
sender.sendClientPacket(new MouseEventPacket(event, x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHostConnected() {
|
||||||
|
return receiver != null && receiver.isConnectionAlive();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,226 +35,224 @@ import com.cloud.consoleproxy.vnc.packet.client.SetEncodingsPacket;
|
|||||||
import com.cloud.consoleproxy.vnc.packet.client.SetPixelFormatPacket;
|
import com.cloud.consoleproxy.vnc.packet.client.SetPixelFormatPacket;
|
||||||
|
|
||||||
public class VncClientPacketSender implements Runnable, PaintNotificationListener, KeyListener, MouseListener, MouseMotionListener, FrameBufferUpdateListener {
|
public class VncClientPacketSender implements Runnable, PaintNotificationListener, KeyListener, MouseListener, MouseMotionListener, FrameBufferUpdateListener {
|
||||||
private static final Logger s_logger = Logger.getLogger(VncClientPacketSender.class);
|
private static final Logger s_logger = Logger.getLogger(VncClientPacketSender.class);
|
||||||
|
|
||||||
// Queue for outgoing packets
|
// Queue for outgoing packets
|
||||||
private final BlockingQueue<ClientPacket> queue = new ArrayBlockingQueue<ClientPacket>(30);
|
private final BlockingQueue<ClientPacket> queue = new ArrayBlockingQueue<ClientPacket>(30);
|
||||||
|
|
||||||
private final DataOutputStream os;
|
private final DataOutputStream os;
|
||||||
private final VncScreenDescription screen;
|
private final VncScreenDescription screen;
|
||||||
private final VncClient vncConnection;
|
private final VncClient vncConnection;
|
||||||
|
|
||||||
private boolean connectionAlive = true;
|
private boolean connectionAlive = true;
|
||||||
|
|
||||||
// Don't send update request again until we receive next frame buffer update
|
// Don't send update request again until we receive next frame buffer update
|
||||||
private boolean updateRequestSent = false;
|
private boolean updateRequestSent = false;
|
||||||
|
|
||||||
public VncClientPacketSender(DataOutputStream os, VncScreenDescription screen, VncClient vncConnection) {
|
public VncClientPacketSender(DataOutputStream os, VncScreenDescription screen, VncClient vncConnection) {
|
||||||
this.os = os;
|
this.os = os;
|
||||||
this.screen = screen;
|
this.screen = screen;
|
||||||
this.vncConnection = vncConnection;
|
this.vncConnection = vncConnection;
|
||||||
|
|
||||||
sendSetPixelFormat();
|
sendSetPixelFormat();
|
||||||
sendSetEncodings();
|
sendSetEncodings();
|
||||||
requestFullScreenUpdate();
|
requestFullScreenUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendClientPacket(ClientPacket packet) {
|
public void sendClientPacket(ClientPacket packet) {
|
||||||
queue.add(packet);
|
queue.add(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
while (connectionAlive) {
|
while (connectionAlive) {
|
||||||
ClientPacket packet = queue.poll(1, TimeUnit.SECONDS);
|
ClientPacket packet = queue.poll(1, TimeUnit.SECONDS);
|
||||||
if (packet != null) {
|
if (packet != null) {
|
||||||
packet.write(os);
|
packet.write(os);
|
||||||
os.flush();
|
os.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
s_logger.error("Unexpected exception: ", e);
|
||||||
|
if (connectionAlive) {
|
||||||
|
closeConnection();
|
||||||
|
vncConnection.shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
s_logger.error("Unexpected exception: ", e);
|
|
||||||
if (connectionAlive) {
|
|
||||||
closeConnection();
|
|
||||||
vncConnection.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendSetEncodings() {
|
|
||||||
queue.add(new SetEncodingsPacket(RfbConstants.SUPPORTED_ENCODINGS_ARRAY));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendSetPixelFormat() {
|
|
||||||
if (!screen.isRGB888_32_LE()) {
|
|
||||||
queue.add(new SetPixelFormatPacket(screen, 32, 24, RfbConstants.LITTLE_ENDIAN, RfbConstants.TRUE_COLOR, 255, 255, 255, 16, 8, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void closeConnection() {
|
|
||||||
connectionAlive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void requestFullScreenUpdate() {
|
|
||||||
queue.add(new FramebufferUpdateRequestPacket(RfbConstants.FRAMEBUFFER_FULL_UPDATE_REQUEST, 0, 0, screen.getFramebufferWidth(), screen
|
|
||||||
.getFramebufferHeight()));
|
|
||||||
updateRequestSent = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void imagePaintedOnScreen() {
|
|
||||||
if (!updateRequestSent) {
|
|
||||||
queue.add(new FramebufferUpdateRequestPacket(RfbConstants.FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST, 0, 0, screen.getFramebufferWidth(), screen
|
|
||||||
.getFramebufferHeight()));
|
|
||||||
updateRequestSent = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void frameBufferPacketReceived() {
|
|
||||||
updateRequestSent = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseDragged(MouseEvent e) {
|
|
||||||
queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseMoved(MouseEvent e) {
|
|
||||||
queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e) {
|
|
||||||
// Nothing to do
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mousePressed(MouseEvent e) {
|
|
||||||
queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseReleased(MouseEvent e) {
|
|
||||||
queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseEntered(MouseEvent e) {
|
|
||||||
// Nothing to do
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseExited(MouseEvent e) {
|
|
||||||
// Nothing to do
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current state of buttons 1 to 8 are represented by bits 0 to 7 of
|
|
||||||
* button-mask respectively, 0 meaning up, 1 meaning down (pressed). On a
|
|
||||||
* conventional mouse, buttons 1, 2 and 3 correspond to the left, middle and
|
|
||||||
* right buttons on the mouse. On a wheel mouse, each step of the wheel
|
|
||||||
* upwards is represented by a press and release of button 4, and each step
|
|
||||||
* downwards is represented by a press and release of button 5.
|
|
||||||
*
|
|
||||||
* @param modifiers
|
|
||||||
* extended modifiers from AWT mouse event
|
|
||||||
* @return VNC mouse button mask
|
|
||||||
*/
|
|
||||||
public static int mapAwtModifiersToVncButtonMask(int modifiers) {
|
|
||||||
int mask = (((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) ? 0x1 : 0) | (((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) ? 0x2 : 0)
|
|
||||||
| (((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) ? 0x4 : 0);
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void keyTyped(KeyEvent e) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void keyPressed(KeyEvent e) {
|
|
||||||
ClientPacket request = new KeyboardEventPacket(RfbConstants.KEY_DOWN, mapAwtKeyToVncKey(e.getKeyCode()));
|
|
||||||
queue.add(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void keyReleased(KeyEvent e) {
|
|
||||||
ClientPacket request = new KeyboardEventPacket(RfbConstants.KEY_UP, mapAwtKeyToVncKey(e.getKeyCode()));
|
|
||||||
queue.add(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int mapAwtKeyToVncKey(int key) {
|
|
||||||
switch (key) {
|
|
||||||
case KeyEvent.VK_BACK_SPACE:
|
|
||||||
return 0xff08;
|
|
||||||
case KeyEvent.VK_TAB:
|
|
||||||
return 0xff09;
|
|
||||||
case KeyEvent.VK_ENTER:
|
|
||||||
return 0xff0d;
|
|
||||||
case KeyEvent.VK_ESCAPE:
|
|
||||||
return 0xff1b;
|
|
||||||
case KeyEvent.VK_INSERT:
|
|
||||||
return 0xff63;
|
|
||||||
case KeyEvent.VK_DELETE:
|
|
||||||
return 0xffff;
|
|
||||||
case KeyEvent.VK_HOME:
|
|
||||||
return 0xff50;
|
|
||||||
case KeyEvent.VK_END:
|
|
||||||
return 0xff57;
|
|
||||||
case KeyEvent.VK_PAGE_UP:
|
|
||||||
return 0xff55;
|
|
||||||
case KeyEvent.VK_PAGE_DOWN:
|
|
||||||
return 0xff56;
|
|
||||||
case KeyEvent.VK_LEFT:
|
|
||||||
return 0xff51;
|
|
||||||
case KeyEvent.VK_UP:
|
|
||||||
return 0xff52;
|
|
||||||
case KeyEvent.VK_RIGHT:
|
|
||||||
return 0xff53;
|
|
||||||
case KeyEvent.VK_DOWN:
|
|
||||||
return 0xff54;
|
|
||||||
case KeyEvent.VK_F1:
|
|
||||||
return 0xffbe;
|
|
||||||
case KeyEvent.VK_F2:
|
|
||||||
return 0xffbf;
|
|
||||||
case KeyEvent.VK_F3:
|
|
||||||
return 0xffc0;
|
|
||||||
case KeyEvent.VK_F4:
|
|
||||||
return 0xffc1;
|
|
||||||
case KeyEvent.VK_F5:
|
|
||||||
return 0xffc2;
|
|
||||||
case KeyEvent.VK_F6:
|
|
||||||
return 0xffc3;
|
|
||||||
case KeyEvent.VK_F7:
|
|
||||||
return 0xffc4;
|
|
||||||
case KeyEvent.VK_F8:
|
|
||||||
return 0xffc5;
|
|
||||||
case KeyEvent.VK_F9:
|
|
||||||
return 0xffc6;
|
|
||||||
case KeyEvent.VK_F10:
|
|
||||||
return 0xffc7;
|
|
||||||
case KeyEvent.VK_F11:
|
|
||||||
return 0xffc8;
|
|
||||||
case KeyEvent.VK_F12:
|
|
||||||
return 0xffc9;
|
|
||||||
case KeyEvent.VK_SHIFT:
|
|
||||||
return 0xffe1;
|
|
||||||
case KeyEvent.VK_CONTROL:
|
|
||||||
return 0xffe3;
|
|
||||||
case KeyEvent.VK_META:
|
|
||||||
return 0xffe7;
|
|
||||||
case KeyEvent.VK_ALT:
|
|
||||||
return 0xffe9;
|
|
||||||
case KeyEvent.VK_ALT_GRAPH:
|
|
||||||
return 0xffea;
|
|
||||||
case KeyEvent.VK_BACK_QUOTE:
|
|
||||||
return 0x0060;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return key;
|
private void sendSetEncodings() {
|
||||||
}
|
queue.add(new SetEncodingsPacket(RfbConstants.SUPPORTED_ENCODINGS_ARRAY));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendSetPixelFormat() {
|
||||||
|
if (!screen.isRGB888_32_LE()) {
|
||||||
|
queue.add(new SetPixelFormatPacket(screen, 32, 24, RfbConstants.LITTLE_ENDIAN, RfbConstants.TRUE_COLOR, 255, 255, 255, 16, 8, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeConnection() {
|
||||||
|
connectionAlive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestFullScreenUpdate() {
|
||||||
|
queue.add(new FramebufferUpdateRequestPacket(RfbConstants.FRAMEBUFFER_FULL_UPDATE_REQUEST, 0, 0, screen.getFramebufferWidth(), screen.getFramebufferHeight()));
|
||||||
|
updateRequestSent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void imagePaintedOnScreen() {
|
||||||
|
if (!updateRequestSent) {
|
||||||
|
queue.add(new FramebufferUpdateRequestPacket(RfbConstants.FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST, 0, 0, screen.getFramebufferWidth(), screen.getFramebufferHeight()));
|
||||||
|
updateRequestSent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void frameBufferPacketReceived() {
|
||||||
|
updateRequestSent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseDragged(MouseEvent e) {
|
||||||
|
queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseMoved(MouseEvent e) {
|
||||||
|
queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseEntered(MouseEvent e) {
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited(MouseEvent e) {
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current state of buttons 1 to 8 are represented by bits 0 to 7 of
|
||||||
|
* button-mask respectively, 0 meaning up, 1 meaning down (pressed). On a
|
||||||
|
* conventional mouse, buttons 1, 2 and 3 correspond to the left, middle and
|
||||||
|
* right buttons on the mouse. On a wheel mouse, each step of the wheel
|
||||||
|
* upwards is represented by a press and release of button 4, and each step
|
||||||
|
* downwards is represented by a press and release of button 5.
|
||||||
|
*
|
||||||
|
* @param modifiers
|
||||||
|
* extended modifiers from AWT mouse event
|
||||||
|
* @return VNC mouse button mask
|
||||||
|
*/
|
||||||
|
public static int mapAwtModifiersToVncButtonMask(int modifiers) {
|
||||||
|
int mask = (((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) ? 0x1 : 0) | (((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) ? 0x2 : 0)
|
||||||
|
| (((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) ? 0x4 : 0);
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyTyped(KeyEvent e) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyPressed(KeyEvent e) {
|
||||||
|
ClientPacket request = new KeyboardEventPacket(RfbConstants.KEY_DOWN, mapAwtKeyToVncKey(e.getKeyCode()));
|
||||||
|
queue.add(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keyReleased(KeyEvent e) {
|
||||||
|
ClientPacket request = new KeyboardEventPacket(RfbConstants.KEY_UP, mapAwtKeyToVncKey(e.getKeyCode()));
|
||||||
|
queue.add(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int mapAwtKeyToVncKey(int key) {
|
||||||
|
switch (key) {
|
||||||
|
case KeyEvent.VK_BACK_SPACE:
|
||||||
|
return 0xff08;
|
||||||
|
case KeyEvent.VK_TAB:
|
||||||
|
return 0xff09;
|
||||||
|
case KeyEvent.VK_ENTER:
|
||||||
|
return 0xff0d;
|
||||||
|
case KeyEvent.VK_ESCAPE:
|
||||||
|
return 0xff1b;
|
||||||
|
case KeyEvent.VK_INSERT:
|
||||||
|
return 0xff63;
|
||||||
|
case KeyEvent.VK_DELETE:
|
||||||
|
return 0xffff;
|
||||||
|
case KeyEvent.VK_HOME:
|
||||||
|
return 0xff50;
|
||||||
|
case KeyEvent.VK_END:
|
||||||
|
return 0xff57;
|
||||||
|
case KeyEvent.VK_PAGE_UP:
|
||||||
|
return 0xff55;
|
||||||
|
case KeyEvent.VK_PAGE_DOWN:
|
||||||
|
return 0xff56;
|
||||||
|
case KeyEvent.VK_LEFT:
|
||||||
|
return 0xff51;
|
||||||
|
case KeyEvent.VK_UP:
|
||||||
|
return 0xff52;
|
||||||
|
case KeyEvent.VK_RIGHT:
|
||||||
|
return 0xff53;
|
||||||
|
case KeyEvent.VK_DOWN:
|
||||||
|
return 0xff54;
|
||||||
|
case KeyEvent.VK_F1:
|
||||||
|
return 0xffbe;
|
||||||
|
case KeyEvent.VK_F2:
|
||||||
|
return 0xffbf;
|
||||||
|
case KeyEvent.VK_F3:
|
||||||
|
return 0xffc0;
|
||||||
|
case KeyEvent.VK_F4:
|
||||||
|
return 0xffc1;
|
||||||
|
case KeyEvent.VK_F5:
|
||||||
|
return 0xffc2;
|
||||||
|
case KeyEvent.VK_F6:
|
||||||
|
return 0xffc3;
|
||||||
|
case KeyEvent.VK_F7:
|
||||||
|
return 0xffc4;
|
||||||
|
case KeyEvent.VK_F8:
|
||||||
|
return 0xffc5;
|
||||||
|
case KeyEvent.VK_F9:
|
||||||
|
return 0xffc6;
|
||||||
|
case KeyEvent.VK_F10:
|
||||||
|
return 0xffc7;
|
||||||
|
case KeyEvent.VK_F11:
|
||||||
|
return 0xffc8;
|
||||||
|
case KeyEvent.VK_F12:
|
||||||
|
return 0xffc9;
|
||||||
|
case KeyEvent.VK_SHIFT:
|
||||||
|
return 0xffe1;
|
||||||
|
case KeyEvent.VK_CONTROL:
|
||||||
|
return 0xffe3;
|
||||||
|
case KeyEvent.VK_META:
|
||||||
|
return 0xffe7;
|
||||||
|
case KeyEvent.VK_ALT:
|
||||||
|
return 0xffe9;
|
||||||
|
case KeyEvent.VK_ALT_GRAPH:
|
||||||
|
return 0xffea;
|
||||||
|
case KeyEvent.VK_BACK_QUOTE:
|
||||||
|
return 0x0060;
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,70 +21,69 @@ package com.cloud.consoleproxy.vnc;
|
|||||||
*/
|
*/
|
||||||
public class VncScreenDescription {
|
public class VncScreenDescription {
|
||||||
|
|
||||||
// Frame buffer size
|
// Frame buffer size
|
||||||
private int framebufferWidth = -1;
|
private int framebufferWidth = -1;
|
||||||
private int framebufferHeight = -1;
|
private int framebufferHeight = -1;
|
||||||
|
|
||||||
// Desktop name
|
// Desktop name
|
||||||
private String desktopName;
|
private String desktopName;
|
||||||
|
|
||||||
// Bytes per pixel
|
// Bytes per pixel
|
||||||
private int bytesPerPixel;
|
private int bytesPerPixel;
|
||||||
|
|
||||||
// Indicates that screen uses format which we want to use:
|
// Indicates that screen uses format which we want to use:
|
||||||
// RGB 24bit packed into 32bit little-endian int.
|
// RGB 24bit packed into 32bit little-endian int.
|
||||||
private boolean rgb888_32_le = false;
|
private boolean rgb888_32_le = false;
|
||||||
|
|
||||||
public VncScreenDescription() {
|
public VncScreenDescription() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store information about server pixel format.
|
* Store information about server pixel format.
|
||||||
*/
|
*/
|
||||||
public void setPixelFormat(int bitsPerPixel, int depth, int bigEndianFlag, int trueColorFlag, int redMax, int greenMax, int blueMax, int redShift,
|
public void setPixelFormat(int bitsPerPixel, int depth, int bigEndianFlag, int trueColorFlag, int redMax, int greenMax, int blueMax, int redShift, int greenShift, int blueShift) {
|
||||||
int greenShift, int blueShift) {
|
|
||||||
|
|
||||||
bytesPerPixel = (bitsPerPixel + 7) / 8;
|
bytesPerPixel = (bitsPerPixel + 7) / 8;
|
||||||
|
|
||||||
rgb888_32_le = (depth == 24 && bitsPerPixel == 32 && redShift == 16 && greenShift == 8 && blueShift == 0 && redMax == 255 && greenMax == 255
|
rgb888_32_le = (depth == 24 && bitsPerPixel == 32 && redShift == 16 && greenShift == 8 && blueShift == 0 && redMax == 255 && greenMax == 255 && blueMax == 255
|
||||||
&& blueMax == 255 && bigEndianFlag == RfbConstants.LITTLE_ENDIAN && trueColorFlag == RfbConstants.TRUE_COLOR);
|
&& bigEndianFlag == RfbConstants.LITTLE_ENDIAN && trueColorFlag == RfbConstants.TRUE_COLOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store information about server screen size.
|
* Store information about server screen size.
|
||||||
*/
|
*/
|
||||||
public void setFramebufferSize(int framebufferWidth, int framebufferHeight) {
|
public void setFramebufferSize(int framebufferWidth, int framebufferHeight) {
|
||||||
this.framebufferWidth = framebufferWidth;
|
this.framebufferWidth = framebufferWidth;
|
||||||
this.framebufferHeight = framebufferHeight;
|
this.framebufferHeight = framebufferHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store server desktop name.
|
* Store server desktop name.
|
||||||
*/
|
*/
|
||||||
public void setDesktopName(String desktopName) {
|
public void setDesktopName(String desktopName) {
|
||||||
this.desktopName = desktopName;
|
this.desktopName = desktopName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters for variables, as usual
|
// Getters for variables, as usual
|
||||||
|
|
||||||
public String getDesktopName() {
|
public String getDesktopName() {
|
||||||
return desktopName;
|
return desktopName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getBytesPerPixel() {
|
public int getBytesPerPixel() {
|
||||||
return bytesPerPixel;
|
return bytesPerPixel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFramebufferHeight() {
|
public int getFramebufferHeight() {
|
||||||
return framebufferHeight;
|
return framebufferHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFramebufferWidth() {
|
public int getFramebufferWidth() {
|
||||||
return framebufferWidth;
|
return framebufferWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRGB888_32_LE() {
|
public boolean isRGB888_32_LE() {
|
||||||
return rgb888_32_le;
|
return rgb888_32_le;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,97 +27,97 @@ import com.cloud.consoleproxy.vnc.packet.server.FramebufferUpdatePacket;
|
|||||||
import com.cloud.consoleproxy.vnc.packet.server.ServerCutText;
|
import com.cloud.consoleproxy.vnc.packet.server.ServerCutText;
|
||||||
|
|
||||||
public class VncServerPacketReceiver implements Runnable {
|
public class VncServerPacketReceiver implements Runnable {
|
||||||
private static final Logger s_logger = Logger.getLogger(VncServerPacketReceiver.class);
|
private static final Logger s_logger = Logger.getLogger(VncServerPacketReceiver.class);
|
||||||
|
|
||||||
private final VncScreenDescription screen;
|
private final VncScreenDescription screen;
|
||||||
private BufferedImageCanvas canvas;
|
private BufferedImageCanvas canvas;
|
||||||
private DataInputStream is;
|
private DataInputStream is;
|
||||||
|
|
||||||
private boolean connectionAlive = true;
|
private boolean connectionAlive = true;
|
||||||
private VncClient vncConnection;
|
private VncClient vncConnection;
|
||||||
private final FrameBufferUpdateListener fburListener;
|
private final FrameBufferUpdateListener fburListener;
|
||||||
private final ConsoleProxyClientListener clientListener;
|
private final ConsoleProxyClientListener clientListener;
|
||||||
|
|
||||||
public VncServerPacketReceiver(DataInputStream is, BufferedImageCanvas canvas, VncScreenDescription screen, VncClient vncConnection,
|
public VncServerPacketReceiver(DataInputStream is, BufferedImageCanvas canvas, VncScreenDescription screen, VncClient vncConnection, FrameBufferUpdateListener fburListener,
|
||||||
FrameBufferUpdateListener fburListener, ConsoleProxyClientListener clientListener) {
|
ConsoleProxyClientListener clientListener) {
|
||||||
this.screen = screen;
|
this.screen = screen;
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
this.is = is;
|
this.is = is;
|
||||||
this.vncConnection = vncConnection;
|
this.vncConnection = vncConnection;
|
||||||
this.fburListener = fburListener;
|
this.fburListener = fburListener;
|
||||||
this.clientListener = clientListener;
|
this.clientListener = clientListener;
|
||||||
}
|
|
||||||
|
|
||||||
public BufferedImageCanvas getCanvas() {
|
|
||||||
return canvas;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
while (connectionAlive) {
|
|
||||||
|
|
||||||
// Read server message type
|
|
||||||
int messageType = is.readUnsignedByte();
|
|
||||||
|
|
||||||
// Invoke packet handler by packet type.
|
|
||||||
switch (messageType) {
|
|
||||||
|
|
||||||
case RfbConstants.SERVER_FRAMEBUFFER_UPDATE: {
|
|
||||||
// Notify sender that frame buffer update is received,
|
|
||||||
// so it can send another frame buffer update request
|
|
||||||
fburListener.frameBufferPacketReceived();
|
|
||||||
// Handle frame buffer update
|
|
||||||
new FramebufferUpdatePacket(canvas, screen, is, clientListener);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case RfbConstants.SERVER_BELL: {
|
|
||||||
serverBell();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case RfbConstants.SERVER_CUT_TEXT: {
|
|
||||||
serverCutText(is);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Unknown server packet type: " + messageType + ".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
s_logger.error("Unexpected exception: ", e);
|
|
||||||
if (connectionAlive) {
|
|
||||||
closeConnection();
|
|
||||||
vncConnection.shutdown();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void closeConnection() {
|
public BufferedImageCanvas getCanvas() {
|
||||||
connectionAlive = false;
|
return canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConnectionAlive() {
|
@Override
|
||||||
return connectionAlive;
|
public void run() {
|
||||||
}
|
try {
|
||||||
|
while (connectionAlive) {
|
||||||
|
|
||||||
/**
|
// Read server message type
|
||||||
* Handle server bell packet.
|
int messageType = is.readUnsignedByte();
|
||||||
*/
|
|
||||||
private void serverBell() {
|
|
||||||
Toolkit.getDefaultToolkit().beep();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// Invoke packet handler by packet type.
|
||||||
* Handle packet with server clip-board.
|
switch (messageType) {
|
||||||
*/
|
|
||||||
private void serverCutText(DataInputStream is) throws IOException {
|
|
||||||
ServerCutText clipboardContent = new ServerCutText(is);
|
|
||||||
StringSelection contents = new StringSelection(clipboardContent.getContent());
|
|
||||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(contents, null);
|
|
||||||
|
|
||||||
s_logger.info("Server clipboard buffer: "+clipboardContent.getContent());
|
case RfbConstants.SERVER_FRAMEBUFFER_UPDATE: {
|
||||||
}
|
// Notify sender that frame buffer update is received,
|
||||||
|
// so it can send another frame buffer update request
|
||||||
|
fburListener.frameBufferPacketReceived();
|
||||||
|
// Handle frame buffer update
|
||||||
|
new FramebufferUpdatePacket(canvas, screen, is, clientListener);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case RfbConstants.SERVER_BELL: {
|
||||||
|
serverBell();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case RfbConstants.SERVER_CUT_TEXT: {
|
||||||
|
serverCutText(is);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unknown server packet type: " + messageType + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
s_logger.error("Unexpected exception: ", e);
|
||||||
|
if (connectionAlive) {
|
||||||
|
closeConnection();
|
||||||
|
vncConnection.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeConnection() {
|
||||||
|
connectionAlive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnectionAlive() {
|
||||||
|
return connectionAlive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle server bell packet.
|
||||||
|
*/
|
||||||
|
private void serverBell() {
|
||||||
|
Toolkit.getDefaultToolkit().beep();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle packet with server clip-board.
|
||||||
|
*/
|
||||||
|
private void serverCutText(DataInputStream is) throws IOException {
|
||||||
|
ServerCutText clipboardContent = new ServerCutText(is);
|
||||||
|
StringSelection contents = new StringSelection(clipboardContent.getContent());
|
||||||
|
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(contents, null);
|
||||||
|
|
||||||
|
s_logger.info("Server clipboard buffer: " + clipboardContent.getContent());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,6 @@ import java.io.IOException;
|
|||||||
|
|
||||||
public interface ClientPacket {
|
public interface ClientPacket {
|
||||||
|
|
||||||
void write(DataOutputStream os) throws IOException;
|
void write(DataOutputStream os) throws IOException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,27 +28,26 @@ import com.cloud.consoleproxy.vnc.RfbConstants;
|
|||||||
*/
|
*/
|
||||||
public class FramebufferUpdateRequestPacket implements ClientPacket {
|
public class FramebufferUpdateRequestPacket implements ClientPacket {
|
||||||
|
|
||||||
private final int incremental;
|
private final int incremental;
|
||||||
private final int x, y, width, height;
|
private final int x, y, width, height;
|
||||||
|
|
||||||
public FramebufferUpdateRequestPacket(int incremental, int x, int y, int width, int height) {
|
public FramebufferUpdateRequestPacket(int incremental, int x, int y, int width, int height) {
|
||||||
this.incremental = incremental;
|
this.incremental = incremental;
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(DataOutputStream os) throws IOException {
|
||||||
|
os.writeByte(RfbConstants.CLIENT_FRAMEBUFFER_UPDATE_REQUEST);
|
||||||
|
|
||||||
@Override
|
os.writeByte(incremental);
|
||||||
public void write(DataOutputStream os) throws IOException {
|
os.writeShort(x);
|
||||||
os.writeByte(RfbConstants.CLIENT_FRAMEBUFFER_UPDATE_REQUEST);
|
os.writeShort(y);
|
||||||
|
os.writeShort(width);
|
||||||
os.writeByte(incremental);
|
os.writeShort(height);
|
||||||
os.writeShort(x);
|
}
|
||||||
os.writeShort(y);
|
|
||||||
os.writeShort(width);
|
|
||||||
os.writeShort(height);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,20 +23,20 @@ import com.cloud.consoleproxy.vnc.RfbConstants;
|
|||||||
|
|
||||||
public class KeyboardEventPacket implements ClientPacket {
|
public class KeyboardEventPacket implements ClientPacket {
|
||||||
|
|
||||||
private final int downFlag, key;
|
private final int downFlag, key;
|
||||||
|
|
||||||
public KeyboardEventPacket(int downFlag, int key) {
|
public KeyboardEventPacket(int downFlag, int key) {
|
||||||
this.downFlag = downFlag;
|
this.downFlag = downFlag;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(DataOutputStream os) throws IOException {
|
public void write(DataOutputStream os) throws IOException {
|
||||||
os.writeByte(RfbConstants.CLIENT_KEYBOARD_EVENT);
|
os.writeByte(RfbConstants.CLIENT_KEYBOARD_EVENT);
|
||||||
|
|
||||||
os.writeByte(downFlag);
|
os.writeByte(downFlag);
|
||||||
os.writeShort(0); // padding
|
os.writeShort(0); // padding
|
||||||
os.writeInt(key);
|
os.writeInt(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,21 +23,21 @@ import com.cloud.consoleproxy.vnc.RfbConstants;
|
|||||||
|
|
||||||
public class MouseEventPacket implements ClientPacket {
|
public class MouseEventPacket implements ClientPacket {
|
||||||
|
|
||||||
private final int buttonMask, x, y;
|
private final int buttonMask, x, y;
|
||||||
|
|
||||||
public MouseEventPacket(int buttonMask, int x, int y) {
|
public MouseEventPacket(int buttonMask, int x, int y) {
|
||||||
this.buttonMask = buttonMask;
|
this.buttonMask = buttonMask;
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(DataOutputStream os) throws IOException {
|
public void write(DataOutputStream os) throws IOException {
|
||||||
os.writeByte(RfbConstants.CLIENT_POINTER_EVENT);
|
os.writeByte(RfbConstants.CLIENT_POINTER_EVENT);
|
||||||
|
|
||||||
os.writeByte(buttonMask);
|
os.writeByte(buttonMask);
|
||||||
os.writeShort(x);
|
os.writeShort(x);
|
||||||
os.writeShort(y);
|
os.writeShort(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,26 +23,23 @@ import com.cloud.consoleproxy.vnc.RfbConstants;
|
|||||||
|
|
||||||
public class SetEncodingsPacket implements ClientPacket {
|
public class SetEncodingsPacket implements ClientPacket {
|
||||||
|
|
||||||
private final int[] encodings;
|
private final int[] encodings;
|
||||||
|
|
||||||
public SetEncodingsPacket(int[] encodings)
|
public SetEncodingsPacket(int[] encodings) {
|
||||||
{
|
this.encodings = encodings;
|
||||||
this.encodings = encodings;
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
@Override
|
public void write(DataOutputStream os) throws IOException {
|
||||||
public void write(DataOutputStream os) throws IOException
|
os.writeByte(RfbConstants.CLIENT_SET_ENCODINGS);
|
||||||
{
|
|
||||||
os.writeByte(RfbConstants.CLIENT_SET_ENCODINGS);
|
os.writeByte(0);// padding
|
||||||
|
|
||||||
os.writeByte(0);//padding
|
os.writeShort(encodings.length);
|
||||||
|
|
||||||
os.writeShort(encodings.length);
|
for (int i = 0; i < encodings.length; i++) {
|
||||||
|
os.writeInt(encodings[i]);
|
||||||
for(int i=0;i<encodings.length;i++)
|
}
|
||||||
{
|
|
||||||
os.writeInt(encodings[i]);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,52 +24,52 @@ import com.cloud.consoleproxy.vnc.VncScreenDescription;
|
|||||||
|
|
||||||
public class SetPixelFormatPacket implements ClientPacket {
|
public class SetPixelFormatPacket implements ClientPacket {
|
||||||
|
|
||||||
private final int bitsPerPixel, depth, bigEndianFlag, trueColourFlag, redMax, greenMax, blueMax, redShift, greenShift, blueShift;
|
private final int bitsPerPixel, depth, bigEndianFlag, trueColourFlag, redMax, greenMax, blueMax, redShift, greenShift, blueShift;
|
||||||
|
|
||||||
private final VncScreenDescription screen;
|
private final VncScreenDescription screen;
|
||||||
|
|
||||||
public SetPixelFormatPacket(VncScreenDescription screen, int bitsPerPixel, int depth, int bigEndianFlag, int trueColorFlag, int redMax, int greenMax,
|
public SetPixelFormatPacket(VncScreenDescription screen, int bitsPerPixel, int depth, int bigEndianFlag, int trueColorFlag, int redMax, int greenMax, int blueMax, int redShift, int greenShift,
|
||||||
int blueMax, int redShift, int greenShift, int blueShift) {
|
int blueShift) {
|
||||||
this.screen = screen;
|
this.screen = screen;
|
||||||
this.bitsPerPixel = bitsPerPixel;
|
this.bitsPerPixel = bitsPerPixel;
|
||||||
this.depth = depth;
|
this.depth = depth;
|
||||||
this.bigEndianFlag = bigEndianFlag;
|
this.bigEndianFlag = bigEndianFlag;
|
||||||
this.trueColourFlag = trueColorFlag;
|
this.trueColourFlag = trueColorFlag;
|
||||||
this.redMax = redMax;
|
this.redMax = redMax;
|
||||||
this.greenMax = greenMax;
|
this.greenMax = greenMax;
|
||||||
this.blueMax = blueMax;
|
this.blueMax = blueMax;
|
||||||
this.redShift = redShift;
|
this.redShift = redShift;
|
||||||
this.greenShift = greenShift;
|
this.greenShift = greenShift;
|
||||||
this.blueShift = blueShift;
|
this.blueShift = blueShift;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(DataOutputStream os) throws IOException {
|
public void write(DataOutputStream os) throws IOException {
|
||||||
os.writeByte(RfbConstants.CLIENT_SET_PIXEL_FORMAT);
|
os.writeByte(RfbConstants.CLIENT_SET_PIXEL_FORMAT);
|
||||||
|
|
||||||
// Padding
|
// Padding
|
||||||
os.writeByte(0);
|
os.writeByte(0);
|
||||||
os.writeByte(0);
|
os.writeByte(0);
|
||||||
os.writeByte(0);
|
os.writeByte(0);
|
||||||
|
|
||||||
// Send pixel format
|
// Send pixel format
|
||||||
os.writeByte(bitsPerPixel);
|
os.writeByte(bitsPerPixel);
|
||||||
os.writeByte(depth);
|
os.writeByte(depth);
|
||||||
os.writeByte(bigEndianFlag);
|
os.writeByte(bigEndianFlag);
|
||||||
os.writeByte(trueColourFlag);
|
os.writeByte(trueColourFlag);
|
||||||
os.writeShort(redMax);
|
os.writeShort(redMax);
|
||||||
os.writeShort(greenMax);
|
os.writeShort(greenMax);
|
||||||
os.writeShort(blueMax);
|
os.writeShort(blueMax);
|
||||||
os.writeByte(redShift);
|
os.writeByte(redShift);
|
||||||
os.writeByte(greenShift);
|
os.writeByte(greenShift);
|
||||||
os.writeByte(blueShift);
|
os.writeByte(blueShift);
|
||||||
|
|
||||||
// Padding
|
// Padding
|
||||||
os.writeByte(0);
|
os.writeByte(0);
|
||||||
os.writeByte(0);
|
os.writeByte(0);
|
||||||
os.writeByte(0);
|
os.writeByte(0);
|
||||||
|
|
||||||
screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag, trueColourFlag, redMax, greenMax, blueMax, redShift, greenShift, blueShift);
|
screen.setPixelFormat(bitsPerPixel, depth, bigEndianFlag, trueColourFlag, redMax, greenMax, blueMax, redShift, greenShift, blueShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,36 +18,36 @@ package com.cloud.consoleproxy.vnc.packet.server;
|
|||||||
|
|
||||||
public abstract class AbstractRect implements Rect {
|
public abstract class AbstractRect implements Rect {
|
||||||
|
|
||||||
protected final int x;
|
protected final int x;
|
||||||
protected final int y;
|
protected final int y;
|
||||||
protected final int width;
|
protected final int width;
|
||||||
protected final int height;
|
protected final int height;
|
||||||
|
|
||||||
public AbstractRect(int x, int y, int width, int height) {
|
public AbstractRect(int x, int y, int width, int height) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getX() {
|
public int getX() {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getY() {
|
public int getY() {
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getWidth() {
|
public int getWidth() {
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getHeight() {
|
public int getHeight() {
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -23,17 +23,17 @@ import java.io.IOException;
|
|||||||
|
|
||||||
public class CopyRect extends AbstractRect {
|
public class CopyRect extends AbstractRect {
|
||||||
|
|
||||||
private final int srcX, srcY;
|
private final int srcX, srcY;
|
||||||
|
|
||||||
public CopyRect(int x, int y, int width, int height, DataInputStream is) throws IOException {
|
public CopyRect(int x, int y, int width, int height, DataInputStream is) throws IOException {
|
||||||
super(x, y, width, height);
|
super(x, y, width, height);
|
||||||
|
|
||||||
srcX = is.readUnsignedShort();
|
srcX = is.readUnsignedShort();
|
||||||
srcY = is.readUnsignedShort();
|
srcY = is.readUnsignedShort();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint(BufferedImage image, Graphics2D graphics) {
|
public void paint(BufferedImage image, Graphics2D graphics) {
|
||||||
graphics.copyArea(srcX, srcY, width, height, x - srcX, y - srcY);
|
graphics.copyArea(srcX, srcY, width, height, x - srcX, y - srcY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,17 +23,17 @@ import com.cloud.consoleproxy.vnc.BufferedImageCanvas;
|
|||||||
|
|
||||||
public class FrameBufferSizeChangeRequest extends AbstractRect {
|
public class FrameBufferSizeChangeRequest extends AbstractRect {
|
||||||
|
|
||||||
private final BufferedImageCanvas canvas;
|
private final BufferedImageCanvas canvas;
|
||||||
|
|
||||||
public FrameBufferSizeChangeRequest(BufferedImageCanvas canvas, int width, int height) {
|
public FrameBufferSizeChangeRequest(BufferedImageCanvas canvas, int width, int height) {
|
||||||
super(0, 0, width, height);
|
super(0, 0, width, height);
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
canvas.setCanvasSize(width, height);
|
canvas.setCanvasSize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paint(BufferedImage offlineImage, Graphics2D graphics) {
|
public void paint(BufferedImage offlineImage, Graphics2D graphics) {
|
||||||
canvas.setCanvasSize(width, height);
|
canvas.setCanvasSize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,75 +29,74 @@ import com.cloud.consoleproxy.vnc.packet.server.Rect;
|
|||||||
|
|
||||||
public class FramebufferUpdatePacket {
|
public class FramebufferUpdatePacket {
|
||||||
|
|
||||||
private final VncScreenDescription screen;
|
private final VncScreenDescription screen;
|
||||||
private final BufferedImageCanvas canvas;
|
private final BufferedImageCanvas canvas;
|
||||||
private final ConsoleProxyClientListener clientListener;
|
private final ConsoleProxyClientListener clientListener;
|
||||||
|
|
||||||
public FramebufferUpdatePacket(BufferedImageCanvas canvas, VncScreenDescription screen, DataInputStream is,
|
public FramebufferUpdatePacket(BufferedImageCanvas canvas, VncScreenDescription screen, DataInputStream is, ConsoleProxyClientListener clientListener) throws IOException {
|
||||||
ConsoleProxyClientListener clientListener) throws IOException {
|
|
||||||
|
|
||||||
this.screen = screen;
|
this.screen = screen;
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
this.clientListener = clientListener;
|
this.clientListener = clientListener;
|
||||||
readPacketData(is);
|
readPacketData(is);
|
||||||
}
|
|
||||||
|
|
||||||
private void readPacketData(DataInputStream is) throws IOException {
|
|
||||||
is.skipBytes(1);// Skip padding
|
|
||||||
|
|
||||||
// Read number of rectangles
|
|
||||||
int numberOfRectangles = is.readUnsignedShort();
|
|
||||||
|
|
||||||
// For all rectangles
|
|
||||||
for (int i = 0; i < numberOfRectangles; i++) {
|
|
||||||
|
|
||||||
// Read coordinate of rectangle
|
|
||||||
int x = is.readUnsignedShort();
|
|
||||||
int y = is.readUnsignedShort();
|
|
||||||
int width = is.readUnsignedShort();
|
|
||||||
int height = is.readUnsignedShort();
|
|
||||||
|
|
||||||
int encodingType = is.readInt();
|
|
||||||
|
|
||||||
// Process rectangle
|
|
||||||
Rect rect;
|
|
||||||
switch (encodingType) {
|
|
||||||
|
|
||||||
case RfbConstants.ENCODING_RAW: {
|
|
||||||
rect = new RawRect(screen, x, y, width, height, is);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case RfbConstants.ENCODING_COPY_RECT: {
|
|
||||||
rect = new CopyRect(x, y, width, height, is);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case RfbConstants.ENCODING_DESKTOP_SIZE: {
|
|
||||||
rect = new FrameBufferSizeChangeRequest(canvas, width, height);
|
|
||||||
if(this.clientListener != null)
|
|
||||||
this.clientListener.onFramebufferSizeChange(width, height);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Unsupported ecnoding: " + encodingType);
|
|
||||||
}
|
|
||||||
|
|
||||||
paint(rect, canvas);
|
|
||||||
|
|
||||||
if(this.clientListener != null)
|
|
||||||
this.clientListener.onFramebufferUpdate(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
private void readPacketData(DataInputStream is) throws IOException {
|
||||||
|
is.skipBytes(1);// Skip padding
|
||||||
|
|
||||||
public void paint(Rect rect, BufferedImageCanvas canvas) {
|
// Read number of rectangles
|
||||||
// Draw rectangle on offline buffer
|
int numberOfRectangles = is.readUnsignedShort();
|
||||||
rect.paint(canvas.getOfflineImage(), canvas.getOfflineGraphics());
|
|
||||||
|
|
||||||
// Request update of repainted area
|
// For all rectangles
|
||||||
canvas.repaint(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
|
for (int i = 0; i < numberOfRectangles; i++) {
|
||||||
}
|
|
||||||
|
// Read coordinate of rectangle
|
||||||
|
int x = is.readUnsignedShort();
|
||||||
|
int y = is.readUnsignedShort();
|
||||||
|
int width = is.readUnsignedShort();
|
||||||
|
int height = is.readUnsignedShort();
|
||||||
|
|
||||||
|
int encodingType = is.readInt();
|
||||||
|
|
||||||
|
// Process rectangle
|
||||||
|
Rect rect;
|
||||||
|
switch (encodingType) {
|
||||||
|
|
||||||
|
case RfbConstants.ENCODING_RAW: {
|
||||||
|
rect = new RawRect(screen, x, y, width, height, is);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case RfbConstants.ENCODING_COPY_RECT: {
|
||||||
|
rect = new CopyRect(x, y, width, height, is);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case RfbConstants.ENCODING_DESKTOP_SIZE: {
|
||||||
|
rect = new FrameBufferSizeChangeRequest(canvas, width, height);
|
||||||
|
if (this.clientListener != null)
|
||||||
|
this.clientListener.onFramebufferSizeChange(width, height);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unsupported ecnoding: " + encodingType);
|
||||||
|
}
|
||||||
|
|
||||||
|
paint(rect, canvas);
|
||||||
|
|
||||||
|
if (this.clientListener != null)
|
||||||
|
this.clientListener.onFramebufferUpdate(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void paint(Rect rect, BufferedImageCanvas canvas) {
|
||||||
|
// Draw rectangle on offline buffer
|
||||||
|
rect.paint(canvas.getOfflineImage(), canvas.getOfflineGraphics());
|
||||||
|
|
||||||
|
// Request update of repainted area
|
||||||
|
canvas.repaint(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,51 +26,50 @@ import java.io.IOException;
|
|||||||
import com.cloud.consoleproxy.vnc.VncScreenDescription;
|
import com.cloud.consoleproxy.vnc.VncScreenDescription;
|
||||||
|
|
||||||
public class RawRect extends AbstractRect {
|
public class RawRect extends AbstractRect {
|
||||||
private final int[] buf;
|
private final int[] buf;
|
||||||
|
|
||||||
public RawRect(VncScreenDescription screen, int x, int y, int width, int height, DataInputStream is) throws IOException {
|
public RawRect(VncScreenDescription screen, int x, int y, int width, int height, DataInputStream is) throws IOException {
|
||||||
super(x, y, width, height);
|
super(x, y, width, height);
|
||||||
|
|
||||||
byte[] bbuf = new byte[width * height * screen.getBytesPerPixel()];
|
byte[] bbuf = new byte[width * height * screen.getBytesPerPixel()];
|
||||||
is.readFully(bbuf);
|
is.readFully(bbuf);
|
||||||
|
|
||||||
// Convert array of bytes to array of int
|
// Convert array of bytes to array of int
|
||||||
int size = width * height;
|
int size = width * height;
|
||||||
buf = new int[size];
|
buf = new int[size];
|
||||||
for (int i = 0, j = 0; i < size; i++, j += 4) {
|
for (int i = 0, j = 0; i < size; i++, j += 4) {
|
||||||
buf[i] = (bbuf[j + 0] & 0xFF) | ((bbuf[j + 1] & 0xFF) << 8) | ((bbuf[j + 2] & 0xFF) << 16) | ((bbuf[j + 3] & 0xFF) << 24);
|
buf[i] = (bbuf[j + 0] & 0xFF) | ((bbuf[j + 1] & 0xFF) << 8) | ((bbuf[j + 2] & 0xFF) << 16) | ((bbuf[j + 3] & 0xFF) << 24);
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void paint(BufferedImage image, Graphics2D graphics) {
|
|
||||||
|
|
||||||
DataBuffer dataBuf = image.getRaster().getDataBuffer();
|
|
||||||
|
|
||||||
switch (dataBuf.getDataType()) {
|
|
||||||
|
|
||||||
case DataBuffer.TYPE_INT: {
|
|
||||||
// We chose RGB888 model, so Raster will use DataBufferInt type
|
|
||||||
DataBufferInt dataBuffer = (DataBufferInt) dataBuf;
|
|
||||||
|
|
||||||
int imageWidth = image.getWidth();
|
|
||||||
int imageHeight = image.getHeight();
|
|
||||||
|
|
||||||
// Paint rectangle directly on buffer, line by line
|
|
||||||
int[] imageBuffer = dataBuffer.getData();
|
|
||||||
for (int srcLine = 0, dstLine = y; srcLine < height && dstLine < imageHeight; srcLine++, dstLine++) {
|
|
||||||
try {
|
|
||||||
System.arraycopy(buf, srcLine * width, imageBuffer, x + dstLine * imageWidth, width);
|
|
||||||
} catch (IndexOutOfBoundsException e) {
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
@Override
|
||||||
throw new RuntimeException("Unsupported data buffer in buffered image: expected data buffer of type int (DataBufferInt). Actual data buffer type: "
|
public void paint(BufferedImage image, Graphics2D graphics) {
|
||||||
+ dataBuf.getClass().getSimpleName());
|
|
||||||
|
DataBuffer dataBuf = image.getRaster().getDataBuffer();
|
||||||
|
|
||||||
|
switch (dataBuf.getDataType()) {
|
||||||
|
|
||||||
|
case DataBuffer.TYPE_INT: {
|
||||||
|
// We chose RGB888 model, so Raster will use DataBufferInt type
|
||||||
|
DataBufferInt dataBuffer = (DataBufferInt) dataBuf;
|
||||||
|
|
||||||
|
int imageWidth = image.getWidth();
|
||||||
|
int imageHeight = image.getHeight();
|
||||||
|
|
||||||
|
// Paint rectangle directly on buffer, line by line
|
||||||
|
int[] imageBuffer = dataBuffer.getData();
|
||||||
|
for (int srcLine = 0, dstLine = y; srcLine < height && dstLine < imageHeight; srcLine++, dstLine++) {
|
||||||
|
try {
|
||||||
|
System.arraycopy(buf, srcLine * width, imageBuffer, x + dstLine * imageWidth, width);
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unsupported data buffer in buffered image: expected data buffer of type int (DataBufferInt). Actual data buffer type: " + dataBuf.getClass().getSimpleName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,10 +21,13 @@ import java.awt.image.BufferedImage;
|
|||||||
|
|
||||||
public interface Rect {
|
public interface Rect {
|
||||||
|
|
||||||
void paint(BufferedImage offlineImage, Graphics2D graphics);
|
void paint(BufferedImage offlineImage, Graphics2D graphics);
|
||||||
|
|
||||||
int getX();
|
int getX();
|
||||||
int getY();
|
|
||||||
int getWidth();
|
int getY();
|
||||||
int getHeight();
|
|
||||||
|
int getWidth();
|
||||||
|
|
||||||
|
int getHeight();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,27 +23,27 @@ import com.cloud.consoleproxy.util.Logger;
|
|||||||
import com.cloud.consoleproxy.vnc.RfbConstants;
|
import com.cloud.consoleproxy.vnc.RfbConstants;
|
||||||
|
|
||||||
public class ServerCutText {
|
public class ServerCutText {
|
||||||
private static final Logger s_logger = Logger.getLogger(ServerCutText.class);
|
private static final Logger s_logger = Logger.getLogger(ServerCutText.class);
|
||||||
|
|
||||||
private String content;
|
private String content;
|
||||||
|
|
||||||
public String getContent() {
|
public String getContent() {
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServerCutText(DataInputStream is) throws IOException {
|
public ServerCutText(DataInputStream is) throws IOException {
|
||||||
readPacketData(is);
|
readPacketData(is);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readPacketData(DataInputStream is) throws IOException {
|
private void readPacketData(DataInputStream is) throws IOException {
|
||||||
is.skipBytes(3);// Skip padding
|
is.skipBytes(3);// Skip padding
|
||||||
int length = is.readInt();
|
int length = is.readInt();
|
||||||
byte buf[] = new byte[length];
|
byte buf[] = new byte[length];
|
||||||
is.readFully(buf);
|
is.readFully(buf);
|
||||||
|
|
||||||
content = new String(buf, RfbConstants.CHARSET);
|
content = new String(buf, RfbConstants.CHARSET);
|
||||||
|
|
||||||
/* LOG */s_logger.info("Clippboard content: " + content);
|
/* LOG */s_logger.info("Clippboard content: " + content);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user