Thursday, July 16, 2009

A great book for asp.net mvc

I learned asp.net mvc recently, and the material I picked was Pro ASP.NET MVC Framework by Steven Sanderson. This book is great for learning asp.net mvc.
During the short period since its publish on April 2009, it quickly earns five star rating on amazon with around 20 reviews. It's a solid evidence for the success of the book.
I can't say Steven has the deepest knowledge of asp.net mvc, but I'm sure he has the best skill to convey his knowledge to audience. As for my experience for learning new technology, I'd like to know:
  • What does it do?
  • What's the benefit of it?
  • How to use it?
  • What's the underlying working mechanism?
  • Any best practice with it?
Steven's writing style perfectly matches my preference. It started off by a big picture of the asp.net mvc including what's it and the benefit of using it and comparison to other alternative technologies. In the following several chapters, Steven guides us through a classical web shopping example with best engineering practices that take advantage of asp.net mvc framework. Like unit testing and inversion of control, etc. After telling us how to use it, the most exciting part about the core architecture of asp.net mvc starts. With knowing what's happening under the hood, one can easily customize mvc to meet his requirements and debugging. The final part is about other practical issues we may encounter such as validation, security and deployment.
This book fully covers everything we want to know about asp.net mvc, in a clear way. I highly recommend it for anyone who is interested in this technology.

Update:
Here is a compare of currently available books about asp.net mvc:
http://www.mikesdotnetting.com/Article/112/ASP.NET-MVC-Battle-of-the-Books

Tuesday, June 16, 2009

Use VI command in bash

If you are a fan of vi editor, you'll be exciting to find out that we can use vi command in shell.
By default, bash has its own set of short cut to make it easier to type and change command. But it's quite different from what we vi users have get used to. It's a pain to try to remember two different style of short cuts.
Fortunately, bash has the ability to allow us to retain our vi habit. To turn this feature on, simply run set -o vi . Then we can verify this feature is enabled by running set -o and see if it shows vi on.
Having turned it on, we can edit our commands just as in vi. By default, the shell is in insert mode. By pressing escape, it turns to command mode in which we can use vi command to move, search around.
For example, we can find a command in history by :
  1. press escape
  2. press /
  3. input search terms
  4. press n to search for next match
To view and edit shortcut keys in bash, we can use the bind build-in command.
Bonus: In windows prompt, we can use F7 to get a command history list and F8 to search in command history.

References:
Master the Linux bash command line with these 10 shortcuts
Useful Keyboard Shortcuts for the DOS Command Prompt in Windows
Find and bind key sequences in bash

Tuesday, May 19, 2009

Minimize Code Explosion of Generic Type

Generic is added to .net framework since version 2, which highly increase the re-usability of commonly used algorithms. It's well known that jit compiler will generate concrete type with given generic type argument at run time. So, it's possible that there will be code explosion if a lot of concrete types are created.

What kind of explosion?
According to the compilation model of .net application. The C#/VB code is first compiled into IL code. Then the jit compiler will compile the IL code into native code on demand. The jit compiler will also generate concrete type with specified type arguments. So, there is only one copy of IL code with generic type argument still in place.
What get duplicated is the native code generated by jit compiler. There is a copy for every method for each concrete type.
Another kind of data has duplication is EEClass and MethodTable. EEClass and MethodTable is type specific data. Strictly speaking, such data don't get duplicated because they are unique to each concrete type.

How .net tries to avoid explosion

In .net framework, two methods are adopted to minimize code explosion.
1. Different invokes of a generic method with the same type argument share the same copy of native code. This only takes effect when these invokes are in the same appdomain.
2. The CLR considers all reference type arguments to be identical. It does this based on the fact that reference variables are pointers (kind of, not accurate expression) to object on the heap. They can be manipulated in the same way.

Verify the optimization
In order to verify that the optimization method acutally behaves that way, we create the following sample and debug it with windbg.

static void Main()
{
List<int> intList = new List<int>();
List<object> objList = new List<object>();
List<system.delegate> delList = new List<system.delegate>();
}

