PageRenderTime 61ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/mordor/http/client.cpp

http://github.com/mozy/mordor
C++ | 1369 lines | 1236 code | 67 blank | 66 comment | 380 complexity | 36e8329611c7fb7db6ff5d883831f85c MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2009 - Mozy, Inc.
  2. #include "client.h"
  3. #include <algorithm>
  4. #include <boost/bind.hpp>
  5. #include "chunked.h"
  6. #include "mordor/assert.h"
  7. #include "mordor/fiber.h"
  8. #include "mordor/log.h"
  9. #include "mordor/scheduler.h"
  10. #include "mordor/streams/limited.h"
  11. #include "mordor/streams/notify.h"
  12. #include "mordor/streams/null.h"
  13. #include "mordor/streams/timeout.h"
  14. #include "mordor/streams/transfer.h"
  15. #include "mordor/timer.h"
  16. #include "mordor/util.h"
  17. #include "mordor/atomic.h"
  18. #include "mordor/socket.h"
  19. #include "multipart.h"
  20. #include "parser.h"
  21. namespace Mordor {
  22. namespace HTTP {
  23. static Logger::ptr g_log = Log::lookup("mordor:http:client");
  24. ClientConnection::ClientConnection(Stream::ptr stream, TimerManager *timerManager)
  25. : Connection(stream),
  26. m_readTimeout(~0ull),
  27. m_idleTimeout(~0ull),
  28. m_timerManager(timerManager),
  29. m_currentRequest(m_pendingRequests.end()),
  30. m_allowNewRequests(true),
  31. m_priorRequestFailed(false),
  32. m_requestCount(0),
  33. m_maxRequestCount(~0ull),
  34. m_priorResponseFailed(~0ull),
  35. m_priorResponseClosed(~0ull)
  36. {
  37. static Atomic<size_t> connectionCount(0);
  38. m_connectionNumber = ++connectionCount;
  39. MORDOR_LOG_TRACE(g_log) << "ClientConnection " << m_connectionNumber << " = " << this;
  40. if (timerManager) {
  41. FilterStream::ptr previous;
  42. FilterStream::ptr filter = boost::dynamic_pointer_cast<FilterStream>(m_stream);
  43. while (filter) {
  44. previous = filter;
  45. filter = boost::dynamic_pointer_cast<FilterStream>(filter->parent());
  46. }
  47. // Put the timeout stream as close to the actual source stream as
  48. // possible, to avoid registering timeouts for stuff that's going to
  49. // complete immediately anyway
  50. if (previous) {
  51. m_timeoutStream.reset(new TimeoutStream(previous->parent(),
  52. *timerManager));
  53. previous->parent(m_timeoutStream);
  54. } else {
  55. m_timeoutStream.reset(new TimeoutStream(m_stream, *timerManager));
  56. m_stream = m_timeoutStream;
  57. }
  58. }
  59. }
  60. ClientConnection::~ClientConnection()
  61. {
  62. if (m_idleTimer)
  63. m_idleTimer->cancel();
  64. }
  65. ClientRequest::ptr
  66. ClientConnection::request(const Request &requestHeaders)
  67. {
  68. ClientRequest::ptr request(new ClientRequest(shared_from_this(), requestHeaders));
  69. request->waitForRequest();
  70. return request;
  71. }
  72. bool
  73. ClientConnection::newRequestsAllowed()
  74. {
  75. boost::mutex::scoped_lock lock(m_mutex);
  76. return m_allowNewRequests && m_priorResponseClosed == ~0ull &&
  77. !m_priorRequestFailed && m_priorResponseFailed == ~0ull &&
  78. m_requestCount < m_maxRequestCount;
  79. }
  80. size_t
  81. ClientConnection::outstandingRequests()
  82. {
  83. boost::mutex::scoped_lock lock(m_mutex);
  84. invariant();
  85. return m_pendingRequests.size();
  86. }
  87. bool
  88. ClientConnection::supportsTimeouts() const
  89. {
  90. return !!m_timerManager;
  91. }
  92. void
  93. ClientConnection::readTimeout(unsigned long long us)
  94. {
  95. MORDOR_ASSERT(m_timeoutStream);
  96. m_readTimeout = us;
  97. }
  98. void
  99. ClientConnection::writeTimeout(unsigned long long us)
  100. {
  101. MORDOR_ASSERT(m_timeoutStream);
  102. m_timeoutStream->writeTimeout(us);
  103. }
  104. void
  105. ClientConnection::idleTimeout(unsigned long long us, boost::function<void ()> dg)
  106. {
  107. MORDOR_ASSERT(us == ~0ull || m_timerManager);
  108. boost::mutex::scoped_lock lock(m_mutex);
  109. if (m_idleTimer) {
  110. m_idleTimer->cancel();
  111. m_idleTimer.reset();
  112. }
  113. m_idleTimeout = us;
  114. m_idleDg = dg;
  115. if (m_idleTimeout != ~0ull && m_pendingRequests.empty())
  116. m_idleTimer = m_timerManager->registerTimer(m_idleTimeout, dg);
  117. }
  118. void
  119. ClientConnection::scheduleNextRequest(ClientRequest *request)
  120. {
  121. bool flush = false;
  122. boost::mutex::scoped_lock lock(m_mutex);
  123. invariant();
  124. MORDOR_ASSERT(m_currentRequest != m_pendingRequests.end());
  125. MORDOR_ASSERT(request == *m_currentRequest);
  126. MORDOR_ASSERT(request->m_requestState == ClientRequest::BODY ||
  127. request->m_requestState == ClientRequest::HEADERS);
  128. MORDOR_LOG_TRACE(g_log) << m_connectionNumber << "-"
  129. << request->m_requestNumber << " request complete";
  130. std::list<ClientRequest *>::iterator it(m_currentRequest);
  131. ++it;
  132. if (it == m_pendingRequests.end()) {
  133. // Do *not* advance m_currentRequest, because we can't let someone else
  134. // start another request until our flush completes below
  135. flush = true;
  136. } else {
  137. request->m_requestState = ClientRequest::COMPLETE;
  138. if (request->m_responseState >= ClientRequest::COMPLETE) {
  139. MORDOR_ASSERT(request == m_pendingRequests.front());
  140. m_pendingRequests.pop_front();
  141. }
  142. m_currentRequest = it;
  143. request = *it;
  144. request->m_requestState = ClientRequest::HEADERS;
  145. MORDOR_ASSERT(request->m_scheduler);
  146. MORDOR_ASSERT(request->m_fiber);
  147. MORDOR_LOG_TRACE(g_log) << m_connectionNumber << "-"
  148. << request->m_requestNumber << " scheduling request";
  149. request->m_scheduler->schedule(request->m_fiber);
  150. request->m_scheduler = NULL;
  151. request->m_fiber.reset();
  152. }
  153. Stream::CloseType closetype = Stream::NONE;
  154. if (flush) {
  155. // Take a trip through the Scheduler, trying to let someone else
  156. // attempt to pipeline
  157. if (Scheduler::getThis()) {
  158. lock.unlock();
  159. Scheduler::yield();
  160. lock.lock();
  161. }
  162. invariant();
  163. std::list<ClientRequest *>::iterator it(m_currentRequest);
  164. ++it;
  165. if (it == m_pendingRequests.end()) {
  166. // Nope, still the end, we really do have to flush
  167. lock.unlock();
  168. } else {
  169. flush = false;
  170. }
  171. if (flush) {
  172. flush = false;
  173. try {
  174. if (!m_allowNewRequests)
  175. closetype = Stream::WRITE;
  176. MORDOR_LOG_TRACE(g_log) << m_connectionNumber << " flushing";
  177. m_stream->flush();
  178. } catch (...) {
  179. request->requestFailed();
  180. throw;
  181. }
  182. lock.lock();
  183. invariant();
  184. }
  185. request->m_requestState = ClientRequest::COMPLETE;
  186. ++m_currentRequest;
  187. if (request->m_responseState >= ClientRequest::COMPLETE) {
  188. MORDOR_ASSERT(request == m_pendingRequests.front());
  189. m_pendingRequests.pop_front();
  190. if (m_priorResponseClosed <= request->m_requestNumber ||
  191. m_priorResponseFailed <= request->m_requestNumber) {
  192. MORDOR_ASSERT(m_pendingRequests.empty());
  193. closetype = Stream::BOTH;
  194. lock.unlock();
  195. MORDOR_LOG_TRACE(g_log) << m_connectionNumber << " closing";
  196. }
  197. }
  198. // Someone else may have queued up while we were flushing
  199. if (!flush && m_currentRequest != m_pendingRequests.end()) {
  200. request = *m_currentRequest;
  201. request->m_requestState = ClientRequest::HEADERS;
  202. MORDOR_ASSERT(request->m_scheduler);
  203. MORDOR_ASSERT(request->m_fiber);
  204. MORDOR_LOG_TRACE(g_log) << m_connectionNumber << "-" << request->m_requestNumber << " scheduling request";
  205. request->m_scheduler->schedule(request->m_fiber);
  206. request->m_scheduler = NULL;
  207. request->m_fiber.reset();
  208. } else {
  209. if (m_timeoutStream) {
  210. if (lock.owns_lock()) {
  211. // m_timeoutStream can be yield out. unlock the boost locker to avoid
  212. // holding a boost thread lock while not occupying the thread which can lead
  213. // to thread re-enter and deadlock refs #138359
  214. lock.unlock();
  215. m_timeoutStream->readTimeout(m_readTimeout);
  216. lock.lock();
  217. } else {
  218. m_timeoutStream->readTimeout(m_readTimeout);
  219. }
  220. }
  221. }
  222. }
  223. if (closetype != Stream::NONE) {
  224. if (closetype == Stream::BOTH || m_stream->supportsHalfClose()) {
  225. try {
  226. m_stream->close(closetype);
  227. } catch (...) {
  228. }
  229. }
  230. }
  231. }
  232. void
  233. ClientConnection::scheduleNextResponse(ClientRequest *request)
  234. {
  235. bool close = false;
  236. {
  237. boost::mutex::scoped_lock lock(m_mutex);
  238. invariant();
  239. MORDOR_ASSERT(!m_pendingRequests.empty());
  240. MORDOR_ASSERT(request == m_pendingRequests.front());
  241. MORDOR_ASSERT(request->m_responseState == ClientRequest::BODY ||
  242. request->m_responseState == ClientRequest::HEADERS);
  243. request->m_responseState = ClientRequest::COMPLETE;
  244. MORDOR_LOG_TRACE(g_log) << m_connectionNumber << "-" << request->m_requestNumber << " response complete";
  245. std::list<ClientRequest *>::iterator it = m_pendingRequests.begin();
  246. ++it;
  247. if (request->m_requestState >= ClientRequest::COMPLETE) {
  248. m_pendingRequests.pop_front();
  249. if (m_priorResponseClosed <= request->m_requestNumber ||
  250. m_priorResponseFailed <= request->m_requestNumber)
  251. close = true;
  252. }
  253. if (it != m_pendingRequests.end()) {
  254. request = *it;
  255. MORDOR_ASSERT(request);
  256. MORDOR_ASSERT(request->m_responseState <= ClientRequest::WAITING ||
  257. request->m_responseState > ClientRequest::COMPLETE);
  258. if (request->m_responseState == ClientRequest::WAITING) {
  259. std::set<ClientRequest *>::iterator it2 = m_waitingResponses.find(request);
  260. MORDOR_ASSERT(it2 != m_waitingResponses.end());
  261. m_waitingResponses.erase(it2);
  262. request->m_responseState = ClientRequest::HEADERS;
  263. MORDOR_ASSERT(request->m_scheduler);
  264. MORDOR_ASSERT(request->m_fiber);
  265. MORDOR_LOG_TRACE(g_log) << m_connectionNumber << "-" << request->m_requestNumber << " scheduling response";
  266. request->m_scheduler->schedule(request->m_fiber);
  267. request->m_scheduler = NULL;
  268. request->m_fiber.reset();
  269. request = NULL;
  270. } else if (request->m_responseState == ClientRequest::PENDING ||
  271. request->m_responseState == ClientRequest::ERROR) {
  272. request = NULL;
  273. }
  274. } else {
  275. if (m_idleTimeout != ~0ull) {
  276. MORDOR_ASSERT(!m_idleTimer);
  277. MORDOR_ASSERT(m_timerManager);
  278. MORDOR_ASSERT(m_idleDg);
  279. m_idleTimer = m_timerManager->registerTimer(m_idleTimeout, m_idleDg);
  280. }
  281. request = NULL;
  282. }
  283. }
  284. if (request) {
  285. MORDOR_ASSERT(request->m_responseState == ClientRequest::CANCELED);
  286. MORDOR_LOG_TRACE(g_log) << m_connectionNumber << "-" << request->m_requestNumber << " skipping response";
  287. request->finish();
  288. request = NULL;
  289. }
  290. if (close && m_stream->supportsHalfClose()) {
  291. MORDOR_ASSERT(!request);
  292. MORDOR_LOG_TRACE(g_log) << m_connectionNumber << " closing";
  293. try {
  294. m_stream->close(Stream::READ);
  295. } catch (...) {
  296. }
  297. }
  298. }
  299. void
  300. ClientConnection::scheduleAllWaitingRequests()
  301. {
  302. MORDOR_ASSERT(m_priorRequestFailed || m_priorResponseFailed != ~0ull ||
  303. m_priorResponseClosed != ~0ull);
  304. // MORDOR_ASSERT(m_mutex.locked());
  305. MORDOR_LOG_TRACE(g_log) << m_connectionNumber << " scheduling all requests";
  306. for (std::list<ClientRequest *>::iterator it(m_currentRequest);
  307. it != m_pendingRequests.end();
  308. ) {
  309. ClientRequest *request = *it;
  310. MORDOR_ASSERT(request->m_requestState != ClientRequest::COMPLETE);
  311. if (request->m_requestState == ClientRequest::WAITING) {
  312. MORDOR_ASSERT(request->m_scheduler);
  313. MORDOR_ASSERT(request->m_fiber);
  314. MORDOR_LOG_TRACE(g_log) << m_connectionNumber << "-" << request->m_requestNumber << " scheduling request";
  315. request->m_scheduler->schedule(request->m_fiber);
  316. request->m_scheduler = NULL;
  317. request->m_fiber.reset();
  318. if (m_currentRequest == it) {
  319. m_currentRequest = it = m_pendingRequests.erase(it);
  320. } else {
  321. it = m_pendingRequests.erase(it);
  322. }
  323. } else {
  324. ++it;
  325. }
  326. }
  327. }
  328. void
  329. ClientConnection::scheduleAllWaitingResponses()
  330. {
  331. MORDOR_ASSERT(m_priorResponseFailed != ~0ull || m_priorResponseClosed != ~0ull);
  332. // MORDOR_ASSERT(m_mutex.locked());
  333. MORDOR_LOG_TRACE(g_log) << m_connectionNumber << " scheduling all responses";
  334. unsigned long long firstResponseToSchedule =
  335. (std::min)(m_priorResponseFailed, m_priorResponseClosed);
  336. std::list<ClientRequest *>::iterator end = m_currentRequest;
  337. if (end != m_pendingRequests.end())
  338. ++end;
  339. for (std::list<ClientRequest *>::iterator it(m_pendingRequests.begin());
  340. it != end;) {
  341. ClientRequest *request = *it;
  342. if (request->m_requestNumber > firstResponseToSchedule) {
  343. switch (request->m_responseState) {
  344. case ClientRequest::PENDING:
  345. case ClientRequest::ERROR:
  346. ++it;
  347. continue;
  348. case ClientRequest::WAITING:
  349. {
  350. std::set<ClientRequest *>::iterator waiting =
  351. m_waitingResponses.find(request);
  352. MORDOR_ASSERT(waiting != m_waitingResponses.end());
  353. MORDOR_ASSERT(request->m_scheduler);
  354. MORDOR_ASSERT(request->m_fiber);
  355. MORDOR_ASSERT(request->m_responseState ==
  356. ClientRequest::WAITING);
  357. MORDOR_LOG_TRACE(g_log) << m_connectionNumber << "-"
  358. << request->m_requestNumber << " scheduling response";
  359. request->m_responseState = ClientRequest::ERROR;
  360. request->m_scheduler->schedule(request->m_fiber);
  361. request->m_scheduler = NULL;
  362. request->m_fiber.reset();
  363. if (request->m_requestState >= ClientRequest::COMPLETE)
  364. it = m_pendingRequests.erase(it);
  365. m_waitingResponses.erase(waiting);
  366. continue;
  367. }
  368. default:
  369. MORDOR_NOTREACHED();
  370. }
  371. }
  372. ++it;
  373. }
  374. }
  375. void
  376. ClientConnection::invariant() const
  377. {
  378. #ifndef NDEBUG
  379. // MORDOR_ASSERT(m_mutex.locked());
  380. bool seenFirstUnrequested = false;
  381. unsigned long long lastRequestNumber = 0;
  382. for (std::list<ClientRequest *>::const_iterator it(m_pendingRequests.begin());
  383. it != m_pendingRequests.end();
  384. ++it) {
  385. ClientRequest *request = *it;
  386. MORDOR_ASSERT(request->m_requestNumber != 0);
  387. if (lastRequestNumber == 0) {
  388. lastRequestNumber = request->m_requestNumber;
  389. } else {
  390. MORDOR_ASSERT(lastRequestNumber + 1 == request->m_requestNumber ||
  391. request->m_requestNumber >= m_priorResponseFailed ||
  392. request->m_requestNumber >= m_priorResponseClosed);
  393. lastRequestNumber = request->m_requestNumber;
  394. }
  395. MORDOR_ASSERT(request->m_requestState < ClientRequest::COMPLETE ||
  396. request->m_responseState < ClientRequest::COMPLETE ||
  397. request->m_responseState == ClientRequest::CANCELED);
  398. // NOTE: it is allowed to have a response complete before the request
  399. // completes, BUT you can't have a response start before the request
  400. // starts
  401. if (request->m_responseState > ClientRequest::WAITING)
  402. MORDOR_ASSERT(request->m_requestState > ClientRequest::WAITING);
  403. if (!seenFirstUnrequested) {
  404. if (request->m_requestState < ClientRequest::COMPLETE) {
  405. seenFirstUnrequested = true;
  406. MORDOR_ASSERT(request->m_requestState >
  407. ClientRequest::WAITING);
  408. MORDOR_ASSERT(m_currentRequest == it);
  409. }
  410. } else {
  411. MORDOR_ASSERT(request->m_requestState == ClientRequest::WAITING);
  412. }
  413. if (it != m_pendingRequests.begin())
  414. MORDOR_ASSERT(request->m_responseState <= ClientRequest::WAITING ||
  415. request->m_responseState > ClientRequest::COMPLETE);
  416. }
  417. if (!seenFirstUnrequested)
  418. MORDOR_ASSERT(m_currentRequest == m_pendingRequests.end());
  419. std::list<ClientRequest *>::const_iterator end = m_currentRequest;
  420. if (end != m_pendingRequests.end())
  421. ++end;
  422. for (std::set<ClientRequest *>::const_iterator it(m_waitingResponses.begin());
  423. it != m_waitingResponses.end();
  424. ++it) {
  425. ClientRequest *request = *it;
  426. MORDOR_ASSERT(request);
  427. MORDOR_ASSERT(request->m_responseState == ClientRequest::WAITING);
  428. MORDOR_ASSERT(std::find<std::list<ClientRequest *>::const_iterator>
  429. (m_pendingRequests.begin(), end, request) != end);
  430. }
  431. #endif
  432. }
  433. void ClientRequest::RequestLogger::logRequest(size_t connNum, long long requestNum, const Request &request, bool censorAuth)
  434. {
  435. if (g_log->enabled(Log::DEBUG)) {
  436. std::ostringstream os;
  437. bool basicAuth = censorAuth ? (stricmp(request.request.authorization.scheme.c_str(), "Basic") == 0) : false;
  438. bool basicProxyAuth = censorAuth ? (stricmp(request.request.proxyAuthorization.scheme.c_str(), "Basic") == 0) : false;
  439. bool oauth = censorAuth ? (stricmp(request.request.authorization.scheme.c_str(), "Bearer") == 0) : false;
  440. bool oauthProxy = censorAuth ? (stricmp(request.request.proxyAuthorization.scheme.c_str(), "Bearer") == 0) : false;
  441. if (basicAuth || basicProxyAuth || oauth || oauthProxy) {
  442. Request censoredRequest(request);
  443. if (basicAuth || oauth)
  444. censoredRequest.request.authorization.param = "<hidden>";
  445. if (basicProxyAuth || oauthProxy)
  446. censoredRequest.request.proxyAuthorization.param = "<hidden>";
  447. os << censoredRequest;
  448. } else {
  449. os << request;
  450. }
  451. MORDOR_LOG_DEBUG(g_log) << connNum << "-" << requestNum << " " << os.str();
  452. } else {
  453. MORDOR_LOG_VERBOSE(g_log) << connNum << "-" << requestNum << " " << request.requestLine;
  454. }
  455. }
  456. void ClientRequest::RequestLogger::logResponse(size_t connNum, long long requestNum, const Request &request, const Response &response)
  457. {
  458. if (g_log->enabled(Log::DEBUG)) {
  459. MORDOR_LOG_DEBUG(g_log) << connNum << "-" << requestNum << " " << response;
  460. } else {
  461. MORDOR_LOG_VERBOSE(g_log) << connNum << "-" << requestNum << " " << response.status;
  462. }
  463. }
  464. /* static */ boost::shared_ptr<ClientRequest::RequestLogger> ClientRequest::msp_requestLogger( new ClientRequest::RequestLogger );
  465. void ClientRequest::setRequestLogger(boost::shared_ptr<ClientRequest::RequestLogger> newRequestLogger)
  466. {
  467. if (newRequestLogger)
  468. msp_requestLogger = newRequestLogger;
  469. else
  470. msp_requestLogger.reset( new ClientRequest::RequestLogger );
  471. }
  472. ClientRequest::ClientRequest(ClientConnection::ptr conn, const Request &request)
  473. : m_conn(conn),
  474. m_requestNumber(0),
  475. m_scheduler(NULL),
  476. m_request(request),
  477. m_requestState(WAITING),
  478. m_responseState(PENDING),
  479. m_badTrailer(false),
  480. m_incompleteTrailer(false),
  481. m_hasResponseBody(false)
  482. {
  483. MORDOR_ASSERT(m_conn);
  484. }
  485. ClientRequest::~ClientRequest()
  486. {
  487. cancel(true);
  488. #ifndef NDEBUG
  489. MORDOR_NOTHROW_ASSERT(m_conn);
  490. boost::mutex::scoped_lock lock(m_conn->m_mutex);
  491. MORDOR_NOTHROW_ASSERT(std::find(m_conn->m_pendingRequests.begin(),
  492. m_conn->m_pendingRequests.end(),
  493. this) == m_conn->m_pendingRequests.end());
  494. MORDOR_NOTHROW_ASSERT(m_conn->m_waitingResponses.find(this) ==
  495. m_conn->m_waitingResponses.end());
  496. #endif
  497. }
  498. Request &
  499. ClientRequest::request()
  500. {
  501. return m_request;
  502. }
  503. bool
  504. ClientRequest::hasRequestBody() const
  505. {
  506. return Connection::hasMessageBody(m_request.general,
  507. m_request.entity, m_request.requestLine.method, INVALID, false);
  508. }
  509. Stream::ptr
  510. ClientRequest::requestStream()
  511. {
  512. if (m_requestState == ERROR) {
  513. if (m_conn->m_priorResponseClosed <= m_requestNumber)
  514. MORDOR_THROW_EXCEPTION(ConnectionVoluntarilyClosedException());
  515. else
  516. MORDOR_THROW_EXCEPTION(PriorRequestFailedException());
  517. }
  518. if (m_requestStream) {
  519. MORDOR_ASSERT(m_request.entity.contentType.type != "multipart");
  520. return m_requestStream;
  521. }
  522. doRequest();
  523. MORDOR_ASSERT(!m_requestMultipart);
  524. MORDOR_ASSERT(m_request.entity.contentType.type != "multipart");
  525. if (!hasRequestBody()) {
  526. m_requestStream = Stream::ptr(&NullStream::get(), &nop<Stream *>);
  527. m_requestStream.reset(new LimitedStream(m_requestStream, 0));
  528. return m_requestStream;
  529. }
  530. MORDOR_ASSERT(m_requestState == BODY);
  531. return m_requestStream = m_conn->getStream(m_request.general, m_request.entity,
  532. m_request.requestLine.method, INVALID,
  533. boost::bind(&ClientRequest::requestDone, this),
  534. boost::bind(&ClientRequest::requestFailed, this), false);
  535. }
  536. Multipart::ptr
  537. ClientRequest::requestMultipart()
  538. {
  539. if (m_requestMultipart)
  540. return m_requestMultipart;
  541. doRequest();
  542. MORDOR_ASSERT(m_request.entity.contentType.type == "multipart");
  543. MORDOR_ASSERT(!m_requestStream);
  544. MORDOR_ASSERT(m_requestState == BODY);
  545. StringMap::const_iterator it = m_request.entity.contentType.parameters.find("boundary");
  546. if (it == m_request.entity.contentType.parameters.end()) {
  547. MORDOR_THROW_EXCEPTION(MissingMultipartBoundaryException());
  548. }
  549. m_requestStream = m_conn->getStream(m_request.general, m_request.entity,
  550. m_request.requestLine.method, INVALID,
  551. boost::bind(&ClientRequest::requestDone, this),
  552. boost::bind(&ClientRequest::requestFailed, this), false);
  553. m_requestMultipart.reset(new Multipart(m_requestStream, it->second));
  554. m_requestMultipart->multipartFinished = boost::bind(&ClientRequest::requestMultipartDone, shared_from_this());
  555. return m_requestMultipart;
  556. }
  557. EntityHeaders &
  558. ClientRequest::requestTrailer()
  559. {
  560. // If transferEncoding is not empty, it must include chunked,
  561. // and it must include chunked in order to have a trailer
  562. MORDOR_ASSERT(!m_request.general.transferEncoding.empty());
  563. return m_requestTrailer;
  564. }
  565. const Response &
  566. ClientRequest::response()
  567. {
  568. ensureResponse();
  569. return m_response;
  570. }
  571. bool
  572. ClientRequest::hasResponseBody()
  573. {
  574. ensureResponse();
  575. if (m_hasResponseBody)
  576. return true;
  577. return Connection::hasMessageBody(m_response.general,
  578. m_response.entity,
  579. m_request.requestLine.method,
  580. m_response.status.status);
  581. }
  582. Stream::ptr
  583. ClientRequest::responseStream()
  584. {
  585. Stream::ptr result = m_responseStream.lock();
  586. if (result || m_hasResponseBody) {
  587. MORDOR_ASSERT(result &&
  588. "responseStream() can only be accessed once without caching it");
  589. MORDOR_ASSERT(m_response.entity.contentType.type != "multipart");
  590. return result;
  591. }
  592. ensureResponse();
  593. if (m_responseState >= COMPLETE) {
  594. m_hasResponseBody = true;
  595. result.reset(&NullStream::get(), &nop<Stream *>);
  596. m_responseStream = result;
  597. return result;
  598. }
  599. MORDOR_ASSERT(m_responseState == BODY);
  600. MORDOR_ASSERT(m_response.entity.contentType.type != "multipart");
  601. result = m_conn->getStream(m_response.general, m_response.entity,
  602. m_request.requestLine.method, m_response.status.status,
  603. boost::bind(&ClientRequest::responseDone, shared_from_this()),
  604. boost::bind(&ClientRequest::cancel, shared_from_this(), true, true), true);
  605. m_hasResponseBody = true;
  606. m_responseStream = result;
  607. return result;
  608. }
  609. const EntityHeaders &
  610. ClientRequest::responseTrailer() const
  611. {
  612. if (m_badTrailer)
  613. MORDOR_THROW_EXCEPTION(BadMessageHeaderException());
  614. if (m_incompleteTrailer)
  615. MORDOR_THROW_EXCEPTION(IncompleteMessageHeaderException());
  616. MORDOR_ASSERT(m_responseState >= COMPLETE);
  617. MORDOR_ASSERT(!m_response.general.transferEncoding.empty());
  618. return m_responseTrailer;
  619. }
  620. Stream::ptr
  621. ClientRequest::stream()
  622. {
  623. MORDOR_ASSERT(m_request.requestLine.method == CONNECT);
  624. ensureResponse();
  625. MORDOR_ASSERT(m_response.status.status == OK);
  626. return m_conn->m_stream;
  627. }
  628. Multipart::ptr
  629. ClientRequest::responseMultipart()
  630. {
  631. MORDOR_ASSERT(m_response.entity.contentType.type == "multipart");
  632. Multipart::ptr result = m_responseMultipart.lock();
  633. if (result) {
  634. MORDOR_ASSERT(m_hasResponseBody);
  635. return result;
  636. }
  637. // You can only ask for the response multipart once
  638. // (to avoid circular references)
  639. MORDOR_ASSERT(!m_hasResponseBody);
  640. ensureResponse();
  641. MORDOR_ASSERT(m_responseState == BODY);
  642. StringMap::const_iterator it = m_response.entity.contentType.parameters.find("boundary");
  643. if (it == m_response.entity.contentType.parameters.end()) {
  644. MORDOR_THROW_EXCEPTION(MissingMultipartBoundaryException());
  645. }
  646. Stream::ptr stream = m_conn->getStream(m_response.general, m_response.entity,
  647. m_request.requestLine.method, m_response.status.status,
  648. NULL,
  649. boost::bind(&ClientRequest::cancel, shared_from_this(), true, true), true);
  650. m_responseStream = stream;
  651. result.reset(new Multipart(stream, it->second));
  652. result->multipartFinished = boost::bind(&ClientRequest::responseDone, shared_from_this());
  653. m_responseMultipart = result;
  654. m_hasResponseBody = true;
  655. return result;
  656. }
  657. void
  658. ClientRequest::cancel(bool abort, bool error)
  659. {
  660. if (m_requestState >= COMPLETE && m_responseState >= COMPLETE)
  661. return;
  662. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber
  663. << (abort ? " aborting" : " cancelling");
  664. if (!abort && m_requestState == WAITING && m_responseState <= WAITING) {
  665. // Just abandon it
  666. m_requestState = CANCELED;
  667. m_responseState = CANCELED;
  668. boost::mutex::scoped_lock lock(m_conn->m_mutex);
  669. m_conn->invariant();
  670. std::list<ClientRequest *>::iterator it =
  671. std::find(m_conn->m_pendingRequests.begin(),
  672. m_conn->m_pendingRequests.end(), this);
  673. MORDOR_ASSERT(it != m_conn->m_pendingRequests.end());
  674. m_conn->m_pendingRequests.erase(it);
  675. if (m_responseState == WAITING) {
  676. std::set<ClientRequest *>::iterator waitIt =
  677. m_conn->m_waitingResponses.find(this);
  678. MORDOR_ASSERT(waitIt != m_conn->m_waitingResponses.end());
  679. m_conn->m_waitingResponses.erase(waitIt);
  680. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber
  681. << " scheduling response";
  682. m_scheduler->schedule(m_fiber);
  683. m_scheduler = NULL;
  684. m_fiber.reset();
  685. }
  686. return;
  687. }
  688. if (m_requestStream) {
  689. FilterStream *filter = static_cast<FilterStream *>(m_requestStream.get());
  690. if (filter->parent().get() != &NullStream::get()) {
  691. // Break the circular reference
  692. NotifyStream::ptr notify =
  693. boost::dynamic_pointer_cast<NotifyStream>(m_requestStream);
  694. MORDOR_ASSERT(notify);
  695. notify->notifyOnClose(NULL);
  696. notify->notifyOnEof = NULL;
  697. notify->notifyOnException = NULL;
  698. notify->parent(Stream::ptr(new Stream()));
  699. }
  700. }
  701. Stream::ptr responseStream = m_responseStream.lock();
  702. ClientRequest::ptr self;
  703. if (responseStream) {
  704. // notify may be holding the last reference to this, so keep ourself in scope
  705. self = shared_from_this();
  706. NotifyStream::ptr notify =
  707. boost::dynamic_pointer_cast<NotifyStream>(responseStream);
  708. MORDOR_ASSERT(notify);
  709. notify->notifyOnClose(NULL);
  710. notify->notifyOnEof = NULL;
  711. notify->notifyOnException = NULL;
  712. }
  713. bool close = false, waiting = m_responseState == WAITING;
  714. if (m_responseState != HEADERS)
  715. abort = true;
  716. {
  717. boost::mutex::scoped_lock lock(m_conn->m_mutex);
  718. m_conn->invariant();
  719. m_conn->m_priorResponseFailed = m_requestNumber;
  720. if (m_requestState < COMPLETE)
  721. m_requestState = error ? ERROR : CANCELED;
  722. if (m_responseState < COMPLETE && abort)
  723. m_responseState = error ? ERROR : CANCELED;
  724. std::list<ClientRequest *>::iterator it =
  725. std::find(m_conn->m_pendingRequests.begin(),
  726. m_conn->m_pendingRequests.end(), this);
  727. MORDOR_ASSERT(it != m_conn->m_pendingRequests.end());
  728. close = it == m_conn->m_pendingRequests.begin();
  729. if (abort) {
  730. if (it == m_conn->m_currentRequest)
  731. m_conn->m_currentRequest = m_conn->m_pendingRequests.erase(it);
  732. else
  733. m_conn->m_pendingRequests.erase(it);
  734. } else if (it == m_conn->m_currentRequest) {
  735. ++m_conn->m_currentRequest;
  736. }
  737. if (waiting) {
  738. std::set<ClientRequest *>::iterator waitIt =
  739. m_conn->m_waitingResponses.find(this);
  740. MORDOR_ASSERT(waitIt != m_conn->m_waitingResponses.end());
  741. m_conn->m_waitingResponses.erase(waitIt);
  742. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber
  743. << " scheduling response";
  744. m_scheduler->schedule(m_fiber);
  745. m_scheduler = NULL;
  746. m_fiber.reset();
  747. }
  748. m_conn->scheduleAllWaitingRequests();
  749. m_conn->scheduleAllWaitingResponses();
  750. }
  751. if (close)
  752. m_conn->m_stream->cancelRead();
  753. m_conn->m_stream->cancelWrite();
  754. }
  755. void
  756. ClientRequest::finish()
  757. {
  758. if (m_requestState != COMPLETE) {
  759. cancel(true);
  760. return;
  761. }
  762. if (hasResponseBody()) {
  763. if (m_response.entity.contentType.type == "multipart") {
  764. Multipart::ptr multipart;
  765. if (m_hasResponseBody)
  766. multipart = m_responseMultipart.lock();
  767. else
  768. multipart = responseMultipart();
  769. if (!multipart)
  770. cancel(true);
  771. else
  772. while(multipart->nextPart());
  773. } else {
  774. Stream::ptr stream;
  775. if (m_hasResponseBody)
  776. stream = m_responseStream.lock();
  777. else
  778. stream = responseStream();
  779. if (!stream)
  780. cancel(true);
  781. else
  782. transferStream(stream, NullStream::get());
  783. }
  784. }
  785. }
  786. void
  787. ClientRequest::waitForRequest()
  788. {
  789. bool firstRequest;
  790. // Put the request in the queue
  791. {
  792. boost::mutex::scoped_lock lock(m_conn->m_mutex);
  793. m_conn->invariant();
  794. if (!m_conn->m_allowNewRequests || m_conn->m_priorResponseClosed != ~0ull) {
  795. m_requestState = m_responseState = ERROR;
  796. MORDOR_THROW_EXCEPTION(ConnectionVoluntarilyClosedException());
  797. }
  798. if (m_conn->m_priorRequestFailed || m_conn->m_priorResponseFailed != ~0ull) {
  799. m_requestState = m_responseState = ERROR;
  800. MORDOR_THROW_EXCEPTION(PriorRequestFailedException());
  801. }
  802. if (m_conn->m_idleTimer) {
  803. m_conn->m_idleTimer->cancel();
  804. m_conn->m_idleTimer.reset();
  805. }
  806. firstRequest = m_conn->m_currentRequest == m_conn->m_pendingRequests.end();
  807. m_requestNumber = ++m_conn->m_requestCount;
  808. m_conn->m_pendingRequests.push_back(this);
  809. if (firstRequest) {
  810. m_conn->m_currentRequest = m_conn->m_pendingRequests.end();
  811. --m_conn->m_currentRequest;
  812. m_requestState = HEADERS;
  813. // Disable read timeouts while a request is in progress
  814. if (m_conn->m_timeoutStream) {
  815. lock.unlock();
  816. m_conn->m_timeoutStream->readTimeout(~0ull);
  817. lock.lock();
  818. }
  819. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber << " requesting";
  820. } else {
  821. m_scheduler = Scheduler::getThis();
  822. m_fiber = Fiber::getThis();
  823. MORDOR_ASSERT(m_scheduler);
  824. MORDOR_ASSERT(m_fiber);
  825. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber << " waiting to request";
  826. }
  827. }
  828. // If we weren't the first request in the queue, we have to wait for
  829. // another request to schedule us
  830. if (!firstRequest) {
  831. Scheduler::yieldTo();
  832. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber << " requesting";
  833. // Check for problems that occurred while we were waiting
  834. boost::mutex::scoped_lock lock(m_conn->m_mutex);
  835. m_conn->invariant();
  836. if (m_conn->m_priorResponseClosed != ~0ull ||
  837. m_conn->m_priorRequestFailed ||
  838. m_conn->m_priorResponseFailed != ~0ull) {
  839. if (m_requestState == HEADERS) {
  840. MORDOR_ASSERT(m_conn->m_currentRequest !=
  841. m_conn->m_pendingRequests.end());
  842. MORDOR_ASSERT(*m_conn->m_currentRequest == this);
  843. m_conn->m_currentRequest =
  844. m_conn->m_pendingRequests.erase(m_conn->m_currentRequest);
  845. MORDOR_ASSERT(m_conn->m_currentRequest ==
  846. m_conn->m_pendingRequests.end());
  847. }
  848. m_requestState = m_responseState = ERROR;
  849. if (m_conn->m_priorResponseClosed != ~0ull)
  850. MORDOR_THROW_EXCEPTION(ConnectionVoluntarilyClosedException());
  851. else
  852. MORDOR_THROW_EXCEPTION(PriorRequestFailedException());
  853. }
  854. }
  855. MORDOR_ASSERT(m_requestState == HEADERS);
  856. }
  857. void
  858. ClientRequest::doRequest()
  859. {
  860. if (m_requestState > HEADERS)
  861. return;
  862. RequestLine &requestLine = m_request.requestLine;
  863. // 1.0, 1.1, or defaulted
  864. MORDOR_ASSERT(requestLine.ver == Version() ||
  865. requestLine.ver == Version(1, 0) ||
  866. requestLine.ver == Version(1, 1));
  867. // Have to request *something*
  868. MORDOR_ASSERT(requestLine.uri.isDefined());
  869. // Host header required with HTTP/1.1
  870. MORDOR_ASSERT(!m_request.request.host.empty() || requestLine.ver != Version(1, 1));
  871. // If any transfer encodings, must include chunked, must have chunked only once, and must be the last one
  872. const ParameterizedList &transferEncoding = m_request.general.transferEncoding;
  873. if (!transferEncoding.empty()) {
  874. MORDOR_ASSERT(stricmp(transferEncoding.back().value.c_str(), "chunked") == 0);
  875. for (ParameterizedList::const_iterator it(transferEncoding.begin());
  876. it + 1 != transferEncoding.end();
  877. ++it) {
  878. // Only the last one can be chunked
  879. MORDOR_ASSERT(stricmp(it->value.c_str(), "chunked") != 0);
  880. // identity is only acceptable in the TE header field
  881. MORDOR_ASSERT(it->value != "identity");
  882. if (it->value == "gzip" ||
  883. it->value == "x-gzip" ||
  884. it->value == "deflate") {
  885. // Known Transfer-Codings
  886. continue;
  887. } else if (it->value == "compress" ||
  888. it->value == "x-compress") {
  889. // Unsupported Transfer-Codings
  890. MORDOR_NOTREACHED();
  891. } else {
  892. // Unknown Transfer-Coding
  893. MORDOR_NOTREACHED();
  894. }
  895. }
  896. }
  897. bool close;
  898. // Default HTTP version... 1.1 if possible
  899. if (requestLine.ver == Version()) {
  900. if (m_request.request.host.empty())
  901. requestLine.ver = Version(1, 0);
  902. else
  903. requestLine.ver = Version(1, 1);
  904. }
  905. // If not specified, try to keep the connection open
  906. StringSet &connection = m_request.general.connection;
  907. if (connection.find("close") == connection.end() && requestLine.ver == Version(1, 0)) {
  908. connection.insert("Keep-Alive");
  909. }
  910. // Determine if we're closing the connection after this request
  911. if (requestLine.ver == Version(1, 0)) {
  912. if (connection.find("Keep-Alive") != connection.end()) {
  913. close = false;
  914. } else {
  915. close = true;
  916. connection.insert("close");
  917. }
  918. } else {
  919. if (connection.find("close") != connection.end()) {
  920. close = true;
  921. } else {
  922. close = false;
  923. }
  924. }
  925. if (close) {
  926. boost::mutex::scoped_lock lock(m_conn->m_mutex);
  927. m_conn->invariant();
  928. m_conn->m_allowNewRequests = false;
  929. }
  930. // TE is a connection-specific header
  931. if (!m_request.request.te.empty())
  932. m_request.general.connection.insert("TE");
  933. try {
  934. // Do the request
  935. std::ostringstream os;
  936. os << m_request;
  937. std::string str = os.str();
  938. msp_requestLogger->logRequest(m_conn->m_connectionNumber, m_requestNumber, m_request);
  939. m_conn->m_stream->write(str.c_str(), str.size());
  940. if (!Connection::hasMessageBody(m_request.general, m_request.entity, requestLine.method, INVALID, false)) {
  941. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber << " no request body";
  942. m_conn->scheduleNextRequest(this);
  943. } else {
  944. m_requestState = BODY;
  945. }
  946. } catch(...) {
  947. requestFailed();
  948. throw;
  949. }
  950. }
  951. void
  952. ClientRequest::ensureResponse()
  953. {
  954. if (m_priorResponseException)
  955. ::Mordor::rethrow_exception(m_priorResponseException);
  956. if (m_responseState == BODY || m_responseState >= COMPLETE)
  957. return;
  958. try {
  959. bool wait = false;
  960. MORDOR_ASSERT(m_responseState == PENDING);
  961. {
  962. boost::mutex::scoped_lock lock(m_conn->m_mutex);
  963. m_conn->invariant();
  964. if (m_conn->m_priorResponseFailed <= m_requestNumber ||
  965. m_conn->m_priorResponseClosed <= m_requestNumber) {
  966. if (m_requestState >= COMPLETE) {
  967. std::list<ClientRequest *>::iterator it;
  968. it = std::find(m_conn->m_pendingRequests.begin(),
  969. m_conn->m_pendingRequests.end(), this);
  970. MORDOR_ASSERT(it != m_conn->m_pendingRequests.end());
  971. m_conn->m_pendingRequests.erase(it);
  972. }
  973. m_responseState = ERROR;
  974. if (m_conn->m_priorResponseClosed <= m_requestNumber)
  975. MORDOR_THROW_EXCEPTION(ConnectionVoluntarilyClosedException());
  976. else
  977. MORDOR_THROW_EXCEPTION(PriorRequestFailedException());
  978. }
  979. MORDOR_ASSERT(!m_conn->m_pendingRequests.empty());
  980. ClientRequest *request = m_conn->m_pendingRequests.front();
  981. if (request != this) {
  982. m_scheduler = Scheduler::getThis();
  983. m_fiber = Fiber::getThis();
  984. MORDOR_ASSERT(m_scheduler);
  985. MORDOR_ASSERT(m_fiber);
  986. MORDOR_VERIFY(m_conn->m_waitingResponses.insert(this).second);
  987. wait = true;
  988. m_responseState = WAITING;
  989. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber<< " waiting for response";
  990. } else {
  991. m_responseState = HEADERS;
  992. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber << " reading response";
  993. }
  994. }
  995. // If we weren't the first response in the queue, wait for someone
  996. // else to schedule us
  997. if (wait) {
  998. Scheduler::yieldTo();
  999. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber << " reading response";
  1000. // Check for problems that occurred while we were waiting
  1001. boost::mutex::scoped_lock lock(m_conn->m_mutex);
  1002. m_conn->invariant();
  1003. if (m_responseState == CANCELED)
  1004. MORDOR_THROW_EXCEPTION(OperationAbortedException());
  1005. if (m_responseState == ERROR) {
  1006. if (m_conn->m_priorResponseClosed <= m_requestNumber)
  1007. MORDOR_THROW_EXCEPTION(ConnectionVoluntarilyClosedException());
  1008. else
  1009. MORDOR_THROW_EXCEPTION(PriorRequestFailedException());
  1010. }
  1011. // Probably means that the Scheduler exited in the above yieldTo,
  1012. // and returned to us, because there is no other work to be done
  1013. try {
  1014. MORDOR_ASSERT(!m_conn->m_pendingRequests.empty());
  1015. MORDOR_ASSERT(m_conn->m_pendingRequests.front() == this);
  1016. } catch(...) {
  1017. m_responseState = PENDING;
  1018. std::set<ClientRequest *>::iterator it = m_conn->m_waitingResponses.find(this);
  1019. if (it != m_conn->m_waitingResponses.end())
  1020. m_conn->m_waitingResponses.erase(it);
  1021. throw;
  1022. }
  1023. }
  1024. try {
  1025. MORDOR_ASSERT(m_responseState == HEADERS);
  1026. // Read and parse headers
  1027. ResponseParser parser(m_response);
  1028. unsigned long long read = parser.run(m_conn->m_stream);
  1029. MORDOR_ASSERT(m_responseState == HEADERS || m_responseState > COMPLETE);
  1030. if (m_responseState > COMPLETE)
  1031. MORDOR_THROW_EXCEPTION(OperationAbortedException());
  1032. if (read == 0ull)
  1033. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  1034. if (parser.error())
  1035. MORDOR_THROW_EXCEPTION(BadMessageHeaderException());
  1036. if (!parser.complete())
  1037. MORDOR_THROW_EXCEPTION(IncompleteMessageHeaderException());
  1038. msp_requestLogger->logResponse(m_conn->m_connectionNumber, m_requestNumber, m_request, m_response);
  1039. bool close = false;
  1040. StringSet &connection = m_response.general.connection;
  1041. StringSet &proxyConnection = m_response.general.proxyConnection; // NON-STANDARD!!!
  1042. if (m_response.status.ver == Version(1, 0)) {
  1043. // When using a HTTP 1.0 proxy server then Keep-Alive may come via the Proxy-Connection header
  1044. // instead of more standard "Connection"
  1045. if (connection.find("Keep-Alive") == connection.end() && proxyConnection.find("Keep-Alive") == proxyConnection.end())
  1046. close = true;
  1047. } else if (m_response.status.ver == Version(1, 1)) {
  1048. if (connection.find("close") != connection.end())
  1049. close = true;
  1050. } else {
  1051. MORDOR_THROW_EXCEPTION(BadMessageHeaderException());
  1052. }
  1053. if (proxyConnection.find("close") != proxyConnection.end())
  1054. close = true;
  1055. ParameterizedList &transferEncoding = m_response.general.transferEncoding;
  1056. // Remove identity from the Transfer-Encodings
  1057. for (ParameterizedList::iterator it(transferEncoding.begin());
  1058. it != transferEncoding.end();
  1059. ++it) {
  1060. if (stricmp(it->value.c_str(), "identity") == 0) {
  1061. it = transferEncoding.erase(it);
  1062. --it;
  1063. }
  1064. }
  1065. if (!transferEncoding.empty()) {
  1066. if (stricmp(transferEncoding.back().value.c_str(), "chunked") != 0) {
  1067. MORDOR_THROW_EXCEPTION(InvalidTransferEncodingException("The last transfer-coding is not chunked."));
  1068. }
  1069. for (ParameterizedList::const_iterator it(transferEncoding.begin());
  1070. it + 1 != transferEncoding.end();
  1071. ++it) {
  1072. if (stricmp(it->value.c_str(), "chunked") == 0) {
  1073. MORDOR_THROW_EXCEPTION(InvalidTransferEncodingException("chunked transfer-coding applied multiple times"));
  1074. } else if (stricmp(it->value.c_str(), "deflate") == 0 ||
  1075. stricmp(it->value.c_str(), "gzip") == 0 ||
  1076. stricmp(it->value.c_str(), "x-gzip") == 0) {
  1077. // Supported transfer-codings
  1078. } else if (stricmp(it->value.c_str(), "compress") == 0 ||
  1079. stricmp(it->value.c_str(), "x-compress") == 0) {
  1080. MORDOR_THROW_EXCEPTION(InvalidTransferEncodingException("compress transfer-coding is unsupported"));
  1081. } else {
  1082. MORDOR_THROW_EXCEPTION(InvalidTransferEncodingException("Unrecognized transfer-coding: " + it->value));
  1083. }
  1084. }
  1085. }
  1086. // If the there is a message body, but it's undelimited, make sure we're
  1087. // closing the connection
  1088. bool hasBody = Connection::hasMessageBody(m_response.general, m_response.entity,
  1089. m_request.requestLine.method, m_response.status.status, false);
  1090. if (hasBody &&
  1091. transferEncoding.empty() && m_response.entity.contentLength == ~0ull &&
  1092. m_response.entity.contentType.type != "multipart") {
  1093. close = true;
  1094. }
  1095. bool connect = m_request.requestLine.method == CONNECT &&
  1096. m_response.status.status == OK;
  1097. if (connect)
  1098. close = true;
  1099. if (close) {
  1100. boost::mutex::scoped_lock lock(m_conn->m_mutex);
  1101. m_conn->invariant();
  1102. m_conn->m_priorResponseClosed = m_requestNumber;
  1103. MORDOR_ASSERT(!m_conn->m_pendingRequests.empty());
  1104. MORDOR_ASSERT(m_conn->m_pendingRequests.front() == this);
  1105. if (!hasBody && m_requestState >= COMPLETE)
  1106. m_conn->m_pendingRequests.pop_front();
  1107. m_responseState = hasBody ? BODY : COMPLETE;
  1108. m_conn->scheduleAllWaitingRequests();
  1109. m_conn->scheduleAllWaitingResponses();
  1110. } else {
  1111. m_responseState = connect ? COMPLETE : BODY;
  1112. }
  1113. if (!hasBody && !connect) {
  1114. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber << " no response body";
  1115. if (close) {
  1116. if (m_conn->m_stream->supportsHalfClose()) {
  1117. try {
  1118. m_conn->m_stream->close(Stream::READ);
  1119. } catch (...) {
  1120. }
  1121. }
  1122. } else {
  1123. m_conn->scheduleNextResponse(this);
  1124. }
  1125. }
  1126. } catch (...) {
  1127. boost::mutex::scoped_lock lock(m_conn->m_mutex);
  1128. m_conn->invariant();
  1129. m_conn->m_priorResponseFailed = (std::min)(m_requestNumber,
  1130. m_conn->m_priorResponseFailed);
  1131. m_responseState = ERROR;
  1132. if (!m_conn->m_pendingRequests.empty() &&
  1133. m_conn->m_pendingRequests.front() == this) {
  1134. if (m_requestState >= COMPLETE)
  1135. m_conn->m_pendingRequests.pop_front();
  1136. m_conn->scheduleAllWaitingRequests();
  1137. m_conn->scheduleAllWaitingResponses();
  1138. }
  1139. if (m_conn->m_priorResponseClosed < m_requestNumber)
  1140. MORDOR_THROW_EXCEPTION(ConnectionVoluntarilyClosedException());
  1141. if (m_conn->m_priorResponseFailed < m_requestNumber)
  1142. MORDOR_THROW_EXCEPTION(PriorRequestFailedException());
  1143. throw;
  1144. }
  1145. } catch (...) {
  1146. m_priorResponseException = boost::current_exception();
  1147. throw;
  1148. }
  1149. }
  1150. void
  1151. ClientRequest::requestMultipartDone()
  1152. {
  1153. MORDOR_ASSERT(m_requestStream);
  1154. m_requestStream->close();
  1155. }
  1156. void
  1157. ClientRequest::requestDone()
  1158. {
  1159. MORDOR_ASSERT(m_requestState == BODY);
  1160. MORDOR_ASSERT(m_requestStream);
  1161. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber << " request complete";
  1162. // Break the circular reference
  1163. NotifyStream::ptr notify =
  1164. boost::dynamic_pointer_cast<NotifyStream>(m_requestStream);
  1165. MORDOR_ASSERT(notify);
  1166. notify->notifyOnClose(NULL);
  1167. notify->notifyOnEof = NULL;
  1168. notify->notifyOnException = NULL;
  1169. if (m_requestStream->supportsSize() && m_requestStream->supportsTell())
  1170. MORDOR_ASSERT(m_requestStream->size() == m_requestStream->tell());
  1171. if (!m_request.general.transferEncoding.empty()) {
  1172. std::ostringstream os;
  1173. os << m_requestTrailer << "\r\n";
  1174. std::string str = os.str();
  1175. MORDOR_LOG_DEBUG(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber << " " << str;
  1176. m_conn->m_stream->write(str.c_str(), str.size());
  1177. }
  1178. m_conn->scheduleNextRequest(this);
  1179. }
  1180. void
  1181. ClientRequest::requestFailed()
  1182. {
  1183. if (m_requestState == ERROR)
  1184. return;
  1185. MORDOR_ASSERT(m_requestState == BODY || m_requestState == HEADERS);
  1186. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber << " request failed";
  1187. if (m_requestStream) {
  1188. // Break the circular reference
  1189. NotifyStream::ptr notify =
  1190. boost::dynamic_pointer_cast<NotifyStream>(m_requestStream);
  1191. MORDOR_ASSERT(notify);
  1192. notify->notifyOnClose(NULL);
  1193. notify->notifyOnEof = NULL;
  1194. notify->notifyOnException = NULL;
  1195. }
  1196. boost::mutex::scoped_lock lock(m_conn->m_mutex);
  1197. m_conn->invariant();
  1198. MORDOR_ASSERT(!m_conn->m_pendingRequests.empty());
  1199. MORDOR_ASSERT(this == *m_conn->m_currentRequest);
  1200. m_conn->m_priorRequestFailed = true;
  1201. if (m_requestState == HEADERS) {
  1202. switch (m_responseState) {
  1203. case PENDING:
  1204. m_responseState = CANCELED;
  1205. m_conn->m_currentRequest =
  1206. m_conn->m_pendingRequests.erase(m_conn->m_currentRequest);
  1207. break;
  1208. case WAITING:
  1209. MORDOR_ASSERT(m_conn->m_waitingResponses.find(this) !=
  1210. m_conn->m_waitingResponses.end());
  1211. m_conn->m_waitingResponses.erase(this);
  1212. m_conn->m_currentRequest =
  1213. m_conn->m_pendingRequests.erase(m_conn->m_currentRequest);
  1214. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-"
  1215. << m_requestNumber << " scheduling response";
  1216. m_scheduler->schedule(m_fiber);
  1217. m_scheduler = NULL;
  1218. m_fiber.reset();
  1219. m_responseState = CANCELED;
  1220. break;
  1221. case HEADERS:
  1222. m_conn->m_stream->cancelRead();
  1223. case BODY:
  1224. ++m_conn->m_currentRequest;
  1225. break;
  1226. default:
  1227. MORDOR_ASSERT(this == m_conn->m_pendingRequests.front());
  1228. ++m_conn->m_currentRequest;
  1229. m_conn->m_pendingRequests.pop_front();
  1230. break;
  1231. }
  1232. } else if (m_responseState >= COMPLETE) {
  1233. MORDOR_ASSERT(this == m_conn->m_pendingRequests.front());
  1234. ++m_conn->m_currentRequest;
  1235. m_conn->m_pendingRequests.pop_front();
  1236. } else {
  1237. ++m_conn->m_currentRequest;
  1238. }
  1239. m_requestState = ERROR;
  1240. m_conn->scheduleAllWaitingRequests();
  1241. // Throw an HTTP exception if we can
  1242. if (m_conn->m_priorResponseClosed <= m_requestNumber)
  1243. MORDOR_THROW_EXCEPTION(ConnectionVoluntarilyClosedException());
  1244. if (m_conn->m_priorResponseFailed < m_requestNumber)
  1245. MORDOR_THROW_EXCEPTION(PriorRequestFailedException());
  1246. }
  1247. void
  1248. ClientRequest::responseDone()
  1249. {
  1250. MORDOR_ASSERT(m_responseState == BODY);
  1251. MORDOR_LOG_TRACE(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber << " response complete";
  1252. // Keep an extra ref to ourself around so we don't destruct if the only ref
  1253. // is in the response stream
  1254. ClientRequest::ptr self;
  1255. try {
  1256. self = shared_from_this();
  1257. } catch (boost::bad_weak_ptr &) {
  1258. // means we're in the destructor
  1259. }
  1260. Stream::ptr stream = m_responseStream.lock();
  1261. MORDOR_ASSERT(stream);
  1262. NotifyStream::ptr notify =
  1263. boost::dynamic_pointer_cast<NotifyStream>(stream);
  1264. MORDOR_ASSERT(notify);
  1265. notify->notifyOnClose(NULL);
  1266. notify->notifyOnEof = NULL;
  1267. notify->notifyOnException = NULL;
  1268. // Make sure every stream in the stack gets a proper EOF
  1269. FilterStream::ptr filter =
  1270. boost::dynamic_pointer_cast<FilterStream>(notify->parent());
  1271. Stream::ptr chunked, limited;
  1272. while (filter && !chunked && !limited) {
  1273. chunked = boost::dynamic_pointer_cast<ChunkedStream>(filter);
  1274. limited = boost::dynamic_pointer_cast<LimitedStream>(filter);
  1275. //redmine issue #86223
  1276. try {
  1277. transferStream(filter, NullStream::get());
  1278. }
  1279. catch( ... ) {
  1280. MORDOR_LOG_TRACE(g_log) << "Ignoring exception";
  1281. }
  1282. filter = boost::dynamic_pointer_cast<FilterStream>(filter->parent());
  1283. }
  1284. if (!m_response.general.transferEncoding.empty()) {
  1285. // Read and parse the trailer
  1286. TrailerParser parser(m_responseTrailer);
  1287. parser.run(m_conn->m_stream);
  1288. if (parser.error()) {
  1289. cancel(true);
  1290. m_badTrailer = true;
  1291. return;
  1292. }
  1293. if (!parser.complete()) {
  1294. cancel(true);
  1295. m_incompleteTrailer = true;
  1296. return;
  1297. }
  1298. MORDOR_LOG_DEBUG(g_log) << m_conn->m_connectionNumber << "-" << m_requestNumber << " " << m_responseTrailer;
  1299. }
  1300. m_conn->scheduleNextResponse(this);
  1301. }
  1302. }}