Fossil

Changes On Branch idle-timeout
Login

Changes On Branch idle-timeout

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

Changes In Branch idle-timeout Excluding Merge-Ins

This is equivalent to a diff from facc1623 to eb750c28

2020-04-10
14:02
Modifications to the "fossil ui" idle timeout feature: (0) Make the Javascript work with IE (lacking the `fetch()' function) (1) Ensure the Javascript keep-alive polling interval is lower than the idle timeout (2) Add the short form `-t' as an alias for the `--idle-timeout' command-line option, since it is now always required to enable legacy behavior. ... (Closed-Leaf check-in: fae4ae05 user: florian tags: idle-time-suggestions)
13:37
For wiki pages that are associated with a check-in, branch, or tag, redirect to the associated /info or /timeline page unless the "p" query parameter is present. ... (check-in: 093943d7 user: drh tags: trunk)
2020-04-09
17:29
Branch closed. See this forum post for an explanation. Was: Fix a harmless compiler warning. ... (Closed-Leaf check-in: eb750c28 user: drh tags: idle-timeout)
17:18
Implement an idle-timeout for windows. ... (check-in: aee74019 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: 39a899e4 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: facc1623 user: drh tags: trunk)
12:32
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. ... (Closed-Leaf check-in: 811ae0c8 user: florian tags: accordion-experiments-2)
11:40
Enhance the "test-file-environment" command so that it works even if run outside of an open checkout and without the -R option. ... (check-in: af6833b6 user: drh tags: trunk)

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 = 0;         /* 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;

Added src/keepalive.js.



















>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
/* This javascript runs on web-pages that need to periodically send
** a keep-alive HTTP request back to the server.  This is typically
** used with the "fossil ui" command with a --idle-timeout set.  The
** HTTP server will stop if it does not receive a new HTTP request
** within some time interval (60 seconds).  This script keeps sending
** new HTTP requests every 20 seconds or so to keep the server running
** while the page is still viewable.
*/
setInterval(function(){fetch('/noop');},15000+10000*Math.random());

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
2649
2650
** 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 are received for N seconds. 
**                       "0" means never. 0 is default for the "server" 
**                       command and "60" is the default for the "ui" command.
**   --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 */







>
>







2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
  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 ){







>

>
>
>
>
>







2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
#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.
  **







|







2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
      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 */







>







2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
    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
**







|







2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
  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.

197
198
199
200
201
202
203
204
205
206
207
208
209


210
211
212
213
214
215
216
217
218
219
220
221


222
223
224
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
  if( ds->s6!=INVALID_SOCKET && listen(ds->s6, SOMAXCONN)==SOCKET_ERROR ){
    return 0;
  }
  return 1;
};

/*
** Accepts connections on DualSocket.
*/
static void DualSocket_accept(DualSocket* pListen, DualSocket* pClient,
                              DualAddr* pClientAddr){
  fd_set rs;
  int rs_count = 0;


  assert( pListen!=NULL && pClient!=NULL && pClientAddr!= NULL );
  DualSocket_init(pClient);
  DualAddr_init(pClientAddr);
  FD_ZERO(&rs);
  if( pListen->s4!=INVALID_SOCKET ){
    FD_SET(pListen->s4, &rs);
    ++rs_count;
  }
  if( pListen->s6!=INVALID_SOCKET ){
    FD_SET(pListen->s6, &rs);
    ++rs_count;
  }


  if( select(rs_count, &rs, 0, 0, 0 /*blocking*/)==SOCKET_ERROR ){
    return;
  }
  if( FD_ISSET(pListen->s4, &rs) ){
    pClient->s4 = accept(pListen->s4, (struct sockaddr*)&pClientAddr->a4.addr,
                         &pClientAddr->a4.len);
  }
  if( FD_ISSET(pListen->s6, &rs) ){
    pClient->s6 = accept(pListen->s6, (struct sockaddr*)&pClientAddr->a6.addr,
                         &pClientAddr->a6.len);
  }

}

/*
** The HttpServer structure holds information about an instance of
** the HTTP server itself.
*/
typedef struct HttpServer HttpServer;







|

|



>
>












>
>
|
<
<








>







197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226


227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
  if( ds->s6!=INVALID_SOCKET && listen(ds->s6, SOMAXCONN)==SOCKET_ERROR ){
    return 0;
  }
  return 1;
};

