/// <summary>
        /// Registers the <see cref="EmbeddedResourceProvider"/> within a <see cref="HostingEnvironment"/>.
        /// </summary>
        /// <remarks>
        /// This function will properly register the <see cref="EmbeddedResourceProvider"/> even when being run from
        /// within a pre-compiled web application.
        /// </remarks>
        public static void Register()
        {
#if MONO
            HostingEnvironment.RegisterVirtualPathProvider(new EmbeddedResourceProvider());
#else
            if (BuildManager.IsPrecompiledApp)
            {
                // HACK: Call internal registration function for virtual path provider in pre-compiled web application
                Type hostingEnvironment = AssemblyInfo.FindType("System.Web.Hosting.HostingEnvironment");

                if ((object)hostingEnvironment == null)
                {
                    throw new InvalidOperationException("Failed to register EmbeddedResourceProvider: could not find type \"System.Web.Hosting.HostingEnvironment\".");
                }

                MethodInfo internalRegisterVirtualPathProvider = hostingEnvironment.GetMethod("RegisterVirtualPathProviderInternal", BindingFlags.NonPublic | BindingFlags.Static);

                if ((object)internalRegisterVirtualPathProvider == null)
                {
                    throw new InvalidOperationException("Failed to register EmbeddedResourceProvider: could not find internal static method \"System.Web.Hosting.HostingEnvironment.RegisterVirtualPathProviderInternal()\".");
                }

                internalRegisterVirtualPathProvider.Invoke(null, new object[] { new EmbeddedResourceProvider() });
            }
            else
            {
                HostingEnvironment.RegisterVirtualPathProvider(new EmbeddedResourceProvider());
            }
#endif
        }
Beispiel #2
0
            /// <summary>
            /// Controls the binding of a serialized object to a type.
            /// </summary>
            /// <param name="assemblyName">Specifies the <see cref="Assembly"/> name of the serialized object.</param>
            /// <param name="typeName">Specifies the <see cref="Type"/> name of the serialized object.</param>
            /// <returns>The type of the object the formatter creates a new instance of.</returns>
            public override Type BindToType(string assemblyName, string typeName)
            {
                // Perform namespace transformations that occurred when migrating to the Grid Solutions Framework
                // from various older versions of code with different namespaces
                string newTypeName = typeName
                                     .Replace("TVA.", "GSF.")
                                     .Replace("TimeSeriesFramework.", "GSF.TimeSeries.")
                                     .Replace("ConnectionTester.", "GSF.PhasorProtocols.") // PMU Connection Tester namespace
                                     .Replace("TVA.Phasors.", "GSF.PhasorProtocols.")      // 2007 TVA Code Library namespace
                                     .Replace("Tva.Phasors.", "GSF.PhasorProtocols.")      // 2008 TVA Code Library namespace
                                     .Replace("BpaPdcStream", "BPAPDCstream")              // 2013 GSF uppercase phasor protocol namespace
                                     .Replace("FNet", "FNET")                              // 2013 GSF uppercase phasor protocol namespace
                                     .Replace("Iec61850_90_5", "IEC61850_90_5")            // 2013 GSF uppercase phasor protocol namespace
                                     .Replace("Ieee1344", "IEEE1344")                      // 2013 GSF uppercase phasor protocol namespace
                                     .Replace("IeeeC37_118", "IEEEC37_118");               // 2013 GSF uppercase phasor protocol namespace

                // Check for 2009 TVA Code Library namespace
                if (newTypeName.StartsWith("PhasorProtocols", StringComparison.Ordinal))
                {
                    newTypeName = "GSF." + newTypeName;
                }

                // Check for 2014 LineFrequency type in the GSF phasor protocol namespace
                if (newTypeName.Equals("GSF.PhasorProtocols.LineFrequency", StringComparison.Ordinal))
                {
                    newTypeName = "GSF.Units.EE.LineFrequency";
                }

                try
                {
                    // Search each assembly in the current application domain for the type with the transformed name
                    return(AssemblyInfo.FindType(newTypeName));
                }
                catch
                {
                    // Fall back on more brute force search when simple search fails
                    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                    {
                        try
                        {
                            Type newType = assembly.GetType(newTypeName);

                            if ((object)newType != null)
                            {
                                return(newType);
                            }
                        }
                        catch
                        {
                            // Ignore errors that occur when attempting to load
                            // types from assemblies as we may still be able to
                            // load the type from a different assembly
                        }
                    }
                }

                // No type found; return null
                return(null);
            }
