1/*
2 * Copyright (C) 2016 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "ParsedContentRange.h"
28
29#include <wtf/StdLibExtras.h>
30#include <wtf/text/StringConcatenateNumbers.h>
31
32namespace WebCore {
33
34static bool areContentRangeValuesValid(int64_t firstBytePosition, int64_t lastBytePosition, int64_t instanceLength)
35{
36 // From <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html>
37 // 14.16 Content-Range
38 // A byte-content-range-spec with a byte-range-resp-spec whose last- byte-pos value is less than its first-byte-pos value,
39 // or whose instance-length value is less than or equal to its last-byte-pos value, is invalid.
40 if (firstBytePosition < 0)
41 return false;
42
43 if (lastBytePosition < firstBytePosition)
44 return false;
45
46 if (instanceLength == ParsedContentRange::UnknownLength)
47 return true;
48
49 return lastBytePosition < instanceLength;
50}
51
52static bool parseContentRange(const String& headerValue, int64_t& firstBytePosition, int64_t& lastBytePosition, int64_t& instanceLength)
53{
54 // From <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html>
55 // 14.16 Content-Range
56 //
57 // Content-Range = "Content-Range" ":" content-range-spec
58 // content-range-spec = byte-content-range-spec
59 // byte-content-range-spec = bytes-unit SP
60 // byte-range-resp-spec "/"
61 // ( instance-length | "*" )
62 // byte-range-resp-spec = (first-byte-pos "-" last-byte-pos)
63 // | "*"
64 // instance-length = 1*DIGIT
65
66 static const char* prefix = "bytes ";
67 static const size_t prefixLength = 6;
68
69 if (!headerValue.startsWith(prefix))
70 return false;
71
72 size_t byteSeparatorTokenLoc = headerValue.find('-', prefixLength);
73 if (byteSeparatorTokenLoc == notFound)
74 return false;
75
76 size_t instanceLengthSeparatorToken = headerValue.find('/', byteSeparatorTokenLoc + 1);
77 if (instanceLengthSeparatorToken == notFound)
78 return false;
79
80 bool isOk = true;
81 String firstByteString = headerValue.substring(prefixLength, byteSeparatorTokenLoc - prefixLength);
82 if (!firstByteString.isAllSpecialCharacters<isASCIIDigit>())
83 return false;
84
85 firstBytePosition = firstByteString.toInt64Strict(&isOk);
86 if (!isOk)
87 return false;
88
89 String lastByteString = headerValue.substring(byteSeparatorTokenLoc + 1, instanceLengthSeparatorToken - (byteSeparatorTokenLoc + 1));
90 if (!lastByteString.isAllSpecialCharacters<isASCIIDigit>())
91 return false;
92
93 lastBytePosition = lastByteString.toInt64Strict(&isOk);
94 if (!isOk)
95 return false;
96
97 String instanceString = headerValue.substring(instanceLengthSeparatorToken + 1);
98 if (instanceString == "*")
99 instanceLength = ParsedContentRange::UnknownLength;
100 else {
101 if (!instanceString.isAllSpecialCharacters<isASCIIDigit>())
102 return false;
103
104 instanceLength = instanceString.toInt64Strict(&isOk);
105 if (!isOk)
106 return false;
107 }
108
109 return areContentRangeValuesValid(firstBytePosition, lastBytePosition, instanceLength);
110}
111
112ParsedContentRange::ParsedContentRange(const String& headerValue)
113{
114 m_isValid = parseContentRange(headerValue, m_firstBytePosition, m_lastBytePosition, m_instanceLength);
115}
116
117ParsedContentRange::ParsedContentRange(int64_t firstBytePosition, int64_t lastBytePosition, int64_t instanceLength)
118 : m_firstBytePosition(firstBytePosition)
119 , m_lastBytePosition(lastBytePosition)
120 , m_instanceLength(instanceLength)
121{
122 m_isValid = areContentRangeValuesValid(m_firstBytePosition, m_lastBytePosition, m_instanceLength);
123}
124
125String ParsedContentRange::headerValue() const
126{
127 if (!m_isValid)
128 return String();
129 if (m_instanceLength == UnknownLength)
130 return makeString("bytes ", m_firstBytePosition, '-', m_lastBytePosition, "/*");
131 return makeString("bytes ", m_firstBytePosition, '-', m_lastBytePosition, '/', m_instanceLength);
132}
133
134}
135