Skip to content

Commit

Permalink
Merge pull request #2771 from vibe-d/disallow_unsafe_rest_interfaces
Browse files Browse the repository at this point in the history
Remove support for non-safe REST methods
  • Loading branch information
l-kramer authored Feb 9, 2024
2 parents d62a245 + cc7b7a7 commit bf95d90
Show file tree
Hide file tree
Showing 12 changed files with 34 additions and 19 deletions.
3 changes: 3 additions & 0 deletions examples/rest-collections/source/api.d
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ module api;
import vibe.web.rest;

interface ForumAPI {
@safe:
// base path /threads/
Collection!ThreadAPI threads();
}

interface ThreadAPI {
@safe:
// define the index parameters used to identify the collection items
struct CollectionIndices {
string _thread_name;
Expand All @@ -27,6 +29,7 @@ interface ThreadAPI {
}

interface PostAPI {
@safe:
// define the index parameters used to identify the collection items
struct CollectionIndices {
string _thread_name;
Expand Down
5 changes: 4 additions & 1 deletion examples/rest-collections/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ class LocalThreadAPI : ThreadAPI {

string[] get()
{
return m_data.threads.keys;
static if (__VERSION__ < 2099) {
// NOTE: .keys is not @safe on older compiler versions
return () @trusted { return m_data.threads.keys; } ();
} else return m_data.threads.keys;
}
}

Expand Down
1 change: 1 addition & 0 deletions examples/rest-js/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import vibe.http.common : HTTPMethod;

// Defines a simple RESTful API
interface ITest {
@safe:
// GET /compute_sum?a=...&b=...
@method(HTTPMethod.GET)
float computeSum(float a, float b);
Expand Down
3 changes: 3 additions & 0 deletions tests/restclient/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import vibe.vibe;

interface ITestAPI
{
@safe:
@property ISub sub();

@method(HTTPMethod.POST) @path("other/path")
Expand All @@ -22,6 +23,7 @@ interface ITestAPI
}

interface ISub {
@safe:
int get(int id);
}

Expand Down Expand Up @@ -49,6 +51,7 @@ class SubAPI : ISub {

interface ITestAPICors
{
@safe:
string getFoo();
string setFoo();
string addFoo();
Expand Down
4 changes: 4 additions & 0 deletions tests/restcollections/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ module app;
import vibe.vibe;

interface API {
@safe:
Collection!ItemAPI items();
}

interface ItemAPI {
@safe:
struct CollectionIndices {
string _item;
}
Expand All @@ -17,6 +19,7 @@ interface ItemAPI {
}

interface SubItemAPI {
@safe:
struct CollectionIndices {
string _item;
int _index;
Expand All @@ -29,6 +32,7 @@ interface SubItemAPI {
}

interface ItemManagerAPI {
@safe:
@property string databaseURL();
}

Expand Down
1 change: 1 addition & 0 deletions tests/vibe.web.rest.1125/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ shared static this()
}

interface ILlama {
@safe:
@bodyParam("llama", "llama")
string updateLlama(string llama = null);
}
Expand Down
2 changes: 2 additions & 0 deletions tests/vibe.web.rest.1140/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import vibe.d;

interface IOrientDBRoot
{
@safe:
@property IOrientDBQuery query();
}

interface IOrientDBQuery
{
@safe:
@method(HTTPMethod.GET)
@path(":db_name/sql/:query/:result_set_size")
Json sql(string _db_name, string _query, int _result_set_size);
Expand Down
1 change: 1 addition & 0 deletions tests/vibe.web.rest.1230/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import std.datetime;
import vibe.d;

interface ITestAPI {
@safe:
string postDefault(int value, bool check = true);
}

Expand Down
2 changes: 2 additions & 0 deletions tests/vibe.web.rest.1922/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ struct AuthInfo {

@requiresAuth
interface IItemAPI {
@safe:
struct CollectionIndices {
string item;
}
Expand All @@ -66,6 +67,7 @@ class ItemAPI : IItemAPI {

@requiresAuth
interface IAuthAPI {
@safe:
@noAuth int getNonAuthNumber(int num);
@anyAuth int getAuthNumber(AuthInfo info, int num);
@anyAuth Collection!IItemAPI items();
Expand Down
1 change: 1 addition & 0 deletions tests/vibe.web.rest.2506/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ struct TestStruct {int i;}

interface IService
{
@safe:
@safe TestStruct getTest(int sleepsecs);
@safe TestStruct getTest2(int sleepsecs);
}
Expand Down
1 change: 1 addition & 0 deletions tests/vibe.web.rest.auth/source/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ struct Auth {

@requiresAuth
interface IService {
@safe:
@noAuth int getPublic();
@anyAuth int getAny();
@anyAuth int getAnyA(Auth auth);
Expand Down
29 changes: 11 additions & 18 deletions web/vibe/web/rest.d
Original file line number Diff line number Diff line change
Expand Up @@ -1562,8 +1562,8 @@ private HTTPServerRequestDelegate jsonMethodHandler(alias Func, size_t ridx, T)(
v = fromRestString!(PT, SerPolicyType)(*pv);
} else static if (sparam.kind == ParameterKind.attributed) {
static if (!__traits(compiles, () @safe { computeAttributedParameterCtx!(CFunc, pname)(inst, req, res); } ()))
pragma(msg, "Non-@safe @before evaluators are deprecated - annotate evaluator function for parameter "~pname~" of "~T.stringof~"."~Method~" as @safe.");
v = () @trusted { return computeAttributedParameterCtx!(CFunc, pname)(inst, req, res); } ();
static assert(false, "`@before` evaluator for REST interface method `" ~ fullyQualifiedName!T ~ "." ~ Method ~ "` must be marked `@safe`.");
v = computeAttributedParameterCtx!(CFunc, pname)(inst, req, res);
} else static if (sparam.kind == ParameterKind.internal) {
if (auto pv = fieldname in req.params)
v = fromRestString!(PT, DefaultPolicy)(urlDecode(*pv));
Expand Down Expand Up @@ -1628,30 +1628,26 @@ private HTTPServerRequestDelegate jsonMethodHandler(alias Func, size_t ridx, T)(
import vibe.internal.meta.funcattr;

static if (!__traits(compiles, () @safe { __traits(getMember, inst, Method)(params); }))
pragma(msg, "Non-@safe methods are deprecated in REST interfaces - Mark " ~
T.stringof ~ "." ~ Method ~ " as @safe.");
static assert(false, "REST interface method `" ~ fullyQualifiedName!T ~ "." ~ Method ~ "` must be marked `@safe`.");

static if (is(RT == void)) {
// TODO: remove after deprecation period
() @trusted { __traits(getMember, inst, Method)(params); } ();
__traits(getMember, inst, Method)(params);
returnHeaders();
res.writeBody(cast(ubyte[])null);
} else static if (isInputStream!RT) {
returnHeaders();
auto ret = () @trusted {
return evaluateOutputModifiers!CFunc(
__traits(getMember, inst, Method)(params), req, res); } ();
auto ret = evaluateOutputModifiers!CFunc(
__traits(getMember, inst, Method)(params), req, res);
res.headers["Content-Type"] = "application/octet-stream";
ret.pipe(res.bodyWriter);
} else {
// TODO: remove after deprecation period
static if (!__traits(compiles, () @safe { evaluateOutputModifiers!Func(RT.init, req, res); } ()))
pragma(msg, "Non-@safe @after evaluators are deprecated - annotate @after evaluator function for " ~
T.stringof ~ "." ~ Method ~ " as @safe.");
static assert(false, "`@after` evaluator for REST interface method `" ~ fullyQualifiedName!T ~ "." ~ Method ~ "` must be marked `@safe`.");

auto ret = () @trusted {
return evaluateOutputModifiers!CFunc(
__traits(getMember, inst, Method)(params), req, res); } ();
auto ret = evaluateOutputModifiers!CFunc(
__traits(getMember, inst, Method)(params), req, res);
returnHeaders();

string accept_str;
Expand All @@ -1670,12 +1666,9 @@ private HTTPServerRequestDelegate jsonMethodHandler(alias Func, size_t ridx, T)(
serializer.serialize!(SerPolicyT!(RestInterface!T.I).PolicyTemplate)(serialized_output, ret);
}))
{
pragma(msg, "Non-@safe serialization of REST return types deprecated - ensure that " ~
RT.stringof~" is safely serializable.");
static assert(false, "Serialization of return type `"~RT.stringof~"` of REST interface method `" ~ fullyQualifiedName!T ~ "." ~ Method ~ "` must be `@safe`.");
}
() @trusted {
serializer.serialize!(SerPolicyT!(RestInterface!T.I).PolicyTemplate)(serialized_output, ret);
}();
serializer.serialize!(SerPolicyT!(RestInterface!T.I).PolicyTemplate)(serialized_output, ret);
res.writeBody(serialized_output.data, serializer.contentType);
}
res.statusCode = HTTPStatus.notAcceptable; // will trigger RestException on the client side
Expand Down

0 comments on commit bf95d90

Please sign in to comment.