Friday, December 11, 2020

Simple bare bones http server using Kestrel similar to HttpListener

All the examples I found for using the ASP.NET Core Kestrel web server implementation had other features in it. While Visual Studio's templates to get up and running with ASP.NET Core are great and make sense for most developers, I wanted to create a more basic application that just uses the Kestrel server implementation. No MVC, no Razor pages, no IHostBuilder, just a method where you process the http requests, like an HttpListener application.

Surprisingly, I found it difficult to find an example like this on the web, so I created one and put it on github https://github.com/mark-cordell/bare-bones-kestrel-server/

Here's how it works:

The project has <OutputType>Exe</OutputType> just like a normal console application and in Main() it has

  var kestrelServer = new KestrelServer(...);
  kestrelServer.StartAsync(...);

You configure how you want it to listen on the IPs and ports you want. For SSL, you can do

  kestrelServer.Options.Listen(ip, port, 
    (ListenOptions listenOptions) => listenOptions.UseHttps(X509Certificate2));

You also specify a class that implements IHttpApplication. In the ProcessRequestAsync method, do whatever processing you want and send back some http headers and response body.

Note: if you don't specify the Content-Length header before sending back the response body, Kestrel uses Transfer-Encoding: chunked. This is different from what happens with HttpListener / HTTP.SYS, which in many cases will compute the Content-Length header for you.

In my example, I used <Project Sdk="Microsoft.NET.Sdk.Web"> rather than <Project Sdk="Microsoft.NET.Sdk"> in the csproj so I wouldn't have to deal with adding all the package references myself. This works fine, except it forces you to have a launchSettings.json file, and by default Visual Studio tries to launch your app using IIS Express when you do Ctrl+F5, but you can override this and make it launch as a normal console app.

Here's the full code

using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BareBonesKestrel
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var loggerFactory = new NoOpLoggerFactory();
            var kestrelServer = new KestrelServer(new ConfigureKestrelServerOptions(), new SocketTransportFactory(new ConfigureSocketTransportOptions(), loggerFactory), loggerFactory);
            kestrelServer.Options.ListenLocalhost(8080);
            kestrelServer.StartAsync(new HttpApp(), CancellationToken.None).GetAwaiter().GetResult();

            // todo: you can update this code to do some other processing on this thread, and you can remove the call to StopAsync() if you don't need it.
            Thread.Sleep(60000);
            Console.WriteLine("shutting down");
            kestrelServer.StopAsync(CancellationToken.None).GetAwaiter().GetResult();
        }

        private class HttpApp : IHttpApplication&lt;IFeatureCollection&gt;
        {
            private readonly byte[] helloWorldBytes = Encoding.UTF8.GetBytes("hello world");

            public IFeatureCollection CreateContext(IFeatureCollection contextFeatures)
            {
                return contextFeatures;
            }

            public void DisposeContext(IFeatureCollection context, Exception exception)
            {
            }

            public async Task ProcessRequestAsync(IFeatureCollection features)
            {
                var request = (IHttpRequestFeature)features[typeof(IHttpRequestFeature)];
                var response = (IHttpResponseFeature)features[typeof(IHttpResponseFeature)];
                var responseBody = (IHttpResponseBodyFeature)features[typeof(IHttpResponseBodyFeature)];
                ////var connection = (IHttpConnectionFeature)features[typeof(IHttpConnectionFeature)];
                response.Headers.ContentLength = this.helloWorldBytes.Length;
                response.Headers.Add("Content-Type", "text/plain");
                await responseBody.Stream.WriteAsync(this.helloWorldBytes, 0, this.helloWorldBytes.Length);
            }
        }

        private sealed class NoOpLogger : ILogger, IDisposable
        {
            public IDisposable BeginScope&lt;TState&gt;(TState state)
            {
                return this;
            }

            public bool IsEnabled(LogLevel logLevel)
            {
                return false;
            }

            public void Log&lt;TState&gt;(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func&lt;TState, Exception, string&gt; formatter)
            {
            }

            public void Dispose()
            {
            }
        }

        private sealed class NoOpLoggerFactory : ILoggerFactory
        {
            private readonly ILogger logger = new NoOpLogger();

            public void Dispose()
            {
            }

            public void AddProvider(ILoggerProvider provider)
            {
            }

            public ILogger CreateLogger(string categoryName)
            {
                return this.logger;
            }
        }

        private sealed class ConfigureKestrelServerOptions : IOptions&lt;KestrelServerOptions&gt;
        {
            public ConfigureKestrelServerOptions()
            {
                this.Value = new KestrelServerOptions()
                {
                };
            }

            public KestrelServerOptions Value { get; }
        }

        public sealed class ConfigureSocketTransportOptions : IOptions&lt;SocketTransportOptions&gt;
        {
            public ConfigureSocketTransportOptions()
            {
                this.Value = new SocketTransportOptions()
                {
                };
            }

            public SocketTransportOptions Value { get; }
        }
    }
}

