PageRenderTime 66ms CodeModel.GetById 2ms app.highlight 57ms RepoModel.GetById 1ms app.codeStats 0ms

/mordor/http/http.cpp

http://github.com/mozy/mordor
C++ | 810 lines | 790 code | 10 blank | 10 comment | 18 complexity | 3a0dd796675576cc3f8b692c2f5c391b MD5 | raw file
  1// Copyright (c) 2009 - Mozy, Inc.
  2
  3#include "http.h"
  4
  5#include <boost/bind.hpp>
  6
  7#include <algorithm>
  8#include <iostream>
  9
 10#include "mordor/assert.h"
 11
 12namespace Mordor {
 13namespace HTTP {
 14
 15static boost::posix_time::time_facet rfc1123Facet_out("%a, %d %b %Y %H:%M:%S GMT",
 16        boost::posix_time::time_facet::period_formatter_type(),
 17        boost::posix_time::time_facet::special_values_formatter_type(),
 18        boost::posix_time::time_facet::date_gen_formatter_type(),
 19        1 /* starting refcount, so this never gets deleted */);
 20
 21std::string
 22quote(const std::string &str, bool alwaysQuote, bool comment)
 23{
 24    if (comment)
 25        alwaysQuote = true;
 26    if (str.empty())
 27        return comment ? "()" : "\"\"";
 28
 29    if (!alwaysQuote && str.find_first_not_of("!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~") == std::string::npos) {
 30        return str;
 31    }
 32
 33    std::string result;
 34    result.reserve(str.length() + 2);
 35    result.append(1, comment ? '(' : '"');
 36
 37    // qdtext = OCTET - CTL - '"' - '\\' | LWS
 38    // CR, LF, and HT are part of LWS, and are allowed
 39    // DEL is a CTL, and is not allowed
 40    // \ is the escape character, and must be escaped itself
 41    // " indicates end-of-string, and must be escaped
 42    static const char * escapedQuote =
 43        "\0\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f"
 44        "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
 45        "\x7f\\\"";
 46    static const char * escapedComment =
 47        "\0\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f"
 48        "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
 49        "\x7f\\()";
 50    const char *escaped = comment ? escapedComment : escapedQuote;
 51    size_t escapedCount = comment ? 33 : 32;
 52    size_t leftParens = 0, rightParens = 0;
 53    if (comment) {
 54        rightParens = std::count(str.begin(), str.end(), ')');
 55    }
 56
 57    size_t lastEscape = 0;
 58    size_t nextEscape = str.find_first_of(escaped, 0, escapedCount);
 59    while (nextEscape != std::string::npos) {
 60        if (str[nextEscape] == '(' && rightParens > 0) {
 61            // Let this one through without quoting (there is at least one
 62            // more matching right paren coming up)
 63            ++leftParens;
 64            --rightParens;
 65            result.append(str.substr(lastEscape, nextEscape - lastEscape + 1));
 66        } else if (str[nextEscape] == ')' && leftParens > 0) {
 67            // Let this one through without quoting (it matches a previous
 68            // left paren)
 69            --leftParens;
 70            result.append(str.substr(lastEscape, nextEscape - lastEscape + 1));
 71        } else {
 72            // Unmatching right parens
 73            if (str[nextEscape] == ')') {
 74                MORDOR_ASSERT(rightParens > 0);
 75                --rightParens;
 76            }
 77            result.append(str.substr(lastEscape, nextEscape - lastEscape));
 78            result.append(1, '\\');
 79            result.append(1, str[nextEscape]);
 80        }
 81        lastEscape = nextEscape + 1;
 82        nextEscape = str.find_first_of(escaped, lastEscape, escapedCount);
 83    }
 84    result.append(str.substr(lastEscape));
 85    result.append(1, comment ? ')' : '"');
 86    return result;
 87}
 88
 89static std::ostream& operator<<(std::ostream& os, const StringSet& set)
 90{
 91    for (StringSet::const_iterator it(set.begin());
 92        it != set.end();
 93        ++it) {
 94        if (it != set.begin())
 95            os << ", ";
 96        os << *it;
 97    }
 98    return os;
 99}
100
101static std::ostream& operator<<(std::ostream& os, const std::vector<std::string>& list)
102{
103    for (std::vector<std::string>::const_iterator it(list.begin());
104        it != list.end();
105        ++it) {
106        if (it != list.begin())
107            os << ", ";
108        os << *it;
109    }
110    return os;
111}
112
113static std::ostream& operator<<(std::ostream& os, const RangeSet& set)
114{
115    MORDOR_ASSERT(!set.empty());
116    os << "bytes=";
117    for (RangeSet::const_iterator it(set.begin());
118        it != set.end();
119        ++it) {
120        if (it != set.begin())
121            os << ", ";
122        if (it->first != ~0ull)
123            os << it->first;
124        os << "-";
125        if (it->second != ~0ull)
126            os << it->second;
127    }
128    return os;
129}
130
131struct serializeStringMapWithRequiredValue
132{
133    serializeStringMapWithRequiredValue(const StringMap &m,
134        const char *delim = ";", bool leadingDelimiter = true)
135        : map(m),
136          delimiter(delim),
137          lead(leadingDelimiter)
138    {}
139    const StringMap& map;
140    const char *delimiter;
141    bool lead;
142};
143
144struct serializeStringMapWithOptionalValue
145{
146    serializeStringMapWithOptionalValue(const StringMap &m) : map(m) {}
147    const StringMap& map;
148};
149
150static std::ostream& operator<<(std::ostream& os, const serializeStringMapWithRequiredValue &map)
151{
152    for (StringMap::const_iterator it(map.map.begin());
153        it != map.map.end();
154        ++it) {
155        if (map.lead || it != map.map.begin())
156            os << map.delimiter;
157        os << it->first << "=" << quote(it->second);
158    }
159    return os;
160}
161
162static std::ostream& operator<<(std::ostream& os, const serializeStringMapWithOptionalValue &map)
163{
164    for (StringMap::const_iterator it(map.map.begin());
165        it != map.map.end();
166        ++it) {
167        os << ";" << it->first;
168        if (!it->second.empty())
169            os << "=" << quote(it->second);
170    }
171    return os;
172}
173
174const std::string GET("GET");
175const std::string HEAD("HEAD");
176const std::string POST("POST");
177const std::string PUT("PUT");
178const std::string DELETE("DELETE");
179const std::string CONNECT("CONNECT");
180const std::string OPTIONS("OPTIONS");
181const std::string TRACE("TRACE");
182
183const char *reason(Status s)
184{
185    switch (s) {
186        case CONTINUE:
187            return "Continue";
188        case SWITCHING_PROTOCOL:
189            return "Switching Protocols";
190
191        case OK:
192            return "OK";
193        case CREATED:
194            return "Created";
195        case ACCEPTED:
196            return "Accepted";
197        case NON_AUTHORITATIVE_INFORMATION:
198            return "Non-Authoritative Information";
199        case NO_CONTENT:
200            return "No Content";
201        case RESET_CONTENT:
202            return "Reset Content";
203        case PARTIAL_CONTENT:
204            return "Partial Content";
205
206        case MULTIPLE_CHOICES:
207            return "Multiple Choices";
208        case MOVED_PERMANENTLY:
209            return "Moved Permanently";
210        case FOUND:
211            return "Found";
212        case SEE_OTHER:
213            return "See Other";
214        case NOT_MODIFIED:
215            return "Not Modified";
216        case USE_PROXY:
217            return "Use Proxy";
218        case TEMPORARY_REDIRECT:
219            return "Temporary Redirect";
220
221        case BAD_REQUEST:
222            return "Bad Request";
223        case UNAUTHORIZED:
224            return "Unauthorized";
225        case PAYMENT_REQUIRED:
226            return "Payment Required";
227        case FORBIDDEN:
228            return "Forbidden";
229        case NOT_FOUND:
230            return "Not Found";
231        case METHOD_NOT_ALLOWED:
232            return "Method Not Allowed";
233        case NOT_ACCEPTABLE:
234            return "Not Acceptable";
235        case PROXY_AUTHENTICATION_REQUIRED:
236            return "Proxy Authentication Required";
237        case REQUEST_TIMEOUT:
238            return "Request Time-out";
239        case CONFLICT:
240            return "Conflict";
241        case GONE:
242            return "Gone";
243        case LENGTH_REQUIRED:
244            return "Length Required";
245        case PRECONDITION_FAILED:
246            return "Precondition Failed";
247        case REQUEST_ENTITY_TOO_LARGE:
248            return "Request Entity Too Large";
249        case REQUEST_URI_TOO_LONG:
250            return "Request-URI Too Long";
251        case UNSUPPORTED_MEDIA_TYPE:
252            return "Unsupported Media Type";
253        case REQUESTED_RANGE_NOT_SATISFIABLE:
254            return "Requested range not satisfiable";
255        case EXPECTATION_FAILED:
256            return "Expectation Failed";
257
258        case INTERNAL_SERVER_ERROR:
259            return "Internal Server Error";
260        case NOT_IMPLEMENTED:
261            return "Not Implemented";
262        case BAD_GATEWAY:
263            return "Bad Gateway";
264        case SERVICE_UNAVAILABLE:
265            return "Service Unavailable";
266        case GATEWAY_TIMEOUT:
267            return "Gateway Time-out";
268        case HTTP_VERSION_NOT_SUPPORTED:
269            return "HTTP Version not supported";
270
271        default:
272            return "<INVALID>";
273    }
274}
275
276bool
277AcceptValue::operator ==(const AcceptValue &rhs) const
278{
279    return stricmp(value.c_str(), rhs.value.c_str()) == 0;
280}
281
282bool
283AcceptValueWithParameters::operator ==(const AcceptValueWithParameters &rhs) const
284{
285    return stricmp(value.c_str(), rhs.value.c_str()) == 0 &&
286        parameters == rhs.parameters;
287}
288
289bool
290isAcceptable(const ChallengeList &list, const std::string &scheme)
291{
292    for (ChallengeList::const_iterator it = list.begin();
293        it != list.end();
294        ++it) {
295        if (stricmp(it->scheme.c_str(), scheme.c_str()) == 0)
296            return true;
297    }
298    return false;
299}
300
301const AuthParams &
302challengeForSchemeAndRealm(const ChallengeList &list,
303    const std::string &scheme, const std::string &realm)
304{
305    for (ChallengeList::const_iterator it = list.begin();
306        it != list.end();
307        ++it) {
308        if (stricmp(it->scheme.c_str(), scheme.c_str()) == 0) {
309            if (realm.empty())
310                return *it;
311            StringMap::const_iterator realmIt = it->parameters.find("realm");
312            if (realmIt != it->parameters.end() &&
313                stricmp(realmIt->second.c_str(), realm.c_str()) == 0)
314                return *it;
315        }
316    }
317    MORDOR_NOTREACHED();
318}
319
320template<class T>
321static bool isAcceptable(const std::vector<T> &list, const T &value, bool defaultMissing)
322{
323    for (typename std::vector<T>::const_iterator it(list.begin()); it != list.end(); ++it) {
324        if (*it == value) {
325            return it->qvalue > 0;
326        }
327    }
328    return defaultMissing;
329}
330
331bool
332isAcceptable(const AcceptListWithParameters &list, const AcceptValueWithParameters &value,
333        bool defaultMissing)
334{
335    return isAcceptable<AcceptValueWithParameters>(list, value, defaultMissing);
336}
337
338bool
339isAcceptable(const AcceptList &list, const AcceptValue &value, bool defaultMissing)
340{
341    return isAcceptable<AcceptValue>(list, value, defaultMissing);
342}
343
344template<class T>
345static const T* preferred(const std::vector<T> &accept, const std::vector<T> &available)
346{
347    MORDOR_ASSERT(!available.empty());
348#ifndef NDEBUG
349    // Assert that the available list is ordered
350    for (typename std::vector<T>::const_iterator it(available.begin()); it != available.end(); ++it) {
351        MORDOR_ASSERT(it->qvalue <= 1000);
352        typename std::vector<T>::const_iterator next(it);
353        ++next;
354        if (next != available.end())
355            MORDOR_ASSERT(it->qvalue >= next->qvalue);
356    }
357#endif
358    const T* res = NULL;
359
360    typename std::vector<T>::const_iterator availableIt(available.begin());
361    while (availableIt != available.end()) {
362        // find the highest qvalues from server's perspective
363        typename std::vector<T>::const_iterator nextIt(availableIt);
364        ++nextIt;
365        while (nextIt != available.end() && nextIt->qvalue == availableIt->qvalue)
366            ++nextIt;
367
368        // find the highest qvalues from client's perspective
369        for (; availableIt != nextIt; ++availableIt) {
370            for (typename std::vector<T>::const_iterator it(accept.begin());
371                 it != accept.end(); ++it) {
372                // client wants this, choose the highest one
373                // if there are same qvalues, choose the 1st one we found
374                if (*it == *availableIt && it->qvalue > 0) {
375                    // qvalue takes ~0u by default, which is equivalent to 1000
376                    if (!res || (std::min)(it->qvalue, 1000u) > (std::min)(res->qvalue, 1000u))
377                        res = &*it;
378                    break;
379                }
380            }
381        }
382        if (res) break;
383    }
384    return res;
385}
386
387const AcceptValueWithParameters *
388preferred(const AcceptListWithParameters &accept, const AcceptListWithParameters &available)
389{
390    return preferred<AcceptValueWithParameters>(accept, available);
391}
392
393const AcceptValue *
394preferred(const AcceptList &accept, const AcceptList &available)
395{
396    return preferred<AcceptValue>(accept, available);
397}
398
399std::ostream& operator<<(std::ostream& os, Status s)
400{
401    return os << (int)s;
402}
403
404std::ostream& operator<<(std::ostream& os, Version v)
405{
406    if (v.major == (unsigned char)~0 || v.minor == (unsigned char)~0)
407        return os << "HTTP/0.0";
408    return os << "HTTP/" << (int)v.major << "." << (int)v.minor;
409}
410
411std::ostream& operator<<(std::ostream& os, const ETag &e)
412{
413    if (e.unspecified)
414        return os << "*";
415    if (e.weak)
416        os << "W/";
417    return os << quote(e.value, true);
418}
419
420std::ostream& operator<<(std::ostream& os, const std::set<ETag> &v)
421{
422    MORDOR_ASSERT(!v.empty());
423    for (std::set<ETag>::const_iterator it = v.begin();
424        it != v.end();
425        ++it) {
426        if (it != v.begin())
427            os << ", ";
428        MORDOR_ASSERT(!it->unspecified || v.size() == 1);
429        os << *it;
430    }
431    return os;
432}
433
434std::ostream& operator<<(std::ostream& os, const Product &p)
435{
436    MORDOR_ASSERT(!p.product.empty());
437    os << p.product;
438    if (!p.version.empty())
439        os << "/" << p.version;
440    return os;
441}
442
443std::ostream& operator<<(std::ostream& os, const ProductList &l)
444{
445    MORDOR_ASSERT(!l.empty());
446    for (ProductList::const_iterator it = l.begin();
447        it != l.end();
448        ++it) {
449        if (it != l.begin())
450            os << ", ";
451        os << *it;
452    }
453    return os;
454}
455
456std::ostream& operator<<(std::ostream& os, const ProductAndCommentList &l)
457{
458    MORDOR_ASSERT(!l.empty());
459    for (ProductAndCommentList::const_iterator it = l.begin();
460        it != l.end();
461        ++it) {
462        if (it != l.begin())
463            os << " ";
464        const Product *product = boost::get<Product>(&*it);
465        if (product)
466            os << *product;
467        else
468            os << quote(boost::get<std::string>(*it), true, true);
469    }
470    return os;
471}
472
473std::ostream& operator<<(std::ostream& os, const ValueWithParameters &v)
474{
475    MORDOR_ASSERT(!v.value.empty());
476    return os << v.value << serializeStringMapWithRequiredValue(v.parameters);
477}
478
479std::ostream& operator<<(std::ostream& os, const ParameterizedList &l)
480{
481    for (ParameterizedList::const_iterator it(l.begin());
482        it != l.end();
483        ++it) {
484        if (it != l.begin())
485            os << ", ";
486        os << *it;
487    }
488    return os;
489}
490
491std::ostream &operator<<(std::ostream &os, const AuthParams &a)
492{
493    MORDOR_ASSERT(a.param.empty() || a.parameters.empty());
494    os << a.scheme;
495    if (!a.param.empty())
496        os << ' ' << a.param;
497    if (!a.parameters.empty())
498        os << ' ' << serializeStringMapWithRequiredValue(a.parameters, ", ", false);
499    return os;
500}
501
502std::ostream &operator<<(std::ostream &os, const ChallengeList &l)
503{
504    for (ChallengeList::const_iterator it(l.begin());
505        it != l.end();
506        ++it) {
507        if (it != l.begin())
508            os << ", ";
509        os << *it;
510    }
511    return os;
512}
513
514std::ostream& operator<<(std::ostream& os, const KeyValueWithParameters &v)
515{
516    MORDOR_ASSERT(!v.key.empty());
517    os << quote(v.key);
518    if (!v.value.empty())
519        os << "=" << quote(v.value)
520            << serializeStringMapWithOptionalValue(v.parameters);
521    return os;
522}
523
524std::ostream& operator<<(std::ostream& os, const ParameterizedKeyValueList &l)
525{
526    MORDOR_ASSERT(!l.empty());
527    for (ParameterizedKeyValueList::const_iterator it(l.begin());
528        it != l.end();
529        ++it) {
530        if (it != l.begin())
531            os << ", ";
532        os << *it;
533    }
534    return os;
535}
536
537std::ostream& operator<<(std::ostream& os, const MediaType &m)
538{
539    MORDOR_ASSERT(!m.type.empty());
540    MORDOR_ASSERT(!m.subtype.empty());
541    return os << m.type << "/" << m.subtype << serializeStringMapWithRequiredValue(m.parameters);
542}
543
544std::ostream& operator<<(std::ostream& os, const ContentRange &cr)
545{
546    os << "bytes ";
547    if (cr.first == ~0ull) {
548        os << '*';
549    } else {
550        os << cr.first << '-';
551        if (cr.last != ~0ull)
552            os << cr.last;
553    }
554    os << '/';
555    if (cr.instance == ~0ull)
556        os << '*';
557    else
558        os << cr.instance;
559    return os;
560}
561
562std::ostream& operator<<(std::ostream& os, const AcceptValue &v)
563{
564    if (v.value.empty())
565        os << '*';
566    else
567        os << v.value;
568    if (v.qvalue != ~0u) {
569        MORDOR_ASSERT(v.qvalue <= 1000);
570        unsigned int qvalue = v.qvalue;
571        unsigned int curPlace = 100;
572        if (qvalue == 1000) {
573            os << ";q=1";
574        } else {
575            os << ";q=0";
576            while (curPlace > 0 && qvalue > 0) {
577                if (curPlace == 100)
578                    os << '.';
579                unsigned int cur = qvalue / curPlace;
580                MORDOR_ASSERT(cur < 10);
581                os << cur;
582                qvalue -= cur * curPlace;
583                curPlace /= 10;
584            }
585        }
586    }
587
588    return os;
589}
590
591std::ostream& operator<<(std::ostream& os, const AcceptList &l)
592{
593    MORDOR_ASSERT(!l.empty());
594    for (AcceptList::const_iterator it(l.begin());
595        it != l.end();
596        ++it) {
597        if (it != l.begin())
598            os << ", ";
599        os << *it;
600    }
601    return os;
602}
603
604std::ostream& operator<<(std::ostream& os, const AcceptValueWithParameters &v)
605{
606    MORDOR_ASSERT(!v.value.empty());
607    os << v.value << serializeStringMapWithRequiredValue(v.parameters);
608    if (v.qvalue != ~0u) {
609        MORDOR_ASSERT(v.qvalue <= 1000);
610        unsigned int qvalue = v.qvalue;
611        unsigned int curPlace = 100;
612        if (qvalue == 1000) {
613            os << ";q=1";
614        } else {
615            os << ";q=0";
616            while (curPlace > 0 && qvalue > 0) {
617                if (curPlace == 100)
618                    os << '.';
619                unsigned int cur = qvalue / curPlace;
620                MORDOR_ASSERT(cur < 10);
621                os << cur;
622                qvalue -= cur * curPlace;
623                curPlace /= 10;
624            }
625        }
626        os << serializeStringMapWithOptionalValue(v.acceptParams);
627    } else {
628        MORDOR_ASSERT(v.acceptParams.empty());
629    }
630
631    return os;
632}
633
634std::ostream& operator<<(std::ostream& os, const AcceptListWithParameters &l)
635{
636    MORDOR_ASSERT(!l.empty());
637    for (AcceptListWithParameters::const_iterator it(l.begin());
638        it != l.end();
639        ++it) {
640        if (it != l.begin())
641            os << ", ";
642        os << *it;
643    }
644    return os;
645}
646
647std::ostream& operator<<(std::ostream& os, const RequestLine &r)
648{
649    // CONNECT is special cased to only do the authority
650    if (r.method == CONNECT) {
651        MORDOR_ASSERT(r.uri.authority.hostDefined());
652        MORDOR_ASSERT(!r.uri.schemeDefined());
653        MORDOR_ASSERT(r.uri.path.isEmpty());
654        MORDOR_ASSERT(!r.uri.queryDefined());
655        MORDOR_ASSERT(!r.uri.fragmentDefined());
656        return os << r.method << ' ' << r.uri.authority << ' ' << r.ver;
657    } else {
658        if (!r.uri.isDefined()) {
659            return os << r.method << " * " << r.ver;
660        } else {
661            MORDOR_ASSERT(!r.uri.fragmentDefined());
662#ifndef NDEBUG
663            // Must be a absolute_URI or a path_absolute (with query allowed)
664            if (!r.uri.schemeDefined()) {
665                MORDOR_ASSERT(!r.uri.authority.hostDefined());
666                MORDOR_ASSERT(r.uri.path.isAbsolute());
667                // The first path segment (after the leading slash)
668                // cannot be empty (unless it is the trailing slash
669                // as well)
670                MORDOR_ASSERT(r.uri.path.segments.size() <= 2 ||
671                    !r.uri.path.segments[1].empty())
672            }
673#endif
674            return os << r.method << " " << r.uri << " " << r.ver;
675        }
676    }
677}
678
679std::ostream& operator<<(std::ostream& os, const StatusLine &s)
680{
681    MORDOR_ASSERT(!s.reason.empty());
682    return os << s.ver << " " << s.status << " " << s.reason;
683}
684
685std::ostream& operator<<(std::ostream& os, const GeneralHeaders &g)
686{
687    os.imbue(std::locale(os.getloc(), &rfc1123Facet_out));
688    if (!g.connection.empty())
689        os << "Connection: " << g.connection << "\r\n";
690    if (!g.date.is_not_a_date_time())
691        os << "Date: " << g.date << "\r\n";
692    if (!g.proxyConnection.empty())
693        os << "Proxy-Connection: " << g.proxyConnection << "\r\n";
694    if (!g.trailer.empty())
695        os << "Trailer: " << g.trailer << "\r\n";
696    if (!g.transferEncoding.empty())
697        os << "Transfer-Encoding: " << g.transferEncoding << "\r\n";
698    if (!g.upgrade.empty())
699        os << "Upgrade: " << g.upgrade << "\r\n";
700    return os;
701}
702
703std::ostream& operator<<(std::ostream& os, const RequestHeaders &r)
704{
705    os.imbue(std::locale(os.getloc(), &rfc1123Facet_out));
706    if (!r.acceptCharset.empty())
707        os << "Accept-Charset: " << r.acceptCharset << "\r\n";
708    if (!r.acceptEncoding.empty())
709        os << "Accept-Encoding: " << r.acceptEncoding << "\r\n";
710    if (!r.authorization.scheme.empty())
711        os << "Authorization: " << r.authorization << "\r\n";
712    if (!r.expect.empty())
713        os << "Expect: " << r.expect << "\r\n";
714    if (!r.host.empty())
715        os << "Host: " << r.host << "\r\n";
716    if (!r.ifMatch.empty())
717        os << "If-Match: " << r.ifMatch << "\r\n";
718    if (!r.ifModifiedSince.is_not_a_date_time())
719        os << "If-Modified-Since: " << r.ifModifiedSince << "\r\n";
720    if (!r.ifNoneMatch.empty())
721        os << "If-None-Match: " << r.ifNoneMatch << "\r\n";
722    const ETag *ifRangeEtag = boost::get<ETag>(&r.ifRange);
723    if (ifRangeEtag && !ifRangeEtag->unspecified)
724        os << "If-Range: " << *ifRangeEtag << "\r\n";
725    const boost::posix_time::ptime *ifRangeHttpDate = boost::get<boost::posix_time::ptime>(&r.ifRange);
726    if (ifRangeHttpDate && !ifRangeHttpDate->is_not_a_date_time())
727        os << "If-Range: " << *ifRangeHttpDate << "\r\n";
728    if (!r.ifUnmodifiedSince.is_not_a_date_time())
729        os << "If-Unmodified-Since: " << r.ifUnmodifiedSince << "\r\n";
730    if (!r.proxyAuthorization.scheme.empty())
731        os << "Proxy-Authorization: " << r.proxyAuthorization << "\r\n";
732    if (!r.range.empty())
733        os << "Range: " << r.range << "\r\n";
734    if (r.referer.isDefined())
735        os << "Referer: " << r.referer << "\r\n";
736    if (!r.te.empty())
737        os << "TE: " << r.te << "\r\n";
738    if (!r.userAgent.empty())
739        os << "User-Agent: " << r.userAgent << "\r\n";
740    return os;
741}
742
743std::ostream& operator<<(std::ostream& os, const ResponseHeaders &r)
744{
745    os.imbue(std::locale(os.getloc(), &rfc1123Facet_out));
746    if (!r.acceptRanges.empty())
747        os << "Accept-Ranges: " << r.acceptRanges << "\r\n";
748    if (!r.eTag.unspecified)
749        os << "ETag: " << r.eTag << "\r\n";
750    if (r.location.isDefined())
751        os << "Location: " << r.location << "\r\n";
752    if (!r.proxyAuthenticate.empty())
753        os << "Proxy-Authenticate: " << r.proxyAuthenticate << "\r\n";
754    const boost::posix_time::ptime *retryAfterHttpDate = boost::get<boost::posix_time::ptime>(&r.retryAfter);
755    if (retryAfterHttpDate && !retryAfterHttpDate->is_not_a_date_time())
756        os << "Retry-After: " << *retryAfterHttpDate << "\r\n";
757    const unsigned long long *retryAfterDeltaSeconds = boost::get<unsigned long long>(&r.retryAfter);
758    if (retryAfterDeltaSeconds && *retryAfterDeltaSeconds != ~0ull)
759        os << "Retry-After: " << *retryAfterDeltaSeconds << "\r\n";
760    if (!r.server.empty())
761        os << "Server: " << r.server << "\r\n";
762    if (!r.wwwAuthenticate.empty())
763        os << "WWW-Authenticate: " << r.wwwAuthenticate << "\r\n";
764    return os;
765}
766
767std::ostream& operator<<(std::ostream& os, const EntityHeaders &e)
768{
769    os.imbue(std::locale(os.getloc(), &rfc1123Facet_out));
770    if (!e.allow.empty())
771        os << "Allow: " << e.allow << "\r\n";
772    if (!e.contentEncoding.empty())
773        os << "Content-Encoding: " << e.contentEncoding << "\r\n";
774    if (e.contentLength != ~0ull)
775        os << "Content-Length: " << e.contentLength << "\r\n";
776    if (!e.contentMD5.empty())
777        os << "Content-MD5: " << e.contentMD5 << "\r\n";
778    if (e.contentRange.first != ~0ull || e.contentRange.last != ~0ull || e.contentRange.instance != ~0ull)
779        os << "Content-Range: " << e.contentRange << "\r\n";
780    if (!e.contentType.type.empty() && !e.contentType.subtype.empty())
781        os << "Content-Type: " << e.contentType << "\r\n";
782    if (!e.expires.is_not_a_date_time())
783        os << "Expires: " << e.expires << "\r\n";
784    if (!e.lastModified.is_not_a_date_time())
785        os << "Last-Modified: " << e.lastModified << "\r\n";
786    for (StringMap::const_iterator it(e.extension.begin());
787        it != e.extension.end();
788        ++it) {
789        os << it->first << ": " << it->second << "\r\n";
790    }
791    return os;
792}
793
794std::ostream& operator<<(std::ostream& os, const Request &r)
795{
796    return os << r.requestLine << "\r\n"
797        << r.general
798        << r.request
799        << r.entity << "\r\n";
800}
801
802std::ostream& operator<<(std::ostream& os, const Response &r)
803{
804    return os << r.status << "\r\n"
805        << r.general
806        << r.response
807        << r.entity << "\r\n";
808}
809
810}}