/* ** When a timer has expired, we dispatch the event handler and re-register the ** timer with the next expiration time if repetitive. Otherwise we just leave ** it */ PRIVATE int Timer_dispatch (HTList * cur, HTList * last) { HTTimer * timer; int ret = HT_ERROR; timer = (HTTimer *)HTList_objectOf(cur); if (timer == NULL) { #if 0 HTDEBUGBREAK("Timer dispatch couldn't find a timer\n"); #endif CLEARME(timer); return HT_ERROR; } #ifdef WWW_WIN_ASYNC /* 2000/07/31 Jens Meggers ([email protected]): On Windows, timers are always repetitive, so we have to delete the timer */ if (DeletePlatformTimer) DeletePlatformTimer(timer); #endif /* WWW_WIN_ASYNC */ if (timer->repetitive) HTTimer_new(timer, timer->cbf, timer->param, timer->millis, YES, YES); else HTList_quickRemoveElement(cur, last); HTTRACE(THD_TRACE, "Timer....... Dispatch timer %p\n" _ timer); ret = (*timer->cbf) (timer, timer->param, HTEvent_TIMEOUT); return ret; }
PUBLIC BOOL HTTimer_refresh (HTTimer * timer, ms_t now) { if (timer == NULL || timer->repetitive == NO) return NO; if (HTTimer_new(timer, timer->cbf, timer->param, timer->millis, YES, YES) == NULL) return NO; return YES; }
PRIVATE int HTBufferWriter_lazyFlush (HTOutputStream * me) { HTNet * net; int delay; if (me->read <= me->data) { return HT_OK; /* nothing to flush */ } /* ** If we are allowed to delay the flush then set a timer with the ** delay descibed by our delay variable. If we can't delay then flush ** right away. */ delay = HTHost_findWriteDelay(me->host, me->lastFlushTime, me->read - me->data); /* ** Flush immediately */ if (!delay) { int status; HTTRACE(STREAM_TRACE, "Buffer...... Flushing %p\n" _ me); if ((status = HTBufferWriter_flush(me)) && me->timer) { HTTimer_delete(me->timer); me->timer = NULL; } return status; } /* ** Set a timer and tell the host we've done the write if ** we have not already started a timer earlier. If a timer ** does already exist then make sure that it hasn't expired. ** This can be the case if we have a really slow client that ** can't parse the data fast enough. */ if (!me->timer) { net = HTHost_getWriteNet(me->host); me->timer = HTTimer_new(NULL, FlushEvent, me, delay, YES, NO); HTHost_unregister(me->host, net, HTEvent_WRITE); HTTRACE(STREAM_TRACE, "Buffer...... Waiting %dms on %p\n" _ delay _ me); } else { if (HTTimer_hasTimerExpired(me->timer)) { HTTRACE(STREAM_TRACE, "Buffer...... Dispatching old timer %p\n" _ me->timer); HTTimer_dispatch(me->timer); me->timer = NULL; } else { HTTRACE(STREAM_TRACE, "Buffer...... Waiting on unexpired timer %p\n" _ me->timer); } } return HT_OK; }
/* ** For a given socket, reqister a request structure, a set of operations, ** a HTEventCallback function, and a priority. For this implementation, ** we allow only a single HTEventCallback function for all operations. ** and the priority field is ignored. */ PUBLIC int HTEventList_register (SOCKET s, HTEventType type, HTEvent * event) { int newset = 0; SockEvents * sockp; HTTRACE(THD_TRACE, "Event....... Register socket %d, request %p handler %p type %s at priority %d\n" _ s _ (void *) event->request _ (void *) event->cbf _ HTEvent_type2str(type) _ (unsigned) event->priority); if (s==INVSOC || HTEvent_INDEX(type) >= HTEvent_TYPES) return 0; /* ** Insert socket into appropriate file descriptor set. We also make sure ** that it is registered in the global set. */ HTTRACE(THD_TRACE, "Event....... Registering socket for %s\n" _ HTEvent_type2str(type)); sockp = SockEvents_get(s, SockEvents_mayCreate); sockp->s = s; sockp->events[HTEvent_INDEX(type)] = event; newset = EventList_remaining(sockp); #ifdef WWW_WIN_ASYNC if (WSAAsyncSelect(s, HTSocketWin, HTwinMsg, HTEvent_BITS(newset)) < 0) { HTTRACE(THD_TRACE, "Event....... WSAAsyncSelect returned `%s'!" _ HTErrnoString(socerrno)); return HT_ERROR; } #else /* WWW_WIN_ASYNC */ FD_SET(s, FdArray+HTEvent_INDEX(type)); HTTRACEDATA((char *) FdArray+HTEvent_INDEX(type), 8, "HTEventList_register: (s:%d)" _ s); if (s > MaxSock) { MaxSock = s ; HTTRACE(THD_TRACE, "Event....... New value for MaxSock is %d\n" _ MaxSock); } #endif /* !WWW_WIN_ASYNC */ /* ** If the timeout has been set (relative in millis) then we register ** a new timeout for this event unless we already have a timer. */ if (event->millis >= 0) { sockp->timeouts[HTEvent_INDEX(type)] = HTTimer_new(sockp->timeouts[HTEvent_INDEX(type)], EventListTimerHandler, sockp, event->millis, YES, YES); } return HT_OK; }
PUBLIC int HTMemLog_open (char * logName, size_t size, BOOL keepOpen) { #ifdef HTDEBUG #ifdef USE_SYSLOG openlog(LogName, LOG_NDELAY, LOG_USER); #else /* USE_SYSLOG */ LogName = logName; KeepOpen = keepOpen; if ((LogFd = open(LogName, OPEN_FLAGS, 0666)) == -1) return HT_ERROR; if (!KeepOpen) close(LogFd); LogBuffSize = size; if ((LogBuff = (char *) HT_MALLOC(size)) == NULL) HT_OUTOFMEM("HTMemLog_open"); LogLen = 0; #endif /* !USE_SYSLOG */ HTTraceData_setCallback(HTMemLog_callback); Timer = HTTimer_new(NULL, MemLogTimeout, NULL, 10000, YES, YES); #endif /* HTDEBUG */ return HT_OK; }
PRIVATE int FileEvent (SOCKET soc, void * pVoid, HTEventType type) { file_info *file = pVoid; /* Specific access information */ int status = HT_ERROR; HTNet * net = file->net; HTRequest * request = HTNet_request(net); HTParentAnchor * anchor = HTRequest_anchor(request); /* ** Initiate a new file structure and bind to request structure ** This is actually state FILE_BEGIN, but it can't be in the state ** machine as we need the structure first. */ if (type == HTEvent_CLOSE) { /* Interrupted */ HTRequest_addError(request, ERR_FATAL, NO, HTERR_INTERRUPTED, NULL, 0, "HTLoadFile"); FileCleanup(request, HT_INTERRUPTED); return HT_OK; } /* Now jump into the machine. We know the state from the previous run */ while (1) { switch (file->state) { case FS_BEGIN: /* We only support safe (GET, HEAD, etc) methods for the moment */ if (!HTMethod_isSafe(HTRequest_method(request))) { HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_ALLOWED, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; break; } /* Check whether we have access to local disk at all */ if (HTLib_secure()) { HTTRACE(PROT_TRACE, "LoadFile.... No access to local file system\n"); file->state = FS_TRY_FTP; break; } file->local = HTWWWToLocal(HTAnchor_physical(anchor), "", HTRequest_userProfile(request)); if (!file->local) { file->state = FS_TRY_FTP; break; } /* Create a new host object and link it to the net object */ { HTHost * host = NULL; if ((host = HTHost_new("localhost", 0)) == NULL) return HT_ERROR; HTNet_setHost(net, host); if (HTHost_addNet(host, net) == HT_PENDING) { HTTRACE(PROT_TRACE, "HTLoadFile.. Pending...\n"); /* move to the hack state */ file->state = FS_PENDING; return HT_OK; } } file->state = FS_DO_CN; break; case FS_PENDING: /* ** 2000/08/10 JK : This is a funny state. Because of the ** internal libwww stacks, when doing multiple local ** requests (e.g., while using the Robot), we need to ask ** again for the host object. If we had jumped directly to ** the FS_DO_CN state, libwww would have blocked because ** of socket starvation. ** This state is similar to FS_BEGINNING, but just requests ** the host object. ** YES. THIS IS AN UGLY HACK!! */ { HTHost * host = NULL; if ((host = HTHost_new("localhost", 0)) == NULL) return HT_ERROR; HTNet_setHost(net, host); if (HTHost_addNet(host, net) == HT_PENDING) { HTTRACE(PROT_TRACE, "HTLoadFile.. Pending...\n"); file->state = FS_PENDING; return HT_OK; } } file->state = FS_DO_CN; break; case FS_DO_CN: /* ** If we have to do content negotiation then find the object that ** fits best into either what the client has indicated in the ** accept headers or what the client has registered on its own. ** The object chosen can in fact be a directory! However, content ** negotiation only makes sense if we can read the directory! ** We stat the file in order to find the size and to see it if ** exists. */ if (HTRequest_negotiation(request) && HTMethod_isSafe(HTRequest_method(request))) { char * conneg = HTMulti(request, file->local,&file->stat_info); if (conneg) { HT_FREE(file->local); file->local = conneg; HTAnchor_setPhysical(anchor, conneg); HTTRACE(PROT_TRACE, "Load File... Found `%s\'\n" _ conneg); } else { HTTRACE(PROT_TRACE, "Load File... Not found - even tried content negotiation\n"); HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; break; } } else { if (HT_STAT(file->local, &file->stat_info) == -1) { HTTRACE(PROT_TRACE, "Load File... Not found `%s\'\n" _ file->local); HTRequest_addError(request, ERR_FATAL, NO, HTERR_NOT_FOUND, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; break; } } /* ** Check to see if the 'localname' is in fact a directory. ** Note that we can't do a HEAD on a directory */ if (((file->stat_info.st_mode) & S_IFMT) == S_IFDIR) { if (HTRequest_method(request) == METHOD_GET) file->state = FS_PARSE_DIR; else { HTRequest_addError(request, ERR_INFO, NO, HTERR_NO_CONTENT, NULL, 0, "HTLoadFile"); file->state = FS_NO_DATA; } break; } /* ** If empty file then only serve it if it is editable. We also get ** the bindings for the file suffixes in lack of better bindings */ { BOOL editable = HTEditable(file->local, &file->stat_info); if (file_suffix_binding) HTBind_getAnchorBindings(anchor); if (editable) HTAnchor_appendAllow(anchor, METHOD_PUT); /* Set the file size */ if (file->stat_info.st_size) HTAnchor_setLength(anchor, file->stat_info.st_size); /* Set the file last modified time stamp */ if (file->stat_info.st_mtime > 0) HTAnchor_setLastModified(anchor, file->stat_info.st_mtime); /* Check to see if we can edit it */ if (!editable && !file->stat_info.st_size) { HTRequest_addError(request, ERR_INFO, NO, HTERR_NO_CONTENT, NULL, 0, "HTLoadFile"); file->state = FS_NO_DATA; } else { file->state = (HTRequest_method(request)==METHOD_GET) ? FS_NEED_OPEN_FILE : FS_GOT_DATA; } } break; case FS_NEED_OPEN_FILE: status = HTFileOpen(net, file->local, HT_FB_RDONLY); if (status == HT_OK) { /* ** Create the stream pipe FROM the channel to the application. ** The target for the input stream pipe is set up using the ** stream stack. */ { HTStream * rstream = HTStreamStack(HTAnchor_format(anchor), HTRequest_outputFormat(request), HTRequest_outputStream(request), request, YES); HTNet_setReadStream(net, rstream); HTRequest_setOutputConnected(request, YES); } /* ** Create the stream pipe TO the channel from the application ** and hook it up to the request object */ { HTOutputStream * output = HTNet_getOutput(net, NULL, 0); HTRequest_setInputStream(request, (HTStream *) output); } /* ** Set up concurrent read/write if this request isn't the ** source for a PUT or POST. As source we don't start reading ** before all destinations are ready. If destination then ** register the input stream and get ready for read */ if (HTRequest_isSource(request) && !HTRequest_destinationsReady(request)) return HT_OK; HTRequest_addError(request, ERR_INFO, NO, HTERR_OK, NULL, 0, "HTLoadFile"); file->state = FS_NEED_BODY; /* If we are _not_ using preemptive mode and we are Unix fd's ** then return here to get the same effect as when we are ** connecting to a socket. That way, HTFile acts just like any ** other protocol module even though we are in fact doing ** blocking connect */ if (HTEvent_isCallbacksRegistered()) { if (!HTRequest_preemptive(request)) { if (!HTNet_preemptive(net)) { HTTRACE(PROT_TRACE, "HTLoadFile.. Returning\n"); HTHost_register(HTNet_host(net), net, HTEvent_READ); } else if (!file->timer) { HTTRACE(PROT_TRACE, "HTLoadFile.. Returning\n"); file->timer = HTTimer_new(NULL, ReturnEvent, file, 1, YES, NO); } return HT_OK; } } } else if (status == HT_WOULD_BLOCK || status == HT_PENDING) return HT_OK; else { HTRequest_addError(request, ERR_INFO, NO, HTERR_INTERNAL, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; /* Error or interrupt */ } break; case FS_NEED_BODY: status = HTHost_read(HTNet_host(net), net); if (status == HT_WOULD_BLOCK) return HT_OK; else if (status == HT_LOADED || status == HT_CLOSED) { file->state = FS_GOT_DATA; } else { HTRequest_addError(request, ERR_INFO, NO, HTERR_FORBIDDEN, NULL, 0, "HTLoadFile"); file->state = FS_ERROR; } break; case FS_PARSE_DIR: status = HTFile_readDir(request, file); if (status == HT_LOADED) file->state = FS_GOT_DATA; else file->state = FS_ERROR; break; case FS_TRY_FTP: { char *url = HTAnchor_physical(anchor); HTAnchor *anchor; char *newname = NULL; StrAllocCopy(newname, "ftp:"); if (!strncmp(url, "file:", 5)) StrAllocCat(newname, url+5); else StrAllocCat(newname, url); anchor = HTAnchor_findAddress(newname); HTRequest_setAnchor(request, anchor); HT_FREE(newname); FileCleanup(request, HT_IGNORE); return HTLoad(request, YES); } break; case FS_GOT_DATA: FileCleanup(request, HT_LOADED); return HT_OK; break; case FS_NO_DATA: FileCleanup(request, HT_NO_DATA); return HT_OK; break; case FS_RETRY: FileCleanup(request, HT_RETRY); return HT_OK; break; case FS_ERROR: FileCleanup(request, HT_ERROR); return HT_OK; break; } } /* End of while(1) */ }