Thursday, November 17, 2011

Math Problem: Evenly spaced points on a sphere - Part 3

Continued from Part 2

Let’s try to find a way to systematically divide the triangles on a sphere into smaller triangles in such a way that the resulting triangles are as evenly spaced as possible, starting with either a tetrahedron, an octahedron, or an icosahedron. One thing we can do is place a new point in the center of each triangle. This is called a Kleetope. It is not possible to create a distribution of points that is as evenly spaced as possible by producing successive Kleeptopes because the resulting triangles will become less and less like equilateral triangles. Some points will be very close to each other and other points will be further apart.

original triangle   subdivided once by threes   subdivided twice by threes

Another thing we can do is split each triangle into four triangles by placing new points at the midpoint of each triangle edge.

original triangle   subdivided once by fours   subdivided twice by fours

When you subdivide a triangle into four triangles on the surface of a sphere, the four triangles are not all the same size. The center one is larger. But as you keep subdividing and the triangles get smaller, the curvature of the sphere makes less and less of a difference and the size of the subdivided triangles become more uniform. So if you can show that a set of points after some number of subdivisions are as evenly spaced as possible, then further subdivisions will also be as evenly spaced as possible. Let’s find the size of the center triangle after subdividing.

triangle with labels

Use the spherical law of cosines twice, once for the bigger triangle, and once for the smaller triangle. We’ll assume the radius of the sphere is 1.

cos(x) = cos(a)^2 + sin(a)^2 cos(t)

cos(2 a) = cos(2 a)^2 + sin(2 a)^2 cos(t)

Solve for cos(t).

cos(t) = ( cos(2 a) - cos(2 a)^2 ) / sin(2 a)^2

Try to simplify using the pythagorean trigonometric identity and the double angle formula.

cos(t) =

cos(t) =

Substitute for cos(t) in the smaller triangle.

cos(x) =

cos(x) =

cos(x) =

cos(x) = ( 3 cos(a)^2 - 1 ) / ( 2 cos(a)^2 )

When the big triangle (2 a) is very small, x is barely bigger than a. When 2 a is 2 pi / 9 (or 40°), x is about 5% bigger than a. When 2 a is 4 pi / 9 (or 80°), x is about 24% bigger than a. Finally, when 2 a is 2 pi / 3 (or 120°), x is twice the size of a, which is the same size as the original triangle. This is the case when the three original points are all evenly spaced around the equator, and the three new points are placed on the midpoints, which are also on the equator.

My guess is that you could take a tetrahedron, an octahedron, or an icosahedron, and iteratively split each triangle into four triangles, and you’d end up with an arrangement of points at each iteration that is as evenly spaced as it could be. Additionally, you could form the Kleeptope one time for each of these iterative arrangements and the arrangement of points would be as evenly spaced as they could be.

Iteratively splitting the tetrahedron

pointsedgesfacesdistance between adjacent points
464109.4712°
10241654.7356° and 90°
349664various