Input sxe ld:mscorlib to instruct windbg to break when the application loads mscorlib module
When windbg breaks, input .loadby sos mscorwks to load sos.dll
Input .chain to confirm the sos extension has been successfully loaded
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos: image 2.0.50727.3053, API 1.0.0, built Fri Jul 25 22:08:38 2008
[path: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos.dll]

Input !bpmd Test.exe Test.Program.Main to set a managed breakpoint in Main method
Input p command several times until we see System.Collections.Generic.List`1 object on the managed stack with !dso command. The output below shows objects we are interested in:
ESP/REG  Object   Name
0019e3a4 01e6bc04 System.Collections.Generic.List`1[[System.Delegate, mscorlib]]
0019e5c4 01e6bbec System.Collections.Generic.List`1[[System.Object, mscorlib]]
0019e5c8 01e6bbc8 System.Collections.Generic.List`1[[System.Int32, mscorlib]]

Input !do 01e6bc04 to dump the first object and we get:
Name: System.Collections.Generic.List`1[[System.Delegate, mscorlib]]
MethodTable: 008126a4
EEClass: 698fca68
Size: 24(0x18) bytes
(C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
Fields:
MT    Field   Offset                 Type VT     Attr    Value Name
69b140bc  40009d8        4      System.Object[]  0 instance 01e6bc1c _items
69b42b38  40009d9        c         System.Int32  1 instance        0 _size
69b42b38  40009da       10         System.Int32  1 instance        0 _version
69b40508  40009db        8        System.Object  0 instance 00000000 _syncRoot
69b140bc  40009dc        0      System.Object[]  0   shared   static _emptyArray
Domain:Value dynamic statics NYI
002efb90:NotInit
...

Input !dumpmt -md 008126a4 to dump method table for this object. We get:
EEClass: 698fca68
Module: 698d1000
Name: System.Collections.Generic.List`1[[System.Delegate, mscorlib]]
mdToken: 0200028d  (C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
BaseSize: 0x18
ComponentSize: 0x0
Number of IFaces in IFaceMap: 6
Slots in VTable: 77
--------------------------------------
MethodDesc Table
Entry MethodDesc      JIT Name
69a96a70   69914934   PreJIT System.Object.ToString()
69a96a90   6991493c   PreJIT System.Object.Equals(System.Object)
69a96b00   6991496c   PreJIT System.Object.GetHashCode()
69b072f0   69914990   PreJIT System.Object.Finalize()
69aef320   69913310   PreJIT System.Collections.Generic.List`1[[System.__Canon, mscorlib]].Add(System.__Canon)
69b03f00   69913318   PreJIT System.Collections.Generic.List`1[[System.__Canon, mscorlib]].System.Collections.IList.Add(System.Object)

And we do the same thing to dump method table for the 2nd and 3rd object. The output is:
EEClass: 698fca68
Module: 698d1000
Name: System.Collections.Generic.List`1[[System.Object, mscorlib]]
mdToken: 0200028d  (C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
BaseSize: 0x18
ComponentSize: 0x0
Number of IFaces in IFaceMap: 6
Slots in VTable: 77
--------------------------------------
MethodDesc Table
Entry MethodDesc      JIT Name
69a96a70   69914934   PreJIT System.Object.ToString()
69a96a90   6991493c   PreJIT System.Object.Equals(System.Object)
69a96b00   6991496c   PreJIT System.Object.GetHashCode()
69b072f0   69914990   PreJIT System.Object.Finalize()
69aef320   69913310   PreJIT System.Collections.Generic.List`1[[System.__Canon, mscorlib]].Add(System.__Canon)
69b03f00   69913318   PreJIT System.Collections.Generic.List`1[[System.__Canon, mscorlib]].System.Collections.IList.Add(System.Object)

EEClass: 698f6c3c
Module: 698d1000
Name: System.Collections.Generic.List`1[[System.Int32, mscorlib]]
mdToken: 0200028d  (C:\Windows\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
BaseSize: 0x18
ComponentSize: 0x0
Number of IFaces in IFaceMap: 6
Slots in VTable: 77
--------------------------------------
MethodDesc Table
Entry MethodDesc      JIT Name
69a96a70   69914934   PreJIT System.Object.ToString()
69a96a90   6991493c   PreJIT System.Object.Equals(System.Object)
69a96b00   6991496c   PreJIT System.Object.GetHashCode()
69b072f0   69914990   PreJIT System.Object.Finalize()
69fd3b60   699ac468   PreJIT System.Collections.Generic.List`1[[System.Int32, mscorlib]].Add(Int32)
69fd2f80   699ac470   PreJIT System.Collections.Generic.List`1[[System.Int32, mscorlib]].System.Collections.IList.Add(System.Object)


From the output, we can easily identify that the method for objList and delList are the same, but the method for intList is different. So we've verified that the code for concrete type of reference type argument are shared.
Although the code is shared, these objects' EEClass are different. So they are actually different types.

Given the debugging skill above, we can also easily verify that different generic instances defined with the same type argument in different scope have the same EEClass.

References:
Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects

Saturday, May 9, 2009

GoAhead Web Server Hang

Symptom:
Recently, we are experiencing process hang with the goAhead web server. The symptom can be reproduced if we disconnect the network cable while the browser is loading a page. When it occurs, we can see that the process doesn't occupy any cpu resource with top command. And we can see there are a lot of connections in ESTABLISHED, CLOSE_WAIT, TIME_WAIT, FIN_WAIT status with netsstat -atn command.
Ayalysis:
From the symptom we observed, there is no doubt it's caused by process hang. Usually, process hang is caused by the process being waiting on some conditions never or take an extreme long time to to satisfy. A typical scenario is dead lock.
We adopted a method that is kind of naive but straightforward to investigate the cause, which is printf. We inserted a lot of printf statement into source code to find out exactly in which method did the web server hanged. This is time consuming but yet effective. By time consuming, we spend more than two days on finding out the calling sequence. By effective, we finally find out that the web server is hanging in network operation.
Aside: It does seems inefficient to do so. Actually, we've tried to attach a debugger to the hung process with gdbserver(cmd: gdbserver --attach IPADDRESS:PORT PID). But in the debugger, it seems to be missing correct symbol information. And even the thread information (cmd: info threads) isn't correct. These information are correct if we attach the debugger to the web server when it's not hung.
The real cause is when the peer of the socket is forcibly disconnected even without sending FIN. So the web server still considers the socket in ESTABLISHED state. Then it will operate on the socket as normal. If the socket is in Blocking mode and doesn't have a timeout specified, the web server will be blocked on reading from or writting to the socket indefinitely.
Solution:
Having found out the cause, it's easy to solve it. We can either specify a timeout on the native socket or set the socket to non-blocking mode. Code below demonstrates how to achieve so.

1. Specify timeout
void websSSLReadEvent(webs_t wp)
{
sptr = socketPrt(wp->sid);
struct timeval tv;
tv.tv_sec = 2; // timeout is two seconds
tv.tv_usec = 0; // it must be set to 0 explicitly, otherwise it may be a random number
int rc = setsockopt(sptr->sock, SOL_SOCKET, SO_RCVTIMEO, (struct timeval*)&tv, sizeof(struct timeval));
rc = setsockopt(sptr->sock, SOL_SOCKET, SO_SNDTIMEO, (struct timeval*)&tv, sizeof(struct timeval));
....
}

2. Clear Blocking mode
void websDone(webs_t wp)
{
....
socketSetBlock(wp->sid, 0); // the second parameter is one originally. so that it will flush everything to the peer in blocking mode to achieve graceful closing
socketFlush(wp->sid);
}

Tuesday, April 21, 2009

Macro expansion and Assembly code

We know that compiler will expand macros before it actually compiles the code. Sometimes it's useful if we can view the result of the expansion, especially when we use macro to implement some functions. Here is how:
Microsoft C++ compiler:
cl.exe source.cpp /E (preprocess to standard output)
cl.exe source.cpp /P (preprocess to file)
gcc:
gcc source.cpp -E (short for expand)



We can also peek into the assembly code generated by the compiler. With the following options, the compiler will generate a source.s file containing the assembly code in current directory.
Microsoft C++ compiler:
cl.exe source.cpp /FAs
gcc:
gcc source.cpp -S


References:
GCC options you should know
Macro Expansion Algorithm
Compiler Options (MSDN)

Friday, April 3, 2009

Detect Stack Corruption

Stack corruption bug is sometimes difficult to fix if we can't find out the steps to reproduce it. The cause of the bug may not be so obvious. The best thing is to have the culprit reveal itself as soon as possible, even before the stack corrupted. In this post, I'll introduce the tool to help discover stack corruption.

Rationale:
The figure below shows the structure of stack frame.It's important to know that stack grows downwards. The callee's frame is at lower position relative to caller's frame, and the callee's local variables are at lower position relative to return address. So, if our code carelessly write to a local variable beyond its boundary, the saved %ebp and return address may be corrupted. The appication may continue running until the callee returns or even later and then crash.
This is a sample code demonstrates this:


1 int foo(int a)

2 {

3 char var[4];

4 strcpy(var, "corrupt me!!!");

5 int a, b;

6 a = a + b;

7 return 0;

8 }

9

10 int bar()

11 {

12 return foo();

13 }


It's not hard to see that the saved %ebp should always stay unchanged during the execution of the callee since it will be used on return to restore the caller's %ebp.
So we can:
  1. Save the saved %ebp value at the beginning of the callee;
  2. Get the saved %ebp value before the callee returns;
  3. Compare these two value to see if they are the same;
Implementation:
The saved %ebp is the value of the memory that %ebp register is pointing to. In order to get its value, we need to use assembly language. But it's not difficult, it usually doesn't take more than one instruction to achieve. Here is the one for GCC on x86 platform.
asm("mov (%%ebp),%0": "=r" (variable for storing ebp's value));

Armed with this knowledge, we have the macro below to help detecting stack corruption.


1 #ifndef _h_DBGHELPER

2 #define _h_DBGHELPER

3

4

5 #include <assert.h>

6

7

8 #define STACKCHECK

9 #ifdef STACKCHECK // stack check enabled

10

11 #define STACK_CHECK_RAND 0xCD000000

12 #define STACK_CHECK_MASK 0x00FFFFFF

13

14 // the internal logic of checking stack state

15 #define STACK_CHECK_END_INTERNAL() u_STACK_CHECK_EBP_VALUE_RETURN = ((u_STACK_CHECK_EBP_VALUE_RETURN & STACK_CHECK_MASK)\

16 | STACK_CHECK_RAND);\

17 if((u_STACK_CHECK_EBP_VALUE_ENTER & ~STACK_CHECK_MASK) != STACK_CHECK_RAND)\

18 {\

19 fprintf(stderr, \

20 "Corrupted u_STACK_CHECK_EBP_VALUE_ENTER!! It's %x\n", u_STACK_CHECK_EBP_VALUE_ENTER);\

21 assert((u_STACK_CHECK_EBP_VALUE_ENTER & ~STACK_CHECK_MASK) == STACK_CHECK_RAND);\

22 }\

23 if((u_STACK_CHECK_EBP_VALUE_RETURN & ~STACK_CHECK_MASK) != STACK_CHECK_RAND)\

24 {\

25 fprintf(stderr, \

26 "Corrupted u_STACK_CHECK_EBP_VALUE_RETURN!! It's %x\n", u_STACK_CHECK_EBP_VALUE_RETURN);\

27 assert((u_STACK_CHECK_EBP_VALUE_RETURN & ~STACK_CHECK_MASK) == STACK_CHECK_RAND);\

28 }\

29 if(u_STACK_CHECK_EBP_VALUE_ENTER != u_STACK_CHECK_EBP_VALUE_RETURN)\

30 {\

31 fprintf(stderr, "Stack overflow!!!\nThe EBP should be %x, but it's %x( %s )\n\n",\

32 u_STACK_CHECK_EBP_VALUE_ENTER, u_STACK_CHECK_EBP_VALUE_RETURN, \

33 (char*)&u_STACK_CHECK_EBP_VALUE_RETURN);\

34 assert(u_STACK_CHECK_EBP_VALUE_RETURN == u_STACK_CHECK_EBP_VALUE_ENTER);\

35 }

36 // end

37

38 #ifndef ARM_9260EK // x86

39 #define STACK_CHECK_BEGIN() unsigned int u_STACK_CHECK_EBP_VALUE_ENTER = 0; \

40 asm("mov (%%ebp),%0"\

41 : "=r" (u_STACK_CHECK_EBP_VALUE_ENTER));\

42 u_STACK_CHECK_EBP_VALUE_ENTER = (u_STACK_CHECK_EBP_VALUE_ENTER & STACK_CHECK_MASK) | STACK_CHECK_RAND

43

44 #define STACK_CHECK_END() do{unsigned int u_STACK_CHECK_EBP_VALUE_RETURN = 0;\

45 asm("mov (%%ebp),%0"\

46 : "=r" (u_STACK_CHECK_EBP_VALUE_RETURN));\

47 STACK_CHECK_END_INTERNAL();}while(0)

48

49

50 #else // arm

51 #define STACK_CHECK_BEGIN() unsigned int u_STACK_CHECK_EBP_VALUE_ENTER = 0; \

52 asm("str fp, %0 \n" \

53 : "=m" (u_STACK_CHECK_EBP_VALUE_ENTER)); \

54 u_STACK_CHECK_EBP_VALUE_ENTER = (u_STACK_CHECK_EBP_VALUE_ENTER & STACK_CHECK_MASK) | STACK_CHECK_RAND

55

56 #define STACK_CHECK_END() do{unsigned int u_STACK_CHECK_EBP_VALUE_RETURN = 0;\

57 asm("str fp, %0 \n" \

58 : "=m" (u_STACK_CHECK_EBP_VALUE_RETURN));\

59 STACK_CHECK_END_INTERNAL();}while(0)

60

61 #endif

62

63

64 #else // STACK Check disabled

65

66 #define STACK_CHECK_BEGIN() do{}while(0)

67 #define STACK_CHECK_END() do{}while(0)

68

69 #endif

70

71 #endif // _h_DBGHELPER


The basic idea of the macro is pretty much the same as I mentioned before. One thing to note is the variables used to keep the value of %ebp register are defined on the stack too. So they are on the current frame and may be corrupted too. In order to avoid this, we have several options. First, we can define them as static so that they will be in global data region rather than stack. But it will be unusable in mutl-threading environment. Second, we can define them on heap. Third, we can use a predefined random value to guard these variables and make sure they're not overwritten.
The third option is the one we used here.

Usage:
We can update previous code to take advantage of this feature as follows:

1 int foo(int a)

2 {

3 STACK_CHECK_BEGIN();

4 char var[4];

5 strcpy(var, "corrupt me!!!");

6 int a, b;

7 a = a + b;

8 STACK_CHECK_END();

9 return 0;

10 }

11

12 int bar()

13 {

14 return foo();

15 }


The application will gracefully assert that it detects a stack corruption just before the foo() method returns.


Microsoft's c++ compiler
and gcc have already provide stack checking functions. But I still think the macro is convenient and my effort has greatly consolidated my understanding of stack structure.

References:

http://blogs.msdn.com/vcblog/archive/2009/03/19/gs.aspx

Saturday, March 21, 2009

Fix bugs with core dump

A perplexity developers usually meet is they release the product to qa team and get a feedback of occasional crash. And the testers don't have a solid reproduction steps. In this case, it's a time-consuming task to find out the cause and fix it. What we need is an efficient postmortem debugging method.

Core dump is a mechanism provided by operating system to automatically capture the address apace of a crashed process into a dump file that can be used to help us debugging.

How to enable it
In most linux distributions, core dump is disabled by default. This can be validated by running "ulimit -a" command from shell. We are very likely to see the results below:
core file size (blocks, -c) 0

It indicates core dump is diabled. To enble it, the simpliest way is invoking "ulimit -c unlimited" command. Ulimit is a bash builtin command, and it only takes effect for current shell and all porcesses spawned in this shell.
If we want to enable it permanently for a user, we can add the command to the ~/.bashrc file.
If we want to enable it globally, we can edit the /etc/security/limits.conf file by setting
* soft core unlimited

Customize core dump name
By default, the core dump file will be created in the same directory that the application is running in with fixed file name "core". The name conforms to the definition in /proc/sys/kernal/core_pattern file.
This pattern can be changed via "sysctl -w kernel.core_pattern=DesiredValue" command. Again, this setting is't persisted. To make it permanent, we can edit the /etc/sysctl.conf file and add the line "kernel.core_pattern=DesiredValue" to it.
The list below is some place holder can be used in the naming pattern to make it more flexible.

%% A single % character
%p PID of dumped process
%u real UID of dumped process
%g real GID of dumped process
%s number of signal causing dump
%t time of dump (seconds since 0:00h, 1 Jan 1970)
%h hostname (same as ’nodename’ returned by uname(2))
%e executable filename

Example
And here is an exmple.

#Makefile
EXE = test
CC = g++
#CFLAGS += -Os
# add debug information
CFLAGS += -g
CFLAGS += -Wall

all:
$(CC) $(CFLAGS) test.cpp -o $(EXE)

.PHONY: clean

clean:
rm $(EXE) -rf


// source code
#include

using namespace std;

void foo()
{
char *buf = "aa";
cout << "before exception" <<>
buf[0] = 'b';// invalid code
cout << "after exception" <<>
}

int main()
{
foo();
return 0;
}


Every time we run the application, it will crash and generate a core dump file. We can analysis the dump file with gdb (gdb test core_dump_file).

Capture live core dump
It's also desirable to capture core dump of a process when it's still running. Such dump is useful for trouble shooting contention and dead lock issues. gcore is the right tool for this purpose.

Reference
http://linuxfocus.berlios.de/English/July2004/article343.shtml

Sunday, March 15, 2009

Extensive usage of Make

Many guys have used make utility with makefile to compile source code. The official introduction of gnu make also introduces it as "Make is a tool which controls the generation of executables and other non-source files of a program from the program's source files".
If we look inside how does make utility works, we'll find it can do much more than compiling code. It's so powerful to make our life much easier, and by our, i mean ordinary people, not just programmers.
The essential point is when combined with shell script, make can assist you doing a sequence of actions to perform automatically.

Typical scenario
Suppose we're writing a book "How to win lottery" which will surely be the best sell all around the world on amazon.com after it's available. Because everyone in different countries would like to have a copy of it, we also need to translate it into different languages.
And a generous, smart programmer provides us a super translation tool that is capable of translating all languages, at no cost.
The last thing is we need to share new chapters to our kind editor by placing documents at //ipaddress/book/(Sorry, I can't share the address with you since it's confidential). She will have some guys to proof reading them.

So, here is our typical working flow:
1. Write / update english version draft
2. Run the translation tool to generate a draft for a different language
3. Save the file according to its language
4. Upload the file to //ipaddress/book
5. Send a email to notify editor

A little bit boring, right ? We need to repeat this again and again when we have new chapter available or the editor asks us to correct errors. Can't we just focus on the writing the book itself ? We have two options, hire a guy to do steps two to five for us, or use make utility.

How to do?
We can define a makefile according to the steps above.

all: generateDraft translate upload sendMail

generateDraft:
draftGenerator.exe -o draft.pdf #this line generate a draft pdf file -o is the argument passed to draftGenerator

translate:
superTranslator.exe --lan $(lan) -o draft_$(lan).pdf #this line translate the draft to specified language and save it as a copy with language in filename

upload:
cp draft_$(lan).pdf //ipaddress/book

sendMail:
#send mail to editor

Then, each time after we update the draft, we can simply use "make lan=chinese"command to ask the compter do the rest for us.

The make utility is actually a parser that read the makefile and perform every actions defined there. So what we need to do is carefully design our makefile.

Summary
Well, to sum up, make utility if useful when we have:
  1. A sequence of steps to perform
  2. Frequent update
make will keep our working process consistent without forgetting to do several steps.

For example, we can define a make file to run all unit tests before comit new code to code respository.

References
Gnu make manual
Compile Apps Your Way With Custom Tasks For The Microsoft Build Engine

Tuesday, March 10, 2009

Enable SSL in goAhead web server


goAhead web server comes with internal ssl support. It's disabled by default, and I haven't seen a tutorial around this topic on the web. So here is the my adventure of enabling it.

Difficulties with goAhead
According to the goAhead's feature page, ssl is fully supported. But it's not so convienent to enable it. The downloaded source package doesn't include the source code of the ssl library that it depends it. Even the distribution package (header file and library file) isn't there.
If we trun the macro WEBS_SSL_SUPPORT definition on, what we get eventually is compilcation error. You may see the error of type SSL isn't defined.
Then we can see there is a mocana project file, so we guess go ahead is using mocana library. And now problem comes, mocana is a comerical product and it's not freely available.

Use openssl instead
Luckily, we found there was a macro named OPENSSL. It's a symbol of openssl can be an alternative option here.
Looking deeply inside the code, we can see that goAhead provides a abstract layer above the underlying ssl library. So that we can change the implementaion easily. See? It's a typical usage of adapter design pattern.
We downloaded the openssl source, compile a linux version binary set. The openssl is a powerful ssl tool set. In addition to the ssl library itself, it also contains several utility tools. Within those tools, openssl is a useful terminal tool. It can act as a ssl server, ssl client, and certificate file generator.
What's specifically useful is we can generate and sign certificate file with it and use them to test our server. This page tells how to generate and sign certificates.
In goAhead's code, it uses three certificate files.
privkey.pem : the private key
cacert.pem : the certificate
server.pem : A combination of private key and cacert. The first part of its content is privkey.pem and the second part is cacert.pem.

Having got those files, we changed the makefile to define WEBS_SSL_SUPPORT and OPENSSL macro to enable ssl. Compiled again and run.

How to visit
The goAhead web server listens on a differnt port for incoming secure connection. The port is defined as SSL_PORT macro n in websSSL.c with default value 433. To test, open the browser and visit https://address:SSL_PORT .
Since we used a self signed certificates, the firefox won't allow access to it. We have to add our site to firefox exception list from "tools - advanced - encryption - view certificates - add exception" .

Thursday, February 26, 2009

Product quality issue

Having invested so much time on the web server, it still seems to be buggy. Based on feedback from qa team, the number of bugs is keeping on climbing. So, what's the problem with it?

1. The requirement isn't clear enough
We've got an design document in ppt format to follow. The document seems to be detailed enough to save us much time on UI and working flow. But the negative effect is we don't perform a thorough thinking and discussion on the requirement.
The requirement review is held after all of the pages have been setup. It's easily for others to have comments on it at this time. But it's a disaster for us. Many of pages need to be improved.
If the requirement analysis meeting is held earlier against the ppt document, things will be much better.

2. Poor work load estimation
A poor estimation will ruin a project. The project will either miss schedule heavily or be of low quality. It's impossible to achive a highly qualified, complete result.
The work for a programming task isn't only consitituted of coding. Designing and debugging are also essential part of it. Careful design and thinking is necessary for mature code. And it requires luck to get your code work the first time without debugging. Also, to ensure quality and minimize testing effort, designning and implementing unit test is necessary.
So, the actual work load for a task may be 3 or 4 times the work load for coding.

3. Inconstant process
An unified process in the whole team is important. This includes principles and policies applied to programming and releasing.
The process should be unified and mandatory across the whole team. It should be improved constantly too.


"Technical debt", an interesting metaphor.

Saturday, November 29, 2008

Living in Huangpu river


Learned from Scott Hanselman's blog there is a ip to location lookup service freely available at http://www.hostip.info/. It shows your location in google map with your ip address.

And it seems I'm living in huangpu river, and I even don't know how to swim though.

Sunday, October 19, 2008

posting failed?

I'm using scribefile to make new post. But each time I clicked post, a error message pops up says it failed. And after that, I can't view this blog any more, always get connection interrupted page.

[Edited] Luckily enough, I'm able to make new post via this site directly.12121212