Fossil

Check-in [39a899e4cf]
Login

Check-in [39a899e4cf]

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

Overview
Comment:Initial code for implementing the idle-timeout feature for "fossil ui". Seems to work in preliminary tests on unix. Windows implementation is incomplete and untested.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | idle-timeout
Files: files | file ages | folders
SHA3-256: 39a899e4cf7da42e2f31802488160385ba134527525fa7d2c306691e03237c88
User & Date: drh 2020-04-09 16:14:27.239
Context
2020-04-09
16:16
Forgot to add the "keepalive.js" source file. ... (check-in: 58dbde20c4 user: drh tags: idle-timeout)
16:14
Initial code for implementing the idle-timeout feature for "fossil ui". Seems to work in preliminary tests on unix. Windows implementation is incomplete and untested. ... (check-in: 39a899e4cf user: drh tags: idle-timeout)
14:53
Fold duplicate parts of the SVG data URIs to simplify the Javascript code -- overall impact on the final size of the gzipped web page is likely minor. The uncompressed source code for the icons is on the [branch/accordion-experiments] wiki page. ... (check-in: facc16233b user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/cgi.c.
1990
1991
1992
1993
1994
1995
1996

1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013

2014
2015
2016
2017
2018
2019
2020
** Return 0 to each child as it runs.  If unable to establish a
** listening socket, return non-zero.
*/
int cgi_http_server(
  int mnPort, int mxPort,   /* Range of TCP ports to try */
  const char *zBrowser,     /* Run this browser, if not NULL */
  const char *zIpAddr,      /* Bind to this IP address, if not null */

  int flags                 /* HTTP_SERVER_* flags */
){
#if defined(_WIN32)
  /* Use win32_http_server() instead */
  fossil_exit(1);
#else
  int listener = -1;           /* The server socket */
  int connection;              /* A socket for each individual connection */
  int nRequest = 0;            /* Number of requests handled so far */
  fd_set readfds;              /* Set of file descriptors for select() */
  socklen_t lenaddr;           /* Length of the inaddr structure */
  int child;                   /* PID of the child process */
  int nchildren = 0;           /* Number of child processes */
  struct timeval delay;        /* How long to wait inside select() */
  struct sockaddr_in inaddr;   /* The socket address */
  int opt = 1;                 /* setsockopt flag */
  int iPort = mnPort;


  while( iPort<=mxPort ){
    memset(&inaddr, 0, sizeof(inaddr));
    inaddr.sin_family = AF_INET;
    if( zIpAddr ){
      inaddr.sin_addr.s_addr = inet_addr(zIpAddr);
      if( inaddr.sin_addr.s_addr == (-1) ){







>
















|
>







1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
** Return 0 to each child as it runs.  If unable to establish a
** listening socket, return non-zero.
*/
int cgi_http_server(
  int mnPort, int mxPort,   /* Range of TCP ports to try */
  const char *zBrowser,     /* Run this browser, if not NULL */
  const char *zIpAddr,      /* Bind to this IP address, if not null */
  int iIdleTimeout,         /* Stop after this many seconds of inactivity */
  int flags                 /* HTTP_SERVER_* flags */
){
#if defined(_WIN32)
  /* Use win32_http_server() instead */
  fossil_exit(1);
#else
  int listener = -1;           /* The server socket */
  int connection;              /* A socket for each individual connection */
  int nRequest = 0;            /* Number of requests handled so far */
  fd_set readfds;              /* Set of file descriptors for select() */
  socklen_t lenaddr;           /* Length of the inaddr structure */
  int child;                   /* PID of the child process */
  int nchildren = 0;           /* Number of child processes */
  struct timeval delay;        /* How long to wait inside select() */
  struct sockaddr_in inaddr;   /* The socket address */
  int opt = 1;                 /* setsockopt flag */
  int iPort = mnPort;          /* TCP port to use */
  time_t stopTime;             /* When to timeout */

  while( iPort<=mxPort ){
    memset(&inaddr, 0, sizeof(inaddr));
    inaddr.sin_family = AF_INET;
    if( zIpAddr ){
      inaddr.sin_addr.s_addr = inet_addr(zIpAddr);
      if( inaddr.sin_addr.s_addr == (-1) ){
2068
2069
2070
2071
2072
2073
2074

2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090

2091
2092
2093
2094
2095
2096
2097
      }
    }else
#endif
    if( system(zBrowser)<0 ){
      fossil_warning("cannot start browser: %s\n", zBrowser);
    }
  }

  while( 1 ){
#if FOSSIL_MAX_CONNECTIONS>0
    while( nchildren>=FOSSIL_MAX_CONNECTIONS ){
      if( wait(0)>=0 ) nchildren--;
    }
#endif
    delay.tv_sec = 0;
    delay.tv_usec = 100000;
    FD_ZERO(&readfds);
    assert( listener>=0 );
    FD_SET( listener, &readfds);
    select( listener+1, &readfds, 0, 0, &delay);
    if( FD_ISSET(listener, &readfds) ){
      lenaddr = sizeof(inaddr);
      connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr);
      if( connection>=0 ){

        child = fork();
        if( child!=0 ){
          if( child>0 ){
            nchildren++;
            nRequest++;
          }
          close(connection);







>
















>







2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
      }
    }else
#endif
    if( system(zBrowser)<0 ){
      fossil_warning("cannot start browser: %s\n", zBrowser);
    }
  }
  if( iIdleTimeout>0 ) stopTime = time(0)+iIdleTimeout;
  while( 1 ){
#if FOSSIL_MAX_CONNECTIONS>0
    while( nchildren>=FOSSIL_MAX_CONNECTIONS ){
      if( wait(0)>=0 ) nchildren--;
    }
#endif
    delay.tv_sec = 0;
    delay.tv_usec = 100000;
    FD_ZERO(&readfds);
    assert( listener>=0 );
    FD_SET( listener, &readfds);
    select( listener+1, &readfds, 0, 0, &delay);
    if( FD_ISSET(listener, &readfds) ){
      lenaddr = sizeof(inaddr);
      connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr);
      if( connection>=0 ){
        if( iIdleTimeout>0 ) stopTime = time(0)+iIdleTimeout;
        child = fork();
        if( child!=0 ){
          if( child>0 ){
            nchildren++;
            nRequest++;
          }
          close(connection);
2123
2124
2125
2126
2127
2128
2129




2130
2131
2132
2133
2134
2135
2136
        if( x<=0 ) break;
        if( WIFSIGNALED(iStatus) && g.fAnyTrace ){
          fprintf(stderr, "/***** Child %d exited on signal %d (%s) *****/\n",
                  x, WTERMSIG(iStatus), strsignal(WTERMSIG(iStatus)));
        }
        nchildren--;
      }




    }
  }
  /* NOT REACHED */
  fossil_exit(1);
#endif
  /* NOT REACHED */
  return 0;







>
>
>
>







2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
        if( x<=0 ) break;
        if( WIFSIGNALED(iStatus) && g.fAnyTrace ){
          fprintf(stderr, "/***** Child %d exited on signal %d (%s) *****/\n",
                  x, WTERMSIG(iStatus), strsignal(WTERMSIG(iStatus)));
        }
        nchildren--;
      }
    }
    /* Stop serving if idle for too long */
    if( iIdleTimeout>0 && stopTime<time(0) ){
      fossil_exit(0);
    }
  }
  /* NOT REACHED */
  fossil_exit(1);
#endif
  /* NOT REACHED */
  return 0;
Changes to src/main.c.
1254
1255
1256
1257
1258
1259
1260










1261
1262
1263
1264
1265
1266
1267
  get_version_blob(&versionInfo, verboseFlag);
  @ <pre>
  @ %h(blob_str(&versionInfo))
  @ </pre>
  style_footer();
}












/*
** Set the g.zBaseURL value to the full URL for the toplevel of
** the fossil tree.  Set g.zTop to g.zBaseURL without the
** leading "http://" and the host and port.
**
** The g.zBaseURL is normally set based on HTTP_HOST and SCRIPT_NAME







>
>
>
>
>
>
>
>
>
>







1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
  get_version_blob(&versionInfo, verboseFlag);
  @ <pre>
  @ %h(blob_str(&versionInfo))
  @ </pre>
  style_footer();
}

/*
** WEBPAGE: noop
**
** Send back an empty HTTP reply.  Deliver no content.
*/
void noop_page(void){
  fossil_free(style_csp(1));
  cgi_set_content_type("text/plain");
}


/*
** Set the g.zBaseURL value to the full URL for the toplevel of
** the fossil tree.  Set g.zTop to g.zBaseURL without the
** leading "http://" and the host and port.
**
** The g.zBaseURL is normally set based on HTTP_HOST and SCRIPT_NAME
2359
2360
2361
2362
2363
2364
2365

2366
2367
2368
2369
2370
2371
2372
**   --baseurl URL    base URL (useful with reverse proxies)
**   --extroot DIR    document root for the /ext extension mechanism
**   --files GLOB     comma-separate glob patterns for static file to serve
**   --host NAME      specify hostname of the server
**   --https          signal a request coming in via https
**   --in FILE        Take input from FILE instead of standard input
**   --ipaddr ADDR    Assume the request comes from the given IP address

**   --localauth      enable automatic login for local connections
**   --nocompress     do not compress HTTP replies
**   --nodelay        omit backoffice processing if it would delay process exit
**   --nojail         drop root privilege but do not enter the chroot jail
**   --nossl          signal that no SSL connections are available
**   --notfound URL   use URL as "HTTP 404, object not found" page.
**   --out FILE       write results to FILE instead of to standard output







>







2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
**   --baseurl URL    base URL (useful with reverse proxies)
**   --extroot DIR    document root for the /ext extension mechanism
**   --files GLOB     comma-separate glob patterns for static file to serve
**   --host NAME      specify hostname of the server
**   --https          signal a request coming in via https
**   --in FILE        Take input from FILE instead of standard input
**   --ipaddr ADDR    Assume the request comes from the given IP address
**   --keep-alive     Include "keepalive.js" in HTML pages
**   --localauth      enable automatic login for local connections
**   --nocompress     do not compress HTTP replies
**   --nodelay        omit backoffice processing if it would delay process exit
**   --nojail         drop root privilege but do not enter the chroot jail
**   --nossl          signal that no SSL connections are available
**   --notfound URL   use URL as "HTTP 404, object not found" page.
**   --out FILE       write results to FILE instead of to standard output
2438
2439
2440
2441
2442
2443
2444

2445
2446
2447
2448
2449
2450
2451
  if( zAltBase ) set_base_url(zAltBase);
  if( find_option("https",0,0)!=0 ){
    zIpAddr = fossil_getenv("REMOTE_HOST"); /* From stunnel */
    cgi_replace_parameter("HTTPS","on");
  }
  zHost = find_option("host", 0, 1);
  if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost);