Forming the Kleetope for each iteration of the tetrahedron

pointsedgesfacesdistance between adjacent points
81812109.4712° and something
267248various
98288192various


Iteratively splitting the octahedron

pointsedgesfacesdistance between adjacent points
612890°
18483245° and 60°
66192128various


Forming the Kleetope for each iteration of the octahedron

pointsedgesfacesdistance between adjacent points
14362490° and something
5014496various
194576384various


Iteratively splitting the icosahedron

pointsedgesfacesdistance between adjacent points
12302063.435°
421208031.717° and 36°
162480320various


Forming the Kleetope for each iteration of the icosahedron

pointsedgesfacesdistance between adjacent points
32906063.435° and something
122360240various
4821440960various


So it looks like we can make a lot of arrangements of points that are probably as evenly spaced as possible, and we can make them larger and larger without limit.

However, it may not possible to mathematically specify an arrangement of points that is as evenly spaced as possible for any number of points n, because when n is a large prime, the optimal arrangement of points is probably unrelated to any patterns of points for any smaller n.

Sunday, November 13, 2011

Math Problem: Evenly spaced points on a sphere - Part 2

Continued from Part 1

In an ideal case, we would be able to position all the points around the sphere so that they formed equilateral triangles and all the adjacent points were exactly the same difference apart.

I should be more specific about what I mean by adjacent points. Draw a line connecting the two points that are closest to each other. Then draw a line connecting the next two points that are the next closest to each other and where the line wouldn’t cross any other existing lines. Continue along like this until no more lines can be drawn. The points with a line connecting them are considered to be adjacent.

In the ideal case, we can divide the surface area of the sphere by the number of triangles to find the surface area along the sphere of each triangle.

Let’s find the relationship between the number of points, the number of triangle edges, and the number of triangular faces. A tetrahedron has four points, six edges, and four faces. Whenever you add another point that is not in the same spot as an existing point, it will either fall along an existing edge or on a face. In either case, there will be three more edges and two more faces.

points edges faces
4 6 4
5 6+3 4+2
6 6+3+3 4+2+2
n 3 (n - 2) 2 (n - 2)


So in the ideal case, the spherical surface area A of each triangle is

A = (2 pi r^2) / (n-2)

Now let’s find the relationship between the distance 'd' along the surface of the sphere between two adjacent points and the spherical surface area of the triangle anchored by these two points. We can use Girard's theorem to find the relationship between the spherical vertex angle 't' and the spherical surface area A, and we can use the spherical law of cosines to find the relationship between the distance 'd' and the vertex angle 't'.

Use the spherical law of cosines.

Let a = d/r

cos(a) = cos(a)^2 + sin(a)^2 cos(t)

Solve for a.

First use the Pythagorean trigonometric identity to substitute.

cos(a) = cos(a)^2 + (1-cos(a)^2) cos(t)

Group the terms.

(1-cos(t)) cos(a)^2 - cos(a) + cos(t) = 0

Use the quadratic formula.

cos(a) = (1 +- sqrt(1 - 4 (1 - cos(t)) cos(t))) / (2 (1 - cos(t)))

Simplify.

cos(a) = (1 +- sqrt(1 - 4 cos(t) + 4 cos(t)^2)) / (2 (1 - cos(t)))

cos(a) = (1 +- (1 - 2 cos(t))) / (2 (1 - cos(t)))

cos(a) = 1 or cos(t) / (1 - cos(t))

We can ignore the case when cos(a) = 1 because that is when a = 0 or 2 π, which is when the two points are in the same location.

Now use Girard's theorem.

A = (3 t - pi) r^2

Solve for t.

t = (A + pi r^2)/(3 r^2)

Substitute for A.

t = (n pi)/(3 (n - 2))

Now substitute this for t in the equation for cos(a) above.

cos(a) = cos((n pi)/(3 (n - 2))) / (1 - cos((n pi)/(3 (n - 2))s))

