/// <summary> /// Creates a new stage instance. /// </summary> /// <param name="service">Service instance to which the stage belongs to.</param> /// <param name="method">Method definintion for stage handler.</param> /// <param name="access">Stage access level.</param> public DreamFeatureStage(IDreamService service, MethodInfo method, DreamAccess access) { if (service == null) { throw new ArgumentNullException("service"); } if (method == null) { throw new ArgumentNullException("method"); } this.Name = service.GetType().FullName + "!" + method.Name; this.Access = access; _method = method; _service = service; // determine what kind of method we were given var parameters = method.GetParameters(); if ((method.ReturnType == typeof(Yield)) && (parameters.Length == 3) && (parameters[0].ParameterType == typeof(DreamContext)) && (parameters[1].ParameterType == typeof(DreamMessage)) && (parameters[2].ParameterType == typeof(Result <DreamMessage>))) { // classical coroutine feature handler _handler = (CoroutineHandler <DreamContext, DreamMessage, Result <DreamMessage> >)Delegate.CreateDelegate(typeof(CoroutineHandler <DreamContext, DreamMessage, Result <DreamMessage> >), service, method); } else { // validate method return type if (method.ReturnType != typeof(Yield) && method.ReturnType != typeof(DreamMessage) && method.ReturnType != typeof(XDoc)) { throw new InvalidCastException(string.Format("feature handler '{0}' has return type {1}, but should be either DreamMessage or IEnumerator<IYield>", method.Name, method.ReturnType)); } // create an execution plan for fetching the necessary parameters to invoke the method _plan = new List <DreamFeatureAdapter>(); foreach (var param in parameters) { // check name-based parameters if (param.Name.EqualsInvariant("verb")) { Assert(method, param, typeof(string)); _plan.Add(new DreamFeatureAdapter(param.Name, GetContextVerb)); } else if (param.Name.EqualsInvariant("path")) { Assert(method, param, typeof(string[]), typeof(string)); if (param.ParameterType == typeof(string)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetContextFeatureSubpath)); } else { _plan.Add(new DreamFeatureAdapter(param.Name, GetContextFeatureSubpathSegments)); } } else if (param.Name.EqualsInvariant("uri")) { Assert(method, param, typeof(XUri)); _plan.Add(new DreamFeatureAdapter(param.Name, GetContextUri)); } else if (param.Name.EqualsInvariant("body")) { Assert(method, param, typeof(XDoc), typeof(string), typeof(Stream), typeof(byte[])); // check which body type is requested if (param.ParameterType == typeof(XDoc)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsDocument)); } else if (param.ParameterType == typeof(string)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsText)); } else if (param.ParameterType == typeof(Stream)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsStream)); } else if (param.ParameterType == typeof(byte[])) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsBytes)); } else { throw new ShouldNeverHappenException(); } } else { // check type-based parameters if (param.ParameterType == typeof(DreamContext)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetContext)); } else if (param.ParameterType == typeof(DreamMessage)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequest)); } else if (param.ParameterType == typeof(Result <DreamMessage>)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetMessageResponse)); } else if (param.ParameterType == typeof(Result <XDoc>)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetDocumentResponse)); } else if (param.ParameterType == typeof(DreamCookie)) { _plan.Add(MakeRequestCookieGetter(param.Name)); } else if (param.ParameterType == typeof(string)) { _plan.Add(MakeContextParamGetter(param.Name)); } else if (param.ParameterType == typeof(string[])) { _plan.Add(MakeContextParamListGetter(param.Name)); } else { _plan.Add(MakeConvertingContextParamGetter(param.Name, param.ParameterType)); } } } } }
/// <summary> /// Creates a new stage instance. /// </summary> /// <param name="service">Service instance to which the stage belongs to.</param> /// <param name="method">Method definintion for stage handler.</param> /// <param name="access">Stage access level.</param> public DreamFeatureStage(IDreamService service, MethodInfo method, DreamAccess access) { if(service == null) { throw new ArgumentNullException("service"); } if(method == null) { throw new ArgumentNullException("method"); } this.Name = service.GetType().FullName + "!" + method.Name; this.Access = access; _method = method; _service = service; // determine what kind of method we were given var parameters = method.GetParameters(); if((method.ReturnType == typeof(Yield)) && (parameters.Length == 3) && (parameters[0].ParameterType == typeof(DreamContext)) && (parameters[1].ParameterType == typeof(DreamMessage)) && (parameters[2].ParameterType == typeof(Result<DreamMessage>))) { // classical coroutine feature handler _handler = (CoroutineHandler<DreamContext, DreamMessage, Result<DreamMessage>>)Delegate.CreateDelegate(typeof(CoroutineHandler<DreamContext, DreamMessage, Result<DreamMessage>>), service, method); } else { // TODO (arnec): Eventually DreamMessage should have a DreamMessage<T> with custom serializers allowing arbitrary return types // validate method return type if(method.ReturnType != typeof(Yield) && method.ReturnType != typeof(DreamMessage) && method.ReturnType != typeof(XDoc)) { throw new InvalidCastException(string.Format("feature handler '{0}' has return type {1}, but should be either DreamMessage or IEnumerator<IYield>", method.Name, method.ReturnType)); } // create an execution plan for fetching the necessary parameters to invoke the method _plan = new List<DreamFeatureAdapter>(); foreach(var param in method.GetParameters()) { var attributes = param.GetCustomAttributes(false); QueryAttribute queryParam = (QueryAttribute)attributes.FirstOrDefault(i => i is QueryAttribute); PathAttribute pathParam = (PathAttribute)attributes.FirstOrDefault(i => i is PathAttribute); HeaderAttribute header = (HeaderAttribute)attributes.FirstOrDefault(i => i is HeaderAttribute); CookieAttribute cookie = (CookieAttribute)attributes.FirstOrDefault(i => i is CookieAttribute); // check attribute-based parameters if(queryParam != null) { // check if a single or a list of query parameters are requested if(param.ParameterType == typeof(string)) { _plan.Add(MakeContextParamGetter(queryParam.Name ?? param.Name)); } else if(param.ParameterType == typeof(string[])) { _plan.Add(MakeContextParamListGetter(queryParam.Name ?? param.Name)); } else { _plan.Add(MakeConvertingContextParamGetter(queryParam.Name ?? param.Name, param.ParameterType)); } } else if(pathParam != null) { if(param.ParameterType == typeof(string)) { _plan.Add(MakeContextParamGetter(pathParam.Name ?? param.Name)); } else { _plan.Add(MakeConvertingContextParamGetter(pathParam.Name ?? param.Name, param.ParameterType)); } } else if(cookie != null) { Assert(method, param, typeof(string), typeof(DreamCookie)); // check which cookie type is requested if(param.ParameterType == typeof(string)) { _plan.Add(MakeRequestCookieValueGetter(cookie.Name ?? param.Name)); } else if(param.ParameterType == typeof(DreamCookie)) { _plan.Add(MakeRequestCookieGetter(cookie.Name ?? param.Name)); } else { throw new ShouldNeverHappenException(); } } else if(header != null) { Assert(method, param, typeof(string)); _plan.Add(MakeRequestHeaderGetter(header.Name ?? param.Name)); } else { // check name-based parameters if(param.Name.EqualsInvariant("verb")) { Assert(method, param, typeof(string)); _plan.Add(new DreamFeatureAdapter(param.Name, GetContextVerb)); } else if(param.Name.EqualsInvariant("path")) { Assert(method, param, typeof(string[]), typeof(string)); if(param.ParameterType == typeof(string)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetContextFeatureSubpath)); } else { _plan.Add(new DreamFeatureAdapter(param.Name, GetContextFeatureSubpathSegments)); } } else if(param.Name.EqualsInvariant("uri")) { Assert(method, param, typeof(XUri)); _plan.Add(new DreamFeatureAdapter(param.Name, GetContextUri)); } else if(param.Name.EqualsInvariant("body")) { Assert(method, param, typeof(XDoc), typeof(string), typeof(Stream), typeof(byte[])); // check which body type is requested if(param.ParameterType == typeof(XDoc)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsDocument)); } else if(param.ParameterType == typeof(string)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsText)); } else if(param.ParameterType == typeof(Stream)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsStream)); } else if(param.ParameterType == typeof(byte[])) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsBytes)); } else { throw new ShouldNeverHappenException(); } } else { // check type-based parameters if(param.ParameterType == typeof(DreamContext)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetContext)); } else if(param.ParameterType == typeof(DreamMessage)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequest)); } else if(param.ParameterType == typeof(Result<DreamMessage>)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetMessageResponse)); } else if(param.ParameterType == typeof(Result<XDoc>)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetDocumentResponse)); } else if(param.ParameterType == typeof(DreamCookie)) { _plan.Add(MakeRequestCookieGetter(param.Name)); } else if(param.ParameterType == typeof(string)) { _plan.Add(MakeContextParamGetter(param.Name)); } else if(param.ParameterType == typeof(string[])) { _plan.Add(MakeContextParamListGetter(param.Name)); } else { _plan.Add(MakeConvertingContextParamGetter(param.Name, param.ParameterType)); } } } } } }
/// <summary> /// Creates a new stage instance. /// </summary> /// <param name="service">Service instance to which the stage belongs to.</param> /// <param name="method">Method definintion for stage handler.</param> /// <param name="access">Stage access level.</param> public DreamFeatureStage(IDreamService service, MethodInfo method, DreamAccess access) { if(service == null) { throw new ArgumentNullException("service"); } if(method == null) { throw new ArgumentNullException("method"); } this.Name = service.GetType().FullName + "!" + method.Name; this.Access = access; _method = method; _service = service; // determine what kind of method we were given var parameters = method.GetParameters(); if((method.ReturnType == typeof(Yield)) && (parameters.Length == 3) && (parameters[0].ParameterType == typeof(DreamContext)) && (parameters[1].ParameterType == typeof(DreamMessage)) && (parameters[2].ParameterType == typeof(Result<DreamMessage>))) { // classical coroutine feature handler _handler = (CoroutineHandler<DreamContext, DreamMessage, Result<DreamMessage>>)Delegate.CreateDelegate(typeof(CoroutineHandler<DreamContext, DreamMessage, Result<DreamMessage>>), service, method); } else { // validate method return type if(method.ReturnType != typeof(Yield) && method.ReturnType != typeof(DreamMessage) && method.ReturnType != typeof(XDoc)) { throw new InvalidCastException(string.Format("feature handler '{0}' has return type {1}, but should be either DreamMessage or IEnumerator<IYield>", method.Name, method.ReturnType)); } // create an execution plan for fetching the necessary parameters to invoke the method _plan = new List<DreamFeatureAdapter>(); foreach(var param in parameters) { // check name-based parameters if(param.Name.EqualsInvariant("verb")) { Assert(method, param, typeof(string)); _plan.Add(new DreamFeatureAdapter(param.Name, GetContextVerb)); } else if(param.Name.EqualsInvariant("path")) { Assert(method, param, typeof(string[]), typeof(string)); if(param.ParameterType == typeof(string)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetContextFeatureSubpath)); } else { _plan.Add(new DreamFeatureAdapter(param.Name, GetContextFeatureSubpathSegments)); } } else if(param.Name.EqualsInvariant("uri")) { Assert(method, param, typeof(XUri)); _plan.Add(new DreamFeatureAdapter(param.Name, GetContextUri)); } else if(param.Name.EqualsInvariant("body")) { Assert(method, param, typeof(XDoc), typeof(string), typeof(Stream), typeof(byte[])); // check which body type is requested if(param.ParameterType == typeof(XDoc)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsDocument)); } else if(param.ParameterType == typeof(string)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsText)); } else if(param.ParameterType == typeof(Stream)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsStream)); } else if(param.ParameterType == typeof(byte[])) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsBytes)); } else { throw new ShouldNeverHappenException(); } } else { // check type-based parameters if(param.ParameterType == typeof(DreamContext)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetContext)); } else if(param.ParameterType == typeof(DreamMessage)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequest)); } else if(param.ParameterType == typeof(Result<DreamMessage>)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetMessageResponse)); } else if(param.ParameterType == typeof(Result<XDoc>)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetDocumentResponse)); } else if(param.ParameterType == typeof(DreamCookie)) { _plan.Add(MakeRequestCookieGetter(param.Name)); } else if(param.ParameterType == typeof(string)) { _plan.Add(MakeContextParamGetter(param.Name)); } else if(param.ParameterType == typeof(string[])) { _plan.Add(MakeContextParamListGetter(param.Name)); } else { _plan.Add(MakeConvertingContextParamGetter(param.Name, param.ParameterType)); } } } } }
private Yield StartService(IDreamService service, XDoc blueprint, string path, XDoc config, Result<XDoc> result) { Result<DreamMessage> r; path = path.ToLowerInvariant(); if(_services.ContainsKey(path)) { string message = string.Format("conflicting uri: {0}", path); _log.Warn(message); throw new ArgumentException(message); } // TODO (steveb): validate all fields in the blueprint (presence & validity) // add fresh information Type type = service.GetType(); XUri sid = config["sid"].AsUri ?? blueprint["sid"].AsUri; XUri owner = config["uri.owner"].AsUri; // create directory of service features DreamFeatureDirectory features = CreateServiceFeatureDirectory(service, blueprint, config); XUri uri = config["uri.self"].AsUri; // now that we have the uri, we can add the storage information (if we already started the host storage service!) Plug serviceStorage = null; if(_storage != null) { var encodedPath = EncodedServicePath(uri); // check if private storage is requested if(!blueprint["setup/private-storage"].IsEmpty) { // set storage configuration // TODO (arnec): currently new private services are rooted inside shared private service, which means they // could be accessed by the shared private users if(_storageType.EqualsInvariant("s3")) { // Note (arnec): For S3 we can't use Path.Combine, since it might use a different separator from '/' var servicePath = new StringBuilder(_storagePath); if(!servicePath[servicePath.Length - 1].Equals('/')) { servicePath.Append("/"); } if(encodedPath[0].Equals('/')) { servicePath.Append(encodedPath.Substring(1)); } else { servicePath.Append(encodedPath); } yield return CreateService( "private-storage/" + encodedPath, "sid://mindtouch.com/2010/10/dream/s3.storage.private", new XDoc("config") .Elem("folder", servicePath.ToString()) .AddNodes(_storageConfig), new Result<Plug>()).Set(v => serviceStorage = v); } else { var servicePath = Path.Combine(_storagePath, encodedPath); yield return CreateService( "private-storage/" + encodedPath, "sid://mindtouch.com/2007/07/dream/storage.private", new XDoc("config").Elem("folder", servicePath), new Result<Plug>()).Set(v => serviceStorage = v); } config.Elem("uri.storage", serviceStorage.Uri); var cookies = Cookies; lock(cookies) { foreach(var cookie in cookies.Fetch(serviceStorage.Uri)) { config.Add(cookie.AsSetCookieDocument); } } } else { // use central private storage config.Elem("uri.storage", _storage.Uri.At(encodedPath)); // get central storage's internal access key DreamCookieJar cookies = Cookies; lock(cookies) { foreach(DreamCookie cookie in cookies.Fetch(_storage.Uri)) { config.Add(cookie.AsSetCookieDocument); } } } } // check if we're bootstrapping (i.e. starting ourself!) if(Self != null) { // add 'internal' access key config.Add(DreamCookie.NewSetCookie("service-key", InternalAccessKey, Self.Uri).AsSetCookieDocument); } // initialize service try { service.Initialize(this, blueprint); } catch { string message = string.Format("StartService: service initialization failed ({0} : {1})", path, sid); _log.Warn(message); throw new DreamBadRequestException(message); } // activate features lock(_features) { _features.Add(uri.Segments, 0, features); } // start service yield return r = Plug.New(uri).At("@config").Put(config, new Result<DreamMessage>(TimeSpan.MaxValue)); XDoc resultDoc; if(r.Value.IsSuccessful) { // report service as started lock(_services) { // TODO (steveb): this operation may fail if two services attempt to register at the same uri; in which case the service should be stopped. _services.Add(uri.Path.ToLowerInvariant(), new ServiceEntry(service, uri, owner, sid, blueprint)); } resultDoc = r.Value.ToDocument(); } else { StopService(uri); if(serviceStorage != null) { StopService(serviceStorage); } // deactivate features lock(_features) { _features.Remove(uri); } _log.ErrorExceptionMethodCall(null, "StartService", (sid != null) ? (object)sid : (object)type.FullName); string message = string.Format("service initialization failed: {0} ({1})", uri, sid); _log.Warn(message); throw new DreamAbortException(r.Value); } _log.DebugFormat("StartService: service started: {0} ({1})", uri, sid); result.Return(resultDoc); }
private DreamFeatureDirectory CreateServiceFeatureDirectory(IDreamService service, XDoc blueprint, XDoc config) { Type type = service.GetType(); string path = config["path"].Contents.ToLowerInvariant(); // add transport information XUri serviceUri = LocalMachineUri.AtAbsolutePath(path); config.Root.Elem("uri.self", serviceUri.ToString()); // compile list of active service features, combined by suffix int serviceUriSegmentsLength = serviceUri.Segments.Length; DreamFeatureDirectory directory = new DreamFeatureDirectory(); var methodInfos = GetMethodInfos(type); foreach(XDoc featureBlueprint in blueprint["features/feature"]) { string methodName = featureBlueprint["method"].Contents; string pattern = featureBlueprint["pattern"].AsText; // TODO (steveb): we should be a little more discerning here as this might trigger false positives bool atConfig = pattern.ContainsInvariantIgnoreCase("@config"); // locate method var methods = methodInfos[methodName]; if(methods.Count() > 1) { var found = string.Join(", ", methods.Select(m => m.DeclaringType.FullName + "!" + m.Name + "(" + string.Join(", ", m.GetParameters().Select(p => p.ParameterType.Name + " " + p.Name).ToArray()) + ")").ToArray()); throw new MissingMethodException(string.Format("found multiple definitions for {0}: {1}", methodName, found)); } if(methods.None()) { throw new MissingMethodException(string.Format("could not find {0} in class {1}", methodName, type.FullName)); } MethodInfo method = methods.First(); // determine access level DreamAccess access; switch(featureBlueprint["access"].AsText) { case null: case "public": access = DreamAccess.Public; break; case "internal": access = DreamAccess.Internal; break; case "private": access = DreamAccess.Private; break; default: throw new NotSupportedException(string.Format("access level is not supported ({0})", methodName)); } // parse pattern string string[] parts = pattern.Split(new[] { ':' }, 2); string verb = parts[0].Trim(); string signature = parts[1].Trim(); if(signature.Length == 0) { signature = string.Empty; } // add feature prologues List<DreamFeatureStage> stages = new List<DreamFeatureStage>(); stages.AddRange(_defaultPrologues); if(!atConfig) { DreamFeatureStage[] custom = service.Prologues; if(!ArrayUtil.IsNullOrEmpty(custom)) { stages.AddRange(custom); } } // add feature handler int mainStageIndex = stages.Count; stages.Add(new DreamFeatureStage(service, method, access)); // add feature epilogues if(!atConfig) { DreamFeatureStage[] custom = service.Epilogues; if(!ArrayUtil.IsNullOrEmpty(custom)) { stages.AddRange(custom); } } stages.AddRange(_defaultEpilogues); // create dream feature and add to service directory var paramAttributes = method.GetCustomAttributes(typeof(DreamFeatureParamAttribute), false).Cast<DreamFeatureParamAttribute>().ToArray(); DreamFeature feature = new DreamFeature(service, serviceUri, mainStageIndex, stages.ToArray(), verb, signature, paramAttributes); directory.Add(feature.PathSegments, serviceUriSegmentsLength, feature); } return directory; }
/// <summary> /// Creates a new stage instance. /// </summary> /// <param name="service">Service instance to which the stage belongs to.</param> /// <param name="method">Method definintion for stage handler.</param> /// <param name="access">Stage access level.</param> public DreamFeatureStage(IDreamService service, MethodInfo method, DreamAccess access) { if (service == null) { throw new ArgumentNullException("service"); } if (method == null) { throw new ArgumentNullException("method"); } this.Name = service.GetType().FullName + "!" + method.Name; this.Access = access; _method = method; _service = service; // determine what kind of method we were given var parameters = method.GetParameters(); if ((method.ReturnType == typeof(Yield)) && (parameters.Length == 3) && (parameters[0].ParameterType == typeof(DreamContext)) && (parameters[1].ParameterType == typeof(DreamMessage)) && (parameters[2].ParameterType == typeof(Result <DreamMessage>))) { // classical coroutine feature handler _handler = (CoroutineHandler <DreamContext, DreamMessage, Result <DreamMessage> >)Delegate.CreateDelegate(typeof(CoroutineHandler <DreamContext, DreamMessage, Result <DreamMessage> >), service, method); } else { // TODO (arnec): Eventually DreamMessage should have a DreamMessage<T> with custom serializers allowing arbitrary return types // validate method return type if (method.ReturnType != typeof(Yield) && method.ReturnType != typeof(DreamMessage) && method.ReturnType != typeof(XDoc)) { throw new InvalidCastException(string.Format("feature handler '{0}' has return type {1}, but should be either DreamMessage or IEnumerator<IYield>", method.Name, method.ReturnType)); } // create an execution plan for fetching the necessary parameters to invoke the method _plan = new List <DreamFeatureAdapter>(); foreach (var param in method.GetParameters()) { var attributes = param.GetCustomAttributes(false); QueryAttribute queryParam = (QueryAttribute)attributes.FirstOrDefault(i => i is QueryAttribute); PathAttribute pathParam = (PathAttribute)attributes.FirstOrDefault(i => i is PathAttribute); HeaderAttribute header = (HeaderAttribute)attributes.FirstOrDefault(i => i is HeaderAttribute); CookieAttribute cookie = (CookieAttribute)attributes.FirstOrDefault(i => i is CookieAttribute); // check attribute-based parameters if (queryParam != null) { // check if a single or a list of query parameters are requested if (param.ParameterType == typeof(string)) { _plan.Add(MakeContextParamGetter(queryParam.Name ?? param.Name)); } else if (param.ParameterType == typeof(string[])) { _plan.Add(MakeContextParamListGetter(queryParam.Name ?? param.Name)); } else { _plan.Add(MakeConvertingContextParamGetter(queryParam.Name ?? param.Name, param.ParameterType)); } } else if (pathParam != null) { if (param.ParameterType == typeof(string)) { _plan.Add(MakeContextParamGetter(pathParam.Name ?? param.Name)); } else { _plan.Add(MakeConvertingContextParamGetter(pathParam.Name ?? param.Name, param.ParameterType)); } } else if (cookie != null) { Assert(method, param, typeof(string), typeof(DreamCookie)); // check which cookie type is requested if (param.ParameterType == typeof(string)) { _plan.Add(MakeRequestCookieValueGetter(cookie.Name ?? param.Name)); } else if (param.ParameterType == typeof(DreamCookie)) { _plan.Add(MakeRequestCookieGetter(cookie.Name ?? param.Name)); } else { throw new ShouldNeverHappenException(); } } else if (header != null) { Assert(method, param, typeof(string)); _plan.Add(MakeRequestHeaderGetter(header.Name ?? param.Name)); } else { // check name-based parameters if (param.Name.EqualsInvariant("verb")) { Assert(method, param, typeof(string)); _plan.Add(new DreamFeatureAdapter(param.Name, GetContextVerb)); } else if (param.Name.EqualsInvariant("path")) { Assert(method, param, typeof(string[]), typeof(string)); if (param.ParameterType == typeof(string)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetContextFeatureSubpath)); } else { _plan.Add(new DreamFeatureAdapter(param.Name, GetContextFeatureSubpathSegments)); } } else if (param.Name.EqualsInvariant("uri")) { Assert(method, param, typeof(XUri)); _plan.Add(new DreamFeatureAdapter(param.Name, GetContextUri)); } else if (param.Name.EqualsInvariant("body")) { Assert(method, param, typeof(XDoc), typeof(string), typeof(Stream), typeof(byte[])); // check which body type is requested if (param.ParameterType == typeof(XDoc)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsDocument)); } else if (param.ParameterType == typeof(string)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsText)); } else if (param.ParameterType == typeof(Stream)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsStream)); } else if (param.ParameterType == typeof(byte[])) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequestAsBytes)); } else { throw new ShouldNeverHappenException(); } } else { // check type-based parameters if (param.ParameterType == typeof(DreamContext)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetContext)); } else if (param.ParameterType == typeof(DreamMessage)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetRequest)); } else if (param.ParameterType == typeof(Result <DreamMessage>)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetMessageResponse)); } else if (param.ParameterType == typeof(Result <XDoc>)) { _plan.Add(new DreamFeatureAdapter(param.Name, GetDocumentResponse)); } else if (param.ParameterType == typeof(DreamCookie)) { _plan.Add(MakeRequestCookieGetter(param.Name)); } else if (param.ParameterType == typeof(string)) { _plan.Add(MakeContextParamGetter(param.Name)); } else if (param.ParameterType == typeof(string[])) { _plan.Add(MakeContextParamListGetter(param.Name)); } else { _plan.Add(MakeConvertingContextParamGetter(param.Name, param.ParameterType)); } } } } } }