View Ticket
Not logged in
Ticket UUID: 2384764107f0d084867503e16461f18b80f72e24
Title: Make the http command working on Windows.
Status: Open Type: Feature_Request
Severity: Important Priority: Medium
Subsystem: Resolution: Open
Last Modified: 2009-09-21 00:37:14
Version Found In: 0eb08b860c
Description & Comments:
This would make it possible to use Fossil with Inetd like servers on Windows, for example http://www.xmailserver.org/wininetd.html.

The following patch against Fossil version 0eb08b860c implements this functionality:

diff -Naur fossil-src/src/cgi.c fossil-src-mod/src/cgi.c
--- fossil-src/src/cgi.c	2009-09-13 09:37:48 +0000
+++ fossil-src-mod/src/cgi.c	2009-09-17 11:25:47 +0000
@@ -29,10 +29,11 @@
 */
 #include "config.h"
 #ifdef __MINGW32__
-#  include <windows.h>           /* for Sleep once server works again */
-#  include <winsock2.h>          /* socket operations */
-#  define sleep Sleep            /* windows does not have sleep, but Sleep */
-#  include <ws2tcpip.h>          
+#  include <windows.h>           /* Windows specific declarations */
+#  include <winsock2.h>          /* Windows socket operations */
+#  include <ws2tcpip.h>          /* for socklen_t */
+#  include <fcntl.h>
+#  include <io.h>
 #else
 #  include <sys/socket.h>
 #  include <netinet/in.h>
@@ -1076,6 +1077,95 @@
   return zResult;
 }
 
+#ifdef __MINGW32__
+/*
+** Data structure related to all variables for the windows socket environment.
+*/
+typedef struct WinSocketData {
+  int     fWSAInit;       /* True, if Windows sockets initialized */
+  SOCKET  hSocket;        /* Handle to the socket */
+} WinSocketData;
+
+static WinSocketData wsd;
+
+/*
+** Exit handler routine. Make sure any remaining output gets flushed to the
+** output stream. If the standard handles are redirected to a socket then
+** make shure the socket gets closed properly and clean up the windows socket
+** environment.
+*/
+static void cgi_handle_http_request_exit(void)
+{
+  fflush(g.httpOut);
+
+  if( wsd.hSocket != INVALID_SOCKET ){
+    shutdown(wsd.hSocket, SD_BOTH);
+    closesocket(wsd.hSocket);
+  }
+  if (wsd.fWSAInit) {
+    WSACleanup();
+  }
+  return;
+}
+
+/*
+** This routine initalizes the environment on windows for the
+** cgi_handle_http_request routine. It sets the standard input and ouput
+** handles to binary mode, initalizes the windows socket environment and tries
+** to get the socket handle from the standard handles. It also establishs a
+** exit routine to clean up on program exit.
+*/
+void cgi_handle_http_request_init(void)
+{
+  WSADATA wd;
+  SOCKET hSock;
+  int socket_type;
+  int socket_type_len = sizeof(socket_type);
+
+  /*
+  ** Initialize the windows socket data structure.
+  */
+  wsd.fWSAInit = 0;
+  wsd.hSocket = INVALID_SOCKET;
+
+  /*
+  ** Register the exit function.
+  */
+  atexit(cgi_handle_http_request_exit);
+
+  /*
+  ** Set the mode of the http input and output streams to binary.
+  */
+  _setmode(_fileno(g.httpIn),  _O_BINARY);
+  _setmode(_fileno(g.httpOut), _O_BINARY);
+
+  /*
+  ** Initialize the windows socket API. This is required if we need to
+  ** call any other Windows Socket function.
+  */
+  if( WSAStartup(MAKEWORD(2,2), &wd) == 0 ) wsd.fWSAInit = 1;
+
+  /*
+  ** Windows Socket handles must be handled differently than file handles.
+  ** There is no function to detect if a file handle is a socket or not, so
+  ** lets call a socket function with the handle from stdin, and if there is
+  ** no error, assume it is a socket!
+  */
+  if( wsd.fWSAInit ){
+    hSock = (SOCKET)_get_osfhandle(_fileno(stdin));
+    if( getsockopt (hSock, SOL_SOCKET, SO_TYPE,
+                    (char *)&socket_type, &socket_type_len) == 0 ){
+      wsd.hSocket = hSock;
+    }
+  }
+  return;
+}
+
+#  define CGI_SOCKET wsd.hSocket
+#else
+#  define CGI_SOCKET fileno(g.httpIn)
+#endif /* __MINGW32__ */
+
 /*
 ** This routine handles a single HTTP request which is coming in on
 ** standard input and which replies on standard output.
@@ -1092,6 +1182,9 @@
   size_t size = sizeof(struct sockaddr_in);
   char zLine[2000];     /* A single line of input. */
 
+#ifdef __MINGW32__
+  cgi_handle_http_request_init();
+#endif
   g.fullHttpReply = 1;
   if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
     malformed_request();
@@ -1116,7 +1209,7 @@
   cgi_setenv("PATH_INFO", zToken);
   cgi_setenv("QUERY_STRING", &zToken[i]);
   if( zIpAddr==0 &&
-        getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName, 
+        getpeername(CGI_SOCKET, (struct sockaddr*)&remoteName, 
                                 (socklen_t*)&size)>=0
   ){
     zIpAddr = inet_ntoa(remoteName.sin_addr);

--tsbg


anonymous added on 2009-09-19 00:36:49:
From reference: " Usage: fossil http REPOSITORY Handle a single HTTP request appearing on stdin. The resulting webpage is delivered on stdout. This method is used to launch an HTTP request handler from inetd, for example. The argument is the name of the repository. "

Test with current Fossil under Windows Vista:

================================================================
E:\dl\fossil-w32-20090915120431>fossil.exe http myclone.fossil
GET / HTTP/1.0
Host: localhost

HTTP/1.0 302 Moved Temporarily
Date: Sat, 19 Sep 2009 00:30:23 GMT
Connection: close
Location: http://localhost/doc/tip/www/index.wiki
Cache-control: no-cache, no-store
Content-Type: text/html; charset=utf-8
Content-Length: 74

<html>
<p>Redirect to http://localhost/doc/tip/www/index.wiki</p>
</html>
=======================

So it seems behave right, like declared.


anonymous claiming to be tsbg added on 2009-09-19 08:46:37:
Yes it does, but only when stdin and stdout are not redirected to a socket. Windows treats file and socket handles differently. Another problem is, you cant't call socket functions like getpeername without initializing the windows socket environment.

Try it with the above mentioned WinInetd server, and you will see no output until you apply the patch.

--tsbg


anonymous claiming to be Andrey added on 2009-09-21 00:37:14:
Seems this is a problem of WinInetd. It should not redirect files to sockets, it should copy between file handle and socket - just like any web-server does when running CGI application. It should not depend of ability of console app to operate with sockets and even know something about sockets. (sorry for my poor English)