In most cases we will not be able to position the points into perfect equilateral triangles of this size because the triangles will start to overlap or leave gaps with each other, but we can do it for n=4 the tetrahedron, n=6 the octahedron, and n=12, the icosahedron.

In the other cases, we can measure the amount of unevenness U by looking at the difference between the ideal distance between adjacent points 'd' and the actual distance between adjacent points dk. Define U as the sum of the squares of these differences:

U = sum( (dk - d)^2 )

For the tetrahedron, octahedron, and icosahedron, U = 0. For the other cases, we will try to position the points so that U is as small as possible.

So the next questions are whether there is a way to systematically position the points to minimize U, and given a configuration of points, how can you tell if U for this configuration is the lowest possible.

You can probably show that U is a local minimum if moving any one point by some small distance delta in any direction causes U to increase, but how do you determine whether that local minimum is the lowest minimum possible?

One way to explore this further is to build a computer simulation of marbles around the surface of a sphere that are all magnetically opposed to each other. Or better yet, let the force between any two adjacent points be proportional to the square of the difference between the actual distance between the points and the ideal distance between the points, so that the points are always being pushed towards the ideal distance.

Continue to Part 3

Saturday, October 29, 2011

Math Problem: Evenly spaced points on a sphere - Part 1

If place points around the circumference of a circle, it is not difficult to position them so that they're evenly spaced.

regular polygons

nangleanglename
32π / 3120°triangle
4π / 290°square
52π / 572°pentagon
n2π / n360 / nn-gon


But suppose you want to position points around the surface of a sphere so that they're evenly spaced.

What is the angle between adjacent points? Is it possible to position the points so that the angle is the same between all adjacent points on the sphere?

Let's look at some examples. For n=4, you have a tetrahedron.
tetrahedron

According to wikipedia, the angle between adjacent points is arccos(-1/3) or about 109.4712°.

For n=6, you have an octahedron. The angle between adjacent points is π / 2 or 90°.

octahedron

To summarize and expand:

nfacestri. facesedgestri. edgesangleanglename
44466arccos(-1/3)109.4712°tetrahedron
6881212π / 290°octahedron
86121218multiple anglescube
12202030302 arctan(2/(1+√5))63.435°icosahedron
n??????

So what's the answer? I don't know, but one thing we have learned is that a cube's points are not all evenly spaced. The angles between them follow a regular pattern. There are twelve edges with an angle between the points of 2 arcsin(1/√3) ≈ 70.528°, and if you include all triangle edges, there are six edges with an angle between the points of 2 arcsin(√2/√3) ≈ 109.4712°.

Continue reading Part 2

Saturday, July 18, 2009

Silverlight freezing when calling XMLHttpRequest?

I had a javascript handler for a Silverlight event that modifies the XAML, uses XMLHttpRequest synchronously, then modifies the XAML again, and I noticed that the browser would sometimes not show the first XAML change while the XMLHttpRequest was pending.

Example XAML:
<?xml version="1.0" encoding="utf-8" ?> 
<Canvas xmlns="http://schemas.microsoft.com/client/2007">
  <TextBlock Name="Txt1" Canvas.Left="25" Canvas.Top="25" FontSize="12" >Click the box</TextBlock>
  <Rectangle Name="Rect1" Canvas.Left="25" Canvas.Top="40" Width="200" Height="35" Fill="#8080FF" MouseLeftButtonDown="rect1_onMouseDown" />
</Canvas>
Example javascript:
function rect1_onMouseDown(sender, mouseEventArgs) {
  var txt1 = sender.findName("Txt1");
  var rect1 = sender.findName("Rect1");

  txt1.text = "In Progress...";
  rect1.Cursor = "Wait";

  var xmlhttp = new XMLHttpRequest();
  xmlhttp.open("GET", "long-running-url.asp", false);
  xmlhttp.send(null);

  rect1.Cursor = "Default";
  txt1.text = "Complete.";
}

