コード例 #1
0
ファイル: request.c プロジェクト: desertsky/wonder
const char *req_HeaderForKey(HTTPRequest *req, const char *key)
{
   if (req->headers)
      return st_valueFor(req->headers, key);
   else
      return NULL;
}
コード例 #2
0
ファイル: transaction.c プロジェクト: ATLTed/wonder
/*
 *	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;
}
コード例 #3
0
ファイル: transaction.c プロジェクト: ATLTed/wonder
/*
 *	Handle the meat of the request:
 *	   - check for a load balanced instance
 *	   - (or) autostart an (or find an autostarted) instance
 *	   - open a socket to the application
 *	   - message the application with the request using HTTP
 *	   - wait for and receive the response
 *	   - free the request
 *	   - return the response
 *
 *	If we can't message the application, retry the request up to
 *	RETRIES times (see config.h).
 *
 */
HTTPResponse *tr_handleRequest(HTTPRequest *req, const char *url, WOURLComponents *wc, const char *server_protocol, const char *docroot)
{
   WOAppReq app;
   int connectionAttempts = 0, searchAttempts = 0;
   HTTPResponse *resp = NULL;
   WOAppHandle appHandle = AC_INVALID_HANDLE;
   _WOApp *woapp = NULL;
   WOInstanceHandle instHandle = AC_INVALID_HANDLE, retryInstHandle;
   _WOInstance *inst;
   int urlVersion, retries = RETRIES;

   memset(&app, 0, sizeof(WOAppReq));
   app.urlVersion = CURRENT_WOF_VERSION_MAJOR;
   app.docroot = docroot;
   app.request = req;				/* valid for request forwarding phase */

   /*
    * copy the application name
    */
   if (wc->applicationName.length != 0 && wc->applicationName.length < WA_MAX_APP_NAME_LENGTH) {
      strncpy(app.name, wc->applicationName.start, wc->applicationName.length);
      app.name[wc->applicationName.length] = '\0';
   }

   /*
    *	find the application
    */
   for (searchAttempts = 0; searchAttempts < 2 && instHandle == AC_INVALID_HANDLE; searchAttempts++)
   {
      /* If the find app succeeds, have to be very careful about the possibility */
      /* that someone else can delete it out from under us. */
      appHandle = ac_findApplication(app.name);
      if (appHandle != AC_INVALID_HANDLE)
      {
         /* cache some info so we don't have to keep the app locked */
         woapp = ac_lockedApp(appHandle);
         urlVersion = woapp->urlVersion;
         app.scheduler = lb_schedulerByName(woapp->loadbalance);
         strncpy(app.redirect_url, woapp->redirect_url, WA_MAX_URL_LENGTH);
         retries = woapp->retries;
         /* extract the request & application information from the URI */
         resp = _collectRequestInformation(&app, wc, url, urlVersion, req);
         if (resp != NULL) {
            ac_unlockApp(appHandle);
            return resp;		/* some kind of error in URI */
         }

         /* note: if we found the app, it is still locked at this point */

         /*
          *	select an app, the request may be requesting a specific instance
          *	or it may be indifferent.  an instance neutral request may be a new
          *	request or a returning one for which the state is not stored in the
          *	app.
          * 	if specified instance does not respond, we'll failover to another
          * 	instance
          */
         if (app.instance[0] == 0) {
            instHandle = tr_selectInstance(&app, woapp);
            if (instHandle != AC_INVALID_HANDLE)
            {
               WOLog(WO_INFO,"Selected new app instance at index %d", instHandle);
               tr_prepareToUseInstance(&app, instHandle);
               ac_unlockInstance(instHandle);
            }
         } else {
            WOLog(WO_INFO,"Selecting specific app instance %s.", app.instance);
            instHandle = ac_findInstance(woapp, app.instance);
            if (instHandle == AC_INVALID_HANDLE)
            {
               WOLog(WO_WARN, "Unable to find instance %s. Attempting to select another.", app.instance);
               instHandle = tr_selectInstance(&app, woapp);
            }
            if (instHandle != AC_INVALID_HANDLE)
            {
               tr_prepareToUseInstance(&app, instHandle);
               ac_unlockInstance(instHandle);
            }
         }

         /* At this point, pendingResponses has been incremented in the instance. */
         /* An instance will not be deleted with a nonzero pendingResponses coung, and */
         /* an app will not be deleted if it has any instances. Now it is ok to unlock the app. */
         ac_unlockApp(appHandle);
      }
      /* If we didn't find the app or if we didn't find a specific requested instance, reload the config */
      if (searchAttempts == 0 && (appHandle == AC_INVALID_HANDLE || (instHandle == AC_INVALID_HANDLE && app.instance[0] == '-')))
      {
         if (app.instance[0] != 0)
            WOLog(WO_INFO, "Requested application '%s' not found. Reloading config.", app.name);
         else
            WOLog(WO_INFO, "Specific instance %s:%s not found. Reloading config.", app.name, app.instance);
         ac_resetConfigTimers();
         ac_readConfiguration();
      } else {
         /* didn't reload the config, so no point in retrying */
         break;
      }
   }

   /*
    *	run the request...
    */
   resp = NULL;
   if (instHandle != AC_INVALID_HANDLE) {
      /* Attempt to send request and read response */
      do {
         /* Fix up URL so app knows it's being load balanced ...  */
         /* We need to do this in the loop to get correct instance number into the request URL */
         req_reformatRequest(req, &app, wc, server_protocol);
         WOLog(WO_INFO,"Sending request to instance number %s, port %d", app.instance, app.port);
         resp = _runRequest(&app, appHandle, instHandle, req);

         /* Cannot retry if we have read some streamed content data because we cannot unwind the byte stream. */
         if (req->haveReadStreamedData)
            retries = 0;
         
         if (resp) {
            if (resp->status == 302) /* redirected */
            {
               if (!req->haveReadStreamedData && st_valueFor(resp->headers, "x-webobjects-refusing-redirection"))
               {
                  /* redirected because instance is refusing new sessions */
                  resp_free(resp);
                  resp = NULL;
                  WOLog(WO_INFO, "Request redirected because instance refusing new sessions.");
               }
            }
         } else {
            if (app.error != err_read)
            {
               connectionAttempts++;
               /* Mark this instance as unresponsive */
               WOLog(WO_INFO,"Marking instance %s dead", app.instance);
               if (app.scheduler->instanceDidNotRespond)
               {
                  inst = ac_lockInstance(instHandle);
                  if (inst)
                  {
                     app.scheduler->instanceDidNotRespond(inst);
                     ac_unlockInstance(instHandle);
                  }
               }
               WOLog(WO_DBG, "connectionAttempts = %d, retries = %d", connectionAttempts, retries);
               ac_readConfiguration();
            }
         }
         if (resp == NULL && connectionAttempts <= retries) {
            /* appHandle is still valid because we have not decremented pendingResponses on the instance */
            woapp = ac_lockApp(appHandle);
            if (woapp)
            {
               retryInstHandle = tr_selectInstance(&app, woapp);
               if (retryInstHandle != AC_INVALID_HANDLE)
               {
                  tr_prepareToUseInstance(&app, retryInstHandle);
                  ac_unlockInstance(retryInstHandle);
               }
               ac_unlockApp(appHandle);
            } else
               retryInstHandle = AC_INVALID_HANDLE;
            /* Decrement pendingResponses on the original instance. It is safe to do now. */
            /* The retry instance has had pendingResponses incremented, which will prevent the */
            /* app from being removed. (Or, there was not other instance to retry, and we are done.) */
            inst = ac_lockInstance(instHandle);
            if (inst)
            {
               if (inst->pendingResponses > 0)
                  inst->pendingResponses--;
               ac_unlockInstance(instHandle);
               instHandle = retryInstHandle;
            }
            if (retryInstHandle != AC_INVALID_HANDLE) {
               WOLog(WO_INFO,"Attempting failover to new instance at index %d", retryInstHandle);
            } else {
               WOLog(WO_INFO,"No new instance located for failover");
            }
            instHandle = retryInstHandle;
         }
      } while (resp == NULL && instHandle != AC_INVALID_HANDLE && connectionAttempts < retries);
      if (instHandle != AC_INVALID_HANDLE)
      {
         /* still have to decrement pendingResponses */
         inst = ac_lockInstance(instHandle);
         if (inst)
         {
            if (inst->pendingResponses > 0)
               inst->pendingResponses--;
            ac_unlockInstance(instHandle);
            /* At this point, the app could be removed from the config at any time. */
            instHandle = retryInstHandle;
         }
      }
   } else {
      if (ac_authorizeAppListing(wc))
         resp = WOAdaptorInfo(req, wc);
      else
         app.error = err_notFound;
   }

   /*
    *	how'd we do?
    */
   if (resp == NULL) {
      /*
       *	we may be able to send a palatable response...
       */
      resp = _errorResponse(&app, wc, req);
   }
   return resp;
}