I need to make CORS work. It seems that jquery $ajax makes a OPTIONS call, and that should return the necessary CORS headers. All my GET and POST already do this, but it doesn't seems to be enough. In NGINX, you would do something like this:
location / {
if ($request_method = OPTIONS ) {
add_header Access-Control-Allow-Origin "http://example.com";
add_header Access-Control-Allow-Methods "GET, OPTIONS";
add_header Access-Control-Allow-Headers "Authorization";
add_header Access-Control-Allow-Credentials "true";
add_header Content-Length 0;
add_header Content-Type text/plain;
return 200;
}
}
How do I do the same in YAWS?
One way to handle an OPTIONS
request is to use a Yaws dispatchmod, which is similar to a Yaws appmod, but Yaws calls it prior to doing any other request handling. Here's an example dispatch module based on the information in your question:
-module(options_dispatcher).
-export([dispatch/1]).
-include_lib("yaws_api.hrl").
dispatch(Arg) ->
Req = yaws_api:arg_req(Arg),
case yaws_api:http_request_method(Req) of
'OPTIONS' ->
Vsn = yaws_api:http_request_version(Req),
Resp = #http_response{
version=Vsn,
status=200,
phrase=yaws_api:code_to_phrase(200)},
HdrVals = [{"Access-Control-Allow-Origin", "http://example.com"},
{"Access-Control-Allow-Methods", "GET, OPTIONS"},
{"Access-Control-Allow-Headers", "Authorization"},
{"Access-Control-Allow-Credentials", "true"},
{"Content-Length", "0"},
{"Content-Type", "text/plain"}],
Headers = lists:foldl(fun({H,V}, Hdrs) ->
yaws_api:set_header(Hdrs, H, V)
end, #headers{}, HdrVals),
HdrStrings = yaws_api:reformat_header(Headers),
Reply = [yaws_api:reformat_response(Resp), "\r\n",
string:join(HdrStrings, "\r\n"), "\r\n\r\n"],
Sock = yaws_api:arg_clisock(Arg),
case yaws_api:get_sslsocket(Sock) of
{ok, SslSock} ->
ssl:send(SslSock, Reply);
undefined ->
gen_tcp:send(Sock, Reply)
end,
done;
_ ->
continue
end.
This code receives a Yaws #arg{}
record, same as an appmod, except notice that a dispatchmod must export a dispatch/1
function whereas an appmod must export an out/1
function. From there it retrieves the request information and checks the HTTP request method. If it's OPTIONS
, the code creates a response record and sets up the response headers, formats them as strings, and then creates the Reply
value which is an iolist
containing the HTTP response status line, the formatted HTTP response headers, and "\r\n\r\n"
to mark the end of the HTTP response. It then uses either ssl:send/2
or gen_tcp:send/2
, based on what type of socket received the request, to send the reply directly. Finally it returns done
to tell Yaws there's no more work to do for that request. For any HTTP method other than OPTIONS
, the code returns continue
to tell Yaws to perform its normal dispatching.
To deploy the dispatcher, compile the code and place the resulting beam file in the Yaws load path. Then modify the server portion of your Yaws config to include the setting:
dispatchmod = options_dispatcher
This tells Yaws that the server has a dispatchmod that should be called as part of the request dispatch flow for that server. Then either start/restart Yaws or use
yaws --hup --id ID
to tell a running instance of Yaws to reload its configuration.