Firefox shows "In Progress..." and then "Complete", but IE waits until everything is finished and then shows "Complete". So when exactly does Silverlight redraw itself when changes are made?

The answer is that changes are not drawn immediately after they are made. They are made the next available chance when no scripts are actively running.

Example that does not redraw on either Firefox or IE.
function rect1_onMouseDown(sender, mouseEventArgs) {
  var txt1 = sender.findName("Txt1");
  var rect1 = sender.findName("Rect1");

  txt1.text = "In Progress...";
  rect1.Cursor = "Wait";

  // keep busy for 0.5 sec
  var start = new Date();
  while ((new Date()).getTime() - start.getTime() < 500) {
    // do nothing
  }
  
  rect1.Cursor = "Default";
  txt1.text = "Complete.";
}

So if you want a redraw while an XMLHttpRequest is pending, do it asynchronously.
function rect1_onMouseDown(sender, mouseEventArgs) {
  var txt1 = sender.findName("Txt1");
  var rect1 = sender.findName("Rect1");

  txt1.text = "In Progress...";
  rect1.Cursor = "Wait";

  var fnOnStateChange = function() {
    if (xmlhttp.readyState == 4) {
      rect1.Cursor = "Default";
      txt1.text = "Complete.";
    }
  };
  var xmlhttp = new XMLHttpRequest();
  xmlhttp.onreadystatechange = fnOnStateChange;
  xmlhttp.open("GET", "long-running-url.asp", true);
  xmlhttp.send(null);
}

Here are a few more examples shown live. The first and third will be redrawn inconsistently and the second one will always redraw during the intermediate "In Progress..." stage. Click the download links below to see the source code for this.

Download source.html, source.xaml

Friday, December 12, 2008

Calling MySQL stored procedures from C# with Connector/Net

Have ever got this error when calling a stored procedure?
MySql.Data.MySqlClient.MySqlException: SELECT command denied to user 'your_user'@'localhost' for table 'proc'
  at MySql.Data.MySqlClient.MySqlStream.OpenPacket () [0x00000] 
  at MySql.Data.MySqlClient.NativeDriver.ReadResult (System.UInt64& affectedRows, System.Int64& lastInsertId) [0x00000] 
  at MySql.Data.MySqlClient.MySqlDataReader.GetResultSet () [0x00000] 
  at MySql.Data.MySqlClient.MySqlDataReader.NextResult () [0x00000] 
Let me explain what is going on here. The first thing Connector/Net does when you call a stored procedure is select the full text of the stored procedure from the server, parse it, and cache information about the parameters. It then uses that information to build the statement for the actual stored procedure call.

It does this by either calling
SELECT * FROM mysql.proc WHERE db LIKE 'mydb_name' AND name LIKE 'MyProcedure';

or by calling
SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA LIKE 'mydb_name' AND ROUTINE_NAME LIKE 'MyProcedure';

Why does it do this?

Let me give you a little background. Microsoft SQL Server has a nice feature in that it gives you a lot of flexibility when you call stored procedure. The syntax is
EXEC MyProcedure @MyParam1=123, @MyParam2='blah';

The parameters can appear in any order and any of the parameters can be specified as optional in the stored procedure. This is nice when you want to add a new parameter to a stored procedure and release it to your website without taking downtime. First you run an ALTER statement to release the new stored procedure with a new optional parameter, then you release the new application code which passes the new parameter to the stored procedure. Everything works fine when the application code and stored procedure are not in sync because the new parameter is optional.

MySQL's syntax is different. It is
CALL MyProcedure(123, 'blah');

The parameters have to appear in the right order and there is no support for optional parameters.