#if defined(_WIN32) && USE_SEE
  zPidKey = find_option("usepidkey", 0, 1);
  if( zPidKey ){
    DWORD processId = 0;
    LPVOID pAddress = NULL;
    SIZE_T nSize = 0;







>







2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
  if( zAltBase ) set_base_url(zAltBase);
  if( find_option("https",0,0)!=0 ){
    zIpAddr = fossil_getenv("REMOTE_HOST"); /* From stunnel */
    cgi_replace_parameter("HTTPS","on");
  }
  zHost = find_option("host", 0, 1);
  if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost);
  if( find_option("keep-alive",0,0) ) style_load_js("keepalive.js");

#if defined(_WIN32) && USE_SEE
  zPidKey = find_option("usepidkey", 0, 1);
  if( zPidKey ){
    DWORD processId = 0;
    LPVOID pAddress = NULL;
    SIZE_T nSize = 0;
2622
2623
2624
2625
2626
2627
2628

2629
2630
2631
2632
2633
2634
2635
** by default.
**
** Options:
**   --baseurl URL       Use URL as the base (useful for reverse proxies)
**   --create            Create a new REPOSITORY if it does not already exist
**   --extroot DIR       Document root for the /ext extension mechanism
**   --files GLOBLIST    Comma-separated list of glob patterns for static files

**   --localauth         enable automatic login for requests from localhost
**   --localhost         listen on 127.0.0.1 only (always true for "ui")
**   --https             Indicates that the input is coming through a reverse
**                       proxy that has already translated HTTPS into HTTP.
**   --max-latency N     Do not let any single HTTP request run for more than N
**                       seconds (only works on unix)
**   --nocompress        Do not compress HTTP replies







>







2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
** by default.
**
** Options:
**   --baseurl URL       Use URL as the base (useful for reverse proxies)
**   --create            Create a new REPOSITORY if it does not already exist
**   --extroot DIR       Document root for the /ext extension mechanism
**   --files GLOBLIST    Comma-separated list of glob patterns for static files
**   --idle-timeout N    Exit if no HTTP requests received for N seconds
**   --localauth         enable automatic login for requests from localhost
**   --localhost         listen on 127.0.0.1 only (always true for "ui")
**   --https             Indicates that the input is coming through a reverse
**                       proxy that has already translated HTTPS into HTTP.
**   --max-latency N     Do not let any single HTTP request run for more than N
**                       seconds (only works on unix)
**   --nocompress        Do not compress HTTP replies
2661
2662
2663
2664
2665
2666
2667


2668
2669
2670
2671
2672
2673
2674
  const char *zTimeout = 0; /* Max runtime of any single HTTP request */
#endif
  int allowRepoList;         /* List repositories on URL "/" */
  const char *zAltBase;      /* Argument to the --baseurl option */
  const char *zFileGlob;     /* Static content must match this */
  char *zIpAddr = 0;         /* Bind to this IP address */
  int fCreate = 0;           /* The --create flag */


  const char *zInitPage = 0; /* Start on this page.  --page option */
#if defined(_WIN32) && USE_SEE
  const char *zPidKey;
#endif

#if defined(_WIN32)
  const char *zStopperFile;    /* Name of file used to terminate server */







>
>







2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
  const char *zTimeout = 0; /* Max runtime of any single HTTP request */
#endif
  int allowRepoList;         /* List repositories on URL "/" */
  const char *zAltBase;      /* Argument to the --baseurl option */
  const char *zFileGlob;     /* Static content must match this */
  char *zIpAddr = 0;         /* Bind to this IP address */
  int fCreate = 0;           /* The --create flag */
  const char *zIdleTimeout;  /* Value of the --idle-timeout flag */
  int iIdle = 0;             /* Idle timeout value */
  const char *zInitPage = 0; /* Start on this page.  --page option */
#if defined(_WIN32) && USE_SEE
  const char *zPidKey;
#endif

#if defined(_WIN32)
  const char *zStopperFile;    /* Name of file used to terminate server */
2694
2695
2696
2697
2698
2699
2700

2701





2702
2703
2704
2705
2706
2707
2708
#endif
  g.useLocalauth = find_option("localauth", 0, 0)!=0;
  Th_InitTraceLog();
  zPort = find_option("port", "P", 1);
  isUiCmd = g.argv[1][0]=='u';
  if( isUiCmd ){
    zInitPage = find_option("page", 0, 1);

  }





  zNotFound = find_option("notfound", 0, 1);
  allowRepoList = find_option("repolist",0,0)!=0;
  if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
  zAltBase = find_option("baseurl", 0, 1);
  fCreate = find_option("create",0,0)!=0;
  if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI;
  if( zAltBase ){







>

>
>
>
>
>







2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
#endif
  g.useLocalauth = find_option("localauth", 0, 0)!=0;
  Th_InitTraceLog();
  zPort = find_option("port", "P", 1);
  isUiCmd = g.argv[1][0]=='u';
  if( isUiCmd ){
    zInitPage = find_option("page", 0, 1);
    iIdle = 60;
  }
  zIdleTimeout = find_option("idle-timeout",0,1);
  if( zIdleTimeout ){
    iIdle = atoi(zIdleTimeout);
  }
  
  zNotFound = find_option("notfound", 0, 1);
  allowRepoList = find_option("repolist",0,0)!=0;
  if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
  zAltBase = find_option("baseurl", 0, 1);
  fCreate = find_option("create",0,0)!=0;
  if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI;
  if( zAltBase ){
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
      zBrowserCmd = mprintf("%s http://%s:%%d/%s &",
                            zBrowser, zIpAddr, zInitPage);
    }
  }
  if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
  if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
  db_close(1);
  if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){
    fossil_fatal("unable to listen on TCP socket %d", iPort);
  }
  /* For the parent process, the cgi_http_server() command above never
  ** returns (except in the case of an error).  Instead, for each incoming
  ** client connection, a child process is created, file descriptors 0
  ** and 1 are bound to that connection, and the child returns.
  **







|







2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
      zBrowserCmd = mprintf("%s http://%s:%%d/%s &",
                            zBrowser, zIpAddr, zInitPage);
    }
  }
  if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
  if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
  db_close(1);
  if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, iIdle, flags) ){
    fossil_fatal("unable to listen on TCP socket %d", iPort);
  }
  /* For the parent process, the cgi_http_server() command above never
  ** returns (except in the case of an error).  Instead, for each incoming
  ** client connection, a child process is created, file descriptors 0
  ** and 1 are bound to that connection, and the child returns.
  **
2832
2833
2834
2835
2836
2837
2838

2839
2840
2841
2842
2843
2844
2845
    g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
  }
  if( flags & HTTP_SERVER_SCGI ){
    cgi_handle_scgi_request();
  }else{
    cgi_handle_http_request(0);
  }

  process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
  if( g.fAnyTrace ){
    fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n",
            getpid());
  }
#else
  /* Win32 implementation */







