by Israel Aece
31. March 2009 14:36
Um dos passos necessários durante a carga do Assembly é análise da assinatura do Strong Name. Independentemente do Code Access Security, essa análise sempre é realizada e muitas aplicações pagam esse preço sem ao menos utilizá-lo. A partir do SP1 do .NET Framework 3.5, a Microsoft incluiu uma funcionalidade chamada de Strong Name Bypass, não mais fazendo essa verificação e, consequentemente, tendo um ganho de performance durante a sua inicialização.
Essa análise não será mais realizada para Assemblies que são carregados em um ambiente full-trusted. Obviamente que essa análise acontecerá quando, para computar a segurança, voce levar em consideração o Strong Name. Se, por algum motivo, voce quiser reabilitar a verificação, então pode recorrer ao elemento bypassTrustedAppStrongNames, definindo o atributo enabled para false, assim como é mostrado abaixo, utilizando o arquivo de configuração:
<configuration>
<runtime>
<bypassTrustedAppStrongNames enabled="false"/>
</runtime>
</configuration>
by Israel Aece
24. March 2009 14:38
Além de algumas novidades que já foram reportadas neste post, o ASP.NET 4.0 trará ainda uma série de funcionalidades muito interessantes e, entre elas, teremos:
OutputCaching Extensível
Quando utilizamos o OutputCaching em uma página, a primeira requisição a executará, e antes de devolver o conteúdo para o cliente, o resultado é armazenado na memória do servidor Web, e com isso, futuras requisições utilizarão esse conteúdo ao invés de reprocessar a página completamente. O ASP.NET 4.0 fornece uma forma altamente flexível para conseguirmos customizar o repositório de caching trabalhando com providers, assim como já acontece nas principais funcionalidades do ASP.NET atual.
Auto-Start de Aplicações
Todos sabemos que as aplicações ASP.NET tem um delay na primeira requisição, que se deve ao fato de efetuar a compilação da mesma. A partir do ASP.NET 4.0, em conjunto com o IIS 7.5, teremos uma funcionalidade chamada de auto-start. Ao configurar esta funcionalidade em uma aplicação, o IIS enviará uma mensagem ao ASP.NET para que a sua aplicação faça o processo necessário para deixá-la pronta para ser consumida. Tudo isso ocorre no momento em que o IIS/AppPool ainda não aceita requisições.
O mais interessante é que ainda podemos interceptar este evento, implementando o método Preload da Interface IProcessHostPreloadClient. Nele podemos fazer outras "pré-inicializações" que antes eram feitas sob demanda.
Redirecionamento Permanente
Como já comentado aqui, agora teremos um método chamado RedirectPermanent que efetuará esse tipo de redirecionamento.
Compressão das variáveis de Sessão
Quando utilizamos variáveis de sessão, elas são armazenadas na memória do servidor onde a aplicação está sendo executada (In-Process). Além disso, temos duas outras possibilidades que é o armazenamento diretamente no SQL Server ou um servidor exclusivo para o armazenamento das variáveis sessão (Out-of-Process). Nestas duas últimas opções, há um overhead extra, que ocorre justamente porque o objeto responsável que armazena as informações, deverá ser serializado antes de enviado para o destino remoto.
Dependendo do que é armazenado dentro das variáveis de sessão, esse processo pode custar muito caro, tanto em termos de comunicação quanto em termos de memória ou espaço físico. A partir do ASP.NET 4.0, o elemento sessionState irá possuir um novo atributo chamado compressionEnabled. Ao definí-lo como True, o ASP.NET fará o processo necessário para comprimir e descomprimir os dados, utilizando a classe GZipStream.
Para aqueles que quiserem mais informações sobre as novas funcionalidades, consulte este documento.
by Israel Aece
24. March 2009 11:59
Quando expomos um serviço através do binding NetTcpBinding, as requisições que chegam até ele, são processadas e, finalmente o retorno é enviado para o respectivo cliente. As mensagens que chegam são processadas no formato FIFO (first-in, first out), ou seja, elas aguardam o processamento em uma fila e, sob demanda, são encaminhadas para o processamento.
Se a velocidade do processamento da mensagem é mais lenta que a velocidade de envio das mensagens, essa fila pode aumentar. Por padrão o binding NetTcpBinding limita, através da propriedade ListenBacklog, o número de requisições pendentes que podem ser enfileiradas. O valor padrão é 10 e, dependendo da quantidade de requisições que chegam para o serviço, esse limite pode exceder e, a seguinte exceção será disparada:
System.ServiceModel.EndpointNotFoundException: Could not connect to net.tcp://localhost:8879/srv. The connection attempt lasted for a time span of 00:00:02.1404880. TCP error code 10061: No connection could be made because the target machine actively refused it 127.0.0.1:8879. ---> System.Net.Sockets.SocketException:No connection could be made because the target machine actively refused it 127.0.0.1:8879
A alternativa aqui é aumentar esse número, definindo um valor próximo a quantidade das conexões simultaneas que eventualmente podem chegar para o serviço. O exemplo abaixo ilustra a configuração desta propriedade no binding NetTcpBinding:
host.AddServiceEndpoint(typeof(IContrato), new NetTcpBinding() { ListenBacklog = 70 }, "srv");
by Israel Aece
17. March 2009 12:01
Quando queremos expor uma propriedade, na maioria das vezes existe um campo privado que serve como "repositório" para a informação que é definida ou lida. A propriedade tem o papel de interceptar a leitura e escrita à este campo privado através de blocos get/set.
Para reduzir o trabalho, a partir da versão 3.0 do C#, é disponibilizado uma funcionalidade chamada de Automatic Properties que, por sua vez, nos permite omitir a criação deste campo privado. Na realidade, essa funcionalidade nada mais é do que "açúcar sintático", já que no momento da compilação, o C# irá injetar o campo privado para armazenar a informação que a propriedade expõe. Como exemplo, imagine que voce cria uma classe com a seguinte definição:
public class Usuario
{
public string Nome { get; set; }
}
No momento da compilação, ela será definida como:
public class Usuario
{
[CompilerGenerated]
private string <Nome>k__BackingField;
public string Nome
{
[CompilerGenerated]
get
{
return this.<Nome>k__BackingField;
}
[CompilerGenerated]
set
{
this.<Nome>k__BackingField = value;
}
}
}
Já vi alguns casos em que esses objetos que fazem uso desta técnica, são marcados com o atributo SerializableAttribute e, como o próprio atributo indica, permitir a serialização do mesmo. Quando um objeto com essas características são expostos através de serviços WCF (talvez quando voce já possuir uma API com essas classes definidas), precisamos de alguns cuidados especiais.
O serializador padrão do WCF é o DataContractSerializer. Esse serializador trabalha em conjunto com os atributos DataContractAttribute e DataMemberAttribute, serializando as propriedades ou campos públicos que são decorados com um desses dois atributos. Esse serializador também suporta classes decoradas com o atributo SerializableAttribute mas, como esse atributo foi desenhado para persistir e reconstituir na íntegra a representação do objeto, todos os campos (privados ou públicos) são serializados. As propriedades aqui são ignoradas.
Ao submeter um objeto com as características que vimos acima para o serializador padrão do WCF, ele persistirá os campos privados, que no nosso caso é o <Nome>k__BackingField, e que na maioria dos casos, não é isso que desejamos. Como exemplo, podemos utilizar o seguinte código para analisar o resultado deste processo:
new DataContractSerializer(typeof(Usuario))
.WriteObject(
File.Create("Usuario.xml"),
new Usuario() { Nome = "Israel" });
Abaixo temos conteúdo Xml correspondente:
<Usuario xmlns="..." xmlns:i="...">
<_x003C_Nome_x003E_k__BackingField>Israel</_x003C_Nome_x003E_k__BackingField>
</Usuario>
Caso voce não possa alterar os atributos que já foram definidos para a classe, uma possibilidade que temos é utilizar o serializador XmlSerializer. Ao contrário do DataContractSerializer, esta classe irá serializar somente as propriedades ou campos públicos. Assim como o exemplo acima, para efetuarmos este teste, podemos utilizar o seguinte código:
new XmlSerializer(typeof(Usuario))
.Serialize(
File.Create("Usuario.xml"),
new Usuario() { Nome = "Israel" });
E, finalmente, o resultado em Xml:
<?xml version="1.0"?>
<Usuario xmlns:xsi="..." xmlns:xsd="...">
<Nome>Israel</Nome>
</Usuario>
O XmlSerializer é utilizado também pelo ASP.NET Web Services (ASMX). Por mais que esta técnica funcione, antes de alterar o serializador padrão do WCF, é importante que voce analise as vantagens e desvantagens da sua utilização e, para isso, poderá recorrer à própria documentação.
by Israel Aece
14. March 2009 12:02
Com a instalação de uma atualização que a Microsoft lliberou, o arquivo hosts (C:\Windows\System32\drivers\etc) pode ter sido alterado, removendo a entrada que tem o apontamento do IP 127.0.0.1 para localhost, fazendo com que os projetos ASP.NET que usam o servidor interno do Visual Studio .NET para hospedar as aplicações deixem de funcionar. Com isso, ao rodar essas aplicações, voce receberá a seguinte mensagem: "Internet Explorer cannot display the webpage".
Como já era de se esperar, basta readicionar esta linha no arquivo para que tudo volte ao normal, assim como é mostrado abaixo:
127.0.0.1 localhost
by Israel Aece
13. March 2009 12:03
Ao criar um serviço WCF, podemos disponibilizá-lo através do protocolo TCP (net.tcp), utilizando uma porta específica. Uma vez que voce publica esse serviço, nenhum outro serviço naquela mesma máquina poderá utilizar aquela porta ao mesmo tempo, e se fizer isso, uma exceção do tipo AddressAlreadyInUseException será disparada exibindo a seguinte mensagem: There is already a listener on IP endpoint [MachineName]:[Port]. Make sure that you are not trying to use this endpoint multiple times in your application and that there are no other applications listening on this endpoint.
Podemos não perceber mas o Windows permite o compartilhamento de uma mesma porta TCP para expor múltiplas aplicações. Um exemplo disso é o HTTP, que permite múltiplas aplicações estarem acessíveis através da porta 80 do protocolo TCP. Isso é possível graças à um listener (HTTP.SYS) que o IIS possue e que, baseando-se no conteúdo da mensagem, encaminha a requisição para a aplicação correspondente processá-la.
Ao instalar o .NET Framework, um serviço chamado Net.Tcp Port Sharing Service é instalado no Windows, dando a possibilidade de múltiplos serviços WCF compartilharem a mesma porta TCP, assim como o HTTP. Esse serviço (hospedado através do processo SMSvcHost.exe (C:\Windows\Microsoft.NET\Framework\v3.0\Windows Communication Foundation)) tem a finalidade de inspecionar a mensagem, extrair o endereço de destino, e baseando-se nele, encaminhar a requisição para a aplicação que a processará.
Além deste serviço funcionando, ainda é necessário uma pequena configuração no código que expõe serviço WCF. O binding NetTcpBinding possui uma propriedade booleana chamada PortSharingEnabled que, por padrão, é definida como False. Voce deverá definí-la como True para todos os serviços que desejarem compartilhar uma porta TCP específica. Ao fazer isso, o WCF se encarrega de iniciar automaticamente o serviço Net.Tcp Port Sharing Service, mas voce pode fazer isso manualmente, ou ainda alterar a configuração do serviço para ele ser inciado automaticamente quando o sistema operacional entrar no ar. O trecho de código abaixo ilustra como podemos proceder para configurar o endpoint com o binding e habilitando o compartilhamento da porta TCP:
host.AddServiceEndpoint(
typeof(IContrato),
new NetTcpBinding() { PortSharingEnabled = true },
new Uri("net.tcp://localhost:7373/NomeDoServico/Cadastro"));
Como os serviços irão compartilhar a mesma porta, a distinção da aplicação é determinada pela URI como um todo, ou seja, o que vem depois da porta na URI do serviço precisa ser diferente entre os serviços, caso contrário voce terá o mesmo problema que antes.
Com esta técnica, vamos permitir múltiplas aplicações coexistirem fisicamente na mesma máquina, mas em processos separados, compartilhando a mesma infraestrutura de rede, melhorando a segurança, já que podemos diminiur a superfície de ataque não exigindo a abertura de portas no firewall de forma aleatória.
by Israel Aece
11. March 2009 12:05
Como já sabemos, a classe ThreadPool disponibiliza um repositório de threads que podemos utilizar em nossas aplicações, delegando a ela uma tarefa para ser executada através de um delegate. Para isso, é muito comum utilizarmos o método QueueUserWorkItem para enfileirar uma nova tarefa. O exemplo abaixo ilustra a sua utilização em uma aplicação qualquer:
ThreadPool.QueueUserWorkItem(state =>
{
Console.WriteLine(new StreamReader(@"C:\Temp\Arquivo.txt").ReadToEnd());
});
Quando utilizamos o método QueueUserWorkItem, antes de enfileirar a tarefa a ser executada, ele captura todas as permissões que foram concedidas e, antes de executá-la, essas permissões são aplicadas a thread que está associada à tarefa. Com isso, se a tarefa que voce está tentando executar de forma assíncrona exigir alguma permissão e ela não foi concedida, uma exceção do tipo SecurityException será disparada. Podemos notar esse comportamento com o seguinte código:
new FileIOPermission(FileIOPermissionAccess.Read, @"C:\Temp\Arquivo.txt").Deny();
ThreadPool.QueueUserWorkItem(state =>
{
Console.WriteLine(new StreamReader(@"C:\Temp\Arquivo.txt").ReadToEnd());
});
No código acima, a aplicação não possui permissão de acesso ao arquivo, e ao executar o método QueueUserWorkItem, a tarefa vinculada também não conseguirá executar a leitura do arquivo, disparando uma exceção. Por menor que seja, utilizar este método sempre tem o overhead para a cópia das permissões atuais e associá-las a thread responsável pela tarefa, para que ela execute no mesmo contexto de segurança.
É justamente neste ponto que entra em cena o método UnsafeQueueUserWorkItem, também da classe ThreadPool. Ao contrário do método QueueUserWorkItem, ele não propaga as permissões para a worker thread, diminuindo o trabalho a ser realizado quando voce enfileira uma nova tarefa ao ThreadPool. A utilização em relação ao que vimos acima muda ligeiramente:
new FileIOPermission(FileIOPermissionAccess.Read, @"C:\Temp\Arquivo.txt").Deny();
ThreadPool.UnsafeQueueUserWorkItem(state =>
{
Console.WriteLine(new StreamReader(@"C:\Temp\Arquivo.txt").ReadToEnd());
}, null);
Como as permissões não são propagadas e avaliadas com o método UnsafeQueueUserWorkItem, mesmo que a aplicação não tenha as devidas permissões, a execução da tarefa ocorre sem maiores problemas.
É importante dizer aqui que a performance é melhorada mas, em contrapartida, a segurança fica vulnerável. Utilizar este tipo de código pode abrir portas para algum código malicioso, que pode utilizá-lo para elevar os privilégios de uma aplicação e, consequentemente, executar tarefas que atualmente ela não tem permissão. Opte sempre por utilizar esta técnica em uma tarefa em que voce não dependerá de nenhum recurso do sistema operacional (sistema de arquivos, registry, SQL Server, etc.), utilizando em conjunto com tarefas que dependam exclusivamente do processador, como é o caso de cálculos complexos, geração de algum conteúdo, etc., melhorando consideravelmente a performance na execução, sem abrir potenciais problemas de segurança.
by Israel Aece
10. March 2009 12:06
A partir do .NET Framework 3.5 SP1 temos a possibilidade de suportar a serialização de tipos complexos em serviços WCF, mesmo que eles não estejam decorados com o atributo DataContractAttribute, DataMemberAttribute ou SerializableAttribute, permitindo o suporte à objetos POCO.
Quando trabalhamos nesta linha, não precisamos nos preocupar com os atributos já que, por padrão, todas as propriedades públicas serão serializadas. Já que o comportamento padrão faz com que todas as propriedades serão serializadas, como podemos determinar que uma delas não deve ser disponibilizada para os consumidores do serviço? Neste caso, temos que explicitamente dizer ao runtime do WCF para não serializar tal propriedade e, para isso, recorremos à utilização do atributo IgnoreDataMemberAttribute, contida no namespace System.Runtime.Serialization, aplicando-o nas propriedades que não queremos que esteja disponível, assim como é mostrado abaixo:
public class Usuario
{
public string Nome { get; set; }
[IgnoreDataMember]
public string Password { get; set; }
}