/*****************************************************************************/ /* CGIplus_gSOAP.c This is a small module providing FastCGI-equivalent functionality for persistent (low-latency, high efficiency) SOAP applications using native WASD CGIplus. It obviously avoids the fuss of building for, and deploying under, FastCGI. Just replace the function calls FCGI_InitVMS() with CGIplus_InitVMS() and FCGI_Accept() with CGIplus_Accept() and you're in business. gSOAP will then use the CGI request processing mechanisms getenv() and but of course without the CGI overheads! For example: #include "CGIplus_gSOAP.h" int main (int argc, char **argv) { struct soap soap; CGIplus_InitVMS (argc, argv); soap_init1 (&soap, SOAP_IO_BUFFER); while (CGIplus_Accept() >= 0) { soap_serve (&soap); soap_end (&soap); } exit (0); } To explicitly run-down the persistent application from within its own code call CGIplus_Exit() and then use the application's normal exit routine. This function just elegantly informs the server the current request has concluded. To build just compile $ CC /NAME=AS_IS /PREFIX=ALL CGIPLUS_GSOAP.C and then link the object module into the SOAP application. Of course the application must be accessed via a path or script name mapped for CGIplus, for example: # WASD_CONFIG_MAP (or HTTPD$MAP) exec+ /cgiplus-bin/* /cgi-bin/* NOTE ---- When testing this wrapper it was noted that application (scripting) process working set and virtual pages steadily increased indicating a small memory leak. However, this wrapper basically only uses C-RTL setenv(), unsetenv() and fread(), in addition to the SOAP application itself, suggesting the leak is somewhere other than the wrapper! On the test-bench some 50,000 requests were passed through the 'calc' (originally FastCGI) sample application without failure. UPDATE: The gSOAPrte (shareable image) versions of sample gSOAP applications show no increase in virtual pages or working set (even after 1,000,000 requests) pointing the finger more emphatically at the the C-RTL, probably the setenv()/unsetenv()/getenv() functions. COPYRIGHT --------- Copyright (C) 2010-2013 Mark G.Daniel This program, comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under the conditions of the GNU GENERAL PUBLIC LICENSE, version 3, or any later version. http://www.gnu.org/licenses/gpl.txt VERSION LOG ----------- 14-AUG-2013 MGD v1.1.2, bugfix; add "ctx=bin" to freopen("SYS$OUTPUT") 02-APR-2013 MGD v1.1.1, bugfix; callout for explicit stream mode 20-MAR-2010 MGD v1.1.0, CGIplus_Exit() for explicit application run-down 13-MAR-2010 MGD v1.0.0, initial */ /*****************************************************************************/ /* minimum VMS V7.3 */ #undef __VMS_VER #define __VMS_VER 70300000 #undef __CRTL_VER #define __CRTL_VER 70300000 #define SOFTWAREVN "1.1.2" #define SOFTWARENM "CGIPLUS_GSOAP" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN #endif #ifdef __VAX # define SOFTWAREID SOFTWARENM " VAX-" SOFTWAREVN #endif #include #include #include #include #include /* mainly to allow easy use of the __unaligned directive */ #define ULONGPTR __unaligned unsigned long* #define USHORTPTR __unaligned unsigned short* /*****************************************************************************/ /* Modify the streams to suit. */ int CGIplus_InitVMS ( int argc, char **argv ) { /*********/ /* begin */ /*********/ if ((stdin = freopen ("HTTP$INPUT", "r", stdin, "ctx=bin")) == NULL) exit (vaxc$errno); if ((stdout = freopen ("SYS$OUTPUT", "w", stdout, "ctx=bin", "ctx=xplct")) == NULL) exit (vaxc$errno); setenv ("GATEWAY_API", SOFTWAREID, 1); return (0); } /*****************************************************************************/ /* If a previous request has been processed then output the CGIplus end-of-stream sentinal on behalf of that request. Then remove (unsetenv()) each of the previous CGI variable values from the C-RTL environment. Wait for the next (or just begin at the first) request's CGI variable stream. Populate the C-RTL environment array (setenv()) with those values. */ int CGIplus_Accept () { # ifndef CGIVAR_STRUCT_SIZE # define CGIVAR_STRUCT_SIZE 8192 # endif # ifndef CGIVAR_NONE # define CGIVAR_NONE NULL # endif # define SOUS sizeof(unsigned short) #define SS$_BUGCHECK 676 static int CgiPlusCheck, CalloutDone, WatchScript, WwwPrefix; static char *CgiPlusEof, *CgiPlusEot = NULL, *CgiPlusEsc = NULL; static char StructBuffer [CGIVAR_STRUCT_SIZE]; static FILE *CgiPlusIn; int len; char *bptr, *cptr, *sptr; /*********/ /* begin */ /*********/ /* detect the CGIplus environment (once) */ if (!CgiPlusCheck) { CgiPlusCheck = 1; /* don't really need the initialisation call */ if (getenv ("GATEWAY_API") == NULL) CGIplus_InitVMS (0, NULL); if ((CgiPlusEof = getenv ("CGIPLUSEOF")) == NULL) return (0); } /* if vanilla CGI then allow just one invocation */ if (CgiPlusEof == NULL) return (-1); /* the CGIPLUSIN stream will be left open */ if (CgiPlusIn == NULL) if ((CgiPlusIn = fopen (getenv("CGIPLUSIN"), "r")) == NULL) exit (vaxc$errno); if (StructBuffer[0]) { /***************/ /* CGIplus EOF */ /***************/ fflush (stdout); fputs (CgiPlusEof, stdout); fflush (stdout); /******************/ /* reset previous */ /******************/ for (bptr = StructBuffer; len = *(USHORTPTR)bptr; bptr += len) { cptr = (bptr += SOUS); if (WwwPrefix) cptr += 4; unsetenv (cptr); } } /*****************************/ /* get the CGIplus variables */ /*****************************/ /* get the starting record (the essentially discardable one) */ for (;;) { cptr = fgets (StructBuffer, sizeof(StructBuffer), CgiPlusIn); if (cptr == NULL) exit (vaxc$errno); /* if the starting sentinal is detected then break */ if (*(USHORTPTR)cptr == '!\0' || *(USHORTPTR)cptr == '!\n' || (*(USHORTPTR)cptr == '!!' && isdigit(*(cptr+2)))) break; } if (*(USHORTPTR)cptr == '!!') { /********************/ /* CGIplus 'struct' */ /********************/ /* get the size of the binary structure */ len = atoi(cptr+2); if (len <= 0 || len > sizeof(StructBuffer)) exit (SS$_BUGCHECK); if (!fread (StructBuffer, 1, len, CgiPlusIn)) exit (vaxc$errno); } else { /*********************/ /* CGIplus 'records' */ /*********************/ /* reconstructs the original 'struct'ure from the records */ sptr = (bptr = StructBuffer) + sizeof(StructBuffer); while (fgets (bptr+SOUS, sptr-(bptr+SOUS), CgiPlusIn) != NULL) { /* first empty record (line) terminates variables */ if (bptr[SOUS] == '\n') break; /* note the location of the length word */ cptr = bptr; for (bptr += SOUS; *bptr && *bptr != '\n'; bptr++); if (*bptr != '\n') exit (SS$_BUGCHECK); *bptr++ = '\0'; if (bptr >= sptr) exit (SS$_BUGCHECK); /* update the length word */ *(USHORTPTR)cptr = bptr - (cptr + SOUS); } if (bptr >= sptr) exit (SS$_BUGCHECK); /* terminate with a zero-length entry */ *(USHORTPTR)bptr = 0; len = (bptr + SOUS) - StructBuffer; } if (!CalloutDone) { /*****************/ /* 'struct' mode */ /*****************/ /* provide the CGI callout to set CGIplus into 'struct' mode */ fflush (stdout); fputs (getenv("CGIPLUSESC"), stdout); fflush (stdout); /* the leading '!' indicates we're not going to read the response */ fputs ("!CGIPLUS: struct", stdout); fflush (stdout); fputs (getenv("CGIPLUSEOT"), stdout); fflush (stdout); /* don't need to do this again (the '!!' tells us what mode) */ CalloutDone = 1; } /**************/ /* set values */ /**************/ sptr = StructBuffer + SOUS; if (*(ULONGPTR)sptr == 'WWW_') WwwPrefix = 1; for (bptr = StructBuffer; len = *(USHORTPTR)bptr; bptr += len) { cptr = sptr = (bptr += SOUS); while (*sptr && *sptr != '=') sptr++; if (!*sptr) exit (SS$_BUGCHECK); *sptr++ = '\0'; if (WwwPrefix) cptr += 4; setenv (cptr, sptr, 1); } /* generate a callout to ensure output is treated stream not record mode */ fflush (stdout); fputs (getenv("CGIPLUSESC"), stdout); fflush (stdout); /* the leading '!' indicates we're not going to read the response */ fputs ("!SCRIPT-CONTROL: X-stream-mode", stdout); fflush (stdout); fputs (getenv("CGIPLUSEOT"), stdout); fflush (stdout); return (0); # undef SOUS } /*****************************************************************************/ /* Just output the end-of-CGIplus sentinal. */ void CGIplus_Exit () { /*********/ /* begin */ /*********/ fflush (stdout); fputs (getenv("CGIPLUSEOF"), stdout); fflush (stdout); } /*****************************************************************************/