CLOUDSTACK-61 Console proxy has plenty of files with CRLF line ending.

This commit is contained in:
Mice Xia 2012-09-08 09:24:34 +08:00
parent f03d438c4c
commit 0bf8c5a18f
57 changed files with 4486 additions and 4494 deletions

View File

@ -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) {

View File

@ -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);
} }
} }

View File

@ -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();
} }
} }
} }

View File

@ -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);
} }
} }
} }
} }

View File

@ -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;
} }
} }

View File

@ -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;
} }
} }

View File

@ -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();
} }

View File

@ -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();
} }
} }
} }

View File

@ -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;
} }
} }

View File

@ -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);
} }
} }
} }

View File

@ -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();
} }
} }

View File

@ -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);
} }
} }

View File

@ -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;
} }
} }

View File

@ -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;
} }

View File

@ -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;
} }
} }

View File

@ -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);
} }
} }

View File

@ -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);
} }

View File

@ -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;
} }
} }

View File

@ -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);
} }
} }
} }
} }
} }

View File

@ -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);
} }

View File

@ -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;
} }
} }

View File

@ -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;
} }
} }

View File

@ -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();
} }
} }

View File

@ -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;
} }
} }

View File

@ -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;
} }
} }

View File

@ -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;
}
} }

View File

@ -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);
} }

View File

@ -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();
} }

View File

@ -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();
} }

View File

@ -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");
} }

View File

@ -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();
}
} }

View File

@ -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;
}
} }

View File

@ -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;
} }
} }

View File

@ -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());
}
} }

View File

@ -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;
} }

View File

@ -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);
}
} }

View File

@ -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);
} }
} }

View File

@ -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);
} }
} }

View File

@ -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]);
} }
}
} }

View File

@ -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);
} }
} }

View File

@ -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;
} }
} }

View File

@ -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);
} }
} }

View File

@ -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);
} }
} }

View File

@ -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());
}
} }

View File

@ -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());
}
} }
}
} }

View File

@ -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();
} }

View File

@ -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);
} }
} }