I want to make a client-side js_of_ocaml application with a server in OCaml, with the limitations described below, and I would like to know if the approach is suitable below or if there is a more efficient one. The server can sometimes send large amounts of data (> 30 MB).
To make the exchange of data between the client and server more secure and more efficient, I use the type t in the .mli file:
type client_to_server =
| Say_Hello
| Do_something_with of int
type server_to_client =
| Ack
| Print of string * int
Then this type is sorted into a string and sent over the network. I know that on the client side some types are missing (Int64.t).
In addition, in the XMLHTTPRequest sent by the client, we want to receive more than one marshalled object from the server, and sometimes in streaming mode (i.e., process the received marshal object (if possible) during the loadingrequest state , and not only during the state done) .
These restrictions force us to use the responseTextXMLHTTPRequest field with the content type application/octet-stream.
In addition, when we return a response from responseText, an encoding conversion is performed because the JavaScript string is in UTF-16. But the marshalling object, which is binary data, we do what is necessary to obtain our binary data (by overriding the encoding with x-user-definedand applying a mask for each character of the string responseText).
(HTTP- OCaml) - :
let process_request req =
let res = process_response req in
let s = Marshal.to_string res [] in
send s
JavaScript- js_of_ocaml caml_marshal_data_size MlString. javascript MlString ( ), ( ) . - javascript.
:
external marshal_total_size : Js.js_string Js.t -> int -> int = "my_marshal_total_size"
external marshal_from_string : Js.js_string Js.t -> int -> 'a = "my_marshal_from_string"
let apply (f:server_to_client -> unit) (str:Js.js_string Js.t) (ofs:int) : int =
let len = str##length in
let rec aux pos =
let tsize =
try Some (pos + My_primitives.marshal_total_size str pos)
with Failure _ -> None
in
match tsize with
| Some tsize when tsize <= len ->
let data = My_primitives.marshal_from_string str pos in
f data;
aux tsize
| _ -> pos
in
aux ofs
let reqcallback f req ofs =
match req##readyState, req##status with
| XmlHttpRequest.DONE, 200 ->
ofs := apply f req##responseText !ofs
| XmlHttpRequest.LOADING, 200 ->
ignore (apply f req##responseText !ofs)
| _, 200 -> ()
| _, i -> process_error i
let send (f:server_to_client -> unit) (order:client_to_server) =
let order = Marshal.to_string order [] in
let msg = Js.string (my_encode order) in (* Do some stuff *)
let req = XmlHttpRequest.create () in
req##_open(Js.string "POST", Js.string "/kernel", Js._true);
req##setRequestHeader(Js.string "Content-Type",
Js.string "application/octet-stream");
req##onreadystatechange <- Js.wrap_callback (reqcallback f req (ref 0));
req##overrideMimeType(Js.string "application/octet-stream; charset=x-user-defined");
req##send(Js.some msg)
:
var my_marshal_header_size = 20;
function my_int_of_char(s, i) {
return (s.charCodeAt(i) & 0xFF);
}
var my_marshal_input_value_from_string = function () {
}
function my_marshal_data_size(s, ofs) {
function get32(s,i) {
return (my_int_of_char(s, i) << 24) | (my_int_of_char(s, i + 1) << 16) |
(my_int_of_char(s, i + 2) << 8) | (my_int_of_char(s, i + 3));
}
if (get32(s, ofs) != (0x8495A6BE|0))
caml_failwith("MyMarshal.data_size");
return (get32(s, ofs + 4));
}
function my_marshal_total_size(s, ofs) {
if ( ofs < 0 || ofs > s.length - my_marshal_header_size )
caml_failwith("Invalid argument");
else return my_marshal_header_size + my_marshal_data_size(s, ofs);
}
OCaml , ?