>







2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
    g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
  }
  if( flags & HTTP_SERVER_SCGI ){
    cgi_handle_scgi_request();
  }else{
    cgi_handle_http_request(0);
  }
  if( iIdle>0 ) style_load_js("keepalive.js");
  process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
  if( g.fAnyTrace ){
    fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n",
            getpid());
  }
#else
  /* Win32 implementation */
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
  if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
  db_close(1);
  if( allowRepoList ){
    flags |= HTTP_SERVER_REPOLIST;
  }
  if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){
    win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile,
                      zAltBase, zNotFound, zFileGlob, zIpAddr, flags);
  }
#endif
}

/*
** COMMAND: test-echo
**







|







2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
  if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
  db_close(1);
  if( allowRepoList ){
    flags |= HTTP_SERVER_REPOLIST;
  }
  if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){
    win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile,
                      zAltBase, zNotFound, zFileGlob, zIpAddr, iIdle, flags);
  }
#endif
}

/*
** COMMAND: test-echo
**
Changes to src/main.mk.
216
217
218
219
220
221
222

223
224
225
226
227
228
229
  $(SRCDIR)/accordion.js \
  $(SRCDIR)/ci_edit.js \
  $(SRCDIR)/copybtn.js \
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/graph.js \
  $(SRCDIR)/href.js \

  $(SRCDIR)/login.js \
  $(SRCDIR)/markdown.md \
  $(SRCDIR)/menu.js \
  $(SRCDIR)/sbsdiff.js \
  $(SRCDIR)/scroll.js \
  $(SRCDIR)/skin.js \
  $(SRCDIR)/sorttable.js \







>







216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  $(SRCDIR)/accordion.js \
  $(SRCDIR)/ci_edit.js \
  $(SRCDIR)/copybtn.js \
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/graph.js \
  $(SRCDIR)/href.js \
  $(SRCDIR)/keepalive.js \
  $(SRCDIR)/login.js \
  $(SRCDIR)/markdown.md \
  $(SRCDIR)/menu.js \
  $(SRCDIR)/sbsdiff.js \
  $(SRCDIR)/scroll.js \
  $(SRCDIR)/skin.js \
  $(SRCDIR)/sorttable.js \
Changes to src/winhttp.c.
517
518
519
520
521
522
523

524
525
526
527
528
529
530
  int mnPort, int mxPort,   /* Range of allowed TCP port numbers */
  const char *zBrowser,     /* Command to launch browser.  (Or NULL) */
  const char *zStopper,     /* Stop server when this file is exists (Or NULL) */
  const char *zBaseUrl,     /* The --baseurl option, or NULL */
  const char *zNotFound,    /* The --notfound option, or NULL */
  const char *zFileGlob,    /* The --fileglob option, or NULL */
  const char *zIpAddr,      /* Bind to this IP address, if not NULL */

  int flags                 /* One or more HTTP_SERVER_ flags */
){
  HANDLE hStoppedEvent;
  WSADATA wd;
  DualSocket ds;
  int idCnt = 0;
  int iPort = mnPort;







>







517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
  int mnPort, int mxPort,   /* Range of allowed TCP port numbers */
  const char *zBrowser,     /* Command to launch browser.  (Or NULL) */
  const char *zStopper,     /* Stop server when this file is exists (Or NULL) */
  const char *zBaseUrl,     /* The --baseurl option, or NULL */
  const char *zNotFound,    /* The --notfound option, or NULL */
  const char *zFileGlob,    /* The --fileglob option, or NULL */
  const char *zIpAddr,      /* Bind to this IP address, if not NULL */
  int idleTimeout,          /* Idle timeout in seconds.  0 means none */
  int flags                 /* One or more HTTP_SERVER_ flags */
){
  HANDLE hStoppedEvent;
  WSADATA wd;
  DualSocket ds;
  int idCnt = 0;
  int iPort = mnPort;
551
552
553
554
555
556
557



558
559
560
561
562
563
564
  }
  if( g.useLocalauth ){
    blob_appendf(&options, " --localauth");
  }
  if( g.thTrace ){
    blob_appendf(&options, " --th-trace");
  }



  if( flags & HTTP_SERVER_REPOLIST ){
    blob_appendf(&options, " --repolist");
  }
  zSkin = skin_in_use();
  if( zSkin ){
    blob_appendf(&options, " --skin %s", zSkin);
  }







