|
 Saturday, October 06, 2007

A Sandbox/Test Server Is a Sandbox For A Reason

That's right. Read it again. sandbox

"A Sandbox Server Is a Sandbox For A Reason." Now, read it again, and again, and again. MEMORIZE IT.

What is a Test/Sandbox Environment?

A test/sandbox environment is a testing playground for the developer/user (in the context of this conversation, its developer). Its an exact copy of the production environment, but does not affect anything in production. It allows the developer to test code before it goes into production. This allows the developer to find errors before they actually cost the company any money. Test/Sandbox environments are an integral part of software development and service providers. They make sure that the quality of the product released is top notch (of course this is assuming proper testing is taking place).

This is nothing new, its not rocket science. Everyone tests before they ship a product (well, anyone with a brain does at least).

When you're working with a service provider you expect them to have a test/sandbox environment that matches there production environment. Tons of service providers have these test servers, here's a few:

  • Google AdWords Preview Tool - Lets you preview searches using Google so you can preview your ad before it goes live.
    • Benefit - You don't get penalized for impressions or clicks. No money is spend, no money is transferred.
  • Google Checkout Test Server - Allows you to post test transactions.
    • Benefits: You can test without being charged or have actual money transferred.
  • PayPal SandBox - Lets you test your Pay Pal transactions.
    • Benefits - You can test without being charged or have actual money transferred.
  • Amazon Mechanical Turk SandBox - Allows the users to test their applications and HITS.
    • Benefits - You don't loose money nor put up a HIT that could be worked on. (You pay for HITS). No money is transferred.
  • Amazon Flexible Payments Service (FPS) - Allows you to test the payment systems you set up.
    • Benefit - Money is not actually transferred.
  • SalesForce Sandbox - Allows developers to post test transactions to the Salesforce Service.
    • Benefits - Eliminates costly development mistakes. Helps save on testing, therefore eliminating money being transferred for a development task.
  • Virtual Machines (Virtual PC, VMWare) - The big daddy of them all. Test your entire environment before you put it into production.
    • Benefits - Eliminate HUGE costs associated with lack of testing/deployment scenarios.

 

These sandbox environments share a common goal - THE GOAL OF ELIMINATING COSTLY MISTAKES

How? Simple. If developers are given a test environment, they can test their code without worrying about messing up production environments. If these services involve money, its VERY IMPORTANT that the said service has a test environment.

A real life story...

Recently I was working on a side project that involved a service provider (none of the listed above) and I found their test server to be quite useful. I could post transactions to it, and do all the testing I needed to. A few weeks later the production site changed, yet the test server remained the same. Not only is this irresponsible as a service provider, its a risk that the service provider puts on themselves. They have now put themselves in a position where the service might now work, and some users might abandon the product. Upon asking the service provider when the test environment would be updated to mimic the production environment I got this response (edited to make sense in this context):

"There's really no point in having an exact copy of production since it's only really designed for practicing entering form data. Our live environment changes daily (along with other criteria) and, other than changes to form validation or form fields, these changes are never reflected in the test environment (which is also why there are some different types in the test environment that don't exist in the live environment)."

dangerHuh? What!?! Lets go over a snippet of this... the first part really makes my jaw drop.

"There's really no point in having an exact copy of production since it's only really designed for practicing entering form data."

Whoa, DANGER, WRONG WAY, TURN BACK, for real... for real. Ok, well, the last time I checked when you have a test environment - its a test/sandbox environment for a reason: It mimics the production environment to allow the developer to perform tests without affecting anything in production. So why is there no point? If its not the exact same thing, then how am I to be sure its going to work the same. That's right, I WONT KNOW.

Unfortunately this service provider has not updated their test server to this day. It's a service that I need to use, but I will openly admit, I think its a bad business decision on their end. I've had to perform all kinds of trickery to validate my code does what is supposed to do, but still, I'm only 65% sure its right. Which sucks, no test server, no way to test everything.

This is a lesson that all service providers and developers should note...

A broken test environment is like having no test environment at all.

Just imagine, if you can't compile your code, you can't ship your code. So, if you cant test your code properly, how can you be sure its doing the right thing? You cant.

Service providers: Make sure you  have a valid test environment that mimics production, EXACTLY.

