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 Unified Diffs Ignore Whitespace Patch

Changes to misc/althttpd.c.

259
260
261
262
263
264
265








































266
267
268
269
270
271
272
....
1135
1136
1137
1138
1139
1140
1141









































































1142
1143
1144
1145
1146
1147
1148
....
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
....
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728

1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
static int useTimeout = 1;       /* True to use times */
static int standalone = 0;       /* Run as a standalone server (no inetd) */
static int ipv6Only = 0;         /* Use IPv6 only */
static int ipv4Only = 0;         /* Use IPv4 only */
static struct rusage priorSelf;  /* Previously report SELF time */
static struct rusage priorChild; /* Previously report CHILD time */
static int mxAge = 120;          /* Cache-control max-age */









































/*
** Double any double-quote characters in a string.
*/
static char *Escape(char *z){
  int i, j;
  int n;
................................................................................
** Count the number of "/" characters in a string.
*/
static int countSlashes(const char *z){
  int n = 0;
  while( *z ) if( *(z++)=='/' ) n++;
  return n;
}










































































/*
** This routine processes a single HTTP request on standard input and
** sends the reply to standard output.  If the argument is 1 it means
** that we are should close the socket without processing additional
** HTTP requests after the current request finishes.  0 means we are
** allowed to keep the connection open and to process additional requests.
................................................................................
  /* Take appropriate action
  */
  if( (statbuf.st_mode & 0100)==0100 && access(zFile,X_OK)==0 ){
    /*
    ** The followings static variables are used to setup the environment
    ** for the CGI script
    */
    static char *default_path = "/bin:/usr/bin";
    static char *gateway_interface = "CGI/1.0";
    static struct {
      char *zEnvName;
      char **pzEnvValue;
    } cgienv[] = {
      { "AUTH_TYPE",                   &zAuthType },
      { "AUTH_CONTENT",                &zAuthArg },
      { "CONTENT_LENGTH",              &zContentLength },
      { "CONTENT_TYPE",                &zContentType },
      { "DOCUMENT_ROOT",               &zHome },
      { "GATEWAY_INTERFACE",           &gateway_interface },
      { "HTTP_ACCEPT",                 &zAccept },
      { "HTTP_ACCEPT_ENCODING",        &zAcceptEncoding },
      { "HTTP_COOKIE",                 &zCookie },
      { "HTTP_HOST",                   &zHttpHost },
      { "HTTP_IF_MODIFIED_SINCE",      &zIfModifiedSince },
      { "HTTP_IF_NONE_MATCH",          &zIfNoneMatch },
      { "HTTP_REFERER",                &zReferer },
      { "HTTP_USER_AGENT",             &zAgent },
      { "PATH",                        &default_path },
      { "PATH_INFO",                   &zPathInfo },
      { "QUERY_STRING",                &zQueryString },
      { "REMOTE_ADDR",                 &zRemoteAddr },
      { "REQUEST_METHOD",              &zMethod },
      { "REQUEST_URI",                 &zScript },
      { "REMOTE_USER",                 &zRemoteUser },
      { "SCRIPT_DIRECTORY",            &zDir },
      { "SCRIPT_FILENAME",             &zFile },
      { "SCRIPT_NAME",                 &zRealScript },
      { "SERVER_NAME",                 &zServerName },
      { "SERVER_PORT",                 &zServerPort },
      { "SERVER_PROTOCOL",             &zProtocol },
    };
    char *zBaseFilename;         /* Filename without directory prefix */
    int seenContentLength = 0;   /* True if Content-length: header seen */
    int contentLength = 0;       /* The content length */
    int nRes = 0;                /* Bytes of payload */
    int nMalloc = 0;             /* Bytes of space allocated to aRes */
    char *aRes = 0;              /* Payload */

    /* If its executable, it must be a CGI program.  Start by
    ** changing directories to the directory holding the program.
    */
    if( chdir(zDir) ){
      char zBuf[1000];
      Malfunction(420, /* LOG: chdir() failed */
................................................................................
        exit(0);
      }
      close(px[1]);
      in = fdopen(px[0], "rb");
    }
    if( in==0 ){
      CgiError();
    }

    /* Read and process the first line of the header returned by the
    ** CGI script.
    */
    if( useTimeout ) alarm(15);
    while( fgets(zLine,sizeof(zLine),in) && !isspace(zLine[0]) ){
      if( strncasecmp(zLine,"Location:",9)==0 ){
        StartResponse("302 Redirect");
        RemoveNewline(zLine);
        z = &zLine[10];
        while( isspace(*z) ){ z++; }
        nOut += printf("Location: %s\r\n",z);
      }else if( strncasecmp(zLine,"Status:",7)==0 ){
        int i;
        for(i=7; isspace(zLine[i]); i++){}
        nOut += printf("%s %s", zProtocol, &zLine[i]);
        strncpy(zReplyStatus, &zLine[i], 3);
        zReplyStatus[3] = 0;
        statusSent = 1;
      }else{
                            /*  123456789 12345 */
        if( strncasecmp(zLine, "Content-length:", 15)==0 ){
          seenContentLength = 1;
          contentLength = atoi(zLine+15);

        }
        StartResponse("200 OK");
        nOut += printf("%s",zLine);
      }
    }

    /* Copy everything else thru without change or analysis.
    */
    StartResponse("200 OK");
    if( useTimeout ) alarm(60*5);
    if( seenContentLength ){
      nOut += printf("%s", zLine);
      while( (contentLength--)>0 && (c = getc(in))!=EOF ){
        putc(c,stdout);
        nOut++;
      }
    }else{
      nRes = 0;
      nMalloc = 1000;
      aRes = malloc(nMalloc+1);
      if( aRes==0 ) Malfunction(600,"Out of memory: %d bytes", nMalloc);
      while( (c = getc(in))!=EOF ){
        if( nRes>=nMalloc ){
          nMalloc = nMalloc*2;
          aRes = realloc(aRes, nMalloc+1);
          if( aRes==0 ){
             Malfunction(610, "Out of memory: %d bytes", nMalloc);
          }
        }
        aRes[nRes++] = c;
      }
      aRes[nRes] = 0;
      nOut += printf("Content-length: %d\r\n\r\n%s", nRes, aRes);
      free(aRes);
    }
    fclose(in);
  }else if( countSlashes(zRealScript)!=countSlashes(zScript) ){
    /* If the request URI for static content contains material past the
    ** actual content file name, report that as a 404 error. */
    NotFound(460); /* LOG: Excess URI content past static file name */
  }else{
    /* If it isn't executable then it
    ** must a simple file that needs to be copied to output.







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

<
<
<
<
<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
....
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
....
1686
1687
1688
1689
1690
1691
1692


































1693





1694
1695
1696
1697
1698
1699
1700
....
1771
1772
1773
1774
1775
1776
1777




















1778




1779
1780



































1781
1782
1783
1784
1785
1786
1787
static int useTimeout = 1;       /* True to use times */
static int standalone = 0;       /* Run as a standalone server (no inetd) */
static int ipv6Only = 0;         /* Use IPv6 only */
static int ipv4Only = 0;         /* Use IPv4 only */
static struct rusage priorSelf;  /* Previously report SELF time */
static struct rusage priorChild; /* Previously report CHILD time */
static int mxAge = 120;          /* Cache-control max-age */
static char *default_path = "/bin:/usr/bin";  /* Default PATH variable */
static char *gateway_interface = "CGI/1.0";   /* CGI version number */

/*
** Mapping between CGI variable names and values stored in
** global variables.
*/
static struct {
  char *zEnvName;
  char **pzEnvValue;
} cgienv[] = {
  { "AUTH_TYPE",                   &zAuthType },
  { "AUTH_CONTENT",                &zAuthArg },
  { "CONTENT_LENGTH",              &zContentLength },
  { "CONTENT_TYPE",                &zContentType },
  { "DOCUMENT_ROOT",               &zHome },
  { "GATEWAY_INTERFACE",           &gateway_interface },
  { "HTTP_ACCEPT",                 &zAccept },
  { "HTTP_ACCEPT_ENCODING",        &zAcceptEncoding },
  { "HTTP_COOKIE",                 &zCookie },
  { "HTTP_HOST",                   &zHttpHost },
  { "HTTP_IF_MODIFIED_SINCE",      &zIfModifiedSince },
  { "HTTP_IF_NONE_MATCH",          &zIfNoneMatch },
  { "HTTP_REFERER",                &zReferer },
  { "HTTP_USER_AGENT",             &zAgent },
  { "PATH",                        &default_path },
  { "PATH_INFO",                   &zPathInfo },
  { "QUERY_STRING",                &zQueryString },
  { "REMOTE_ADDR",                 &zRemoteAddr },
  { "REQUEST_METHOD",              &zMethod },
  { "REQUEST_URI",                 &zScript },
  { "REMOTE_USER",                 &zRemoteUser },
  { "SCRIPT_DIRECTORY",            &zDir },
  { "SCRIPT_FILENAME",             &zFile },
  { "SCRIPT_NAME",                 &zRealScript },
  { "SERVER_NAME",                 &zServerName },
  { "SERVER_PORT",                 &zServerPort },
  { "SERVER_PROTOCOL",             &zProtocol },
};


/*
** Double any double-quote characters in a string.
*/
static char *Escape(char *z){
  int i, j;
  int n;
................................................................................
** Count the number of "/" characters in a string.
*/
static int countSlashes(const char *z){
  int n = 0;
  while( *z ) if( *(z++)=='/' ) n++;
  return n;
}

/*
** A CGI or SCGI script has run and is sending its reply back across
** the channel "in".  Process this reply into an appropriate HTTP reply.
** Close the "in" channel when done.
*/
static void CgiHandleReply(FILE *in){
  int seenContentLength = 0;   /* True if Content-length: header seen */
  int contentLength = 0;       /* The content length */
  int nRes = 0;                /* Bytes of payload */
  int nMalloc = 0;             /* Bytes of space allocated to aRes */
  char *aRes = 0;              /* Payload */
  int c;                       /* Next character from in */
  char *z;                     /* Pointer to something inside of zLine */
  char zLine[1000];            /* One line of reply from the CGI script */

  if( useTimeout ) alarm(15);
  while( fgets(zLine,sizeof(zLine),in) && !isspace(zLine[0]) ){
    if( strncasecmp(zLine,"Location:",9)==0 ){
      StartResponse("302 Redirect");
      RemoveNewline(zLine);
      z = &zLine[10];
      while( isspace(*z) ){ z++; }
      nOut += printf("Location: %s\r\n",z);
    }else if( strncasecmp(zLine,"Status:",7)==0 ){
      int i;
      for(i=7; isspace(zLine[i]); i++){}
      nOut += printf("%s %s", zProtocol, &zLine[i]);
      strncpy(zReplyStatus, &zLine[i], 3);
      zReplyStatus[3] = 0;
      statusSent = 1;
    }else{
                          /*  123456789 12345 */
      if( strncasecmp(zLine, "Content-length:", 15)==0 ){
        seenContentLength = 1;
        contentLength = atoi(zLine+15);
      }
      StartResponse("200 OK");
      nOut += printf("%s",zLine);
    }
  }

  /* Copy everything else thru without change or analysis.
  */
  StartResponse("200 OK");
  if( useTimeout ) alarm(60*5);
  if( seenContentLength ){
    nOut += printf("%s", zLine);
    while( (contentLength--)>0 && (c = getc(in))!=EOF ){
      putc(c,stdout);
      nOut++;
    }
  }else{
    nRes = 0;
    nMalloc = 1000;
    aRes = malloc(nMalloc+1);
    if( aRes==0 ) Malfunction(600,"Out of memory: %d bytes", nMalloc);
    while( (c = getc(in))!=EOF ){
      if( nRes>=nMalloc ){
        nMalloc = nMalloc*2;
        aRes = realloc(aRes, nMalloc+1);
        if( aRes==0 ){
           Malfunction(610, "Out of memory: %d bytes", nMalloc);
        }
      }
      aRes[nRes++] = c;
    }
    aRes[nRes] = 0;
    nOut += printf("Content-length: %d\r\n\r\n%s", nRes, aRes);
    free(aRes);
  }
  fclose(in);
}

/*
** This routine processes a single HTTP request on standard input and
** sends the reply to standard output.  If the argument is 1 it means
** that we are should close the socket without processing additional
** HTTP requests after the current request finishes.  0 means we are
** allowed to keep the connection open and to process additional requests.
................................................................................
  /* Take appropriate action
  */
  if( (statbuf.st_mode & 0100)==0100 && access(zFile,X_OK)==0 ){
    /*
    ** The followings static variables are used to setup the environment
    ** for the CGI script
    */


































    char *zBaseFilename;         /* Filename without directory prefix */






    /* If its executable, it must be a CGI program.  Start by
    ** changing directories to the directory holding the program.
    */
    if( chdir(zDir) ){
      char zBuf[1000];
      Malfunction(420, /* LOG: chdir() failed */
................................................................................
        exit(0);
      }
      close(px[1]);
      in = fdopen(px[0], "rb");
    }
    if( in==0 ){
      CgiError();




















    }else{




      CgiHandleReply(in);
    }



































  }else if( countSlashes(zRealScript)!=countSlashes(zScript) ){
    /* If the request URI for static content contains material past the
    ** actual content file name, report that as a 404 error. */
    NotFound(460); /* LOG: Excess URI content past static file name */
  }else{
    /* If it isn't executable then it
    ** must a simple file that needs to be copied to output.