netwerk/protocol/http/nsHttpChunkedDecoder.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/protocol/http/nsHttpChunkedDecoder.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,163 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +// HttpLog.h should generally be included first
    1.10 +#include "HttpLog.h"
    1.11 +#include <errno.h>
    1.12 +#include "nsHttpChunkedDecoder.h"
    1.13 +#include <algorithm>
    1.14 +
    1.15 +namespace mozilla {
    1.16 +namespace net {
    1.17 +
    1.18 +//-----------------------------------------------------------------------------
    1.19 +// nsHttpChunkedDecoder <public>
    1.20 +//-----------------------------------------------------------------------------
    1.21 +
    1.22 +nsresult
    1.23 +nsHttpChunkedDecoder::HandleChunkedContent(char *buf,
    1.24 +                                           uint32_t count,
    1.25 +                                           uint32_t *contentRead,
    1.26 +                                           uint32_t *contentRemaining)
    1.27 +{
    1.28 +    LOG(("nsHttpChunkedDecoder::HandleChunkedContent [count=%u]\n", count));
    1.29 +
    1.30 +    *contentRead = 0;
    1.31 +
    1.32 +    // from RFC2617 section 3.6.1, the chunked transfer coding is defined as:
    1.33 +    //
    1.34 +    //   Chunked-Body    = *chunk
    1.35 +    //                     last-chunk
    1.36 +    //                     trailer
    1.37 +    //                     CRLF
    1.38 +    //   chunk           = chunk-size [ chunk-extension ] CRLF
    1.39 +    //                     chunk-data CRLF
    1.40 +    //   chunk-size      = 1*HEX
    1.41 +    //   last-chunk      = 1*("0") [ chunk-extension ] CRLF
    1.42 +    //
    1.43 +    //   chunk-extension = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
    1.44 +    //   chunk-ext-name  = token
    1.45 +    //   chunk-ext-val   = token | quoted-string
    1.46 +    //   chunk-data      = chunk-size(OCTET)
    1.47 +    //   trailer         = *(entity-header CRLF)
    1.48 +    //
    1.49 +    // the chunk-size field is a string of hex digits indicating the size of the
    1.50 +    // chunk.  the chunked encoding is ended by any chunk whose size is zero,
    1.51 +    // followed by the trailer, which is terminated by an empty line.
    1.52 +
    1.53 +    while (count) {
    1.54 +        if (mChunkRemaining) {
    1.55 +            uint32_t amt = std::min(mChunkRemaining, count);
    1.56 +
    1.57 +            count -= amt;
    1.58 +            mChunkRemaining -= amt;
    1.59 +
    1.60 +            *contentRead += amt;
    1.61 +            buf += amt;
    1.62 +        }
    1.63 +        else if (mReachedEOF)
    1.64 +            break; // done
    1.65 +        else {
    1.66 +            uint32_t bytesConsumed = 0;
    1.67 +
    1.68 +            nsresult rv = ParseChunkRemaining(buf, count, &bytesConsumed);
    1.69 +            if (NS_FAILED(rv)) return rv;
    1.70 +
    1.71 +            count -= bytesConsumed;
    1.72 +
    1.73 +            if (count) {
    1.74 +                // shift buf by bytesConsumed
    1.75 +                memmove(buf, buf + bytesConsumed, count);
    1.76 +            }
    1.77 +        }
    1.78 +    }
    1.79 +
    1.80 +    *contentRemaining = count;
    1.81 +    return NS_OK;
    1.82 +}
    1.83 +
    1.84 +//-----------------------------------------------------------------------------
    1.85 +// nsHttpChunkedDecoder <private>
    1.86 +//-----------------------------------------------------------------------------
    1.87 +
    1.88 +nsresult
    1.89 +nsHttpChunkedDecoder::ParseChunkRemaining(char *buf,
    1.90 +                                          uint32_t count,
    1.91 +                                          uint32_t *bytesConsumed)
    1.92 +{
    1.93 +    NS_PRECONDITION(mChunkRemaining == 0, "chunk remaining should be zero");
    1.94 +    NS_PRECONDITION(count, "unexpected");
    1.95 +
    1.96 +    *bytesConsumed = 0;
    1.97 +
    1.98 +    char *p = static_cast<char *>(memchr(buf, '\n', count));
    1.99 +    if (p) {
   1.100 +        *p = 0;
   1.101 +        if ((p > buf) && (*(p-1) == '\r')) // eliminate a preceding CR
   1.102 +            *(p-1) = 0;
   1.103 +        *bytesConsumed = p - buf + 1;
   1.104 +
   1.105 +        // make buf point to the full line buffer to parse
   1.106 +        if (!mLineBuf.IsEmpty()) {
   1.107 +            mLineBuf.Append(buf);
   1.108 +            buf = (char *) mLineBuf.get();
   1.109 +        }
   1.110 +
   1.111 +        if (mWaitEOF) {
   1.112 +            if (*buf) {
   1.113 +                LOG(("got trailer: %s\n", buf));
   1.114 +                // allocate a header array for the trailers on demand
   1.115 +                if (!mTrailers) {
   1.116 +                    mTrailers = new nsHttpHeaderArray();
   1.117 +                }
   1.118 +                mTrailers->ParseHeaderLine(buf);
   1.119 +            }
   1.120 +            else {
   1.121 +                mWaitEOF = false;
   1.122 +                mReachedEOF = true;
   1.123 +                LOG(("reached end of chunked-body\n"));
   1.124 +            }
   1.125 +        }
   1.126 +        else if (*buf) {
   1.127 +            char *endptr;
   1.128 +            unsigned long parsedval; // could be 64 bit, could be 32
   1.129 +
   1.130 +            // ignore any chunk-extensions
   1.131 +            if ((p = PL_strchr(buf, ';')) != nullptr)
   1.132 +                *p = 0;
   1.133 +
   1.134 +            // mChunkRemaining is an uint32_t!
   1.135 +            parsedval = strtoul(buf, &endptr, 16);
   1.136 +            mChunkRemaining = (uint32_t) parsedval;
   1.137 +
   1.138 +            if ((endptr == buf) ||
   1.139 +                ((errno == ERANGE) && (parsedval == ULONG_MAX))  ||
   1.140 +                (parsedval != mChunkRemaining) ) {
   1.141 +                LOG(("failed parsing hex on string [%s]\n", buf));
   1.142 +                return NS_ERROR_UNEXPECTED;
   1.143 +            }
   1.144 +
   1.145 +            // we've discovered the last chunk
   1.146 +            if (mChunkRemaining == 0)
   1.147 +                mWaitEOF = true;
   1.148 +        }
   1.149 +
   1.150 +        // ensure that the line buffer is clear
   1.151 +        mLineBuf.Truncate();
   1.152 +    }
   1.153 +    else {
   1.154 +        // save the partial line; wait for more data
   1.155 +        *bytesConsumed = count;
   1.156 +        // ignore a trailing CR
   1.157 +        if (buf[count-1] == '\r')
   1.158 +            count--;
   1.159 +        mLineBuf.Append(buf, count);
   1.160 +    }
   1.161 +
   1.162 +    return NS_OK;
   1.163 +}
   1.164 +
   1.165 +} // namespace mozilla::net
   1.166 +} // namespace mozilla

mercurial