PageRenderTime 379ms CodeModel.GetById 80ms app.highlight 242ms RepoModel.GetById 40ms app.codeStats 1ms

/mordor/http/server.cpp

http://github.com/mozy/mordor
C++ | 1290 lines | 1127 code | 71 blank | 92 comment | 367 complexity | b3895f7c835b636b5460d48104ced277 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1// Copyright (c) 2009 - Mozy, Inc.
   2
   3#include "server.h"
   4
   5#include <boost/bind.hpp>
   6
   7#include "mordor/fiber.h"
   8#include "mordor/scheduler.h"
   9#include "mordor/socket.h"
  10#include "mordor/streams/null.h"
  11#include "mordor/streams/transfer.h"
  12#include "mordor/timer.h"
  13#include "multipart.h"
  14#include "parser.h"
  15
  16namespace Mordor {
  17namespace HTTP {
  18
  19static Logger::ptr g_log = Log::lookup("mordor:http:server");
  20
  21ServerConnection::ServerConnection(Stream::ptr stream, boost::function<void (ServerRequest::ptr)> dg, const std::string &id)
  22: Connection(stream),
  23  m_dg(dg),
  24  m_requestCount(0),
  25  m_priorRequestFailed(~0ull),
  26  m_priorRequestClosed(~0ull),
  27  m_priorResponseClosed(~0ull),
  28  m_clientClosed(false)
  29{
  30    MORDOR_ASSERT(m_dg);
  31    std::ostringstream os;
  32    os << id << std::hex << (unsigned long long) this;
  33    m_context = os.str();
  34}
  35
  36void
  37ServerConnection::onClientConnectionClose(weak_ptr self)
  38{
  39    ServerConnection::ptr strongSelf = self.lock();
  40    if (!strongSelf) {
  41        return;
  42    }
  43    MORDOR_LOG_TRACE(g_log) << m_context << " remote closed";
  44    m_clientClosed = true;
  45}
  46
  47void
  48ServerConnection::processRequests()
  49{
  50    m_stream->onRemoteClose(boost::bind(&ServerConnection::onClientConnectionClose, this, weak_ptr(shared_from_this())));
  51    boost::recursive_mutex::scoped_lock lock(m_mutex);
  52    invariant();
  53    scheduleNextRequest(NULL);
  54}
  55
  56std::vector<ServerRequest::const_ptr>
  57ServerConnection::requests()
  58{
  59    std::vector<ServerRequest::const_ptr> result;
  60    boost::recursive_mutex::scoped_lock lock(m_mutex);
  61    invariant();
  62    for (std::list<ServerRequest *>::const_iterator it(m_pendingRequests.begin());
  63        it != m_pendingRequests.end();
  64        ++it) {
  65        result.push_back((*it)->shared_from_this());
  66    }
  67    return result;
  68}
  69
  70void
  71ServerConnection::scheduleNextRequest(ServerRequest *request)
  72{
  73    // MORDOR_ASSERT(m_mutex.locked());
  74    MORDOR_ASSERT(request || m_requestCount == 0);
  75    if (m_requestCount == 0 ||
  76        (request && request->m_requestNumber == m_requestCount &&
  77        request->m_requestState == ServerRequest::COMPLETE &&
  78        m_priorRequestFailed == ~0ull && m_priorRequestClosed == ~0ull &&
  79        m_priorResponseClosed == ~0ull)) {
  80        ServerRequest::ptr nextRequest(new ServerRequest(shared_from_this()));
  81        m_pendingRequests.push_back(nextRequest.get());
  82        MORDOR_LOG_TRACE(g_log) << nextRequest->context() << " scheduling request";
  83        Scheduler::getThis()->schedule(boost::bind(&ServerRequest::doRequest,
  84            nextRequest));
  85    }
  86}
  87
  88void
  89ServerConnection::requestComplete(ServerRequest *request)
  90{
  91    MORDOR_ASSERT(request);
  92    bool close = false;
  93    {
  94        boost::recursive_mutex::scoped_lock lock(m_mutex);
  95        invariant();
  96        MORDOR_ASSERT(!m_pendingRequests.empty());
  97        MORDOR_ASSERT(request == m_pendingRequests.back());
  98        MORDOR_ASSERT(request->m_requestState == ServerRequest::HEADERS ||
  99            request->m_requestState == ServerRequest::BODY);
 100        MORDOR_LOG_TRACE(g_log) << request->context() << " request complete";
 101        request->m_requestState = ServerRequest::COMPLETE;
 102        close = request->m_willClose;
 103        if (request->m_responseState >= ServerRequest::COMPLETE) {
 104            MORDOR_ASSERT(request == m_pendingRequests.front());
 105            m_pendingRequests.pop_front();
 106            if (!close)
 107                scheduleNextRequest(request);
 108        }
 109        if (!close) {
 110            if (request->m_pipeline)
 111                scheduleNextRequest(request);
 112        } else {
 113            m_priorRequestClosed = request->m_requestNumber;
 114            MORDOR_LOG_TRACE(g_log) << m_context << " closing";
 115        }
 116    }
 117    if (close && m_stream->supportsHalfClose())
 118        m_stream->close(Stream::READ);
 119}
 120
 121void
 122ServerConnection::responseComplete(ServerRequest *request)
 123{
 124    if (request->m_willClose) {
 125        MORDOR_LOG_TRACE(g_log) << m_context << " closing";
 126        try {
 127            m_stream->close();
 128        } catch (...) {
 129            MORDOR_LOG_DEBUG(g_log) << request->context()
 130                << " Unexpected exception: "
 131                << boost::current_exception_diagnostic_information();
 132            if (m_clientClosed) {
 133                MORDOR_LOG_DEBUG(g_log) << request->context()
 134                    << " exception ignored: client already closed";
 135            } else {
 136                request->cancel();
 137                throw;
 138            }
 139        }
 140    } else {
 141        MORDOR_LOG_TRACE(g_log) << m_context << " flushing";
 142        m_stream->flush();
 143    }
 144    {
 145        boost::recursive_mutex::scoped_lock lock(m_mutex);
 146        invariant();
 147        MORDOR_ASSERT(request->m_responseState == ServerRequest::HEADERS ||
 148            ServerRequest::BODY);
 149        MORDOR_ASSERT(!m_pendingRequests.empty());
 150        MORDOR_LOG_TRACE(g_log) << request->context() << " response complete";
 151        std::list<ServerRequest *>::iterator it = m_pendingRequests.begin();
 152        MORDOR_ASSERT(request == *it);
 153        ++it;
 154        if (it != m_pendingRequests.end()) {
 155            std::set<ServerRequest *>::iterator waitIt(m_waitingResponses.find(*it));
 156            if (waitIt != m_waitingResponses.end()) {
 157                request->m_responseState = ServerRequest::COMPLETE;
 158                if (request->m_requestState >= ServerRequest::COMPLETE) {
 159                    m_pendingRequests.pop_front();
 160                    scheduleNextRequest(request);
 161                }
 162                request = *it;
 163                request->m_responseState = ServerRequest::HEADERS;
 164                m_waitingResponses.erase(waitIt);
 165                MORDOR_LOG_TRACE(g_log) << request->context() << " scheduling response";
 166                request->m_scheduler->schedule(request->m_fiber);
 167                return;
 168            }
 169        } else {
 170            if (request->m_requestState >= ServerRequest::COMPLETE)
 171                scheduleNextRequest(request);
 172        }
 173        if (request->m_willClose) {
 174            m_priorResponseClosed = request->m_requestNumber;
 175        }
 176    }
 177
 178    boost::recursive_mutex::scoped_lock lock(m_mutex);
 179    invariant();
 180    MORDOR_ASSERT(!m_pendingRequests.empty());
 181    MORDOR_ASSERT(request == m_pendingRequests.front());
 182    request->m_responseState = ServerRequest::COMPLETE;
 183    if (request->m_requestState >= ServerRequest::COMPLETE)
 184        m_pendingRequests.pop_front();
 185
 186    // Someone else may have queued up while we were flushing
 187    if (!m_pendingRequests.empty()) {
 188        request = m_pendingRequests.front();
 189        std::set<ServerRequest *>::iterator waitIt(m_waitingResponses.find(request));
 190        if (waitIt != m_waitingResponses.end()) {
 191            m_waitingResponses.erase(waitIt);
 192            request->m_responseState = ServerRequest::HEADERS;
 193            MORDOR_LOG_TRACE(g_log) << request->context() << " scheduling response";
 194            request->m_scheduler->schedule(request->m_fiber);
 195            return;
 196        }
 197    }
 198}
 199
 200void
 201ServerConnection::scheduleAllWaitingResponses()
 202{
 203    MORDOR_ASSERT(m_priorRequestFailed != ~0ull || m_priorResponseClosed != ~0ull);
 204    // MORDOR_ASSERT(m_mutex.locked());
 205    MORDOR_LOG_TRACE(g_log) << m_context << " scheduling all responses";
 206
 207    unsigned long long firstFailedRequest = (std::min)(m_priorRequestFailed,
 208        m_priorResponseClosed);
 209    for (std::list<ServerRequest *>::iterator it(m_pendingRequests.begin());
 210        it != m_pendingRequests.end();
 211        ++it) {
 212        ServerRequest *request = *it;
 213        if (request->m_requestNumber < firstFailedRequest)
 214            continue;
 215        std::set<ServerRequest *>::iterator waiting = m_waitingResponses.find(request);
 216        if (waiting != m_waitingResponses.end()) {
 217            MORDOR_LOG_TRACE(g_log) << request->context() << " scheduling response";
 218            request->m_scheduler->schedule(request->m_fiber);
 219            it = m_pendingRequests.erase(it);
 220            --it;
 221            m_waitingResponses.erase(waiting);
 222        }
 223    }
 224}
 225
 226void
 227ServerConnection::invariant() const
 228{
 229    // MORDOR_ASSERT(m_mutex.locked());
 230    bool seenResponseNotDone = false;
 231    for (std::list<ServerRequest *>::const_iterator it(m_pendingRequests.begin());
 232        it != m_pendingRequests.end();
 233        ++it) {
 234        ServerRequest *request = *it;
 235        MORDOR_ASSERT(request->m_requestState < ServerRequest::COMPLETE ||
 236            request->m_responseState < ServerRequest::COMPLETE);
 237        if (seenResponseNotDone) {
 238            MORDOR_ASSERT(request->m_responseState < ServerRequest::COMPLETE);
 239        } else {
 240            seenResponseNotDone = request->m_responseState
 241                < ServerRequest::COMPLETE;
 242        }
 243        if (request->m_requestState < ServerRequest::COMPLETE) {
 244            ++it;
 245            MORDOR_ASSERT(it == m_pendingRequests.end());
 246            break;
 247        }
 248    }
 249    for (std::set<ServerRequest *>::const_iterator it(m_waitingResponses.begin());
 250        it != m_waitingResponses.end();
 251        ++it) {
 252        MORDOR_ASSERT((*it)->m_responseState == ServerRequest::WAITING);
 253    }
 254}
 255
 256void
 257ServerConnection::cancel()
 258{
 259    boost::recursive_mutex::scoped_lock lock(m_mutex);
 260    MORDOR_LOG_VERBOSE(g_log) << m_context << " server connection cancelled";
 261    m_stream->cancelRead();
 262    m_stream->cancelWrite();
 263}
 264
 265
 266ServerRequest::ServerRequest(ServerConnection::ptr conn)
 267: m_conn(conn),
 268  m_requestNumber(++conn->m_requestCount),
 269  m_scheduler(NULL),
 270  m_requestState(HEADERS),
 271  m_responseState(PENDING),
 272  m_willClose(false),
 273  m_pipeline(false),
 274  m_startTime(0)
 275{
 276    std::ostringstream os;
 277    os << m_conn->context() << "-" << m_requestNumber;
 278    m_context = os.str();
 279}
 280
 281ServerRequest::~ServerRequest()
 282{
 283    MORDOR_LOG_TRACE(g_log) << this << " " << context() << " server request destroyed";
 284    cancel();
 285}
 286
 287bool
 288ServerRequest::hasRequestBody() const
 289{
 290    if (m_requestStream)
 291        return true;
 292    return Connection::hasMessageBody(m_request.general,
 293        m_request.entity,
 294        m_request.requestLine.method,
 295        INVALID);
 296}
 297
 298Stream::ptr
 299ServerRequest::requestStream()
 300{
 301    MORDOR_ASSERT(!m_requestMultipart);
 302    MORDOR_ASSERT(m_request.entity.contentType.type != "multipart");
 303    if (m_requestStream)
 304        return m_requestStream;
 305    return m_requestStream = m_conn->getStream(m_request.general, m_request.entity,
 306        m_request.requestLine.method, INVALID,
 307        boost::bind(&ServerRequest::requestDone, this),
 308        boost::bind(&ServerRequest::cancel, this), true);
 309}
 310
 311Multipart::ptr
 312ServerRequest::requestMultipart()
 313{
 314    if (m_requestMultipart)
 315        return m_requestMultipart;
 316    MORDOR_ASSERT(m_request.entity.contentType.type == "multipart");
 317    MORDOR_ASSERT(!m_requestStream);
 318    StringMap::const_iterator it = m_request.entity.contentType.parameters.find("boundary");
 319    if (it == m_request.entity.contentType.parameters.end() || it->second.empty()) {
 320        throw std::runtime_error("No boundary with multipart");
 321    }
 322    m_requestStream = m_conn->getStream(m_request.general, m_request.entity,
 323        m_request.requestLine.method, INVALID,
 324        NULL,
 325        boost::bind(&ServerRequest::cancel, this), true);
 326    m_requestMultipart.reset(new Multipart(m_requestStream, it->second));
 327    m_requestMultipart->multipartFinished = boost::bind(&ServerRequest::requestDone, this);
 328    return m_requestMultipart;
 329}
 330
 331const EntityHeaders &
 332ServerRequest::requestTrailer() const
 333{
 334    // If transferEncoding is not empty, it must include chunked,
 335    // and it must include chunked in order to have a trailer
 336    MORDOR_ASSERT(!m_request.general.transferEncoding.empty());
 337    MORDOR_ASSERT(m_requestState == COMPLETE);
 338    return m_requestTrailer;
 339}
 340
 341bool
 342ServerRequest::hasResponseBody() const
 343{
 344    if (m_responseStream)
 345        return true;
 346    return Connection::hasMessageBody(m_response.general,
 347        m_response.entity,
 348        m_request.requestLine.method,
 349        m_response.status.status,
 350        false);
 351}
 352
 353Stream::ptr
 354ServerRequest::responseStream()
 355{
 356    MORDOR_ASSERT(!m_responseMultipart);
 357    MORDOR_ASSERT(m_response.entity.contentType.type != "multipart");
 358    if (m_responseStream)
 359        return m_responseStream;
 360    commit();
 361    return m_responseStream = m_conn->getStream(m_response.general, m_response.entity,
 362        m_request.requestLine.method, m_response.status.status,
 363        boost::bind(&ServerRequest::responseDone, this),
 364        boost::bind(&ServerRequest::cancel, this), false);
 365}
 366
 367Multipart::ptr
 368ServerRequest::responseMultipart()
 369{
 370    if (m_responseMultipart)
 371        return m_responseMultipart;
 372    MORDOR_ASSERT(m_response.entity.contentType.type == "multipart");
 373    MORDOR_ASSERT(!m_responseStream);
 374    StringMap::const_iterator it = m_response.entity.contentType.parameters.find("boundary");
 375    if (it == m_response.entity.contentType.parameters.end()) {
 376        throw std::runtime_error("No boundary with multipart");
 377    }
 378    commit();
 379    m_responseStream = m_conn->getStream(m_response.general, m_response.entity,
 380        m_request.requestLine.method, m_response.status.status,
 381        boost::bind(&ServerRequest::responseDone, this),
 382        boost::bind(&ServerRequest::cancel, this), false);
 383    m_responseMultipart.reset(new Multipart(m_responseStream, it->second));
 384    m_responseMultipart->multipartFinished = boost::bind(&ServerRequest::responseMultipartDone, this);
 385    return m_responseMultipart;
 386}
 387
 388EntityHeaders &
 389ServerRequest::responseTrailer()
 390{
 391    MORDOR_ASSERT(!m_response.general.transferEncoding.empty());
 392    return m_responseTrailer;
 393}
 394
 395void
 396ServerRequest::processNextRequest()
 397{
 398    boost::recursive_mutex::scoped_lock lock(m_conn->m_mutex);
 399    m_pipeline = true;
 400    m_conn->invariant();
 401    m_conn->scheduleNextRequest(this);
 402}
 403
 404void
 405ServerRequest::cancel()
 406{
 407    if (m_requestState >= COMPLETE && m_responseState >= COMPLETE)
 408        return;
 409    boost::recursive_mutex::scoped_lock lock(m_conn->m_mutex);
 410    MORDOR_LOG_INFO(g_log) << m_context
 411        << " aborting with requestState: " << m_requestState
 412        << " responseState: " << m_responseState
 413        << " priorRequestFailed: " << m_conn->m_priorRequestFailed;
 414    m_conn->invariant();
 415    if (m_requestState < COMPLETE)
 416        m_requestState = ERROR;
 417    if (m_responseState < COMPLETE)
 418        m_responseState = ERROR;
 419    m_conn->m_stream->cancelRead();
 420    m_conn->m_stream->cancelWrite();
 421    m_conn->m_priorRequestFailed = (std::min)(m_conn->m_priorRequestFailed,
 422        m_requestNumber);
 423    std::list<ServerRequest *>::iterator it =
 424        std::find(m_conn->m_pendingRequests.begin(),
 425            m_conn->m_pendingRequests.end(), this);
 426    if (it != m_conn->m_pendingRequests.end())
 427        m_conn->m_pendingRequests.erase(it);
 428    m_conn->scheduleAllWaitingResponses();
 429}
 430
 431void
 432ServerRequest::readFinish()
 433{
 434    boost::recursive_mutex::scoped_lock lock(m_conn->m_mutex);
 435    MORDOR_LOG_INFO(g_log) << m_context << " server request read finish";
 436    m_conn->invariant();
 437    m_requestState = COMPLETE;
 438    m_responseState = COMPLETE;
 439    m_conn->m_stream->cancelRead();
 440    std::list<ServerRequest *>::iterator it =
 441        std::find(m_conn->m_pendingRequests.begin(),
 442            m_conn->m_pendingRequests.end(), this);
 443    if (it != m_conn->m_pendingRequests.end())
 444        m_conn->m_pendingRequests.erase(it);
 445}
 446
 447void
 448ServerRequest::finish()
 449{
 450    if (m_responseState < COMPLETE) {
 451        if (hasResponseBody())
 452            MORDOR_LOG_WARNING(g_log) << m_context
 453                << " incomplete response";
 454        if (committed() && hasResponseBody()) {
 455            MORDOR_LOG_INFO(g_log) << m_context
 456                << " committed and hasResponseBody, cancel...";
 457            cancel();
 458            return;
 459        }
 460        commit();
 461        if (hasResponseBody()) {
 462            MORDOR_LOG_INFO(g_log) << m_context
 463                << " commit and hasResponseBody, cancel...";
 464            cancel();
 465            return;
 466        }
 467    }
 468    discardRequestBody();
 469}
 470
 471void
 472ServerRequest::doRequest()
 473{
 474    MORDOR_ASSERT(m_requestState == HEADERS);
 475
 476    try {
 477        // Read and parse headers
 478        RequestParser parser(m_request);
 479        try {
 480            unsigned long long consumed = parser.run(m_conn->m_stream);
 481            m_startTime = TimerManager::now();
 482            if (consumed == 0 && !parser.error() && !parser.complete()) {
 483                // EOF
 484                MORDOR_LOG_TRACE(g_log) << m_conn->context() << " No more request";
 485                readFinish();
 486                return;
 487            }
 488            if (parser.error() || !parser.complete()) {
 489                MORDOR_LOG_WARNING(g_log) << m_context
 490                    << " parser error: " << parser.error()
 491                    << " parser complete: " << parser.complete();
 492                m_requestState = ERROR;
 493                m_conn->m_priorRequestClosed = m_requestNumber;
 494                m_request.requestLine.method = "INVALID";
 495                respondError(shared_from_this(), BAD_REQUEST, "Unable to parse request.", true);
 496                return;
 497            }
 498        } catch (SocketException &) {
 499            MORDOR_LOG_WARNING(g_log) << m_context
 500                << " Unexpected SocketException";
 501            cancel();
 502            return;
 503        } catch (BrokenPipeException &) {
 504            MORDOR_LOG_WARNING(g_log) << m_context
 505                << " Unexpected BrokenPipeException";
 506            cancel();
 507            return;
 508        } catch (UnexpectedEofException &) {
 509            MORDOR_LOG_WARNING(g_log) << m_context
 510                << " Unexpected BrokenPipeException";
 511            cancel();
 512            return;
 513        }
 514        if (g_log->enabled(Log::DEBUG)) {
 515            std::string webAuth, proxyAuth;
 516            bool hideAuth = false, hideProxyAuth = false;
 517            if (stricmp(m_request.request.authorization.scheme.c_str(), "Basic") == 0) {
 518                webAuth = m_request.request.authorization.param;
 519                m_request.request.authorization.param = "<hidden>";
 520                hideAuth = true;
 521            }
 522            if (stricmp(m_request.request.proxyAuthorization.scheme.c_str(), "Basic") == 0) {
 523                proxyAuth = m_request.request.proxyAuthorization.param;
 524                m_request.request.proxyAuthorization.param = "<hidden>";
 525                hideProxyAuth = true;
 526            }
 527            MORDOR_LOG_DEBUG(g_log) << m_context << " " << m_request;
 528            if (hideAuth)
 529                m_request.request.authorization.param = webAuth;
 530            if (hideProxyAuth)
 531                m_request.request.proxyAuthorization.param = proxyAuth;
 532        } else {
 533            MORDOR_LOG_VERBOSE(g_log) << m_context
 534                << " " << m_request.requestLine;
 535        }
 536
 537        if (m_request.requestLine.ver.major != 1) {
 538            m_requestState = ERROR;
 539            respondError(shared_from_this(), HTTP_VERSION_NOT_SUPPORTED, "", true);
 540            return;
 541        }
 542        StringSet &connection = m_request.general.connection;
 543        if (m_request.requestLine.ver == Version(1, 0) &&
 544            connection.find("Keep-Alive") == connection.end())
 545            m_willClose = true;
 546        if (connection.find("close") != connection.end())
 547            m_willClose = true;
 548
 549        // Host header required with HTTP/1.1
 550        if (m_request.requestLine.ver >= Version(1, 1) && m_request.request.host.empty()) {
 551            m_requestState = ERROR;
 552            respondError(shared_from_this(), BAD_REQUEST, "Host header is required with HTTP/1.1", true);
 553            return;
 554        }
 555
 556        ParameterizedList &transferEncoding = m_request.general.transferEncoding;
 557        // Remove identity from the Transfer-Encodings
 558        for (ParameterizedList::iterator it(transferEncoding.begin());
 559            it != transferEncoding.end();
 560            ++it) {
 561            if (stricmp(it->value.c_str(), "identity") == 0) {
 562                it = transferEncoding.erase(it);
 563                --it;
 564            }
 565        }
 566        if (!transferEncoding.empty()) {
 567            if (stricmp(transferEncoding.back().value.c_str(), "chunked") != 0) {
 568                m_requestState = ERROR;
 569                respondError(shared_from_this(), BAD_REQUEST, "The last transfer-coding is not chunked.", true);
 570                return;
 571            }
 572            if (!transferEncoding.back().parameters.empty()) {
 573                m_requestState = ERROR;
 574                respondError(shared_from_this(), NOT_IMPLEMENTED, "Unknown parameter to chunked transfer-coding.", true);
 575                return;
 576            }
 577            for (ParameterizedList::const_iterator it(transferEncoding.begin());
 578                it + 1 != transferEncoding.end();
 579                ++it) {
 580                if (stricmp(it->value.c_str(), "chunked") == 0) {
 581                    m_requestState = ERROR;
 582                    respondError(shared_from_this(), BAD_REQUEST, "chunked transfer-coding applied multiple times.", true);
 583                    return;
 584                } else if (stricmp(it->value.c_str(), "deflate") == 0 ||
 585                    stricmp(it->value.c_str(), "gzip") == 0 ||
 586                    stricmp(it->value.c_str(), "x-gzip") == 0) {
 587                    // Supported transfer-codings
 588                } else if (stricmp(it->value.c_str(), "compress") == 0 ||
 589                    stricmp(it->value.c_str(), "x-compress") == 0) {
 590                    m_requestState = ERROR;
 591                    respondError(shared_from_this(), NOT_IMPLEMENTED,
 592                        "compress transfer-coding is not supported");
 593                    return;
 594                } else {
 595                    m_requestState = ERROR;
 596                    respondError(shared_from_this(), NOT_IMPLEMENTED,
 597                        "Unrecognized transfer-coding: " + it->value);
 598                    return;
 599                }
 600            }
 601        }
 602
 603        // Check expectations
 604        const ParameterizedKeyValueList &expect = m_request.request.expect;
 605        for (ParameterizedKeyValueList::const_iterator it(expect.begin());
 606            it != expect.end();
 607            ++it) {
 608            if (stricmp(it->key.c_str(), "100-continue") == 0) {
 609                if (!it->value.empty() || !it->parameters.empty()) {
 610                    m_requestState = ERROR;
 611                    respondError(shared_from_this(), EXPECTATION_FAILED,
 612                        "Unrecognized parameters to 100-continue expectation");
 613                    return;
 614                }
 615                // http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.2.3
 616                // A client MUST NOT send an Expect request-header field (section
 617                // 14.20) with the "100-continue" expectation if it does not intend
 618                // to send a request body.
 619                if (!Connection::hasMessageBody(m_request.general, m_request.entity,
 620                    m_request.requestLine.method, INVALID, false)) {
 621                    m_requestState = ERROR;
 622                    respondError(shared_from_this(), BAD_REQUEST,
 623                        "Cannot use 100-continue expectation without a request body");
 624                    return;
 625                }
 626            } else {
 627                m_requestState = ERROR;
 628                respondError(shared_from_this(), EXPECTATION_FAILED,
 629                    "Unrecognized expectation: " + it->key);
 630                return;
 631            }
 632        }
 633
 634        // TE is a connection-specific header
 635        if (!m_request.request.te.empty())
 636            m_request.general.connection.insert("TE");
 637
 638        if (!Connection::hasMessageBody(m_request.general, m_request.entity,
 639            m_request.requestLine.method, INVALID)) {
 640            MORDOR_LOG_TRACE(g_log) << m_context << " no request body";
 641            m_conn->requestComplete(this);
 642        } else {
 643            m_requestState = BODY;
 644        }
 645        m_conn->m_dg(shared_from_this());
 646        finish();
 647    } catch (OperationAbortedException &) {
 648        // Do nothing (this occurs when a pipelined request fails because a
 649        // prior request closed the connection)
 650    } catch (PriorRequestFailedException &) {
 651        MORDOR_LOG_ERROR(g_log) << m_context << " Prior request failed since: "
 652            << m_conn << "-" << m_conn->m_priorRequestFailed;
 653    } catch (Assertion &) {
 654         if (m_requestState == ERROR || m_responseState == ERROR)
 655            throw;
 656        MORDOR_LOG_ERROR(g_log) << m_context
 657            << " Unexpected exception: "
 658            << boost::current_exception_diagnostic_information();
 659        if (m_responseState < COMPLETE) {
 660            try {
 661                if (!committed())
 662                    respondError(shared_from_this(), INTERNAL_SERVER_ERROR);
 663                finish();
 664                // Somebody got our 500; they should report the error, so drop
 665                // the assertion
 666                return;
 667            } catch(...) {
 668                // Swallow any exceptions that happen while trying to report
 669                // the error - re-throw the assertion instead
 670            }
 671        }
 672        throw;
 673    } catch (...) {
 674        if (m_requestState == ERROR || m_responseState == ERROR)
 675            return;
 676        MORDOR_LOG_ERROR(g_log) << m_context
 677            << " Unexpected exception: "
 678            << boost::current_exception_diagnostic_information();
 679        if (m_responseState < COMPLETE) {
 680            try {
 681                if (!committed())
 682                    respondError(shared_from_this(), INTERNAL_SERVER_ERROR);
 683                finish();
 684            } catch(...) {
 685                // Swallow any exceptions that happen while trying to report the error
 686            }
 687        }
 688    }
 689}
 690
 691
 692void
 693ServerRequest::commit()
 694{
 695    if (m_responseState != PENDING)
 696        return;
 697
 698    if (m_response.general.connection.find("close") != m_response.general.connection.end())
 699        m_willClose = true;
 700
 701    if (m_response.status.ver == Version()) {
 702        m_response.status.ver = m_request.requestLine.ver;
 703        // only support HTTP 1.0 and 1.1, default 1.1
 704        if (m_response.status.ver != Version(1, 0))
 705            m_response.status.ver = Version(1, 1);
 706    }
 707    MORDOR_ASSERT(m_response.status.ver == Version(1, 0) ||
 708           m_response.status.ver == Version(1, 1));
 709
 710    // Use chunked encoding for undelimited bodies on 1.1, or force the
 711    // connection to close on 1.0
 712    if (m_response.entity.contentLength == ~0ull &&
 713        m_response.general.transferEncoding.empty() &&
 714        m_response.entity.contentType.type != "multipart") {
 715        if (m_response.status.ver == Version(1, 1) && isAcceptable(m_request.request.te,
 716            AcceptValueWithParameters("chunked"), true))
 717            m_response.general.transferEncoding.push_back("chunked");
 718        else
 719            m_willClose = true;
 720    }
 721
 722    if (m_willClose)
 723        m_response.general.connection.insert("close");
 724    else if (m_response.status.ver == Version(1, 0))
 725        m_response.general.connection.insert("Keep-Alive");
 726
 727    if (m_response.status.reason.empty())
 728        m_response.status.reason = reason(m_response.status.status);
 729
 730    const ParameterizedList &transferEncoding = m_request.general.transferEncoding;
 731    // Transfer encodings only allowed on HTTP/1.1+
 732    MORDOR_ASSERT(m_response.status.ver >= Version(1, 1) || transferEncoding.empty());
 733
 734    // If any transfer encodings, must include chunked, must have chunked only once, and must be the last one
 735    if (!transferEncoding.empty()) {
 736        MORDOR_ASSERT(m_response.status.ver == Version(1, 1));
 737        MORDOR_ASSERT(stricmp(transferEncoding.back().value.c_str(), "chunked") == 0);
 738        for (ParameterizedList::const_iterator it(transferEncoding.begin());
 739            it + 1 != transferEncoding.end();
 740            ++it) {
 741            // Only the last one can be chunked
 742            MORDOR_ASSERT(stricmp(it->value.c_str(), "chunked") != 0);
 743            // identity is only acceptable in the TE header field
 744            MORDOR_ASSERT(it->value != "identity");
 745            if (it->value == "gzip" ||
 746                it->value == "x-gzip" ||
 747                it->value == "deflate") {
 748                // Known Transfer-Codings
 749            } else if (it->value == "compress" ||
 750                it->value == "x-compress") {
 751                // Unsupported Transfer-Codings
 752                MORDOR_ASSERT(false);
 753            } else {
 754                // Unknown Transfer-Coding
 755                MORDOR_ASSERT(false);
 756            }
 757        }
 758    }
 759    if (m_response.status.status == UNAUTHORIZED) {
 760        MORDOR_ASSERT(!m_response.response.wwwAuthenticate.empty());
 761    } else if (m_response.status.status == PROXY_AUTHENTICATION_REQUIRED) {
 762        MORDOR_ASSERT(!m_response.response.proxyAuthenticate.empty());
 763    }
 764
 765    MORDOR_ASSERT(m_response.status.status != INVALID);
 766
 767    bool wait = false;
 768    {
 769        boost::recursive_mutex::scoped_lock lock(m_conn->m_mutex);
 770        m_conn->invariant();
 771        if (m_conn->m_priorRequestFailed < m_requestNumber ||
 772            m_conn->m_priorResponseClosed < m_requestNumber) {
 773            std::list<ServerRequest *>::iterator it;
 774            it = std::find(m_conn->m_pendingRequests.begin(), m_conn->m_pendingRequests.end(),
 775                this);
 776            MORDOR_ASSERT(it != m_conn->m_pendingRequests.end());
 777            m_conn->m_pendingRequests.erase(it);
 778            if (m_conn->m_priorRequestFailed < m_requestNumber)
 779                MORDOR_THROW_EXCEPTION(PriorRequestFailedException());
 780            else
 781                MORDOR_THROW_EXCEPTION(ConnectionVoluntarilyClosedException());
 782        }
 783        MORDOR_ASSERT(!m_conn->m_pendingRequests.empty());
 784        ServerRequest *request = m_conn->m_pendingRequests.front();
 785        if (request != this) {
 786            m_responseState = WAITING;
 787            MORDOR_VERIFY(m_conn->m_waitingResponses.insert(this).second);
 788            m_scheduler = Scheduler::getThis();
 789            m_fiber = Fiber::getThis();
 790            wait = true;
 791            MORDOR_LOG_TRACE(g_log) << m_context << " waiting to respond";
 792        } else {
 793            m_responseState = HEADERS;
 794            MORDOR_LOG_TRACE(g_log) << m_context << " responding";
 795            if (m_willClose) {
 796                m_conn->m_priorResponseClosed = m_requestNumber;
 797                m_conn->scheduleAllWaitingResponses();
 798            }
 799        }
 800    }
 801    // If we weren't the first response in the queue, wait for someone
 802    // else to schedule us
 803    if (wait) {
 804        Scheduler::yieldTo();
 805        m_scheduler = NULL;
 806        m_fiber.reset();
 807        MORDOR_LOG_TRACE(g_log) << m_context << " responding";
 808        // Check for problems that occurred while we were waiting
 809        boost::recursive_mutex::scoped_lock lock(m_conn->m_mutex);
 810        m_conn->invariant();
 811        if (m_conn->m_priorRequestFailed <= m_requestNumber)
 812            MORDOR_THROW_EXCEPTION(PriorRequestFailedException());
 813        else if (m_conn->m_priorResponseClosed <= m_requestNumber)
 814            MORDOR_THROW_EXCEPTION(ConnectionVoluntarilyClosedException());
 815        MORDOR_ASSERT(!m_conn->m_pendingRequests.empty());
 816        MORDOR_ASSERT(m_conn->m_pendingRequests.front() == this);
 817        if (m_willClose) {
 818            m_conn->m_priorResponseClosed = m_requestNumber;
 819            m_conn->scheduleAllWaitingResponses();
 820        }
 821        m_responseState = HEADERS;
 822    }
 823
 824    try {
 825        // Write the headers
 826        std::ostringstream os;
 827        os << m_response;
 828        std::string str = os.str();
 829        if (g_log->enabled(Log::DEBUG)) {
 830            MORDOR_LOG_DEBUG(g_log) << m_context << " " << str;
 831        } else {
 832            MORDOR_LOG_VERBOSE(g_log) << m_context << " " << m_response.status;
 833        }
 834        m_conn->m_stream->write(str.c_str(), str.size());
 835
 836        if (!Connection::hasMessageBody(m_response.general, m_response.entity,
 837            m_request.requestLine.method, m_response.status.status, false)) {
 838            MORDOR_LOG_TRACE(g_log) << m_context << " no response body";
 839            responseDone();
 840        } else {
 841            m_responseState = BODY;
 842        }
 843    } catch(...) {
 844        boost::recursive_mutex::scoped_lock lock(m_conn->m_mutex);
 845        m_conn->invariant();
 846        m_conn->m_priorRequestFailed = (std::min)(m_conn->m_priorRequestFailed,
 847            m_requestNumber);
 848        m_conn->scheduleAllWaitingResponses();
 849        throw;
 850    }
 851}
 852
 853
 854void
 855ServerRequest::requestDone()
 856{
 857    MORDOR_LOG_TRACE(g_log) << m_context << " request complete";
 858    m_requestStream.reset();
 859    if (!m_request.general.transferEncoding.empty()) {
 860        // Read and parse the trailer
 861        TrailerParser parser(m_requestTrailer);
 862        parser.run(m_conn->m_stream);
 863        if (parser.error()) {
 864            cancel();
 865            throw std::runtime_error("Error parsing trailer");
 866        }
 867        // it is possible that the TrailerParser is not error but not complete
 868        // if there is nothing to read from the stream
 869        // according to https://tools.ietf.org/html/rfc7230#section-4.1.2
 870        // chunked trailer is optional
 871        if (parser.complete()) {
 872            MORDOR_LOG_DEBUG(g_log) << m_context << " " << m_requestTrailer;
 873        } else if (!m_request.general.trailer.empty()) {
 874            MORDOR_LOG_WARNING(g_log) << m_context
 875                << " request has non empty trailer header but no trailer part";
 876        }
 877    }
 878    m_conn->requestComplete(this);
 879}
 880
 881void
 882ServerRequest::responseMultipartDone()
 883{
 884    MORDOR_ASSERT(m_responseStream);
 885    m_responseStream->close();
 886}
 887
 888void
 889ServerRequest::responseDone()
 890{
 891    MORDOR_LOG_TRACE(g_log) << m_context << " response complete";
 892    if (m_responseStream && m_responseStream->supportsSize() && m_responseStream->supportsTell())
 893        MORDOR_ASSERT(m_responseStream->size() == m_responseStream->tell());
 894    m_responseStream.reset();
 895    if (!m_response.general.transferEncoding.empty() &&
 896        m_request.requestLine.method != HEAD) {
 897        std::ostringstream os;
 898        os << m_responseTrailer << "\r\n";
 899        std::string str = os.str();
 900        MORDOR_LOG_DEBUG(g_log) << m_context << " " << str;
 901        m_conn->m_stream->write(str.c_str(), str.size());
 902    }
 903    MORDOR_LOG_INFO(g_log) << m_context << " "
 904        << m_request.requestLine << " " << m_response.status.status;
 905    m_conn->responseComplete(this);
 906}
 907
 908void
 909ServerRequest::discardRequestBody()
 910{
 911    if (m_requestState == BODY && !m_willClose) {
 912        if (m_request.entity.contentType.type == "multipart") {
 913            if (!m_requestMultipart)
 914                m_requestMultipart = requestMultipart();
 915            while(m_requestMultipart->nextPart());
 916        } else {
 917            if (!m_requestStream)
 918                m_requestStream = requestStream();
 919            MORDOR_ASSERT(m_requestStream);
 920            transferStream(m_requestStream, NullStream::get());
 921        }
 922    }
 923}
 924
 925void
 926respondError(ServerRequest::ptr request, Status status,
 927    const std::string &message, bool closeConnection, bool clearETag)
 928{
 929    MORDOR_ASSERT(!request->committed());
 930    request->response().status.status = status;
 931    if (closeConnection)
 932        request->response().general.connection.insert("close");
 933    request->response().general.transferEncoding.clear();
 934    request->response().general.trailer.clear();
 935    request->response().entity.contentLength = message.size();
 936    request->response().entity.contentType.type.clear();
 937    if (clearETag)
 938        request->response().response.eTag = ETag();
 939    if (!message.empty()) {
 940        request->response().entity.contentType.type = "text";
 941        request->response().entity.contentType.subtype = "plain";
 942        if (request->request().requestLine.method == HEAD) {
 943            request->finish();
 944        } else {
 945            Stream::ptr responseStream = request->responseStream();
 946            responseStream->write(message.c_str(), message.size());
 947            responseStream->close();
 948        }
 949    } else {
 950        request->finish();
 951    }
 952}
 953
 954void
 955respondStream(ServerRequest::ptr request, Stream &response)
 956{
 957    MORDOR_ASSERT(!request->committed());
 958    unsigned long long size = ~0ull;
 959    if (response.supportsSize())
 960        size = response.size();
 961    bool trailers = (size == ~0ull &&
 962        isAcceptable(request->request().request.te, "trailers"));
 963    // Only advertise byte range request if it would be possible on the current
 964    // request
 965    if (trailers || size != ~0ull)
 966        request->response().response.acceptRanges.insert("bytes");
 967    ParameterizedList &transferEncoding =
 968        request->response().general.transferEncoding;
 969    transferEncoding.clear();
 970    const RangeSet &range = request->request().request.range;
 971    bool fullEntity = range.empty();
 972    // Validate range request
 973    // If we don't know the size, and we don't support trailers, have to send
 974    // the whole thing
 975    if (size == ~0ull && !trailers)
 976        fullEntity = true;
 977    // If we're using trailers, it only works for a single range
 978    if (trailers && range.size() > 1)
 979        fullEntity = true;
 980    const ETag *ifRange =
 981        boost::get<ETag>(&request->request().request.ifRange);
 982    if (!fullEntity && ifRange && !ifRange->unspecified &&
 983        !ifRange->strongCompare(request->response().response.eTag))
 984        fullEntity = true;
 985    // Timestamps with If-Range is not supported (at this level we can't know
 986    // if it was possible for the entity to have changed multiple times in a
 987    // single second; see
 988    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.3)
 989    const boost::posix_time::ptime *httpDate =
 990        boost::get<boost::posix_time::ptime>(
 991            &request->request().request.ifRange);
 992    if (httpDate && !httpDate->is_not_a_date_time())
 993        fullEntity = true;
 994
 995    // TODO: sort and merge overlapping ranges
 996    unsigned long long previousLast = 0;
 997    for (RangeSet::const_iterator it(range.begin());
 998        it != range.end();
 999        ++it) {
1000        if (fullEntity)
1001            break;
1002        // Invalid; first is after last
1003        if (it->first > it->second && it->first != ~0ull) {
1004            fullEntity = true;
1005            break;
1006        }
1007        // Unsupported - suffix range when we can't determine the size
1008        if (it->first == ~0ull && size == ~0ull) {
1009            fullEntity = true;
1010            break;
1011        }
1012        // First byte is beyond end of stream
1013        if (it->first >= size && size != ~0ull && it->first != ~0ull) {
1014            respondError(request, REQUESTED_RANGE_NOT_SATISFIABLE);
1015            return;
1016        }
1017        // Suffix range is greater than entire stream
1018        if (it->first == ~0ull && it->second >= size) {
1019            fullEntity = true;
1020            break;
1021        }
1022        // Regular range contains entire stream
1023        if (it->first == 0 && it->second >= size - 1) {
1024            fullEntity = true;
1025            break;
1026        }
1027        // Unsupported: un-ordered range and stream doesn't support seeking
1028        if (it != range.begin()) {
1029            if (it->first <= previousLast && !response.supportsSeek()) {
1030                fullEntity = true;
1031                break;
1032            }
1033        }
1034        if (it->first == ~0ull)
1035            previousLast = size - 1;
1036        else
1037            previousLast = it->second;
1038    }
1039    // We can satisfy the range request
1040    if (!fullEntity) {
1041        // Multiple ranges; must use multipart; we don't support chunked
1042        // encoding (does it make sense?), so don't advertise or worry about
1043        // trailers or transfer encodings
1044        if (range.size() > 1) {
1045            MediaType contentType = request->response().entity.contentType;
1046            request->response().entity.contentLength = ~0;
1047            request->response().entity.contentType.type = "multipart";
1048            request->response().entity.contentType.subtype = "byteranges";
1049            request->response().entity.contentType.parameters["boundary"] =
1050                Multipart::randomBoundary();
1051            request->response().status.status = PARTIAL_CONTENT;
1052            unsigned long long currentPos = 0;
1053
1054            if (request->request().requestLine.method != HEAD) {
1055                // force use chunked encoding. Reason:
1056                // - Content-Type: multipart/byteranges itself can't tell
1057                //   when the response body end, and
1058                // - Content-Length is not applicable here as well.
1059                if (transferEncoding.empty())
1060                    transferEncoding.push_back("chunked");
1061                Multipart::ptr multipart = request->responseMultipart();
1062                for (RangeSet::const_iterator it(range.begin());
1063                    it != range.end();
1064                    ++it) {
1065                    BodyPart::ptr part = multipart->nextPart();
1066                    part->headers().contentType = contentType;
1067                    ContentRange &cr = part->headers().contentRange;
1068                    cr.instance = size;
1069                    if (it->first == ~0ull) {
1070                        if (it->second > size)
1071                            cr.first = 0;
1072                        else
1073                            cr.first = size - it->second;
1074                    } else {
1075                        cr.first = it->first;
1076                        cr.last = (std::min)(it->second, size - 1);
1077                    }
1078                    if (response.supportsSeek())
1079                        response.seek(cr.first);
1080                    else
1081                        transferStream(response, NullStream::get(), cr.first - currentPos);
1082                    transferStream(response, part->stream(), cr.last - cr.first + 1);
1083                    part->stream()->close();
1084                    currentPos = cr.last + 1;
1085                }
1086                multipart->finish();
1087            }
1088        } else {
1089            // Single range; we'll support compression (advertise it, and use
1090            // it if requested) and trailers (if necessary)
1091            request->response().status.status = PARTIAL_CONTENT;
1092            if (request->request().requestLine.ver >= Version(1, 1)) {
1093                AcceptListWithParameters available;
1094                available.push_back(AcceptValueWithParameters("deflate", 1000));
1095                available.push_back(AcceptValueWithParameters("gzip", 500));
1096                available.push_back(AcceptValueWithParameters("x-gzip", 500));
1097                const AcceptValueWithParameters *preferredEncoding =
1098                    preferred(request->request().request.te, available);
1099                if (preferredEncoding) {
1100                    transferEncoding.push_back(preferredEncoding->value);
1101                    transferEncoding.push_back("chunked");
1102                }
1103            }
1104            // Set up headers (for trailers, or not for trailers)
1105            ContentRange *cr;
1106            if (trailers) {
1107                request->response().general.trailer.insert("Content-Range");
1108                if (transferEncoding.empty())
1109                    transferEncoding.push_back("chunked");
1110                cr = &request->responseTrailer().contentRange;
1111                cr->first = range.front().first;
1112                cr->last = range.front().second;
1113                MORDOR_ASSERT(cr->first != ~0ull);
1114            } else {
1115                cr = &request->response().entity.contentRange;
1116                cr->instance = size;
1117                if (range.front().first == ~0ull) {
1118                    if (range.front().second > size)
1119                        cr->first = 0;
1120                    else
1121                        cr->first = size - range.front().second;
1122                    cr->last = size - 1;
1123                } else {
1124                    cr->first = range.front().first;
1125                    cr->last = (std::min)(range.front().second, size - 1);
1126                }
1127                request->response().entity.contentLength = cr->last - cr->first + 1;
1128            }
1129
1130            bool isHead = (request->request().requestLine.method == HEAD);
1131
1132            // Seek to the correct position
1133            // Skip the seek if it's a HEAD, and we don't need to check for
1134            // out-of-range because we already know the size
1135            if (!isHead || trailers) {
1136                if (response.supportsSeek()) {
1137                    response.seek(cr->first, Stream::BEGIN);
1138                } else {
1139                    // Can't seek, have to do this the old fashioned way
1140                    unsigned long long transferred = transferStream(
1141                        response, NullStream::get(), cr->first, UNTILEOF);
1142                    // Make sure we're not out-of-range
1143                    if (transferred != cr->first) {
1144                        respondError(request, REQUESTED_RANGE_NOT_SATISFIABLE);
1145                        return;
1146                    }
1147                }
1148            }
1149            if (isHead) {
1150                // On a head, when we don't know the size of the stream, we
1151                // just need to verify that the Range isn't beyond the end of
1152                // the stream (by reading a single byte)
1153                if (trailers) {
1154                    char byte;
1155                    if (response.read(&byte, 1) == 0) {
1156                        respondError(request, REQUESTED_RANGE_NOT_SATISFIABLE);
1157                        return;
1158                    }
1159                }
1160            } else {
1161                unsigned long long transferred = 0;
1162                // There's an edge case where either the stream is seekable,
1163                // but not sizable, or just not sizeable, and so we're pointing
1164                // exactly at eof (or beyond), and don't know it; need to read
1165                // a byte without committing to see if we can actually satisfy
1166                // the request
1167                if (trailers) {
1168                    char byte;
1169                    transferred = response.read(&byte, 1);
1170                    if (transferred == 0) {
1171                        respondError(request, REQUESTED_RANGE_NOT_SATISFIABLE);
1172                        return;
1173                    }
1174                    request->responseStream()->write(&byte, 1);
1175                }
1176                // Actually transfer the requested range
1177                transferred += transferStream(response,
1178                    request->responseStream(),
1179                    cr->last - cr->first + 1 - transferred,
1180                    trailers ? UNTILEOF : EXACT);
1181                if (trailers) {
1182                    // Set how much was actually transferred
1183                    cr->last = cr->first + transferred - 1;
1184                    // And discard the rest of the stream to get the size
1185                    cr->instance = cr->last + transferStream(response,
1186                        NullStream::get()) + 1;
1187                } else if (transferred != cr->last - cr->first + 1) {
1188                    // The stream LIED about its size!
1189                    MORDOR_THROW_EXCEPTION(UnexpectedEofException());
1190                }
1191                request->responseStream()->close();
1192            }
1193        }
1194    } else {
1195        // User only asked for, or it's only possible to send the full entity
1196        request->response().entity.contentLength = size;
1197        if (request->request().requestLine.ver >= Version(1, 1)) {
1198            AcceptListWithParameters available;
1199            available.push_back(AcceptValueWithParameters("deflate", 1000));
1200            available.push_back(AcceptValueWithParameters("gzip", 500));
1201            available.push_back(AcceptValueWithParameters("x-gzip", 500));
1202            const AcceptValueWithParameters *preferredEncoding =
1203                preferred(request->request().request.te, available);
1204            if (preferredEncoding) {
1205                transferEncoding.push_back(preferredEncoding->value);
1206                transferEncoding.push_back("chunked");
1207            }
1208        }
1209        if (request->request().requestLine.method != HEAD) {
1210            if (size != 0u) {
1211                transferStream(response, request->responseStream(), size);
1212                request->responseStream()->close();
1213            }
1214        }
1215    }
1216    if (request->request().requestLine.method == HEAD)
1217        request->finish();
1218}
1219
1220void
1221respondStream(ServerRequest::ptr request, Stream::ptr response)
1222{
1223    respondStream(request, *response);
1224}
1225
1226bool ifMatch(ServerRequest::ptr request, const ETag &eTag)
1227{
1228    const std::set<ETag> &ifMatch = request->request().request.ifMatch;
1229
1230    // Special case *; if it's there, and there's no ETag, fail
1231    if (ifMatch.size() == 1 && ifMatch.begin()->unspecified) {
1232        if (eTag.unspecified) {
1233            respondError(request, PRECONDITION_FAILED);
1234            return false;
1235        }
1236    } else if (!ifMatch.empty()) {
1237        bool matched = false;
1238        for (std::set<ETag>::const_iterator it(ifMatch.begin());
1239            it != ifMatch.end();
1240            ++it) {
1241            if (it->strongCompare(eTag)) {
1242                matched = true;
1243                break;
1244            }
1245        }
1246        if (!matched) {
1247            respondError(request, PRECONDITION_FAILED);
1248            return false;
1249        }
1250    }
1251
1252    const std::string &method = request->request().requestLine.method;
1253    bool getOrHead = (method == GET || method == HEAD);
1254    const std::set<ETag> &ifNoneMatch = request->request().request.ifNoneMatch;
1255    // Special case *; if it's there, and there is an entity, fail
1256    if (ifNoneMatch.size() == 1 && ifNoneMatch.begin()->unspecified) {
1257        if (!eTag.unspecified) {
1258            if (getOrHead)
1259                request->response().response.eTag = eTag;
1260            respondError(request, getOrHead ? NOT_MODIFIED : PRECONDITION_FAILED,
1261                std::string(), false, !getOrHead);
1262            return false;
1263        }
1264    } else {
1265        bool matched = false;
1266        for (std::set<ETag>::const_iterator it(ifNoneMatch.begin());
1267            it != ifNoneMatch.end();
1268            ++it) {
1269            if (getOrHead && it->weakCompare(eTag)) {
1270                matched = true;
1271                break;
1272            } else if (

Large files files are truncated, but you can click here to view the full file