Categories
.NET

Implementación de un pequeño Servidor Web

El siguiente código, originalmente publicado por Eric Carter, muestra la implementación de un pequeño "Servidor Web" con C#, para hacerlo funcionar necesitarán del .NET Framework 2.0 (podría correr en versiones anteriores haciendo ligeras modificaciones al código).

csharp:

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using System.Collections.Specialized;

namespace http
{
    public class FakeWebServer
    {
        private const string URL_REPLACE = "{URL}";
        private static readonly Regex urlRegex = new Regex(@"^(GET|POST) /(.*?) (HTTP[^\s]+)",
                                        RegexOptions.Compiled | RegexOptions.IgnoreCase);

        // Almacena las URLs verdaderas y falsas
        public static readonly Dictionary<string, string> FakeUrls;

        static FakeWebServer()
        {
            FakeUrls = new Dictionary<string, string>();
        }

        private TcpListener listener;
        string contents;

        public FakeWebServer(int port, string responseContents)
        {
            this.contents = responseContents;

            // 'Escuchar' en cualquier dirección
            listener = new TcpListener(IPAddress.Any, port);
            listener.Start();

            Thread t = new Thread(delegate()
            {
                AcceptClients();
            });
            t.Start();
        }
        public void AcceptClients()
        {
            while (true)
            {
                using (TcpClient client = listener.AcceptTcpClient())
                {
                    if (client.Connected) // Nuevo cliente
                    {
                        // Leer los datos enviados
                        NetworkStream stream = client.GetStream();
                        byte[] data = new byte[1024];

                        stream.Read(data, 0, data.Length);

                        string request = Encoding.UTF8.GetString(data);

                        // Sólo tomar en cuenta los datos presentes en el QueryString

                        // Obtener la versión del protocolo y la URL del 'Request'
                        MatchCollection matches = urlRegex.Matches(request);

                        string qs = matches[0].Groups[2].Value.TrimStart('?');
                        NameValueCollection paramArray = HttpUtility.ParseQueryString(qs);

                        foreach (string key in paramArray.AllKeys)
                        {
                            if (FakeUrls.TryGetValue(paramArray[key], out qs))
                                break;
                        }                       
                       
                        System.Diagnostics.Debug.WriteLine("Query String: " + matches[0].Groups[2].Value);

                        // Reemplazar las URLs
                        if (!string.IsNullOrEmpty(qs) && !string.IsNullOrEmpty(contents))
                            contents = contents.Replace(URL_REPLACE, qs);

                        // Enviar las cabeceras necesarias y el contenido
                        SendHeaders(matches[0].Groups[3].Value, null, contents.Length, "200 OK", client);
                        SendToBrowser(Encoding.UTF8.GetBytes(contents), client);
                    }
                }
            }
        }

        public void SendHeaders(string httpVersion, string mimeHeader, int totalBytes, string statusCode, TcpClient tcpClient)
        {
            StringBuilder responseBuilder = new StringBuilder();

            if (string.IsNullOrEmpty(mimeHeader))
                mimeHeader = "text/html";

            responseBuilder.Append(httpVersion);
            responseBuilder.Append(' ');
            responseBuilder.AppendLine(statusCode);
            responseBuilder.AppendLine("Server: Fake Web Server");
            responseBuilder.Append("Content-Type: ");
            responseBuilder.AppendLine(mimeHeader);
            responseBuilder.AppendLine("Accept-Ranges: bytes");
            responseBuilder.Append("Content-Length: ");
            responseBuilder.AppendLine(totalBytes.ToString());
            responseBuilder.AppendLine("");

            Byte[] bSendData = Encoding.UTF8.GetBytes(responseBuilder.ToString());
            SendToBrowser(bSendData, tcpClient);

            System.Diagnostics.Debug.WriteLine("Total Bytes : " + totalBytes.ToString());
        }

        public void SendToBrowser(Byte[] data, TcpClient tcpClient)
        {
            if (tcpClient.Connected)
            {
                NetworkStream stream = tcpClient.GetStream();

                stream.Write(data, 0, data.Length);
                stream.Flush();
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("Connection Dropped....");
            }

        }
    }
}

Como habrán podido observar el "Servidor Web", entrega tontamente casi el mismo contenido, sólo reemplaza cada aparición de {URL} en la respuesta en base a los parámetros solicitados.

En una siguiente entrada explicaré la valiosa ayuda que presta ese pedazo de código, en la explotación de otro bug de una aplicación ya algo conocida por este blog.

Categories
.NET Expresiones Regulares Varios

Rendimiento de expresiones regulares

Luego de leer los artículos publicados por Manuel (a.k.a melkorcete), me parece que se olvidó comentar que no siempre es mejor usar Expresiones Regulares.

A continuación, un ejemplo -trivial- que pone en mayúsculas las letras que son precedidas por espacios en blanco. La diferencia en rendimiento, se nota más cuando el texto es más grande.

csharp:

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics;