Developers: Demand a test environment. Ruthlessly test your code. Unit test to hell. Functional test your code like hell.

#    Comments [0] |
 Monday, September 17, 2007

Watermarking Images in ASP.NET with an HttpHandler

This will be a first of a couple posts about this ImageHandler and ways it can be used.

At times I've worked for different very creative and artistic companies (here, here, here and here) and during those times I have wanted to watermark an image for one reason or another. Either the image was protected and was not supposed to be redistributed or the client just wanted to make sure their URL was present on the image. The reasons are plentiful.

The image(s) I'm using in this project is the logo for the company I work for.

You can download the handler and a sample web site to run this handler in at the bottom of this post.

How To

I've written a quick example of how to watermark all images (jpg, png, gif or bmp) on a web site. 

It uses a simple HttpHandler that you can drop into the App_Code folder of your ASP.NET Project. With a quick addition to your web.config file you start using this.

Video

To understand what I'm saying, watch this screen cast (this screen cast has no sound):

ImageHandlerScreenCast

Now lets look at some code...

Code

(web.Config)

<?xml version="1.0"?>
<configuration>
    <system.web>
        <httpHandlers>
            <add verb="GET" type="ImageHandler" path="*.jpg,*.png,*.gif,*.bmp"/>
        </httpHandlers>
        <compilation debug="true"/></system.web>
</configuration>

ImageHandler.cs

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Net.Mime;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

/// <summary>
/// Summary description for ImageHandler
/// </summary>
public class ImageHandler : IHttpHandler
{
    public ImageHandler()
    {
    }

    public string GetContentType(String path)
    {
        switch (Path.GetExtension(path))
        {
            case ".bmp": return "Image/bmp";
            case ".gif": return "Image/gif";
            case ".jpg": return "Image/jpeg";
            case ".png": return "Image/png";
            default: break;
        }
        return String.Empty; 
    }

    public ImageFormat GetImageFormat(String path)
    {
        switch (Path.GetExtension(path).ToLower())
        {
            case ".bmp": return ImageFormat.Bmp;
            case ".gif": return ImageFormat.Gif;
            case ".jpg": return ImageFormat.Jpeg;
            case ".png": return ImageFormat.Png;
            default: return null;
        }
    }
    
    protected byte[] WatermarkImage(HttpContext context)
    {

        byte[] imageBytes = null;
        if (File.Exists(context.Request.PhysicalPath))
        {
            // Normally you'd put this in a config file somewhere.
            string watermark = "John Doe - © EXAMPLE Company 2007";

            Image image = Image.FromFile(context.Request.PhysicalPath);

            Graphics graphic;
            if (image.PixelFormat != PixelFormat.Indexed && 
image.PixelFormat != PixelFormat.Format8bppIndexed && 
image.PixelFormat != PixelFormat.Format4bppIndexed && 
image.PixelFormat != PixelFormat.Format1bppIndexed)
            {
                // Graphic is not a Indexed (GIF) image
                graphic = Graphics.FromImage(image);
            }
            else
            {
                /* Cannot create a graphics object from an indexed (GIF) image. 
                 * So we're going to copy the image into a new bitmap so 
                 * we can work with it. */
                Bitmap indexedImage = new Bitmap(image);
                graphic = Graphics.FromImage(indexedImage);

                // Draw the contents of the original bitmap onto the new bitmap. 
                graphic.DrawImage(image, 0, 0, image.Width, image.Height);
                image = indexedImage;
            }
            graphic.SmoothingMode = SmoothingMode.AntiAlias & SmoothingMode.HighQuality;

            Font myFont = new Font("Arial", 15);
            SolidBrush brush = new SolidBrush(Color.FromArgb(80, Color.White));

            /* This gets the size of the graphic so we can determine 
             * the loop counts and placement of the watermarked text. */
            SizeF textSize = graphic.MeasureString(watermark, myFont);

            // Write the text across the image. 
            for (int y = 0; y < image.Height; y++)
            {
                for (int x = 0; x < image.Width; x++)
                {
                    PointF pointF = new PointF(x, y);
                    graphic.DrawString(watermark, myFont, brush, pointF);
                    x += Convert.ToInt32(textSize.Width);
                }
                y += Convert.ToInt32(textSize.Height);
            }


            using (MemoryStream memoryStream = new MemoryStream())
            {
                image.Save(memoryStream, GetImageFormat(context.Request.PhysicalPath));
                imageBytes = memoryStream.ToArray();
            }

        }
        return imageBytes;
    }

