This stuff is so simple ! For the past couple of months, everyone from my friendly neighborhood software guru to Microsoft have been trying to convince me that server side programming is much too difficult for someone like me. I believed them and kept away from servers and anything to do with them. Instead I concentrated on ActiveX, Java, Netscape Plugins and other such technologies. Finally, after some deliberation, I decided to see for myself just how difficult servers really were and boy, was I surprised ! Server side programming turned out to be even easier than ActiveX or Perl CGI, infact it was so easy that I refused to believe that I really was doing what everyone had convinced me was humanly impossible, programming my own server.
After that I just couldn't stop and this is the latest in the series of server programming tutorials I've put up. So go ahead and find out for yourself if programming servers is much too difficult for you !
You should have the Windows NT server 4.0, the IIS 2.0 and Visual C++ 4.0. Go into any subdirectory and create a project which should be for a .dll . Then insert the files m1.cpp and m1.def into it.
m1.cpp #include <ctype.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <io.h> #include <iostream.h> #include <strstrea.h> #include <fstream.h> #include <httpext.h> #include <httpfilt.h> void abc(char *p){FILE *fp=fopen("c:\\vij11.txt","a+");fprintf(fp,"%s\n",p);fclose(fp);} BOOL WINAPI DllEntryPoint (HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpv) { return (TRUE);} BOOL WINAPI GetFilterVersion (PHTTP_FILTER_VERSION pFilterVersion) { pFilterVersion->dwFilterVersion = HTTP_FILTER_REVISION; strcpy (pFilterVersion->lpszFilterDesc,"CVTDOC - Converts document or data into HTML if HTML not present or older"); pFilterVersion->dwFlags= (SF_NOTIFY_ORDER_HIGH | SF_NOTIFY_SECURE_PORT | SF_NOTIFY_NONSECURE_PORT | SF_NOTIFY_URL_MAP ); return TRUE; } DWORD WINAPI HttpFilterProc (PHTTP_FILTER_CONTEXT pFC,DWORD dwNotificationType,LPVOID pvNotification) { HTTP_FILTER_URL_MAP *p=(HTTP_FILTER_URL_MAP *)pvNotification; abc(p->pszPhysicalPath); strcpy(p->pszPhysicalPath,"C:\\inetsrv\\wwwroot\\default.htm"); return SF_STATUS_REQ_HANDLED_NOTIFICATION; } m1.def LIBRARY M1 EXPORTS GetFilterVersion HttpFilterProc
As you can see, there are a total of ten header files ! We don't know if all of them are needed or not, but it's better to be on the safe side. The header files that gave us a problem were httpext.h and httpfilt.h, because they don't come along with ActiveX or Visual C++ 4.0. Instead, you have to install MSDN and retrieve these files from the include subdirectory within the MSTools directory created by it. So if you're using nmake or building a project or whatever, make sure you have these two header files ( httpext.h and httpfilt.h).
Once you have all the headers in place, insert this program and compile it into a .dll. This .dll has to be copied into some subdirectory or the other where you have execute permission. ( We have copied it into the c:\inetsrv\wwwroot\vijay subdirectory ). The next step is to tell IIS to load this .dll. Now to do this, shut down the server using the Internet Service Manager i.e. select the first option, wwwserver, click on a menu - option - Properties and click on Stop Service. Once the server is down, start up regedit. In the registry, click on HKEY_LOCAL_MACHINE, select SYSTEM, then CurrentControlSet, Services, W3SVC and finally click on Parameter. You will find FilterDLLs on the right hand side of the splitter. If you double click on it, you will see only one .dll. To add your dll , you put a comma and then write the full pathname of your .dll ( we wrote c:\inetsrv\wwwroot\vijay\ m1.dll ). You'll have to do this for each and every .dll you want loaded. Once you're through ! with this, click on OK, restart the server (using Internet Service Manager) and then type the server's IP address or your domain name in your browser.
Typing this in will, by default, load the file called default.htm. This is because when the server starts, it knows it has to call two .dlls (i.e. Filter DLLs), one written by Microsoft, the other created by you. In the second .dll ( m1.dll ) the function GetFilterVersion will be called (it is exported in the .def file). This function receives a pointer to a structure HTTP_FILTER_VERSION. One of the members of this structure is lpszFilterDesc, which is some string. Since this string just holds some version information, you can write any junk you want. The member dwFilterVersion of the structure is there to store version information.e.g. http://www.vmukhi.com/ http://202.54.3.74/ http://localhost/
The most crucial and important member in the structure HTTP_FILTER_VERSION, is dwFlags which has been initialized to some number or the other. The first #define (SF_NOTIFY_ORDER_HIGH) talks about the priority, which is set to high, (we will come to that later) the second and the third #defines inform the dll whether the connection has taken place on a secure or non-secure port, and the last #define is for url mapping. The last one is very important because when you write the url as http://202.54.3.74 (ending or not ending with a slash) and press Enter, this address is picked up by the server who then converts it into an actual DOS like pathname. So in the case of IIS, when you just write http://202.54.3.74, it will be converted into c:\inetsrv\wwwroot\default.htm, (the IIS has been installed in c:\inetsrv). Now if you have aliases in your address, it can all become a teeny weenie bit complicated. For example http://202.54.3.74 will become c:\inetsrv\wwwroot\default.htm a! nd http://202.54.3.74/Scripts will point to c:\inetsrv\scripts . These are known as aliases.
Now because one of the flags is SF_NOTIFY_URL_MAP, your dll will be informed whenever the server converts the address written in the browser into an actual filename (i.e. a complete file name) on the hard disk. The server will, after converting the address into the actual filename, call the function HTTP_FILTER_PROC (which is also exported in the def file ). This function is passed three parameters of which the first is a pointer which we can ignore, the second is the reason for this function being called and the last one is also a pointer .
What we are trying to say here is that these guys at Microsoft have sat down and decided about the number of things that the Internet Information Server does before it receives and after it sends back information. So the function HTTP_FILTER_PROC can be called for any number of reasons. Now because it can be called for various reason, the second parameter, dwNotification is the reason the function will be called in this instance and the last parameter is a pointer to a void. A pointer to a void is a pretty meaningless title, but once you've said 'pointer to a void', you can cast the void to anything you like later on.
Now here we know that HTTP_FILTER_PROC will be called from our .dll only when a conversion takes place from the name you wrote ( virtual server name) to an actual filename on disk. So what we now do is create a variable p which is a pvNotification (just a big variable name) but is now a pointer to HTTP_FILTER_URL_MAP (HTTP_FILTER_URL_MAP is nothing but a structure tag). After that, we have the function abc which writes to a file on the disk. Now if you just stop here and run this program without writing anything more, you will see the full pathname each time someone accesses your server.
Now this is where the programming comes in. What we can do is change the value which is stored in pszPhysicalPath to C:\\inetsrv\\wwwroot\\default.htm, so anyone who now connects to your server, no matter what address he enters, will see the same page ( default.htm ) over and over again. Obviously it's a very dumb thing to do but it is possible.
Lets take a simple case, assuming that we decided to move file hierarchies within a certain subdirectory. So you can have a statement that checks what file the user wants to access and then you can point the browser to the new location of those files. The client will remain oblivious to all of this.
Now lets take the second example.
This second program can be compiled safely enough, but the linker will fail since the server is on and the .dll is locked in memory. So before you link, be sure to switch the server off. Only when you're through linking should you restart the server.M1.CPP #include <ctype.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <io.h> #include <iostream.h> #include <strstrea.h> #include <fstream.h> #include <httpext.h> #include <httpfilt.h> void abc(char *p){FILE *fp=fopen("c:\\vij11.txt","a+");fprintf(fp,"%s\n",p);fclose(fp);} BOOL WINAPI DllEntryPoint (HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpv){ return (TRUE);} BOOL WINAPI GetFilterVersion (PHTTP_FILTER_VERSION pFilterVersion) { pFilterVersion->dwFilterVersion = HTTP_FILTER_REVISION; strcpy (pFilterVersion->lpszFilterDesc,"CVTDOC - Converts document or data into HTML if HTML not present or older"); pFilterVersion->dwFlags= (SF_NOTIFY_ORDER_HIGH | SF_NOTIFY_SECURE_PORT | SF_NOTIFY_NONSECURE_PORT | SF_NOTIFY_READ_RAW_DATA ); return TRUE; } DWORD WINAPI HttpFilterProc (PHTTP_FILTER_CONTEXT pFC,DWORD dwNotificationType,LPVOID pvNotification) { HTTP_FILTER_RAW_DATA *p=(HTTP_FILTER_RAW_DATA *)pvNotification; char *p1=(char *)p->pvInData; abc(p1); return SF_STATUS_REQ_HANDLED_NOTIFICATION; }
Here we've said that if the user's browser asks for something, notify the dll. As you know, whenever a browser asks for data, the server gets it's headers (READ_RAW_DATA is for the headers). There is also a return value, which, since we're not going to go into detail, simply tells the server what action it should perform. If each of the filters asks for READ_RAW_DATA, then the first filter will tell IIS whether to call the other filters or not. In this program, we will be handling the notification ourselves.
Use different browsers to access the site and see the error messages.
This third example is actually two programs rolled into one.
Ignore the FOR loop in this program for the time being.M1.CPP #include <ctype.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <io.h> #include <iostream.h> #include <strstrea.h> #include <fstream.h> #include <httpext.h> #include <httpfilt.h> void abc(char *p){FILE *fp=fopen("c:\\vij11.txt","a+");fprintf(fp,"%s\n",p);fclose(fp);} BOOL WINAPI DllEntryPoint (HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpv){ return (TRUE);} BOOL WINAPI GetFilterVersion (PHTTP_FILTER_VERSION pFilterVersion) { pFilterVersion->dwFilterVersion = HTTP_FILTER_REVISION; strcpy (pFilterVersion->lpszFilterDesc,"CVTDOC - Converts document or data into HTML if HTML not present or older"); pFilterVersion->dwFlags= (SF_NOTIFY_ORDER_HIGH | SF_NOTIFY_SECURE_PORT | SF_NOTIFY_NONSECURE_PORT | SF_NOTIFY_SEND_RAW_DATA ); return TRUE; } DWORD WINAPI HttpFilterProc (PHTTP_FILTER_CONTEXT pFC,DWORD dwNotificationType,LPVOID pvNotification) { HTTP_FILTER_RAW_DATA *p=(HTTP_FILTER_RAW_DATA *)pvNotification; char *p1=(char *)p->pvInData; for ( int z = 1 ; z <= p->cbInData ; z ++) { if ( p1[z] >= 'A' && p1[z] <= 'Z' ) p1[z] = p1[z] + 32; } abc(p1); return SF_STATUS_REQ_HANDLED_NOTIFICATION; }
When the flag is SF_NOTIFY_SEND_RAW_DATA, it means that whenever the server is sending data to the client, the function HttpFilterProc in the dll will be called. Here we have a structure which looks like HTTP_FILTER_RAW_DATA and which has a member named pvInData (we cast it as a pointer to a char). It is a pointer to the data the server is going to send the client. The structure HTTP_FILTER_RAW_DATA also has member called cbInData which stands for the size of the data to be sent.
Now the reason we cast pvInData is because the data to be sent will not always be a char, it could be an image, something which is obviously not a pointer to a char. So if the guy wants a .bmp file, it will not send an html file, it will not send a text file, it will now send the necessary bytes of data and these bytes are now of a bmp file. So now make sure that when you run this dll, you don't have any images on your page. If you do, then once they've passed through the masher, they will look like nothing you've ever seen before !!!
The FOR loop will execute as many times as needed to send the data across. If the file size is 4000 bytes, then it will loop 4000 times. Each time it spots an upper case alphabet, it converts it into a lower case one. So it will seem like your server doesn't support capitals !! This means that you can now remove objectionable words from HTML documents on the fly, or even perform dynamic translations ! You now have the option to check and change whatever data leaves your server.
This program tells you how often an event takes place. So you can now write a simple query and find out the order in which these events take place. You can see the sequence in which they are called and the frequency with which they are called. This program gives you a deeper understanding of how all this really works.M1.CPP #include <ctype.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <io.h> #include <iostream.h> #include <strstrea.h> #include <fstream.h> #include <httpext.h> #include <httpfilt.h> void abc(char *p){FILE *fp=fopen("c:\\vij11.txt","a+");fprintf(fp,"%s\n",p);fclose(fp);} BOOL WINAPI DllEntryPoint (HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpv){ return (TRUE);} BOOL WINAPI GetFilterVersion (PHTTP_FILTER_VERSION pFilterVersion) { pFilterVersion->dwFilterVersion = HTTP_FILTER_REVISION; strcpy (pFilterVersion->lpszFilterDesc,"CVTDOC - Converts document or data into HTML if HTML not present or older"); pFilterVersion->dwFlags= SF_NOTIFY_ORDER_HIGH | SF_NOTIFY_SECURE_PORT | SF_NOTIFY_NONSECURE_PORT | SF_NOTIFY_SEND_RAW_DATA | SF_NOTIFY_PREPROC_HEADERS | SF_NOTIFY_AUTHENTICATION | SF_NOTIFY_URL_MAP | SF_NOTIFY_LOG | SF_NOTIFY_END_OF_NET_SESSION | SF_NOTIFY_ACCESS_DENIED | SF_NOTIFY_READ_RAW_DATA ; return TRUE; } DWORD WINAPI HttpFilterProc (PHTTP_FILTER_CONTEXT pFC,DWORD dwNotificationType,LPVOID pvNotification) { if ( dwNotificationType == SF_NOTIFY_PREPROC_HEADERS) abc("SF_NOTIFY_PREPROC_HEADERS"); if ( dwNotificationType == SF_NOTIFY_AUTHENTICATION) abc("SF_NOTIFY_AUTHENTICATION"); if ( dwNotificationType == SF_NOTIFY_URL_MAP) abc("SF_NOTIFY_URL_MAP"); if ( dwNotificationType == SF_NOTIFY_LOG) abc("SF_NOTIFY_LOG"); if ( dwNotificationType == SF_NOTIFY_END_OF_NET_SESSION) abc("SF_NOTIFY_END_OF_NET_SESSION"); if ( dwNotificationType == SF_NOTIFY_SEND_RAW_DATA) abc("SF_NOTIFY_SEND_RAW_DATA"); if ( dwNotificationType == SF_NOTIFY_ACCESS_DENIED) abc("SF_NOTIFY_ACCESS_DENIED"); if ( dwNotificationType == SF_NOTIFY_SECURE_PORT) abc("SF_NOTIFY_SECURE_PORT"); if ( dwNotificationType == SF_NOTIFY_NONSECURE_PORT) abc("SF_NOTIFY_NONSECURE_PORT"); if ( dwNotificationType == SF_NOTIFY_READ_RAW_DATA) abc("SF_NOTIFY_READ_RAW_DATA"); return SF_STATUS_REQ_NEXT_NOTIFICATION; }
In this last program, the http headers will be preprocessed and the function HttpFilterProc will be called right after that happens. Unfortunately, this pointer to the structure HTTP_FILTER_PREPROC_HEADERS does not have any variables, just lot's of pointers to functions. The function GetHeader is given the first pointer that HTTP_FILTER_PROC is called with, the second parameter is given as url (you can use urlmethod and many more), the third one is an array , and the fourth parameter is an unsigned long (Just typing long will give an error) which tells you how large the array is. If you print that value out, you will get whatever you wrote after the name of the computer (www.microsoft.com) or to be more specific, whatever you have written after the first slash. After that we change it to s.htm. Which now means that whenever any user comes to our site, no matter what he writes, he will get the same file (s.htm). So you can now see that there are two ways of redirecting the u! ser's output. You could do it as shown in program no. 1 or follow the procedure detailed above..M1.CPP #include <ctype.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <io.h> #include <iostream.h> #include <strstrea.h> #include <fstream.h> #include <httpext.h> #include <httpfilt.h> char aa[1000]; void abc(char *p){FILE *fp=fopen("c:\\vij11.txt","a+");fprintf(fp,"%s\n",p);fclose(fp);} BOOL WINAPI DllEntryPoint (HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpv){ return (TRUE);} BOOL WINAPI GetFilterVersion (PHTTP_FILTER_VERSION pFilterVersion) { pFilterVersion->dwFilterVersion = HTTP_FILTER_REVISION; strcpy (pFilterVersion->lpszFilterDesc,"CVTDOC - Converts document or data into HTML if HTML not present or older"); pFilterVersion->dwFlags= SF_NOTIFY_ORDER_HIGH | SF_NOTIFY_SECURE_PORT | SF_NOTIFY_NONSECURE_PORT | SF_NOTIFY_PREPROC_HEADERS ; return TRUE; } DWORD WINAPI HttpFilterProc (PHTTP_FILTER_CONTEXT pFC,DWORD dwNotificationType,LPVOID pvNotification) { HTTP_FILTER_PREPROC_HEADERS *p=(HTTP_FILTER_PREPROC_HEADERS *)pvNotification; unsigned long d = sizeof(aa);; p->GetHeader(pFC,"url",aa,&d); p->SetHeader(pFC,"url","/s.htm"); abc(aa); return SF_STATUS_REQ_NEXT_NOTIFICATION; }
I personally think that this type of programming will eventually replace ISAPI for CGI programming. Usually, when we talk about CGI, we're thinking of .exe files and perl scripts that get called and then respond by sending back an HTML document, but here, we're changing the fundamental nature of how a server reacts to requests. This is because the effect of these .dlls is server wide.
In the last example for instance, when ever a preprocessed header comes over, your function is called. So no matter what you're doing, your function is the boss. Quite honestly, I'm still pretty stunned at how simple all this stuff really is. Infact I've a mind to send some scathing mail off to Microsoft for scaring most people off server side programming and as for that babbling software guru...
Mr. Vijay Mukhi
Ms. Sonal Kotecha
Mr. Arsalan Zaidi
Move back to the Vijay Mukhi's Technology Cornucopia Page to learn more about the other new Internet Technologies.