Damn ugly HTTP interface "equivalent" for machi_cr_client.erl basic API
This goes to show that mixing implementation and protocol and API and lots of other stuff ... is cool for the quick hack to do one thing but really sucks when trying to do more than one thing. * Proof-of-concept only: add HTTP/1.0'ish 'PUT' interface to be the rough equivalent of machi_cr_client:append_chunk/3 * Proof-of-concept only: add HTTP/1.0'ish 'GET' interface to be the rough equivalent of machi_cr_client:read_chunk/4 Example use: `append_chunk` % curl http://127.0.0.1:4444/foo -0 -T /etc/hosts -v * Hostname was NOT found in DNS cache * Trying 127.0.0.1... * Connected to 127.0.0.1 (127.0.0.1) port 4444 (#0) > PUT /foo HTTP/1.0 > User-Agent: curl/7.37.1 > Host: 127.0.0.1:4444 > Accept: */* > Content-Length: 338 > * We are completely uploaded and fine * HTTP 1.0, assume close after body < HTTP/1.0 201 Created < Location: foo.50EI18AX.21 < X-Offset: 3052 < X-Size: 338 < * Closing connection 0 Example_use: `read_chunk` curl 'http://127.0.0.1:4444/foo.50EI18AX.21?offset=3052&size=338' -0 -v * Hostname was NOT found in DNS cache * Trying 127.0.0.1... * Connected to 127.0.0.1 (127.0.0.1) port 4444 (#0) > GET /foo.50EI18AX.21?offset=3052&size=338 HTTP/1.0 > User-Agent: curl/7.37.1 > Host: 127.0.0.1:4444 > Accept: */* > * HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Content-Length: 338 < ## # Host Database # # localhost is used to configure the loopback interface # when the system is booting. Do not change this entry. ## 127.0.0.1 localhost 127.0.0.1 test.localhost 255.255.255.255 broadcasthost ::1 localhost fe80::1%lo0 localhost # Xxxxxxx Yyyyy 192.168.99.222 zzzzz 127.0.0.1 aaaaaaaa.bb.ccccccccc.com * Closing connection 0
This commit is contained in:
parent
6575872c88
commit
6cebf39723
1 changed files with 130 additions and 3 deletions
|
@ -96,6 +96,10 @@
|
|||
props = [] :: list() % proplist
|
||||
}).
|
||||
|
||||
-record(http_goop, {
|
||||
len % content-length
|
||||
}).
|
||||
|
||||
start_link([{FluName, TcpPort, DataDir}|Rest])
|
||||
when is_atom(FluName), is_integer(TcpPort), is_list(DataDir) ->
|
||||
{ok, spawn_link(fun() -> main2(FluName, TcpPort, DataDir, Rest) end)}.
|
||||
|
@ -328,6 +332,10 @@ net_server_loop(Sock, #state{flu_name=FluName, data_dir=DataDir}=S) ->
|
|||
do_projection_command(Sock, LenHex, S);
|
||||
<<"WEDGE-STATUS\n">> ->
|
||||
do_wedge_status(FluName, Sock);
|
||||
<<"PUT ", _/binary>>=PutLine ->
|
||||
http_server_hack(FluName, PutLine, Sock, S);
|
||||
<<"GET ", _/binary>>=PutLine ->
|
||||
http_server_hack(FluName, PutLine, Sock, S);
|
||||
_ ->
|
||||
machi_util:verb("Else Got: ~p\n", [Line]),
|
||||
io:format(user, "TODO: Else Got: ~p\n", [Line]),
|
||||
|
@ -446,6 +454,21 @@ do_net_server_readwrite_common(Sock, OffsetHex, LenHex, FileBin, DataDir,
|
|||
do_net_server_readwrite_common2(Sock, OffsetHex, LenHex, FileBin, DataDir,
|
||||
FileOpts, DoItFun,
|
||||
EpochID, Wedged_p, CurrentEpochId) ->
|
||||
NoSuchFileFun = fun(Sck) ->
|
||||
ok = gen_tcp:send(Sck, <<"ERROR NO-SUCH-FILE\n">>)
|
||||
end,
|
||||
BadIoFun = fun(Sck) ->
|
||||
ok = gen_tcp:send(Sck, <<"ERROR BAD-IO\n">>)
|
||||
end,
|
||||
do_net_server_readwrite_common2(Sock, OffsetHex, LenHex, FileBin, DataDir,
|
||||
FileOpts, DoItFun,
|
||||
EpochID, Wedged_p, CurrentEpochId,
|
||||
NoSuchFileFun, BadIoFun).
|
||||
|
||||
do_net_server_readwrite_common2(Sock, OffsetHex, LenHex, FileBin, DataDir,
|
||||
FileOpts, DoItFun,
|
||||
EpochID, Wedged_p, CurrentEpochId,
|
||||
NoSuchFileFun, BadIoFun) ->
|
||||
<<Offset:64/big>> = machi_util:hexstr_to_bin(OffsetHex),
|
||||
<<Len:32/big>> = machi_util:hexstr_to_bin(LenHex),
|
||||
{_, Path} = machi_util:make_data_filename(DataDir, FileBin),
|
||||
|
@ -464,12 +487,11 @@ do_net_server_readwrite_common2(Sock, OffsetHex, LenHex, FileBin, DataDir,
|
|||
FileOpts, DoItFun,
|
||||
EpochID, Wedged_p, CurrentEpochId);
|
||||
{error, enoent} when OptsHasRead ->
|
||||
ok = gen_tcp:send(Sock, <<"ERROR NO-SUCH-FILE\n">>);
|
||||
ok = NoSuchFileFun(Sock);
|
||||
_Else ->
|
||||
ok = gen_tcp:send(Sock, <<"ERROR BAD-IO\n">>)
|
||||
ok = BadIoFun(Sock)
|
||||
end.
|
||||
|
||||
|
||||
do_net_server_write(Sock, OffsetHex, LenHex, FileBin, DataDir,
|
||||
EpochID, Wedged_p, CurrentEpochId) ->
|
||||
CSumPath = machi_util:make_checksum_filename(DataDir, FileBin),
|
||||
|
@ -816,3 +838,108 @@ make_listener_regname(BaseName) ->
|
|||
|
||||
make_projection_server_regname(BaseName) ->
|
||||
list_to_atom(atom_to_list(BaseName) ++ "_pstore2").
|
||||
|
||||
http_server_hack(FluName, Line1, Sock, S) ->
|
||||
{ok, {http_request, HttpOp, URI0, _HttpV}, _x} =
|
||||
erlang:decode_packet(http_bin, Line1, [{line_length,4095}]),
|
||||
MyURI = case URI0 of
|
||||
{abs_path, Path} -> <<"/", Rest/binary>> = Path,
|
||||
Rest;
|
||||
_ -> URI0
|
||||
end,
|
||||
Hdrs = http_harvest_headers(Sock),
|
||||
G = digest_header_goop(Hdrs, #http_goop{}),
|
||||
case HttpOp of
|
||||
'PUT' ->
|
||||
http_server_hack_put(Sock, G, FluName, MyURI);
|
||||
'GET' ->
|
||||
http_server_hack_get(Sock, G, FluName, MyURI, S)
|
||||
end,
|
||||
ok = gen_tcp:close(Sock),
|
||||
exit(normal).
|
||||
|
||||
http_server_hack_put(Sock, G, FluName, MyURI) ->
|
||||
ok = inet:setopts(Sock, [{packet, raw}]),
|
||||
{ok, Chunk} = gen_tcp:recv(Sock, G#http_goop.len, 60*1000),
|
||||
CSum = machi_util:checksum_chunk(Chunk),
|
||||
try
|
||||
FluName ! {seq_append, self(), MyURI, Chunk, CSum, 0}
|
||||
catch error:badarg ->
|
||||
error_logger:error_msg("Message send to ~p gave badarg, make certain server is running with correct registered name\n", [?MODULE])
|
||||
end,
|
||||
receive
|
||||
{assignment, Offset, File} ->
|
||||
Out = io_lib:format("HTTP/1.0 201 Created\r\nLocation: ~s\r\n"
|
||||
"X-Offset: ~w\r\nX-Size: ~w\r\n\r\n",
|
||||
[File, Offset, byte_size(Chunk)]),
|
||||
ok = gen_tcp:send(Sock, Out);
|
||||
wedged ->
|
||||
ok = gen_tcp:send(Sock, <<"HTTP/1.0 499 WEDGED\r\n\r\n">>)
|
||||
after 10*1000 ->
|
||||
ok = gen_tcp:send(Sock, <<"HTTP/1.0 499 TIMEOUT\r\n\r\n">>)
|
||||
end.
|
||||
|
||||
http_server_hack_get(Sock, _G, _FluName, MyURI, S) ->
|
||||
DataDir = S#state.data_dir,
|
||||
{Wedged_p, CurrentEpochId} = ets:lookup_element(S#state.etstab, epoch, 2),
|
||||
EpochID = <<"unused">>,
|
||||
NoSuchFileFun = fun(Sck) ->
|
||||
ok = gen_tcp:send(Sck, "HTTP/1.0 455 NOT-WRITTEN\r\n\r\n")
|
||||
end,
|
||||
BadIoFun = fun(Sck) ->
|
||||
ok = gen_tcp:send(Sck, "HTTP/1.0 466 BAD-IO\r\n\r\n")
|
||||
end,
|
||||
DoItFun = fun(FH, Offset, Len) ->
|
||||
case file:pread(FH, Offset, Len) of
|
||||
{ok, Bytes} when byte_size(Bytes) == Len ->
|
||||
Hdrs = io_lib:format("HTTP/1.0 200 OK\r\nContent-Length: ~w\r\n\r\n", [Len]),
|
||||
gen_tcp:send(Sock, [Hdrs, Bytes]);
|
||||
{ok, Bytes} ->
|
||||
machi_util:verb("ok read but wanted ~p got ~p: ~p @ offset ~p\n",
|
||||
[Len, size(Bytes), Bytes, Offset]),
|
||||
ok = gen_tcp:send(Sock, "HTTP/1.0 455 PARTIAL-READ\r\n\r\n");
|
||||
eof ->
|
||||
ok = gen_tcp:send(Sock, "HTTP/1.0 455 NOT-WRITTEN\r\n\r\n");
|
||||
_Else2 ->
|
||||
machi_util:verb("Else2 ~p ~p ~P\n",
|
||||
[Offset, Len, _Else2, 20]),
|
||||
ok = gen_tcp:send(Sock, "HTTP/1.0 466 ERROR BAD-READ\r\n\r\n")
|
||||
end
|
||||
end,
|
||||
[File, OptsBin] = binary:split(MyURI, <<"?">>),
|
||||
Opts = split_uri_options(OptsBin),
|
||||
OffsetHex = machi_util:int_to_hexstr(proplists:get_value(offset, Opts), 64),
|
||||
LenHex = machi_util:int_to_hexstr(proplists:get_value(size, Opts), 32),
|
||||
do_net_server_readwrite_common2(Sock, OffsetHex, LenHex, File, DataDir,
|
||||
[read, binary, raw], DoItFun,
|
||||
EpochID, Wedged_p, CurrentEpochId,
|
||||
NoSuchFileFun, BadIoFun).
|
||||
|
||||
http_harvest_headers(Sock) ->
|
||||
ok = inet:setopts(Sock, [{packet, httph}]),
|
||||
http_harvest_headers(gen_tcp:recv(Sock, 0, ?SERVER_CMD_READ_TIMEOUT),
|
||||
Sock, []).
|
||||
|
||||
http_harvest_headers({ok, http_eoh}, _Sock, Acc) ->
|
||||
Acc;
|
||||
http_harvest_headers({error, _}, _Sock, _Acc) ->
|
||||
[];
|
||||
http_harvest_headers({ok, Hdr}, Sock, Acc) ->
|
||||
http_harvest_headers(gen_tcp:recv(Sock, 0, ?SERVER_CMD_READ_TIMEOUT),
|
||||
Sock, [Hdr|Acc]).
|
||||
|
||||
digest_header_goop([], G) ->
|
||||
G;
|
||||
digest_header_goop([{http_header, _, 'Content-Length', _, Str}|T], G) ->
|
||||
digest_header_goop(T, G#http_goop{len=list_to_integer(Str)});
|
||||
digest_header_goop([_H|T], G) ->
|
||||
digest_header_goop(T, G).
|
||||
|
||||
split_uri_options(OpsBin) ->
|
||||
L = binary:split(OpsBin, <<"&">>),
|
||||
[case binary:split(X, <<"=">>) of
|
||||
[<<"offset">>, Bin] ->
|
||||
{offset, binary_to_integer(Bin)};
|
||||
[<<"size">>, Bin] ->
|
||||
{size, binary_to_integer(Bin)}
|
||||
end || X <- L].
|
||||
|
|
Loading…
Reference in a new issue