Browse Source

代码初始化2

master
AICells 5 years ago
parent
commit
1e5f792c5b
16 changed files with 3998 additions and 7 deletions
  1. +238
    -0
      src/comMisc/utList.erl
  2. +74
    -0
      src/comMisc/utMisc.erl
  3. +129
    -0
      src/comMisc/utRate.erl
  4. +34
    -1
      src/dataType/utTypeCast.erl
  5. +349
    -0
      src/dynamicCompile/dynamic_compile.erl
  6. +85
    -0
      src/dynamicCompile/dynamic_kvs.erl
  7. +1136
    -0
      src/dynamicCompile/meta.erl
  8. +29
    -0
      src/dynamicCompile/meta_session.erl
  9. +186
    -0
      src/dynamicCompile/reloader.erl
  10. +1049
    -0
      src/dynamicCompile/smerl.erl
  11. +159
    -0
      src/httpSocket/http.erl
  12. +344
    -0
      src/httpSocket/leo_http.erl
  13. +55
    -0
      src/httpSocket/utHttpOn.erl
  14. +88
    -0
      src/httpSocket/utHttpUrl.erl
  15. +29
    -0
      src/httpSocket/utSocket.erl
  16. +14
    -6
      src/timeDate/utTime.erl

+ 238
- 0
src/comMisc/utList.erl View File

@ -0,0 +1,238 @@
-module(utList).
-define(TRUE, true).
-define(FALSE, false).
-define(BREAK, break).
-define(BREAK(Value), {?BREAK, Value}).
-define(CONTINUE, continue).
-define(CONTINUE(Value), {?CONTINUE, Value}).
-define(UNDEFINED, undefined).
-compile([export_all, nowarn_export_all]).
while(Fun, CurData) ->
case Fun(CurData) of
?BREAK -> CurData;
?BREAK(BreakData) -> BreakData;
?CONTINUE ->
while(Fun, CurData);
?CONTINUE(Continue) ->
while(Fun, Continue)
end.
%% For loop.
for(N, Fun) ->
for(1, N, 1, Fun).
for(Start, End, Fun) ->
for(Start, End, 1, Fun).
for(End, End, _Step, Fun) ->
Fun(End);
for(Start, End, Step, Fun) ->
Fun(Start),
for(Start + Step, End, Step, Fun).
%% for循环 @return {ok, State}
forWithState(Index, Max, _Fun, State) when Index > Max ->
{ok, State};
forWithState(Max, Max, Fun, State) ->
Fun(Max, State);
forWithState(Index, Max, Fun, State) ->
{ok, NewState} = Fun(Index, State),
forWithState(Index + 1, Max, Fun, NewState).
forWhile(Fun, State) ->
forWhile(?CONTINUE, Fun, State).
forWhile(?BREAK, _Fun, State) ->
State;
forWhile(?CONTINUE, Fun, State) ->
{Next, NewState} = Fun(State),
forWhile(Next, Fun, NewState).
%%
randFormList([]) ->
false;
randFormList(List) ->
lists:nth(rand:uniform_s(1, length(List)), List).
%%
randFormListOnce([]) ->
{false, []};
randFormListOnce(List) ->
Nth = rand:uniform_s(1, length(List)),
NthData = lists:nth(Nth, List),
{NthData, lists:delete(NthData, List)}.
randDelete(RemainList) ->
Pos = rand:uniform_s(1, length(RemainList)),
remove_nth(Pos, RemainList).
randDelete(DelCount, List) ->
FunDel =
fun(_, RemainList) ->
RemainList(RemainList)
end,
lists:foldl(FunDel, List, lists:seq(1, DelCount)).
%%length(List) >= Num
%%[1,2,3,4,5,6,7,8,9][1,2,4]
getRandFromList(Num, List) ->
ListSize = length(List),
FunDel =
fun(N, List1) ->
Random = rand:uniform_s(1, (ListSize - N + 1)),
Elem = lists:nth(Random, List1),
lists:delete(Elem, List1)
end,
Result = lists:foldl(FunDel, List, lists:seq(1, Num)),
List -- Result.
%% List =[] List2
confuseList(List) ->
confuseList(List, []).
confuseList(List, Result) ->
confuseList(List, rand:uniform_s(1, length(List)), Result).
confuseList(_List, 0, Result) ->
Result;
confuseList(List, Nth, Result) ->
{NthData, Remain} = extract_member(rand:uniform_s(1, Nth), List),
confuseList(Remain, length(Remain), [NthData | Result]).
%%Nth个元素{NthData, RemainList}
extract_member(Nth, List) ->
extract_member(Nth, List, []).
extract_member(1, List1, List2) ->
case List1 of
[R] ->
{R, List2};
[R | L] ->
{R, List2 ++ L}
end;
extract_member(Nth, [R | List1], List2) ->
extract_member(Nth - 1, List1, List2 ++ [R]).
%%Tuple元素数量List转成Tuple {TupleList, RemainList}
list_to_tuple(List, TupleCount) ->
list_to_tuple(List, TupleCount, []).
list_to_tuple(List, TupleCount, Result) when length(List) >= TupleCount ->
{List1, List2} = lists:split(TupleCount, List),
list_to_tuple(List2, TupleCount, [list_to_tuple(List1) | Result]);
list_to_tuple(List, _TupleCount, Result) ->
{lists:reverse(Result), List}.
%%(5, [1,2,3,5,5,5,3]) ----> 3
repeatCount(Element, List) ->
repeatCount(Element, List, 0).
repeatCount(Element, List, Count) ->
case lists:member(Element, List) of
false -> Count;
true ->
repeatCount(Element, lists:delete(Element, List), Count + 1)
end.
remove_nth(RemainList, Pos) ->
Head = lists:sublist(RemainList, 1, Pos - 1),
End = lists:sublist(RemainList, Pos + 1, length(RemainList)),
Head ++ End.
%%(5, [1,2,4,4,5,5,4,5]) -> [1,2,4,4,4]
remove_element(Element, List) ->
case lists:member(Element, List) of
false -> List;
true -> remove_element(Element, lists:delete(Element, List))
end.
%% [1,2,3,2,3,4] ---> [1,2,3,4]
remove_repeat(List) ->
remove_repeat(List, []).
remove_repeat([], Result) ->
Result;
remove_repeat([L | List], Result) ->
case lists:member(L, Result) of
false -> remove_repeat(List, Result ++ [L]);
true -> remove_repeat(List, Result)
end.
%%Key去查List中元素的Nth位相等的匹配到的元素Fun(Element)
find_elements(Key, Nth, List, Fun) ->
InnerFun = fun(Element) ->
case element(Nth, Element) =:= Key of
true ->
{true, Fun(Element)};
false ->
false
end
end,
lists:filtermap(InnerFun, List).
%%list进行去重
%%Replicat 01
%%Sort 01
filter_list(List, Replicat, Sort) ->
if Replicat == 0 andalso Sort == 0 ->
List;
true ->
if Replicat == 1 andalso Sort == 1 ->
lists:usort(List);
true ->
if Sort == 1 ->
lists:sort(List);
true ->
lists:reverse(filter_replicat(List, []))
end
end
end.
%%list去重
filter_replicat([], List) ->
List;
filter_replicat([H | Rest], List) ->
Bool = lists:member(H, List),
List1 =
if Bool == true ->
[[] | List];
true ->
[H | List]
end,
List2 = lists:filter(fun(T) -> T =/= [] end, List1),
filter_replicat(Rest, List2).
%%
get_intersection_list(A, B) when is_list(A) andalso is_list(B) ->
lists:filter(fun(X) -> lists:member(X, A) end, B).
%%
get_unite_list(A, B) when is_list(A) andalso is_list(B) ->
C = A ++ B,
lists:usort(C).
%%
get_subtract_list(A, B) when is_list(A) andalso is_list(B) ->
Insection = get_intersection_list(A, B),
Unite = get_unite_list(A, B),
lists:filter(fun(X) -> lists:member(X, Insection) =:= false end, Unite).
merge(L) ->
merge(L, []).
merge([{Key, Value} | T], AccList) ->
NewAccList =
case lists:keyfind(Key, 1, AccList) of
false ->
[{Key, Value} | AccList];
{Key, OldValue} ->
lists:keyreplace(Key, 1, AccList, {Key, OldValue + Value})
end,
merge(T, NewAccList);
merge([], AccList) ->
AccList.

+ 74
- 0
src/comMisc/utMisc.erl View File

