Monday, October 25, 2004

Using TLinkedRio

If you need to test your webservices before they're deployed, you needn't build a client. To test your functions, simply create a TLinkedRio, and cast it to your interface. The TLinkedRio will also store the XML passed in files (for debugging)

For instance, for a procedure named Yawn in an exposed Interface IICancelWait:


function TICancelWait.Yawn: string;
begin
Sleep( 10000 );

Result := 'Excuse Me';
end;

To test it in the service itself, you can drop a button in the Server form (this is a WAD project) and a label, with the following code in the button's handler:

procedure TForm1.Button1Click(Sender: TObject);
var MyRio : TLinkedRio;
begin
// use a TLinkedRIO

MyRio := TLinkedRio.Create(nil) ;
Label1.Caption := (MyRio as IICancelWait).Yawn;
end;

Notice that you don't need to free MyRio. A TLinkedRio will automatically get freed when it goes out of scope, if the Owner is null.

All you have to do is include the SoapLinked unit and the interface units in your server project.

You can even store the Request and Response in XML files to check the XML formatting or for other validations. Instead of Create, call CreateFile.


MyRio := TLinkedRio.CreateFile(nil,
ExtractFilePath(Application.ExeName)+'Req.xml',
ExtractFilePath(Application.ExeName)+'Resp.xml');

Note: Application.ExeName will only work for WAD executables. You need to use GetModuleFileName for ISAPI dlls, instead of Application.ExeName.

Wednesday, October 20, 2004

Advanced Web Services

I presented a paper at Borcon 2003, on Advanced web services. You can now see it online at http://www.agnisoft.com/white_papers/advancedws. Tell me what you think.

Monday, October 18, 2004

Optimize your ISAPI Dlls - Increase MaxConnections!

When your web application is called (as an ISAPI, NSAPI or Apache module), the Application spawns a new thread for a request. Within the context of this thread, your main web module is create.

When the request is handled and response is sent, the created instance is then:

  • Freed, if you set Application.CacheConnections=False. (It's true by default)
  • If Cache Connections is true, then the webmodule is cached in an internal array for reuse at the next request.

Setting CacheConnections to false will create a webmodule for each request - something we don't want, unless there's a real reason for doing so. So let's keep CacheConnections on. But, what if you have one request being handled when another request comes in? The application will look inside the cache - if a webmodule is available, it is used. Otherwise a new thread is created, with a new instance of a the webmodule is created to handle the request. This happens until the number of currently active connections reaches the value in Application.MaxConnections, after which an exception is raised which says "Too many active connections". This shows up as an Internal Server Error on the browser.

Now the default for Application.MaxConnections is 32 - so if you have any more than 32 connections active at any time, you will see internal server errors. 32 is woefully inadequate for anything that's in production, so you must increase this number - but do remember to test the "before" and "after" because at some point your memory usage will trade off against performance.

Note: All you have to do is set Application.MaxConnections in your .dpr file.

Wednesday, October 13, 2004

SOAP Basic Authentication - D7

Back after a break. It's been quite a while since I wrote, so I'll cut to the chase. Question on newsgroups: How do I set username/password for Basic Authentication on SOAP.

Issues: The HTTPRio.HTTPWebNode.UserName and .Password properties are used only in situations where theres a proxy. But let's assume you wanted to use these for basic authentication. You're going to have to set an event handler for HttpRio.HttpWebNode.OnBeforePost. You get a Data parameter here, and this is the HRequest used for sending the data.

All you now have to do is write something like this in the event handler:


if not InternetSetOption(Data,
INTERNET_OPTION_USERNAME,
PChar(HTTPRIO1.HTTPWebNode.UserName),
Length(HTTPRIO1.HTTPWebNode.UserName)) then
ShowMessage(SysErrorMessage(GetLastError));

if not InternetSetOption(Data,
INTERNET_OPTION_PASSWORD,
PChar(HTTPRIO1.HTTPWebNode.Password),
Length (HTTPRIO1.HTTPWebNode.Password)) then
ShowMessage(SysErrorMessage(GetLastError));


You can of course set any other password you want. Note: You need to add WinInet and SOAPHttpTrans to your uses clause.