W3C libwww on Windows NT
Modifications to libwww 3.1 for Windows and Windows/NT support.
History
The Initial work involved in porting libWWWW to Windows/NT involved
using the Winsock API, as defined in winsock.h.
Most changes were as expected (e.g. file naming conventions had to be
smoothed over, setting correct feature macros to include/exclude various
Unix APIs).
The biggest problem arose in the handling of the console in the built-in
HTEventLoop function. Unix systems assume that all file descriptors are
select()-able, including stdin. This is not true under Win32: the C
runtime supports the canonical FILE *, file _descriptors_ (aka C runtime
handles, or CRTs), and Win32 file handles (what is returned by the
Win32 CreateFile() call). Sockets under Win32 are file handles, for example.
However, only sockets can be passed to the select() call. We describe
the console workaround later in this note.
The Windows/NT port depends on the following variables
_WINDOWS
- defined for Windows/3.x
WIN32
- defined for Win32
_WINSOCKAPI_
- defined if Winsock compliant with Microsoft extensions defined in
Microsoft's winsock.h as the gaurd variable
Building for Windows/NT
LibWWW for Windows/NT is built as a static library using Visual C++ 2.1
running under Windows/NT 3.51beta. To build the library, create a new
project, add all .c files, and then remove the HTWAIS.c file. Define
_WINDOWS,WIN32, and _DEBUG as part of your project settings. I've only
compiled a debugging version, so some of the finer details of compiling
under Visual C++ elude me. I haven't (yet) thought about the issues
involved in building libwww as a DLL.
Building the linemode browser is equally simple. I have built it as a
console application; simply add the library directory as one of the
include directories, and include the library and wsock32.lib (32 bit
Winsock library) as part of your link command.
Workarounds for NT
New methods to iterate over fd_sets
In Unix, file descriptors are small postive numbers, and usually
contrained to be <= 64. Under Win32, sockets are file handles (32 bit
integers). One cannot safely interate between 1 and the maximum fd
value, since that value is potentially very large. We work around this
by creating a separate fd_set which represents all sockets ever
registered, and simply iterate through this set to enumerate all sockets
registered with the library. The fd_set implemention in Winsock is a
counted array; note as well that the maxsock parameter in select() is
not used in the Winsock implementation.
sockets aren't file descriptors, and vice versa
The library uses NETREAD() and NETWRITE() macros to hide the underlying
system call used to read a socket. However, the linemode browser also
can read from a file, given certain options. Whilst read() and write()
will work on any file descriptor under Unix, the same is _not_
true for Win32. Hence, a vicious hack: if the fd is <=10, we use read(),
else we use recv(). A better solution to this is solicited.
The Console hack
Our first effort was to get the linemode browser running. However, the
linemode browser calls the library with the file_descriptor of stdin.
This doesn't work under NT: only sockets are select()-able. Hence, we
implemented a busy wait: if we call HTEvent_register() with descriptor
0, we assume that its the console, and record this fact. Then, in the
event loop, we check to see if the console is ready, and then check to
see if any sockets are ready. If the console isn't registered, then we
just issue the select() call as written, which will block indefinitely.
The NT version of the library offers the following interfaces.
- Blocking i/o is always supported in the library.
- a Linemode browser interface, as described above. The linemode
browser is implemented as an NT console application that Supports
non-blocking i/o.
- Non-blocking i/o (the library's "active" mode) is supported,
provided that the developer only passes in sockets for registration.
- We have started to add support for a message based interface as
well, via the WSAAsyncSelect() call. The HTRequest structure has been
extended to include an HWND member and an winMsg member. These members
can be set explicitly for each HTRequest, or two global variables can
be set; the member variables are initialized from these values. If the
HWND variable is set, and non-blocking i/o is requested, then the
library calls WSAAsyncSelect() with the appropriate flags set, and
returns with a status code of HT_WOULDBLOCK.
In order for this to work, however, the application must implement it's
own event loop; and, specifically, the application must also trap the
library's registration of sockets for it's own use. In order to do so,
the developer must re-implement the functionality of the library's event
loop, and re-implement the functions that the library calls to register
sockets for its own use.
Here are the OSF/RI, we are re-implementing the HTEvent() module, and
removing the HTThread module for the next release of the library. It is
our intention that the library call the HTEvent modules and not those
currently in the HTThread.c module.
The new API for the HTEvent module will be available soon as a separate
document.
Some Cautions in developing for NT and Unix
Before checking source into your local revision control system, make
sure that one remove carriage-returns from the source documents!
Otherwise, they may be seen as differences.
Set tabs stops in your local editor to 8 spaces, and forbid your editor
from coverting tabs to space (in other words, we want actual tabs in
the source files).
Testing Results
We have only tested the library using the linemode browser, although we
have tested the interface in blocking and non-blocking mode. The
Windows GUI interface is only partially implemented, and has _not_ been
tested. The new PUT and POST methods have likewise not been tested on
NT for this pre-release
Charlie Brooks
OSF Research Institute, 11 Cambridge Center, 4th Floor, Cambridge, MA
02142,
(617) 621 8758, (617) 621 8696 (FAX) cbrooks@osf.org