#include "WORecordRequestStream.h"
#include "WOHttpTransaction.h"
+#include <unistd.h>
+#include <sys/wait.h>
+
@interface WOHttpAdaptor(Server)
/* accessors */
static NSString *WOPort = nil;
static int WOHttpAdaptorSendTimeout = 10;
static int WOHttpAdaptorReceiveTimeout = 10;
+static int WOHttpAdaptorForkCount = 0;
static id allow = nil;
static BOOL debugOn = NO;
[ud integerForKey:@"WOHttpAdaptorReceiveTimeout"];
if (allow == nil) {
- allow = [ud objectForKey:@"WOHttpAllowHost"];
- if (allow == nil) {
+ if ((allow = [ud objectForKey:@"WOHttpAllowHost"]) == nil) {
allow = [NSArray arrayWithObjects:
- @"localhost", @"localhost.localdomain", nil];
+ @"localhost", @"localhost.localdomain",
+ @"127.0.0.1", nil];
}
-
+
if (![allow isKindOfClass:[NSArray class]])
allow = [NSArray arrayWithObject:allow];
allow = [allow copy];
}
+ WOHttpAdaptorForkCount =
+ [[ud objectForKey:@"WOHttpAdaptorForkCount"] intValue];
+
if (WOCoreOnHTTPAdaptorException)
[logger warnWithFormat:@"will dump core on HTTP adaptor exception!"];
}
return [addr autorelease];
}
+- (void)_registerForSignals {
+#if !defined(__MINGW32__)
+ UnixSignalHandler *us = [UnixSignalHandler sharedHandler];
+
+ [us addObserver:self selector:@selector(handleSIGPIPE:)
+ forSignal:SIGPIPE immediatelyNotifyOnSignal:NO];
+#endif
+}
+
+- (id<NGSocketAddress>)addressFromDefaultsOfApplication:(WOCoreApplication*)_a{
+ id<NGSocketAddress> lAddress = nil;
+ const char *cstr;
+
+ if ([WOPort isEqualToString:@"auto"]) {
+ if ((lAddress = [self autoBindAddress]) != nil)
+ return lAddress;
+ }
+
+ if ((cstr = [WOPort cString]) != NULL) {
+ if (isdigit(*cstr) && index(cstr, ':') == NULL) {
+ NSNumber *p;
+
+ p = [(WOCoreApplication *)[_a class] port];
+ if (p == nil) p = (id)WOPort;
+
+ lAddress =
+ [NGInternetSocketAddress wildcardAddressWithPort:[p intValue]];
+ if (lAddress != nil)
+ return lAddress;
+ }
+ }
+
+ return NGSocketAddressFromString(WOPort);
+}
+
+- (id<NGSocketAddress>)addressFromArguments:(NSDictionary *)_args {
+ id<NGSocketAddress> lAddress = nil;
+ NSString *arg = nil;
+ const char *cstr;
+
+ if ((arg = [_args objectForKey:@"-p"]) != nil)
+ return [NGInternetSocketAddress wildcardAddressWithPort:[arg intValue]];
+
+ if ((arg = [_args objectForKey:@"-WOPort"]) == nil)
+ return nil;
+
+ lAddress = nil;
+ if ([arg isEqualToString:@"auto"])
+ lAddress = [self autoBindAddress];
+
+ if ((lAddress == nil) && (cstr = [arg cString])) {
+ if (isdigit(*cstr)) {
+ lAddress =
+ [NGInternetSocketAddress wildcardAddressWithPort:[arg intValue]];
+ }
+ }
+ if (lAddress == nil)
+ lAddress = NGSocketAddressFromString(arg);
+ return lAddress;
+}
+
- (id)initWithName:(NSString *)_name
arguments:(NSDictionary *)_args
application:(WOCoreApplication *)_application
if ([[_application recordingPath] length] > 0)
WOHttpAdaptor_LogStream = YES;
-#if !defined(__MINGW32__)
- {
- UnixSignalHandler *us = [UnixSignalHandler sharedHandler];
-
- [us addObserver:self selector:@selector(handleSIGPIPE:)
- forSignal:SIGPIPE
- immediatelyNotifyOnSignal:NO];
- }
-#endif
+ [self _registerForSignals];
+
+ if ([_args count] < 1)
+ self->address = [self addressFromDefaultsOfApplication:_application];
+ else
+ self->address = [self addressFromArguments:_args];
- if ([_args count] < 1) {
- const char *cstr;
-
- self->address = nil;
-
- if ([WOPort isEqualToString:@"auto"])
- self->address = [self autoBindAddress];
-
- if ((self->address == nil) && ((cstr = [WOPort cString]) != NULL)) {
- if (isdigit(*cstr) && index(cstr, ':') == NULL) {
- NSNumber *p;
-
- p = [(WOCoreApplication *)[_application class] port];
- if (p == nil) p = (id)WOPort;
-
- self->address =
- [NGInternetSocketAddress wildcardAddressWithPort:[p intValue]];
- }
- }
- if (self->address == nil)
- self->address = NGSocketAddressFromString(WOPort);
- }
- else {
- NSString *arg = nil;
-
- if ((arg = [_args objectForKey:@"-p"])) {
- self->address =
- [NGInternetSocketAddress wildcardAddressWithPort:[arg intValue]];
- }
- else if ((arg = [_args objectForKey:@"-WOPort"])) {
- const char *cstr;
-
- self->address = nil;
-
- if ([arg isEqualToString:@"auto"])
- self->address = [self autoBindAddress];
-
- if ((self->address == nil) && (cstr = [arg cString])) {
- if (isdigit(*cstr)) {
- self->address =
- [NGInternetSocketAddress wildcardAddressWithPort:[arg intValue]];
- }
- }
- if (self->address == nil)
- self->address = NGSocketAddressFromString(arg);
- }
- }
self->address = [self->address retain];
if (self->address == nil) {
return self->address;
}
+/* forking */
+
+static pid_t *childPIDs = NULL;
+static BOOL isForkMaster = YES;
+
+- (void)forkChildren {
+ unsigned i;
+
+ if (WOHttpAdaptorForkCount == 0)
+ return;
+
+ [self logWithFormat:@"Note: forking %d children for socket processing.",
+ WOHttpAdaptorForkCount];
+
+#if !defined(__MINGW32__)
+ [[UnixSignalHandler sharedHandler]
+ addObserver:self selector:@selector(handleSIGCHLD:)
+ forSignal:SIGCHLD immediatelyNotifyOnSignal:NO];
+#endif
+
+ childPIDs = calloc(WOHttpAdaptorForkCount + 1, sizeof(pid_t));
+ for (i = 0; i < WOHttpAdaptorForkCount; i++) {
+ childPIDs[i] = fork();
+
+ if (childPIDs[i] == 0) {
+ /* child */
+ isForkMaster = NO;
+ return;
+ }
+ else if (childPIDs[i] > 0)
+ printf("Note: successfully forked child: %i\n", childPIDs[i]);
+ else
+ [self errorWithFormat:@"failed to fork child %i.", i];
+ }
+}
+- (void)killChildren {
+ int i;
+
+ if (!isForkMaster)
+ return;
+
+ for (i = 0; i < WOHttpAdaptorForkCount; i++) {
+ if (childPIDs[i] != 0)
+ kill(childPIDs[i], SIGKILL);
+ }
+}
+
+- (void)checkStatusOfChildren {
+ /*
+ Note: currently this does not refork crashed processes. Reforking is harder
+ than it may sound because the crash can happen at arbitary execution
+ states.
+ That is, the "master process" is not virgin anymore, eg it might have
+ open database connections.
+
+ So the solution might be to refork the whole cluster once a minimum
+ backend threshold is reached.
+ */
+ unsigned int i;
+
+ if (!isForkMaster)
+ return;
+
+ for (i = 0; i < WOHttpAdaptorForkCount; i++) {
+ pid_t result;
+ int status;
+
+ if (childPIDs[i] == 0)
+ continue;
+
+ result = waitpid(childPIDs[i], &status, WNOHANG);
+ if (result == 0) /* did not exit yet */
+ continue;
+
+ if (result == -1) { /* error */
+ [self errorWithFormat:@"failed to get status of child %i: %s",
+ childPIDs[i], strerror(errno)];
+ continue;
+ }
+
+ [self logWithFormat:@"Note: child %i terminated.", childPIDs[i]];
+ childPIDs[i] = 0;
+ }
+}
+
/* events */
- (void)handleSIGPIPE:(int)_signal {
[self warnWithFormat:@"caught SIGPIPE !"];
}
+- (void)handleSIGCHLD:(int)_signal {
+ [self checkStatusOfChildren];
+}
- (void)registerForEvents {
int backlog;
addFileObject:self->socket
activities:NSPosixReadableActivity
forMode:NSDefaultRunLoopMode];
+
+ [self forkChildren];
}
- (void)unregisterForEvents {
+ [self killChildren];
+
[(WORunLoop *)[WORunLoop currentRunLoop]
removeFileObject:self->socket forMode:NSDefaultRunLoopMode];
[[NSNotificationCenter defaultCenter] removeObserver:self];