/*
** Accepts connections on DualSocket.  Return 
*/
static int DualSocket_accept(DualSocket* pListen, DualSocket* pClient,
                              DualAddr* pClientAddr){
  fd_set rs;
  int rs_count = 0;
  int rc = 0;
  struct timeval delay;
  assert( pListen!=NULL && pClient!=NULL && pClientAddr!= NULL );
  DualSocket_init(pClient);
  DualAddr_init(pClientAddr);
  FD_ZERO(&rs);
  if( pListen->s4!=INVALID_SOCKET ){
    FD_SET(pListen->s4, &rs);
    ++rs_count;
  }
  if( pListen->s6!=INVALID_SOCKET ){
    FD_SET(pListen->s6, &rs);
    ++rs_count;
  }
  delay.tv_sec = 2;
  delay.tv_usec = 0;
  rc = select(rs_count, &rs, 0, 0, &delay);


  if( FD_ISSET(pListen->s4, &rs) ){
    pClient->s4 = accept(pListen->s4, (struct sockaddr*)&pClientAddr->a4.addr,
                         &pClientAddr->a4.len);
  }
  if( FD_ISSET(pListen->s6, &rs) ){
    pClient->s6 = accept(pListen->s6, (struct sockaddr*)&pClientAddr->a6.addr,
                         &pClientAddr->a6.len);
  }
  return rc;
}

/*
** The HttpServer structure holds information about an instance of
** the HTTP server itself.
*/
typedef struct HttpServer HttpServer;
512
513
514
515
516
517
518
519
520
521
522
523

524
525
526
527
528
529
530
531
532
533

534
535
536
537
538
539
540
/*
** Start a listening socket and process incoming HTTP requests on
** that socket.
*/
void win32_http_server(
  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;
  Blob options;
  wchar_t zTmpPath[MAX_PATH];
  const char *zSkin;

#if USE_SEE
  const char *zSavedKey = 0;
  size_t savedKeySize = 0;
#endif

  blob_zero(&options);
  if( PB("HTTPS") ){







|




>










>







515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
/*
** Start a listening socket and process incoming HTTP requests on
** that socket.
*/
void win32_http_server(
  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 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 iIdleTimeout,         /* 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;
  Blob options;
  wchar_t zTmpPath[MAX_PATH];
  const char *zSkin;
  time_t stopTime = 0;      /* When to stop due to idle timeout */
#if USE_SEE
  const char *zSavedKey = 0;
  size_t savedKeySize = 0;
#endif

  blob_zero(&options);
  if( PB("HTTPS") ){
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);
  }







>
>
>







556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
  }
  if( g.useLocalauth ){
    blob_appendf(&options, " --localauth");
  }
  if( g.thTrace ){
    blob_appendf(&options, " --th-trace");
  }
  if( iIdleTimeout>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);
  }
630
631
632
633
634
635
636

637
638
639
640
641

642
643


644

645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663

664
665
666
667
668
669
670
671
672
673
674
675
676

677
678
679
680
681
682

683
684
685
686
687
688
689
    pServer->listener = ds;
    file_delete(zStopper);
    _beginthread(win32_server_stopper, 0, (void*)pServer);
  }
  /* Set the service status to running and pass the listener socket to the
  ** service handling procedures. */
  win32_http_service_running(&ds);

  for(;;){
    DualSocket client;
    DualAddr client_addr;
    HttpRequest *pRequest;
    int wsaError;


    DualSocket_accept(&ds, &client, &client_addr);


    if( client.s4==INVALID_SOCKET && client.s6==INVALID_SOCKET ){

      /* If the service control handler has closed the listener socket,
      ** cleanup and return, otherwise report a fatal error. */
      wsaError =  WSAGetLastError();
      DualSocket_close(&ds);
      if( (wsaError==WSAEINTR) || (wsaError==WSAENOTSOCK) ){
        WSACleanup();
        return;
      }else{
        WSACleanup();
        fossil_panic("error from accept()");
      }
    }
    if( client.s4!=INVALID_SOCKET ){
      pRequest = fossil_malloc(sizeof(HttpRequest));
      pRequest->id = ++idCnt;
      pRequest->s = client.s4;
      memcpy(&pRequest->addr, &client_addr.a4, sizeof(client_addr.a4));
      pRequest->flags = flags;
      pRequest->zOptions = blob_str(&options);

      if( flags & HTTP_SERVER_SCGI ){
        _beginthread(win32_scgi_request, 0, (void*)pRequest);
      }else{
        _beginthread(win32_http_request, 0, (void*)pRequest);
      }
    }
    if( client.s6!=INVALID_SOCKET ){
      pRequest = fossil_malloc(sizeof(HttpRequest));
      pRequest->id = ++idCnt;
      pRequest->s = client.s6;
      memcpy(&pRequest->addr, &client_addr.a6, sizeof(client_addr.a6));
      pRequest->flags = flags;
      pRequest->zOptions = blob_str(&options);

      if( flags & HTTP_SERVER_SCGI ){
        _beginthread(win32_scgi_request, 0, (void*)pRequest);
      }else{
        _beginthread(win32_http_request, 0, (void*)pRequest);
      }
    }

  }
  DualSocket_close(&ds);
  WSACleanup();
  SetEvent(hStoppedEvent);
  CloseHandle(hStoppedEvent);
}








