/* * 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; }
void req_removeHeader(HTTPRequest *req,const char *key) { st_removeKey((strtbl *)req->headers, key); }