/* * run the request */ static HTTPResponse *_runRequest(WOAppReq *app, WOAppHandle woappHandle, WOInstanceHandle instHandle, HTTPRequest *req) { WOConnection *c; HTTPResponse *resp = NULL; int send_status, keepConnection, retryRequest = 0; const char *idString = NULL; WOLog(WO_INFO,"Trying to contact %s:%s on %s(%d)", app->name,app->instance,app->host,app->port); /* Tag the request with a unique identifier (if it hasn't been already) */ /* (It might already have been tagged if we are retrying the request due to refusing new sessions, for example) */ idString = req_HeaderForKey(req, REQUEST_ID_HEADER); if (idString == NULL) { String *requestID = tr_uniqueID(); if (requestID) { req_addHeader(req, REQUEST_ID_HEADER, requestID->text, STR_COPYVALUE); idString = req_HeaderForKey(req, REQUEST_ID_HEADER); str_free(requestID); } } do { c = tr_open(instHandle); /* connect */ if (c == NULL) { WOLog(WO_INFO,"%s:%s NOT LISTENING on %s(%d)", app->name,app->instance,app->host,app->port); app->error = err_connect; return NULL; } /* * app found and is listening on port */ if (app->scheduler->beginTransaction) app->scheduler->beginTransaction(app, instHandle); /* Make sure that we're the only connection header, and we're explicit about the setting */ req_removeHeader(req,CONNECTION); if (c->isPooled) { req_addHeader(req,CONNECTION,HTTP_KEEP_ALIVE,0); } else { req_addHeader(req,CONNECTION,HTTP_CLOSE,0); } WOLog(WO_INFO,"%s:%s on %s(%d) connected [pooled: %s]", app->name, app->instance, app->host, app->port, c->isPooled ? "Yes" : "No"); /* * send the request.... */ send_status = req_sendRequest(req, c->fd); if (send_status != 0) { if ((send_status == TR_RESET) && (retryRequest == 0) && !req->haveReadStreamedData) { /* If we get here the connection was reset. This means the instance has either quit or crashed. */ /* Bump the generation number so all pooled connections to this instance will be invalidated. */ /* Then retry the request with a new connection. If the instance is not running the retry will */ /* fail with a different error and the instance will be marked dead. */ _WOInstance *inst = ac_lockInstance(instHandle); /* note: if we get here, keepConnection == 0 --> this connection will be closed */ if (inst) { ac_cycleInstance(inst, c->generation); ac_unlockInstance(instHandle); } retryRequest++; WOLog(WO_INFO, "retrying request due to connection reset"); /* Must close connection before continuing */ tr_close(c, instHandle, 0); continue; } else { WOLog(WO_ERR,"Failed to send request"); tr_close(c, instHandle, 0); /* close app connection */ if (send_status == -1) app->error = err_read; else app->error = err_send; return NULL; } } /* Note that we have a request queued */ WOLog(WO_INFO,"Request %s sent, awaiting response", req->request_str); /* While the app is processing the request, take the opportunity to check/update the config. */ ac_readConfiguration(); /* * now wait for the response... */ resp = resp_getResponseHeaders(c, instHandle); /* go ahead and read the first chunk of response data */ if (resp && req->method != HTTP_HEAD_METHOD) { if (resp_getResponseContent(resp, 1) == -1) { resp_free(resp); resp = NULL; } } /* Validate the ID */ if (idString && resp) { const char *respID = st_valueFor(resp->headers, REQUEST_ID_HEADER); if (respID != NULL) { if (strcmp(respID, idString) != 0) { WOLog(WO_ERR, "Got response with wrong ID! Dumping response. request ID = %s, response ID = %s", idString, respID); /* note this will cause the connection to be closed below */ resp_free(resp); resp = NULL; } else st_removeKey(resp->headers, REQUEST_ID_HEADER); } else WOLog(WO_WARN, "Got response with no ID."); } app->response = resp; /* * check if this connection can be kept open */ keepConnection = 0; #ifndef CGI /* doesn't make sense to keep the connection for CGI */ if (resp && resp->headers) { const char *keepAlive; keepAlive = st_valueFor(resp->headers, CONNECTION); if (keepAlive) { /* if the keep alive header is set, honor the value */ if (strcasecmp(keepAlive, HTTP_KEEP_ALIVE) == 0) keepConnection = 1; } else { /* no keep alive header - keep alive by default for HTTP/1.1 only */ if (resp->flags & RESP_HTTP11) keepConnection = 1; } } #endif if (resp != NULL) { if (app->scheduler->finalizeTransaction) if (app->scheduler->finalizeTransaction(app, instHandle)) keepConnection = 0; st_removeKey(resp->headers, REFUSING_SESSIONS_HEADER); st_removeKey(resp->headers, LOAD_AVERAGE_HEADER); st_removeKey(resp->headers, CONNECTION); WOLog(WO_INFO,"received ->%d %s",resp->status,resp->statusMsg); retryRequest = 0; } else { if (c != NULL && tr_connectionWasReset(c)) { /* If we get here the connection was reset. This means the instance has either quit or crashed. */ /* Bump the generation number so all pooled connections to this instance will be invalidated. */ /* Then retry the request with a new connection. If the instance is not running the retry will */ /* fail with a different error and the instance will be marked dead. */ /* Note that only one retry due to a connection reset error is allowed. This is to prevent an */ /* infinite loop if the instance dies while processing the request and restarts quickly enough */ /* to process the retry. */ _WOInstance *inst = ac_lockInstance(instHandle); /* note: if we get here, keepConnection == 0 --> this connection will be closed */ if (inst) { ac_cycleInstance(inst, c->generation); ac_unlockInstance(instHandle); } retryRequest++; if (retryRequest == 1) WOLog(WO_INFO, "retrying request due to connection reset"); } else app->error = err_response; } if (resp && resp->content_read < resp->content_length) { resp->keepConnection = keepConnection; resp->instHandle = instHandle; resp->flags |= RESP_CLOSE_CONNECTION; } else { tr_close(c, instHandle, keepConnection); } } while (retryRequest == 1); return resp; }
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; }
/* * finish the job that the api stubs started.. */ static HTTPResponse *_collectRequestInformation(WOAppReq *app, WOURLComponents *wc, const char *url, int urlVersion, HTTPRequest *req) { const char *urlerr; /* * we need to complete the URL parsing... */ if ((urlVersion == 4) || (urlVersion == 3)) { urlerr = WOParseAndCheckURL(wc, url, urlVersion, req->shouldProcessUrl); } else { urlerr = "Unsupported URL version"; WOLog(WO_WARN, "Unsupported URL version (%d) on %s", urlVersion, app->name); } if (urlerr != NULL) return resp_errorResponse(urlerr, HTTP_BAD_REQUEST); /* * pick up the app name & host (if any) & instance */ if (wc->applicationName.length == 0) return resp_errorResponse(NO_APPNAME, HTTP_BAD_REQUEST); strncpy(app->name, wc->applicationName.start, wc->applicationName.length); app->name[wc->applicationName.length] = '\0'; if (app->name[wc->applicationName.length - 1] == '/') /* garbage? */ app->name[wc->applicationName.length - 1] = '\0'; if (wc->applicationHost.length > 0) { strncpy(app->host, wc->applicationHost.start, wc->applicationHost.length); app->host[wc->applicationHost.length] = '\0'; } else app->host[0] = '\0'; /* * is the session/application information in the URL? * if not, look in the cookies for the application instance number * in the cookies. */ app->instance[0] = 0; /* default to any instance */ if (wc->applicationNumber.length > 0) { if (wc->applicationNumber.length < WA_MAX_INSTANCE_NUMBER_LENGTH) { memcpy(app->instance, wc->applicationNumber.start, wc->applicationNumber.length); app->instance[wc->applicationNumber.length] = 0; } } else { const char *cookie, *woinst; cookie = req_HeaderForKey(req, COOKIE); if (cookie && ((woinst = strstr(cookie, INST_COOKIE)) != NULL)) { const char *instid = &woinst[sizeof(INST_COOKIE)-1]; int len = 0; while (len < WA_MAX_INSTANCE_NUMBER_LENGTH && instid[len] != ';' && instid[len]) len++; if (len < WA_MAX_INSTANCE_NUMBER_LENGTH) { memcpy(app->instance, instid, len); app->instance[len] = 0; } // remove any quotes from the instance number #ifdef _MSC_VER // SWK Start VC can't define attributes here using '{' fixed it { #endif char *before, *after; before = after = app->instance; while(*before){ if((*before == '\'') || (*before == '"')){ before++; } *after++ = *before++; } *after = 0; #ifdef _MSC_VER // SWK End } #endif WOLog(WO_INFO,"Cookie instance %s from %s",app->instance,cookie); } } app->urlVersion = (wc->webObjectsVersion.start) ? atoi(wc->webObjectsVersion.start) : CURRENT_WOF_VERSION_MAJOR; /* * Add the adaptor identifier header. * WOFramework checks for the presence of this header, so the name should not change. * (The value of the header is ignored by WOFramework.) */ req_addHeader(req, "x-webobjects-adaptor-version", WA_adaptorName, 0); return NULL; /* no errors */ }
/* * 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; }