Permalink
Browse files

Implement path-based claims, as well as vhost-based claims

  • Loading branch information...
1 parent 8707f11 commit 59180800272899d080597849f53679e950d71f73 @tonyg committed Jun 17, 2009
Showing with 190 additions and 142 deletions.
  1. +0 −2 NOTES
  2. +15 −29 src/reflect_meta.erl
  3. +61 −96 src/reflect_request_queue.erl
  4. +27 −0 src/reqparser_path.erl
  5. +24 −0 src/reqparser_vhost.erl
  6. +7 −4 src/reversehttp.app
  7. +46 −0 src/reversehttp.erl
  8. +10 −11 src/reversehttp_web.erl
View
2 NOTES
@@ -16,8 +16,6 @@ When considering policies for unsubscription, perhaps include a
generation-number with a subscription? That way, flapping can be
avoided by auto-unsubscribing only a given generation...
-Support non-vhost-based claims
-
Investigate the use of global:register_name to replace the ets table
- works, but slow; mnesia better?
View
@@ -1,33 +1,25 @@
-module(reflect_meta).
--export([handle/4]).
+-export([handle/5]).
-define(RPC_TIMEOUT, 10000).
-handle(Req, AP, [], _QueryFields) ->
+handle(Req, _Config, AccessUrl, [], _QueryFields) ->
reply_with(Req, "Access Point",
- gen_info(Req, AP) ++
- [{h2, <<"Registered Labels">>},
+ gen_info(Req, AccessUrl) ++
+ [{h2, <<"Registered Delegations">>},
{table, [{class, "meta-table"}],
[{tr, [], [{th, X} ||
- X <- [<<"Label root">>, <<"Details">>]]}] ++
- [format_vhost(AP, X) || X <- all_vhosts()]}]).
+ X <- [<<"Root URL">>, <<"Details">>]]}] ++
+ [format_vhost(X) || X <- all_vhosts()]}]);
+handle(Req, _Config, _AccessUrl, _PathComponents, _QueryFields) ->
+ Req:not_found().
-gen_info(Req, AP) ->
+gen_info(Req, AccessUrl) ->
[{h2, <<"General Info">>},
- {p, [<<"Access point info: ">>, hlink(apurl(Req, AP))]},
+ {p, [<<"Access point info: ">>, hlink(AccessUrl)]},
{p, [<<"Site root: ">>, hlink("http://" ++ Req:get_header_value(host) ++ "/")]}].
-apurl(Req, AP) ->
- "http://" ++ Req:get_header_value(host) ++ AP.
-
-vhroot(VHost) ->
- "http://" ++ VHost ++ "/".
-
-%% vhurl(AP, VHost) ->
-%% [Label | Labels] = string:tokens(VHost, "."),
-%% "http://" ++ string:join(Labels, ".") ++ AP ++ "/" ++ Label.
-
template(Title, BodyElts) ->
{html,
[{head,
@@ -61,12 +53,6 @@ all_vhosts() ->
%% (N = {reflect_vhost_manager, request, H, RN}) <- global:registered_names(),
%% H =:= HostLabel ].
-keyget(K, L, Def) ->
- case lists:keysearch(K, 1, L) of
- {value, {_, V}} -> V;
- false -> Def
- end.
-
explain_status(infinity, PollerCount, _RequestCount) ->
integer_to_list(PollerCount) ++
case PollerCount of
@@ -80,10 +66,10 @@ explain_status(ExpiryMs, _PollerCount, RequestCount) ->
_ -> "ms; " ++ integer_to_list(RequestCount) ++ " request(s) waiting"
end.
-format_vhost(_AP, VHost) ->
+format_vhost(VHost) ->
Info = reflect_vhost_manager:info(VHost),
{tr, [],
- [{td, [], [hlink(vhroot(VHost))]},
- {td, [], [explain_status(keyget(expiry_ms, Info, 0),
- keyget(poller_count, Info, 0),
- keyget(request_count, Info, 0))]}]}.
+ [{td, [], [hlink(VHost)]},
+ {td, [], [explain_status(reversehttp:lookup(expiry_ms, Info, 0),
+ reversehttp:lookup(poller_count, Info, 0),
+ reversehttp:lookup(request_count, Info, 0))]}]}.
@@ -9,67 +9,50 @@
%% A reversehttp request can be one of the following:
%%
%% - a request for real content from the main vhost
-%% - a request to set up a virtual host
-%% - a poll for requests sent to a virtual host
-%% - a reply to a request sent to a virtual host
+%% - a request to set up a delegated portion of URL space
+%% - a poll for requests sent to a delegated portion of URL space
+%% - a reply to a request sent to a delegated portion of URL space
%% - a tunnelled outbound request to be relayed
%%
%% First and foremost, if it's a request for a virtual host, we need
%% to pass it through unmolested. Otherwise, we get to examine the
%% request further to see which of the other four categories it falls
%% into.
-handle(Req, ExceptionHosts) ->
- case Req:get_header_value(host) of
- undefined ->
- error(Req, 400, "Missing Host HTTP header");
- MixedCaseHost ->
- Host = string:to_lower(MixedCaseHost),
- case lists:keysearch(Host, 1, ExceptionHosts) of
- {value, {_, AccessPoints}} ->
- %% The request was for one of our configured
- %% access-point hosts. Now, check the raw_path to
- %% see if it matches an access-point path.
- handle_exception_host(Req, AccessPoints);
- false ->
- single_request(Req, Host)
- end
- end.
-
-%%--------------------------------------------------------------------
-
-handle_exception_host(Req, AccessPoints) ->
- {Path, QueryPart, _Fragment} = mochiweb_util:urlsplit_path(Req:get(raw_path)),
- case find_access_point(Path, AccessPoints) of
- {ok, AccessPoint, PathComponents} ->
- %% The request was for one of our access points.
- QueryFields = mochiweb_util:parse_qs(QueryPart),
+handle(Req, Config) ->
+ Mod = reqparser(Config),
+ case Mod:analyse(Req, Config) of
+ {error, Code, ExplanatoryBodyText} ->
+ error(Req, Code, ExplanatoryBodyText),
+ ok;
+ {single_request, ExternalAppUrlPrefix} ->
+ single_request(Req, ExternalAppUrlPrefix),
+ ok;
+ {normal, Path} ->
+ {normal, Path};
+ {access_point, AccessUrl, PathComponents, QueryFields} ->
handle_reverse_http(Req,
+ Config,
Req:get(method),
- AccessPoint,
+ AccessUrl,
PathComponents,
- QueryFields);
- {error, not_found} ->
- %% It's a request for content from the main vhost(s). The
- %% special token exception_host is passed back to our
- %% caller to indicate that they should serve content as
- %% usual.
- exception_host
+ QueryFields),
+ ok
end.
-single_request(Req, Host) ->
+single_request(Req, ExternalAppUrlPrefix) ->
case request_host(Req) of
{error, Reason} ->
error(Req, 400, "Could not determine your IP address", Reason);
{ok, RequestHost} ->
FormattedRequest = format_req(Req, Req:recv_body()),
Msg = #poll_response{requesting_client = RequestHost,
formatted_request = FormattedRequest},
- case reflect_vhost_manager:request(Host, Msg) of
+ case reflect_vhost_manager:request(ExternalAppUrlPrefix, Msg) of
{error, not_found} ->
- error(Req, 404, "Virtual host not found", Host);
+ error(Req, 404, "Delegation not found", ExternalAppUrlPrefix);
{error, noproc} ->
- error(Req, 503, "Virtual host manager crashed", Msg);
+ error(Req, 503, "Delegation manager crashed", Msg);
{error, {timeout, poller}} ->
error(Req, 504, "No available servers");
{error, {timeout, downstream}} ->
@@ -89,7 +72,7 @@ single_request(Req, Host) ->
end
end.
-handle_reverse_http(Req, 'POST', _AP, ["_relay", HostAndPort], _QueryFields) ->
+handle_reverse_http(Req, _Config, 'POST', _AccessUrl, ["_relay", HostAndPort], _QueryFields) ->
case extract_host_and_port(HostAndPort) of
{error, _} ->
error(Req, 400, "Bad host:port in relay request");
@@ -109,7 +92,7 @@ handle_reverse_http(Req, 'POST', _AP, ["_relay", HostAndPort], _QueryFields) ->
end
end;
-handle_reverse_http(Req, 'POST', AP, [], UrlQueryFields) ->
+handle_reverse_http(Req, Config, 'POST', AccessUrl, [], UrlQueryFields) ->
BodyQueryFields = case Req:recv_body() of
undefined -> [];
V -> mochiweb_util:parse_qs(V)
@@ -118,47 +101,46 @@ handle_reverse_http(Req, 'POST', AP, [], UrlQueryFields) ->
case lists:keysearch("name", 1, QueryFields) of
{value, {_, MixedCaseHostLabel}} ->
HostLabel = string:to_lower(MixedCaseHostLabel),
- Token = case lists:keysearch("token", 1, QueryFields) of
- {value, {_, T}} -> T;
- false -> random_id(HostLabel)
- end,
- LeaseSecondsStr = case lists:keysearch("lease", 1, QueryFields) of
- {value, {_, L}} -> L;
- false -> "0"
- end,
+ Token = reversehttp:lookup("token", QueryFields, random_id(HostLabel)),
+ LeaseSecondsStr = reversehttp:lookup("lease", QueryFields, "0"),
case catch list_to_integer(LeaseSecondsStr) of
{'EXIT', _} ->
error(Req, 400, "Invalid lease seconds setting", LeaseSecondsStr);
LeaseSeconds ->
ReqName = random_id({HostLabel, Token}),
- Headers = [link_header(format_request_url(Req,
- AP, HostLabel, Token, ReqName),
- "first"),
- link_header(format_ext_vhost_url(Req, HostLabel),
- "related"),
- {"Location", format_int_vhost_url(Req, AP, HostLabel, Token)}],
- Host = expand_host_label(Req, HostLabel),
- case reflect_vhost_manager:configure(Host, Token, LeaseSeconds) of
+ Mod = reqparser(Config),
+ ExternalAppUrlPrefix = Mod:ext_url(Req, Config, HostLabel),
+ FirstUrl = format_request_url(AccessUrl, HostLabel, Token, ReqName),
+ IntUrl = format_int_vhost_url(AccessUrl, HostLabel, Token),
+ Headers = [link_header(FirstUrl, "first"),
+ link_header(ExternalAppUrlPrefix, "related"),
+ {"Location", IntUrl}],
+ case reflect_vhost_manager:configure(ExternalAppUrlPrefix,
+ Token,
+ LeaseSeconds) of
{ok, existing, _Pid} ->
Req:respond({204, Headers, []});
{ok, new, _Pid} ->
Req:respond({201, Headers, []});
{error, bad_token} ->
error(Req, 403, "Bad token", HostLabel);
{error, Reason} ->
- error(Req, 502, "Could not configure vhost manager", {Host, Reason})
+ error(Req, 502, "Could not configure delegation manager",
+ {ExternalAppUrlPrefix, Reason})
end
end;
false ->
error(Req, 400, "Missing label parameter", QueryFields)
end;
-handle_reverse_http(Req, 'GET', AP, [HostLabel, Token, ReqName], _QueryFields) ->
+handle_reverse_http(Req, Config, 'GET', AccessUrl, [HostLabel, Token, ReqName], _QueryFields) ->
NextReqName = random_id({HostLabel, Token}),
- Headers = [link_header(format_request_url(Req, AP, HostLabel, Token, NextReqName), "next")],
- Host = expand_host_label(Req, HostLabel),
+ Headers = [link_header(format_request_url(AccessUrl, HostLabel, Token, NextReqName),
+ "next")],
+ Mod = reqparser(Config),
+ ExternalAppUrlPrefix = Mod:ext_url(Req, Config, HostLabel),
case reflect_vhost_manager:poll(
- Host, Token, ReqName,
+ ExternalAppUrlPrefix, Token, ReqName,
fun (#poll_response{requesting_client = RC, formatted_request = FR}) ->
Req:respond({200,
Headers ++ [{'Content-type', "message/http"},
@@ -167,7 +149,7 @@ handle_reverse_http(Req, 'GET', AP, [HostLabel, Token, ReqName], _QueryFields) -
ok
end) of
{error, noproc} ->
- error(Req, 503, "Virtual host manager crashed", HostLabel);
+ error(Req, 503, "Delegation manager crashed", HostLabel);
{error, timeout} ->
Req:respond({204, Headers, []});
{error, bad_token} ->
@@ -178,7 +160,7 @@ handle_reverse_http(Req, 'GET', AP, [HostLabel, Token, ReqName], _QueryFields) -
ok
end;
-handle_reverse_http(Req, 'POST', _AP, [HostLabel, _Token, ReqName], _QueryFields) ->
+handle_reverse_http(Req, _Config, 'POST', _AccessUrl, [HostLabel, _Token, ReqName], _QueryFields) ->
case reflect_vhost_manager:respond(
ReqName,
fun () ->
@@ -195,35 +177,27 @@ handle_reverse_http(Req, 'POST', _AP, [HostLabel, _Token, ReqName], _QueryFields
Req:respond({202, [], []})
end;
-handle_reverse_http(Req, 'GET', AP, PathComponents, QueryFields) ->
- handle_meta(Req, AP, PathComponents, QueryFields);
+handle_reverse_http(Req, Config, 'GET', AccessUrl, PathComponents, QueryFields) ->
+ handle_meta(Req, Config, AccessUrl, PathComponents, QueryFields);
-handle_reverse_http(Req, Method, AP, PathComponents, QueryFields) ->
- error(Req, 400, "Bad Reverse-HTTP Request", {Method, AP, PathComponents, QueryFields}).
+handle_reverse_http(Req, _Config, Method, AccessUrl, PathComponents, QueryFields) ->
+ error(Req, 400, "Bad Reverse-HTTP Request", {Method, AccessUrl, PathComponents, QueryFields}).
-ifdef(ENABLE_META).
-handle_meta(Req, AP, PathComponents, QueryFields) ->
- reflect_meta:handle(Req, AP, PathComponents, QueryFields).
+handle_meta(Req, Config, AccessUrl, PathComponents, QueryFields) ->
+ reflect_meta:handle(Req, Config, AccessUrl, PathComponents, QueryFields).
-else.
-handle_meta(Req, AP, PathComponents, QueryFields) ->
+handle_meta(_Req, _Config, _AccessUrl, _PathComponents, _QueryFields) ->
ok.
-endif.
%%--------------------------------------------------------------------
-expand_host_label(Req, HostLabel) ->
- HostLabel ++ "." ++ string:to_lower(Req:get_header_value(host)).
-
-format_ext_vhost_url(Req, HostLabel) ->
- "http://" ++ expand_host_label(Req, HostLabel) ++ "/".
+format_int_vhost_url(AccessUrl, HostLabel, Token) ->
+ AccessUrl ++ "/" ++ HostLabel ++ "/" ++ Token.
-format_int_vhost_url(Req, AccessPoint, HostLabel, Token) ->
- "http://" ++ Req:get_header_value(host) ++
- AccessPoint ++ "/" ++ HostLabel ++ "/" ++ Token.
-
-format_request_url(Req, AccessPoint, HostLabel, Token, ReqName) ->
- "http://" ++ Req:get_header_value(host) ++
- AccessPoint ++ "/" ++ HostLabel ++ "/" ++ Token ++ "/" ++ ReqName.
+format_request_url(AccessUrl, HostLabel, Token, ReqName) ->
+ AccessUrl ++ "/" ++ HostLabel ++ "/" ++ Token ++ "/" ++ ReqName.
link_header(Url, Rel) ->
{'Link', "<" ++ Url ++ ">; rel=\"" ++ Rel ++ "\""}.
@@ -241,18 +215,6 @@ error(Req, StatusCode, ExplanatoryBodyText, ExtraInfo) ->
ExtraInfo}),
Req:respond({StatusCode, [], integer_to_list(StatusCode) ++ " " ++ ExplanatoryBodyText}).
-find_access_point(_Path, []) ->
- {error, not_found};
-find_access_point(Path, [AccessPoint | AccessPoints]) ->
- case lists:prefix(AccessPoint, Path) of
- true ->
- StrippedPath = string:substr(Path, length(AccessPoint) + 1),
- PathComponents = string:tokens(StrippedPath, "/"),
- {ok, AccessPoint, PathComponents};
- false ->
- find_access_point(Path, AccessPoints)
- end.
-
request_host(Req) ->
Sock = Req:get(socket),
case inet:peername(Sock) of
@@ -297,3 +259,6 @@ format_req(Req, Body) ->
_ ->
[MethodLine, Headers, Body]
end.
+
+reqparser(Config) ->
+ reversehttp:lookup(request_parser_module, Config, reqparser_vhost).
View
@@ -0,0 +1,27 @@
+-module(reqparser_path).
+
+-export([analyse/2, ext_url/3]).
+
+analyse(Req, Config) ->
+ case reversehttp:match_access_point(Req, Config) of
+ V = {access_point, _, _, _} ->
+ V;
+ {normal, Path} ->
+ case extract_label(Path) of
+ {ok, HostLabel} ->
+ {single_request, ext_url(Req, Config, HostLabel)};
+ {error, no_slash} ->
+ {normal, Path}
+ end
+ end.
+
+extract_label(Path) ->
+ case string:chr(Path, $/) of
+ 0 ->
+ {error, no_slash};
+ N ->
+ {ok, string:left(Path, N - 1)}
+ end.
+
+ext_url(Req, _Config, HostLabel) ->
+ "http://" ++ string:to_lower(Req:get_header_value(host)) ++ "/" ++ HostLabel ++ "/".
Oops, something went wrong.

0 comments on commit 5918080

Please sign in to comment.