public Yield Spawn(DreamContext context, DreamMessage request, Result <DreamMessage> response) { var guid = Guid.NewGuid(); ContextVar = new ContextLifeSpan(guid); context.SetState(guid); context.SetState(ContextVar); ContextLifeSpan capturedInner = null; yield return(Async.Fork(() => { var innerContextVar = DreamContext.Current.GetState <ContextLifeSpan>(); capturedInner = innerContextVar; if (innerContextVar == ContextVar) { throw new Exception("spawned context instances were same"); } if (innerContextVar.Guid != guid) { throw new Exception("spawned context guid is wrong"); } if (innerContextVar.IsDisposed) { throw new Exception("subcall: context is disposed"); } }, new Result())); var contextVar = context.GetState <ContextLifeSpan>(); if (contextVar == null) { throw new Exception("context instance is gone"); } if (capturedInner == contextVar) { throw new Exception("outer instance was changed to inner"); } if (!capturedInner.IsDisposed) { throw new Exception("inner instance wasn't disposed after closure completion"); } if (contextVar.Guid != guid) { throw new Exception("context guid is wrong"); } if (contextVar != ContextVar) { throw new Exception("context instance changed"); } if (contextVar.IsDisposed) { throw new Exception("context is disposed"); } response.Return(DreamMessage.Ok()); yield break; }
public Yield CheckEpilogue(DreamContext context, DreamMessage request, Result <DreamMessage> response) { EpilogueData = StringUtil.CreateAlphaNumericKey(8); _log.DebugFormat("setting epilogue data in Env #{0}", TaskEnv.Current.GetHashCode()); context.SetState("epilogue", EpilogueData); response.Return(DreamMessage.Ok()); yield break; }
public Yield Ping(DreamContext context, DreamMessage request, Result <DreamMessage> response) { var guid = Guid.NewGuid(); ContextVar = new ContextLifeSpan(guid); context.SetState(ContextVar); response.Return(DreamMessage.Ok()); yield break; }
public Yield Exception(DreamContext context, DreamMessage request, Result <DreamMessage> response) { _log.Debug("in exception feature"); var guid = Guid.NewGuid(); ContextVar = new ContextLifeSpan(guid); context.SetState(ContextVar); throw new CustomException(); }
public Yield CallPlug(DreamContext context, DreamMessage request, Result <DreamMessage> response) { _log.Debug("callplug start"); var guid = Guid.NewGuid(); ContextVar = new ContextLifeSpan(guid); context.SetState(guid); _log.Debug("setting disposable state"); context.SetState(ContextVar); Result <DreamMessage> sub; _log.Debug("calling plug"); yield return(sub = Self.At("calledplug").GetAsync()); _log.Debug("return from plug"); if (!sub.Value.IsSuccessful) { response.Return(sub.Value); } var contextVar = context.GetState <ContextLifeSpan>(); if (contextVar == null) { throw new Exception("context instance is gone"); } if (contextVar.Guid != guid) { throw new Exception("context guid is wrong"); } if (contextVar != ContextVar) { throw new Exception("context instance changed"); } if (contextVar.IsDisposed) { throw new Exception("context is disposed"); } _log.Debug("callplug return"); response.Return(DreamMessage.Ok()); _log.Debug("callplug end"); yield break; }
private Yield PrologueStats(DreamContext context, DreamMessage request, Result <DreamMessage> response) { // initialize stopwatch timer System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); context.SetState("stats-stopwatch", sw); // continue processing response.Return(request); yield break; }
private Yield Prologue(DreamContext context, DreamMessage request, Result <DreamMessage> response) { _log.Debug("in prologue"); if ("disposal".EqualsInvariant(context.Feature.Signature) && context.IsTaskEnvDisposed) { throw new Exception("context disposed in prologue"); } PrologueData = StringUtil.CreateAlphaNumericKey(8); _log.DebugFormat("setting prologue data in Env #{0}", TaskEnv.Current.GetHashCode()); context.SetState("prologue", PrologueData); response.Return(DreamMessage.Ok()); yield break; }
public Yield CallCoroutine(DreamContext context, DreamMessage request, Result <DreamMessage> response) { _log.Debug("callcoroutine start"); var guid = Guid.NewGuid(); ContextVar = new ContextLifeSpan(guid); context.SetState(guid); context.SetState(ContextVar); if (context.GetState <ContextLifeSpan>() == null) { throw new Exception("context instance was never set"); } _log.DebugFormat("callcoroutine calling coroutine within Env #{0}", TaskEnv.Current.GetHashCode()); yield return(Coroutine.Invoke(SubCall, new Result())); var contextVar = context.GetState <ContextLifeSpan>(); _log.DebugFormat("callcoroutine coroutine returned within Env #{0}", TaskEnv.Current.GetHashCode()); if (contextVar == null) { throw new Exception("context instance is gone"); } if (contextVar.Guid != guid) { throw new Exception("context guid is wrong"); } if (contextVar != ContextVar) { throw new Exception("context instance changed"); } if (contextVar.IsDisposed) { throw new Exception("context is disposed"); } response.Return(DreamMessage.Ok()); yield break; }
public Yield CalledPlug(DreamContext context, DreamMessage request, Result <DreamMessage> response) { _log.Debug("calledplug start"); var contextVar = context.GetState <ContextLifeSpan>(); if (contextVar != null) { throw new Exception("called plug context instance already exists"); } context.SetState(new ContextLifeSpan(Guid.NewGuid())); _log.Debug("calledplug return"); response.Return(DreamMessage.Ok()); _log.Debug("calledplug end"); yield break; }
private Result<DreamMessage> SubmitRequestAsync(string verb, XUri uri, IPrincipal user, DreamMessage request, Result<DreamMessage> response, Action completion) { if(string.IsNullOrEmpty(verb)) { if(completion != null) { completion(); } throw new ArgumentNullException("verb"); } if(uri == null) { if(completion != null) { completion(); } throw new ArgumentNullException("uri"); } if(request == null) { if(completion != null) { completion(); } throw new ArgumentNullException("request"); } if(response == null) { if(completion != null) { completion(); } throw new ArgumentNullException("response"); } // ensure environment is still running if(!IsRunning) { response.Return(DreamMessage.InternalError("host not running")); if(completion != null) { completion(); } return response; } try { Interlocked.Increment(ref _requestCounter); // check if we were not able to begin processing the request DreamMessage failed = BeginRequest(completion, uri, request); if(failed != null) { response.Return(failed); EndRequest(completion, uri, request); return response; } // check if 'verb' is overwritten by a processing parameter verb = verb.ToUpperInvariant(); string requestedVerb = (uri.GetParam(DreamInParam.VERB, null) ?? request.Headers.MethodOverride ?? verb).ToUpperInvariant(); if( verb.EqualsInvariant(Verb.POST) || ( verb.EqualsInvariant(Verb.GET) && ( requestedVerb.EqualsInvariant(Verb.OPTIONS) || requestedVerb.EqualsInvariant(Verb.HEAD) ) ) ) { verb = requestedVerb; } // check if an origin was specified request.Headers.DreamOrigin = uri.GetParam(DreamInParam.ORIGIN, request.Headers.DreamOrigin); // check if a public uri is supplied XUri publicUri = XUri.TryParse(uri.GetParam(DreamInParam.URI, null) ?? request.Headers.DreamPublicUri); XUri transport = XUri.TryParse(request.Headers.DreamTransport) ?? uri.WithoutCredentialsPathQueryFragment(); if(publicUri == null) { // check if request is local if(transport.Scheme.EqualsInvariantIgnoreCase("local")) { // local:// uris with no public-uri specifier default to the configured public-uri publicUri = _publicUri; } else { // check if the request was forwarded through Apache mod_proxy string proxyOverride = uri.GetParam(DreamInParam.HOST, null); if(string.IsNullOrEmpty(proxyOverride)) { proxyOverride = request.Headers.ForwardedHost; } string serverPath = string.Join("/", transport.Segments); if(proxyOverride != null) { // request used an override, append path of public-uri serverPath = string.Join("/", _publicUri.Segments); } // set the uri scheme based-on the incoming scheme and the override header string scheme = transport.Scheme; if("On".EqualsInvariantIgnoreCase(request.Headers.FrontEndHttps ?? "")) { scheme = Scheme.HTTPS; } scheme = uri.GetParam(DreamInParam.SCHEME, scheme); // set the host port string hostPort = proxyOverride ?? request.Headers.Host ?? uri.HostPort; publicUri = new XUri(string.Format("{0}://{1}", scheme, hostPort)).AtPath(serverPath); } request.Headers.DreamPublicUri = publicUri.ToString(); } // set host header request.Headers.Host = publicUri.HostPort; // convert incoming uri to local:// XUri localFeatureUri = uri.ChangePrefix(uri.WithoutPathQueryFragment(), _localMachineUri); // check if path begins with public uri path if((transport.Segments.Length > 0) && localFeatureUri.PathStartsWith(transport.Segments)) { localFeatureUri = localFeatureUri.WithoutFirstSegments(transport.Segments.Length); } // check if the path is the application root and whether we have special behavior for that if(localFeatureUri.Path.IfNullOrEmpty("/") == "/") { if(!string.IsNullOrEmpty(_rootRedirect)) { localFeatureUri = localFeatureUri.AtAbsolutePath(_rootRedirect); } else if(IsDebugEnv) { localFeatureUri = localFeatureUri.AtAbsolutePath("/host/services"); } } // find the requested feature List<DreamFeature> features; lock(_features) { features = _features.Find(localFeatureUri); } DreamFeature feature = null; if(features != null) { // TODO (steveb): match the incoming mime-type to the feature's acceptable mime-types (mime-type overloading) // match the request verb to the feature verb foreach(DreamFeature entry in features) { if((entry.Verb == "*") || entry.Verb.EqualsInvariant(verb)) { feature = entry; break; } } // check if this is an OPTIONS request and there is no defined feature for it if(verb.EqualsInvariant(Verb.OPTIONS) && ((feature == null) || feature.Verb.EqualsInvariant("*"))) { // list all allowed methods List<string> methods = new List<string>(); foreach(DreamFeature entry in features) { if(!methods.Contains(entry.Verb)) { methods.Add(entry.Verb); } } methods.Sort(StringComparer.Ordinal.Compare); DreamMessage result = DreamMessage.Ok(); result.Headers.Allow = string.Join(", ", methods.ToArray()); response.Return(result); // decrease counter for external requests EndRequest(completion, uri, request); return response; } } // check if a feature was found if(feature == null) { DreamMessage result; // check if any feature was found if((features == null) || (features.Count == 0)) { string msg = verb + " URI: " + uri.ToString(false) + " LOCAL: " + localFeatureUri.ToString(false) + " PUBLIC: " + publicUri + " TRANSPORT: " + transport; _log.WarnMethodCall("ProcessRequest: feature not found", msg); result = DreamMessage.NotFound("resource not found"); } else { string msg = verb + " " + uri.ToString(false); _log.WarnMethodCall("ProcessRequest: method not allowed", msg); List<string> methods = new List<string>(); foreach(DreamFeature entry in features) { if(!methods.Contains(entry.Verb)) { methods.Add(entry.Verb); } } methods.Sort(StringComparer.Ordinal.Compare); result = DreamMessage.MethodNotAllowed(methods.ToArray(), "allowed methods are " + string.Join(", ", methods.ToArray())); } response.Return(result); // decrease counter for external requests EndRequest(completion, uri, request); return response; } // add uri to aliases list if(_memorizeAliases) { lock(_aliases) { _aliases[transport] = transport; _aliases[publicUri] = publicUri; } } // create context DreamContext context = new DreamContext(this, verb, localFeatureUri, feature, publicUri, _publicUri, request, CultureInfo.InvariantCulture, GetRequestLifetimeScopeFactory(feature.Service)); // attach request id to the context context.SetState(DreamHeaders.DREAM_REQUEST_ID, request.Headers.DreamRequestId); // add user to context context.User = user; // build linked-list of feature calls var chain = new Result<DreamMessage>(TimeSpan.MaxValue, TaskEnv.Current).WhenDone(result => { // extract message DreamMessage message; if(result.HasValue) { message = result.Value; } else if(result.Exception is DreamAbortException) { message = ((DreamAbortException)result.Exception).Response; } else if(result.Exception is DreamCachedResponseException) { message = ((DreamCachedResponseException)result.Exception).Response; } else { _log.ErrorExceptionFormat(response.Exception, "Failed Feature '{0}' Chain [{1}:{2}]: {3}", feature.MainStage.Name, verb, localFeatureUri.Path, response.Exception.Message ); message = DreamMessage.InternalError(result.Exception); } // decrease counter for external requests EndRequest(completion, uri, request); // need to manually dispose of the context, since we're already attaching and detaching it by hand to TaskEnvs throughout the chain if(response.IsCanceled) { _log.DebugFormat("response for '{0}' has already returned", context.Uri.Path); response.ConfirmCancel(); ((ITaskLifespan)context).Dispose(); } else { ((ITaskLifespan)context).Dispose(); response.Return(message); } }); for(int i = feature.Stages.Length - 1; i >= 0; --i) { var link = new DreamFeatureChain(feature.Stages[i], i == feature.MainStageIndex, context, chain, (i > 0) ? feature.Stages[i - 1].Name : "first"); chain = new Result<DreamMessage>(TimeSpan.MaxValue, TaskEnv.Current).WhenDone(link.Handler); } // kick-off new task AsyncUtil.Fork( () => chain.Return(request), TaskEnv.New(TimerFactory), new Result(TimeSpan.MaxValue, response.Env).WhenDone(res => { if(!res.HasException) { return; } _log.ErrorExceptionFormat(res.Exception, "handler for {0}:{1} failed", context.Verb, context.Uri.ToString(false)); ((ITaskLifespan)context).Dispose(); // forward exception to recipient response.Throw(res.Exception); // decrease counter for external requests EndRequest(completion, uri, request); }) ); } catch(Exception e) { response.Throw(e); EndRequest(completion, uri, request); } return response; }
public Yield Spawn(DreamContext context, DreamMessage request, Result<DreamMessage> response) { var guid = Guid.NewGuid(); ContextVar = new ContextLifeSpan(guid); context.SetState(guid); context.SetState(ContextVar); ContextLifeSpan capturedInner = null; yield return Async.Fork(() => { var innerContextVar = DreamContext.Current.GetState<ContextLifeSpan>(); capturedInner = innerContextVar; if(innerContextVar == ContextVar) { throw new Exception("spawned context instances were same"); } if(innerContextVar.Guid != guid) { throw new Exception("spawned context guid is wrong"); } if(innerContextVar.IsDisposed) { throw new Exception("subcall: context is disposed"); } }, new Result()); var contextVar = context.GetState<ContextLifeSpan>(); if(contextVar == null) { throw new Exception("context instance is gone"); } if(capturedInner == contextVar) { throw new Exception("outer instance was changed to inner"); } if(!capturedInner.IsDisposed) { throw new Exception("inner instance wasn't disposed after closure completion"); } if(contextVar.Guid != guid) { throw new Exception("context guid is wrong"); } if(contextVar != ContextVar) { throw new Exception("context instance changed"); } if(contextVar.IsDisposed) { throw new Exception("context is disposed"); } response.Return(DreamMessage.Ok()); yield break; }
public Yield CalledPlug(DreamContext context, DreamMessage request, Result<DreamMessage> response) { _log.Debug("calledplug start"); var contextVar = context.GetState<ContextLifeSpan>(); if(contextVar != null) { throw new Exception("called plug context instance already exists"); } context.SetState(new ContextLifeSpan(Guid.NewGuid())); _log.Debug("calledplug return"); response.Return(DreamMessage.Ok()); _log.Debug("calledplug end"); yield break; }
public Yield CallCoroutine(DreamContext context, DreamMessage request, Result<DreamMessage> response) { _log.Debug("callcoroutine start"); var guid = Guid.NewGuid(); ContextVar = new ContextLifeSpan(guid); context.SetState(guid); context.SetState(ContextVar); if(context.GetState<ContextLifeSpan>() == null) { throw new Exception("context instance was never set"); } _log.DebugFormat("callcoroutine calling coroutine within Env #{0}", TaskEnv.Current.GetHashCode()); yield return Coroutine.Invoke(SubCall, new Result()); var contextVar = context.GetState<ContextLifeSpan>(); _log.DebugFormat("callcoroutine coroutine returned within Env #{0}", TaskEnv.Current.GetHashCode()); if(contextVar == null) { throw new Exception("context instance is gone"); } if(contextVar.Guid != guid) { throw new Exception("context guid is wrong"); } if(contextVar != ContextVar) { throw new Exception("context instance changed"); } if(contextVar.IsDisposed) { throw new Exception("context is disposed"); } response.Return(DreamMessage.Ok()); yield break; }
public Yield CallPlug(DreamContext context, DreamMessage request, Result<DreamMessage> response) { _log.Debug("callplug start"); var guid = Guid.NewGuid(); ContextVar = new ContextLifeSpan(guid); context.SetState(guid); _log.Debug("setting disposable state"); context.SetState(ContextVar); Result<DreamMessage> sub; _log.Debug("calling plug"); yield return sub = Self.At("calledplug").GetAsync(); _log.Debug("return from plug"); if(!sub.Value.IsSuccessful) { response.Return(sub.Value); } var contextVar = context.GetState<ContextLifeSpan>(); if(contextVar == null) { throw new Exception("context instance is gone"); } if(contextVar.Guid != guid) { throw new Exception("context guid is wrong"); } if(contextVar != ContextVar) { throw new Exception("context instance changed"); } if(contextVar.IsDisposed) { throw new Exception("context is disposed"); } _log.Debug("callplug return"); response.Return(DreamMessage.Ok()); _log.Debug("callplug end"); yield break; }
public Yield CheckEpilogue(DreamContext context, DreamMessage request, Result<DreamMessage> response) { EpilogueData = StringUtil.CreateAlphaNumericKey(8); _log.DebugFormat("setting epilogue data in Env #{0}", TaskEnv.Current.GetHashCode()); context.SetState("epilogue", EpilogueData); response.Return(DreamMessage.Ok()); yield break; }
public Yield PostExtensionFunction(DreamContext context, DreamMessage request, Result <DreamMessage> response) { var name = context.GetParam("function"); XUri uri = Self.At(name); // check if deki server is permitted to invoke this function if (this is IDreamServiceLicense) { string deki; if (context.ServiceLicense.TryGetValue("deki", out deki) && !deki.EqualsInvariant(request.Headers[DEKI_HEADER] ?? string.Empty)) { throw new DreamAbortException(DreamMessage.Forbidden("deki server is not licensed for this service")); } } // check if any functions were found DekiScriptInvocationTargetDescriptor descriptor; if (!_functions.TryGetValue(uri, out descriptor)) { response.Return(DreamMessage.NotFound(string.Format("function {0} not found", context.GetParam("function")))); yield break; } // check if invoker has access to function if ((descriptor.Access != DreamAccess.Public) && (descriptor.Access > DetermineAccess(context, request))) { response.Return(DreamMessage.Forbidden("insufficient access privileges")); yield break; } // check if request has a requested culture context.Culture = HttpUtil.GetCultureInfoFromHeader(request.Headers.AcceptLanguage, context.Culture); // check for implicit arguments context.SetState(GetImplicitEnvironment(request, _publicDigitalSignature)); // create custom environment DekiScriptEnv env = CreateEnvironment(); // create custom target for custom environment var target = descriptor.Target as DekiScriptExpressionInvocationTarget; if (target != null) { // TODO (steveb): re-initializing the invocation target works for the first call, but not if the function calls another function in the same extension! target = new DekiScriptExpressionInvocationTarget(target.Access, target.Parameters, target.Expression, env); } // invoke target DekiScriptLiteral eval; if (target != null) { eval = target.Invoke(ScriptRuntime, DekiScriptLiteral.FromXml(request.ToDocument())); } else { eval = descriptor.Target.Invoke(ScriptRuntime, DekiScriptLiteral.FromXml(request.ToDocument())); } // invoke function response.Return(DreamMessage.Ok(new DekiScriptList().Add(eval).ToXml())); yield break; }
public Yield Exception(DreamContext context, DreamMessage request, Result<DreamMessage> response) { _log.Debug("in exception feature"); var guid = Guid.NewGuid(); ContextVar = new ContextLifeSpan(guid); context.SetState(ContextVar); throw new CustomException(); }
public Yield Ping(DreamContext context, DreamMessage request, Result<DreamMessage> response) { var guid = Guid.NewGuid(); ContextVar = new ContextLifeSpan(guid); context.SetState(ContextVar); response.Return(DreamMessage.Ok()); yield break; }
private Yield Prologue(DreamContext context, DreamMessage request, Result<DreamMessage> response) { _log.Debug("in prologue"); if("disposal".EqualsInvariant(context.Feature.Signature) && context.IsTaskEnvDisposed) { throw new Exception("context disposed in prologue"); } PrologueData = StringUtil.CreateAlphaNumericKey(8); _log.DebugFormat("setting prologue data in Env #{0}", TaskEnv.Current.GetHashCode()); context.SetState("prologue", PrologueData); response.Return(DreamMessage.Ok()); yield break; }
private Yield PrologueStats(DreamContext context, DreamMessage request, Result<DreamMessage> response) { var sw = Stopwatch.StartNew(); context.SetState("stats-stopwatch", sw); response.Return(request); yield break; }