@ -0,0 +1,74 @@
-module(utMisc).
-compile([export_all, nowarn_export_all]).
%%
fileLog(T, F, A, Mod, Line) ->
{ok, Fl} = file:open("logs/error_log.txt", [write, append]),
Format = list_to_binary("#" ++ T ++ " ~s[~w:~w] " ++ F ++ "\r\n~n"),
{{Y, M, D}, {H, I, S}} = erlang:localtime(),
Date = list_to_binary([integer_to_list(Y), "-", integer_to_list(M), "-", integer_to_list(D), " ", integer_to_list(H), ":", integer_to_list(I), ":", integer_to_list(S)]),
io:format(Fl, unicode:characters_to_list(Format), [Date, Mod, Line] ++ A),
file:close(Fl).
compile_base_data(Table, ModName, IDPoses) ->
ModNameString = com_util:term_to_string(ModName),
HeadString =
"-module(" ++ ModNameString ++ ").
-compile(export_all).
",
BaseDataList = db_base:select_all(Table, "*", []),
ContentString =
lists:foldl(fun(BaseData0, PreString) ->
FunChange =
fun(Field) ->
if is_integer(Field) -> Field;
true ->
case com_util:bitstring_to_term(Field) of
undefined ->
Field;
Term ->
Term
end
end
end,
BaseData = [FunChange(Item) || Item <- BaseData0],
Base = list_to_tuple([Table | BaseData]),
BaseString = com_util:term_to_string(Base),
IDs = [element(Pos, Base) || Pos <- IDPoses],
IDList0 = lists:foldl(fun(ID, PreString2) ->
IDList =
if erlang:is_integer(ID) ->
integer_to_list(ID);
true ->
ID
end,
PreString2 ++ "," ++ IDList
end, [], IDs),
[_ | IDList] = IDList0,
PreString ++
"get(" ++
IDList ++
") ->" ++
BaseString ++
";
"
end
, "", BaseDataList),
_List0 = [",_" || _Pos <- IDPoses],
[_ | _List] = lists:flatten(_List0),
ErrorString = "get(" ++ _List ++ ") -> undefined.
",
FinalString = HeadString ++ ContentString ++ ErrorString,
%% ?PRINT("string=~s~n",[FinalString]),
try
{Mod, Code} = dynamic_compile:from_string(FinalString),
code:load_binary(Mod, ModNameString ++ ".erl", Code)
catch
Type:Error -> io:format("Error compiling (~p): ~p~n", [Type, Error])
end,
ok.

+ 129
- 0
src/comMisc/utRate.erl View File

@ -0,0 +1,129 @@
-module(utRate).
-export([happen/1,
choose/1,
choose_n_uniq/2,
range/1,
collect/2,
select_n/2, %% return N unique items
collect_n/2, %% return N items, same item may appear than once
collect_one/1,
rand_list/1,
rand_list_n/2]).
-on_load(init/0).
init() ->
% mtwist:seed(time_utils:now()),
rand:seed(exs1024, erlang:timestamp()),
ok.
happen(Rate) when Rate == 1.0 -> true;
happen(Rate) when Rate < 1 ->
happen(Rate * 10000);
happen(Rate) ->
% RandomValue = mtwist:uniform(10000),
RandomValue = rand:uniform(10000),
compare(RandomValue, Rate).
%% Rates = [{RateA, ValueA}, {RateB, ValueB}, {default, DefaultValue}],
choose([{_Rate, Value}]) -> Value;
choose(Rates) ->
{_Rate, Value} = do_choose(Rates),
Value.
do_choose(Rates) ->
MaxValue = lists:foldl(fun({Rate, _}, Result) ->
Result + Rate
end, 0, Rates),
RandomValue = rand:uniform(MaxValue),
{Rate, Value} = choose(Rates, RandomValue, 0),
{Rate, Value}.
choose([{Rate, Value}|Rates], RandomValue, Offset) ->
case RandomValue =< Rate + Offset of
true -> {Rate, Value};
false -> choose(Rates, RandomValue, Offset + Rate)
end.
choose_n_uniq(Rates, N) ->
choose_n_uniq(Rates, N, []).
choose_n_uniq(_Rates, 0, Result) -> Result;
choose_n_uniq(Rates, N, Result) ->
{Rate, Value} = do_choose(Rates),
NRates = lists:delete({Rate, Value}, Rates),
choose_n_uniq(NRates, N - 1, [Value|Result]).
%% Range = [{RateA, ValueA}, {RateB, ValueB}, {default, DefaultValue}],
%% select the happened Rate
range(Range) ->
% select(mtwist:uniform(10000), Range).
select(rand:uniform(10000), Range).
select(RandomValue, [{Rate, Value}|_Range]) when RandomValue =< Rate -> Value;
select(_RandomValue, [{default, Value}]) -> Value;
select(RandomValue, [{_Rate, _Value}|Range]) ->
select(RandomValue, Range).
compare(A, B) when A < B -> true;
compare(_A, _B) -> false.
%% Rand return N elements from Rates
%% Rates = [{RateA, ValueA}, {RateB, ValueB}, {RateC, ValueC}],
%% N =< length(Rates)
collect(Rates, N) ->
MaxValue = lists:foldl(fun({Rate, _}, Result) ->
Result + Rate
end, 0, Rates),
collect(N, Rates, MaxValue, []).
collect(0, _Rates, _MaxValue, Result) -> Result;
collect(N, Rates, MaxValue, Result) ->
% RandValue = mtwist:uniform(MaxValue),
RandValue = rand:uniform(MaxValue),
{Rate, Value} = choose(Rates, RandValue, 0),
NewRates = lists:delete({Rate, Value}, Rates),
collect(N - 1, NewRates, MaxValue - Rate, [Value|Result]).
%% Select N elements from List
select_n(List, N) -> select_n(List, N, []).
select_n([], _N, Result) -> Result;
select_n(_List, 0, Result) -> Result;
select_n(List, N, Result) ->
Len = length(List),
% Index = mtwist:uniform(Len) + 1,
Index = rand:uniform(Len),
Elem = lists:nth(Index, List),
select_n(lists:delete(Elem, List), N - 1, [Elem|Result]).
collect_n(List, N) -> collect_n(List, N, []).
collect_n(_List, 0, Result) -> Result;
collect_n(List, N, Result) ->
Len = length(List),
% Index = trunc(mtwist:uniform(Len)) + 1,
Index = rand:uniform(Len),
Elem = lists:nth(Index, List),
collect_n(List, N - 1, [Elem|Result]).
collect_one(List) ->
[Elem] = collect_n(List, 1),
Elem.
rand_list(List) ->
[X || {_, X} <- lists:sort([{rand:uniform(), N} || N <- List])].
rand_list_n(List, Limit) ->
SortedList = lists:sort([{rand:uniform(), N} || N <- List]),
{Result, _} = misc_utils:each(fun({_Fator, Elem}, {Acc, Idx}) ->
if
Idx =:= Limit ->
{break, {Acc, Idx}};
Idx < Limit ->
{[Elem|Acc], Idx + 1}
end
end, {[], 0}, SortedList),
Result.

+ 34
- 1
src/dataType/utTypeCast.erl View File

@ -109,4 +109,37 @@ string_to_term(String) ->
end;
_Error ->
undefined
end.
end.
%% utf8编码的列表
listToUtfString(List) ->
unicode:characters_to_list(erlang:list_to_binary(List), utf8).
%%unicode编码范围 0x4e00 - 0x9fa5
% (4 * 16 * 16 * 16 + 14 * 16 * 16)
-define(UNICODE_CHINESE_BEGIN, 16#4e00).
% (9 * 16 * 16 * 16 + 15 * 16 * 16 + 10 * 16 + 5)
-define(UNICODE_CHINESE_END, 16#9fa5).
%% desc
%% parm UTF8String UTF8编码的字符串
%% return {,}
getChineseNum(UTF8String) ->
UnicodeList = unicode:characters_to_list(list_to_binary(UTF8String)),
Fun = fun(Num, {Sum}) ->
case Num >= ?UNICODE_CHINESE_BEGIN andalso Num =< ?UNICODE_CHINESE_END of
true ->
{Sum + 1};
false ->
{Sum}
end
end,
{ChineseCount} = lists:foldl(Fun, {0}, UnicodeList),
OtherCount = length(UnicodeList) - ChineseCount,
{ChineseCount, OtherCount}.
termToBase64(Term) ->
base64:encode(term_to_binary(Term)).
base64ToTerm(Base64String) ->
binary_to_term(base64:decode(Base64String)).

+ 349
- 0
src/dynamicCompile/dynamic_compile.erl View File

@ -0,0 +1,349 @@
%%%-------------------------------------------------------------------
%%% @author root
%%% @copyright (C) 2015, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 30. 2015 8:32
%%%-------------------------------------------------------------------
%% Copyright (c) 2007
%% Mats Cronqvist <mats.cronqvist@ericsson.com>
%% Chris Newcombe <chris.newcombe@gmail.com>
%% Jacob Vorreuter <jacob.vorreuter@gmail.com>
%%
%% Permission is hereby granted, free of charge, to any person
%% obtaining a copy of this software and associated documentation
%% files (the "Software"), to deal in the Software without
%% restriction, including without limitation the rights to use,
%% copy, modify, merge, publish, distribute, sublicense, and/or sell
%% copies of the Software, and to permit persons to whom the
%% Software is furnished to do so, subject to the following
%% conditions:
%%
%% The above copyright notice and this permission notice shall be
%% included in all copies or substantial portions of the Software.
%%
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
%% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
%% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
%% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
%% OTHER DEALINGS IN THE SOFTWARE.
%%%-------------------------------------------------------------------
%%% File : dynamic_compile.erl
%%% Description :
%%% Authors : Mats Cronqvist <mats.cronqvist@ericsson.com>
%%% Chris Newcombe <chris.newcombe@gmail.com>
%%% Jacob Vorreuter <jacob.vorreuter@gmail.com>
%%% TODO :
%%% - add support for limit include-file depth (and prevent circular references)
%%% prevent circular macro expansion set FILE correctly when -module() is found
%%% -include_lib support $ENVVAR in include filenames
%%% substitute-stringize (??MACRO)
%%% -undef/-ifdef/-ifndef/-else/-endif
%%% -file(File, Line)
%%%-------------------------------------------------------------------
-module(dynamic_compile).
%% API
-export([init/0, load_from_string/1, load_from_string/2]).
-export([from_string/1, from_string/2, get_forms_from_src/1]).
-import(lists, [reverse/1, keyreplace/4]).
-export([module_src/1, module_lt/1, modules/0]).
-record(module, {name = 0, loadtime = 0, src}).
init() ->
ets:new(?MODULE, [public, set, named_table, {keypos, #module.name}]),
ok.
module_src(Module) ->
init_if_notinit(),
case ets:lookup(?MODULE, Module) of
[] -> false;
[#module{src = Src}] -> Src
end.
module_lt(Module) ->
init_if_notinit(),
case ets:lookup(?MODULE, Module) of
[] -> false;
[#module{loadtime = Lt}] -> Lt
end.
modules() ->
init_if_notinit(),
ets:tab2list(?MODULE).
module_new(Module, Src) ->
ets:insert(?MODULE, #module{name = Module, src = Src}).
init_if_notinit() ->
case ets:info(?MODULE) of
undefined ->
init();
_ ->
ignore
end.
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function:
%% Description:
%% Compile module from string and load into VM
%%--------------------------------------------------------------------
load_from_string(CodeStr) ->
load_from_string(CodeStr, []).
load_from_string(CodeStr, CompileFormsOptions) ->
{Mod, Bin} = from_string(CodeStr, CompileFormsOptions),
code:load_binary(Mod, [], Bin),
ok.
%% module_new(Mod, CodeStr).
%%--------------------------------------------------------------------
%% Function:
%% Description:
%% Returns a binary that can be used with
%% code:load_binary(Module, ModuleFilenameForInternalRecords, Binary).
%%--------------------------------------------------------------------
from_string(CodeStr) ->
from_string(CodeStr, []).
% takes Options as for compile:forms/2
from_string(CodeStr, CompileFormsOptions) ->
%% Initialise the macro dictionary with the default predefined macros,
%% (adapted from epp.erl:predef_macros/1
Filename = "compiled_from_string",
%%Machine = list_to_atom(erlang:system_info(machine)),
Ms0 = dict:new(),
% Ms1 = dict:store('FILE', {[], "compiled_from_string"}, Ms0),
% Ms2 = dict:store('LINE', {[], 1}, Ms1), % actually we might add special code for this
% Ms3 = dict:store('MODULE', {[], undefined}, Ms2),
% Ms4 = dict:store('MODULE_STRING', {[], undefined}, Ms3),
% Ms5 = dict:store('MACHINE', {[], Machine}, Ms4),
% InitMD = dict:store(Machine, {[], true}, Ms5),
InitMD = Ms0,
%% From the docs for compile:forms:
%% When encountering an -include or -include_dir directive, the compiler searches for header files in the following directories:
%% 1. ".", the current working directory of the file server;
%% 2. the base name of the compiled file;
%% 3. the directories specified using the i option. The directory specified last is searched first.
%% In this case, #2 is meaningless.
IncludeSearchPath = ["." | reverse([Dir || {i, Dir} <- CompileFormsOptions])],
{RevForms, _OutMacroDict} = scan_and_parse(CodeStr, Filename, 1, [], InitMD, IncludeSearchPath),
Forms = [{attribute, 0, file, {"compiled_from_string", 0}} | reverse([{eof, 0} | RevForms])],
%% note: 'binary' is forced as an implicit option, whether it is provided or not.
case compile:forms(Forms, CompileFormsOptions) of
{ok, ModuleName, CompiledCodeBinary} when is_binary(CompiledCodeBinary) ->
{ModuleName, CompiledCodeBinary};
{ok, ModuleName, CompiledCodeBinary, []} when is_binary(CompiledCodeBinary) -> % empty warnings list
{ModuleName, CompiledCodeBinary};
{ok, _ModuleName, _CompiledCodeBinary, Warnings} ->
throw({?MODULE, warnings, Warnings});
Other ->
throw({?MODULE, compile_forms, Other})
end.
get_forms_from_src(Src) ->
case scan_and_parse(Src, none, 1, [], dict:new(), []) of
{RevForms, _} -> [{attribute, 0, file, {"compiled_from_string", 0}} | reverse([{eof, 0} | RevForms])]
end.
%%====================================================================
%% Internal functions
%%====================================================================
%%% Code from Mats Cronqvist
%%% See http://www.erlang.org/pipermail/erlang-questions/2007-March/025507.html
%%%## 'scan_and_parse'
%%%
%%% basically we call the OTP scanner and parser (erl_scan and
%%% erl_parse) line-by-line, but check each scanned line for (or
%%% definitions of) macros before parsing.
%% returns {ReverseForms, FinalMacroDict}
scan_and_parse([], _CurrFilename, _CurrLine, RevForms, MacroDict, _IncludeSearchPath) ->
{RevForms, MacroDict};
scan_and_parse(RemainingText, CurrFilename, CurrLine, RevForms, MacroDict, IncludeSearchPath) ->
case scanner(RemainingText, CurrLine, MacroDict) of
{tokens, NLine, NRemainingText, Toks} ->
{ok, Form0} = erl_parse:parse_form(Toks),
{Form, Forms} = normalize_record(Form0),
scan_and_parse(NRemainingText, CurrFilename, NLine, [Form | Forms ++ RevForms], MacroDict, IncludeSearchPath);
{macro, NLine, NRemainingText, NMacroDict} ->
scan_and_parse(NRemainingText, CurrFilename, NLine, RevForms, NMacroDict, IncludeSearchPath);
{include, NLine, NRemainingText, IncludeFilename} ->
IncludeFileRemainingTextents = read_include_file(IncludeFilename, IncludeSearchPath),
%%io:format("include file ~p contents: ~n~p~nRemainingText = ~p~n", [IncludeFilename,IncludeFileRemainingTextents, RemainingText]),
%% Modify the FILE macro to reflect the filename
%%IncludeMacroDict = dict:store('FILE', {[],IncludeFilename}, MacroDict),
IncludeMacroDict = MacroDict,
%% Process the header file (inc. any nested header files)
{RevIncludeForms, IncludedMacroDict} = scan_and_parse(IncludeFileRemainingTextents, IncludeFilename, 1, [], IncludeMacroDict, IncludeSearchPath),
%io:format("include file results = ~p~n", [R]),
%% Restore the FILE macro in the NEW MacroDict (so we keep any macros defined in the header file)
%%NMacroDict = dict:store('FILE', {[],CurrFilename}, IncludedMacroDict),
NMacroDict = IncludedMacroDict,
%% Continue with the original file
scan_and_parse(NRemainingText, CurrFilename, NLine, RevIncludeForms ++ RevForms, NMacroDict, IncludeSearchPath);
done ->
scan_and_parse([], CurrFilename, CurrLine, RevForms, MacroDict, IncludeSearchPath)
end.
scanner(Text, Line, MacroDict) ->
case erl_scan:tokens([], Text, Line) of
{done, {ok, Toks, NLine}, LeftOverChars} ->
case pre_proc(Toks, MacroDict) of
{tokens, NToks} -> {tokens, NLine, LeftOverChars, NToks};
{macro, NMacroDict} -> {macro, NLine, LeftOverChars, NMacroDict};
{include, Filename} -> {include, NLine, LeftOverChars, Filename}
end;
{more, _Continuation} ->
%% This is supposed to mean "term is not yet complete" (i.e. a '.' has
%% not been reached yet).
%% However, for some bizarre reason we also get this if there is a comment after the final '.' in a file.
%% So we check to see if Text only consists of comments.
case is_only_comments(Text) of
true ->
done;
false ->
throw({incomplete_term, _Continuation, Text, Line})
end
end.
is_only_comments(Text) -> is_only_comments(Text, not_in_comment).
is_only_comments([], _) -> true;
is_only_comments([$ | T], not_in_comment) ->
is_only_comments(T, not_in_comment); % skipping whitspace outside of comment
is_only_comments([$\t | T], not_in_comment) ->
is_only_comments(T, not_in_comment); % skipping whitspace outside of comment
is_only_comments([$\n | T], not_in_comment) ->
is_only_comments(T, not_in_comment); % skipping whitspace outside of comment
is_only_comments([$% | T], not_in_comment) -> is_only_comments(T, in_comment); % found start of a comment
is_only_comments(_, not_in_comment) -> false;
% found any significant char NOT in a comment
is_only_comments([$\n | T], in_comment) -> is_only_comments(T, not_in_comment); % found end of a comment
is_only_comments([_ | T], in_comment) -> is_only_comments(T, in_comment). % skipping over in-comment chars
%%%## 'pre-proc'
%%%
%%% have to implement a subset of the pre-processor, since epp insists
%%% on running on a file.
%%% only handles 2 cases;
%% -define(MACRO, something).
%% -define(MACRO(VAR1,VARN),{stuff,VAR1,more,stuff,VARN,extra,stuff}).
pre_proc([{'-', _}, {atom, _, define}, {'(', _}, {_, _, Name} | DefToks], MacroDict) ->
false = dict:is_key(Name, MacroDict),
case DefToks of
[{',', _} | Macro] ->
{macro, dict:store(Name, {[], macro_body_def(Macro, [])}, MacroDict)};
[{'(', _} | Macro] ->
{macro, dict:store(Name, macro_params_body_def(Macro, []), MacroDict)}
end;
pre_proc([{'-', _}, {atom, _, include}, {'(', _}, {string, _, Filename}, {')', _}, {dot, _}], _MacroDict) ->
{include, Filename};
pre_proc(Toks, MacroDict) ->
{tokens, subst_macros(Toks, MacroDict)}.
macro_params_body_def([{')', _}, {',', _} | Toks], RevParams) ->
{reverse(RevParams), macro_body_def(Toks, [])};
macro_params_body_def([{var, _, Param} | Toks], RevParams) ->
macro_params_body_def(Toks, [Param | RevParams]);
macro_params_body_def([{',', _}, {var, _, Param} | Toks], RevParams) ->
macro_params_body_def(Toks, [Param | RevParams]).
macro_body_def([{')', _}, {dot, _}], RevMacroBodyToks) ->
reverse(RevMacroBodyToks);
macro_body_def([Tok | Toks], RevMacroBodyToks) ->
macro_body_def(Toks, [Tok | RevMacroBodyToks]).
subst_macros(Toks, MacroDict) ->
reverse(subst_macros_rev(Toks, MacroDict, [])).
%% returns a reversed list of tokes
subst_macros_rev([{'?', _}, {_, LineNum, 'LINE'} | Toks], MacroDict, RevOutToks) ->
%% special-case for ?LINE, to avoid creating a new MacroDict for every line in the source file
subst_macros_rev(Toks, MacroDict, [{integer, LineNum, LineNum}] ++ RevOutToks);
subst_macros_rev([{'?', _}, {_, _, Name}, {'(', _} = Paren | Toks], MacroDict, RevOutToks) ->
case dict:fetch(Name, MacroDict) of
{[], MacroValue} ->
%% This macro does not have any vars, so ignore the fact that the invocation is followed by "(...stuff"
%% Recursively expand any macro calls inside this macro's value
%% TODO: avoid infinite expansion due to circular references (even indirect ones)
RevExpandedOtherMacrosToks = subst_macros_rev(MacroValue, MacroDict, []),
subst_macros_rev([Paren | Toks], MacroDict, RevExpandedOtherMacrosToks ++ RevOutToks);
ParamsAndBody ->
%% This macro does have vars.
%% Collect all of the passe arguments, in an ordered list
{NToks, Arguments} = subst_macros_get_args(Toks, []),
%% Expand the varibles
ExpandedParamsToks = subst_macros_subst_args_for_vars(ParamsAndBody, Arguments),
%% Recursively expand any macro calls inside this macro's value
%% TODO: avoid infinite expansion due to circular references (even indirect ones)
RevExpandedOtherMacrosToks = subst_macros_rev(ExpandedParamsToks, MacroDict, []),
subst_macros_rev(NToks, MacroDict, RevExpandedOtherMacrosToks ++ RevOutToks)
end;
subst_macros_rev([{'?', _}, {_, _, Name} | Toks], MacroDict, RevOutToks) ->
%% This macro invocation does not have arguments.
%% Therefore the definition should not have parameters
{[], MacroValue} = dict:fetch(Name, MacroDict),
%% Recursively expand any macro calls inside this macro's value
%% TODO: avoid infinite expansion due to circular references (even indirect ones)
RevExpandedOtherMacrosToks = subst_macros_rev(MacroValue, MacroDict, []),
subst_macros_rev(Toks, MacroDict, RevExpandedOtherMacrosToks ++ RevOutToks);
subst_macros_rev([Tok | Toks], MacroDict, RevOutToks) ->
subst_macros_rev(Toks, MacroDict, [Tok | RevOutToks]);
subst_macros_rev([], _MacroDict, RevOutToks) -> RevOutToks.
subst_macros_get_args([{')', _} | Toks], RevArgs) ->
{Toks, reverse(RevArgs)};
subst_macros_get_args([{',', _}, {var, _, ArgName} | Toks], RevArgs) ->
subst_macros_get_args(Toks, [ArgName | RevArgs]);
subst_macros_get_args([{var, _, ArgName} | Toks], RevArgs) ->
subst_macros_get_args(Toks, [ArgName | RevArgs]).
subst_macros_subst_args_for_vars({[], BodyToks}, []) ->
BodyToks;
subst_macros_subst_args_for_vars({[Param | Params], BodyToks}, [Arg | Args]) ->
NBodyToks = keyreplace(Param, 3, BodyToks, {var, 1, Arg}),
subst_macros_subst_args_for_vars({Params, NBodyToks}, Args).
read_include_file(Filename, IncludeSearchPath) ->
case file:path_open(IncludeSearchPath, Filename, [read, raw, binary]) of
{ok, IoDevice, FullName} ->
{ok, Data} = file:read(IoDevice, filelib:file_size(FullName)),
file:close(IoDevice),
binary_to_list(Data);
{error, Reason} ->
throw({failed_to_read_include_file, Reason, Filename, IncludeSearchPath})
end.
normalize_record({attribute, La, record, {Record, Fields}} = Form) ->
case epp:normalize_typed_record_fields(Fields) of
{typed, NewFields} ->
{{attribute, La, record, {Record, NewFields}},
[{attribute, La, type,
{{record, Record}, Fields, []}}]};
not_typed ->
{Form, []}
end;
normalize_record(Form) ->
{Form, []}.

+ 85
- 0
src/dynamicCompile/dynamic_kvs.erl View File

@ -0,0 +1,85 @@
-module(dynamic_kvs).
-define(boot_type_supervisor, supervisor).
-define(boot_type_worker, worker).
-define(boot_type_simple_worker, simple_worker).
-type boot_type() :: ?boot_type_simple_worker | ?boot_type_supervisor | ?boot_type_worker.
-define(MATH_INT32_MAX, 4294967296).
-record(boot, {module :: atom(), type :: boot_type(), hasname = true :: boolean(), params :: any()}).
-export([start/1, start/0, get_name/1, init/1, handle_call/3, new/1, new/2]).
%% API
-export([set/3]).
-record(dynamic_kvs, {
modules = [] :: [{Mod :: module(), [{Key :: atom(), Val :: any()}]}]
}).
start(FatherPID) ->
boot:start_child(FatherPID, #boot{module = ?MODULE, params = {}, type = ?boot_type_worker}).
start() ->
boot:start(#boot{module = ?MODULE, params = {}, type = ?boot_type_worker}).
get_name(_) ->
?MODULE.
init(_) ->
{ok, #dynamic_kvs{modules = []}}.
new(ModuleName) ->
new(ModuleName, []).
new(ModuleName, Kvs) ->
boot:call(?MODULE, {new, ModuleName, Kvs}).
set(ModuleName, Key, Val) ->
boot:call(?MODULE, {set, ModuleName, Key, Val}).
handle_call({new, ModuleName, Kvs}, _, DynamicKvs) ->
gen(ModuleName, Kvs),
NewDynamicKvs =
case lists:keyfind(ModuleName, 1, DynamicKvs#dynamic_kvs.modules) of
false ->
DynamicKvs#dynamic_kvs{modules = [{ModuleName, Kvs} | DynamicKvs#dynamic_kvs.modules]};
_ ->
DynamicKvs
end,
{reply, ok, NewDynamicKvs};
handle_call({set, ModuleName, Key, Val}, _, DynamicKvs) ->
case lists:keytake(ModuleName, 1, DynamicKvs#dynamic_kvs.modules) of
false ->
{reply, {error, not_find_module}, DynamicKvs};
{value, {_, Kvs}, RemainMods} ->
NewKvs = com_lists:keyreplace(Key, 1, Kvs, {Key, Val}),
gen(ModuleName, NewKvs),
{reply, ok, DynamicKvs#dynamic_kvs{modules = [{ModuleName, NewKvs} | RemainMods]}}
end.
concat(List) ->
lists:concat(List).
gen(ModuleName, Kvs) ->
Meta = meta:new(ModuleName),
Fun =
fun({K, V}, MetaTemp) ->
FunStr = concat([K, "() ->\n\t", com_util:term_to_string(V), ".\n"]),
case meta:add_func(MetaTemp, FunStr) of
{ok, MetaTemp2} -> MetaTemp2;
Error ->
io:format("module(~p) define func(~s) error ~p~n", [ModuleName, FunStr, Error]),
exit(Error)
end
end,
FinalMeta = lists:foldl(Fun, Meta, Kvs),
FinalMeta2 = meta:add_func(FinalMeta, concat(["set(Key, Val) ->\n\tdynamic_kvs:set(", ModuleName, ", Key, Val).\n"])),
case meta:compile(FinalMeta2) of
{error, Error} ->
io:format("error ~p", [Error]),
ok;
ok ->
ok
end.

+ 1136
- 0
src/dynamicCompile/meta.erl
File diff suppressed because it is too large
View File


+ 29
- 0
src/dynamicCompile/meta_session.erl View File

@ -0,0 +1,29 @@
-module(meta_session).
-include("meta.hrl").
%% parse transform 'meta_session'
-export([parse_transform/2, funstring_get/1]).
parse_transform(Forms, _Options) ->
case meta:new_from_forms(Forms) of
{ok, Meta} ->
Module = meta:get_modulename(Meta),
{ok, MetaGet} = meta:add_func(Meta, funstring_get(Module)),
{ok, MetaSet} = meta:add_func(MetaGet, funstring_set(Module)),
{ok, MetaDel} = meta:add_func(MetaSet, funstring_del(Module)),
MetaFinal = MetaDel,
meta:to_forms(MetaFinal);
Error ->
io:format("parse_transform error-----------> ~p ~n", [Error]),
Forms
end.
funstring_get(Module) ->
"get(Key, Default) -> com_util:dic_get({" ++ Module ++ ",Key},Default).".
funstring_set(Module) ->
"set(Key, Value) -> com_util:dic_set({" ++ Module ++ ",Key},Value).".
funstring_del(Module) ->
"del(Key) -> com_util:dic_erase({" ++ Module ++ ",Key}).".

+ 186
- 0
src/dynamicCompile/reloader.erl View File

@ -0,0 +1,186 @@
%% The MIT License (MIT)
%%
%% Copyright (c) 2014-2024
%% Savin Max <mafei.198@gmail.com>
%%
%% Permission is hereby granted, free of charge, to any person obtaining a copy
%% of this software and associated documentation files (the "Software"), to deal
%% in the Software without restriction, including without limitation the rights
%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
%% copies of the Software, and to permit persons to whom the Software is
%% furnished to do so, subject to the following conditions:
%%
%% The above copyright notice and this permission notice shall be included in all
%% copies or substantial portions of the Software.
%%
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
%% SOFTWARE.
%%
%% @doc Erlang module for automatically reloading modified modules
%% during development.
-module(reloader).
-include_lib("kernel/include/file.hrl").
-behaviour(gen_server).
-export([start/0, start_link/0]).
-export([register_after_reload/1]).
-export([stop/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-export([all_changed/0]).
-export([is_changed/1]).
-export([reload_modules/1]).
-record(state, {last,
tref,
after_reload_callback}).
%% External API
%% @spec start() -> ServerRet
%% @doc Start the reloader.
start() ->
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
%% @spec start_link() -> ServerRet
%% @doc Start the reloader.
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
%% @spec stop() -> ok
%% @doc Stop the reloader.
stop() ->
gen_server:call(?MODULE, stop).
register_after_reload(Fun) ->
gen_server:call(?MODULE, {register_after_reload, Fun}).
%% gen_server callbacks
%% @spec init([]) -> {ok, State}
%% @doc gen_server init, opens the server in an initial state.
init([]) ->
{ok, TRef} = timer:send_interval(timer:seconds(1), doit),
{ok, #state{last = stamp(), tref = TRef}}.
%% @spec handle_call(Args, From, State) -> tuple()
%% @doc gen_server callback.
handle_call(stop, _From, State) ->
{stop, shutdown, stopped, State};
handle_call({register_after_reload, Fun}, _From, State) ->
{reply, ok, State#state{after_reload_callback = Fun}};
handle_call(_Req, _From, State) ->
{reply, {error, badrequest}, State}.
%% @spec handle_cast(Cast, State) -> tuple()
%% @doc gen_server callback.
handle_cast(_Req, State) ->
{noreply, State}.
%% @spec handle_info(Info, State) -> tuple()
%% @doc gen_server callback.
handle_info(doit, #state{after_reload_callback = Fun} = State) ->
Now = stamp(),
ReloadedModules = doit(State#state.last, Now),
if
ReloadedModules =/= [] andalso Fun =/= undefined ->
Fun(ReloadedModules);
true -> ok
end,
{noreply, State#state{last = Now}};
handle_info(_Info, State) ->
{noreply, State}.
%% @spec terminate(Reason, State) -> ok
%% @doc gen_server termination callback.
terminate(_Reason, State) ->
{ok, cancel} = timer:cancel(State#state.tref),
ok.
%% @spec code_change(_OldVsn, State, _Extra) -> State
%% @doc gen_server code_change callback (trivial).
code_change(_Vsn, State, _Extra) ->
{ok, State}.
%% @spec reload_modules([atom()]) -> [{module, atom()} | {error, term()}]
%% @doc code:purge/1 and code:load_file/1 the given list of modules in order,
%% return the results of code:load_file/1.
reload_modules(Modules) ->
[begin code:purge(M), code:load_file(M) end || M <- Modules].
%% @spec all_changed() -> [atom()]
%% @doc Return a list of beam modules that have changed.
all_changed() ->
[M || {M, Fn} <- code:all_loaded(), is_list(Fn), is_changed(M)].
%% @spec is_changed(atom()) -> boolean()
%% @doc true if the loaded module is a beam with a vsn attribute
%% and does not match the on-disk beam file, returns false otherwise.
is_changed(M) ->
try
module_vsn(M:module_info()) =/= module_vsn(code:get_object_code(M))
catch _:_ ->
false
end.
%% Internal API
module_vsn({M, Beam, _Fn}) ->
{ok, {M, Vsn}} = beam_lib:version(Beam),
Vsn;
module_vsn(L) when is_list(L) ->
{_, Attrs} = lists:keyfind(attributes, 1, L),
{_, Vsn} = lists:keyfind(vsn, 1, Attrs),
Vsn.
doit(From, To) ->
lists:foldl(fun({Module, Filename}, Acc) ->
case is_list(Filename) of
false -> Acc;
true ->
case file:read_file_info(Filename) of
{ok, #file_info{mtime = Mtime}} when Mtime >= From, Mtime < To ->
case reload(Module) of
error -> Acc;
_ -> [Module|Acc]
end;
{ok, _} ->
% unmodified;
Acc;
{error, enoent} ->
%% The Erlang compiler deletes existing .beam files if
%% recompiling fails. Maybe it's worth spitting out a
%% warning here, but I'd want to limit it to just once.
% gone;
Acc;
{error, Reason} ->
io:format("Error reading ~s's file info: ~p~n",
[Filename, Reason]),
% error
Acc
end
end
end, [], code:all_loaded()).
reload(Module) ->
io:format("Reloading ~p ...", [Module]),
code:purge(Module),
case code:load_file(Module) of
{module, Module} ->
io:format(" ok.~n"),
reload;
{error, Reason} ->
io:format(" fail: ~p.~n", [Reason]),
error
end.
stamp() ->
erlang:localtime().

+ 1049
- 0
src/dynamicCompile/smerl.erl
File diff suppressed because it is too large
View File


+ 159
- 0
src/httpSocket/http.erl View File

@ -0,0 +1,159 @@
-module(http).
-behaviour(gen_server).
-define(JSON_CONTENT, {"Content-Type", "application/json"}).
-define(HTTP_CLIENT_TIMEOUT, 10000).
%% Connection pool size: 100, Each connection queue size: 100
-define(HTTP_CLIENT_OPTIONS, [{max_sessions, 100}, {max_pipeline_size, 100}]).
-define(SERVER, ?MODULE).
-define(RESEND_AFTER_DELAY, 20000). % 20 secs
-define(MAX_RETRY, 13).
%% API
-export([start_link/0,
request/3,
async_request/3,
queue_request/3,
queue_request/4]).
%% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-record(state, {}).
request(Url, Method, Params) ->
request(Url, Method, Params, false).
async_request(Url, Method, Params) ->
request(Url, Method, Params, true).
request(Url, Method, Params, IsAsync) ->
Options = case IsAsync of
true -> ?HTTP_CLIENT_OPTIONS ++ [{stream_to, self()}];
false -> ?HTTP_CLIENT_OPTIONS
end,
JsonString = case Params of
[] -> Params;
_ -> jsx:encode(Params)
end,
ibrowse:send_req(Url, [?JSON_CONTENT], Method, JsonString, Options, ?HTTP_CLIENT_TIMEOUT).
%%%===================================================================
%%% API
%%%===================================================================
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
queue_request(Url, Method, Params) ->
gen_server:cast(?SERVER, {queue_request, Url, Method, Params, undefined}).
queue_request(Url, Method, Params, CallBack) ->
gen_server:cast(?SERVER, {queue_request, Url, Method, Params, CallBack}).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
-record(request, {uuid, url, method, params, retry}).
init([]) ->
erlang:send_after(?RESEND_AFTER_DELAY, self(), resend_all_requests),
{ok, #state{}}.
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast({queue_request, Url, Method, Params, CallBack}, State) ->
Uuid = uuid_factory:gen(),
Request = #request{uuid = Uuid,
url = Url,
method = Method,
params = Params,
retry = 0},
shared_data:write(Request),
if
CallBack =:= undefined -> ok;
true -> game_counter:set({request, Uuid}, CallBack)
end,
do_send_request(Request),
{noreply, State}.
handle_info(resend_all_requests, State) ->
ets:foldl(fun(Request, _) ->
do_send_request(Request)
end, unused, request),
{noreply, State};
handle_info({resend_requst, Uuid}, State) ->
case shared_data:find(request, Uuid) of
undefined -> ok;
Request -> do_send_request(Request)
end,
{noreply, State};
handle_info({ibrowse_async_headers, ReqId, Code, _Headers}, State) ->
%% error_logger:info_msg("handle_info:Response:~p, ~p~n", [ReqId, Code]),
put({code, ReqId}, Code),
{noreply, State};
handle_info({ibrowse_async_response, ReqId, Response}, State) ->
Code = get({code, ReqId}),
Uuid = get({req_id, ReqId}),
%% error_logger:info_msg("handle_info:Response: ~p~n", [Response]),
if
Code =:= "200" ->
case game_counter:get({request, Uuid}) of
undefined -> ok;
CallBack ->
CallBack(Response),
game_counter:del({request, Uuid})
end,
shared_data:find(request, Uuid),
erase({req_id, ReqId}),
erase({code, ReqId});
true ->
case shared_data:find(request, Uuid) of
undefined -> ok;
Request ->
NewRequest = Request#request{retry = Request#request.retry + 1},
Retry = NewRequest#request.retry,
if
Retry > ?MAX_RETRY ->
shared_data:delete(request, Uuid);
true ->
shared_data:write(NewRequest),
Delay = math:pow(2, Retry) * ?RESEND_AFTER_DELAY,
logger:info("Http Resend Dealy: ~p, Uuid: ~p~n", [Delay, Uuid]),
erlang:send_after(trunc(Delay), self(), {resend_requst, Uuid})
end
end
end,
{noreply, State};
handle_info({ibrowse_async_response_end, _ReqId}, State) ->
{noreply, State};
handle_info(Msg, State) ->
error_logger:info_msg("handle_info Msg: ~p~n", [Msg]),
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
do_send_request(Request) ->
%% error_logger:info_msg("do_send_request: ~p", [Request]),
{ibrowse_req_id, _ReqId} = async_request(Request#request.url,
Request#request.method,
Request#request.params),
%%
shared_data:delete(request, Request#request.uuid).
%% put({req_id, ReqId}, Request#request.uuid).
%%

+ 344
- 0
src/httpSocket/leo_http.erl View File

@ -0,0 +1,344 @@
%%======================================================================
%%
%% Leo Commons
%%
%% Copyright (c) 2012-2017 Rakuten, Inc.
%%
%% This file is provided to you under the Apache License,
%% Version 2.0 (the "License"); you may not use this file
%% except in compliance with the License. You may obtain
%% a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing,
%% software distributed under the License is distributed on an
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
%% KIND, either express or implied. See the License for the
%% specific language governing permissions and limitations
%% under the License.
%%
%% ---------------------------------------------------------------------
%% leo_http - Utils for HTTP/S3-API
%%
%% @doc leo_http is utilities for HTTP and S3-API
%% @reference https://github.com/leo-project/leo_commons/blob/master/src/leo_http.erl
%% @end
%%======================================================================
-module(leo_http).
-author('Yoshiyuki Kanno').
-author('Yosuke Hara').
-export([key/2, key/3,
get_headers/2, get_headers/3, get_amz_headers/1,
get_headers4cow/2, get_headers4cow/3, get_amz_headers4cow/1,
rfc1123_date/1, web_date/1, url_encode/2
]).
-include_lib("eunit/include/eunit.hrl").
-define(S3_DEFAULT_ENDPOINT, <<"s3.amazonaws.com">>).
-define(SLASH_BIN, <<"/">>).
-define(EMPYT_BIN, <<>>).
%% @doc Retrieve a filename(KEY) from Host and Path.
%%
-spec(key(Host, Path) ->
{binary(), binary()} when Host::binary(),
Path::binary()).
key(Host, Path) ->
key([?S3_DEFAULT_ENDPOINT], Host, Path).
%% @doc Retrieve a filename(KEY) from Host and Path.
%%
-spec(key(EndPoints, Host, Path) ->
{binary(), binary()} when EndPoints::[binary()],
Host::binary(),
Path::binary()).
key(EndPoints, Host, Path) ->
EmptyBin = <<>>,
EndPoint = case lists:foldl(
fun(E, Acc) ->
case binary:match(Host, E) of
nomatch ->
Acc;
{_,_} ->
case (byte_size(Acc) < byte_size(E)) of
true -> E;
false -> Acc
end
end
end, EmptyBin, EndPoints) of
EmptyBin ->
[];
Ret ->
Ret
end,
key_1(EndPoint, Host, Path).
%% @doc Retrieve a filename(KEY) from Host and Path.
%% @private
key_1(EndPoint, Host, Path) ->
Index = case EndPoint of
[] ->
0;
_ ->
case binary:match(Host, EndPoint) of
nomatch ->
0;
{Pos, _} ->
Pos + 1
end
end,
key_2(Index, Host, Path).
%% @doc "S3-Bucket" is equal to the host.
%% @private
key_2(0, Host, Path) ->
case binary:match(Path, [?SLASH_BIN]) of
nomatch ->
{Host, <<Host/binary, ?SLASH_BIN/binary>>};
_ ->
[_, Top|_] = binary:split(Path, [?SLASH_BIN], [global]),
case Top of
Host ->
Path2 = binary:part(Path, {1, byte_size(Path) -1}),
case binary:split(Path2, [?SLASH_BIN], [global]) of
[] ->
{?EMPYT_BIN, Path2};
[Bucket|_] ->
{Bucket, Path2}
end;
_ ->
{Host, <<Host/binary, Path/binary>>}
end
end;
%% @doc "S3-Bucket" is included in the path
%% @private
key_2(1,_Host, ?SLASH_BIN) ->
{?EMPYT_BIN, ?SLASH_BIN};
key_2(1,_Host, Path) ->
case binary:match(Path, [?SLASH_BIN]) of
nomatch ->
{?EMPYT_BIN, ?SLASH_BIN};
_ ->
Path2 = binary:part(Path, {1, byte_size(Path) -1}),
case binary:split(Path2, [?SLASH_BIN], [global]) of
[] ->
{?EMPYT_BIN, Path2};
[Bucket|_] ->
{Bucket, Path2}
end
end;
%% @doc "S3-Bucket" is included in the host
%% @private
key_2(Index, Host, Path) ->
Bucket = binary:part(Host, {0, Index -2}),
{Bucket, <<Bucket/binary, Path/binary>>}.
%% @doc Retrieve AMZ-S3-related headers
%% assume that TreeHeaders is generated by mochiweb_header
%%
-spec(get_headers(TreeHeaders, FilterFun) ->
list() when TreeHeaders::gb_trees:tree(),
FilterFun::function()).
get_headers(TreeHeaders, FilterFun) when is_function(FilterFun) ->
Iter = gb_trees:iterator(TreeHeaders),
get_headers(Iter, FilterFun, []).
%% @doc Retrieve AMZ-S3-related headers
%% assume that TreeHeaders is generated by mochiweb_header
%%
-spec(get_headers(TreeHeaders, FilterFun, Acc) ->
list() when TreeHeaders::gb_trees:iter(),
FilterFun::function(),
Acc::[any()]
).
get_headers(Iter, FilterFun, Acc) ->
case gb_trees:next(Iter) of
none ->
Acc;
{Key, Val, Iter2} ->
case FilterFun(Key) of
true -> get_headers(Iter2, FilterFun, [Val|Acc]);
false -> get_headers(Iter2, FilterFun, Acc)
end
end.
%% @doc Retrieve headers for cowboy
%%
-spec(get_headers4cow(Headers, FilterFun) ->
list() when Headers::[any()],
FilterFun::function()).
get_headers4cow(Headers, FilterFun) when is_function(FilterFun) ->
get_headers4cow(Headers, FilterFun, []).
%% @doc Retrieve headers for Cowboy
%%
-spec(get_headers4cow(Headers, FilterFun, Acc) ->
[{string(), string()}] when Headers::[{binary(), binary()}],
FilterFun::function(),
Acc::[any()]).
get_headers4cow([], _FilterFun, Acc) ->
Acc;
get_headers4cow([{K, V}|Rest], FilterFun, Acc) when is_binary(K) ->
case FilterFun(K) of
true ->
get_headers4cow(Rest, FilterFun,
[{binary_to_list(K), binary_to_list(V)}|Acc]);
false ->
get_headers4cow(Rest, FilterFun, Acc)
end;
get_headers4cow([_|Rest], FilterFun, Acc) ->
get_headers4cow(Rest, FilterFun, Acc).
%% @doc Retrieve AMZ-S3-related headers
%%
-spec(get_amz_headers(TreeHeaders) ->
list() when TreeHeaders::gb_trees:tree()).
get_amz_headers(TreeHeaders) ->
get_headers(TreeHeaders, fun is_amz_header/1).
%% @doc Retrieve AMZ-S3-related headers for Cowboy
%%
-spec(get_amz_headers4cow(ListHeaders) ->
[{string(), string()}] when ListHeaders::[{binary(), binary()}]).
get_amz_headers4cow(ListHeaders) ->
get_headers4cow(ListHeaders, fun is_amz_header/1).
%% @doc Retrieve RFC-1123 formated data
%%
-spec(rfc1123_date(DateSec) ->
string() when DateSec::integer()).
rfc1123_date(DateSec) ->
%% NOTE:
%% Don't use http_util:rfc1123 on R14B*.
%% In this func, There is no error handling for `local_time_to_universe`
%% So badmatch could occur. This result in invoking huge context switched.
{{Y,M,D},{H,MI,S}} = calendar:gregorian_seconds_to_datetime(DateSec),
Mon = month(M),
W = weekday(Y,M,D),
lists:flatten(
io_lib:format("~3s, ~2.10.0B ~3s ~4.10B ~2.10.0B:~2.10.0B:~2.10.0B GMT",
[W, D, Mon, Y, H, MI, S])).
%% @doc Convert gregorian seconds to date formated data( YYYY-MM-DDTHH:MI:SS000Z )
%%
-spec(web_date(GregSec) ->
string() when GregSec::integer()).
web_date(GregSec) when is_integer(GregSec) ->
{{Y,M,D},{H,MI,S}} = calendar:gregorian_seconds_to_datetime(GregSec),
lists:flatten(io_lib:format("~4.10.0B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0B.000Z",[Y,M,D,H,MI,S])).
%%--------------------------------------------------------------------
%%% INTERNAL FUNCTIONS
%%--------------------------------------------------------------------
%% @doc Is it AMZ-S3's header?
%% @private
-spec(is_amz_header(string()|binary()) ->
boolean()).
is_amz_header(<<"x-amz-", _Rest/binary>>) -> true;
is_amz_header(<<"X-Amz-", _Rest/binary>>) -> true;
is_amz_header(Key) when is_binary(Key) ->
false;
is_amz_header(Key) ->
(string:str(string:to_lower(Key), "x-amz-") == 1).
%% @doc Get weekday as string
%% @private
-spec(weekday(pos_integer(), pos_integer(), pos_integer()) ->
string()).
weekday(Y, M, D) ->
weekday(calendar:day_of_the_week(Y, M, D)).
weekday(1) -> "Mon";
weekday(2) -> "Tue";
weekday(3) -> "Wed";
weekday(4) -> "Thu";
weekday(5) -> "Fri";
weekday(6) -> "Sat";
weekday(7) -> "Sun".
%% @doc Get month as string
%% @private
-spec(month(pos_integer()) ->
string()).
month( 1) -> "Jan";
month( 2) -> "Feb";
month( 3) -> "Mar";
month( 4) -> "Apr";
month( 5) -> "May";
month( 6) -> "Jun";
month( 7) -> "Jul";
month( 8) -> "Aug";
month( 9) -> "Sep";
month(10) -> "Oct";
month(11) -> "Nov";
month(12) -> "Dec".
%% @doc URL encode a string binary.
%% The `noplus' option disables the default behaviour of quoting space
%% characters, `\s', as `+'. The `upper' option overrides the default behaviour
%% of writing hex numbers using lowecase letters to using uppercase letters
%% instead.
-spec url_encode(binary(), [noplus|upper|noslash]) -> binary().
url_encode(Bin, Opts) ->
Plus = not lists:member(noplus, Opts),
Upper = lists:member(upper, Opts),
Slash = not lists:member(noslash, Opts),
url_encode(Bin, <<>>, Plus, Upper, Slash).
-spec url_encode(binary(), binary(), boolean(), boolean(), boolean()) -> binary().
url_encode(<<C, Rest/binary>>, Acc, P=Plus, U=Upper, S=Slash) ->
if C >= $0,
C =< $9 ->
url_encode(Rest, <<Acc/binary, C>>, P, U, S);
C >= $A,
C =< $Z ->
url_encode(Rest, <<Acc/binary, C>>, P, U, S);
C >= $a,
C =< $z ->
url_encode(Rest, <<Acc/binary, C>>, P, U, S);
C =:= $.;
C =:= $-;
C =:= $~;
C =:= $_ ->
url_encode(Rest, <<Acc/binary, C>>, P, U, S);
C =:= $/ , not Slash ->
url_encode(Rest, <<Acc/binary, $/>>, P, U, S);
C =:= $ , Plus ->
url_encode(Rest, <<Acc/binary, $+>>, P, U, S);
true ->
H = C band 16#F0 bsr 4,
L = C band 16#0F,
H1 = if Upper -> to_hexu(H); true -> to_hexl(H) end,
L1 = if Upper -> to_hexu(L); true -> to_hexl(L) end,
url_encode(Rest, <<Acc/binary, $%, H1, L1>>, P, U, S)
end;
url_encode(<<>>, Acc, _Plus, _Upper, _Slash) ->
Acc.
-spec to_hexu(byte()) -> byte().
to_hexu(C) when C < 10 -> $0 + C;
to_hexu(C) when C < 17 -> $A + C - 10.
-spec to_hexl(byte()) -> byte().
to_hexl(C) when C < 10 -> $0 + C;
to_hexl(C) when C < 17 -> $a + C - 10.

+ 55
- 0
src/httpSocket/utHttpOn.erl View File

@ -0,0 +1,55 @@
-module(utHttpOn).
%% API
-export([]).
%% API
-export([
req/6, %% Post, Put, Delete Only
req_get/3, %% Get Only
url_encode/1
]).
-export([get_unique_ref/1, get_conv_ref/0]).
get_conv_ref() ->
get_unique_ref(20).
get_unique_ref(Length) ->
IntPass = random_num(36, Length),
list_to_binary(lists:flatten(io_lib:format("~.36B", [IntPass]))).
random_num(NumeralSystemBase, Length) ->
Min = round(math:pow(NumeralSystemBase, Length - 1)),
Max = round(math:pow(NumeralSystemBase, Length)),
crypto:rand_uniform(Min, Max).
req(Method, Url, Headers, ContType, Body, HttpOpts)
when Method == post orelse Method == put orelse Method == delete ->
HttpClOpts = [{sync, true}, {body_format, binary}],
Resp = httpc:request(Method, {eu_types:to_list(Url), Headers, ContType, Body}, HttpOpts, HttpClOpts),
minimize_resp(Resp).
req_get(Url, Headers, HttpOpts) ->
HttpClOpts = [{sync, true}, {body_format, binary}],
Resp = httpc:request(get, {eu_types:to_list(Url), Headers}, HttpOpts, HttpClOpts),
minimize_resp(Resp).
minimize_resp(Resp) ->
case Resp of
{ok, {{_NewVrsn, 200, _}, _Headers, RespBody}} ->
{ok, 200, RespBody};
{ok, {{_NewVrsn, HttpCode, _}, _Headers, RespBody}} ->
{error, HttpCode, RespBody};
Any -> Any
end.
url_encode(Data) ->
url_encode(Data, "").
url_encode([], Acc) ->
Acc;
url_encode([{Key, Value} | R], "") ->
url_encode(R, edoc_lib:escape_uri(Key) ++ "=" ++ edoc_lib:escape_uri(Value));
url_encode([{Key, Value} | R], Acc) ->
url_encode(R, Acc ++ "&" ++ edoc_lib:escape_uri(Key) ++ "=" ++ edoc_lib:escape_uri(Value)).

+ 88
- 0
src/httpSocket/utHttpUrl.erl View File

@ -0,0 +1,88 @@
-module(utHttpUrl).
%% API
-export([]).
-export([
url_encode_request_body/1,
url_encode/1,
integer_to_hex/1,
get_ip_from_headers/2,
get_x_real_ip/1
]).
url_encode_request_body(Args) ->
lists:concat(
lists:foldl(
fun(Rec, []) -> [Rec]; (Rec, Ac) -> [Rec, "&" | Ac] end,
[],
[K ++ "=" ++ url_encode(V) || {K, V} <- Args]
)
).
%% encode url params
url_encode(T) when is_binary(T) ->
binary_to_list(T);
url_encode([H | T]) ->
if
H >= $a, $z >= H ->
[H | url_encode(T)];
H >= $A, $Z >= H ->
[H | url_encode(T)];
H >= $0, $9 >= H ->
[H | url_encode(T)];
H == $_; H == $.; H == $-; H == $/; H == $: -> % FIXME: more..
[H | url_encode(T)];
true ->
case integer_to_hex(H) of
[X, Y] ->
[$%, X, Y | url_encode(T)];
[X] ->
[$%, $0, X | url_encode(T)]
end
end;
url_encode([]) -> [].
integer_to_hex(I) ->
case catch erlang:integer_to_list(I, 16) of
{'EXIT', _} ->
old_integer_to_hex(I);
Int ->
Int
end.
old_integer_to_hex(I) when I < 10 ->
integer_to_list(I);
old_integer_to_hex(I) when I < 16 ->
[I - 10 + $A];
old_integer_to_hex(I) when I >= 16 ->
N = trunc(I / 16),
old_integer_to_hex(N) ++ old_integer_to_hex(I rem 16).
get_ip_from_headers(PeerIp, Headers) ->
RealIp = proplists:get_value(<<"x-real-ip">>, Headers),
ForwardedForRaw = proplists:get_value(<<"x-forwarded-for">>, Headers),
ForwardedFor = case ForwardedForRaw of
undefined -> undefined;
ForwardedForRaw ->
case re:run(ForwardedForRaw, "^(?<first_ip>[^\\,]+)",
[{capture, [first_ip], binary}]) of
{match, [FirstIp]} -> FirstIp;
_Any -> undefined
end
end,
{ok, PeerAddr} = if
is_binary(RealIp) -> inet_parse:address(binary_to_list(RealIp));
is_binary(ForwardedFor) -> inet_parse:address(binary_to_list(ForwardedFor));
true -> {ok, PeerIp}
end,
PeerAddr.
get_x_real_ip(Headers) ->
RealIp = proplists:get_value(<<"x-real-ip">>, Headers),
RealIp.

+ 29
- 0
src/httpSocket/utSocket.erl View File

@ -0,0 +1,29 @@
-module(utSocket).
-export([
socket2ip/1
, socket2port/1
, str2ip/1
]).
%% socket转IP
socket2ip(Socket) ->
case inet:peername(Socket) of
{ok, {{A, B, C, D}, _}} ->
string:join(lists:map(fun com_type:to_list/1, [A, B, C, D]), ".");
_ ->
""
end.
socket2port(Socket) ->
case inet:peername(Socket) of
{ok, {_, Port}} -> Port;
_ -> 0
end.
str2ip(IP) when is_list(IP) ->
[A1, A2, A3, A4] = string:tokens(IP, "."),
{list_to_integer(A1), list_to_integer(A2), list_to_integer(A3), list_to_integer(A4)};
str2ip(IP) when is_tuple(IP) ->
IP.

+ 14
- 6
src/timeDate/utTime.erl View File

@ -556,21 +556,29 @@ lMonthName(11) -> <<"November">>;
lMonthName(12) -> <<"December">>.
%% version
-spec dateNum() -> integer().
dateNum() ->
-spec dateNumber() -> integer().
dateNumber() ->
{Year, Month, Day} = erlang:date(),
Year * 10000 + Month * 100 + Day.
%% version
-spec dateNum(date()) -> integer().
dateNum({Year, Month, Day}) ->
-spec dateNumber(date()) -> integer().
dateNumber({Year, Month, Day}) ->
Year * 10000 + Month * 100 + Day.
%% version
-spec dateNum(Year :: year(), Month :: month(), Day :: day()) -> integer().
dateNum(Year, Month, Day) ->
-spec dateNumber(Year :: year(), Month :: month(), Day :: day()) -> integer().
dateNumber(Year, Month, Day) ->
Year * 10000 + Month * 100 + Day.
%% dateNumber
-spec numberDate(DateNumber :: integer()) -> date().
numberDate(DateNumber) ->
Y = DateNumber div 10000,
M = DateNumber rem 10000 div 100,
D = DateNumber rem 100,
{Y, M, D}.
%% time()
-spec secToDayTime(Secs :: timestamp()) -> {Day :: integer(), Time :: time()}.
secToDayTime(Secs) ->

Loading…
Cancel
Save