// This might throw AICurlNoEasyHandle.
LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action, std::string const& url, Injector* body,
	LLHTTPClient::ResponderPtr responder, AIHTTPHeaders& headers, AIPerService::Approvement* approved,
	bool keepalive, bool is_auth, bool compression) :
    mAction(action), mURL(url), mKeepAlive(keepalive), mIsAuth(is_auth), mNoCompression(!compression),
	mBody(body), mResponder(responder), mHeaders(headers), mResponderNameCache(std::string("LLURLRequest:") + std::string(responder ? responder->getName() : "<uninitialized>"))
{
	if (approved)
	{
		AICurlEasyRequest_wat(*mCurlEasyRequest)->setApproved(approved);
	}
}
void AICurlEasyRequestStateMachine::multiplex_impl(state_type run_state)
{
  switch (run_state)
  {
	case AICurlEasyRequestStateMachine_addRequest:
	{
	  set_state(AICurlEasyRequestStateMachine_waitAdded);
	  idle();							// Wait till AICurlEasyRequestStateMachine::added_to_multi_handle() is called.

	  // This is a work around for the case that this request had a bad url, in order to avoid a crash later on.
	  bool empty_url = AICurlEasyRequest_rat(*mCurlEasyRequest)->getLowercaseServicename().empty();
	  if (empty_url)
	  {
		AICurlEasyRequest_wat(*mCurlEasyRequest)->aborted(HTTP_INTERNAL_ERROR_OTHER, "Not a valid URL.");
		abort();
		break;
	  }

	  // Only AFTER going idle, add request to curl thread; this is needed because calls to set_state() are
	  // ignored when the statemachine is not idle, and theoretically the callbacks could be called
	  // immediately after this call.
	  mAdded = true;
	  mCurlEasyRequest.addRequest();	// This causes the state to be changed, now or later, to
										//   AICurlEasyRequestStateMachine_removed_after_finished.

	  // The state at this point is thus one of
	  // 1) AICurlEasyRequestStateMachine_waitAdded (idle)
	  // 2) AICurlEasyRequestStateMachine_removed_after_finished (running)

	  if (mTotalDelayTimeout > 0.f)
	  {
		// Set an inactivity timer.
		// This shouldn't really be necessary, except in the case of a bug
		// in libcurl; but lets be sure and set a timer for inactivity.
		mTimer = new AITimer;
		mTimer->setInterval(mTotalDelayTimeout);
		mTimer->run(this, AICurlEasyRequestStateMachine_timedOut, false, false);
	  }
	  break;
	}
	case AICurlEasyRequestStateMachine_waitAdded:
	{
	  // Nothing to do.
	  idle();
	  break;
	}
	case AICurlEasyRequestStateMachine_timedOut:
	{
	  // It is possible that exactly at this point the state changes into
	  // AICurlEasyRequestStateMachine_removed_after_finished, with as result that mTimedOut
	  // is set while we will continue with that state. Hence that mTimedOut
	  // is explicitly reset in that state.

	  // Libcurl failed to deliver within a reasonable time... Abort operation in order
	  // to free this curl easy handle and to notify the application that it didn't work.
	  mTimedOut = true;
	  llassert(mAdded);
	  mAdded = false;
	  mCurlEasyRequest.removeRequest();
	  idle();							// Wait till AICurlEasyRequestStateMachine::removed_from_multi_handle() is called.
	  break;
	}
	case AICurlEasyRequestStateMachine_removed_after_finished:
	{
	  if (!mHandled)
	  {
		// Only do this once.
		mHandled = true;

		if (mTimer)
		{
		  // Stop the timer. Note that it's the main thread that generates timer events,
		  // so we're certain that there will be no time out anymore if we reach this point.
		  mTimer->abort();
		}

		// The request finished and either data or an error code is available.
		AICurlEasyRequest_wat easy_request_w(*mCurlEasyRequest);
		easy_request_w->processOutput();
	  }

	  // See above.
	  mTimedOut = false;
	  /* Fall-Through */
	}
	case AICurlEasyRequestStateMachine_removed:
	{
	  // The request was removed from the multi handle.

	  // We're done. If we timed out, abort -- or else the application will
	  // think that getResult() will return a valid error code from libcurl.
	  if (mTimedOut)
	  {
		AICurlEasyRequest_wat(*mCurlEasyRequest)->aborted(HTTP_INTERNAL_ERROR_CURL_LOCKUP, "Request timeout, aborted.");
		abort();
	  }
	  else
		finish();

	  break;
	}
	case AICurlEasyRequestStateMachine_bad_file_descriptor:
	{
	  AICurlEasyRequest_wat(*mCurlEasyRequest)->aborted(HTTP_INTERNAL_ERROR_CURL_BADSOCKET, "File descriptor went bad! Aborted.");
	  abort();
	}
  }
}