>





>

|
>
>
|
>



















>













>






>







638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
    pServer->listener = ds;
    file_delete(zStopper);
    _beginthread(win32_server_stopper, 0, (void*)pServer);
  }
  /* Set the service status to running and pass the listener socket to the
  ** service handling procedures. */
  win32_http_service_running(&ds);
  if( iIdleTimeout>0 ) stopTime = time(0) + iIdleTimeout;
  for(;;){
    DualSocket client;
    DualAddr client_addr;
    HttpRequest *pRequest;
    int wsaError;
    int nSock;

    nSock = DualSocket_accept(&ds, &client, &client_addr);
    if( nSock==SOCKET_ERROR
     && client.s4==INVALID_SOCKET
     && client.s6==INVALID_SOCKET
    ){
      /* If the service control handler has closed the listener socket,
      ** cleanup and return, otherwise report a fatal error. */
      wsaError =  WSAGetLastError();
      DualSocket_close(&ds);
      if( (wsaError==WSAEINTR) || (wsaError==WSAENOTSOCK) ){
        WSACleanup();
        return;
      }else{
        WSACleanup();
        fossil_panic("error from accept()");
      }
    }
    if( client.s4!=INVALID_SOCKET ){
      pRequest = fossil_malloc(sizeof(HttpRequest));
      pRequest->id = ++idCnt;
      pRequest->s = client.s4;
      memcpy(&pRequest->addr, &client_addr.a4, sizeof(client_addr.a4));
      pRequest->flags = flags;
      pRequest->zOptions = blob_str(&options);
      if( iIdleTimeout>0 ) stopTime = time(0) + iIdleTimeout;
      if( flags & HTTP_SERVER_SCGI ){
        _beginthread(win32_scgi_request, 0, (void*)pRequest);
      }else{
        _beginthread(win32_http_request, 0, (void*)pRequest);
      }
    }
    if( client.s6!=INVALID_SOCKET ){
      pRequest = fossil_malloc(sizeof(HttpRequest));
      pRequest->id = ++idCnt;
      pRequest->s = client.s6;
      memcpy(&pRequest->addr, &client_addr.a6, sizeof(client_addr.a6));
      pRequest->flags = flags;
      pRequest->zOptions = blob_str(&options);
      if( iIdleTimeout>0 ) stopTime = time(0) + iIdleTimeout;
      if( flags & HTTP_SERVER_SCGI ){
        _beginthread(win32_scgi_request, 0, (void*)pRequest);
      }else{
        _beginthread(win32_http_request, 0, (void*)pRequest);
      }
    }
    if( iIdleTimeout>0 && stopTime<time(0) ) break;
  }
  DualSocket_close(&ds);
  WSACleanup();
  SetEvent(hStoppedEvent);
  CloseHandle(hStoppedEvent);
}

842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
  ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  ssStatus.dwServiceSpecificExitCode = 0;
  win32_report_service_status(SERVICE_START_PENDING, NO_ERROR, 3000);

   /* Execute the http server */
  win32_http_server(hsData.port, hsData.port,
                    NULL, NULL, hsData.zBaseUrl, hsData.zNotFound,
                    hsData.zFileGlob, 0, hsData.flags);

  /* Service has stopped now. */
  win32_report_service_status(SERVICE_STOPPED, NO_ERROR, 0);
  return;
}

/*







|







858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
  ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  ssStatus.dwServiceSpecificExitCode = 0;
  win32_report_service_status(SERVICE_START_PENDING, NO_ERROR, 3000);

   /* Execute the http server */
  win32_http_server(hsData.port, hsData.port,
                    NULL, NULL, hsData.zBaseUrl, hsData.zNotFound,
                    hsData.zFileGlob, 0, 0, hsData.flags);

  /* Service has stopped now. */
  win32_report_service_status(SERVICE_STOPPED, NO_ERROR, 0);
  return;
}

/*

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 \