Beispiel #3
0
        private async Task ProcessHTTPHandlerAsync(string pageName, bool embeddedResource, HttpRequestMessage request, HttpResponseMessage response, CancellationToken cancellationToken)
        {
            string fileName = GetResourceFileName(pageName, embeddedResource);

            if (!m_handlerTypeCache.TryGetValue(fileName, out Type handlerType))
            {
                if (!ResourceExists(fileName, embeddedResource))
                {
                    response.StatusCode = HttpStatusCode.NotFound;
                    return;
                }

                using (Stream source = await OpenResourceAsync(fileName, embeddedResource, cancellationToken))
                {
                    string handlerHeader;

                    // Parse class name from ASHX handler header parameters
                    using (StreamReader reader = new StreamReader(source))
                        handlerHeader = (await reader.ReadToEndAsync()).RemoveCrLfs().Trim();

                    // Clean up header formatting to make parsing easier
                    handlerHeader = handlerHeader.RemoveDuplicateWhiteSpace().Replace(" =", "=").Replace("= ", "=");

                    string[] tokens = handlerHeader.Split(' ');

                    if (!tokens.Any(token => token.Equals("WebHandler", StringComparison.OrdinalIgnoreCase)))
                    {
                        throw new InvalidOperationException($"Expected \"WebHandler\" file type not found in ASHX file header: {handlerHeader}");
                    }

                    Dictionary <string, string> parameters = handlerHeader.ReplaceCaseInsensitive("WebHandler", "").Replace("<%", "").Replace("%>", "").Replace("@", "").Trim().ParseKeyValuePairs(' ');

                    if (!parameters.TryGetValue("Class", out string className))
                    {
                        throw new InvalidOperationException($"Missing \"Class\" parameter in ASHX file header: {handlerHeader}");
                    }

                    // Remove quotes from class name
                    className = className.Substring(1, className.Length - 2).Trim();

                    handlerType = AssemblyInfo.FindType(className);

                    if (m_handlerTypeCache.TryAdd(fileName, handlerType))
                    {
                        OnStatusMessage($"Cached handler type [{handlerType?.FullName}] for file \"{fileName}\"");
                    }
                }
            }

            IHostedHttpHandler handler = null;

            if ((object)handlerType != null)
            {
                handler = Activator.CreateInstance(handlerType) as IHostedHttpHandler;
            }

            if ((object)handler == null)
            {
                throw new InvalidOperationException($"Failed to create hosted HTTP handler \"{handlerType?.FullName}\" - make sure class implements IHostedHttpHandler interface.");
            }

            if (m_options.ClientCacheEnabled && handler.UseClientCache)
            {
                if (PublishResponseContent(request, response, handler.GetContentHash(request)))
                {
                    await handler.ProcessRequestAsync(request, response, cancellationToken);
                }
            }
            else
            {
                await handler.ProcessRequestAsync(request, response, cancellationToken);
            }
        }