Now let's look at how stored procedures are called from the application code. Calling Microsoft SQL Server from C# code looks like this
string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
using (System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection(connStr)) {
  System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand("MyProcedure", conn);
  cmd.CommandType = System.Data.CommandType.StoredProcedure;
  cmd.Parameters.Add("@MyParam1", System.Data.SqlDbType.Int).Value = 123;
  cmd.Parameters.Add("@MyParam2", System.Data.SqlDbType.VarChar, 40).Value = "blah";
  conn.Open();
  cmd.ExecuteNonQuery();
}
MySQL Connector/Net mimics this syntax fairly closely.
string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
using (MySql.Data.MySqlClient.MySqlConnection conn = new MySql.Data.MySqlClient.MySqlConnection(connStr)) {
  MySql.Data.MySqlClient.MySqlCommand cmd = new MySql.Data.MySqlClient.MySqlCommand("MyProcedure", conn);
  cmd.CommandType = System.Data.CommandType.StoredProcedure;
  cmd.Parameters.Add("@MyParam1", MySql.Data.MySqlClient.MySqlDbType.Int32).Value = 123;
  cmd.Parameters.Add("@MyParam2", MySql.Data.MySqlClient.MySqlDbType.String).Value = "blah";
  conn.Open();
  cmd.ExecuteNonQuery();
}
So in order for Connector/Net to figure out what order to put the parameters when it creates the CALL statement it gets the stored procedure definition from the server and looks at the order that the parameters appear in the original CREATE PROCEDURE statement.

There are a couple of problems with this.
  1. The account the application code is running under may not have select permissions on mysql.proc and if you are using shared web hosting it may not be possible to give your account these permissions.
  2. There is a performance penalty for retrieving and storing the stored procedure bodies.
So what I recommend doing is building the CALL statement yourself as a parameterized query.

My recommended way to call MySQL stored procedures
string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
using (MySql.Data.MySqlClient.MySqlConnection conn = new MySql.Data.MySqlClient.MySqlConnection(connStr)) {
  MySql.Data.MySqlClient.MySqlCommand cmd = new MySql.Data.MySqlClient.MySqlCommand();
  cmd.Connection = conn;
  cmd.CommandText = "CALL MyProcedure(@MyParam1, @MyParam2);";
  cmd.Parameters.AddWithValue("@MyParam1", 123);
  cmd.Parameters.AddWithValue("@MyParam2", "blah");
  // Or if you prefer this style:
  //cmd.Parameters.Add("@MyParam1", MySql.Data.MySqlClient.MySqlDbType.Int32).Value = 123;
  //cmd.Parameters.Add("@MyParam2", MySql.Data.MySqlClient.MySqlDbType.String).Value = "blah";
  conn.Open();
  cmd.ExecuteNonQuery();
}
Connector/Net will go through the CommandText and replace @MyParam1 and @MyParam2 with the values you provide so the statement that gets sent to the MySQL server is just CALL MyProcedure(123, 'blah'); It's a good idea to do it with parameters because the library code will properly escape single quote characters. If you don't do this, the SQL statement could end up with syntax errors, or worse, end users might be able to run malicious SQL queries against your MySQL server.

Output parameters

Connector/Net does not handle parameters with Direction = System.Data.ParameterDirection.Output unless CommandType = System.Data.CommandType.StoredProcedure. In our case we're using CommandType.Text so we can only use ParameterDirection.Input.

So what do we do if we want to call a stored procedure that has an output parameter?

We use MySQL User Variables. This is the statement we will send to the MySQL server.
CALL MyProcedure(@MyOutputParam1, @MyOutputParam2, 123, 'blah');
SELECT @MyOutputParam1, @MyOutputParam2;


But before we can make this work there are a couple things we need to take care of. One is that we need to include Allow User Variables=true; in the connection string.

Server=localhost; Port=3306; Database=your_database; Uid=your_user; Pwd=your_password; Connect Timeout=30; Allow User Variables=true;

If we don't do this Connector/Net will throw an exception. This is because it checks that the parameter names in the CommandText match up with the parameters that have been added to the list and it doesn't have a way to distinguish between user variables and parameters. If we set Allow User Variables=true; it will skip this check.

