Fossil SCM

Make the '--stopper' option for the 'server' command on Windows work as advertised even when the server thread is being blocked by accept().

mistachkin 2016-10-05 04:40 trunk
Commit a6eb6510756fdda20cefb4c693a892c062a7c8e0
1 file changed +88 -15
+88 -15
--- src/winhttp.c
+++ src/winhttp.c
@@ -24,10 +24,24 @@
2424
/* This code is for win32 only */
2525
#include <windows.h>
2626
#include <process.h>
2727
#include "winhttp.h"
2828
29
+/*
30
+** The HttpServer structure holds information about an instance of
31
+** the HTTP server itself.
32
+*/
33
+typedef struct HttpServer HttpServer;
34
+struct HttpServer {
35
+ HANDLE hStoppedEvent; /* Event to signal when server is stopped,
36
+ ** must be closed by callee. */
37
+ char *zStopper; /* The stopper file name, must be freed by
38
+ ** callee. */
39
+ SOCKET listener; /* Socket on which the server is listening,
40
+ ** may be closed by callee. */
41
+};
42
+
2943
/*
3044
** The HttpRequest structure holds information about each incoming
3145
** HTTP request.
3246
*/
3347
typedef struct HttpRequest HttpRequest;
@@ -70,10 +84,51 @@
7084
const char *zService,
7185
const char *zErr
7286
){
7387
fossil_fatal("unable to %s service '%s': %s", zOp, zService, zErr);
7488
}
89
+
90
+/*
91
+** Make sure the server stops as soon as possible after the stopper file
92
+** is found. If there is no stopper file name, do nothing.
93
+*/
94
+static void win32_server_stopper(void *pAppData){
95
+ HttpServer *p = (HttpServer*)pAppData;
96
+ if( p!=0 ){
97
+ HANDLE hStoppedEvent = p->hStoppedEvent;
98
+ const char *zStopper = p->zStopper;
99
+ SOCKET listener = p->listener;
100
+ if( hStoppedEvent!=NULL && zStopper!=0 && listener!=INVALID_SOCKET ){
101
+ while( 1 ){
102
+ DWORD dwResult = WaitForMultipleObjectsEx(1, &hStoppedEvent, FALSE,
103
+ 1000, TRUE);
104
+ if( dwResult!=WAIT_IO_COMPLETION && dwResult!=WAIT_TIMEOUT ){
105
+ /* The event is either invalid, signaled, or abandoned. Bail
106
+ ** out now because those conditions should indicate the parent
107
+ ** thread is dead or dying. */
108
+ break;
109
+ }
110
+ if( file_size(zStopper)>=0 ){
111
+ /* The stopper file has been found. Attempt to close the server
112
+ ** listener socket now and then exit. */
113
+ closesocket(listener);
114
+ p->listener = INVALID_SOCKET;
115
+ break;
116
+ }
117
+ }
118
+ }
119
+ if( hStoppedEvent!=NULL ){
120
+ CloseHandle(hStoppedEvent);
121
+ p->hStoppedEvent = NULL;
122
+ }
123
+ if( zStopper!=0 ){
124
+ fossil_free(p->zStopper);
125
+ p->zStopper = 0;
126
+ }
127
+ fossil_free(p);
128
+ }
129
+}
75130
76131
/*
77132
** Process a single incoming HTTP request.
78133
*/
79134
static void win32_http_request(void *pAppData){
@@ -163,11 +218,11 @@
163218
if( in ) fclose(in);
164219
closesocket(p->s);
165220
file_delete(zRequestFName);
166221
file_delete(zReplyFName);
167222
file_delete(zCmdFName);
168
- free(p);
223
+ fossil_free(p);
169224
}
170225
171226
/*
172227
** Process a single incoming SCGI request.
173228
*/
@@ -225,11 +280,11 @@
225280
if( out ) fclose(out);
226281
if( in ) fclose(in);
227282
closesocket(p->s);
228283
file_delete(zRequestFName);
229284
file_delete(zReplyFName);
230
- free(p);
285
+ fossil_free(p);
231286
}
232287
233288
234289
/*
235290
** Start a listening socket and process incoming HTTP requests on
@@ -243,19 +298,19 @@
243298
const char *zNotFound, /* The --notfound option, or NULL */
244299
const char *zFileGlob, /* The --fileglob option, or NULL */
245300
const char *zIpAddr, /* Bind to this IP address, if not NULL */
246301
int flags /* One or more HTTP_SERVER_ flags */
247302
){
303
+ HANDLE hStoppedEvent;
248304
WSADATA wd;
249305
SOCKET s = INVALID_SOCKET;
250306
SOCKADDR_IN addr;
251307
int idCnt = 0;
252308
int iPort = mnPort;
253309
Blob options;
254310
wchar_t zTmpPath[MAX_PATH];
255311
256
- if( zStopper ) file_delete(zStopper);
257312
blob_zero(&options);
258313
if( zBaseUrl ){
259314
blob_appendf(&options, " --baseurl %s", zBaseUrl);
260315
}
261316
if( zNotFound ){
@@ -324,17 +379,35 @@
324379
zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort);
325380
fossil_print("Launch webbrowser: %s\n", zBrowser);
326381
fossil_system(zBrowser);
327382
}
328383
fossil_print("Type Ctrl-C to stop the HTTP server\n");
384
+ /* Create an event used to signal when this server is exiting. */
385
+ hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
386
+ assert( hStoppedEvent!=NULL );
387
+ /* If there is a stopper file name, start the dedicated thread now.
388
+ ** It will attempt to close the listener socket within 1 second of
389
+ ** the stopper file being created. */
390
+ if( zStopper ){
391
+ HttpServer *pServer = fossil_malloc(sizeof(HttpServer));
392
+ memset(pServer, 0, sizeof(HttpServer));
393
+ DuplicateHandle(GetCurrentProcess(), hStoppedEvent,
394
+ GetCurrentProcess(), &pServer->hStoppedEvent,
395
+ 0, FALSE, DUPLICATE_SAME_ACCESS);
396
+ assert( pServer->hStoppedEvent!=NULL );
397
+ pServer->zStopper = fossil_strdup(zStopper);
398
+ pServer->listener = s;
399
+ file_delete(zStopper);
400
+ _beginthread(win32_server_stopper, 0, (void*)pServer);
401
+ }
329402
/* Set the service status to running and pass the listener socket to the
330403
** service handling procedures. */
331404
win32_http_service_running(s);
332405
for(;;){
333406
SOCKET client;
334407
SOCKADDR_IN client_addr;
335
- HttpRequest *p;
408
+ HttpRequest *pRequest;
336409
int len = sizeof(client_addr);
337410
int wsaError;
338411
339412
client = accept(s, (struct sockaddr*)&client_addr, &len);
340413
if( client==INVALID_SOCKET ){
@@ -347,27 +420,27 @@
347420
}else{
348421
closesocket(s);
349422
WSACleanup();
350423
fossil_fatal("error from accept()");
351424
}
352
- }else if( zStopper && file_size(zStopper)>=0 ){
353
- break;
354
- }
355
- p = fossil_malloc( sizeof(*p) );
356
- p->id = ++idCnt;
357
- p->s = client;
358
- p->addr = client_addr;
359
- p->flags = flags;
360
- p->zOptions = blob_str(&options);
425
+ }
426
+ pRequest = fossil_malloc(sizeof(HttpRequest));
427
+ pRequest->id = ++idCnt;
428
+ pRequest->s = client;
429
+ pRequest->addr = client_addr;
430
+ pRequest->flags = flags;
431
+ pRequest->zOptions = blob_str(&options);
361432
if( flags & HTTP_SERVER_SCGI ){
362
- _beginthread(win32_scgi_request, 0, (void*)p);
433
+ _beginthread(win32_scgi_request, 0, (void*)pRequest);
363434
}else{
364
- _beginthread(win32_http_request, 0, (void*)p);
435
+ _beginthread(win32_http_request, 0, (void*)pRequest);
365436
}
366437
}
367438
closesocket(s);
368439
WSACleanup();
440
+ SetEvent(hStoppedEvent);
441
+ CloseHandle(hStoppedEvent);
369442
}
370443
371444
/*
372445
** The HttpService structure is used to pass information to the service main
373446
** function and to the service control handler function.
374447
--- src/winhttp.c
+++ src/winhttp.c
@@ -24,10 +24,24 @@
24 /* This code is for win32 only */
25 #include <windows.h>
26 #include <process.h>
27 #include "winhttp.h"
28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29 /*
30 ** The HttpRequest structure holds information about each incoming
31 ** HTTP request.
32 */
33 typedef struct HttpRequest HttpRequest;
@@ -70,10 +84,51 @@
70 const char *zService,
71 const char *zErr
72 ){
73 fossil_fatal("unable to %s service '%s': %s", zOp, zService, zErr);
74 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
76 /*
77 ** Process a single incoming HTTP request.
78 */
79 static void win32_http_request(void *pAppData){
@@ -163,11 +218,11 @@
163 if( in ) fclose(in);
164 closesocket(p->s);
165 file_delete(zRequestFName);
166 file_delete(zReplyFName);
167 file_delete(zCmdFName);
168 free(p);
169 }
170
171 /*
172 ** Process a single incoming SCGI request.
173 */
@@ -225,11 +280,11 @@
225 if( out ) fclose(out);
226 if( in ) fclose(in);
227 closesocket(p->s);
228 file_delete(zRequestFName);
229 file_delete(zReplyFName);
230 free(p);
231 }
232
233
234 /*
235 ** Start a listening socket and process incoming HTTP requests on
@@ -243,19 +298,19 @@
243 const char *zNotFound, /* The --notfound option, or NULL */
244 const char *zFileGlob, /* The --fileglob option, or NULL */
245 const char *zIpAddr, /* Bind to this IP address, if not NULL */
246 int flags /* One or more HTTP_SERVER_ flags */
247 ){
 
248 WSADATA wd;
249 SOCKET s = INVALID_SOCKET;
250 SOCKADDR_IN addr;
251 int idCnt = 0;
252 int iPort = mnPort;
253 Blob options;
254 wchar_t zTmpPath[MAX_PATH];
255
256 if( zStopper ) file_delete(zStopper);
257 blob_zero(&options);
258 if( zBaseUrl ){
259 blob_appendf(&options, " --baseurl %s", zBaseUrl);
260 }
261 if( zNotFound ){
@@ -324,17 +379,35 @@
324 zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort);
325 fossil_print("Launch webbrowser: %s\n", zBrowser);
326 fossil_system(zBrowser);
327 }
328 fossil_print("Type Ctrl-C to stop the HTTP server\n");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
329 /* Set the service status to running and pass the listener socket to the
330 ** service handling procedures. */
331 win32_http_service_running(s);
332 for(;;){
333 SOCKET client;
334 SOCKADDR_IN client_addr;
335 HttpRequest *p;
336 int len = sizeof(client_addr);
337 int wsaError;
338
339 client = accept(s, (struct sockaddr*)&client_addr, &len);
340 if( client==INVALID_SOCKET ){
@@ -347,27 +420,27 @@
347 }else{
348 closesocket(s);
349 WSACleanup();
350 fossil_fatal("error from accept()");
351 }
352 }else if( zStopper && file_size(zStopper)>=0 ){
353 break;
354 }
355 p = fossil_malloc( sizeof(*p) );
356 p->id = ++idCnt;
357 p->s = client;
358 p->addr = client_addr;
359 p->flags = flags;
360 p->zOptions = blob_str(&options);
361 if( flags & HTTP_SERVER_SCGI ){
362 _beginthread(win32_scgi_request, 0, (void*)p);
363 }else{
364 _beginthread(win32_http_request, 0, (void*)p);
365 }
366 }
367 closesocket(s);
368 WSACleanup();
 
 
369 }
370
371 /*
372 ** The HttpService structure is used to pass information to the service main
373 ** function and to the service control handler function.
374
--- src/winhttp.c
+++ src/winhttp.c
@@ -24,10 +24,24 @@
24 /* This code is for win32 only */
25 #include <windows.h>
26 #include <process.h>
27 #include "winhttp.h"
28
29 /*
30 ** The HttpServer structure holds information about an instance of
31 ** the HTTP server itself.
32 */
33 typedef struct HttpServer HttpServer;
34 struct HttpServer {
35 HANDLE hStoppedEvent; /* Event to signal when server is stopped,
36 ** must be closed by callee. */
37 char *zStopper; /* The stopper file name, must be freed by
38 ** callee. */
39 SOCKET listener; /* Socket on which the server is listening,
40 ** may be closed by callee. */
41 };
42
43 /*
44 ** The HttpRequest structure holds information about each incoming
45 ** HTTP request.
46 */
47 typedef struct HttpRequest HttpRequest;
@@ -70,10 +84,51 @@
84 const char *zService,
85 const char *zErr
86 ){
87 fossil_fatal("unable to %s service '%s': %s", zOp, zService, zErr);
88 }
89
90 /*
91 ** Make sure the server stops as soon as possible after the stopper file
92 ** is found. If there is no stopper file name, do nothing.
93 */
94 static void win32_server_stopper(void *pAppData){
95 HttpServer *p = (HttpServer*)pAppData;
96 if( p!=0 ){
97 HANDLE hStoppedEvent = p->hStoppedEvent;
98 const char *zStopper = p->zStopper;
99 SOCKET listener = p->listener;
100 if( hStoppedEvent!=NULL && zStopper!=0 && listener!=INVALID_SOCKET ){
101 while( 1 ){
102 DWORD dwResult = WaitForMultipleObjectsEx(1, &hStoppedEvent, FALSE,
103 1000, TRUE);
104 if( dwResult!=WAIT_IO_COMPLETION && dwResult!=WAIT_TIMEOUT ){
105 /* The event is either invalid, signaled, or abandoned. Bail
106 ** out now because those conditions should indicate the parent
107 ** thread is dead or dying. */
108 break;
109 }
110 if( file_size(zStopper)>=0 ){
111 /* The stopper file has been found. Attempt to close the server
112 ** listener socket now and then exit. */
113 closesocket(listener);
114 p->listener = INVALID_SOCKET;
115 break;
116 }
117 }
118 }
119 if( hStoppedEvent!=NULL ){
120 CloseHandle(hStoppedEvent);
121 p->hStoppedEvent = NULL;
122 }
123 if( zStopper!=0 ){
124 fossil_free(p->zStopper);
125 p->zStopper = 0;
126 }
127 fossil_free(p);
128 }
129 }
130
131 /*
132 ** Process a single incoming HTTP request.
133 */
134 static void win32_http_request(void *pAppData){
@@ -163,11 +218,11 @@
218 if( in ) fclose(in);
219 closesocket(p->s);
220 file_delete(zRequestFName);
221 file_delete(zReplyFName);
222 file_delete(zCmdFName);
223 fossil_free(p);
224 }
225
226 /*
227 ** Process a single incoming SCGI request.
228 */
@@ -225,11 +280,11 @@
280 if( out ) fclose(out);
281 if( in ) fclose(in);
282 closesocket(p->s);
283 file_delete(zRequestFName);
284 file_delete(zReplyFName);
285 fossil_free(p);
286 }
287
288
289 /*
290 ** Start a listening socket and process incoming HTTP requests on
@@ -243,19 +298,19 @@
298 const char *zNotFound, /* The --notfound option, or NULL */
299 const char *zFileGlob, /* The --fileglob option, or NULL */
300 const char *zIpAddr, /* Bind to this IP address, if not NULL */
301 int flags /* One or more HTTP_SERVER_ flags */
302 ){
303 HANDLE hStoppedEvent;
304 WSADATA wd;
305 SOCKET s = INVALID_SOCKET;
306 SOCKADDR_IN addr;
307 int idCnt = 0;
308 int iPort = mnPort;
309 Blob options;
310 wchar_t zTmpPath[MAX_PATH];
311
 
312 blob_zero(&options);
313 if( zBaseUrl ){
314 blob_appendf(&options, " --baseurl %s", zBaseUrl);
315 }
316 if( zNotFound ){
@@ -324,17 +379,35 @@
379 zBrowser = mprintf(zBrowser /*works-like:"%d"*/, iPort);
380 fossil_print("Launch webbrowser: %s\n", zBrowser);
381 fossil_system(zBrowser);
382 }
383 fossil_print("Type Ctrl-C to stop the HTTP server\n");
384 /* Create an event used to signal when this server is exiting. */
385 hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
386 assert( hStoppedEvent!=NULL );
387 /* If there is a stopper file name, start the dedicated thread now.
388 ** It will attempt to close the listener socket within 1 second of
389 ** the stopper file being created. */
390 if( zStopper ){
391 HttpServer *pServer = fossil_malloc(sizeof(HttpServer));
392 memset(pServer, 0, sizeof(HttpServer));
393 DuplicateHandle(GetCurrentProcess(), hStoppedEvent,
394 GetCurrentProcess(), &pServer->hStoppedEvent,
395 0, FALSE, DUPLICATE_SAME_ACCESS);
396 assert( pServer->hStoppedEvent!=NULL );
397 pServer->zStopper = fossil_strdup(zStopper);
398 pServer->listener = s;
399 file_delete(zStopper);
400 _beginthread(win32_server_stopper, 0, (void*)pServer);
401 }
402 /* Set the service status to running and pass the listener socket to the
403 ** service handling procedures. */
404 win32_http_service_running(s);
405 for(;;){
406 SOCKET client;
407 SOCKADDR_IN client_addr;
408 HttpRequest *pRequest;
409 int len = sizeof(client_addr);
410 int wsaError;
411
412 client = accept(s, (struct sockaddr*)&client_addr, &len);
413 if( client==INVALID_SOCKET ){
@@ -347,27 +420,27 @@
420 }else{
421 closesocket(s);
422 WSACleanup();
423 fossil_fatal("error from accept()");
424 }
425 }
426 pRequest = fossil_malloc(sizeof(HttpRequest));
427 pRequest->id = ++idCnt;
428 pRequest->s = client;
429 pRequest->addr = client_addr;
430 pRequest->flags = flags;
431 pRequest->zOptions = blob_str(&options);
 
 
432 if( flags & HTTP_SERVER_SCGI ){
433 _beginthread(win32_scgi_request, 0, (void*)pRequest);
434 }else{
435 _beginthread(win32_http_request, 0, (void*)pRequest);
436 }
437 }
438 closesocket(s);
439 WSACleanup();
440 SetEvent(hStoppedEvent);
441 CloseHandle(hStoppedEvent);
442 }
443
444 /*
445 ** The HttpService structure is used to pass information to the service main
446 ** function and to the service control handler function.
447

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button