Beispiel #4
0
        private void CopyModelAsCsvToStream(SecurityPrincipal securityPrincipal, NameValueCollection requestParameters, Stream responseStream, Action flushResponse, CompatibleCancellationToken cancellationToken)
        {
            string modelName     = requestParameters["ModelName"];
            string hubName       = requestParameters["HubName"];
            string connectionID  = requestParameters["ConnectionID"];
            string filterText    = requestParameters["FilterText"];
            string sortField     = requestParameters["SortField"];
            bool   sortAscending = requestParameters["SortAscending"].ParseBoolean();
            bool   showDeleted   = requestParameters["ShowDeleted"].ParseBoolean();

            string[] parentKeys = requestParameters["ParentKeys"].Split(',').Select(parentKey => parentKey.Replace(','.RegexEncode(), ",")).ToArray();

            if (string.IsNullOrEmpty(modelName))
            {
                throw new ArgumentNullException(nameof(modelName), "Cannot download CSV data: no model type name was specified.");
            }

            if (string.IsNullOrEmpty(hubName))
            {
                throw new ArgumentNullException(nameof(hubName), "Cannot download CSV data: no hub type name was specified.");
            }

            Type modelType = AssemblyInfo.FindType(modelName);

            if ((object)modelType == null)
            {
                throw new InvalidOperationException($"Cannot download CSV data: failed to find model type \"{modelName}\" in loaded assemblies.");
            }

            Type hubType = AssemblyInfo.FindType(hubName);

            if ((object)hubType == null)
            {
                throw new InvalidOperationException($"Cannot download CSV data: failed to find hub type \"{hubName}\" in loaded assemblies.");
            }

            IRecordOperationsHub hub;

            // Record operation tuple defines method name and allowed roles
            Tuple <string, string> queryRecordCountOperation;
            Tuple <string, string> queryRecordsOperation;
            string queryRoles;

            try
            {
                // Create a local record operations hub instance so that CSV export can query same record set that is visible in active hub context
                hub = Activator.CreateInstance(hubType) as IRecordOperationsHub;

                if ((object)hub == null)
                {
                    throw new SecurityException($"Cannot download CSV data: hub type \"{hubName}\" is not a IRecordOperationsHub, access cannot be validated.");
                }

                // Assign provided connection ID from active hub context to our local hub instance so that any session based data will be available to query functions
                hub.ConnectionID = connectionID;

                Tuple <string, string>[] recordOperations;

                try
                {
                    // Get any authorized query roles as defined in hub records operations for modeled table, default to read allowed for query
                    recordOperations = hub.RecordOperationsCache.GetRecordOperations(modelType);

                    if ((object)recordOperations == null)
                    {
                        throw new NullReferenceException();
                    }
                }
                catch (KeyNotFoundException ex)
                {
                    throw new SecurityException($"Cannot download CSV data: hub type \"{hubName}\" does not define record operations for \"{modelName}\", access cannot be validated.", ex);
                }

                // Get record operation for querying record count
                queryRecordCountOperation = recordOperations[(int)RecordOperation.QueryRecordCount];

                if ((object)queryRecordCountOperation == null)
                {
                    throw new NullReferenceException();
                }

                // Get record operation for querying records
                queryRecordsOperation = recordOperations[(int)RecordOperation.QueryRecords];

                if ((object)queryRecordsOperation == null)
                {
                    throw new NullReferenceException();
                }

                // Get any defined role restrictions for record query operation - access to CSV download will based on these roles
                queryRoles = string.IsNullOrEmpty(queryRecordsOperation.Item1) ? "*" : queryRecordsOperation.Item2 ?? "*";
            }
            catch (Exception ex)
            {
                throw new SecurityException($"Cannot download CSV data: failed to instantiate hub type \"{hubName}\" or access record operations, access cannot be validated.", ex);
            }

            DataContext dataContext = hub.DataContext;

            // Validate current user has access to requested data
            if (!dataContext.UserIsInRole(securityPrincipal, queryRoles))
            {
                throw new SecurityException($"Cannot download CSV data: access is denied for user \"{securityPrincipal?.Identity.Name ?? "Undefined"}\", minimum required roles = {queryRoles.ToDelimitedString(", ")}.");
            }

            const int TargetBufferSize = 524288;

            StringBuilder        readBuffer  = new StringBuilder(TargetBufferSize * 2);
            ManualResetEventSlim bufferReady = new ManualResetEventSlim(false);
            List <string>        writeBuffer = new List <string>();
            object writeBufferLock           = new object();
            bool   readComplete = false;

            ITableOperations table;

            string[] fieldNames;
            bool     hasDeletedField;

            table = dataContext.Table(modelType);

            fieldNames = table.GetFieldNames(false).Where(field => (!(table.FieldHasAttribute <GSF.Data.Model.CSVExcludeFieldAttribute>(field)))).ToArray();

            hasDeletedField = !string.IsNullOrEmpty(dataContext.GetIsDeletedFlag(modelType));

            Task readTask = Task.Factory.StartNew(() =>
            {
                try
                {
                    const int PageSize = 250;

                    // Get query operation methods
                    MethodInfo queryRecordCount = hubType.GetMethod(queryRecordCountOperation.Item1);
                    MethodInfo queryRecords     = hubType.GetMethod(queryRecordsOperation.Item1);

                    // Setup query parameters
                    List <object> queryRecordCountParameters = new List <object>();
                    List <object> queryRecordsParameters     = new List <object>();

                    // Add current show deleted state parameter, if model defines a show deleted field
                    if (hasDeletedField)
                    {
                        queryRecordCountParameters.Add(showDeleted);
                    }

                    // Add any parent key restriction parameters
                    if (parentKeys.Length > 0 && parentKeys[0].Length > 0)
                    {
                        queryRecordCountParameters.AddRange(parentKeys.Select((value, i) =>
                        {
                            Type type = queryRecordCount.GetParameters()[i].ParameterType;

                            if (type == typeof(string))
                            {
                                return((object)value);
                            }

                            if (type == typeof(Guid))
                            {
                                return((object)Guid.Parse(value));
                            }

                            return(Convert.ChangeType(value, type));
                        }));
                    }

                    // Add parameters for query records from query record count parameters - they match up to this point
                    queryRecordsParameters.AddRange(queryRecordCountParameters);

                    // Add sort field parameter
                    queryRecordsParameters.Add(sortField);

                    // Add ascending sort order parameter
                    queryRecordsParameters.Add(sortAscending);

                    // Track parameter index for current page to query
                    int pageParameterIndex = queryRecordsParameters.Count;

                    // Add page index parameter
                    queryRecordsParameters.Add(0);

                    // Add page size parameter
                    queryRecordsParameters.Add(PageSize);

                    // Add filter text parameter
                    queryRecordCountParameters.Add(filterText);
                    queryRecordsParameters.Add(filterText);

                    // Read queried records in page sets so there is not a memory burden and long initial query delay on very large data sets
                    int recordCount = (int)queryRecordCount.Invoke(hub, queryRecordCountParameters.ToArray());
                    int totalPages  = Math.Max((int)Math.Ceiling(recordCount / (double)PageSize), 1);

                    // Read data pages
                    for (int page = 0; page < totalPages && !cancellationToken.IsCancelled; page++)
                    {
                        // Update desired page to query
                        queryRecordsParameters[pageParameterIndex] = page + 1;

                        // Query page records
                        IEnumerable records = queryRecords.Invoke(hub, queryRecordsParameters.ToArray()) as IEnumerable ?? Enumerable.Empty <object>();
                        int exportCount     = 0;

                        // Export page records
                        foreach (object record in records)
                        {
                            // Periodically check for client cancellation
                            if (exportCount++ % (PageSize / 4) == 0 && cancellationToken.IsCancelled)
                            {
                                break;
                            }

                            readBuffer.AppendLine(string.Join(",", fieldNames.Select(fieldName => $"\"{table.GetFieldValue(record, fieldName)}\"")));

                            if (readBuffer.Length < TargetBufferSize)
                            {
                                continue;
                            }

                            lock (writeBufferLock)
                                writeBuffer.Add(readBuffer.ToString());

                            readBuffer.Clear();
                            bufferReady.Set();
                        }
                    }

                    if (readBuffer.Length > 0)
                    {
                        lock (writeBufferLock)
                            writeBuffer.Add(readBuffer.ToString());
                    }
                }
                finally
                {
                    readComplete = true;
                    bufferReady.Set();
                }
            },
                                                  cancellationToken);

            Task writeTask = Task.Factory.StartNew(() =>
            {
                using (StreamWriter writer = new StreamWriter(responseStream))
                {
                    //Ticks exportStart = DateTime.UtcNow.Ticks;
                    string[] localBuffer;

                    Action flushStream = () =>
                    {
                        writer.Flush();

                        if ((object)flushResponse != null)
                        {
                            flushResponse();
                        }
                    };

                    // Write column headers
                    writer.WriteLine(string.Join(",", fieldNames.Select(fieldName => $"\"{fieldName}\"")));
                    flushStream();

                    while ((writeBuffer.Count > 0 || !readComplete) && !cancellationToken.IsCancelled)
                    {
                        bufferReady.Wait(cancellationToken);
                        bufferReady.Reset();

                        lock (writeBufferLock)
                        {
                            localBuffer = writeBuffer.ToArray();
                            writeBuffer.Clear();
                        }

                        foreach (string buffer in localBuffer)
                        {
                            writer.Write(buffer);
                        }
                    }

                    // Flush stream
                    flushStream();
                    //Debug.WriteLine("Export time: " + (DateTime.UtcNow.Ticks - exportStart).ToElapsedTimeString(3));
                }
            },
                                                   cancellationToken);

            Task.WaitAll(readTask, writeTask);
        }
        private async Task CopyModelAsCsvToStreamAsync(NameValueCollection requestParameters, Stream responseStream, Func <bool> isCancelled, Func <Task> flushResponseAsync = null)
        {
            SecurityProviderCache.ValidateCurrentProvider();

            string modelName     = requestParameters["ModelName"];
            string hubName       = requestParameters["HubName"];
            string filterText    = requestParameters["FilterText"];
            string sortField     = requestParameters["SortField"];
            bool   sortAscending = requestParameters["SortAscending"].ParseBoolean();
            bool   showDeleted   = requestParameters["ShowDeleted"].ParseBoolean();

            string[]  parentKeys = requestParameters["ParentKeys"].Split(',');
            const int PageSize   = 250;

            if (string.IsNullOrEmpty(modelName))
            {
                throw new ArgumentNullException(nameof(modelName), "Cannot download CSV data: no model type name was specified.");
            }

            if (string.IsNullOrEmpty(hubName))
            {
                throw new ArgumentNullException(nameof(hubName), "Cannot download CSV data: no hub type name was specified.");
            }

            Type modelType = AssemblyInfo.FindType(modelName);

            if ((object)modelType == null)
            {
                throw new InvalidOperationException($"Cannot download CSV data: failed to find model type \"{modelName}\" in loaded assemblies.");
            }

            Type hubType = AssemblyInfo.FindType(hubName);

            if ((object)hubType == null)
            {
                throw new InvalidOperationException($"Cannot download CSV data: failed to find hub type \"{hubName}\" in loaded assemblies.");
            }

            IRecordOperationsHub hub;

            // Record operation tuple defines method name and allowed roles
            Tuple <string, string> queryRecordCountOperation;
            Tuple <string, string> queryRecordsOperation;
            string queryRoles;

            try
            {
                hub = Activator.CreateInstance(hubType) as IRecordOperationsHub;

                if ((object)hub == null)
                {
                    throw new SecurityException($"Cannot download CSV data: hub type \"{hubName}\" is not a IRecordOperationsHub, access cannot be validated.");
                }

                Tuple <string, string>[] recordOperations;

                try
                {
                    // Get any authorized query roles as defined in hub records operations for modeled table, default to read allowed for query
                    recordOperations = hub.RecordOperationsCache.GetRecordOperations(modelType);

                    if ((object)recordOperations == null)
                    {
                        throw new NullReferenceException();
                    }
                }
                catch (KeyNotFoundException ex)
                {
                    throw new SecurityException($"Cannot download CSV data: hub type \"{hubName}\" does not define record operations for \"{modelName}\", access cannot be validated.", ex);
                }

                // Get record operation for querying record count
                queryRecordCountOperation = recordOperations[(int)RecordOperation.QueryRecordCount];

                if ((object)queryRecordCountOperation == null)
                {
                    throw new NullReferenceException();
                }

                // Get record operation for querying records
                queryRecordsOperation = recordOperations[(int)RecordOperation.QueryRecords];

                if ((object)queryRecordsOperation == null)
                {
                    throw new NullReferenceException();
                }

                // Get any defined role restrictions for record query operation - access to CSV download will based on these roles
                queryRoles = string.IsNullOrEmpty(queryRecordsOperation.Item1) ? "*" : queryRecordsOperation.Item2 ?? "*";
            }
            catch (Exception ex)
            {
                throw new SecurityException($"Cannot download CSV data: failed to instantiate hub type \"{hubName}\" or access record operations, access cannot be validated.", ex);
            }

            using (DataContext dataContext = new DataContext())
                using (StreamWriter writer = new StreamWriter(responseStream))
                {
                    // Validate current user has access to requested data
                    if (!dataContext.UserIsInRole(queryRoles))
                    {
                        throw new SecurityException($"Cannot download CSV data: access is denied for user \"{Thread.CurrentPrincipal.Identity?.Name ?? "Undefined"}\", minimum required roles = {queryRoles.ToDelimitedString(", ")}.");
                    }

                    AdoDataConnection connection = dataContext.Connection;
                    ITableOperations  table      = dataContext.Table(modelType);
                    string[]          fieldNames = table.GetFieldNames(false);

                    Func <Task> flushAsync = async() =>
                    {
                        // ReSharper disable once AccessToDisposedClosure
                        await writer.FlushAsync();

                        if ((object)flushResponseAsync != null)
                        {
                            await flushResponseAsync();
                        }
                    };

                    // Write column headers
                    await writer.WriteLineAsync(string.Join(",", fieldNames.Select(fieldName => connection.EscapeIdentifier(fieldName, true))));
                    await flushAsync();

                    // See if modeled table has a flag field that represents a deleted row
                    bool hasDeletedField = !string.IsNullOrEmpty(dataContext.GetIsDeletedFlag(modelType));

                    // Get query operation methods
                    MethodInfo queryRecordCount = hubType.GetMethod(queryRecordCountOperation.Item1);
                    MethodInfo queryRecords     = hubType.GetMethod(queryRecordsOperation.Item1);

                    // Setup query parameters
                    List <object> queryRecordCountParameters = new List <object>();
                    List <object> queryRecordsParameters     = new List <object>();

                    // Add current show deleted state parameter, if model defines a show deleted field
                    if (hasDeletedField)
                    {
                        queryRecordCountParameters.Add(showDeleted);
                    }

                    // Add any parent key restriction parameters
                    if (parentKeys.Length > 0 && parentKeys[0].Length > 0)
                    {
                        queryRecordCountParameters.AddRange(parentKeys);
                    }

                    // Add parameters for query records from query record count parameters - they match up to this point
                    queryRecordsParameters.AddRange(queryRecordCountParameters);

                    // Add sort field parameter
                    queryRecordsParameters.Add(sortField);

                    // Add ascending sort order parameter
                    queryRecordsParameters.Add(sortAscending);

                    // Track parameter index for current page to query
                    int pageParameterIndex = queryRecordsParameters.Count;

                    // Add page index parameter
                    queryRecordsParameters.Add(0);

                    // Add page size parameter
                    queryRecordsParameters.Add(PageSize);

                    // Add filter text parameter
                    queryRecordCountParameters.Add(filterText);
                    queryRecordsParameters.Add(filterText);

                    // Read queried records in page sets so there is not a memory burden and long initial query delay on very large data sets
                    int recordCount = (int)queryRecordCount.Invoke(hub, queryRecordCountParameters.ToArray());
                    int totalPages  = Math.Max((int)Math.Ceiling(recordCount / (double)PageSize), 1);

                    // Write data pages
                    for (int page = 0; page < totalPages && !isCancelled(); page++)
                    {
                        // Update desired page to query
                        queryRecordsParameters[pageParameterIndex] = page + 1;

                        // Query page records
                        IEnumerable records     = queryRecords.Invoke(hub, queryRecordsParameters.ToArray()) as IEnumerable ?? Enumerable.Empty <object>();
                        int         exportCount = 0;

                        // Export page records
                        foreach (object record in records)
                        {
                            // Periodically check for client cancellation
                            if (exportCount++ % (PageSize / 4) == 0 && isCancelled())
                            {
                                break;
                            }

                            await writer.WriteLineAsync(string.Join(",", fieldNames.Select(fieldName => $"\"{table.GetFieldValue(record, fieldName)}\"")));
                        }

                        await flushAsync();
                    }
                }
        }