├── .gitignore ├── LICENSE ├── README ├── assets └── www │ ├── index.html │ └── js │ └── websocket.js └── src └── com └── strumsoft └── websocket └── phonegap ├── Base64.java ├── WebSocket.java └── WebSocketFactory.java /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | bin 3 | gen 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 STRUMSOFT (http://www.strumsoft.com) 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Note: this plugin will be added to Phonegap-android core very soon. :) Here is the forked project: https://github.com/anismiles/phonegap-android 2 | 3 | 4 | Websocket Android Plugin with Phonegap integration 5 | -------------------------------------------------- 6 | 7 | This is a Java library that implements Websockt API (Draft-75/76) for Android platform. Library uses java.nio.* packages for 8 | efficient non-blocking evented behavior. It easily gets integrated with Phonegap framework too. 9 | 10 | Usage (native Android) 11 | ---------------------- 12 | 13 | 1. Copy Java source into your source folder. 14 | 2. Create a class, say WebSocketListener extending WebSocket (refer: com.strumsoft.websocket.WebSocket) 15 | 3. Implement following methods 16 | 1. onOpen 17 | 2. onClose 18 | 3. onMessage 19 | 4. onReconnect (WebSocketListener will try to reconnect to the server in case of connection failure) 20 | 21 | Usage (Phonegap) 22 | ---------------- 23 | 24 | 1. Copy Java source into your source folder. 25 | 2. Copy websocket.js in your assets/www/js folder 26 | 3. Attach com.strumsoft.websocket.phonegap.WebSocketFactory to WebView, like this: 27 | 28 | @Override 29 | public void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | super.loadUrl(" file:///android_asset/www/index.html "); 32 | 33 | // attach websocket factory 34 | appView.addJavascriptInterface(new WebSocketFactory(appView), "WebSocketFactory"); 35 | } 36 | 37 | 4. In your page, create a new WebSocket, and overload its method 'onmessage', 'onopen', 'onclose', like this: 38 | 39 | // new socket 40 | var socket = new WebSocket('ws://192.168.1.153:8081'); 41 | 42 | // push a message after the connection is established. 43 | socket.onopen = function() { 44 | socket.send('{ "type": "join", "game_id": "game/6"}') 45 | }; 46 | 47 | // alerts message pushed from server 48 | socket.onmessage = function(msg) { 49 | alert(JSON.stringify(msg.data)); 50 | }; 51 | 52 | // alert close event 53 | socket.onclose = function() { 54 | alert('closed'); 55 | }; 56 | 57 | 58 | ps: It doesn't support 'onerror' event, and various states as defined by WebSocket APIs yet. I am working on it. By the way, if you like the project, join the force. -------------------------------------------------------------------------------- /assets/www/index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |11 | [code] 12 | // new socket 13 | var socket = new WebSocket('ws://122.168.196.27:8081/'); 14 | 15 | // push a message after the connection is established. 16 | socket.onopen = function() { 17 | socket.send('Hello World') 18 | }; 19 | 20 | // alerts message pushed from server 21 | socket.onmessage = function(msg) { 22 | alert(JSON.stringify(msg)); 23 | }; 24 | 25 | // alert close event 26 | socket.onclose = function() { 27 | alert('closed'); 28 | }; 29 | 30 | [/code] 31 |32 | 33 | 34 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /assets/www/js/websocket.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Animesh Kumar (https://github.com/anismiles) 3 | * Copyright (c) 2010 Strumsoft (https://strumsoft.com) 4 | * 5 | * Permission is hereby granted, free of charge, to any person 6 | * obtaining a copy of this software and associated documentation 7 | * files (the "Software"), to deal in the Software without 8 | * restriction, including without limitation the rights to use, 9 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following 12 | * conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be 15 | * included in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | * OTHER DEALINGS IN THE SOFTWARE. 25 | * 26 | */ 27 | (function() { 28 | 29 | // window object 30 | var global = window; 31 | 32 | // WebSocket Object. All listener methods are cleaned up! 33 | var WebSocket = global.WebSocket = function(url) { 34 | // get a new websocket object from factory (check com.strumsoft.websocket.WebSocketFactory.java) 35 | this.socket = WebSocketFactory.getInstance(url); 36 | // store in registry 37 | if(this.socket) { 38 | WebSocket.store[this.socket.getId()] = this; 39 | } else { 40 | throw new Error('Websocket instantiation failed! Address might be wrong.'); 41 | } 42 | }; 43 | 44 | // private property 45 | WebSocket._keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 46 | 47 | // private method for decoding base64 48 | WebSocket._decode = function (input) { 49 | var output = ""; 50 | var chr1, chr2, chr3; 51 | var enc1, enc2, enc3, enc4; 52 | var i = 0; 53 | 54 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 55 | 56 | while (i < input.length) { 57 | 58 | enc1 = this._keyStr.indexOf(input.charAt(i++)); 59 | enc2 = this._keyStr.indexOf(input.charAt(i++)); 60 | enc3 = this._keyStr.indexOf(input.charAt(i++)); 61 | enc4 = this._keyStr.indexOf(input.charAt(i++)); 62 | 63 | chr1 = (enc1 << 2) | (enc2 >> 4); 64 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 65 | chr3 = ((enc3 & 3) << 6) | enc4; 66 | 67 | output = output + String.fromCharCode(chr1); 68 | 69 | if (enc3 != 64) { 70 | output = output + String.fromCharCode(chr2); 71 | } 72 | if (enc4 != 64) { 73 | output = output + String.fromCharCode(chr3); 74 | } 75 | 76 | } 77 | 78 | output = this._utf8_decode(output); 79 | 80 | return output; 81 | 82 | } 83 | 84 | // private method for UTF-8 decoding 85 | WebSocket._utf8_decode = function (utftext) { 86 | var string = ""; 87 | var i = 0; 88 | var c = c1 = c2 = 0; 89 | 90 | while ( i < utftext.length ) { 91 | 92 | c = utftext.charCodeAt(i); 93 | 94 | if (c < 128) { 95 | string += String.fromCharCode(c); 96 | i++; 97 | } 98 | else if((c > 191) && (c < 224)) { 99 | c2 = utftext.charCodeAt(i+1); 100 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 101 | i += 2; 102 | } 103 | else { 104 | c2 = utftext.charCodeAt(i+1); 105 | c3 = utftext.charCodeAt(i+2); 106 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 107 | i += 3; 108 | } 109 | 110 | } 111 | 112 | return string; 113 | } 114 | 115 | // storage to hold websocket object for later invokation of event methods 116 | WebSocket.store = {}; 117 | 118 | // static event methods to call event methods on target websocket objects 119 | WebSocket.onmessage = function (evt) { 120 | WebSocket.store[evt._target]['onmessage'].call(global, this._decode(evt._data)); 121 | } 122 | 123 | WebSocket.onopen = function (evt) { 124 | WebSocket.store[evt._target]['onopen'].call(global, evt); 125 | } 126 | 127 | WebSocket.onclose = function (evt) { 128 | WebSocket.store[evt._target]['onclose'].call(global, evt); 129 | } 130 | 131 | WebSocket.onerror = function (evt) { 132 | WebSocket.store[evt._target]['onerror'].call(global, evt); 133 | } 134 | 135 | // instance event methods 136 | WebSocket.prototype.send = function(data) { 137 | this.socket.send(data); 138 | } 139 | 140 | WebSocket.prototype.close = function() { 141 | this.socket.close(); 142 | } 143 | 144 | WebSocket.prototype.getReadyState = function() { 145 | this.socket.getReadyState(); 146 | } 147 | ///////////// Must be overloaded 148 | WebSocket.prototype.onopen = function(){ 149 | throw new Error('onopen not implemented.'); 150 | }; 151 | 152 | // alerts message pushed from server 153 | WebSocket.prototype.onmessage = function(msg){ 154 | throw new Error('onmessage not implemented.'); 155 | }; 156 | 157 | // alerts message pushed from server 158 | WebSocket.prototype.onerror = function(msg){ 159 | throw new Error('onerror not implemented.'); 160 | }; 161 | 162 | // alert close event 163 | WebSocket.prototype.onclose = function(){ 164 | throw new Error('onclose not implemented.'); 165 | }; 166 | })(); 167 | -------------------------------------------------------------------------------- /src/com/strumsoft/websocket/phonegap/Base64.java: -------------------------------------------------------------------------------- 1 | /** 2 | *
Encodes and decodes to and from Base64 notation.
3 | *Homepage: http://iharder.net/base64.
4 | * 5 | *Example:
6 | * 7 | *String encoded = Base64.encode( myByteArray );
8 | * byte[] myByteArray = Base64.decode( encoded );
10 | *
11 | * The options parameter, which appears in a few places, is used to pass 12 | * several pieces of information to the encoder. In the "higher level" methods such as 13 | * encodeBytes( bytes, options ) the options parameter can be used to indicate such 14 | * things as first gzipping the bytes before encoding them, not inserting linefeeds, 15 | * and encoding using the URL-safe and Ordered dialects.
16 | * 17 | *Note, according to RFC3548, 18 | * Section 2.1, implementations should not add line feeds unless explicitly told 19 | * to do so. I've got Base64 set to this behavior now, although earlier versions 20 | * broke lines by default.
21 | * 22 | *The constants defined in Base64 can be OR-ed together to combine options, so you 23 | * might make a call like this:
24 | * 25 | *String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DO_BREAK_LINES );
26 | * to compress the data before encoding it and then making the output have newline characters.
27 | *Also...
28 | *String encoded = Base64.encodeBytes( crazyString.getBytes() );
29 | *
30 | *
31 | *
32 | * 33 | * Change Log: 34 | *
35 | *137 | * I am placing this code in the Public Domain. Do with it as you will. 138 | * This software comes with no guarantees or warranties but with 139 | * plenty of well-wishing instead! 140 | * Please visit http://iharder.net/base64 141 | * periodically to check for updates or to contribute improvements. 142 | *
143 | * 144 | * @author Robert Harder 145 | * @author rob@iharder.net 146 | * @version 2.3.7 147 | */ 148 | package com.strumsoft.phonegap.websocket; 149 | 150 | public class Base64 151 | { 152 | 153 | /* ******** P U B L I C F I E L D S ******** */ 154 | 155 | 156 | /** No options specified. Value is zero. */ 157 | public final static int NO_OPTIONS = 0; 158 | 159 | /** Specify encoding in first bit. Value is one. */ 160 | public final static int ENCODE = 1; 161 | 162 | 163 | /** Specify decoding in first bit. Value is zero. */ 164 | public final static int DECODE = 0; 165 | 166 | 167 | /** Specify that data should be gzip-compressed in second bit. Value is two. */ 168 | public final static int GZIP = 2; 169 | 170 | /** Specify that gzipped data should not be automatically gunzipped. */ 171 | public final static int DONT_GUNZIP = 4; 172 | 173 | 174 | /** Do break lines when encoding. Value is 8. */ 175 | public final static int DO_BREAK_LINES = 8; 176 | 177 | /** 178 | * Encode using Base64-like encoding that is URL- and Filename-safe as described 179 | * in Section 4 of RFC3548: 180 | * http://www.faqs.org/rfcs/rfc3548.html. 181 | * It is important to note that data encoded this way is not officially valid Base64, 182 | * or at the very least should not be called Base64 without also specifying that is 183 | * was encoded using the URL- and Filename-safe dialect. 184 | */ 185 | public final static int URL_SAFE = 16; 186 | 187 | 188 | /** 189 | * Encode using the special "ordered" dialect of Base64 described here: 190 | * http://www.faqs.org/qa/rfcc-1940.html. 191 | */ 192 | public final static int ORDERED = 32; 193 | 194 | 195 | /* ******** P R I V A T E F I E L D S ******** */ 196 | 197 | 198 | /** Maximum line length (76) of Base64 output. */ 199 | private final static int MAX_LINE_LENGTH = 76; 200 | 201 | 202 | /** The equals sign (=) as a byte. */ 203 | private final static byte EQUALS_SIGN = (byte)'='; 204 | 205 | 206 | /** The new line character (\n) as a byte. */ 207 | private final static byte NEW_LINE = (byte)'\n'; 208 | 209 | 210 | /** Preferred encoding. */ 211 | private final static String PREFERRED_ENCODING = "US-ASCII"; 212 | 213 | 214 | private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding 215 | private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding 216 | 217 | 218 | /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ 219 | 220 | /** The 64 valid Base64 values. */ 221 | /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ 222 | private final static byte[] _STANDARD_ALPHABET = { 223 | (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', 224 | (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', 225 | (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 226 | (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', 227 | (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', 228 | (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', 229 | (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 230 | (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', 231 | (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 232 | (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' 233 | }; 234 | 235 | 236 | /** 237 | * Translates a Base64 value to either its 6-bit reconstruction value 238 | * or a negative number indicating some other meaning. 239 | **/ 240 | private final static byte[] _STANDARD_DECODABET = { 241 | -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 242 | -5,-5, // Whitespace: Tab and Linefeed 243 | -9,-9, // Decimal 11 - 12 244 | -5, // Whitespace: Carriage Return 245 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 246 | -9,-9,-9,-9,-9, // Decimal 27 - 31 247 | -5, // Whitespace: Space 248 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 249 | 62, // Plus sign at decimal 43 250 | -9,-9,-9, // Decimal 44 - 46 251 | 63, // Slash at decimal 47 252 | 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine 253 | -9,-9,-9, // Decimal 58 - 60 254 | -1, // Equals sign at decimal 61 255 | -9,-9,-9, // Decimal 62 - 64 256 | 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' 257 | 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' 258 | -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 259 | 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' 260 | 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' 261 | -9,-9,-9,-9,-9 // Decimal 123 - 127 262 | ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139 263 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 264 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 265 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 266 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 267 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 268 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 269 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 270 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 271 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 272 | }; 273 | 274 | 275 | /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ 276 | 277 | /** 278 | * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: 279 | * http://www.faqs.org/rfcs/rfc3548.html. 280 | * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." 281 | */ 282 | private final static byte[] _URL_SAFE_ALPHABET = { 283 | (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', 284 | (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', 285 | (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 286 | (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', 287 | (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', 288 | (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', 289 | (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 290 | (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', 291 | (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', 292 | (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_' 293 | }; 294 | 295 | /** 296 | * Used in decoding URL- and Filename-safe dialects of Base64. 297 | */ 298 | private final static byte[] _URL_SAFE_DECODABET = { 299 | -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 300 | -5,-5, // Whitespace: Tab and Linefeed 301 | -9,-9, // Decimal 11 - 12 302 | -5, // Whitespace: Carriage Return 303 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 304 | -9,-9,-9,-9,-9, // Decimal 27 - 31 305 | -5, // Whitespace: Space 306 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 307 | -9, // Plus sign at decimal 43 308 | -9, // Decimal 44 309 | 62, // Minus sign at decimal 45 310 | -9, // Decimal 46 311 | -9, // Slash at decimal 47 312 | 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine 313 | -9,-9,-9, // Decimal 58 - 60 314 | -1, // Equals sign at decimal 61 315 | -9,-9,-9, // Decimal 62 - 64 316 | 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' 317 | 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' 318 | -9,-9,-9,-9, // Decimal 91 - 94 319 | 63, // Underscore at decimal 95 320 | -9, // Decimal 96 321 | 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' 322 | 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' 323 | -9,-9,-9,-9,-9 // Decimal 123 - 127 324 | ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139 325 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 326 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 327 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 328 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 329 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 330 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 331 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 332 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 333 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 334 | }; 335 | 336 | 337 | 338 | /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ 339 | 340 | /** 341 | * I don't get the point of this technique, but someone requested it, 342 | * and it is described here: 343 | * http://www.faqs.org/qa/rfcc-1940.html. 344 | */ 345 | private final static byte[] _ORDERED_ALPHABET = { 346 | (byte)'-', 347 | (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', 348 | (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', 349 | (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', 350 | (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', 351 | (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 352 | (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', 353 | (byte)'_', 354 | (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', 355 | (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', 356 | (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 357 | (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z' 358 | }; 359 | 360 | /** 361 | * Used in decoding the "ordered" dialect of Base64. 362 | */ 363 | private final static byte[] _ORDERED_DECODABET = { 364 | -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 365 | -5,-5, // Whitespace: Tab and Linefeed 366 | -9,-9, // Decimal 11 - 12 367 | -5, // Whitespace: Carriage Return 368 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 369 | -9,-9,-9,-9,-9, // Decimal 27 - 31 370 | -5, // Whitespace: Space 371 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 372 | -9, // Plus sign at decimal 43 373 | -9, // Decimal 44 374 | 0, // Minus sign at decimal 45 375 | -9, // Decimal 46 376 | -9, // Slash at decimal 47 377 | 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine 378 | -9,-9,-9, // Decimal 58 - 60 379 | -1, // Equals sign at decimal 61 380 | -9,-9,-9, // Decimal 62 - 64 381 | 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M' 382 | 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z' 383 | -9,-9,-9,-9, // Decimal 91 - 94 384 | 37, // Underscore at decimal 95 385 | -9, // Decimal 96 386 | 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm' 387 | 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z' 388 | -9,-9,-9,-9,-9 // Decimal 123 - 127 389 | ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139 390 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 391 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 392 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 393 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 394 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 395 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 396 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 397 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 398 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 399 | }; 400 | 401 | 402 | /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ 403 | 404 | 405 | /** 406 | * Returns one of the _SOMETHING_ALPHABET byte arrays depending on 407 | * the options specified. 408 | * It's possible, though silly, to specify ORDERED and URLSAFE 409 | * in which case one of them will be picked, though there is 410 | * no guarantee as to which one will be picked. 411 | */ 412 | private final static byte[] getAlphabet( int options ) { 413 | if ((options & URL_SAFE) == URL_SAFE) { 414 | return _URL_SAFE_ALPHABET; 415 | } else if ((options & ORDERED) == ORDERED) { 416 | return _ORDERED_ALPHABET; 417 | } else { 418 | return _STANDARD_ALPHABET; 419 | } 420 | } // end getAlphabet 421 | 422 | 423 | /** 424 | * Returns one of the _SOMETHING_DECODABET byte arrays depending on 425 | * the options specified. 426 | * It's possible, though silly, to specify ORDERED and URL_SAFE 427 | * in which case one of them will be picked, though there is 428 | * no guarantee as to which one will be picked. 429 | */ 430 | private final static byte[] getDecodabet( int options ) { 431 | if( (options & URL_SAFE) == URL_SAFE) { 432 | return _URL_SAFE_DECODABET; 433 | } else if ((options & ORDERED) == ORDERED) { 434 | return _ORDERED_DECODABET; 435 | } else { 436 | return _STANDARD_DECODABET; 437 | } 438 | } // end getAlphabet 439 | 440 | 441 | 442 | /** Defeats instantiation. */ 443 | private Base64(){} 444 | 445 | 446 | 447 | 448 | /* ******** E N C O D I N G M E T H O D S ******** */ 449 | 450 | 451 | /** 452 | * Encodes up to the first three bytes of array threeBytes 453 | * and returns a four-byte array in Base64 notation. 454 | * The actual number of significant bytes in your array is 455 | * given by numSigBytes. 456 | * The array threeBytes needs only be as big as 457 | * numSigBytes. 458 | * Code can reuse a byte array by passing a four-byte array as b4. 459 | * 460 | * @param b4 A reusable byte array to reduce array instantiation 461 | * @param threeBytes the array to convert 462 | * @param numSigBytes the number of significant bytes in your array 463 | * @return four byte array in Base64 notation. 464 | * @since 1.5.1 465 | */ 466 | private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) { 467 | encode3to4( threeBytes, 0, numSigBytes, b4, 0, options ); 468 | return b4; 469 | } // end encode3to4 470 | 471 | 472 | /** 473 | *Encodes up to three bytes of the array source 474 | * and writes the resulting four Base64 bytes to destination. 475 | * The source and destination arrays can be manipulated 476 | * anywhere along their length by specifying 477 | * srcOffset and destOffset. 478 | * This method does not check to make sure your arrays 479 | * are large enough to accomodate srcOffset + 3 for 480 | * the source array or destOffset + 4 for 481 | * the destination array. 482 | * The actual number of significant bytes in your array is 483 | * given by numSigBytes.
484 | *This is the lowest level of the encoding methods with 485 | * all possible parameters.
486 | * 487 | * @param source the array to convert 488 | * @param srcOffset the index where conversion begins 489 | * @param numSigBytes the number of significant bytes in your array 490 | * @param destination the array to hold the conversion 491 | * @param destOffset the index where output will be put 492 | * @return the destination array 493 | * @since 1.3 494 | */ 495 | private static byte[] encode3to4( 496 | byte[] source, int srcOffset, int numSigBytes, 497 | byte[] destination, int destOffset, int options ) { 498 | 499 | byte[] ALPHABET = getAlphabet( options ); 500 | 501 | // 1 2 3 502 | // 01234567890123456789012345678901 Bit position 503 | // --------000000001111111122222222 Array position from threeBytes 504 | // --------| || || || | Six bit groups to index ALPHABET 505 | // >>18 >>12 >> 6 >> 0 Right shift necessary 506 | // 0x3f 0x3f 0x3f Additional AND 507 | 508 | // Create buffer with zero-padding if there are only one or two 509 | // significant bytes passed in the array. 510 | // We have to shift left 24 in order to flush out the 1's that appear 511 | // when Java treats a value as negative that is cast from a byte to an int. 512 | int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) 513 | | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) 514 | | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); 515 | 516 | switch( numSigBytes ) 517 | { 518 | case 3: 519 | destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; 520 | destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; 521 | destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; 522 | destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; 523 | return destination; 524 | 525 | case 2: 526 | destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; 527 | destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; 528 | destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; 529 | destination[ destOffset + 3 ] = EQUALS_SIGN; 530 | return destination; 531 | 532 | case 1: 533 | destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; 534 | destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; 535 | destination[ destOffset + 2 ] = EQUALS_SIGN; 536 | destination[ destOffset + 3 ] = EQUALS_SIGN; 537 | return destination; 538 | 539 | default: 540 | return destination; 541 | } // end switch 542 | } // end encode3to4 543 | 544 | 545 | 546 | /** 547 | * Performs Base64 encoding on theraw
ByteBuffer,
548 | * writing it to the encoded
ByteBuffer.
549 | * This is an experimental feature. Currently it does not
550 | * pass along any options (such as {@link #DO_BREAK_LINES}
551 | * or {@link #GZIP}.
552 | *
553 | * @param raw input buffer
554 | * @param encoded output buffer
555 | * @since 2.3
556 | */
557 | public static void encode( java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded ){
558 | byte[] raw3 = new byte[3];
559 | byte[] enc4 = new byte[4];
560 |
561 | while( raw.hasRemaining() ){
562 | int rem = Math.min(3,raw.remaining());
563 | raw.get(raw3,0,rem);
564 | Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS );
565 | encoded.put(enc4);
566 | } // end input remaining
567 | }
568 |
569 |
570 | /**
571 | * Performs Base64 encoding on the raw
ByteBuffer,
572 | * writing it to the encoded
CharBuffer.
573 | * This is an experimental feature. Currently it does not
574 | * pass along any options (such as {@link #DO_BREAK_LINES}
575 | * or {@link #GZIP}.
576 | *
577 | * @param raw input buffer
578 | * @param encoded output buffer
579 | * @since 2.3
580 | */
581 | public static void encode( java.nio.ByteBuffer raw, java.nio.CharBuffer encoded ){
582 | byte[] raw3 = new byte[3];
583 | byte[] enc4 = new byte[4];
584 |
585 | while( raw.hasRemaining() ){
586 | int rem = Math.min(3,raw.remaining());
587 | raw.get(raw3,0,rem);
588 | Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS );
589 | for( int i = 0; i < 4; i++ ){
590 | encoded.put( (char)(enc4[i] & 0xFF) );
591 | }
592 | } // end input remaining
593 | }
594 |
595 |
596 |
597 |
598 | /**
599 | * Serializes an object and returns the Base64-encoded
600 | * version of that serialized object.
601 | *
602 | * As of v 2.3, if the object 603 | * cannot be serialized or there is another error, 604 | * the method will throw an java.io.IOException. This is new to v2.3! 605 | * In earlier versions, it just returned a null value, but 606 | * in retrospect that's a pretty poor way to handle it.
607 | * 608 | * The object is not GZip-compressed before being encoded. 609 | * 610 | * @param serializableObject The object to encode 611 | * @return The Base64-encoded object 612 | * @throws java.io.IOException if there is an error 613 | * @throws NullPointerException if serializedObject is null 614 | * @since 1.4 615 | */ 616 | public static String encodeObject( java.io.Serializable serializableObject ) 617 | throws java.io.IOException { 618 | return encodeObject( serializableObject, NO_OPTIONS ); 619 | } // end encodeObject 620 | 621 | 622 | 623 | /** 624 | * Serializes an object and returns the Base64-encoded 625 | * version of that serialized object. 626 | * 627 | *As of v 2.3, if the object 628 | * cannot be serialized or there is another error, 629 | * the method will throw an java.io.IOException. This is new to v2.3! 630 | * In earlier versions, it just returned a null value, but 631 | * in retrospect that's a pretty poor way to handle it.
632 | * 633 | * The object is not GZip-compressed before being encoded. 634 | *635 | * Example options:
636 | * GZIP: gzip-compresses object before encoding it. 637 | * DO_BREAK_LINES: break lines at 76 characters 638 | *639 | *
640 | * Example: encodeObject( myObj, Base64.GZIP )
or
641 | *
642 | * Example: encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES )
643 | *
644 | * @param serializableObject The object to encode
645 | * @param options Specified options
646 | * @return The Base64-encoded object
647 | * @see Base64#GZIP
648 | * @see Base64#DO_BREAK_LINES
649 | * @throws java.io.IOException if there is an error
650 | * @since 2.0
651 | */
652 | public static String encodeObject( java.io.Serializable serializableObject, int options )
653 | throws java.io.IOException {
654 |
655 | if( serializableObject == null ){
656 | throw new NullPointerException( "Cannot serialize a null object." );
657 | } // end if: null
658 |
659 | // Streams
660 | java.io.ByteArrayOutputStream baos = null;
661 | java.io.OutputStream b64os = null;
662 | java.util.zip.GZIPOutputStream gzos = null;
663 | java.io.ObjectOutputStream oos = null;
664 |
665 |
666 | try {
667 | // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
668 | baos = new java.io.ByteArrayOutputStream();
669 | b64os = new Base64.OutputStream( baos, ENCODE | options );
670 | if( (options & GZIP) != 0 ){
671 | // Gzip
672 | gzos = new java.util.zip.GZIPOutputStream(b64os);
673 | oos = new java.io.ObjectOutputStream( gzos );
674 | } else {
675 | // Not gzipped
676 | oos = new java.io.ObjectOutputStream( b64os );
677 | }
678 | oos.writeObject( serializableObject );
679 | } // end try
680 | catch( java.io.IOException e ) {
681 | // Catch it and then throw it immediately so that
682 | // the finally{} block is called for cleanup.
683 | throw e;
684 | } // end catch
685 | finally {
686 | try{ oos.close(); } catch( Exception e ){}
687 | try{ gzos.close(); } catch( Exception e ){}
688 | try{ b64os.close(); } catch( Exception e ){}
689 | try{ baos.close(); } catch( Exception e ){}
690 | } // end finally
691 |
692 | // Return value according to relevant encoding.
693 | try {
694 | return new String( baos.toByteArray(), PREFERRED_ENCODING );
695 | } // end try
696 | catch (java.io.UnsupportedEncodingException uue){
697 | // Fall back to some Java default
698 | return new String( baos.toByteArray() );
699 | } // end catch
700 |
701 | } // end encode
702 |
703 |
704 |
705 | /**
706 | * Encodes a byte array into Base64 notation.
707 | * Does not GZip-compress data.
708 | *
709 | * @param source The data to convert
710 | * @return The data in Base64-encoded form
711 | * @throws NullPointerException if source array is null
712 | * @since 1.4
713 | */
714 | public static String encodeBytes( byte[] source ) {
715 | // Since we're not going to have the GZIP encoding turned on,
716 | // we're not going to have an java.io.IOException thrown, so
717 | // we should not force the user to have to catch it.
718 | String encoded = null;
719 | try {
720 | encoded = encodeBytes(source, 0, source.length, NO_OPTIONS);
721 | } catch (java.io.IOException ex) {
722 | assert false : ex.getMessage();
723 | } // end catch
724 | assert encoded != null;
725 | return encoded;
726 | } // end encodeBytes
727 |
728 |
729 |
730 | /**
731 | * Encodes a byte array into Base64 notation.
732 | *
733 | * Example options:
734 | * GZIP: gzip-compresses object before encoding it. 735 | * DO_BREAK_LINES: break lines at 76 characters 736 | * Note: Technically, this makes your encoding non-compliant. 737 | *738 | *
739 | * Example: encodeBytes( myData, Base64.GZIP )
or
740 | *
741 | * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )
742 | *
743 | *
744 | *
As of v 2.3, if there is an error with the GZIP stream, 745 | * the method will throw an java.io.IOException. This is new to v2.3! 746 | * In earlier versions, it just returned a null value, but 747 | * in retrospect that's a pretty poor way to handle it.
748 | * 749 | * 750 | * @param source The data to convert 751 | * @param options Specified options 752 | * @return The Base64-encoded data as a String 753 | * @see Base64#GZIP 754 | * @see Base64#DO_BREAK_LINES 755 | * @throws java.io.IOException if there is an error 756 | * @throws NullPointerException if source array is null 757 | * @since 2.0 758 | */ 759 | public static String encodeBytes( byte[] source, int options ) throws java.io.IOException { 760 | return encodeBytes( source, 0, source.length, options ); 761 | } // end encodeBytes 762 | 763 | 764 | /** 765 | * Encodes a byte array into Base64 notation. 766 | * Does not GZip-compress data. 767 | * 768 | *As of v 2.3, if there is an error, 769 | * the method will throw an java.io.IOException. This is new to v2.3! 770 | * In earlier versions, it just returned a null value, but 771 | * in retrospect that's a pretty poor way to handle it.
772 | * 773 | * 774 | * @param source The data to convert 775 | * @param off Offset in array where conversion should begin 776 | * @param len Length of data to convert 777 | * @return The Base64-encoded data as a String 778 | * @throws NullPointerException if source array is null 779 | * @throws IllegalArgumentException if source array, offset, or length are invalid 780 | * @since 1.4 781 | */ 782 | public static String encodeBytes( byte[] source, int off, int len ) { 783 | // Since we're not going to have the GZIP encoding turned on, 784 | // we're not going to have an java.io.IOException thrown, so 785 | // we should not force the user to have to catch it. 786 | String encoded = null; 787 | try { 788 | encoded = encodeBytes( source, off, len, NO_OPTIONS ); 789 | } catch (java.io.IOException ex) { 790 | assert false : ex.getMessage(); 791 | } // end catch 792 | assert encoded != null; 793 | return encoded; 794 | } // end encodeBytes 795 | 796 | 797 | 798 | /** 799 | * Encodes a byte array into Base64 notation. 800 | *801 | * Example options:
802 | * GZIP: gzip-compresses object before encoding it. 803 | * DO_BREAK_LINES: break lines at 76 characters 804 | * Note: Technically, this makes your encoding non-compliant. 805 | *806 | *
807 | * Example: encodeBytes( myData, Base64.GZIP )
or
808 | *
809 | * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )
810 | *
811 | *
812 | *
As of v 2.3, if there is an error with the GZIP stream, 813 | * the method will throw an java.io.IOException. This is new to v2.3! 814 | * In earlier versions, it just returned a null value, but 815 | * in retrospect that's a pretty poor way to handle it.
816 | * 817 | * 818 | * @param source The data to convert 819 | * @param off Offset in array where conversion should begin 820 | * @param len Length of data to convert 821 | * @param options Specified options 822 | * @return The Base64-encoded data as a String 823 | * @see Base64#GZIP 824 | * @see Base64#DO_BREAK_LINES 825 | * @throws java.io.IOException if there is an error 826 | * @throws NullPointerException if source array is null 827 | * @throws IllegalArgumentException if source array, offset, or length are invalid 828 | * @since 2.0 829 | */ 830 | public static String encodeBytes( byte[] source, int off, int len, int options ) throws java.io.IOException { 831 | byte[] encoded = encodeBytesToBytes( source, off, len, options ); 832 | 833 | // Return value according to relevant encoding. 834 | try { 835 | return new String( encoded, PREFERRED_ENCODING ); 836 | } // end try 837 | catch (java.io.UnsupportedEncodingException uue) { 838 | return new String( encoded ); 839 | } // end catch 840 | 841 | } // end encodeBytes 842 | 843 | 844 | 845 | 846 | /** 847 | * Similar to {@link #encodeBytes(byte[])} but returns 848 | * a byte array instead of instantiating a String. This is more efficient 849 | * if you're working with I/O streams and have large data sets to encode. 850 | * 851 | * 852 | * @param source The data to convert 853 | * @return The Base64-encoded data as a byte[] (of ASCII characters) 854 | * @throws NullPointerException if source array is null 855 | * @since 2.3.1 856 | */ 857 | public static byte[] encodeBytesToBytes( byte[] source ) { 858 | byte[] encoded = null; 859 | try { 860 | encoded = encodeBytesToBytes( source, 0, source.length, Base64.NO_OPTIONS ); 861 | } catch( java.io.IOException ex ) { 862 | assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); 863 | } 864 | return encoded; 865 | } 866 | 867 | 868 | /** 869 | * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns 870 | * a byte array instead of instantiating a String. This is more efficient 871 | * if you're working with I/O streams and have large data sets to encode. 872 | * 873 | * 874 | * @param source The data to convert 875 | * @param off Offset in array where conversion should begin 876 | * @param len Length of data to convert 877 | * @param options Specified options 878 | * @return The Base64-encoded data as a String 879 | * @see Base64#GZIP 880 | * @see Base64#DO_BREAK_LINES 881 | * @throws java.io.IOException if there is an error 882 | * @throws NullPointerException if source array is null 883 | * @throws IllegalArgumentException if source array, offset, or length are invalid 884 | * @since 2.3.1 885 | */ 886 | public static byte[] encodeBytesToBytes( byte[] source, int off, int len, int options ) throws java.io.IOException { 887 | 888 | if( source == null ){ 889 | throw new NullPointerException( "Cannot serialize a null array." ); 890 | } // end if: null 891 | 892 | if( off < 0 ){ 893 | throw new IllegalArgumentException( "Cannot have negative offset: " + off ); 894 | } // end if: off < 0 895 | 896 | if( len < 0 ){ 897 | throw new IllegalArgumentException( "Cannot have length offset: " + len ); 898 | } // end if: len < 0 899 | 900 | if( off + len > source.length ){ 901 | throw new IllegalArgumentException( 902 | String.format( "Cannot have offset of %d and length of %d with array of length %d", off,len,source.length)); 903 | } // end if: off < 0 904 | 905 | 906 | 907 | // Compress? 908 | if( (options & GZIP) != 0 ) { 909 | java.io.ByteArrayOutputStream baos = null; 910 | java.util.zip.GZIPOutputStream gzos = null; 911 | Base64.OutputStream b64os = null; 912 | 913 | try { 914 | // GZip -> Base64 -> ByteArray 915 | baos = new java.io.ByteArrayOutputStream(); 916 | b64os = new Base64.OutputStream( baos, ENCODE | options ); 917 | gzos = new java.util.zip.GZIPOutputStream( b64os ); 918 | 919 | gzos.write( source, off, len ); 920 | gzos.close(); 921 | } // end try 922 | catch( java.io.IOException e ) { 923 | // Catch it and then throw it immediately so that 924 | // the finally{} block is called for cleanup. 925 | throw e; 926 | } // end catch 927 | finally { 928 | try{ gzos.close(); } catch( Exception e ){} 929 | try{ b64os.close(); } catch( Exception e ){} 930 | try{ baos.close(); } catch( Exception e ){} 931 | } // end finally 932 | 933 | return baos.toByteArray(); 934 | } // end if: compress 935 | 936 | // Else, don't compress. Better not to use streams at all then. 937 | else { 938 | boolean breakLines = (options & DO_BREAK_LINES) != 0; 939 | 940 | //int len43 = len * 4 / 3; 941 | //byte[] outBuff = new byte[ ( len43 ) // Main 4:3 942 | // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding 943 | // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines 944 | // Try to determine more precisely how big the array needs to be. 945 | // If we get it right, we don't have to do an array copy, and 946 | // we save a bunch of memory. 947 | int encLen = ( len / 3 ) * 4 + ( len % 3 > 0 ? 4 : 0 ); // Bytes needed for actual encoding 948 | if( breakLines ){ 949 | encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters 950 | } 951 | byte[] outBuff = new byte[ encLen ]; 952 | 953 | 954 | int d = 0; 955 | int e = 0; 956 | int len2 = len - 2; 957 | int lineLength = 0; 958 | for( ; d < len2; d+=3, e+=4 ) { 959 | encode3to4( source, d+off, 3, outBuff, e, options ); 960 | 961 | lineLength += 4; 962 | if( breakLines && lineLength >= MAX_LINE_LENGTH ) 963 | { 964 | outBuff[e+4] = NEW_LINE; 965 | e++; 966 | lineLength = 0; 967 | } // end if: end of line 968 | } // en dfor: each piece of array 969 | 970 | if( d < len ) { 971 | encode3to4( source, d+off, len - d, outBuff, e, options ); 972 | e += 4; 973 | } // end if: some padding needed 974 | 975 | 976 | // Only resize array if we didn't guess it right. 977 | if( e <= outBuff.length - 1 ){ 978 | // If breaking lines and the last byte falls right at 979 | // the line length (76 bytes per line), there will be 980 | // one extra byte, and the array will need to be resized. 981 | // Not too bad of an estimate on array size, I'd say. 982 | byte[] finalOut = new byte[e]; 983 | System.arraycopy(outBuff,0, finalOut,0,e); 984 | //System.err.println("Having to resize array from " + outBuff.length + " to " + e ); 985 | return finalOut; 986 | } else { 987 | //System.err.println("No need to resize array."); 988 | return outBuff; 989 | } 990 | 991 | } // end else: don't compress 992 | 993 | } // end encodeBytesToBytes 994 | 995 | 996 | 997 | 998 | 999 | /* ******** D E C O D I N G M E T H O D S ******** */ 1000 | 1001 | 1002 | /** 1003 | * Decodes four bytes from array source 1004 | * and writes the resulting bytes (up to three of them) 1005 | * to destination. 1006 | * The source and destination arrays can be manipulated 1007 | * anywhere along their length by specifying 1008 | * srcOffset and destOffset. 1009 | * This method does not check to make sure your arrays 1010 | * are large enough to accomodate srcOffset + 4 for 1011 | * the source array or destOffset + 3 for 1012 | * the destination array. 1013 | * This method returns the actual number of bytes that 1014 | * were converted from the Base64 encoding. 1015 | *This is the lowest level of the decoding methods with 1016 | * all possible parameters.
1017 | * 1018 | * 1019 | * @param source the array to convert 1020 | * @param srcOffset the index where conversion begins 1021 | * @param destination the array to hold the conversion 1022 | * @param destOffset the index where output will be put 1023 | * @param options alphabet type is pulled from this (standard, url-safe, ordered) 1024 | * @return the number of decoded bytes converted 1025 | * @throws NullPointerException if source or destination arrays are null 1026 | * @throws IllegalArgumentException if srcOffset or destOffset are invalid 1027 | * or there is not enough room in the array. 1028 | * @since 1.3 1029 | */ 1030 | private static int decode4to3( 1031 | byte[] source, int srcOffset, 1032 | byte[] destination, int destOffset, int options ) { 1033 | 1034 | // Lots of error checking and exception throwing 1035 | if( source == null ){ 1036 | throw new NullPointerException( "Source array was null." ); 1037 | } // end if 1038 | if( destination == null ){ 1039 | throw new NullPointerException( "Destination array was null." ); 1040 | } // end if 1041 | if( srcOffset < 0 || srcOffset + 3 >= source.length ){ 1042 | throw new IllegalArgumentException( String.format( 1043 | "Source array with length %d cannot have offset of %d and still process four bytes.", source.length, srcOffset ) ); 1044 | } // end if 1045 | if( destOffset < 0 || destOffset +2 >= destination.length ){ 1046 | throw new IllegalArgumentException( String.format( 1047 | "Destination array with length %d cannot have offset of %d and still store three bytes.", destination.length, destOffset ) ); 1048 | } // end if 1049 | 1050 | 1051 | byte[] DECODABET = getDecodabet( options ); 1052 | 1053 | // Example: Dk== 1054 | if( source[ srcOffset + 2] == EQUALS_SIGN ) { 1055 | // Two ways to do the same thing. Don't know which way I like best. 1056 | //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) 1057 | // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); 1058 | int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) 1059 | | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); 1060 | 1061 | destination[ destOffset ] = (byte)( outBuff >>> 16 ); 1062 | return 1; 1063 | } 1064 | 1065 | // Example: DkL= 1066 | else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) { 1067 | // Two ways to do the same thing. Don't know which way I like best. 1068 | //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) 1069 | // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) 1070 | // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); 1071 | int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) 1072 | | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) 1073 | | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); 1074 | 1075 | destination[ destOffset ] = (byte)( outBuff >>> 16 ); 1076 | destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); 1077 | return 2; 1078 | } 1079 | 1080 | // Example: DkLE 1081 | else { 1082 | // Two ways to do the same thing. Don't know which way I like best. 1083 | //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) 1084 | // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) 1085 | // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) 1086 | // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); 1087 | int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) 1088 | | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) 1089 | | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) 1090 | | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); 1091 | 1092 | 1093 | destination[ destOffset ] = (byte)( outBuff >> 16 ); 1094 | destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); 1095 | destination[ destOffset + 2 ] = (byte)( outBuff ); 1096 | 1097 | return 3; 1098 | } 1099 | } // end decodeToBytes 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | /** 1106 | * Low-level access to decoding ASCII characters in 1107 | * the form of a byte array. Ignores GUNZIP option, if 1108 | * it's set. This is not generally a recommended method, 1109 | * although it is used internally as part of the decoding process. 1110 | * Special case: if len = 0, an empty array is returned. Still, 1111 | * if you need more speed and reduced memory footprint (and aren't 1112 | * gzipping), consider this method. 1113 | * 1114 | * @param source The Base64 encoded data 1115 | * @return decoded data 1116 | * @since 2.3.1 1117 | */ 1118 | public static byte[] decode( byte[] source ) 1119 | throws java.io.IOException { 1120 | byte[] decoded = null; 1121 | // try { 1122 | decoded = decode( source, 0, source.length, Base64.NO_OPTIONS ); 1123 | // } catch( java.io.IOException ex ) { 1124 | // assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); 1125 | // } 1126 | return decoded; 1127 | } 1128 | 1129 | 1130 | 1131 | /** 1132 | * Low-level access to decoding ASCII characters in 1133 | * the form of a byte array. Ignores GUNZIP option, if 1134 | * it's set. This is not generally a recommended method, 1135 | * although it is used internally as part of the decoding process. 1136 | * Special case: if len = 0, an empty array is returned. Still, 1137 | * if you need more speed and reduced memory footprint (and aren't 1138 | * gzipping), consider this method. 1139 | * 1140 | * @param source The Base64 encoded data 1141 | * @param off The offset of where to begin decoding 1142 | * @param len The length of characters to decode 1143 | * @param options Can specify options such as alphabet type to use 1144 | * @return decoded data 1145 | * @throws java.io.IOException If bogus characters exist in source data 1146 | * @since 1.3 1147 | */ 1148 | public static byte[] decode( byte[] source, int off, int len, int options ) 1149 | throws java.io.IOException { 1150 | 1151 | // Lots of error checking and exception throwing 1152 | if( source == null ){ 1153 | throw new NullPointerException( "Cannot decode null source array." ); 1154 | } // end if 1155 | if( off < 0 || off + len > source.length ){ 1156 | throw new IllegalArgumentException( String.format( 1157 | "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, len ) ); 1158 | } // end if 1159 | 1160 | if( len == 0 ){ 1161 | return new byte[0]; 1162 | }else if( len < 4 ){ 1163 | throw new IllegalArgumentException( 1164 | "Base64-encoded string must have at least four characters, but length specified was " + len ); 1165 | } // end if 1166 | 1167 | byte[] DECODABET = getDecodabet( options ); 1168 | 1169 | int len34 = len * 3 / 4; // Estimate on array size 1170 | byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output 1171 | int outBuffPosn = 0; // Keep track of where we're writing 1172 | 1173 | byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space 1174 | int b4Posn = 0; // Keep track of four byte input buffer 1175 | int i = 0; // Source array counter 1176 | byte sbiDecode = 0; // Special value from DECODABET 1177 | 1178 | for( i = off; i < off+len; i++ ) { // Loop through source 1179 | 1180 | sbiDecode = DECODABET[ source[i]&0xFF ]; 1181 | 1182 | // White space, Equals sign, or legit Base64 character 1183 | // Note the values such as -5 and -9 in the 1184 | // DECODABETs at the top of the file. 1185 | if( sbiDecode >= WHITE_SPACE_ENC ) { 1186 | if( sbiDecode >= EQUALS_SIGN_ENC ) { 1187 | b4[ b4Posn++ ] = source[i]; // Save non-whitespace 1188 | if( b4Posn > 3 ) { // Time to decode? 1189 | outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); 1190 | b4Posn = 0; 1191 | 1192 | // If that was the equals sign, break out of 'for' loop 1193 | if( source[i] == EQUALS_SIGN ) { 1194 | break; 1195 | } // end if: equals sign 1196 | } // end if: quartet built 1197 | } // end if: equals sign or better 1198 | } // end if: white space, equals sign or better 1199 | else { 1200 | // There's a bad input character in the Base64 stream. 1201 | throw new java.io.IOException( String.format( 1202 | "Bad Base64 input character decimal %d in array position %d", ((int)source[i])&0xFF, i ) ); 1203 | } // end else: 1204 | } // each input character 1205 | 1206 | byte[] out = new byte[ outBuffPosn ]; 1207 | System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); 1208 | return out; 1209 | } // end decode 1210 | 1211 | 1212 | 1213 | 1214 | /** 1215 | * Decodes data from Base64 notation, automatically 1216 | * detecting gzip-compressed data and decompressing it. 1217 | * 1218 | * @param s the string to decode 1219 | * @return the decoded data 1220 | * @throws java.io.IOException If there is a problem 1221 | * @since 1.4 1222 | */ 1223 | public static byte[] decode( String s ) throws java.io.IOException { 1224 | return decode( s, NO_OPTIONS ); 1225 | } 1226 | 1227 | 1228 | 1229 | /** 1230 | * Decodes data from Base64 notation, automatically 1231 | * detecting gzip-compressed data and decompressing it. 1232 | * 1233 | * @param s the string to decode 1234 | * @param options encode options such as URL_SAFE 1235 | * @return the decoded data 1236 | * @throws java.io.IOException if there is an error 1237 | * @throws NullPointerException if s is null 1238 | * @since 1.4 1239 | */ 1240 | public static byte[] decode( String s, int options ) throws java.io.IOException { 1241 | 1242 | if( s == null ){ 1243 | throw new NullPointerException( "Input string was null." ); 1244 | } // end if 1245 | 1246 | byte[] bytes; 1247 | try { 1248 | bytes = s.getBytes( PREFERRED_ENCODING ); 1249 | } // end try 1250 | catch( java.io.UnsupportedEncodingException uee ) { 1251 | bytes = s.getBytes(); 1252 | } // end catch 1253 | // 1254 | 1255 | // Decode 1256 | bytes = decode( bytes, 0, bytes.length, options ); 1257 | 1258 | // Check to see if it's gzip-compressed 1259 | // GZIP Magic Two-Byte Number: 0x8b1f (35615) 1260 | boolean dontGunzip = (options & DONT_GUNZIP) != 0; 1261 | if( (bytes != null) && (bytes.length >= 4) && (!dontGunzip) ) { 1262 | 1263 | int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); 1264 | if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) { 1265 | java.io.ByteArrayInputStream bais = null; 1266 | java.util.zip.GZIPInputStream gzis = null; 1267 | java.io.ByteArrayOutputStream baos = null; 1268 | byte[] buffer = new byte[2048]; 1269 | int length = 0; 1270 | 1271 | try { 1272 | baos = new java.io.ByteArrayOutputStream(); 1273 | bais = new java.io.ByteArrayInputStream( bytes ); 1274 | gzis = new java.util.zip.GZIPInputStream( bais ); 1275 | 1276 | while( ( length = gzis.read( buffer ) ) >= 0 ) { 1277 | baos.write(buffer,0,length); 1278 | } // end while: reading input 1279 | 1280 | // No error? Get new bytes. 1281 | bytes = baos.toByteArray(); 1282 | 1283 | } // end try 1284 | catch( java.io.IOException e ) { 1285 | e.printStackTrace(); 1286 | // Just return originally-decoded bytes 1287 | } // end catch 1288 | finally { 1289 | try{ baos.close(); } catch( Exception e ){} 1290 | try{ gzis.close(); } catch( Exception e ){} 1291 | try{ bais.close(); } catch( Exception e ){} 1292 | } // end finally 1293 | 1294 | } // end if: gzipped 1295 | } // end if: bytes.length >= 2 1296 | 1297 | return bytes; 1298 | } // end decode 1299 | 1300 | 1301 | 1302 | /** 1303 | * Attempts to decode Base64 data and deserialize a Java 1304 | * Object within. Returns null if there was an error. 1305 | * 1306 | * @param encodedObject The Base64 data to decode 1307 | * @return The decoded and deserialized object 1308 | * @throws NullPointerException if encodedObject is null 1309 | * @throws java.io.IOException if there is a general error 1310 | * @throws ClassNotFoundException if the decoded object is of a 1311 | * class that cannot be found by the JVM 1312 | * @since 1.5 1313 | */ 1314 | public static Object decodeToObject( String encodedObject ) 1315 | throws java.io.IOException, java.lang.ClassNotFoundException { 1316 | return decodeToObject(encodedObject,NO_OPTIONS,null); 1317 | } 1318 | 1319 | 1320 | /** 1321 | * Attempts to decode Base64 data and deserialize a Java 1322 | * Object within. Returns null if there was an error. 1323 | * If loader is not null, it will be the class loader 1324 | * used when deserializing. 1325 | * 1326 | * @param encodedObject The Base64 data to decode 1327 | * @param options Various parameters related to decoding 1328 | * @param loader Optional class loader to use in deserializing classes. 1329 | * @return The decoded and deserialized object 1330 | * @throws NullPointerException if encodedObject is null 1331 | * @throws java.io.IOException if there is a general error 1332 | * @throws ClassNotFoundException if the decoded object is of a 1333 | * class that cannot be found by the JVM 1334 | * @since 2.3.4 1335 | */ 1336 | public static Object decodeToObject( 1337 | String encodedObject, int options, final ClassLoader loader ) 1338 | throws java.io.IOException, java.lang.ClassNotFoundException { 1339 | 1340 | // Decode and gunzip if necessary 1341 | byte[] objBytes = decode( encodedObject, options ); 1342 | 1343 | java.io.ByteArrayInputStream bais = null; 1344 | java.io.ObjectInputStream ois = null; 1345 | Object obj = null; 1346 | 1347 | try { 1348 | bais = new java.io.ByteArrayInputStream( objBytes ); 1349 | 1350 | // If no custom class loader is provided, use Java's builtin OIS. 1351 | if( loader == null ){ 1352 | ois = new java.io.ObjectInputStream( bais ); 1353 | } // end if: no loader provided 1354 | 1355 | // Else make a customized object input stream that uses 1356 | // the provided class loader. 1357 | else { 1358 | ois = new java.io.ObjectInputStream(bais){ 1359 | @Override 1360 | public Class> resolveClass(java.io.ObjectStreamClass streamClass) 1361 | throws java.io.IOException, ClassNotFoundException { 1362 | Class c = Class.forName(streamClass.getName(), false, loader); 1363 | if( c == null ){ 1364 | return super.resolveClass(streamClass); 1365 | } else { 1366 | return c; // Class loader knows of this class. 1367 | } // end else: not null 1368 | } // end resolveClass 1369 | }; // end ois 1370 | } // end else: no custom class loader 1371 | 1372 | obj = ois.readObject(); 1373 | } // end try 1374 | catch( java.io.IOException e ) { 1375 | throw e; // Catch and throw in order to execute finally{} 1376 | } // end catch 1377 | catch( java.lang.ClassNotFoundException e ) { 1378 | throw e; // Catch and throw in order to execute finally{} 1379 | } // end catch 1380 | finally { 1381 | try{ bais.close(); } catch( Exception e ){} 1382 | try{ ois.close(); } catch( Exception e ){} 1383 | } // end finally 1384 | 1385 | return obj; 1386 | } // end decodeObject 1387 | 1388 | 1389 | 1390 | /** 1391 | * Convenience method for encoding data to a file. 1392 | * 1393 | *As of v 2.3, if there is a error, 1394 | * the method will throw an java.io.IOException. This is new to v2.3! 1395 | * In earlier versions, it just returned false, but 1396 | * in retrospect that's a pretty poor way to handle it.
1397 | * 1398 | * @param dataToEncode byte array of data to encode in base64 form 1399 | * @param filename Filename for saving encoded data 1400 | * @throws java.io.IOException if there is an error 1401 | * @throws NullPointerException if dataToEncode is null 1402 | * @since 2.1 1403 | */ 1404 | public static void encodeToFile( byte[] dataToEncode, String filename ) 1405 | throws java.io.IOException { 1406 | 1407 | if( dataToEncode == null ){ 1408 | throw new NullPointerException( "Data to encode was null." ); 1409 | } // end iff 1410 | 1411 | Base64.OutputStream bos = null; 1412 | try { 1413 | bos = new Base64.OutputStream( 1414 | new java.io.FileOutputStream( filename ), Base64.ENCODE ); 1415 | bos.write( dataToEncode ); 1416 | } // end try 1417 | catch( java.io.IOException e ) { 1418 | throw e; // Catch and throw to execute finally{} block 1419 | } // end catch: java.io.IOException 1420 | finally { 1421 | try{ bos.close(); } catch( Exception e ){} 1422 | } // end finally 1423 | 1424 | } // end encodeToFile 1425 | 1426 | 1427 | /** 1428 | * Convenience method for decoding data to a file. 1429 | * 1430 | *As of v 2.3, if there is a error, 1431 | * the method will throw an java.io.IOException. This is new to v2.3! 1432 | * In earlier versions, it just returned false, but 1433 | * in retrospect that's a pretty poor way to handle it.
1434 | * 1435 | * @param dataToDecode Base64-encoded data as a string 1436 | * @param filename Filename for saving decoded data 1437 | * @throws java.io.IOException if there is an error 1438 | * @since 2.1 1439 | */ 1440 | public static void decodeToFile( String dataToDecode, String filename ) 1441 | throws java.io.IOException { 1442 | 1443 | Base64.OutputStream bos = null; 1444 | try{ 1445 | bos = new Base64.OutputStream( 1446 | new java.io.FileOutputStream( filename ), Base64.DECODE ); 1447 | bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); 1448 | } // end try 1449 | catch( java.io.IOException e ) { 1450 | throw e; // Catch and throw to execute finally{} block 1451 | } // end catch: java.io.IOException 1452 | finally { 1453 | try{ bos.close(); } catch( Exception e ){} 1454 | } // end finally 1455 | 1456 | } // end decodeToFile 1457 | 1458 | 1459 | 1460 | 1461 | /** 1462 | * Convenience method for reading a base64-encoded 1463 | * file and decoding it. 1464 | * 1465 | *As of v 2.3, if there is a error, 1466 | * the method will throw an java.io.IOException. This is new to v2.3! 1467 | * In earlier versions, it just returned false, but 1468 | * in retrospect that's a pretty poor way to handle it.
1469 | * 1470 | * @param filename Filename for reading encoded data 1471 | * @return decoded byte array 1472 | * @throws java.io.IOException if there is an error 1473 | * @since 2.1 1474 | */ 1475 | public static byte[] decodeFromFile( String filename ) 1476 | throws java.io.IOException { 1477 | 1478 | byte[] decodedData = null; 1479 | Base64.InputStream bis = null; 1480 | try 1481 | { 1482 | // Set up some useful variables 1483 | java.io.File file = new java.io.File( filename ); 1484 | byte[] buffer = null; 1485 | int length = 0; 1486 | int numBytes = 0; 1487 | 1488 | // Check for size of file 1489 | if( file.length() > Integer.MAX_VALUE ) 1490 | { 1491 | throw new java.io.IOException( "File is too big for this convenience method (" + file.length() + " bytes)." ); 1492 | } // end if: file too big for int index 1493 | buffer = new byte[ (int)file.length() ]; 1494 | 1495 | // Open a stream 1496 | bis = new Base64.InputStream( 1497 | new java.io.BufferedInputStream( 1498 | new java.io.FileInputStream( file ) ), Base64.DECODE ); 1499 | 1500 | // Read until done 1501 | while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) { 1502 | length += numBytes; 1503 | } // end while 1504 | 1505 | // Save in a variable to return 1506 | decodedData = new byte[ length ]; 1507 | System.arraycopy( buffer, 0, decodedData, 0, length ); 1508 | 1509 | } // end try 1510 | catch( java.io.IOException e ) { 1511 | throw e; // Catch and release to execute finally{} 1512 | } // end catch: java.io.IOException 1513 | finally { 1514 | try{ bis.close(); } catch( Exception e) {} 1515 | } // end finally 1516 | 1517 | return decodedData; 1518 | } // end decodeFromFile 1519 | 1520 | 1521 | 1522 | /** 1523 | * Convenience method for reading a binary file 1524 | * and base64-encoding it. 1525 | * 1526 | *As of v 2.3, if there is a error, 1527 | * the method will throw an java.io.IOException. This is new to v2.3! 1528 | * In earlier versions, it just returned false, but 1529 | * in retrospect that's a pretty poor way to handle it.
1530 | * 1531 | * @param filename Filename for reading binary data 1532 | * @return base64-encoded string 1533 | * @throws java.io.IOException if there is an error 1534 | * @since 2.1 1535 | */ 1536 | public static String encodeFromFile( String filename ) 1537 | throws java.io.IOException { 1538 | 1539 | String encodedData = null; 1540 | Base64.InputStream bis = null; 1541 | try 1542 | { 1543 | // Set up some useful variables 1544 | java.io.File file = new java.io.File( filename ); 1545 | byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4+1),40) ]; // Need max() for math on small files (v2.2.1); Need +1 for a few corner cases (v2.3.5) 1546 | int length = 0; 1547 | int numBytes = 0; 1548 | 1549 | // Open a stream 1550 | bis = new Base64.InputStream( 1551 | new java.io.BufferedInputStream( 1552 | new java.io.FileInputStream( file ) ), Base64.ENCODE ); 1553 | 1554 | // Read until done 1555 | while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) { 1556 | length += numBytes; 1557 | } // end while 1558 | 1559 | // Save in a variable to return 1560 | encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); 1561 | 1562 | } // end try 1563 | catch( java.io.IOException e ) { 1564 | throw e; // Catch and release to execute finally{} 1565 | } // end catch: java.io.IOException 1566 | finally { 1567 | try{ bis.close(); } catch( Exception e) {} 1568 | } // end finally 1569 | 1570 | return encodedData; 1571 | } // end encodeFromFile 1572 | 1573 | /** 1574 | * Reads infile and encodes it to outfile. 1575 | * 1576 | * @param infile Input file 1577 | * @param outfile Output file 1578 | * @throws java.io.IOException if there is an error 1579 | * @since 2.2 1580 | */ 1581 | public static void encodeFileToFile( String infile, String outfile ) 1582 | throws java.io.IOException { 1583 | 1584 | String encoded = Base64.encodeFromFile( infile ); 1585 | java.io.OutputStream out = null; 1586 | try{ 1587 | out = new java.io.BufferedOutputStream( 1588 | new java.io.FileOutputStream( outfile ) ); 1589 | out.write( encoded.getBytes("US-ASCII") ); // Strict, 7-bit output. 1590 | } // end try 1591 | catch( java.io.IOException e ) { 1592 | throw e; // Catch and release to execute finally{} 1593 | } // end catch 1594 | finally { 1595 | try { out.close(); } 1596 | catch( Exception ex ){} 1597 | } // end finally 1598 | } // end encodeFileToFile 1599 | 1600 | 1601 | /** 1602 | * Reads infile and decodes it to outfile. 1603 | * 1604 | * @param infile Input file 1605 | * @param outfile Output file 1606 | * @throws java.io.IOException if there is an error 1607 | * @since 2.2 1608 | */ 1609 | public static void decodeFileToFile( String infile, String outfile ) 1610 | throws java.io.IOException { 1611 | 1612 | byte[] decoded = Base64.decodeFromFile( infile ); 1613 | java.io.OutputStream out = null; 1614 | try{ 1615 | out = new java.io.BufferedOutputStream( 1616 | new java.io.FileOutputStream( outfile ) ); 1617 | out.write( decoded ); 1618 | } // end try 1619 | catch( java.io.IOException e ) { 1620 | throw e; // Catch and release to execute finally{} 1621 | } // end catch 1622 | finally { 1623 | try { out.close(); } 1624 | catch( Exception ex ){} 1625 | } // end finally 1626 | } // end decodeFileToFile 1627 | 1628 | 1629 | /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ 1630 | 1631 | 1632 | 1633 | /** 1634 | * A {@link Base64.InputStream} will read data from another 1635 | * java.io.InputStream, given in the constructor, 1636 | * and encode/decode to/from Base64 notation on the fly. 1637 | * 1638 | * @see Base64 1639 | * @since 1.3 1640 | */ 1641 | public static class InputStream extends java.io.FilterInputStream { 1642 | 1643 | private boolean encode; // Encoding or decoding 1644 | private int position; // Current position in the buffer 1645 | private byte[] buffer; // Small buffer holding converted data 1646 | private int bufferLength; // Length of buffer (3 or 4) 1647 | private int numSigBytes; // Number of meaningful bytes in the buffer 1648 | private int lineLength; 1649 | private boolean breakLines; // Break lines at less than 80 characters 1650 | private int options; // Record options used to create the stream. 1651 | private byte[] decodabet; // Local copies to avoid extra method calls 1652 | 1653 | 1654 | /** 1655 | * Constructs a {@link Base64.InputStream} in DECODE mode. 1656 | * 1657 | * @param in the java.io.InputStream from which to read data. 1658 | * @since 1.3 1659 | */ 1660 | public InputStream( java.io.InputStream in ) { 1661 | this( in, DECODE ); 1662 | } // end constructor 1663 | 1664 | 1665 | /** 1666 | * Constructs a {@link Base64.InputStream} in 1667 | * either ENCODE or DECODE mode. 1668 | *1669 | * Valid options:
1670 | * ENCODE or DECODE: Encode or Decode as data is read. 1671 | * DO_BREAK_LINES: break lines at 76 characters 1672 | * (only meaningful when encoding) 1673 | *1674 | *
1675 | * Example: new Base64.InputStream( in, Base64.DECODE )
1676 | *
1677 | *
1678 | * @param in the java.io.InputStream from which to read data.
1679 | * @param options Specified options
1680 | * @see Base64#ENCODE
1681 | * @see Base64#DECODE
1682 | * @see Base64#DO_BREAK_LINES
1683 | * @since 2.0
1684 | */
1685 | public InputStream( java.io.InputStream in, int options ) {
1686 |
1687 | super( in );
1688 | this.options = options; // Record for later
1689 | this.breakLines = (options & DO_BREAK_LINES) > 0;
1690 | this.encode = (options & ENCODE) > 0;
1691 | this.bufferLength = encode ? 4 : 3;
1692 | this.buffer = new byte[ bufferLength ];
1693 | this.position = -1;
1694 | this.lineLength = 0;
1695 | this.decodabet = getDecodabet(options);
1696 | } // end constructor
1697 |
1698 | /**
1699 | * Reads enough of the input stream to convert
1700 | * to/from Base64 and returns the next byte.
1701 | *
1702 | * @return next byte
1703 | * @since 1.3
1704 | */
1705 | @Override
1706 | public int read() throws java.io.IOException {
1707 |
1708 | // Do we need to get data?
1709 | if( position < 0 ) {
1710 | if( encode ) {
1711 | byte[] b3 = new byte[3];
1712 | int numBinaryBytes = 0;
1713 | for( int i = 0; i < 3; i++ ) {
1714 | int b = in.read();
1715 |
1716 | // If end of stream, b is -1.
1717 | if( b >= 0 ) {
1718 | b3[i] = (byte)b;
1719 | numBinaryBytes++;
1720 | } else {
1721 | break; // out of for loop
1722 | } // end else: end of stream
1723 |
1724 | } // end for: each needed input byte
1725 |
1726 | if( numBinaryBytes > 0 ) {
1727 | encode3to4( b3, 0, numBinaryBytes, buffer, 0, options );
1728 | position = 0;
1729 | numSigBytes = 4;
1730 | } // end if: got data
1731 | else {
1732 | return -1; // Must be end of stream
1733 | } // end else
1734 | } // end if: encoding
1735 |
1736 | // Else decoding
1737 | else {
1738 | byte[] b4 = new byte[4];
1739 | int i = 0;
1740 | for( i = 0; i < 4; i++ ) {
1741 | // Read four "meaningful" bytes:
1742 | int b = 0;
1743 | do{ b = in.read(); }
1744 | while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC );
1745 |
1746 | if( b < 0 ) {
1747 | break; // Reads a -1 if end of stream
1748 | } // end if: end of stream
1749 |
1750 | b4[i] = (byte)b;
1751 | } // end for: each needed input byte
1752 |
1753 | if( i == 4 ) {
1754 | numSigBytes = decode4to3( b4, 0, buffer, 0, options );
1755 | position = 0;
1756 | } // end if: got four characters
1757 | else if( i == 0 ){
1758 | return -1;
1759 | } // end else if: also padded correctly
1760 | else {
1761 | // Must have broken out from above.
1762 | throw new java.io.IOException( "Improperly padded Base64 input." );
1763 | } // end
1764 |
1765 | } // end else: decode
1766 | } // end else: get data
1767 |
1768 | // Got data?
1769 | if( position >= 0 ) {
1770 | // End of relevant data?
1771 | if( /*!encode &&*/ position >= numSigBytes ){
1772 | return -1;
1773 | } // end if: got data
1774 |
1775 | if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) {
1776 | lineLength = 0;
1777 | return '\n';
1778 | } // end if
1779 | else {
1780 | lineLength++; // This isn't important when decoding
1781 | // but throwing an extra "if" seems
1782 | // just as wasteful.
1783 |
1784 | int b = buffer[ position++ ];
1785 |
1786 | if( position >= bufferLength ) {
1787 | position = -1;
1788 | } // end if: end
1789 |
1790 | return b & 0xFF; // This is how you "cast" a byte that's
1791 | // intended to be unsigned.
1792 | } // end else
1793 | } // end if: position >= 0
1794 |
1795 | // Else error
1796 | else {
1797 | throw new java.io.IOException( "Error in Base64 code reading stream." );
1798 | } // end else
1799 | } // end read
1800 |
1801 |
1802 | /**
1803 | * Calls {@link #read()} repeatedly until the end of stream
1804 | * is reached or len bytes are read.
1805 | * Returns number of bytes read into array or -1 if
1806 | * end of stream is encountered.
1807 | *
1808 | * @param dest array to hold values
1809 | * @param off offset for array
1810 | * @param len max number of bytes to read into array
1811 | * @return bytes read into array or -1 if end of stream is encountered.
1812 | * @since 1.3
1813 | */
1814 | @Override
1815 | public int read( byte[] dest, int off, int len )
1816 | throws java.io.IOException {
1817 | int i;
1818 | int b;
1819 | for( i = 0; i < len; i++ ) {
1820 | b = read();
1821 |
1822 | if( b >= 0 ) {
1823 | dest[off + i] = (byte) b;
1824 | }
1825 | else if( i == 0 ) {
1826 | return -1;
1827 | }
1828 | else {
1829 | break; // Out of 'for' loop
1830 | } // Out of 'for' loop
1831 | } // end for: each byte read
1832 | return i;
1833 | } // end read
1834 |
1835 | } // end inner class InputStream
1836 |
1837 |
1838 |
1839 |
1840 |
1841 |
1842 | /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1843 |
1844 |
1845 |
1846 | /**
1847 | * A {@link Base64.OutputStream} will write data to another
1848 | * java.io.OutputStream, given in the constructor,
1849 | * and encode/decode to/from Base64 notation on the fly.
1850 | *
1851 | * @see Base64
1852 | * @since 1.3
1853 | */
1854 | public static class OutputStream extends java.io.FilterOutputStream {
1855 |
1856 | private boolean encode;
1857 | private int position;
1858 | private byte[] buffer;
1859 | private int bufferLength;
1860 | private int lineLength;
1861 | private boolean breakLines;
1862 | private byte[] b4; // Scratch used in a few places
1863 | private boolean suspendEncoding;
1864 | private int options; // Record for later
1865 | private byte[] decodabet; // Local copies to avoid extra method calls
1866 |
1867 | /**
1868 | * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1869 | *
1870 | * @param out the java.io.OutputStream to which data will be written.
1871 | * @since 1.3
1872 | */
1873 | public OutputStream( java.io.OutputStream out ) {
1874 | this( out, ENCODE );
1875 | } // end constructor
1876 |
1877 |
1878 | /**
1879 | * Constructs a {@link Base64.OutputStream} in
1880 | * either ENCODE or DECODE mode.
1881 | *
1882 | * Valid options:
1883 | * ENCODE or DECODE: Encode or Decode as data is read. 1884 | * DO_BREAK_LINES: don't break lines at 76 characters 1885 | * (only meaningful when encoding) 1886 | *1887 | *
1888 | * Example: new Base64.OutputStream( out, Base64.ENCODE )
1889 | *
1890 | * @param out the java.io.OutputStream to which data will be written.
1891 | * @param options Specified options.
1892 | * @see Base64#ENCODE
1893 | * @see Base64#DECODE
1894 | * @see Base64#DO_BREAK_LINES
1895 | * @since 1.3
1896 | */
1897 | public OutputStream( java.io.OutputStream out, int options ) {
1898 | super( out );
1899 | this.breakLines = (options & DO_BREAK_LINES) != 0;
1900 | this.encode = (options & ENCODE) != 0;
1901 | this.bufferLength = encode ? 3 : 4;
1902 | this.buffer = new byte[ bufferLength ];
1903 | this.position = 0;
1904 | this.lineLength = 0;
1905 | this.suspendEncoding = false;
1906 | this.b4 = new byte[4];
1907 | this.options = options;
1908 | this.decodabet = getDecodabet(options);
1909 | } // end constructor
1910 |
1911 |
1912 | /**
1913 | * Writes the byte to the output stream after
1914 | * converting to/from Base64 notation.
1915 | * When encoding, bytes are buffered three
1916 | * at a time before the output stream actually
1917 | * gets a write() call.
1918 | * When decoding, bytes are buffered four
1919 | * at a time.
1920 | *
1921 | * @param theByte the byte to write
1922 | * @since 1.3
1923 | */
1924 | @Override
1925 | public void write(int theByte)
1926 | throws java.io.IOException {
1927 | // Encoding suspended?
1928 | if( suspendEncoding ) {
1929 | this.out.write( theByte );
1930 | return;
1931 | } // end if: supsended
1932 |
1933 | // Encode?
1934 | if( encode ) {
1935 | buffer[ position++ ] = (byte)theByte;
1936 | if( position >= bufferLength ) { // Enough to encode.
1937 |
1938 | this.out.write( encode3to4( b4, buffer, bufferLength, options ) );
1939 |
1940 | lineLength += 4;
1941 | if( breakLines && lineLength >= MAX_LINE_LENGTH ) {
1942 | this.out.write( NEW_LINE );
1943 | lineLength = 0;
1944 | } // end if: end of line
1945 |
1946 | position = 0;
1947 | } // end if: enough to output
1948 | } // end if: encoding
1949 |
1950 | // Else, Decoding
1951 | else {
1952 | // Meaningful Base64 character?
1953 | if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) {
1954 | buffer[ position++ ] = (byte)theByte;
1955 | if( position >= bufferLength ) { // Enough to output.
1956 |
1957 | int len = Base64.decode4to3( buffer, 0, b4, 0, options );
1958 | out.write( b4, 0, len );
1959 | position = 0;
1960 | } // end if: enough to output
1961 | } // end if: meaningful base64 character
1962 | else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) {
1963 | throw new java.io.IOException( "Invalid character in Base64 data." );
1964 | } // end else: not white space either
1965 | } // end else: decoding
1966 | } // end write
1967 |
1968 |
1969 |
1970 | /**
1971 | * Calls {@link #write(int)} repeatedly until len
1972 | * bytes are written.
1973 | *
1974 | * @param theBytes array from which to read bytes
1975 | * @param off offset for array
1976 | * @param len max number of bytes to read into array
1977 | * @since 1.3
1978 | */
1979 | @Override
1980 | public void write( byte[] theBytes, int off, int len )
1981 | throws java.io.IOException {
1982 | // Encoding suspended?
1983 | if( suspendEncoding ) {
1984 | this.out.write( theBytes, off, len );
1985 | return;
1986 | } // end if: supsended
1987 |
1988 | for( int i = 0; i < len; i++ ) {
1989 | write( theBytes[ off + i ] );
1990 | } // end for: each byte written
1991 |
1992 | } // end write
1993 |
1994 |
1995 |
1996 | /**
1997 | * Method added by PHIL. [Thanks, PHIL. -Rob]
1998 | * This pads the buffer without closing the stream.
1999 | * @throws java.io.IOException if there's an error.
2000 | */
2001 | public void flushBase64() throws java.io.IOException {
2002 | if( position > 0 ) {
2003 | if( encode ) {
2004 | out.write( encode3to4( b4, buffer, position, options ) );
2005 | position = 0;
2006 | } // end if: encoding
2007 | else {
2008 | throw new java.io.IOException( "Base64 input not properly padded." );
2009 | } // end else: decoding
2010 | } // end if: buffer partially full
2011 |
2012 | } // end flush
2013 |
2014 |
2015 | /**
2016 | * Flushes and closes (I think, in the superclass) the stream.
2017 | *
2018 | * @since 1.3
2019 | */
2020 | @Override
2021 | public void close() throws java.io.IOException {
2022 | // 1. Ensure that pending characters are written
2023 | flushBase64();
2024 |
2025 | // 2. Actually close the stream
2026 | // Base class both flushes and closes.
2027 | super.close();
2028 |
2029 | buffer = null;
2030 | out = null;
2031 | } // end close
2032 |
2033 |
2034 |
2035 | /**
2036 | * Suspends encoding of the stream.
2037 | * May be helpful if you need to embed a piece of
2038 | * base64-encoded data in a stream.
2039 | *
2040 | * @throws java.io.IOException if there's an error flushing
2041 | * @since 1.5.1
2042 | */
2043 | public void suspendEncoding() throws java.io.IOException {
2044 | flushBase64();
2045 | this.suspendEncoding = true;
2046 | } // end suspendEncoding
2047 |
2048 |
2049 | /**
2050 | * Resumes encoding of the stream.
2051 | * May be helpful if you need to embed a piece of
2052 | * base64-encoded data in a stream.
2053 | *
2054 | * @since 1.5.1
2055 | */
2056 | public void resumeEncoding() {
2057 | this.suspendEncoding = false;
2058 | } // end resumeEncoding
2059 |
2060 |
2061 |
2062 | } // end inner class OutputStream
2063 |
2064 |
2065 | } // end class Base64
2066 |
--------------------------------------------------------------------------------
/src/com/strumsoft/websocket/phonegap/WebSocket.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2010 Nathan Rajlich (https://github.com/TooTallNate)
3 | * Copyright (c) 2010 Animesh Kumar (https://github.com/anismiles)
4 | * Copyright (c) 2010 Strumsoft (https://strumsoft.com)
5 | *
6 | * Permission is hereby granted, free of charge, to any person
7 | * obtaining a copy of this software and associated documentation
8 | * files (the "Software"), to deal in the Software without
9 | * restriction, including without limitation the rights to use,
10 | * copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the
12 | * Software is furnished to do so, subject to the following
13 | * conditions:
14 | *
15 | * The above copyright notice and this permission notice shall be
16 | * included in all copies or substantial portions of the Software.
17 | *
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 | * OTHER DEALINGS IN THE SOFTWARE.
26 | */
27 | package com.strumsoft.websocket.phonegap;
28 |
29 | import java.io.IOException;
30 | import java.io.UnsupportedEncodingException;
31 | import java.net.InetSocketAddress;
32 | import java.net.URI;
33 | import java.nio.ByteBuffer;
34 | import java.nio.channels.NotYetConnectedException;
35 | import java.nio.channels.SelectionKey;
36 | import java.nio.channels.Selector;
37 | import java.nio.channels.SocketChannel;
38 | import java.security.MessageDigest;
39 | import java.security.NoSuchAlgorithmException;
40 | import java.util.Iterator;
41 | import java.util.Random;
42 | import java.util.Set;
43 | import java.util.concurrent.BlockingQueue;
44 | import java.util.concurrent.LinkedBlockingQueue;
45 |
46 | import android.app.Activity;
47 | import android.content.Context;
48 | import android.os.Handler;
49 | import android.os.Message;
50 | import android.util.Log;
51 | import android.view.inputmethod.InputMethodManager;
52 | import android.webkit.WebView;
53 |
54 | /**
55 | * The WebSocket is an implementation of WebSocket Client API, and
56 | * expects a valid "ws://" URI to connect to. When connected, an instance
57 | * recieves important events related to the life of the connection, like
58 | * onOpen, onClose, onError and
59 | * onMessage. An instance can send messages to the server via the
60 | * send method.
61 | *
62 | * @author Animesh Kumar
63 | */
64 | public class WebSocket implements Runnable {
65 |
66 | /**
67 | * Enum for WebSocket Draft
68 | */
69 | public enum Draft {
70 | DRAFT75, DRAFT76
71 | }
72 |
73 | // //////////////// CONSTANT
74 | /**
75 | * The connection has not yet been established.
76 | */
77 | public final static int WEBSOCKET_STATE_CONNECTING = 0;
78 | /**
79 | * The WebSocket connection is established and communication is possible.
80 | */
81 | public final static int WEBSOCKET_STATE_OPEN = 1;
82 | /**
83 | * The connection is going through the closing handshake.
84 | */
85 | public final static int WEBSOCKET_STATE_CLOSING = 2;
86 | /**
87 | * The connection has been closed or could not be opened.
88 | */
89 | public final static int WEBSOCKET_STATE_CLOSED = 3;
90 |
91 | /**
92 | * An empty string
93 | */
94 | private static String BLANK_MESSAGE = "";
95 | /**
96 | * The javascript method name for onOpen event.
97 | */
98 | private static String EVENT_ON_OPEN = "onopen";
99 | /**
100 | * The javascript method name for onMessage event.
101 | */
102 | private static String EVENT_ON_MESSAGE = "onmessage";
103 | /**
104 | * The javascript method name for onClose event.
105 | */
106 | private static String EVENT_ON_CLOSE = "onclose";
107 | /**
108 | * The javascript method name for onError event.
109 | */
110 | private static String EVENT_ON_ERROR = "onerror";
111 | /**
112 | * The default port of WebSockets, as defined in the spec.
113 | */
114 | public static final int DEFAULT_PORT = 80;
115 | /**
116 | * The WebSocket protocol expects UTF-8 encoded bytes.
117 | */
118 | public static final String UTF8_CHARSET = "UTF-8";
119 | /**
120 | * The byte representing Carriage Return, or \r
121 | */
122 | public static final byte DATA_CR = (byte) 0x0D;
123 | /**
124 | * The byte representing Line Feed, or \n
125 | */
126 | public static final byte DATA_LF = (byte) 0x0A;
127 | /**
128 | * The byte representing the beginning of a WebSocket text frame.
129 | */
130 | public static final byte DATA_START_OF_FRAME = (byte) 0x00;
131 | /**
132 | * The byte representing the end of a WebSocket text frame.
133 | */
134 | public static final byte DATA_END_OF_FRAME = (byte) 0xFF;
135 |
136 | // //////////////// INSTANCE Variables
137 | /**
138 | * The WebView instance from Phonegap DroidGap
139 | */
140 | private final WebView appView;
141 | /**
142 | * The unique id for this instance (helps to bind this to javascript events)
143 | */
144 | private String id;
145 | /**
146 | * The URI this client is supposed to connect to.
147 | */
148 | private URI uri;
149 | /**
150 | * The port of the websocket server
151 | */
152 | private int port;
153 | /**
154 | * The Draft of the WebSocket protocol the Client is adhering to.
155 | */
156 | private Draft draft;
157 | /**
158 | * The SocketChannel instance to use for this server connection.
159 | * This is used to read and write data to.
160 | */
161 | private SocketChannel socketChannel;
162 | /**
163 | * The 'Selector' used to get event keys from the underlying socket.
164 | */
165 | private Selector selector;
166 | /**
167 | * Keeps track of whether or not the client thread should continue running.
168 | */
169 | private boolean running;
170 | /**
171 | * Internally used to determine whether to recieve data as part of the
172 | * remote handshake, or as part of a text frame.
173 | */
174 | private boolean handshakeComplete;
175 | /**
176 | * The 1-byte buffer reused throughout the WebSocket connection to read
177 | * data.
178 | */
179 | private ByteBuffer buffer;
180 | /**
181 | * The bytes that make up the remote handshake.
182 | */
183 | private ByteBuffer remoteHandshake;
184 | /**
185 | * The bytes that make up the current text frame being read.
186 | */
187 | private ByteBuffer currentFrame;
188 | /**
189 | * Queue of buffers that need to be sent to the client.
190 | */
191 | private BlockingQueue