The other thing we have to take care of is the data type of the user variables. Connector/Net does not handle result sets with user variables in them properly.

To see for yourself, try running this query:
SET @a = true, @b = 222, @c = -1, @d = 'abcd';
SELECT @a, @b, @c, @d, true, 222, -1, 'abcd';
Connector/Net returns the first three columns in the result set as byte[] and sets the contents of the byte array to be the raw bytes sent back by the MySQL server. The fourth column is a normal string and the rest of the columns are all handled properly.

The MySQL Command Line Client, on the other hand, handles user variables properly. You can see it here.
mysql> SET @a = true, @b = 222, @c = -1, @d = 'abcd';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @a, @b, @c, @d, true, 222, -1, 'abcd';
+------+------+------+------+------+-----+----+------+
| @a   | @b   | @c   | @d   | TRUE | 222 | -1 | abcd |
+------+------+------+------+------+-----+----+------+
| 1    | 222  | -1   | abcd |    1 | 222 | -1 | abcd |
+------+------+------+------+------+-----+----+------+
1 row in set (0.00 sec)
I was curious what was going on so I intercepted the response sent back by the MySQL server to see if I could tell why Connector/Net was failing. It sends the column definitions first and then the contents of each row. I could see that the row data was the same for column @a and column TRUE, column @b and column 222, and so on, but the header containing the column definitions were different. I didn't investigate further and instead looked for a workaround.

Here is what I did to make it work.

CALL MyProcedure(@MyOutputNum1, @MyOutputString2, 123, 'blah');
SELECT CAST(@MyOutputNum1 AS SIGNED), @MyOutputString2;


If your parameters are not signed integers you will need to cast them as something else. Read about using CAST and CONVERT. Note that SIGNED and UNSIGNED are both 64 bit integers.

Now that I know what to do I can create the application code.
string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
using (MySql.Data.MySqlClient.MySqlConnection conn = new MySql.Data.MySqlClient.MySqlConnection(connStr)) {
  MySql.Data.MySqlClient.MySqlCommand cmd = new MySql.Data.MySqlClient.MySqlCommand();
  cmd.Connection = conn;
  cmd.CommandText = "CALL MyProcedure(@MyOutputNum1, @MyOutputString2, ?MyParam1, ?MyParam2); SELECT CAST(@MyOutputNum1 AS SIGNED), @MyOutputString2;";
  // I am using the ?param style to make it easy to differentiate between user variables and parameters. @MyParam1 would also work.
  cmd.Parameters.AddWithValue("?MyParam1", 123);
  cmd.Parameters.AddWithValue("?MyParam2", "blah");
  conn.Open();
  MySql.Data.MySqlClient.MySqlDataReader rdr = cmd.ExecuteReader();
  // If MyProcedure returns a result set that will come first so you will need: 
  //   while (rdr.Read()) {...} 
  //   and rdr.NextResult();
  // Now get the output parameters.
  long myOutputNum1 = -1;
  string myOutputString2 = null;
  if (rdr.Read()) {
    int i = 0;
    if (rdr.FieldCount > i && !rdr.IsDBNull(i)) myOutputNum1 = rdr.GetInt64(i);
    i++;
    if (rdr.FieldCount > i && !rdr.IsDBNull(i)) myOutputString2 = rdr.GetString(i);
  }
}
That worked.

Saturday, December 6, 2008

Javascript and C# encryption

If you are unable to use SSL and you want to use encryption, one option is to use javascript to encrypt and send your data. Then on the server side you can decrypt the data, process it, and send back an encrypted response to be decrypted by javascript.

I have created a page which does this. I used AES (also known as Rijndael) for the private key encryption algorithm. The .NET framework has a built-in class library implementation of this called System.Security.Cryptography.RijndaelManaged and there are some javascript implementations out there on the web. I copied one of them and made some modifications to it, such as adding Base64 encoding.

The private key is hard coded in the server side code and must be entered into a textbox on the page by the user.

Download the code