Last active
December 18, 2015 03:19
-
-
Save haf/5717121 to your computer and use it in GitHub Desktop.
Hacking async APIs for the LULz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Threading.Tasks; | |
using ConsoleApplication2.Authorization; | |
using ConsoleApplication2.ServiceStack; | |
using ConsoleApplication2.Sugar; | |
namespace ConsoleApplication2 | |
{ | |
internal class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var p = new Program(); | |
p.BootstrapServerWith( | |
new AuthorizationService(), | |
new HeartbeatSentinel()); | |
} | |
RequestPipeline RequestPipeline | |
{ | |
get { return new RequestPipeline(); } | |
} | |
void BootstrapServerWith( | |
AuthorizationService tenantUserSvc, | |
HeartbeatSentinel sentinel) | |
{ | |
RequestPipeline.Add(async (req, resp, dto) => | |
{ | |
var decision = await tenantUserSvc.DecideOn(req.DeserializeAuthorCtx()); | |
return decision.WriteToResponse(resp, "You have to provide a tenant AND and user"); | |
}); | |
RequestPipeline.Add(async (req, resp, dto) => | |
{ | |
var reply = await sentinel.RepliesWithin(5.Milliseconds()); | |
if (reply.TimedOut) | |
return new ContinueWith(new FailoverPipeline()) as FilterDecision; | |
return new Continue(); | |
}); | |
} | |
} | |
internal class FailoverPipeline | |
: RequestPipeline | |
{ | |
} | |
internal class Continue | |
: FilterDecision | |
{ | |
} | |
internal class HeartbeatSentinel | |
{ | |
public async Task<HeartbeatReply> RepliesWithin(TimeSpan milliseconds) | |
{ | |
return new HeartbeatReply(); | |
} | |
} | |
internal class HeartbeatReply | |
{ | |
public bool TimedOut | |
{ | |
get { return false; } | |
} | |
} | |
public class AuthorizationService | |
{ | |
public async Task<AuthorizationDecision> DecideOn(AuthorizationContext ctx) | |
{ | |
return ctx.UserId != Guid.Empty && ctx.TenantId != Guid.Empty | |
? new AllowDecision() as AuthorizationDecision | |
: new DenyDecision() as AuthorizationDecision; | |
} | |
} | |
namespace Authorization | |
{ | |
public abstract class AuthorizationDecision | |
{ | |
public FilterDecision WriteToResponse(HttpResp resp, string friendlyOuterMessage) | |
{ | |
resp.ContentType = "text/plain"; | |
resp.Write(friendlyOuterMessage); //Whatever you want in body | |
return HandleDecision(resp); | |
} | |
protected abstract FilterDecision HandleDecision(HttpResp resp); | |
} | |
public class AllowDecision | |
: AuthorizationDecision | |
{ | |
protected override FilterDecision HandleDecision(HttpResp resp) | |
{ | |
throw new NotImplementedException(); | |
} | |
} | |
public interface AuthorizationContext | |
{ | |
Guid UserId { get; } | |
Guid TenantId { get; } | |
} | |
public class DenyDecision | |
: AuthorizationDecision | |
{ | |
protected override FilterDecision HandleDecision(HttpResp resp) | |
{ | |
resp.RedirectTo("/login"); | |
return new BreakPipeline(); | |
} | |
} | |
public static class HttpReqEx | |
{ | |
public static AuthorizationContext DeserializeAuthorCtx(this HttpReq req) | |
{ | |
return new MissingContext(); | |
} | |
} | |
internal class MissingContext : AuthorizationContext | |
{ | |
public MissingContext() | |
{ | |
UserId = Guid.Empty; | |
TenantId = Guid.Empty; | |
} | |
public Guid UserId { get; private set; } | |
public Guid TenantId { get; private set; } | |
} | |
} | |
namespace AppPipelines | |
{ | |
} | |
namespace ServiceStack | |
{ | |
public interface HttpReq | |
{ | |
} | |
public class RequestPipeline | |
{ | |
public void Add<T>(Func<HttpReq, HttpResp, RequestDto, Task<T>> action) | |
where T : FilterDecision | |
{ | |
} | |
} | |
public interface FilterDecision | |
{ | |
} | |
internal class BreakPipeline | |
: FilterDecision | |
{ | |
} | |
internal class ContinueWith | |
: FilterDecision | |
{ | |
public ContinueWith(RequestPipeline next) | |
{ | |
Next = next; | |
} | |
RequestPipeline Next { get; set; } | |
} | |
public interface HttpResp | |
{ | |
string ContentType { set; } | |
void Write(string content); | |
void RedirectTo(string siteAbsoluteUri); | |
} | |
public interface RequestDto | |
{ | |
} | |
} | |
namespace Sugar | |
{ | |
public static class IntegerEx | |
{ | |
public static TimeSpan Milliseconds(this int input) | |
{ | |
return new TimeSpan(0, 0, 0, 0, input); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment