Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/protocol/http/nsHttpChunkedDecoder.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
// HttpLog.h should generally be included first
7
#include "HttpLog.h"
8
9
#include <errno.h>
10
#include "nsHttpChunkedDecoder.h"
11
#include <algorithm>
12
#include "plstr.h"
13
14
#include "mozilla/Unused.h"
15
16
namespace mozilla {
17
namespace net {
18
19
//-----------------------------------------------------------------------------
20
// nsHttpChunkedDecoder <public>
21
//-----------------------------------------------------------------------------
22
23
nsresult
24
nsHttpChunkedDecoder::HandleChunkedContent(char *buf,
25
                                           uint32_t count,
26
                                           uint32_t *contentRead,
27
                                           uint32_t *contentRemaining)
28
0
{
29
0
    LOG(("nsHttpChunkedDecoder::HandleChunkedContent [count=%u]\n", count));
30
0
31
0
    *contentRead = 0;
32
0
33
0
    // from RFC2616 section 3.6.1, the chunked transfer coding is defined as:
34
0
    //
35
0
    //   Chunked-Body    = *chunk
36
0
    //                     last-chunk
37
0
    //                     trailer
38
0
    //                     CRLF
39
0
    //   chunk           = chunk-size [ chunk-extension ] CRLF
40
0
    //                     chunk-data CRLF
41
0
    //   chunk-size      = 1*HEX
42
0
    //   last-chunk      = 1*("0") [ chunk-extension ] CRLF
43
0
    //
44
0
    //   chunk-extension = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
45
0
    //   chunk-ext-name  = token
46
0
    //   chunk-ext-val   = token | quoted-string
47
0
    //   chunk-data      = chunk-size(OCTET)
48
0
    //   trailer         = *(entity-header CRLF)
49
0
    //
50
0
    // the chunk-size field is a string of hex digits indicating the size of the
51
0
    // chunk.  the chunked encoding is ended by any chunk whose size is zero,
52
0
    // followed by the trailer, which is terminated by an empty line.
53
0
54
0
    while (count) {
55
0
        if (mChunkRemaining) {
56
0
            uint32_t amt = std::min(mChunkRemaining, count);
57
0
58
0
            count -= amt;
59
0
            mChunkRemaining -= amt;
60
0
61
0
            *contentRead += amt;
62
0
            buf += amt;
63
0
        }
64
0
        else if (mReachedEOF)
65
0
            break; // done
66
0
        else {
67
0
            uint32_t bytesConsumed = 0;
68
0
69
0
            nsresult rv = ParseChunkRemaining(buf, count, &bytesConsumed);
70
0
            if (NS_FAILED(rv)) return rv;
71
0
72
0
            count -= bytesConsumed;
73
0
74
0
            if (count) {
75
0
                // shift buf by bytesConsumed
76
0
                memmove(buf, buf + bytesConsumed, count);
77
0
            }
78
0
        }
79
0
    }
80
0
81
0
    *contentRemaining = count;
82
0
    return NS_OK;
83
0
}
84
85
//-----------------------------------------------------------------------------
86
// nsHttpChunkedDecoder <private>
87
//-----------------------------------------------------------------------------
88
89
nsresult
90
nsHttpChunkedDecoder::ParseChunkRemaining(char *buf,
91
                                          uint32_t count,
92
                                          uint32_t *bytesConsumed)
93
0
{
94
0
    MOZ_ASSERT(mChunkRemaining == 0, "chunk remaining should be zero");
95
0
    MOZ_ASSERT(count, "unexpected");
96
0
97
0
    *bytesConsumed = 0;
98
0
99
0
    char *p = static_cast<char *>(memchr(buf, '\n', count));
100
0
    if (p) {
101
0
        *p = 0;
102
0
        count = p - buf; // new length
103
0
        *bytesConsumed = count + 1; // length + newline
104
0
        if ((p > buf) && (*(p-1) == '\r')) { // eliminate a preceding CR
105
0
            *(p-1) = 0;
106
0
            count--;
107
0
        }
108
0
109
0
        // make buf point to the full line buffer to parse
110
0
        if (!mLineBuf.IsEmpty()) {
111
0
            mLineBuf.Append(buf, count);
112
0
            buf = (char *) mLineBuf.get();
113
0
            count = mLineBuf.Length();
114
0
        }
115
0
116
0
        if (mWaitEOF) {
117
0
            if (*buf) {
118
0
                LOG(("got trailer: %s\n", buf));
119
0
                // allocate a header array for the trailers on demand
120
0
                if (!mTrailers) {
121
0
                    mTrailers = new nsHttpHeaderArray();
122
0
                }
123
0
124
0
                nsHttpAtom hdr = {nullptr};
125
0
                nsAutoCString headerNameOriginal;
126
0
                nsAutoCString val;
127
0
                if (NS_SUCCEEDED(mTrailers->ParseHeaderLine(nsDependentCSubstring(buf, count),
128
0
                                                            &hdr, &headerNameOriginal, &val))) {
129
0
                    if (hdr == nsHttp::Server_Timing) {
130
0
                        Unused << mTrailers->SetHeaderFromNet(hdr, headerNameOriginal,
131
0
                                                              val, true);
132
0
                    }
133
0
                }
134
0
            }
135
0
            else {
136
0
                mWaitEOF = false;
137
0
                mReachedEOF = true;
138
0
                LOG(("reached end of chunked-body\n"));
139
0
            }
140
0
        }
141
0
        else if (*buf) {
142
0
            char *endptr;
143
0
            unsigned long parsedval; // could be 64 bit, could be 32
144
0
145
0
            // ignore any chunk-extensions
146
0
            if ((p = PL_strchr(buf, ';')) != nullptr)
147
0
                *p = 0;
148
0
149
0
            // mChunkRemaining is an uint32_t!
150
0
            parsedval = strtoul(buf, &endptr, 16);
151
0
            mChunkRemaining = (uint32_t) parsedval;
152
0
153
0
            if ((endptr == buf) ||
154
0
                ((errno == ERANGE) && (parsedval == ULONG_MAX))  ||
155
0
                (parsedval != mChunkRemaining) ) {
156
0
                LOG(("failed parsing hex on string [%s]\n", buf));
157
0
                return NS_ERROR_UNEXPECTED;
158
0
            }
159
0
160
0
            // we've discovered the last chunk
161
0
            if (mChunkRemaining == 0)
162
0
                mWaitEOF = true;
163
0
        }
164
0
165
0
        // ensure that the line buffer is clear
166
0
        mLineBuf.Truncate();
167
0
    }
168
0
    else {
169
0
        // save the partial line; wait for more data
170
0
        *bytesConsumed = count;
171
0
        // ignore a trailing CR
172
0
        if (buf[count-1] == '\r')
173
0
            count--;
174
0
        mLineBuf.Append(buf, count);
175
0
    }
176
0
177
0
    return NS_OK;
178
0
}
179
180
} // namespace net
181
} // namespace mozilla