>
>
>







552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
  }
  if( g.useLocalauth ){
    blob_appendf(&options, " --localauth");
  }
  if( g.thTrace ){
    blob_appendf(&options, " --th-trace");
  }
  if( idleTimeout>0 ){
    blob_appendf(&options, " --keep-alive");
  }
  if( flags & HTTP_SERVER_REPOLIST ){
    blob_appendf(&options, " --repolist");
  }
  zSkin = skin_in_use();
  if( zSkin ){
    blob_appendf(&options, " --skin %s", zSkin);
  }
Changes to win/Makefile.mingw.
638
639
640
641
642
643
644

645
646
647
648
649
650
651
  $(SRCDIR)/accordion.js \
  $(SRCDIR)/ci_edit.js \
  $(SRCDIR)/copybtn.js \
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/graph.js \
  $(SRCDIR)/href.js \

  $(SRCDIR)/login.js \
  $(SRCDIR)/markdown.md \
  $(SRCDIR)/menu.js \
  $(SRCDIR)/sbsdiff.js \
  $(SRCDIR)/scroll.js \
  $(SRCDIR)/skin.js \
  $(SRCDIR)/sorttable.js \







>







638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
  $(SRCDIR)/accordion.js \
  $(SRCDIR)/ci_edit.js \
  $(SRCDIR)/copybtn.js \
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/graph.js \
  $(SRCDIR)/href.js \
  $(SRCDIR)/keepalive.js \
  $(SRCDIR)/login.js \
  $(SRCDIR)/markdown.md \
  $(SRCDIR)/menu.js \
  $(SRCDIR)/sbsdiff.js \
  $(SRCDIR)/scroll.js \
  $(SRCDIR)/skin.js \
  $(SRCDIR)/sorttable.js \
