diff --git a/.travis.yml b/.travis.yml index adca6b6c..3df2a610 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ language: erlang sudo: false install: "true" # don't let travis run get-deps otp_release: + - 21.0 + - 20.0 - 19.1 - 18.3 - 17.5 diff --git a/include/wm_compat.hrl b/include/wm_compat.hrl new file mode 100644 index 00000000..672f2619 --- /dev/null +++ b/include/wm_compat.hrl @@ -0,0 +1,7 @@ +-ifndef(deprecate_stacktrace). +-define(STPATTERN(Pattern), Pattern). +-define(STACKTRACE, erlang:get_stacktrace()). +-else. +-define(STPATTERN(Pattern), Pattern:__STACKTRACE). +-define(STACKTRACE, __STACKTRACE). +-endif. diff --git a/include/wm_reqdata.hrl b/include/wm_reqdata.hrl index 551c39a6..b6841599 100644 --- a/include/wm_reqdata.hrl +++ b/include/wm_reqdata.hrl @@ -33,10 +33,10 @@ | defined_in_create, req_qs = defined_in_create :: [{string(), string()}] | defined_in_create, - req_headers :: webmachine_headers:headers(), + req_headers :: webmachine:headers(), req_body=not_fetched_yet, resp_redirect = false :: boolean(), - resp_headers = webmachine_headers:empty() :: webmachine_headers:headers(), + resp_headers = webmachine_headers:empty() :: webmachine:headers(), resp_body = <<>> :: webmachine:response_body(), %% follow_request : range responce for range request, normal responce for non-range one %% ignore_request : normal resopnse for either range reuqest or non-range one diff --git a/include/wm_resource.hrl b/include/wm_resource.hrl index 588405df..3381669c 100644 --- a/include/wm_resource.hrl +++ b/include/wm_resource.hrl @@ -1 +1 @@ --record(wm_resource, {module, modstate, modexports, trace}). +-record(wm_resource, {module, modstate, trace}). diff --git a/src/webmachine.app.src b/src/webmachine.app.src index ad14f7b1..363e92d0 100644 --- a/src/webmachine.app.src +++ b/src/webmachine.app.src @@ -23,7 +23,7 @@ %% string() for the "Server" response header ]}, - {contributors,["Sean Cribbs", "Joe DeVivo" "Bryan Fink", + {maintainers,["Sean Cribbs", "Joe DeVivo", "Bryan Fink", "Kelly McLaughlin", "Jared Morrow", "Andy Gross", "Steve Vinoski"]}, {licenses,["Apache"]}, diff --git a/src/webmachine_app.erl b/src/webmachine_app.erl index 35fa74a3..0c15a08c 100644 --- a/src/webmachine_app.erl +++ b/src/webmachine_app.erl @@ -27,7 +27,7 @@ -include("webmachine_logger.hrl"). --define(QUIP, "cafe not found"). +-define(QUIP, "greased slide to failure"). %% @spec start(_Type, _StartArgs) -> ServerRet %% @doc application start callback for webmachine. diff --git a/src/webmachine_decision_core.erl b/src/webmachine_decision_core.erl index 115f8d15..bd618ad0 100644 --- a/src/webmachine_decision_core.erl +++ b/src/webmachine_decision_core.erl @@ -23,6 +23,13 @@ -author('Bryan Fink '). -export([handle_request/2]). -include("webmachine_logger.hrl"). +-include("wm_compat.hrl"). + +%% Suppress Erlang/OTP 21 warnings about the new method to retrieve +%% stacktraces. +-ifdef(OTP_RELEASE). +-compile({nowarn_deprecated_function, [{erlang, get_stacktrace, 0}]}). +-endif. handle_request(Resource, ReqState) -> _ = [erase(X) || X <- [decision, code, req_body, bytes_written, tmp_reqstate]], diff --git a/src/webmachine_dispatcher.erl b/src/webmachine_dispatcher.erl index dffa9aa7..280fdbab 100644 --- a/src/webmachine_dispatcher.erl +++ b/src/webmachine_dispatcher.erl @@ -628,7 +628,7 @@ make_reqdata(Path) -> MochiReq = mochiweb_request:new(testing, 'GET', Path, {1, 1}, mochiweb_headers:make([])), Req = webmachine:new_request(mochiweb, MochiReq), - {RD, _} = Req:get_reqdata(), + {RD, _} = webmachine_request:get_reqdata(Req), RD. -endif. diff --git a/src/webmachine_mochiweb.erl b/src/webmachine_mochiweb.erl index 6b59daee..d75ebf9d 100644 --- a/src/webmachine_mochiweb.erl +++ b/src/webmachine_mochiweb.erl @@ -63,7 +63,7 @@ start(Options) -> webmachine_router:init_routes(DGroup, DispatchList), _ = [application_set_unless_env_or_undef(K, V) || {K, V} <- WMOptions], MochiName = list_to_atom(to_list(PName) ++ "_mochiweb"), - LoopFun = fun(X) -> loop(DGroup, X) end, + LoopFun = {?MODULE, loop, [DGroup]}, mochiweb_http:start([{name, MochiName}, {loop, LoopFun} | OtherOptions]). stop() -> @@ -74,10 +74,8 @@ stop() -> stop(Name) -> mochiweb_http:stop(Name). --spec loop(any(), - mochiweb_request()) -> - ok. -loop(Name, MochiReq) -> +-spec loop(mochiweb_request(), any()) -> ok. +loop(MochiReq, Name) -> case new_webmachine_req(MochiReq) of {{error, NewRequestError}, ErrorReq} -> handle_error(500, {error, NewRequestError}, ErrorReq); diff --git a/src/webmachine_resource.erl b/src/webmachine_resource.erl index dce8ddaf..3547097e 100644 --- a/src/webmachine_resource.erl +++ b/src/webmachine_resource.erl @@ -17,9 +17,10 @@ -module(webmachine_resource). -author('Justin Sheehy '). -author('Andy Gross '). --export([new/4, wrap/2]). +-export([new/3, wrap/2]). -export([do/3,log_d/2,stop/1]). +-include("wm_compat.hrl"). -include("wm_resource.hrl"). -include("wm_reqdata.hrl"). -include("wm_reqstate.hrl"). @@ -27,11 +28,22 @@ -type t() :: #wm_resource{}. -export_type([t/0]). -new(R_Mod, R_ModState, R_ModExports, R_Trace) -> +-define(CALLBACK_ARITY, 2). + +%% Suppress Erlang/OTP 21 warnings about the new method to retrieve +%% stacktraces. +-ifdef(OTP_RELEASE). +-compile({nowarn_deprecated_function, [{erlang, get_stacktrace, 0}]}). +-endif. + +new(R_Mod, R_ModState, R_Trace) -> + case erlang:module_loaded(R_Mod) of + false -> code:ensure_loaded(R_Mod); + true -> ok + end, #wm_resource{ module = R_Mod, modstate = R_ModState, - modexports = R_ModExports, trace = R_Trace }. @@ -118,15 +130,13 @@ default(_) -> wrap(Mod, Args) -> case Mod:init(Args) of {ok, ModState} -> - {ok, webmachine_resource:new(Mod, ModState, - orddict:from_list(Mod:module_info(exports)), false)}; + {ok, webmachine_resource:new(Mod, ModState, false)}; {{trace, Dir}, ModState} -> {ok, File} = open_log_file(Dir, Mod), log_decision(File, v3b14), log_call(File, attempt, Mod, init, Args), log_call(File, result, Mod, init, {{trace, Dir}, ModState}), - {ok, webmachine_resource:new(Mod, ModState, - orddict:from_list(Mod:module_info(exports)), File)}; + {ok, webmachine_resource:new(Mod, ModState, File)}; _ -> {stop, bad_init_arg} end. @@ -136,7 +146,6 @@ do(#wm_resource{}=Res, Fun, ReqProps) -> do(Fun, ReqProps, #wm_resource{ module=R_Mod, - modexports=R_ModExports, trace=R_Trace }=Req) when is_atom(Fun) andalso is_list(ReqProps) -> @@ -155,21 +164,20 @@ do(Fun, ReqProps, %% Do not need the embedded state anymore TrimData = ReqData#wm_reqdata{wm_state=undefined}, {Reply, - webmachine_resource:new(R_Mod, NewModState, R_ModExports, R_Trace), + webmachine_resource:new(R_Mod, NewModState, R_Trace), ReqState#wm_reqstate{reqdata=TrimData}}. handle_wm_call(Fun, ReqData, #wm_resource{ module=R_Mod, modstate=R_ModState, - modexports=R_ModExports, trace=R_Trace }=Req) -> case default(Fun) of no_default -> resource_call(Fun, ReqData, Req); Default -> - case orddict:is_key(Fun, R_ModExports) of + case erlang:function_exported(R_Mod, Fun, ?CALLBACK_ARITY) of true -> resource_call(Fun, ReqData, Req); false -> @@ -200,12 +208,13 @@ resource_call(F, ReqData, _ -> log_call(R_Trace, attempt, R_Mod, F, [ReqData, R_ModState]) end, Result = try + %% Note: the argument list must match the definition of CALLBACK_ARITY apply(R_Mod, F, [ReqData, R_ModState]) catch C:R:Stacktrace -> Reason = {C, R, trim_trace(Stacktrace)}, {{error, Reason}, ReqData, R_ModState} end, - case R_Trace of + case R_Trace of false -> nop; _ -> log_call(R_Trace, result, R_Mod, F, Result) end, diff --git a/test/decision_core_test.erl b/test/decision_core_test.erl index 857d733d..2fb03340 100644 --- a/test/decision_core_test.erl +++ b/test/decision_core_test.erl @@ -17,10 +17,20 @@ -ifdef(TEST). +-include("wm_compat.hrl"). -include("wm_reqdata.hrl"). -include_lib("eunit/include/eunit.hrl"). --compile(export_all). +-export([size_stream_raises_error/2, process_post_for_created_p11/3, get_streamed_body/2, send_streamed_body/2, + accept_text/2, writer_response/2, known_length_body/2, range_response/2, stream_content_md5/0, + validate_checksum_for_md5stream/3, process_post_for_md5_stream/3, init/1, service_available/2, + validate_content_checksum/2, is_authorized/2, allowed_methods/2, known_methods/2, uri_too_long/2, + known_content_type/2, valid_entity_length/2, malformed_request/2, forbidden/2, valid_content_headers/2, + content_types_provided/2, content_types_accepted/2, language_available/2, charsets_provided/2, + encodings_provided/2, resource_exists/2, generate_etag/2, last_modified/2, moved_permanently/2, + moved_temporarily/2, previously_existed/2, allow_missing_post/2, post_is_create/2, process_post/2, + create_path/2, is_conflict/2, multiple_choices/2, base_uri/2, base_uri_add_slash/1, expires/2, + delete_resource/2, delete_completed/2, to_html/2]). -define(RESOURCE, atom_to_list(?MODULE)). -define(RESOURCE_PATH, "/" ++ ?RESOURCE). @@ -883,10 +893,15 @@ not_modified_j18_via_h12() -> %% 304 result via L17 not_modified_l17() -> + % Because httpd_util:rfc1123_date converts the date to GMT, it is offset by + % the local time zone. To get a Last-Modified date equal to the + % If-Modified-Since date we must convert it back from the RFC 1123 format. + % This avoids the test case failing in any time zone "east" of GMT :-P + RFC1123LastYear = httpd_util:rfc1123_date(?FIRST_DAY_OF_LAST_YEAR), + DateTimeLastYear = httpd_util:convert_request_date(RFC1123LastYear), put_setting(allowed_methods, ?DEFAULT_ALLOWED_METHODS), - put_setting(last_modified, ?FIRST_DAY_OF_LAST_YEAR), + put_setting(last_modified, DateTimeLastYear), put_setting(expires, ?FIRST_DAY_OF_NEXT_YEAR), - RFC1123LastYear = httpd_util:rfc1123_date(?FIRST_DAY_OF_LAST_YEAR), Headers = [{"If-Modified-Since", RFC1123LastYear}], {ok, Result} = httpc:request(get, {url(), Headers}, [], []), ?assertMatch({{"HTTP/1.1", 304, "Not Modified"}, _, _}, Result), diff --git a/test/wm_integration_test.erl b/test/wm_integration_test.erl index 9aac6413..63b83521 100644 --- a/test/wm_integration_test.erl +++ b/test/wm_integration_test.erl @@ -17,8 +17,6 @@ -include_lib("eunit/include/eunit.hrl"). -include("webmachine.hrl"). --compile([export_all]). - integration_test_() -> {foreach, %% Setup