static bool IsRethrowOrEmitException(IOperationCoordinator coordinator, ILightNodeOptions options, IDictionary <string, object> environment, Exception ex) { var exString = ex.ToString(); coordinator.OnProcessInterrupt(options, environment, InterruptReason.ExecuteFailed, exString); switch (options.ErrorHandlingPolicy) { case ErrorHandlingPolicy.ReturnInternalServerError: environment.EmitInternalServerError(); environment.EmitStringMessage("500 InternalServerError"); return(false); case ErrorHandlingPolicy.ReturnInternalServerErrorIncludeErrorDetails: environment.EmitInternalServerError(); environment.EmitStringMessage(exString); return(false); case ErrorHandlingPolicy.ThrowException: default: environment.EmitInternalServerError(); return(true); } }
internal static object[] BindParameter(IDictionary<string, object> environment, ILightNodeOptions options, IOperationCoordinator coordinator, ValueProvider valueProvider, ParameterInfoSlim[] arguments) { var methodParameters = new object[arguments.Length]; for (int i = 0; i < arguments.Length; i++) { var item = arguments[i]; var _values = valueProvider.GetValue(item.Name); var value = _values as string; var values = _values as List<string>; var isEmpty = _values == null; var byteArray = _values as byte[]; // TODO:Experimental support if (byteArray != null && item.ParameterTypeIsArray && item.ParameterType == typeof(byte[])) { methodParameters[i] = byteArray; continue; } if (isEmpty && !item.ParameterTypeIsArray) { if (item.IsOptional) { methodParameters[i] = item.DefaultValue; continue; } else if ((!item.ParameterTypeIsString || options.ParameterStringImplicitNullAsDefault) && (item.ParameterTypeIsClass || item.ParameterTypeIsNullable)) { methodParameters[i] = null; continue; } else { coordinator.OnProcessInterrupt(options, environment, InterruptReason.ParameterBindMissing, "Lack of Parameter:" + item.Name); options.Logger.ParameterBindMissing(OperationMissingKind.LackOfParameter, item.Name); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ThrowException) { throw new ParameterMissingException(OperationMissingKind.LackOfParameter, item.Name); } else { environment.EmitBadRequest(); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ReturnErrorStatusCodeIncludeErrorDetails) { environment.EmitStringMessage("Lack of Parameter:" + item.Name); } return null; } } } else if (!item.ParameterTypeIsArray) { var conv = TypeBinder.GetConverter(item.ParameterType, !options.ParameterEnumAllowsFieldNameParse); if (conv == null) throw new InvalidOperationException("critical:register code is broken"); object pValue; if (conv(value ?? values[0], out pValue)) { methodParameters[i] = pValue; continue; } else if (item.IsOptional) { methodParameters[i] = item.DefaultValue; continue; } else if ((!item.ParameterTypeIsString || options.ParameterStringImplicitNullAsDefault) && (item.ParameterTypeIsClass || item.ParameterTypeIsNullable)) { methodParameters[i] = null; continue; } else { coordinator.OnProcessInterrupt(options, environment, InterruptReason.ParameterBindMissing, "Mismatch ParameterType:" + item.Name); options.Logger.ParameterBindMissing(OperationMissingKind.MissmatchParameterType, item.Name); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ThrowException) { throw new ParameterMissingException(OperationMissingKind.MissmatchParameterType, item.Name); } else { environment.EmitBadRequest(); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ReturnErrorStatusCodeIncludeErrorDetails) { environment.EmitStringMessage("Mismatch ParameterType:" + item.Name); } return null; } } } var arrayConv = TypeBinder.GetArrayConverter(item.ParameterType, !options.ParameterEnumAllowsFieldNameParse); if (arrayConv == null) throw new InvalidOperationException("critical:register code is broken"); methodParameters[i] = arrayConv((values != null) ? values : (value != null) ? new[] { value } : (IList<string>)new string[0]); continue; } return methodParameters; }
Task InvokeRecursive(int index, IReadOnlyList <LightNodeFilterAttribute> filters, ILightNodeOptions options, OperationContext context, IOperationCoordinator coordinator) { index += 1; if (filters.Count != index) { // chain next filter return(filters[index].Invoke(context, () => InvokeRecursive(index, filters, options, context, coordinator))); } else { // execute operation return(coordinator.ExecuteOperation(options, context, ExecuteOperation)); } }
public Task Execute(ILightNodeOptions options, OperationContext context, IOperationCoordinator coordinator) { var targetFilters = coordinator.GetFilters(options, context, filters); return(InvokeRecursive(-1, targetFilters, options, context, coordinator)); }
// Accept, Accept-Encoding flow internal IContentFormatter NegotiateFormat(IDictionary <string, object> environment, string ext, ILightNodeOptions options, IOperationCoordinator coorinator) { var requestHeader = environment["owin.RequestHeaders"] as IDictionary <string, string[]>; string[] accepts; if (ForceUseFormatter != null) { return(ForceUseFormatter); } if (!string.IsNullOrWhiteSpace(ext)) { // Ext match -> ContentEncoding match var selectedFormatters = formatterByExt[ext] as ICollection <IContentFormatter> ?? formatterByExt[ext].ToArray(); if (!selectedFormatters.Any()) { coorinator.OnProcessInterrupt(options, environment, InterruptReason.NegotiateFormatFailed, "Ext:" + ext); options.Logger.NegotiateFormatFailed(OperationMissingKind.NegotiateFormatFailed, ext); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ThrowException) { throw new NegotiateFormatFailedException(OperationMissingKind.NegotiateFormatFailed, ext); } else { environment.EmitNotAcceptable(); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ReturnErrorStatusCodeIncludeErrorDetails) { environment.EmitStringMessage("NegotiateFormat failed, ext:" + ext); } } return(null); } return(SelectAcceptEncodingFormatter(requestHeader, selectedFormatters)); } else if (requestHeader.TryGetValue("Accept", out accepts)) { if (optionFormatters.Length == 1) { return(options.DefaultFormatter); // optimize path, defaultFormatter only } // MediaType match -> ContentEncoding match var acceptsValues = GetDescendingQualityHeaderValues(accepts); var formatters = acceptsValues.SelectMany(x => formatterByMediaType[x.Item3]).ToArray(); if (formatters.Length == 0) { // only accept-encoding goto CONTENT_ENCODING_MATCH; } return(SelectAcceptEncodingFormatter(requestHeader, formatters)); } // ContentEncoding match CONTENT_ENCODING_MATCH: { if (optionFormatters.Length == 1) { return(options.DefaultFormatter); // optimize path, defaultFormatter only } // ContentEncoding match string[] rawAcceptEncoding; if (!requestHeader.TryGetValue("Accept-Encoding", out rawAcceptEncoding)) { return(options.DefaultFormatter); } var acceptEncodings = GetDescendingQualityHeaderValues(rawAcceptEncoding); var formatter = acceptEncodings .Select(kvp => formatterByContentEncoding[kvp.Item3].FirstOrDefault()) .FirstOrDefault(x => x != null); if (formatter == null) { return(options.DefaultFormatter); } return(formatter); } }
internal static object[] BindParameter(HttpContext httpContext, ILightNodeOptions options, IOperationCoordinator coordinator, ValueProvider valueProvider, ParameterInfoSlim[] arguments) { var methodParameters = new object[arguments.Length]; for (int i = 0; i < arguments.Length; i++) { var item = arguments[i]; var _values = valueProvider.GetValue(item.Name); var value = _values as string; var values = _values as List <string>; var isEmpty = _values == null; if (isEmpty && !item.ParameterTypeIsArray) { if (item.IsOptional) { methodParameters[i] = item.DefaultValue; continue; } else if ((!item.ParameterTypeIsString || options.ParameterStringImplicitNullAsDefault) && (item.ParameterTypeIsClass || item.ParameterTypeIsNullable)) { methodParameters[i] = null; continue; } else { coordinator.OnProcessInterrupt(options, httpContext, InterruptReason.ParameterBindMissing, "Lack of Parameter:" + item.Name); options.Logger.ParameterBindMissing(OperationMissingKind.LackOfParameter, item.Name); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ThrowException) { throw new ParameterMissingException(OperationMissingKind.LackOfParameter, item.Name); } else { httpContext.EmitBadRequest(); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ReturnErrorStatusCodeIncludeErrorDetails) { httpContext.EmitStringMessage("Lack of Parameter:" + item.Name); } return(null); } } } else if (!item.ParameterTypeIsArray) { var conv = TypeBinder.GetConverter(item.ParameterType, !options.ParameterEnumAllowsFieldNameParse); if (conv == null) { throw new InvalidOperationException("critical:register code is broken"); } object pValue; if (conv(value ?? values[0], out pValue)) { methodParameters[i] = pValue; continue; } else if (item.IsOptional) { methodParameters[i] = item.DefaultValue; continue; } else if ((!item.ParameterTypeIsString || options.ParameterStringImplicitNullAsDefault) && (item.ParameterTypeIsClass || item.ParameterTypeIsNullable)) { methodParameters[i] = null; continue; } else { coordinator.OnProcessInterrupt(options, httpContext, InterruptReason.ParameterBindMissing, "Mismatch ParameterType:" + item.Name); options.Logger.ParameterBindMissing(OperationMissingKind.MissmatchParameterType, item.Name); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ThrowException) { throw new ParameterMissingException(OperationMissingKind.MissmatchParameterType, item.Name); } else { httpContext.EmitBadRequest(); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ReturnErrorStatusCodeIncludeErrorDetails) { httpContext.EmitStringMessage("Mismatch ParameterType:" + item.Name); } return(null); } } } var arrayConv = TypeBinder.GetArrayConverter(item.ParameterType, !options.ParameterEnumAllowsFieldNameParse); if (arrayConv == null) { throw new InvalidOperationException("critical:register code is broken"); } methodParameters[i] = arrayConv((values != null) ? values : (value != null) ? new[] { value } : (IList <string>) new string[0]); continue; } return(methodParameters); }
Task InvokeRecursive(int index, IReadOnlyList<LightNodeFilterAttribute> filters, ILightNodeOptions options, OperationContext context, IOperationCoordinator coordinator) { index += 1; if (filters.Count != index) { // chain next filter return filters[index].Invoke(context, () => InvokeRecursive(index, filters, options, context, coordinator)); } else { // execute operation return coordinator.ExecuteOperation(options, context, ExecuteOperation); } }
OperationHandler SelectHandler(IDictionary <string, object> environment, IOperationCoordinator coorinator, out AcceptVerbs verb, out string ext) { // out default verb = AcceptVerbs.Get; ext = ""; var path = environment["owin.RequestPath"] as string; var method = environment["owin.RequestMethod"] as string; // extract path var keyBase = path.Trim('/').Split('/'); if (keyBase.Length != 2) { goto NOT_FOUND; } // extract "extension" for media type var extStart = keyBase[1].LastIndexOf("."); if (extStart != -1) { ext = keyBase[1].Substring(extStart + 1); keyBase[1] = keyBase[1].Substring(0, keyBase[1].Length - ext.Length - 1); } // {ClassName, MethodName} var key = new RequestPath(keyBase[0], keyBase[1]); OperationHandler handler; if (handlers.TryGetValue(key, out handler)) { // verb check if (StringComparer.OrdinalIgnoreCase.Equals(method, "GET")) { verb = AcceptVerbs.Get; } else if (StringComparer.OrdinalIgnoreCase.Equals(method, "POST")) { verb = AcceptVerbs.Post; } else if (StringComparer.OrdinalIgnoreCase.Equals(method, "PUT")) { verb = AcceptVerbs.Put; } else if (StringComparer.OrdinalIgnoreCase.Equals(method, "DELETE")) { verb = AcceptVerbs.Delete; } else if (StringComparer.OrdinalIgnoreCase.Equals(method, "PATCH")) { verb = AcceptVerbs.Patch; } else { goto VERB_MISSING; } if (!handler.AcceptVerb.HasFlag(verb)) { goto VERB_MISSING; } return(handler); // OK } else { goto NOT_FOUND; } VERB_MISSING: coorinator.OnProcessInterrupt(options, environment, InterruptReason.MethodNotAllowed, "MethodName:" + method); options.Logger.MethodNotAllowed(OperationMissingKind.MethodNotAllowed, path, method); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ThrowException) { throw new MethodNotAllowedException(OperationMissingKind.MethodNotAllowed, path, method); } else { environment.EmitMethodNotAllowed(); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ReturnErrorStatusCodeIncludeErrorDetails) { environment.EmitStringMessage("MethodNotAllowed:" + method); } return(null); } NOT_FOUND: coorinator.OnProcessInterrupt(options, environment, InterruptReason.OperationNotFound, "SearchedPath:" + path); options.Logger.OperationNotFound(OperationMissingKind.OperationNotFound, path); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ThrowException) { throw new OperationNotFoundException(OperationMissingKind.MethodNotAllowed, path); } else { environment.EmitNotFound(); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ReturnErrorStatusCodeIncludeErrorDetails) { environment.EmitStringMessage("OperationNotFound:" + path); } return(null); } }
public Task Execute(ILightNodeOptions options, OperationContext context, IOperationCoordinator coordinator) { var targetFilters = coordinator.GetFilters(options, context, filters); return InvokeRecursive(-1, targetFilters, options, context, coordinator); }
// Accept, Accept-Encoding flow internal IContentFormatter NegotiateFormat(IDictionary<string, object> environment, string ext, ILightNodeOptions options, IOperationCoordinator coorinator) { var requestHeader = environment["owin.RequestHeaders"] as IDictionary<string, string[]>; string[] accepts; if (ForceUseFormatter != null) return ForceUseFormatter; if (!string.IsNullOrWhiteSpace(ext)) { // Ext match -> ContentEncoding match var selectedFormatters = formatterByExt[ext] as ICollection<IContentFormatter> ?? formatterByExt[ext].ToArray(); if (!selectedFormatters.Any()) { coorinator.OnProcessInterrupt(options, environment, InterruptReason.NegotiateFormatFailed, "Ext:" + ext); options.Logger.NegotiateFormatFailed(OperationMissingKind.NegotiateFormatFailed, ext); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ThrowException) { throw new NegotiateFormatFailedException(OperationMissingKind.NegotiateFormatFailed, ext); } else { environment.EmitNotAcceptable(); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ReturnErrorStatusCodeIncludeErrorDetails) { environment.EmitStringMessage("NegotiateFormat failed, ext:" + ext); } } return null; } return SelectAcceptEncodingFormatter(requestHeader, selectedFormatters); } else if (requestHeader.TryGetValue("Accept", out accepts)) { if (optionFormatters.Length == 1) return options.DefaultFormatter; // optimize path, defaultFormatter only // MediaType match -> ContentEncoding match var acceptsValues = GetDescendingQualityHeaderValues(accepts); var formatters = acceptsValues.SelectMany(x => formatterByMediaType[x.Item3]).ToArray(); if (formatters.Length == 0) { // only accept-encoding goto CONTENT_ENCODING_MATCH; } return SelectAcceptEncodingFormatter(requestHeader, formatters); } // ContentEncoding match CONTENT_ENCODING_MATCH: { if (optionFormatters.Length == 1) return options.DefaultFormatter; // optimize path, defaultFormatter only // ContentEncoding match string[] rawAcceptEncoding; if (!requestHeader.TryGetValue("Accept-Encoding", out rawAcceptEncoding)) { return options.DefaultFormatter; } var acceptEncodings = GetDescendingQualityHeaderValues(rawAcceptEncoding); var formatter = acceptEncodings .Select(kvp => formatterByContentEncoding[kvp.Item3].FirstOrDefault()) .FirstOrDefault(x => x != null); if (formatter == null) return options.DefaultFormatter; return formatter; } }
OperationHandler SelectHandler(IDictionary<string, object> environment, IOperationCoordinator coorinator, out AcceptVerbs verb, out string ext) { // out default verb = AcceptVerbs.Get; ext = ""; var path = environment["owin.RequestPath"] as string; var method = environment["owin.RequestMethod"] as string; // extract path var keyBase = path.Trim('/').Split('/'); if (keyBase.Length != 2) { goto NOT_FOUND; } // extract "extension" for media type var extStart = keyBase[1].LastIndexOf("."); if (extStart != -1) { ext = keyBase[1].Substring(extStart + 1); keyBase[1] = keyBase[1].Substring(0, keyBase[1].Length - ext.Length - 1); } // {ClassName, MethodName} var key = new RequestPath(keyBase[0], keyBase[1]); OperationHandler handler; if (handlers.TryGetValue(key, out handler)) { // verb check if (StringComparer.OrdinalIgnoreCase.Equals(method, "GET")) { verb = AcceptVerbs.Get; } else if (StringComparer.OrdinalIgnoreCase.Equals(method, "POST")) { verb = AcceptVerbs.Post; } else if (StringComparer.OrdinalIgnoreCase.Equals(method, "PUT")) { verb = AcceptVerbs.Put; } else if (StringComparer.OrdinalIgnoreCase.Equals(method, "DELETE")) { verb = AcceptVerbs.Delete; } else if (StringComparer.OrdinalIgnoreCase.Equals(method, "PATCH")) { verb = AcceptVerbs.Patch; } else { goto VERB_MISSING; } if (!handler.AcceptVerb.HasFlag(verb)) { goto VERB_MISSING; } return handler; // OK } else { goto NOT_FOUND; } VERB_MISSING: coorinator.OnProcessInterrupt(options, environment, InterruptReason.MethodNotAllowed, "MethodName:" + method); options.Logger.MethodNotAllowed(OperationMissingKind.MethodNotAllowed, path, method); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ThrowException) { throw new MethodNotAllowedException(OperationMissingKind.MethodNotAllowed, path, method); } else { environment.EmitMethodNotAllowed(); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ReturnErrorStatusCodeIncludeErrorDetails) { environment.EmitStringMessage("MethodNotAllowed:" + method); } return null; } NOT_FOUND: coorinator.OnProcessInterrupt(options, environment, InterruptReason.OperationNotFound, "SearchedPath:" + path); options.Logger.OperationNotFound(OperationMissingKind.OperationNotFound, path); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ThrowException) { throw new OperationNotFoundException(OperationMissingKind.MethodNotAllowed, path); } else { environment.EmitNotFound(); if (options.OperationMissingHandlingPolicy == OperationMissingHandlingPolicy.ReturnErrorStatusCodeIncludeErrorDetails) { environment.EmitStringMessage("OperationNotFound:" + path); } return null; } }
static bool IsRethrowOrEmitException(IOperationCoordinator coordinator, ILightNodeOptions options, IDictionary<string, object> environment, Exception ex) { var exString = ex.ToString(); coordinator.OnProcessInterrupt(options, environment, InterruptReason.ExecuteFailed, exString); switch (options.ErrorHandlingPolicy) { case ErrorHandlingPolicy.ReturnInternalServerError: environment.EmitInternalServerError(); environment.EmitStringMessage("500 InternalServerError"); return false; case ErrorHandlingPolicy.ReturnInternalServerErrorIncludeErrorDetails: environment.EmitInternalServerError(); environment.EmitStringMessage(exString); return false; case ErrorHandlingPolicy.ThrowException: default: environment.EmitInternalServerError(); return true; } }
public BackgroundHostedService(ILogger <BackgroundHostedService> logger, IOperationCoordinator operationCoordinator) { this.logger = logger; this.operationCoordinator = operationCoordinator; }