Skip to content

Instantly share code, notes, and snippets.

@physacco
Created March 9, 2013 09:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save physacco/5123696 to your computer and use it in GitHub Desktop.
Save physacco/5123696 to your computer and use it in GitHub Desktop.
Send, receive and process binary data in web browser. Using XMLHTTPRequest, ArrayBuffer and ArrayBufferViews. Tested on Firefox 20.0.
# encoding: utf-8
# Tested on ruby 1.9.2
class HTTPNotFound < Exception
end
class MimeTypes
HTML = "text/html; charset=utf-8"
JAVASCRIPT = "application/javascript: charset=utf-8"
BINARY = "application/octet-stream; charset=binary"
def self.guess(ext)
case ext
when ".htm", ".html"
HTML
when ".js"
JAVASCRIPT
else
BINARY
end
end
end
class Server
def call(env)
@env = env
@path = env["PATH_INFO"]
begin
static_dispatch
rescue HTTPNotFound
dynamic_dispatch
end
end
def static_dispatch
path = (@path == "/") ? "/index.html" : @path
fullpath = File.join Dir.pwd, "public", path
raise HTTPNotFound.new(@path) unless File.exists? fullpath
headers = {
"Content-Type" => MimeTypes::guess(File.extname(path)),
"Content-Length" => File.size(fullpath).to_s
}
[200, headers, File.new(fullpath)]
end
def dynamic_dispatch
case @path
when "/transform"
serve_transform
else
error404
end
end
def error404
body = "404 not found"
headers = {
"Content-Type" => MimeTypes::HTML,
"Content-Length" => body.size.to_s
}
[404, headers, [body]]
end
def serve_transform
if @env["REQUEST_METHOD"] == "POST"
encrypted_data = @env["rack.input"].read
else
encrypted_data = ""
end
# decrypt
data = encrypted_data.each_byte.map{|b| b ^ 37}.pack("C*")
data.force_encoding "UTF-16BE"
# transform
data1 = data.upcase
# encrypt
body = data1.each_byte.map{|b| b ^ 37}.pack("C*")
headers = {
"Content-Type" => MimeTypes::BINARY,
"Content-Length" => body.size.to_s
}
[200, headers, [body]]
end
end
run Server.new
.
|-- config.ru
`-- public
|-- index.html
`-- js
`-- test.js
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Send, Receive and Process Binary Data</title>
<script type="text/javascript" src="/js/transform.js"></script>
</head>
<body>
<h1>Convert a string to upper case</h1>
<input id="input" type="text" placeholder="input some words..." />
<input id="transform" type="button" value="->" />
<input id="output" type="text" />
</body>
</html>
(function (window, document) {
var ajaxload = function (config) {
var i, xhr, type, url, headers, data, callback, errback;
type = config.type || "GET";
url = config.url;
headers = config.headers || {};
data = config.data;
xhr = new XMLHttpRequest();
xhr.open(type, url, true);
if (config.responseType) {
xhr.responseType = config.responseType;
}
for (i in headers) {
if (config.hasOwnProperty(i)) {
xhr.setRequestHeader(i, headers[i]);
}
}
callback = function (e) {
if (config.success) {
config.success(xhr.response, xhr.statusText, xhr);
}
};
errback = function (e) {
if (config.error) {
config.error(e, xhr);
}
}
xhr.onload = callback;
xhr.onabort = errback;
xhr.onerror = errback;
xhr.ontimeout = errback;
xhr.send(data);
};
var binajax = function (config) {
ajaxload({
type: config.type,
url: config.url,
headers: {
"Content-Type": "application/octet-stream; charset=binary"
},
data: config.data,
responseType: "arraybuffer",
error: config.errback,
success: function (buffer, statusText, xhr) {
if (config.callback) {
config.callback(buffer);
}
}
});
};
// UTF16-BE encoded ArrayBuffer -> String
ab2str = function (buffer) {
var i, bytelen, buffer1, uint8array, uint16array;
bytelen = buffer.byteLength;
buffer1 = new ArrayBuffer(bytelen);
uint8array = new Uint8Array(buffer);
uint16array = new Uint16Array(buffer1);
for (i = 0; i < bytelen; i += 2) {
uint16array[i / 2] = (uint8array[i] << 8) + uint8array[i + 1];
}
return String.fromCharCode.apply(null, uint16array);
};
// String -> UTF16-BE encoded ArrayBuffer
str2ab = function (str) {
var i, bytelen, buffer, uint8array, code;
bytelen = str.length * 2; // 2 bytes for each char
buffer = new ArrayBuffer(bytelen);
uint8array = new Uint8Array(buffer);
for (i = 0; i < bytelen; i += 2) {
code = str.charCodeAt(i / 2);
uint8array[i] = code >> 8;
uint8array[i + 1] = code % 256;
}
return buffer;
};
xorcrypt = function (buffer, key) {
var i, bytelen, buffer1, uint8array, uint8array1;
bytelen = buffer.byteLength;
buffer1 = new ArrayBuffer(bytelen);
uint8array = new Uint8Array(buffer);
uint8array1 = new Uint8Array(buffer1);
for (i = 0; i < bytelen; i += 1) {
uint8array1[i] = uint8array[i] ^ key;
}
return buffer1;
};
var transform = function (e) {
var input, output, text, key, ibuffer;
input = document.getElementById("input");
output = document.getElementById("output");
text = input.value.replace(/^\s+/, '').replace(/\s+$/, '');
if (text === "") {
output.value = "";
return false;
}
key = 37;
ibuffer = xorcrypt(str2ab(text), key);
binajax({
type: "POST",
url: "/transform",
data: ibuffer,
callback: function (obuffer) {
output.value = ab2str(xorcrypt(obuffer, key));
}
});
};
window.onload = function (e) {
var button = document.getElementById("transform");
button.onclick = transform;
};
}(window, document));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment