Documentation Source Text

Check-in [3e667aef3a]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:In althttpd.c, refactor some of the CGI processing logic as a preliminary step toward adding SCGI support.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256:3e667aef3a1ffab709868ee1ca17a07911bdb7d8e8050cb7ffc6c32fd818a1c8
User & Date: drh 2019-02-15 18:16:17
Context
2019-02-15
20:31
Add SCGI support to the althttpd.c web server. check-in: 201d18b836 user: drh tags: trunk
20:14
Preliminary support for SCGI in althttpd. check-in: b3aaed91ba user: drh tags: althttpd-scgi
18:16
In althttpd.c, refactor some of the CGI processing logic as a preliminary step toward adding SCGI support. check-in: 3e667aef3a user: drh tags: trunk
17:00
Enhancements to althttpd.c: Add the --input FILE command-line option to simplify debugging using lldb. Improvements to comments. check-in: a537e6f3fc user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to misc/althttpd.c.

   259    259   static int useTimeout = 1;       /* True to use times */
   260    260   static int standalone = 0;       /* Run as a standalone server (no inetd) */
   261    261   static int ipv6Only = 0;         /* Use IPv6 only */
   262    262   static int ipv4Only = 0;         /* Use IPv4 only */
   263    263   static struct rusage priorSelf;  /* Previously report SELF time */
   264    264   static struct rusage priorChild; /* Previously report CHILD time */
   265    265   static int mxAge = 120;          /* Cache-control max-age */
          266  +static char *default_path = "/bin:/usr/bin";  /* Default PATH variable */
          267  +static char *gateway_interface = "CGI/1.0";   /* CGI version number */
          268  +
          269  +/*
          270  +** Mapping between CGI variable names and values stored in
          271  +** global variables.
          272  +*/
          273  +static struct {
          274  +  char *zEnvName;
          275  +  char **pzEnvValue;
          276  +} cgienv[] = {
          277  +  { "AUTH_TYPE",                   &zAuthType },
          278  +  { "AUTH_CONTENT",                &zAuthArg },
          279  +  { "CONTENT_LENGTH",              &zContentLength },
          280  +  { "CONTENT_TYPE",                &zContentType },
          281  +  { "DOCUMENT_ROOT",               &zHome },
          282  +  { "GATEWAY_INTERFACE",           &gateway_interface },
          283  +  { "HTTP_ACCEPT",                 &zAccept },
          284  +  { "HTTP_ACCEPT_ENCODING",        &zAcceptEncoding },
          285  +  { "HTTP_COOKIE",                 &zCookie },
          286  +  { "HTTP_HOST",                   &zHttpHost },
          287  +  { "HTTP_IF_MODIFIED_SINCE",      &zIfModifiedSince },
          288  +  { "HTTP_IF_NONE_MATCH",          &zIfNoneMatch },
          289  +  { "HTTP_REFERER",                &zReferer },
          290  +  { "HTTP_USER_AGENT",             &zAgent },
          291  +  { "PATH",                        &default_path },
          292  +  { "PATH_INFO",                   &zPathInfo },
          293  +  { "QUERY_STRING",                &zQueryString },
          294  +  { "REMOTE_ADDR",                 &zRemoteAddr },
          295  +  { "REQUEST_METHOD",              &zMethod },
          296  +  { "REQUEST_URI",                 &zScript },
          297  +  { "REMOTE_USER",                 &zRemoteUser },
          298  +  { "SCRIPT_DIRECTORY",            &zDir },
          299  +  { "SCRIPT_FILENAME",             &zFile },
          300  +  { "SCRIPT_NAME",                 &zRealScript },
          301  +  { "SERVER_NAME",                 &zServerName },
          302  +  { "SERVER_PORT",                 &zServerPort },
          303  +  { "SERVER_PROTOCOL",             &zProtocol },
          304  +};
          305  +
   266    306   
   267    307   /*
   268    308   ** Double any double-quote characters in a string.
   269    309   */
   270    310   static char *Escape(char *z){
   271    311     int i, j;
   272    312     int n;
................................................................................
  1135   1175   ** Count the number of "/" characters in a string.
  1136   1176   */
  1137   1177   static int countSlashes(const char *z){
  1138   1178     int n = 0;
  1139   1179     while( *z ) if( *(z++)=='/' ) n++;
  1140   1180     return n;
  1141   1181   }
         1182  +
         1183  +/*
         1184  +** A CGI or SCGI script has run and is sending its reply back across
         1185  +** the channel "in".  Process this reply into an appropriate HTTP reply.
         1186  +** Close the "in" channel when done.
         1187  +*/
         1188  +static void CgiHandleReply(FILE *in){
         1189  +  int seenContentLength = 0;   /* True if Content-length: header seen */
         1190  +  int contentLength = 0;       /* The content length */
         1191  +  int nRes = 0;                /* Bytes of payload */
         1192  +  int nMalloc = 0;             /* Bytes of space allocated to aRes */
         1193  +  char *aRes = 0;              /* Payload */
         1194  +  int c;                       /* Next character from in */
         1195  +  char *z;                     /* Pointer to something inside of zLine */
         1196  +  char zLine[1000];            /* One line of reply from the CGI script */
         1197  +
         1198  +  if( useTimeout ) alarm(15);
         1199  +  while( fgets(zLine,sizeof(zLine),in) && !isspace(zLine[0]) ){
         1200  +    if( strncasecmp(zLine,"Location:",9)==0 ){
         1201  +      StartResponse("302 Redirect");
         1202  +      RemoveNewline(zLine);
         1203  +      z = &zLine[10];
         1204  +      while( isspace(*z) ){ z++; }
         1205  +      nOut += printf("Location: %s\r\n",z);
         1206  +    }else if( strncasecmp(zLine,"Status:",7)==0 ){
         1207  +      int i;
         1208  +      for(i=7; isspace(zLine[i]); i++){}
         1209  +      nOut += printf("%s %s", zProtocol, &zLine[i]);
         1210  +      strncpy(zReplyStatus, &zLine[i], 3);
         1211  +      zReplyStatus[3] = 0;
         1212  +      statusSent = 1;
         1213  +    }else{
         1214  +                          /*  123456789 12345 */
         1215  +      if( strncasecmp(zLine, "Content-length:", 15)==0 ){
         1216  +        seenContentLength = 1;
         1217  +        contentLength = atoi(zLine+15);
         1218  +      }
         1219  +      StartResponse("200 OK");
         1220  +      nOut += printf("%s",zLine);
         1221  +    }
         1222  +  }
         1223  +
         1224  +  /* Copy everything else thru without change or analysis.
         1225  +  */
         1226  +  StartResponse("200 OK");
         1227  +  if( useTimeout ) alarm(60*5);
         1228  +  if( seenContentLength ){
         1229  +    nOut += printf("%s", zLine);
         1230  +    while( (contentLength--)>0 && (c = getc(in))!=EOF ){
         1231  +      putc(c,stdout);
         1232  +      nOut++;
         1233  +    }
         1234  +  }else{
         1235  +    nRes = 0;
         1236  +    nMalloc = 1000;
         1237  +    aRes = malloc(nMalloc+1);
         1238  +    if( aRes==0 ) Malfunction(600,"Out of memory: %d bytes", nMalloc);
         1239  +    while( (c = getc(in))!=EOF ){
         1240  +      if( nRes>=nMalloc ){
         1241  +        nMalloc = nMalloc*2;
         1242  +        aRes = realloc(aRes, nMalloc+1);
         1243  +        if( aRes==0 ){
         1244  +           Malfunction(610, "Out of memory: %d bytes", nMalloc);
         1245  +        }
         1246  +      }
         1247  +      aRes[nRes++] = c;
         1248  +    }
         1249  +    aRes[nRes] = 0;
         1250  +    nOut += printf("Content-length: %d\r\n\r\n%s", nRes, aRes);
         1251  +    free(aRes);
         1252  +  }
         1253  +  fclose(in);
         1254  +}
  1142   1255   
  1143   1256   /*
  1144   1257   ** This routine processes a single HTTP request on standard input and
  1145   1258   ** sends the reply to standard output.  If the argument is 1 it means
  1146   1259   ** that we are should close the socket without processing additional
  1147   1260   ** HTTP requests after the current request finishes.  0 means we are
  1148   1261   ** allowed to keep the connection open and to process additional requests.
................................................................................
  1573   1686     /* Take appropriate action
  1574   1687     */
  1575   1688     if( (statbuf.st_mode & 0100)==0100 && access(zFile,X_OK)==0 ){
  1576   1689       /*
  1577   1690       ** The followings static variables are used to setup the environment
  1578   1691       ** for the CGI script
  1579   1692       */
  1580         -    static char *default_path = "/bin:/usr/bin";
  1581         -    static char *gateway_interface = "CGI/1.0";
  1582         -    static struct {
  1583         -      char *zEnvName;
  1584         -      char **pzEnvValue;
  1585         -    } cgienv[] = {
  1586         -      { "AUTH_TYPE",                   &zAuthType },
  1587         -      { "AUTH_CONTENT",                &zAuthArg },
  1588         -      { "CONTENT_LENGTH",              &zContentLength },
  1589         -      { "CONTENT_TYPE",                &zContentType },
  1590         -      { "DOCUMENT_ROOT",               &zHome },
  1591         -      { "GATEWAY_INTERFACE",           &gateway_interface },
  1592         -      { "HTTP_ACCEPT",                 &zAccept },
  1593         -      { "HTTP_ACCEPT_ENCODING",        &zAcceptEncoding },
  1594         -      { "HTTP_COOKIE",                 &zCookie },
  1595         -      { "HTTP_HOST",                   &zHttpHost },
  1596         -      { "HTTP_IF_MODIFIED_SINCE",      &zIfModifiedSince },
  1597         -      { "HTTP_IF_NONE_MATCH",          &zIfNoneMatch },
  1598         -      { "HTTP_REFERER",                &zReferer },
  1599         -      { "HTTP_USER_AGENT",             &zAgent },
  1600         -      { "PATH",                        &default_path },
  1601         -      { "PATH_INFO",                   &zPathInfo },
  1602         -      { "QUERY_STRING",                &zQueryString },
  1603         -      { "REMOTE_ADDR",                 &zRemoteAddr },
  1604         -      { "REQUEST_METHOD",              &zMethod },
  1605         -      { "REQUEST_URI",                 &zScript },
  1606         -      { "REMOTE_USER",                 &zRemoteUser },
  1607         -      { "SCRIPT_DIRECTORY",            &zDir },
  1608         -      { "SCRIPT_FILENAME",             &zFile },
  1609         -      { "SCRIPT_NAME",                 &zRealScript },
  1610         -      { "SERVER_NAME",                 &zServerName },
  1611         -      { "SERVER_PORT",                 &zServerPort },
  1612         -      { "SERVER_PROTOCOL",             &zProtocol },
  1613         -    };
  1614   1693       char *zBaseFilename;         /* Filename without directory prefix */
  1615         -    int seenContentLength = 0;   /* True if Content-length: header seen */
  1616         -    int contentLength = 0;       /* The content length */
  1617         -    int nRes = 0;                /* Bytes of payload */
  1618         -    int nMalloc = 0;             /* Bytes of space allocated to aRes */
  1619         -    char *aRes = 0;              /* Payload */
  1620   1694   
  1621   1695       /* If its executable, it must be a CGI program.  Start by
  1622   1696       ** changing directories to the directory holding the program.
  1623   1697       */
  1624   1698       if( chdir(zDir) ){
  1625   1699         char zBuf[1000];
  1626   1700         Malfunction(420, /* LOG: chdir() failed */
................................................................................
  1697   1771           exit(0);
  1698   1772         }
  1699   1773         close(px[1]);
  1700   1774         in = fdopen(px[0], "rb");
  1701   1775       }
  1702   1776       if( in==0 ){
  1703   1777         CgiError();
  1704         -    }
  1705         -
  1706         -    /* Read and process the first line of the header returned by the
  1707         -    ** CGI script.
  1708         -    */
  1709         -    if( useTimeout ) alarm(15);
  1710         -    while( fgets(zLine,sizeof(zLine),in) && !isspace(zLine[0]) ){
  1711         -      if( strncasecmp(zLine,"Location:",9)==0 ){
  1712         -        StartResponse("302 Redirect");
  1713         -        RemoveNewline(zLine);
  1714         -        z = &zLine[10];
  1715         -        while( isspace(*z) ){ z++; }
  1716         -        nOut += printf("Location: %s\r\n",z);
  1717         -      }else if( strncasecmp(zLine,"Status:",7)==0 ){
  1718         -        int i;
  1719         -        for(i=7; isspace(zLine[i]); i++){}
  1720         -        nOut += printf("%s %s", zProtocol, &zLine[i]);
  1721         -        strncpy(zReplyStatus, &zLine[i], 3);
  1722         -        zReplyStatus[3] = 0;
  1723         -        statusSent = 1;
  1724         -      }else{
  1725         -                            /*  123456789 12345 */
  1726         -        if( strncasecmp(zLine, "Content-length:", 15)==0 ){
  1727         -          seenContentLength = 1;
  1728         -          contentLength = atoi(zLine+15);
  1729         -        }
  1730         -        StartResponse("200 OK");
  1731         -        nOut += printf("%s",zLine);
  1732         -      }
  1733         -    }
  1734         -
  1735         -    /* Copy everything else thru without change or analysis.
  1736         -    */
  1737         -    StartResponse("200 OK");
  1738         -    if( useTimeout ) alarm(60*5);
  1739         -    if( seenContentLength ){
  1740         -      nOut += printf("%s", zLine);
  1741         -      while( (contentLength--)>0 && (c = getc(in))!=EOF ){
  1742         -        putc(c,stdout);
  1743         -        nOut++;
  1744         -      }
  1745   1778       }else{
  1746         -      nRes = 0;
  1747         -      nMalloc = 1000;
  1748         -      aRes = malloc(nMalloc+1);
  1749         -      if( aRes==0 ) Malfunction(600,"Out of memory: %d bytes", nMalloc);
  1750         -      while( (c = getc(in))!=EOF ){
  1751         -        if( nRes>=nMalloc ){
  1752         -          nMalloc = nMalloc*2;
  1753         -          aRes = realloc(aRes, nMalloc+1);
  1754         -          if( aRes==0 ){
  1755         -             Malfunction(610, "Out of memory: %d bytes", nMalloc);
  1756         -          }
  1757         -        }
  1758         -        aRes[nRes++] = c;
  1759         -      }
  1760         -      aRes[nRes] = 0;
  1761         -      nOut += printf("Content-length: %d\r\n\r\n%s", nRes, aRes);
  1762         -      free(aRes);
         1779  +      CgiHandleReply(in);
  1763   1780       }
  1764         -    fclose(in);
  1765   1781     }else if( countSlashes(zRealScript)!=countSlashes(zScript) ){
  1766   1782       /* If the request URI for static content contains material past the
  1767   1783       ** actual content file name, report that as a 404 error. */
  1768   1784       NotFound(460); /* LOG: Excess URI content past static file name */
  1769   1785     }else{
  1770   1786       /* If it isn't executable then it
  1771   1787       ** must a simple file that needs to be copied to output.