Changes to win/Makefile.msc.
545
546
547
548
549
550
551

552
553
554
555
556
557
558
        $(SRCDIR)\accordion.js \
        $(SRCDIR)\ci_edit.js \
        $(SRCDIR)\copybtn.js \
        $(SRCDIR)\diff.tcl \
        $(SRCDIR)\forum.js \
        $(SRCDIR)\graph.js \
        $(SRCDIR)\href.js \

        $(SRCDIR)\login.js \
        $(SRCDIR)\markdown.md \
        $(SRCDIR)\menu.js \
        $(SRCDIR)\sbsdiff.js \
        $(SRCDIR)\scroll.js \
        $(SRCDIR)\skin.js \
        $(SRCDIR)\sorttable.js \







>







545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
        $(SRCDIR)\accordion.js \
        $(SRCDIR)\ci_edit.js \
        $(SRCDIR)\copybtn.js \
        $(SRCDIR)\diff.tcl \
        $(SRCDIR)\forum.js \
        $(SRCDIR)\graph.js \
        $(SRCDIR)\href.js \
        $(SRCDIR)\keepalive.js \
        $(SRCDIR)\login.js \
        $(SRCDIR)\markdown.md \
        $(SRCDIR)\menu.js \
        $(SRCDIR)\sbsdiff.js \
        $(SRCDIR)\scroll.js \
        $(SRCDIR)\skin.js \
        $(SRCDIR)\sorttable.js \