    #region IHttpHandler Members

    public bool IsReusable
    {
        get { return false; }
    }

    public void ProcessRequest(HttpContext context)
    {
        context.Response.Clear();
        context.Response.ContentType = GetContentType(context.Request.PhysicalPath);
        byte[] imageBytes = WatermarkImage(context);
        if (imageBytes != null)
        {
            context.Response.OutputStream.Write(imageBytes, 0, imageBytes.Length);
        }
        else
        {
            // No bytes = no image which equals NO FILE. :)  
            // Therefore send a 404 - not found response. 
            context.Response.StatusCode = 404;
        }
        context.Response.End();
    }

    #endregion
}

Explanation

With the configuration above, we will capture all jpg, png, gif and bmp images as they are requested from the HttpRequest object.

As we encounter the image, we find it, then create an image and graphics object from it and then loop through the images height/width ratio and write some text on top of the image.

A key note about watermarking. You can simply write some text over the screen but then the image behind it would not be visible. You should ensure that the font you're overlaying is transparent therefore the image shows through the font that is on top. This is done by using the Color.FromArgpb method, which is in this piece of code:

Color.FromArgb(80, Color.White)

The "80" is a alpha value. Valid values are 0-255. If you're at 0, the color is invisible. If you're at 255, the color is solid. Here, I'm using 80. You can see the text, but you can see through it. Play with this number to see what works best for you.

Conclusion

Its easy to watermark images in ASP.NET. In future posts I'll show you how to protect certain directories images as well as how to do some other cool stuff with it like denying access (anti-leech and leech re-routing).

Download ImageHandler.zip (1.57 KB)  (This is just the cs file)

Download Example ImageHandlerDemoSite.zip (84.7 KB) (Demo site for demonstration)

#    Comments [15] |
 Friday, September 14, 2007

Belkin is Gangster

A fun little tidbit for today...

nwa I can't believe I didn't notice this before, but... BELKIN IS GANGSTER.

They're Corporate Headquarters is located in Compton CA. That, my friends, is gangster in my book. 

 

 

 

 

Don't believe me? Check out this screen shot form their web site:

BelkinHqScreenshot

#    Comments [0] |
 Thursday, September 13, 2007

Automated Form Posting with .NET / ASP.NET's CURL

At times you'll encounter an issue where a client, customer or merchant does not provide an endpoint to an application you're required to use. Basically, the application you're working with does not have a web service, nor do you have access to connect to the database directly but you HAVE TO get data into the system and the only thing available is a web form on a remote server. 

In PHP, you can utilize CURL to communicate with servers and post forms through code.  In .NET, we have the System.Net namespace which contains a ton of little goodies. It is ASP.NET's implementation that relates to PHP's CURL library. The MSDN describes this namespace perfectly:

The System.Net namespace provides a simple programming interface for many of the protocols used on networks today.

I've used this namespace many times and I have actually used the System.Net.WebClient class in my C# Google Geocode Class. Which brings me to...

Purpose of the blog entry...

In this example I'm going to show you how to use the HttpWebRequest class to post form data to a website to send a text message to your phone (utilizing the website as the service). I've already talked about how to send a text message to your phone from .NET, but this time I want to do it using an online service to demonstrate the use of these two classes.

Note: You need to be aware of how HTTP requests work. You need to know that there are headers being sent, Cookies, Content Types, etc. Without a basic grasp on the the request/response model concepts  you'll be left scratching your head wondering what the hell is going on behind the scenes.

Getting Started

We first need to identify what headers are being sent. I use Fiddler to do this. Fiddler allows you to view the HTTP request/responses down to a very fine grain of detail.

Then, I'll go to the web page where I'm trying to make the request and then I'll submit the form I'm working with. Once the form submission is complete, I'll look at the headers that are being sent (if any at all). In this case we have some because the site we're working with is PHP based, while we're working with .NET. INTEGRATION BABY!!

Here is a screen shot of Fiddler and the items I'm look at. (click to get a bigger shot)

Fiddler1