class Program
{
    delegate string DoCapitalize(string str);
    static Stopwatch clock = new Stopwatch();
    static Dictionary<string, double> stats = new Dictionary<string,double>();

    static void Run(string str, DoCapitalize method)
    {
        clock.Stop();
        clock.Reset();

        clock.Start();
        method(str);
        clock.Stop();

        if (stats.ContainsKey(method.Method.Name))
            stats[method.Method.Name] += clock.ElapsedTicks;
        else
            stats[method.Method.Name] = clock.ElapsedTicks;
    }
    static void Main(string[] args)
    {
        string str = @"Cada año y medio los computadores doblan su velocidad,
entonces sale otra versión de Windows, que los ralentiza a su velocidad original"
;       

        DoCapitalize regex = new DoCapitalize(RegexCapitalize);
        DoCapitalize normal = new DoCapitalize(Capitalize);
        Console.Read();
        int n = 1000;
        for (int i = 0; i < n; i++)
        {
            Run(str, regex);
            Run(str, normal);
        }
        Console.WriteLine("\tStats for {0} iterations\n", n);
        foreach(KeyValuePair<string, double> item in stats)
        {
            Console.WriteLine("{0} --> Tiempo: {1:.##}", item.Key, item.Value / n);
        }
    }
    static string RegexCapitalize(string str)
    {
        return Regex.Replace(str, @"\s[a-z]", new MatchEvaluator(ToUpper)); // @"\s."
    }
    static string ToUpper(Match m)
    {
        return m.Value.ToUpper();
    }
    static string Capitalize(string str)
    {       
        StringBuilder sb=new StringBuilder();
        sb.Append(str[0].ToString().ToUpper());
        for (int i = 1; i < str.Length; i++)
        {
            char c = str[i];
           
            if (char.IsWhiteSpace(str[i-1]) && char.IsLower(c))
                c = char.ToUpper(c);
            sb.Append(c);
        }
        return sb.ToString();
    }
}

Cabe destacar que implementar nuestros propios métodos, puede tener consecuencias no deseadas, como el tiempo necesario para implementar un algoritmo alternativo y más importante, la eficiencia del mismo.

Categories
ASP.NET

Atlas Control Toolkit, ¿javascript fácil = javascript pesado?

Scott Guthrie, acaba de anunciar la disponibilidad de un nuevo CTP de Atlas Control Toolkit.

The "Atlas" Control Toolkit contains controls and templates that make adding client-side functionality to your site incredibly easy.

A pesar de que algunos están emocionados y nos muestran ejemplos de uso, IMHO, creo que varios de estos controles son prácticamente inútiles en comparación a lo que se puede hacer con otras librerías muchísimo más pequeñas... ya que la supuesta facilidad de uso de estos controles, trae como consecuencia que el navegador del cliente descargue una cantidad considerable de javascript que no usará, esto último debido a la falta de modularización de Atlas ASP.NET AJAX.

Finalmente, creo que es decisión de cada uno, determinar el escenario adecuado para el uso de estos controles u otros frameworks javascript.

Categories
.NET Varios

Videos de Lang.NET Symposium

Michael Lehman anuncia que ya están disponibles para descargar los videos de Lang.NET Symposium -un evento que convoca a investigadores relacionados con temas de lenguajes de programación y compiladores.

La lista completa de videos está disponible en http://www.langnetsymposium.com/speakers.asp

Categories
ASP.NET

CSS Friendly ASP.NET 2.0 Control Adapters

Como ya había comentando en un post anterior, hacer que el motor de ASP.NET 1.x genere código (X)HTML relativamente correcto para todos los navegadores, es una tarea bastante trabajosa, porque los controles estándar no generan código muy limpio que digamos, esto empeora si usas controles de terceros -sin código fuente- que sólo funcionan bien en Internet Explorer, si bien es cierto que estos problemas se mitigan con algúnas técnicas (configurando algunas cosas en el web.config, escribiendo tus propios controles e inclusive usando módulos http para corregir el HTML generado), es un poco frustrante, cuando existen casos en los que no puedes hacer nada!

Felizmente, muchos de estos problemas han sido solucionados en ASP.NET 2, aunque todavía hay varios controles estándar y de terceros que generan código semánticamente incorrecto (Ejm Menu, FormView, DetailsView, etc), ahora existe la posibilidad de modificar la salida de estos controles sin necesidad de tener el código fuente.

El título del post hace referencia a un proyecto que muestra la forma de implementar los denominados Control Adapters, característica que comenté en el párrafo anterior. La beta 1 de este proyecto fue publicada en Abril de 2006, este mes se ha publicado la Beta 2 con bastantes mejoras y correcciones de fallas, además de traer ejemplos nuevos ejemplos para controles como GridView, Login, CreateUserWizard, etc.

Sin duda, ahora los desarrolladores que trabajen con asp.net, tienen más facilidad para producir páginas con XHTML correcto y válido... aunque también hay que reconocer, que a muchos no les importan este tipo de cosas!