strdict *sd_new(int hint) { strdict *sd = WOMALLOC(sizeof(strdict)); sd->capacity = (hint != 0) ? hint : 8; sd->head = WOMALLOC(sizeof(strdictel) * sd->capacity); sd->count = 0; return sd; }
XMLCTokenizer *xmlcTokenizerInit() { XMLCTokenizer *s = (XMLCTokenizer*)WOMALLOC(sizeof(XMLCTokenizer)); #if usePropertyTable XMLCUInt i; /* create character property table */ /* FIXME: creating property table not thread safe */ for (i=0; i<0x100; ++i) { XMLCUInt properties = 0; if (isLetterTest(i)) properties = properties | PROPERTY_isLetter; if (isNameStartCharTest(i)) properties = properties | PROPERTY_isNameStartChar; if (isDigitTest(i)) properties = properties | PROPERTY_isDigit; if (isNameCharTest(i)) properties = properties | PROPERTY_isNameChar; if (isEOLTest(i)) properties = properties | PROPERTY_isEOL; if (isWhiteSpaceTest(i)) properties = properties | PROPERTY_isWhiteSpace; characterPropertiesTable[i] = properties; } #endif xmlcTokenizerReset(s); return s; }
strtbl *st_new(int hint) { strtbl *st = WOMALLOC(sizeof(strtbl)); if (st) { memset(st, 0, sizeof(strtbl)); st_setCapacity(st, hint); } return st; }
/* * The entry point for the parser. * Returns nonzero if there was an error during parsing. */ static int xml_parseConfiguration(char *buf, int len) { XMLCDocumentHandler handler; WOXMLEdits config; XMLCParser *parser; int error = 0, i; /* initialize the config struct */ config.current_element = NULL; config.new_apps = wolist_new(16); config.current_app = NULL; config.current_app_instances = NULL; config.current_instance = NULL; config.new_app_instances = wolist_new(16); config.error = 0; config.errorLocation = buf; if (len == 0) return 1; /* no content is considered an error */ /* Set up a new document handler struct for the parser */ memcpy(&handler, &_document, sizeof(XMLCDocumentHandler)); handler.document = (XMLCDocument *)(&config); /* set up and invoke the xml parser */ parser = xmlcParserInit(); xmlcTokenizerSetBuffer(parser->tokenizer, buf, len); xmlcParserSetPreserveWhiteSpace(parser, 0); error = (int)xmlcParserParse(parser, &handler); if (error != 0) { /* config error */ WOLog(WO_ERR,"Error parsing configuration: %s", xmlcParserErrorDescription(error)); if ((intptr_t)config.errorLocation < (intptr_t)buf + len) { char *badconfig = WOMALLOC((len+1)*sizeof(char)); strncpy(badconfig, buf, len); badconfig[len] = '\0'; WOLog(WO_ERR,"Error near:\n%s", config.errorLocation); WOFREE(badconfig); } } else { /* * load the new settings... */ if (config.new_apps) for (i=0; i<wolist_count(config.new_apps); i++) ac_updateApplication((strtbl *)wolist_elementAt(config.new_apps, i), (list *)wolist_elementAt(config.new_app_instances, i)); } /* clean up and return */ freeWOXMLEdits(&config); xmlcParserDealloc(parser); return error; }
static void copyHeaderForServerVariable(char *var, EXTENSION_CONTROL_BLOCK *p, HTTPRequest *req) { char *buf, *value, stackBuf[2048]; DWORD bufsz = sizeof(stackBuf), pos; WOLog(WO_DBG, "reading buffer for server variable %s", var); if (p->GetServerVariable(p->ConnID, var, stackBuf, &bufsz) == TRUE) { buf = stackBuf; } else { if(GetLastError() == 122) // ERROR_INSUFFICIENT_BUFFER { WOLog(WO_DBG, "buffer too small; need %d", bufsz); buf = WOMALLOC(bufsz); if (p->GetServerVariable(p->ConnID, var, buf, &bufsz) != TRUE) { WOFREE(buf); WOLog(WO_ERR, "Could not get header."); return; } } else return; // header not set } if (buf) { //WOLog(WO_DBG, "got raw buffer: %s", buf); pos = 0; while (pos < bufsz) { while (pos < bufsz && buf[pos] < 33) pos++; if (pos < bufsz) { /* got start of new value */ value = &buf[pos]; do { while (pos < bufsz && buf[pos] != '\r') pos++; if (pos + 2 < bufsz && buf[pos+1] == '\n' && (buf[pos+2] == ' ' || buf[pos+2] == '\t')) { /* got a multiline header; change CRLF to whitespace and keep parsing */ buf[pos] = ' '; buf[pos+1] = ' '; } else { buf[pos] = 0; } } while (pos < bufsz && buf[pos] != 0); WOLog(WO_DBG, "found value=\"%s\"", value ? value : "(NULL)"); if (value) req_addHeader(req, var, value, STR_COPYKEY|STR_COPYVALUE); else req_addHeader(req, var, "", STR_COPYKEY); } } if (buf != stackBuf) WOFREE(buf); } return; }
int req_sendRequest(HTTPRequest *req, net_fd socket) { struct iovec *buffers, *bufferIterator; int bufferCount, result; buffers = WOMALLOC((st_count(req->headers) * 4 + 3) * sizeof(struct iovec)); buffers[0].iov_base = req->request_str; buffers[0].iov_len = strlen(req->request_str); bufferIterator = &buffers[1]; st_perform(req->headers, (st_perform_callback)setupIOVec, &bufferIterator); bufferIterator->iov_base = "\r\n"; bufferIterator->iov_len = 2; bufferCount = st_count(req->headers) * 4 + 2; if (req->content_length > 0) { bufferCount++; bufferIterator++; bufferIterator->iov_base = req->content; bufferIterator->iov_len = req->content_buffer_size; } result = transport->sendBuffers(socket, buffers, bufferCount); /* If we are streaming the content data, continue until we have sent everything. */ /* Note that we reuse buffers, and the existing content-data buffer. */ if (req->content_length > req->content_buffer_size) { long total_sent = req->content_buffer_size; long len_read, amount_to_read; req->haveReadStreamedData = 1; while (total_sent < req->content_length && result == 0) { amount_to_read = req->content_length - total_sent; if (amount_to_read > req->content_buffer_size) amount_to_read = req->content_buffer_size; len_read = req->getMoreContent(req, req->content, amount_to_read, 0); if (len_read > 0) { buffers[0].iov_base = req->content; buffers[0].iov_len = len_read; result = transport->sendBuffers(socket, buffers, 1); total_sent += len_read; } else if (len_read < 0) { WOLog(WO_ERR, "Failed to read streamed content."); result = -1; } } } WOFREE(buffers); if (result == 0) result = transport->flush_connection(socket); else WOLog(WO_ERR, "error sending request"); return result; }
void req_allocateContent(HTTPRequest *req, unsigned content_length, int allowStreaming) { if (req) { req->content_buffer_size = content_length; if (allowStreaming && req->content_buffer_size > REQUEST_STREAMED_THRESHOLD) { req->content_buffer_size = REQUEST_STREAMED_THRESHOLD; WOLog(WO_DBG, "req_allocateContent(): content will be streamed. content length = %d", content_length); } req->content = WOMALLOC(req->content_buffer_size); if (!req->content) req->content_buffer_size = 0; } }
void sd_setCapacity(strdict *sd, unsigned newsize) { strdictel *newElements; if (sd->head != NULL) newElements = WOREALLOC(sd->head, newsize * sizeof(strdictel)); else newElements = WOMALLOC(newsize * sizeof(strdictel)); if (newElements) { sd->head = newElements; sd->capacity = newsize; } return; }
/* * Called from the xml parser. * Here is where we begin parsing <application>... or <instance>... * or <adaptor>... */ static void createElement(XMLCDocument *document, const XMLCCharacter *name, const unsigned int length) { WOXMLEdits *config = (WOXMLEdits *)document; if (config->error != 0) /* would be nice to tell parser to stop */ return; config->errorLocation = name; if (length == 7 && strncasecmp(name, "adaptor", length) == 0) { /* do nothing; don't generate a warning */ } else if (length == 11 && strncasecmp(name, "application", length) == 0) { /* begin <application> */ if (config->current_element != NULL) { WOLog(WO_ERR,"Error parsing config: found unexpected <application> tag"); config->error = 1; return; } /* create new app settings dictionary, instance list, and set current_element to the app dictionary*/ config->current_app = st_new(8); wolist_add(config->new_apps, config->current_app); config->current_app_instances = wolist_new(8); wolist_add(config->new_app_instances, config->current_app_instances); config->current_element = config->current_app; } else if (length == 8 && strncasecmp(name, "instance", length) == 0) { /* begin <instance> */ if (config->current_element != config->current_app || config->current_app == NULL) { WOLog(WO_ERR,"Error parsing config: found unexpected <instance> tag"); config->error = 1; return; } /* create new instance settings dictionary and set current_element to point to it */ config->current_instance = st_new(8); wolist_add(config->current_app_instances, config->current_instance); config->current_element = config->current_instance; } else { /* Got something unexpected. Ignore the tag. */ char *buffer = WOMALLOC(length+1); strncpy(buffer,name,length); buffer[length] = '\0'; WOLog(WO_WARN,"Unknown tag in XML: \"%s\"",buffer); config->current_element = NULL; WOFREE(buffer); } return; }
WA_recursiveLock WA_createLock(const char *name) { WinRecursiveLock *lock; lock = WOMALLOC(sizeof(WinRecursiveLock)); if (lock) { lock->lock = CreateMutex(NULL, FALSE, NULL); if (name) lock->name = name; else lock->name = unnamedLock; } else WOLog(WO_ERR, "WA_createLock(): could not malloc"); return (WA_recursiveLock)lock; }
/* * Attempt to expand the capacity of the string table. * If the allocation fails, the string table is unchanged. */ static void st_setCapacity(strtbl *st, unsigned newsize) { strtblelem *newHead; if (newsize < MIN_CAPACITY) newsize = MIN_CAPACITY; if (st->head != NULL) newHead = WOREALLOC(st->head, newsize * sizeof(strtblelem)); else newHead = WOMALLOC(newsize * sizeof(strtblelem)); if (newHead) { st->head = newHead; st->capacity = newsize; } else WOLog(WO_ERR, "st_setCapacity(): failed to expand capacity (%d)", newsize); return; }
/* You're responsible for freeing the header returned ! */ static char *getHeader(EXTENSION_CONTROL_BLOCK *p, const char *key) { #define BUFSZ 2048 char temp_hdr[BUFSZ]; DWORD len = BUFSZ; char *hdr = NULL; if (p->GetServerVariable(p->ConnID, (char *)key, temp_hdr, &len) == TRUE) { if (len!=0) { /* len includes the terminating 0 */ hdr = WOMALLOC((len)*sizeof(char)); strncpy(hdr, temp_hdr, len); hdr[len-1]='\0'; return hdr; } } return NULL; }
WA_recursiveLock WA_createLock(const char *name) { CThreadRecursiveLock *lock; lock = WOMALLOC(sizeof(CThreadRecursiveLock)); if (lock) { mutex_init(&lock->m); condition_init(&lock->c); lock->lockCount = 0; lock->lockingThread = NULL; if (name) lock->name = name; else lock->name = unnamedLock; } return lock; }
WA_recursiveLock WA_createLock(const char *name) { PThreadRecursiveLock *lock; lock = WOMALLOC(sizeof(PThreadRecursiveLock)); if (lock) { pthread_mutex_init(&lock->m, NULL); pthread_cond_init(&lock->c, NULL); lock->lockCount = 0; lock->lockingThread = 0; if (name) lock->name = name; else lock->name = unnamedLock; } return lock; }
WA_recursiveLock WA_createLock(const char *name) { NSAPIThreadRecursiveLock *lock; lock = WOMALLOC(sizeof(NSAPIThreadRecursiveLock)); if (lock) { lock->crit = crit_init(); lock->condvar = condvar_init(lock->crit); lock->lockCount = 0; lock->lockingThread = NULL; if (name) lock->name = name; else lock->name = unnamedLock; } if (!lock) WOLog(WO_ERR, "WA_createLock() failed for lock %s", name ? name : unnamedLock); return lock; }
/* * reformat URL to conform to our norms. also has side effect of * setting 'REQUEST_METHOD' header */ void req_reformatRequest(HTTPRequest *req, WOAppReq *app, WOURLComponents *wc, const char *http_version) { char *default_http_version = "HTTP/1.1"; int http_version_length = http_version ? strlen(http_version) : strlen(default_http_version); wc->applicationName.start = app->name; wc->applicationName.length = strlen(app->name); wc->applicationNumber.start = app->instance; /* note that this is by reference */ wc->applicationNumber.length = strlen(app->instance); wc->applicationHost.start = app->host; wc->applicationHost.length = strlen(app->host); if (req->request_str) WOFREE(req->request_str); /* METHOD + SizeURL + SPACE + http_version_length + \r\n + NULL) */ req->request_str = WOMALLOC(strlen(req->method_str) + 1 + SizeURL(wc) + 1 + http_version_length + 2 + 1); strcpy(req->request_str, req->method_str); strcat(req->request_str," "); req_addHeader(req, REQUEST_METHOD_HEADER, req->method_str, 0); ComposeURL(req->request_str + strlen(req->request_str), wc, req->shouldProcessUrl); strcat(req->request_str," "); if (http_version) { strcat(req->request_str,http_version); if (strcasecmp(http_version,"HTTP/1.1") == 0) { req_addHeader(req, "Host", app->host, 0); } } else { strcat(req->request_str,default_http_version); req_addHeader(req, "Host", app->host, 0); } strcat(req->request_str,"\r\n"); WOLog(WO_INFO,"New request is %s",req->request_str); return; }
/* * Obtain a lock on a chunk of shared memory. The range of memory * begins add address addr, and is size bytes in length. If exclusive * is zero the lock is a shared lock for read only access to the region. * If exclusive is nonzero the lock is an exclusive lock for write * access to the region. * The return value is a handle which must be supplied to the WOShmem_unlock * function when the lock is released. If some error occurrs and * a lock could not be obtained, NULL is returned. */ void *WOShmem_lock(const void *addr, size_t size, int exclusive) { struct flock *lockInfo; ptrdiff_t offset; LockInfo *info = NULL; if (addr && WOShmem_fd != -1) { offset = addr_to_offset(addr); if (offset >= 0 && offset + size < WOShmem_size) { /* This gets called a lot, so as an optimization to avoid the malloc() overhead */ /* we keep a cache of LockInfo's. */ WA_lock(WOShmem_mutex); info = WOShmem_lockInfoCache; if (info) WOShmem_lockInfoCache = info->cache; WA_unlock(WOShmem_mutex); /* if there wasn't one in the cache, malloc a new one */ if (!info) info = WOMALLOC(sizeof(LockInfo)); if (info) { lockInfo = &info->flockInfo; if (lock_file_section(WOShmem_fd, offset, size, lockInfo, exclusive)) { /* failed; put the info struct back on the cache */ WA_lock(WOShmem_mutex); info->cache = WOShmem_lockInfoCache; WOShmem_lockInfoCache = info; WA_unlock(WOShmem_mutex); info = NULL; } } } } return info; }
int resp_getResponseContent(HTTPResponse *resp, int allowStreaming) { int ret = 0; if (resp->content_length) { int count, amountToRead; if (resp->content == NULL) { resp->content_buffer_size = resp->content_length; if (allowStreaming && (resp->content_buffer_size > RESPONSE_STREAMED_SIZE)) resp->content_buffer_size = RESPONSE_STREAMED_SIZE; resp->content = WOMALLOC(resp->content_buffer_size); } amountToRead = resp->content_length - resp->content_read; if (amountToRead > resp->content_buffer_size) amountToRead = resp->content_buffer_size; count = transport->recvbytes(resp->instanceConnection->fd, resp->content, amountToRead); if (count != amountToRead) { // 2009/06/09: either the content length was wrong, unset or the // application has died. Please use the return value // to resolve this situation in the caller method (if // desired): this method returns 0 on success, -1 if no // data was received from the WebObjects application and // > 0 if an incomplete data package was received (the // return value describes the number of received bytes // in such a situation). WOLog(WO_WARN, "Received an unexpected number of bytes (expected %d bytes, got %d)", amountToRead, count); resp->content_valid = 0; ret = ((count == 0)? -1 : count); } else { resp->content_read += amountToRead; resp->content_valid = amountToRead; } } if(ret != 0) resp->flags |= RESP_LENGTH_INVALID; return ret; }
ShmemArray *sha_alloc(const char *name, void *arrayBase, size_t elementSize, unsigned int elementCount) { ShmemArray *array; int i; array = WOMALLOC(sizeof(ShmemArray) + sizeof(ShmemArrayElement) * (elementCount-1)); if (array) { array->name = WOSTRDUP(name); array->elementSize = elementSize; array->elementCount = elementCount; for (i=0; i<array->elementCount; i++) { array->elements[i].element = (void *)(arrayBase + elementSize * i); array->elements[i].lock = WA_createLock("array element lock"); array->elements[i].writeLock = WA_createLock("array element write lock"); array->elements[i].lockCount = 0; array->elements[i].lockHandle = NULL; array->elements[i].localData = NULL; array->elements[i].localDataCleanupCallbacks = NULL; } } return array; }
/* * the thing that gets called... */ __declspec(dllexport) DWORD HttpExtensionProc(EXTENSION_CONTROL_BLOCK *p) { HTTPRequest *req; HTTPResponse *resp = NULL; WOURLComponents wc = WOURLComponents_Initializer; const char *reqerr; const char *qs; char *script_name; char *server_protocol; char *uri; WOURLError urlerr; if (!adaptorEnabled) { WOLog(WO_ERR, "WebObjects adaptor disabled."); return HSE_STATUS_ERROR; } /* * extract WebObjects request components from URI */ script_name = getHeader(p, CGI_SCRIPT_NAME); uri = WOMALLOC(strlen(p->lpszPathInfo) + strlen(script_name) + 1); strcpy(uri, script_name); strcat(uri, p->lpszPathInfo); WOLog(WO_INFO,"<WebObjects ISAPI> new request: %s", uri); WOFREE(script_name); urlerr = WOParseApplicationName(&wc, uri); if (urlerr != WOURLOK) { const char *_urlerr; _urlerr = WOURLstrerror(urlerr); WOLog(WO_INFO,"URL Parsing Error: %s", _urlerr); if (urlerr == WOURLInvalidApplicationName) { if (ac_authorizeAppListing(&wc)) { resp = WOAdaptorInfo(NULL, &wc); WOFREE(uri); /* this has to be freed before a return in this function */ return die_resp(p, resp); } else { WOFREE(uri); /* this has to be freed before a return in this function */ return die(p, _urlerr, HTTP_NOT_FOUND); } } WOFREE(uri); /* this has to be freed before a return in this function */ return die(p, _urlerr, HTTP_BAD_REQUEST); } /* * build the request... */ req = req_new(p->lpszMethod, NULL); /* * validate the method */ reqerr = req_validateMethod(req); if (reqerr) { req_free(req); WOFREE(uri); /* this has to be freed before a return in this function */ return die(p,reqerr, HTTP_BAD_REQUEST); } /* * get the headers.... */ copyHeaders(p, req); /* * get form data if any * assume that POSTs with content length will be reformatted to GETs later */ req->content_length = p->cbTotalBytes; if (req->content_length > 0) { char *buffer = WOMALLOC(req->content_length); if (p->cbAvailable > 0) memcpy(buffer, p->lpbData, p->cbAvailable); /* * IIS has a weird (or is it convenient?) data queuing mechanism... */ if (req->content_length > p->cbAvailable) { DWORD len; DWORD totalLength, fetchedLength; len = req->content_length - p->cbAvailable; totalLength = len; fetchedLength = 0; while (fetchedLength < totalLength) { len = totalLength - fetchedLength; if (p->ReadClient (p->ConnID,buffer + p->cbAvailable + fetchedLength, &len) != TRUE) { WOFREE(buffer); req_free(req); return die(p, INV_FORM_DATA, HTTP_BAD_REQUEST); } fetchedLength += len; } } req->content = buffer; if (req_HeaderForKey(req, CONTENT_LENGTH) == NULL) { char *length; length = (char *)WOMALLOC(32); if (length) { sprintf(length,"%d",req->content_length); req_addHeader(req, CONTENT_LENGTH, length, STR_FREEVALUE); } if (p->lpszContentType != NULL) req_addHeader(req, CONTENT_TYPE, p->lpszContentType, 0); } } /* Always get the query string */ qs = p->lpszQueryString; wc.queryString.start = qs; wc.queryString.length = qs ? strlen(qs) : 0; /* * message the application & collect the response * * note that handleRequest free()'s the 'req' for us */ if (resp == NULL) { /* if no error so far... */ req->api_handle = p; /* stash this... */ server_protocol = getHeader(p, CGI_SERVER_PROTOCOL); resp = tr_handleRequest(req, uri, &wc, server_protocol, NULL); WOFREE(server_protocol); } if (resp != NULL) { sendResponse(p, resp); resp_free(resp); } WOFREE(uri); /* this has to be freed before a return in this function */ req_free(req); #if defined(FINDLEAKS) showleaks(); #endif return HSE_STATUS_SUCCESS; }
/* * Copy Headers into the request. * * This is kind of like an easter egg hunt, CGI equivilant headers are * stashed all over in different pblocks. Do the best we can without * missing any... */ static void copyHeaders(pblock *pb, Session *sn, Request *rq, HTTPRequest *req) { int i; const char *hdrval; char *portstr; const char *server; /* * the following line will generate a compiler warning. uncomment if * Netscape ever implements it. request_loadheaders(sn,rq); */ /* * first, blindly copy the request headers */ for (i=0; i < rq->headers->hsize; i++) { struct pb_entry *entry = rq->headers->ht[i]; while (entry != NULL) { pb_param *hdr = entry->param; if (hdr != NULL) req_addHeader(req, hdr->name, hdr->value, 0); entry = entry->next; } } for (i=0; i < rq->vars->hsize; i++) { struct pb_entry *entry = rq->vars->ht[i]; while (entry != NULL) { pb_param *hdr = entry->param; if (hdr != NULL) { /* * BEGIN Support for getting the client's certificate as one liner. */ if (strcmp((const char *)hdr->name, "auth-cert") == 0 && hdr->value != NULL) { const char *val = (const char *)make_cert_one_line((char *)hdr->value); if(val != NULL){ req_addHeader(req, "SSL_CLIENT_CERT", val, 0); //WOLog(WO_DBG, "Adding server variable %s", hdr->name); //WOLog(WO_DBG, "With value %s", hdr->value); } /* * END Support for getting the client's certificate. */ else { req_addHeader(req, hdr->name, hdr->value, 0); //WOLog(WO_DBG, "Adding server variable %s", hdr->name); //WOLog(WO_DBG, "With value %s", hdr->value); } } } entry = entry->next; } } if (req->method == HTTP_POST_METHOD) req_addHeader(req,"REQUEST_METHOD","POST", 0); else if (req->method == HTTP_HEAD_METHOD) req_addHeader(req,"REQUEST_METHOD","HEAD", 0); else req_addHeader(req,"REQUEST_METHOD","GET", 0); /* * collect up the server specific headers */ cpyhdr("ip", sn->client, req, "REMOTE_ADDR"); cpyhdr("query", rq->reqpb, req, "QUERY_STRING"); cpyhdr("protocol", rq->reqpb, req, "SERVER_PROTOCOL"); hdrval = session_maxdns(sn); if (!hdrval) hdrval = session_dns(sn); if (hdrval) req_addHeader(req, "REMOTE_HOST", hdrval, 0); req_addHeader(req, "SERVER_SOFTWARE", system_version(), 0); portstr = (char *)WOMALLOC(32); if (portstr) { util_itoa(server_portnum, portstr); req_addHeader(req, "SERVER_PORT", portstr, STR_FREEVALUE); } /* * Netscape claims to have fixed this in 3, if it causes a problem * comment it out */ server = server_hostname; if (server != NULL) req_addHeader(req, "SERVER_NAME", server, 0); return; }
int req_sendRequest(HTTPRequest *req, net_fd socket) { struct iovec *buffers; int bufferCount, appStatus; int browserStatus = 0; String *headersString; buffers = WOMALLOC(3 * sizeof(struct iovec)); headersString = str_create(req->request_str, 0); if (headersString) { st_perform(req->headers, (st_perform_callback)req_appendHeader, headersString); } buffers[0].iov_base = headersString->text; buffers[0].iov_len = headersString->length; buffers[1].iov_base = "\r\n"; buffers[1].iov_len = 2; bufferCount = 2; if (req->content_length > 0) { bufferCount++; buffers[2].iov_base = req->content; buffers[2].iov_len = req->content_buffer_size; } appStatus = transport->sendBuffers(socket, buffers, bufferCount); str_free(headersString); /* If we are streaming the content data, continue until we have sent everything. */ /* Note that we reuse buffers, and the existing content-data buffer. */ if (req->content_length > req->content_buffer_size) { long total_sent = req->content_buffer_size; long len_read, amount_to_read; req->haveReadStreamedData = 1; while (total_sent < req->content_length) { amount_to_read = req->content_length - total_sent; if (amount_to_read > req->content_buffer_size) amount_to_read = req->content_buffer_size; len_read = req->getMoreContent(req, req->content, amount_to_read, 0); if (len_read > 0) { if(appStatus == 0) { buffers[0].iov_base = req->content; buffers[0].iov_len = len_read; appStatus = transport->sendBuffers(socket, buffers, 1); // 2009/04/28: in case of a transport error, carry on with reading // incoming input stream (= browser data). That way, // the browser (hopefully) switch to the receive mode // after sending the complete request and receives/shows // the adaptor error message (old behaviour: endless // sending/uploading view). if(appStatus != 0) { WOLog(WO_ERR, "Failed to send streamed content."); } } total_sent += len_read; } else if (len_read < 0) { WOLog(WO_ERR, "Failed to read streamed content."); browserStatus = -1; break; } } } WOFREE(buffers); if(browserStatus != 0) WOLog(WO_ERR, "error receiving request"); if (appStatus == 0) { // 2009/04/30: as long as we haven't received any error message from // the instance, flush the socket to complete the data // transfer! appStatus = transport->flush_connection(socket); } else WOLog(WO_ERR, "error sending request"); return ((appStatus != 0) ? appStatus : browserStatus); }
/* * the thing that gets called... */ __declspec(dllexport) DWORD __stdcall HttpExtensionProc(EXTENSION_CONTROL_BLOCK *p) { HTTPRequest *req; HTTPResponse *resp = NULL; WOURLComponents wc = WOURLComponents_Initializer; const char *reqerr; const char *qs; char *script_name; char *server_protocol; char *uri; WOURLError urlerr; if (!adaptorEnabled) { WOLog(WO_ERR, "WebObjects adaptor disabled."); return HSE_STATUS_ERROR; } // Deactivate IIS 7.x stream buffering // IIS 7.x (and above?) behaves differently from IIS 6 by introducing // output buffering ISAPI Extension output // This could cause interrupted and hence incomplete streaming output // This change does deactivate the output buffering in IIS 7.x // (see http://support.microsoft.com/kb/946086) and does not harm // when called within IIS 6 // p->ServerSupportFunction (p->ConnID, HSE_REQ_SET_FLUSH_FLAG, (LPVOID) TRUE, NULL, NULL ); /* * extract WebObjects request components from URI */ script_name = getHeader(p, CGI_SCRIPT_NAME); uri = WOMALLOC(strlen(p->lpszPathInfo) + strlen(script_name) + 1); strcpy(uri, script_name); strcat(uri, p->lpszPathInfo); WOLog(WO_INFO,"<WebObjects ISAPI> new request: %s", uri); WOFREE(script_name); urlerr = WOParseApplicationName(&wc, uri); if (urlerr != WOURLOK) { const char *_urlerr; _urlerr = WOURLstrerror(urlerr); WOLog(WO_INFO,"URL Parsing Error: %s", _urlerr); if (urlerr == WOURLInvalidApplicationName) { if (ac_authorizeAppListing(&wc)) { resp = WOAdaptorInfo(NULL, &wc); WOFREE(uri); /* this has to be freed before a return in this function */ return die_resp(p, resp); } else { WOFREE(uri); /* this has to be freed before a return in this function */ return die(p, _urlerr, HTTP_NOT_FOUND); } } WOFREE(uri); /* this has to be freed before a return in this function */ return die(p, _urlerr, HTTP_BAD_REQUEST); } /* * build the request... */ req = req_new(p->lpszMethod, NULL); req->api_handle = p; /* * get the headers.... */ copyHeaders(p, req); /* * validate the method */ reqerr = req_validateMethod(req); if (reqerr) { req_free(req); WOFREE(uri); /* this has to be freed before a return in this function */ return die(p,reqerr, HTTP_BAD_REQUEST); } /* * get form data if any * assume that POSTs with content length will be reformatted to GETs later */ req->content_length = p->cbTotalBytes; if (req->content_length > 0) { req_allocateContent(req, req->content_length, 1); req->getMoreContent = (req_getMoreContentCallback)readContentData; req->total_len_read = 0; if (req->content_buffer_size == 0) { WOFREE(uri); /* this has to be freed before a return in this function */ req_free(req); return die(p, ALLOCATION_FAILURE, HTTP_SERVER_ERROR); } if (readContentData(req, req->content, req->content_buffer_size, 1) == -1) { WOFREE(uri); /* this has to be freed before a return in this function */ req_free(req); return die(p, WOURLstrerror(WOURLInvalidPostData), HTTP_BAD_REQUEST); } } /* Always get the query string */ qs = p->lpszQueryString; wc.queryString.start = qs; wc.queryString.length = qs ? strlen(qs) : 0; /* * message the application & collect the response * * note that handleRequest free()'s the 'req' for us */ if (resp == NULL) { /* if no error so far... */ server_protocol = getHeader(p, CGI_SERVER_PROTOCOL); resp = tr_handleRequest(req, uri, &wc, server_protocol, NULL); WOFREE(server_protocol); } if (resp != NULL) { sendResponse(p, resp); resp_free(resp); } WOFREE(uri); /* this has to be freed before a return in this function */ req_free(req); #if defined(FINDLEAKS) showleaks(); #endif return HSE_STATUS_SUCCESS; }
static int readContentData(HTTPRequest *req, void *dataBuffer, int dataSize, int mustFill) { EXTENSION_CONTROL_BLOCK *p = (EXTENSION_CONTROL_BLOCK *)req->api_handle; DWORD len_remaining = dataSize; DWORD total_len_read = 0; char *buffer = (char *)dataBuffer; MS_BOOL readStatus; unsigned int lenZeroCounter = 0; DWORD len; if(p->cbAvailable > req->total_len_read) { len = p->cbAvailable - req->total_len_read; if(len > dataSize) len = dataSize; memcpy(buffer, p->lpbData + req->total_len_read, len); total_len_read += len; len_remaining -= len; } /* * IIS has a weird (or is it convenient?) data queuing mechanism... */ while(len_remaining > 0 && (mustFill || total_len_read == 0)) { len = len_remaining; readStatus = p->ReadClient (p->ConnID,buffer + total_len_read, &len); if(readStatus == TRUE) { // 2009/04/29: avoid endless loops, because the ReadClient function // will return TRUE but with zero bytes read if the // socket on which the server is listening to the client // is closed!!! lenZeroCounter = ((len == 0)? (lenZeroCounter + 1) : 0); if(lenZeroCounter > MAGIC_LEN_ZERO_LIMIT) { readStatus = FALSE; } } if(readStatus != TRUE) { if(lenZeroCounter > MAGIC_LEN_ZERO_LIMIT) { WOLog(WO_ERR,"ReadClient failed (client socket closed?)."); } else { WOLog(WO_ERR,"ReadClient failed"); } die(p, INV_FORM_DATA, HTTP_BAD_REQUEST); return -1; } total_len_read += len; len_remaining -= len; } // still required? - BEGIN if (req_HeaderForKey(req, CONTENT_LENGTH) == NULL) { char *length; length = (char *)WOMALLOC(32); if (length) { sprintf(length,"%lu",req->content_length); req_addHeader(req, CONTENT_LENGTH, length, STR_FREEVALUE); } if (p->lpszContentType != NULL) req_addHeader(req, CONTENT_TYPE, p->lpszContentType, 0); } // still required? - END req->total_len_read += total_len_read; return total_len_read; }
static void copyHeadersAllRaw(EXTENSION_CONTROL_BLOCK *p, HTTPRequest *req) { char *buf, *key, *value, stackBuf[2048]; DWORD bufsz = sizeof(stackBuf), pos; /** client certificate support BYTE* CertificateBuf = (BYTE*)calloc(MAX_CERT_SIZE, sizeof(BYTE)); CERT_CONTEXT_EX ccex; ccex.cbAllocated = MAX_CERT_SIZE; ccex.CertContext.pbCertEncoded = CertificateBuf; end client certificate support **/ if (p->GetServerVariable(p->ConnID, "ALL_RAW", stackBuf, &bufsz) == TRUE) { buf = stackBuf; } else { WOLog(WO_DBG, "buffer too small; need %d", bufsz); buf = WOMALLOC(bufsz); if (p->GetServerVariable(p->ConnID, "ALL_RAW", buf, &bufsz) != TRUE) { WOFREE(buf); WOLog(WO_ERR, "Could not get headers."); return; } } if (buf) { //WOLog(WO_DBG, "got raw buffer: %s", buf); pos = 0; while (pos < bufsz) { while (pos < bufsz && buf[pos] < 31) pos++; if (pos < bufsz) { /* got start of new header */ key = &buf[pos]; value = NULL; /* search for ':' */ while (pos < bufsz && buf[pos] != ':') pos++; /* change ':' to 0 to terminate key */ buf[pos] = 0; pos++; /* look for start of value */ while (pos < bufsz && (buf[pos] == ' ' || buf[pos] == '\t')) pos++; if (pos < bufsz && buf[pos] > 31) { /* got start of value */ value = &buf[pos]; do { while (pos < bufsz && buf[pos] != '\r') pos++; if (pos + 2 < bufsz && buf[pos+1] == '\n' && (buf[pos+2] == ' ' || buf[pos+2] == '\t')) { /* got a multiline header; change CRLF to whitespace and keep parsing */ buf[pos] = ' '; buf[pos+1] = ' '; } else { buf[pos] = 0; } } while (pos < bufsz && buf[pos] != 0); } // do not pass connection setting from client to WO app as this interferes // with our own connection pooling if (strcasecmp(key, CONNECTION) == 0) continue; WOLog(WO_DBG, "found key=\"%s\", value=\"%s\"", key, value ? value : "(NULL)"); if (value) req_addHeader(req, key, value, STR_COPYKEY|STR_COPYVALUE); else req_addHeader(req, key, "", STR_COPYKEY); } } if (buf != stackBuf) WOFREE(buf); } /** client certificate support if (p->ServerSupportFunction(p->ConnID, HSE_REQ_GET_CERT_INFO_EX, (LPVOID)&ccex, 0, 0) == FALSE) { WOLog(WO_DBG, "Didn't get a certificate, oh well."); } else { // ccex now contains valid client certificate information. DWORD clen = ccex.CertContext.cbCertEncoded; char* cstr = make_cert_one_line((BYTE*)CertificateBuf, clen); //Don't bother copying the key or value req_addHeader(req, "SSL_CLIENT_CERT", cstr, 0); free(CertificateBuf); free(cstr); } end client certificate support **/ return; }
/* * the request handler... */ NSAPI_PUBLIC int WebObjectsRequest(pblock *pb, Session *sn, Request *rq) { HTTPRequest *req; HTTPResponse *resp = NULL; WOURLComponents wc = WOURLComponents_Initializer; const char *reqerr; const char *qs; int retval; char *uri; WOURLError urlerr; if (!adaptorEnabled) return REQ_NOACTION; /* spew debug info */ dump_pb(sn->client,"service.sn->client"); dump_pb(pb,"service.pb"); dump_pb(rq->vars,"service.rq->vars"); dump_pb(rq->reqpb,"service.rq->reqpb"); dump_pb(rq->headers,"service.rq->headers"); dump_pb(rq->srvhdrs,"service.rq->srvhdrs"); uri = pblock_findval("uri", rq->reqpb); WOLog(WO_INFO,"<WebObjects NSAPI> new request: %s", uri); urlerr = WOParseApplicationName(&wc, uri); if (urlerr != WOURLOK) { const char *_urlerr; _urlerr = WOURLstrerror(urlerr); WOLog(WO_INFO,"URL Parsing Error: %s", _urlerr); if (urlerr == WOURLInvalidApplicationName) { if (ac_authorizeAppListing(&wc)) { resp = WOAdaptorInfo(NULL, &wc); die_resp(sn, rq, resp); } else { die(sn, rq, _urlerr, HTTP_NOT_FOUND); } } die(sn, rq, _urlerr, HTTP_BAD_REQUEST); } /* * build the request .... */ req = req_new( pblock_findval("method", rq->reqpb), NULL); /* * validate the method */ reqerr = req_validateMethod(req); if (reqerr) { req_free(req); return die(sn,rq,reqerr, HTTP_BAD_REQUEST); } /* * copy the headers.. */ copyHeaders(pb, sn, rq, req); /* * get form data if any * assume that POSTs with content length will be reformatted to GETs later */ if (req->content_length > 0) { int len_remaining = req->content_length; char *buffer = WOMALLOC(req->content_length); char *data = buffer; int c; while (len_remaining-- > 0) { if ((c = netbuf_getc(sn->inbuf)) == IO_ERROR) { log_error(0,"WebObjects",sn,rq,"Error reading form data"); WOFREE(buffer); req_free(req); return die(sn,rq,INV_FORM_DATA, HTTP_BAD_REQUEST); } *data++ = c; } req->content = buffer; } /* Always get the query string */ qs = pblock_findval("query", rq->reqpb); wc.queryString.start = qs; wc.queryString.length = qs ? strlen(qs) : 0; /* * message the application & collect the response * * note that handleRequest free()'s the 'req' for us */ if (resp == NULL) { /* if no error so far... */ req->api_handle = rq; /* stash this in case it's needed */ resp = tr_handleRequest(req, uri, &wc, pblock_findval("protocol",rq->reqpb), NULL); } if (resp != NULL) { retval = sendResponse(sn, rq, resp); resp_free(resp); } else { retval = REQ_EXIT; /* no response from app - bail */ } req_free(req); #if defined(FINDLEAKS) showleaks(); #endif return retval; }
static net_fd openapp(const char *hostName, int port, unsigned short cto, unsigned short sto, unsigned short rto, int sbufsiz, int rbufsiz) { int s = 0; struct in_addr; net_fd appfd; struct hostent *host; host = hl_find(hostName); if (host == NULL) { WOLog(WO_ERR, "openapp(): host lookup failed for %s", hostName); return NULL_FD; } WOLog(WO_INFO, "attempting to connect to %s on port %d",host->h_name,port); s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { char *errMsg = WA_errorDescription(WA_error()); WOLog(WO_ERR,"couldn't create socket to %s (%d): %s", host->h_name, port, errMsg); WA_freeErrorDescription(errMsg); return NULL_FD; } /* set send buffer size */ if (sbufsiz != 0) { if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void *)&sbufsiz, sizeof(sbufsiz)) < 0) { char *errMsg = WA_errorDescription(WA_error()); WOLog(WO_WARN, "openapp(): error setting send buffer size to %d: %s", sbufsiz, errMsg); WA_freeErrorDescription(errMsg); } } /* set receive buffer size */ if (rbufsiz != 0) { if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void *)&rbufsiz, sizeof(rbufsiz)) < 0) { char *errMsg = WA_errorDescription(WA_error()); WOLog(WO_WARN, "openapp(): error setting receive buffer size to %d: %s", rbufsiz, errMsg); WA_freeErrorDescription(errMsg); } } /* Set the socket to be non-blocking. */ if (setBlockingState(s, 1) == -1) { char *errMsg = WA_errorDescription(WA_error()); WOLog(WO_ERR,"openapp(): couldn't set socket to nonblocking"); WA_freeErrorDescription(errMsg); closeSocket(s); return NULL_FD; } /* attempt to connect */ if (nonBlockingConnectHostent(s, cto, host, port) < 0) { char *errMsg = WA_errorDescription(WA_error()); WOLog(WO_ERR,"couldn't connect to %s (%d): %s", host->h_name, port, errMsg); WA_freeErrorDescription(errMsg); closeSocket(s); return NULL_FD; } /* set up the buffer */ appfd = WOMALLOC(sizeof(netfd)); appfd->s = s; appfd->status = TR_OK; appfd->pos = 0; appfd->send_to = sto; appfd->recv_to = rto; appfd->count = 0; return appfd; }
int main() { #ifdef PROFILE int i; #endif const char *config_url, *username, *password, *config_options; strtbl *options = NULL; int exit_status = 0; // install SIGUSR1, SIGPIPE, SIGTERM handler signal(SIGTERM, sig_handler); signal(SIGUSR1, sig_handler); signal(SIGPIPE, sig_handler); /* Provide a hook via an environment variable to define the config URL */ config_url = getenv(WO_CONFIG_URL); if (!config_url) { /* Flat file URL */ /* config_url = "file:///Local/Library/WebObjects/Configuration/WOConfig.xml"; */ /* Local wotaskd */ /* config_url = "http://localhost:1085"; */ /* Multicast URL */ config_url = CONFIG_URL; /* Actually "webobjects://239.128.14.2:1085"; */ } WOLog(WO_INFO,"<FastCGI> config url is %s", config_url); options = st_new(8); st_add(options, WOCONFIG, config_url, 0); /* * If your webserver is configured to pass these environment variables, we use them to * protect WOAdaptorInfo output. */ username = getenv(WO_ADAPTOR_INFO_USERNAME); if (username && strlen(username) != 0) { st_add(options, WOUSERNAME, username, 0); password = getenv(WO_ADAPTOR_INFO_PASSWORD); if(password && strlen(password) != 0) { st_add(options, WOPASSWORD, password, 0); } } config_options = getenv(WO_CONFIG_OPTIONS); if (config_options) st_add(options, WOOPTIONS, config_options, 0); /* * SECURITY ALERT * * To disable WOAdaptorInfo, uncomment the next line. * st_add(options, WOUSERNAME, "disabled", 0); * * To specify an WOAdaptorInfo username and password, uncomment the next two lines. * st_add(options, WOUSERNAME, "joe", 0); * st_add(options, WOPASSWORD, "secret", 0); * */ if (init_adaptor(options)) { WOLog( WO_ERR, "<FastCGI> Adaptor initialization failed."); exit(-1); } WOLog( WO_INFO,"<FastCGI> process started" ); while (!should_terminate) { HTTPRequest *req; HTTPResponse *resp = NULL; WOURLComponents wc = WOURLComponents_Initializer; const char *qs; unsigned int qs_len; char *url; const char *script_name, *path_info; const char *reqerr; WOURLError urlerr; FCGX_ParamArray hdrp_org; exit_status = FCGX_Accept(&in, &out, &err, &hdrp ); if ( exit_status < 0 ) { break; } #ifdef PROFILE for (i=0; i < 50000; i++) { #endif WOLog( WO_INFO,"<FastCGI> request accepted" ); #ifdef WIN32 _setmode(_fileno(stdout), _O_BINARY); _setmode(_fileno(stdin), _O_BINAR1Y); #endif script_name = FCGX_GetParam( CGI_SCRIPT_NAME, hdrp); path_info = FCGX_GetParam( CGI_PATH_INFO, hdrp); WOLog( WO_INFO,"<FastCGI> CGI_SCRIPT_NAME = %s", script_name ); WOLog( WO_INFO,"<FastCGI> CGI_PATH_INFO = %s", path_info ); if (script_name == NULL) { prepareAndSendErrorResponse(INV_SCRIPT, HTTP_NOT_FOUND); break; } else if (path_info == NULL) { path_info = "/"; } /* * extract WebObjects application name from URI */ url = WOMALLOC(strlen(path_info) + strlen(script_name) + 1); strcpy(url, script_name); strcat(url, path_info); WOLog(WO_INFO,"<FastCGI> new request: %s",url); urlerr = WOParseApplicationName(&wc, url); if (urlerr != WOURLOK) { const char *_urlerr; _urlerr = WOURLstrerror(urlerr); WOLog(WO_INFO,"<FastCGI> URL Parsing Error: %s", _urlerr); if (urlerr == WOURLInvalidApplicationName) { if (ac_authorizeAppListing(&wc)) { resp = WOAdaptorInfo(NULL, &wc); sendErrorResponse(resp); } else { prepareAndSendErrorResponse(_urlerr, HTTP_NOT_FOUND); } WOFREE(url); break; } prepareAndSendErrorResponse(_urlerr, HTTP_BAD_REQUEST); WOFREE(url); break; } /* * build the request... */ req = req_new( FCGX_GetParam("REQUEST_METHOD", hdrp), NULL); /* * validate the method */ reqerr = req_validateMethod(req); if (reqerr) { prepareAndSendErrorResponse(reqerr, HTTP_BAD_REQUEST); WOFREE(url); break; } /* * copy the headers. This looks wierd... all we're doing is copying * *every* environment variable into our headers. It may be beyond * the spec, but more information probably won't hurt. */ hdrp_org=hdrp; while (hdrp && *hdrp) { char *key, *value; /* copy env. line. */ key = WOSTRDUP(*hdrp); for (value = key; *value && !isspace((int)*value) && (*value != '='); value++) {} if (*value) { *value++ = '\0'; /* null terminate 'key' */ } while (*value && (isspace((int)*value) || (*value == '='))) { value++; } /* BEGIN Support for getting the client's certificate. */ if (strcmp((const char *)key, "SSL_CLIENT_CERTIFICATE") == 0 || strcmp((const char *)key, "SSL_SERVER_CERTIFICATE") == 0 ) { value = 0; WOLog(WO_INFO,"<FastCGI> DROPPING ENV VAR (DUPLICATE) = %s", key); } if (strcmp((const char *)key, "SSL_CLIENT_CERT") == 0 || strcmp((const char *)key, "SSL_SERVER_CERT") == 0) { value = make_cert_one_line(value); //WOLog(WO_INFO,"<FastCGI> PASSING %s = %s", key, value); } /* END Support for getting the client's certificate */ if (key && *key && value && *value) { /* must specify copy key and value because key translation might replace this key, and value lives in the same buffer */ req_addHeader(req, key, value, STR_COPYKEY|STR_COPYVALUE); } /* BEGIN Support for getting the client's certificate */ if (freeValueNeeded ) { free(value); freeValueNeeded=0; } /* END Support for getting the client's certificate */ WOFREE(key); hdrp++; /* next env variable */ } hdrp=hdrp_org; /* * get form data if any * assume that POSTs with content length will be reformatted to GETs later */ WOLog ( WO_INFO, "Getting request data, length: %d",req->content_length ); if (req->content_length > 0) { req_allocateContent(req, req->content_length, 1); req->getMoreContent = (req_getMoreContentCallback)readContentData; WOLog ( WO_INFO, "content_buffer_size: %d",req->content_buffer_size ); if (req->content_buffer_size == 0) { prepareAndSendErrorResponse(ALLOCATION_FAILURE, HTTP_SERVER_ERROR); WOFREE(url); break; } if (readContentData(req, req->content, req->content_buffer_size, 1) == -1) { prepareAndSendErrorResponse(WOURLstrerror(WOURLInvalidPostData), HTTP_BAD_REQUEST); WOFREE(url); break; } } /* Always get the query string */ qs = FCGX_GetParam("QUERY_STRING", hdrp); if (qs) { qs_len = strlen(qs); } else { qs_len = 0; } if (qs_len > 0) { wc.queryString.start = qs; wc.queryString.length = qs_len; WOLog(WO_INFO,"<FastCGI> new request with Query String: %s", qs); } /* * message the application & collect the response */ resp = tr_handleRequest(req, url, &wc, FCGX_GetParam(CGI_SERVER_PROTOCOL, hdrp), documentRoot()); if (resp != NULL) { sendResponse(resp); resp_free(resp); /* dump the response */ } WOFREE(url); req_free(req); #if defined(FINDLEAKS) showleaks(); #endif } #ifdef PROFILE } #endif st_free(options); WOLog( WO_INFO,"<FastCGI> process exiting" ); return exit_status; }