I'm looking for the HTTP Headers that are sent across the wire. I captured those in the red-highlighted area on the screen shot above. Now that I know what URL I need to send to (newsend.php - look on the left hand side of the screen shot). I can then set up my HttpWebRequest object and prepare to send it off.

Enough Jibba-Jabba, Lets see some code!

Please note, this is a Console App that was written to do this. You'd normally separate these concerns out into layers. Its a example app, what more did you want? :) (The entire example app can be downloaded at the bottom of this post).

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;

namespace HttpRequestExample
{
    class Program
    {
        static void Main(string[] args)
        {
            SendTextMessage();
            Console.WriteLine("Press [ENTER] to quit"); 
            Console.ReadLine(); 
        }

        /// <summary>
        /// Sends a Text Message
        /// </summary>
        private static void SendTextMessage()
        {
            string phoneNumber = "YourPhoneNumber"; // Put your phone number here, player.
            string provider = "tmobile"; // Possible Values: ... (see download for full comment) 
            string message = "Test from example application! DateTime:" + DateTime.Now.ToString();
            string headerVars = String.Format(
                "refer=txt2day&to={0}&from=Your+Email+%28optional%29&provider={1}&message={2}&submit.x=90&submit.y=50", 
                phoneNumber, provider, message); 

            
            HttpWebRequest textMessageRequest =  (HttpWebRequest)WebRequest.Create("http://www.txt2day.com/newsend.php");
            textMessageRequest.Method = "POST";
            textMessageRequest.ContentType = "application/x-www-form-urlencoded";
            textMessageRequest.ContentLength = headerVars.Length;
            AddPostVarsToHeader(textMessageRequest, headerVars);
            
            string responseString;
            using (StreamReader streamReader = new StreamReader(textMessageRequest.GetResponse().GetResponseStream()))
            {
                // The response string has the HTML response (if its HTML)
                responseString = streamReader.ReadToEnd();
                
                /* At this point the Txt Message should be sent. 
                 * Scrape the response to check for a thank you message.
                 * On the Txt2Day.com site, the thank you message looks like this:
                 * Thanks! Your Message Has Been Sent
                 */ 
                Console.WriteLine("Message Sent: {0}", responseString.Contains("Thanks"));
                Console.WriteLine("Note: This message can take a few minutes (to a couple hours from my testing) to arrive."); 
            }
        }


        /// <summary>
        /// Adds the <paramref name="values"/> to the the http headers. 
        /// </summary>
        /// <param name="request">The HTTP Request</param>
        /// <param name="values">The values to add to the headers</param>
        private static void AddPostVarsToHeader(WebRequest request, string values)
        {
            using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream(), ASCIIEncoding.ASCII))
            {
                streamWriter.Write(values);
                streamWriter.Close();
            }
        }
    }
}

Code Explanation

Setting Up The HttpWebRequest Object

            string phoneNumber = "YourPhoneNumber"; // Put your phone number here, player.
            string provider = "tmobile"; // Possible Values: ... (see download for full comment) 
            string message = "Test from example application! DateTime:" + DateTime.Now.ToString();
            string headerVars = String.Format(
                "refer=txt2day&to={0}&from=Your+Email+%28optional%29&provider={1}&message={2}&submit.x=90&submit.y=50", 
                phoneNumber, provider, message); 

            
            HttpWebRequest textMessageRequest =  (HttpWebRequest)WebRequest.Create("http://www.txt2day.com/newsend.php");
            textMessageRequest.Method = "POST";
            textMessageRequest.ContentType = "application/x-www-form-urlencoded";
            textMessageRequest.ContentLength = headerVars.Length;

The HttpWebRequest object uses the static Create method of its Abstract Base Class, WebRequest.

The  "phoneNumber" variable is the phone number you want to text.

The "provider" variable is the name of the provider. The attached solution has the full list of available providers (there are a ton of them in there).

The variable "headerVars" is actually what is posted to the server when the submit button is clicked. If you look at the format of the string you'll see "to" and "from" and "provider" and "message". If you look at the source of the website, you'll notice that the form elements share thsoe names. That's not a coincidence. These are the actual POST vars that are posted to the server. In this portion of the code we are rebuilding the Headers.

Since we are sending headers we need to specify the length, which is done by setting the ContentLength of the HttpWebRequest.

Next, we put the values into the request object itself by calling:

AddPostVarsToHeader(textMessageRequest, headerVars);

Which is defined as:

/// <summary>
/// Adds the <paramref name="values"/> to the the http headers. 
/// </summary>
/// <param name="request">The HTTP Request</param>
/// <param name="values">The values to add to the headers</param>
private static void AddPostVarsToHeader(WebRequest request, string values)
{
    using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream(), ASCIIEncoding.ASCII))
    {
       streamWriter.Write(values);
       streamWriter.Close();
    }
}

Here, we write the values into the request stream itself. Now we are ready to send it off and get a request. Here's how we do that:

             string responseString;
            using (StreamReader streamReader = new StreamReader(textMessageRequest.GetResponse().GetResponseStream()))
            {
                // The response string has the HTML response (if its HTML)
                responseString = streamReader.ReadToEnd();
                
                /* At this point the Txt Message should be sent. 
                 * Scrape the response to check for a thank you message.
                 * On the Txt2Day.com site, the thank you message looks like this:
                 * Thanks! Your Message Has Been Sent
                 */ 
                Console.WriteLine("Message Sent: {0}", responseString.Contains("Thanks"));
                Console.WriteLine("Note: This message can take a few minutes (to a couple hours from my testing) to arrive."); 
            }

There are many ways to obtain the Response from the Request, but at this point I just want to take the Response Stream and write it to the string. What this is doing is sending a web request to the page we defined (newsend.php) with our form variables placed into the headers, and then after the server processes it, it will return a response, in which we capture it from the response stream.

This stream is actually the HTML that is returned from the "thanks.php" page (step through the code in the debugger and look at the HttpWewbRequest.GetResponse() method. It returns a WebResponse which has all kinds of good info about the response.

With the HTML that is returned we can scrape it to see if the word "Thanks!" is included, which signifies that a message was sent. (On the website, when you get to the thanks.php page after manually submitting a message the web site displays: "Thanks! Your message has been sent". Or something very similar. I know that the message does contain the "Thanks" word though. :)

*Note: This service on the website of sending SMS/Text Messages is not SUPER reliable. I was doing some testing and found that sometimes I'd send a message and I'd get it within 5 seconds, other times it was 5-6 hours  before I got my text message on my phone.

At this point you've connected to a web site through code, posted a request that contains variables for the server, and had it perform an action. You then received the page and scraped it to find the overall result.

Download

HttpRequestExample.zip (4 KB)

#    Comments [0] |
 Tuesday, September 11, 2007

Google AdWords & Discover Card

GoogleAdwordsLogo This last weekend I was setting up a Google AdWords campaign and I was blown away when I got to the payment screen. Google AdWords DOE S NOT support Discover Card as a payment method.

Yeah, for real. WHAT?!?!?! One of the largest web sites in the world does not discover_logo_gray accept Discover Card?!?! I can go to Walgreens, Cold Stone Creamery, and even Rubios and they all still accept Discover Card. But Google AdWords does not?

I'm not happy about this and I think its kind of ridiculous. Google has their own Checkout Service which allows users use a Discover Card for payment, but you cannot use it for AdWords? Oh COME ON NOW. I'm sure there is some good reason, but I even emailed support and here's their response:

Hello Donn,

Thank you for your email. I understand you would like to make payments towards your AdWords account with your Discover card. Please note that available payment options depend on your location and currency.


At this time, Google only accepts the forms of payment listed at
https://adwords.google.com/select/AfpoFinder.

We're continually working on expanding our payment options to better serve our advertisers' needs. We apologize for any inconvenience.

If you have additional questions, please visit our Help Center at
https://adwords.google.com/support to find answers to many frequently asked questions. Or, try our Learning Center at
http://www.google.com/adwords/learningcenter/ for self-paced lessons that cover the scope of AdWords.

We look forward to providing you with the most effective advertising available.


Sincerely,

The Google AdWords Team

 

So I go visit their forms of payment area and there is nothing in regards to Discover Card.

Normally this wouldn't upset me, but I have certain benefits that I receive when I use certain cards. So using this card delivers benefits that I'm trying to utilize. Anyway, I'll have to use a Visa or Mastercard for now.

But I'm still not happy about it - this is definitely WEAK SAUCE in my book.

#    Comments [0] |