Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Lots of parsing but no test cases and proper dir structure

  • Loading branch information...
commit 7ef05a7b3fac30c4b096090f3caf63da766164c2 1 parent b91705a
@trung authored
View
61 temp/server/action_message.hrl
@@ -1,28 +1,33 @@
-%% define amf0
--define(VERSION_1, 1).
-%% not used anymore but just there
--define(VERSION_2, 2).
-%% define amf3
--define(VERSION_3, 3).
-
--define(STATUS_METHOD, "/onStatus").
--define(RESULT_METHOD, "/onResult").
-
--define(AMF_CONTENT_TYPE, "application/x-amf").
--define(XML_CONTENT_TYPE, "application/xml").
-
--record(header, {headerName = "", mustUnderstand = false, data = <<>>}).
--record(body, {targetUri = "", responseUri = "", data = <<>> }).
-
--record(action_message, {
- version = ?VERSION_1,
- headers = [], %% list of 'header'
- bodies = [] %% list of 'body'
- }).
-
--record(trait, {
- className = "",
- externalizable = false,
- dynamic = false,
- properties = []
- }).
+%% define amf0
+-define(VERSION_1, 1).
+%% not used anymore but just there
+-define(VERSION_2, 2).
+%% define amf3
+-define(VERSION_3, 3).
+
+-define(STATUS_METHOD, "/onStatus").
+-define(RESULT_METHOD, "/onResult").
+
+-define(AMF_CONTENT_TYPE, "application/x-amf").
+-define(XML_CONTENT_TYPE, "application/xml").
+
+-define(OBJECT_REF_TABLE, objectRefTable).
+-define(TRAIT_REF_TABLE, traitRefTable).
+-define(STRING_REF_TABLE, stringRefTable).
+
+-record(header, {headerName = "", mustUnderstand = false, data = <<>>}).
+-record(body, {targetUri = "", responseUri = "", data = <<>> }).
+
+-record(action_message, {
+ version = ?VERSION_1,
+ headers = [], %% list of 'header'
+ bodies = [] %% list of 'body'
+ }).
+
+-record(trait, {
+ className = "",
+ externalizable = false,
+ dynamic = false,
+ properties = []
+ }).
+
View
7 temp/server/amf0.erl
@@ -1,7 +1,7 @@
-module(amf0).
-author("trung@mdkt.org").
--export([read_object/1, read_u8/1, read_u16/1, read_u32/1, read_string/1]).
+-export([read_object/1, read_u8/1, read_u16/1, read_u32/1, read_string/1, reset/0]).
-include("action_message.hrl").
@@ -24,6 +24,11 @@
-define(typed_object_marker, 16#10).
-define(avm_plus_object_marker, 16#11).
+%% Clear ETS tables
+reset() ->
+ _ = amf3:reset(),
+ {ok}.
+
read_u8(<<Value:8, Rest/binary>>) ->
{ok, Value, Rest}.
View
99 temp/server/amf3.erl
@@ -1,7 +1,9 @@
-module(amf3).
--export([read_object/1]).
+-export([read_object/1, reset/0]).
-include("action_message.hrl").
+-include("messages.hrl").
+-include("flex_classes.hrl").
-define(undefined_marker, 16#00).
-define(null_marker, 16#01).
@@ -16,6 +18,51 @@
-define(object_marker, 16#0A).
-define(xml_marker, 16#0B).
+%% Clear ETS tables: string, object and trait
+reset() ->
+ _ = clear(?OBJECT_REF_TABLE),
+ _ = clear(?TRAIT_REF_TABLE),
+ _ = clear(?STRING_REF_TABLE),
+ {ok}.
+
+%% delete all objects
+clear(TableName) ->
+ try ets:delete_all_objects(TableName) of
+ true ->
+ {ok}
+ catch
+ error:Error ->
+ {bad, Error}
+ end.
+
+%% Make sure the table exist, if not, create new one
+sure_exist(TableName) ->
+ case ets:info(TableName) of
+ undefined ->
+ ets:new(TableName, [named_table]),
+ _ = ets:insert(TableName, {counter, 0}),
+ {ok, created};
+ _ ->
+ {ok, exists}
+ end.
+
+%% wrapper of ets:insert, just make sure the table exists
+insert(TableName, Obj) ->
+ _ = sure_exist(TableName),
+ {ok, {counter, Count}} = read(TableName, counter),
+ io:fwrite("Inserting into ~p - index: ~p - value: ~p ~n", [TableName, Count, Obj]),
+ _ = ets:insert(TableName, {Count, Obj}),
+ _ = ets:insert(TableName, {counter, Count+1}),
+ {ok, inserted, Count}.
+
+%% wrapper of ets:lookup
+read(TableName, Ref) ->
+ case ets:lookup(TableName, Ref) of
+ [] ->
+ {bad, {not_found, TableName, Ref}};
+ [Obj|_] ->
+ {ok, Obj}
+ end.
%% The high bit of the first 3 bytes are used as flags to determine
%% whether the next byte is part of the integer.
@@ -48,7 +95,7 @@ read_object_reference(Ref) ->
%% Read object from ETS based on the key Ref
%% Return {ok, Obj} or {bad, Reason}
read_trait_reference(Ref) ->
- {bad, {"Not yet implemented: read_trait_reference", Ref}}.
+ read(?TRAIT_REF_TABLE, Ref).
%% Read object from ETS based on the key Ref
%% Return {ok, Str} or {bad, Reason}
@@ -59,6 +106,10 @@ write_object_reference(Ref, Obj) ->
%% TODO store the {Ref, Obj} to ETS
{ok, Ref, Obj}.
+write_trait_reference(Obj) ->
+ {ok, inserted, Ref} = insert(?TRAIT_REF_TABLE, Obj),
+ {ok, Ref, Obj}.
+
%% Not store if string is empty
write_string_reference(Ref, Str) when length(Str) == 0 ->
{ok, Ref, Str};
@@ -132,6 +183,7 @@ read_trait(Ref, Bin) ->
{ok, ClassName, BinAfterClassName} = read_string(Bin),
{ok, Properties, BinAfterProperties} = read_properties(BinAfterClassName, 0, PropertyCount, []),
Trait = #trait{className = ClassName, externalizable = Externalizable, dynamic = Dynamic, properties = Properties},
+ {ok, _, _} = write_trait_reference(Trait),
{ok, Trait, BinAfterProperties}
end.
@@ -188,6 +240,31 @@ read_array(Bin) ->
end
end.
+is_type(externalizable, true, ?FC_ARRAYCOLLECTION) -> true;
+is_type(externalizable, true, ?FC_OBJECTPROXY) -> true;
+is_type(externalizable, true, _) -> false;
+is_type(externalizable, false, _) -> not_externalizable.
+
+read_object_property(Bin, [], Object) ->
+ {ok, Object, Bin};
+read_object_property(Bin, [PropertyStr|Tail], PropertyMap) when is_list(PropertyMap) ->
+ {ok, Value, NextBin} = read_object(Bin),
+ read_object_property(NextBin, Tail, PropertyMap ++ [{PropertyStr, Value}]);
+read_object_property(Bin, [PropertyStr|Tail], Object) ->
+ {ok, Value, NextBin} = read_object(Bin),
+ PropertyName = record_utils:to_term(PropertyStr),
+ {ok, NewObject, _} = record_utils:set(Object, PropertyName, Value),
+ read_object_property(NextBin, Tail, NewObject).
+
+read_object_with_trait(Bin, TraitObj) when is_record(TraitObj, trait) ->
+ case record_utils:fc_to_record(TraitObj#trait.className) of
+ {ok, undefined} ->
+ {ok, PropertyMap, NextBin} = read_object_property(Bin, TraitObj#trait.properties, []),
+ {ok, {asObject, PropertyMap}, NextBin};
+ {ok, NewObject} ->
+ read_object_property(Bin, TraitObj#trait.properties, NewObject)
+ end.
+
%% Return {ok, value|Value, Rest} or {bad, Reason}
read_object(<<?undefined_marker:8, Rest/binary>>) -> {bad, {"Undefined marker ", Rest}};
read_object(<<?null_marker:8, Rest/binary>>) -> {ok, null, Rest};
@@ -195,7 +272,7 @@ read_object(<<?false_marker:8, Rest/binary>>) -> {ok, false, Rest};
read_object(<<?true_marker:8, Rest/binary>>) -> {ok, true, Rest};
read_object(<<?integer_marker:8, Rest/binary>>) -> read_uint_29(Rest);
read_object(<<?double_marker:8, Rest/binary>>) -> {bad, {"Not yet implemented marker", Rest}};
-read_object(<<?string_marker:8, Rest/binary>>) -> {bad, {"Not yet implemented marker", Rest}};
+read_object(<<?string_marker:8, Rest/binary>>) -> read_string(Rest);
read_object(<<?xml_doc_marker:8, Rest/binary>>) -> {bad, {"Not yet implemented marker", Rest}};
read_object(<<?date_marker:8, Rest/binary>>) -> {bad, {"Not yet implemented marker", Rest}};
read_object(<<?array_marker:8, Rest/binary>>) -> read_array(Rest);
@@ -206,9 +283,19 @@ read_object(<<?object_marker:8, Rest/binary>>) ->
{ok, ObjRef} = read_object_reference(Ref bsr 1),
{ok, ObjRef, BinAfterRef};
_ ->
- {ok, TraitObj, _BinAfterTrait} = read_trait(Ref, BinAfterRef),
- io:fwrite("Trait: ~p~n", [TraitObj]),
- {bad, "To be continued..."}
+ io:fwrite("Ref: ~p - ", [Ref]),
+ {ok, TraitObj, BinAfterTrait} = read_trait(Ref, BinAfterRef),
+ % io:fwrite("Trait: ~p~n", [TraitObj]),
+ case is_type(externalizable, TraitObj#trait.externalizable, TraitObj#trait.className) of
+ true ->
+ read_object(BinAfterTrait);
+ false ->
+ {bad, {"Externalizable class not supported", TraitObj}};
+ not_externalizable ->
+ %% subsequence binary contains values in order of property array in TraitObj
+ %% object will have the format: {object, [{propertyName=term(), Value}, ...]}
+ read_object_with_trait(BinAfterTrait, TraitObj)
+ end
end;
read_object(<<?xml_marker:8, Rest/binary>>) -> {bad, {"Not yet implemented marker", Rest}};
View
4 temp/server/deserializer.erl
@@ -12,6 +12,8 @@ read_header(Bin) ->
{ok, _DataLen, DataBin} = amf0:read_u32(BinAfterMustUnderstand),
%% Don't care about DataLen, just read
%% {DataBin, NextBin} = split_binary(Rest2, DataLen),
+ %% clear ETS tables
+ amf0:reset(),
{ok, Data, NextBin} = amf0:read_object(DataBin),
Header = #header{headerName = HeaderName, mustUnderstand = case MustUnderstand of 0 -> false; _Other -> true end, data = Data},
{ok, Header, NextBin}.
@@ -29,6 +31,8 @@ read_body(Bin) ->
{ok, _DataLen, DataBin} = amf0:read_u32(BinAfterResponseUri),
%% Don't care about DataLen, just read
%% {DataBin, NextBin} = split_binary(Rest2, DataLen),
+ %% clear ETS tables
+ amf0:reset(),
{ok, Data, NextBin} = amf0:read_object(DataBin),
Body = #body{targetUri = TargetUri, responseUri = ResponseUri, data=Data},
{ok, Body, NextBin}.
View
3  temp/server/flex_classes.hrl
@@ -0,0 +1,3 @@
+-define(FC_ARRAYCOLLECTION, "flex.messaging.io.ArrayCollection").
+-define(FC_OBJECTPROXY, "flex.messaging.io.ObjectProxy").
+-define(FC_REMOTINGMESSAGE, "flex.messaging.messages.RemotingMessage").
View
22 temp/server/messages.hrl
@@ -0,0 +1,22 @@
+-record(abstract_message, {
+ clientId,
+ destination = "",
+ messageId = "",
+ timestamp = 0,
+ timeToLive = 0,
+ headers = [],
+ body
+ }).
+
+-record(rpc_message, {
+ parent = #abstract_message{},
+ remoteUsername = "",
+ remotePassword = ""
+ }).
+
+-record(remoting_message, {
+ parent = #rpc_message{},
+ source = "",
+ operation = "",
+ parameters = []
+ }).
View
68 temp/server/record_utils.erl
@@ -0,0 +1,68 @@
+-module(record_utils).
+-author("trung@mdkt.org").
+-compile(export_all).
+
+-include("messages.hrl").
+-include("flex_classes.hrl").
+
+%% Setters for records defined in messages.hrl file
+%% return {ok, NewObject, {propertyName, NewValue}}
+set(Obj, clientId, Value) when is_record(Obj, abstract_message) ->
+ NewObj = Obj#abstract_message{clientId = Value},
+ {ok, NewObj, {clientId, Value}};
+set(Obj, destination, Value) when is_record(Obj, abstract_message) ->
+ NewObj = Obj#abstract_message{destination = Value},
+ {ok, NewObj, {destination, Value}};
+set(Obj, messageId, Value) when is_record(Obj, abstract_message) ->
+ NewObj = Obj#abstract_message{messageId = Value},
+ {ok, NewObj, {messageId, Value}};
+set(Obj, timestamp, Value) when is_record(Obj, abstract_message) ->
+ NewObj = Obj#abstract_message{timestamp = Value},
+ {ok, NewObj, {timestamp, Value}};
+set(Obj, timeToLive, Value) when is_record(Obj, abstract_message) ->
+ NewObj = Obj#abstract_message{timeToLive = Value},
+ {ok, NewObj, {timeToLive, Value}};
+set(Obj, headers, Value) when is_record(Obj, abstract_message) ->
+ NewObj = Obj#abstract_message{headers = Value},
+ {ok, NewObj, {headers, Value}};
+set(Obj, body, Value) when is_record(Obj, abstract_message) ->
+ NewObj = Obj#abstract_message{body = Value},
+ {ok, NewObj, {body, Value}};
+
+set(Obj, remoteUsername, Value) when is_record(Obj, rpc_message) ->
+ NewObj = Obj#rpc_message{remoteUsername = Value},
+ {ok, NewObj, {remoteUsername, Value}};
+set(Obj, remotePassword, Value) when is_record(Obj, rpc_message) ->
+ NewObj = Obj#rpc_message{remotePassword = Value},
+ {ok, NewObj, {remotePassword, Value}};
+set(Obj, parent, Value) when is_record(Obj, rpc_message) and is_record(Value, abstract_message) ->
+ NewObj = Obj#rpc_message{parent = Value},
+ {ok, NewObj, {parent, Value}};
+set(Obj, AbstractMessageProperty, Value) when is_record(Obj, rpc_message) and is_atom(AbstractMessageProperty) ->
+ {ok, NewAbstractMessage, _} = set(Obj#rpc_message.parent, AbstractMessageProperty, Value),
+ set(Obj, parent, NewAbstractMessage);
+
+set(Obj, parent, Value) when is_record(Obj, remoting_message) and is_record(Value, rpc_message) ->
+ NewObj = Obj#remoting_message{parent = Value},
+ {ok, NewObj, {parent, Value}};
+set(Obj, source, Value) when is_record(Obj, remoting_message) ->
+ NewObj = Obj#remoting_message{source = Value},
+ {ok, NewObj, {source, Value}};
+set(Obj, operation, Value) when is_record(Obj, remoting_message) ->
+ NewObj = Obj#remoting_message{operation = Value},
+ {ok, NewObj, {operation, Value}};
+set(Obj, parameters, Value) when is_record(Obj, remoting_message) ->
+ NewObj = Obj#remoting_message{parameters = Value},
+ {ok, NewObj, {parameters, Value}};
+set(Obj, RpcMessageProperty, Value) when is_record(Obj, remoting_message) and is_atom(RpcMessageProperty) ->
+ {ok, NewRpcMessage, _} = set(Obj#remoting_message.parent, RpcMessageProperty, Value),
+ set(Obj, parent, NewRpcMessage).
+
+fc_to_record(?FC_REMOTINGMESSAGE) -> {ok, #remoting_message{}};
+fc_to_record(_) -> {ok, undefined}.
+
+%% Convert String to term, Str must be term-like string
+to_term(Str) ->
+ {ok, Tokens, _} = erl_scan:string(Str),
+ {ok, Term} = erl_parse:parse_term(Tokens ++ [{dot,1}]),
+ Term.
Please sign in to comment.
Something went wrong with that request. Please try again.