File: SubprotocolUtil.cs

package info (click to toggle)
mono 6.8.0.105%2Bdfsg-3.3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,284,512 kB
  • sloc: cs: 11,172,132; xml: 2,850,069; ansic: 671,653; cpp: 122,091; perl: 59,366; javascript: 30,841; asm: 22,168; makefile: 20,093; sh: 15,020; python: 4,827; pascal: 925; sql: 859; sed: 16; php: 1
file content (133 lines) | stat: -rw-r--r-- 5,474 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//------------------------------------------------------------------------------
// <copyright file="SubProtocolUtil.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

namespace System.Web.WebSockets {
    using System;
    using System.Collections.Generic;
    using System.Linq;

    // Utility class for creating and parsing "Sec-WebSocket-Protocol" headers
    //
    // From the WebSocket protocol spec, sec. 4.1:
    // 10.  The request MAY include a header field with the name "Sec-
    //      WebSocket-Protocol".  If present, this value indicates one or
    //      more comma separated subprotocol the client wishes to speak,
    //      ordered by preference.  The elements that comprise this value
    //      MUST be non-empty strings with characters in the range U+0021 to
    //      U+007E not including separator characters as defined in
    //      [RFC2616], and MUST all be unique strings.  The ABNF for the
    //      value of this header field is 1#token, where the definitions of
    //      constructs and rules are as given in [RFC2616].
    //
    // RFC 2616, sec. 2.1:
    // #rule
    //    A construct "#" is defined, similar to "*", for defining lists of
    //    elements. The full form is "<n>#<m>element" indicating at least
    //    <n> and at most <m> elements, each separated by one or more commas
    //    (",") and OPTIONAL linear white space (LWS). This makes the usual
    //    form of lists very easy; a rule such as
    //       ( *LWS element *( *LWS "," *LWS element ))
    //    can be shown as
    //       1#element
    //    Wherever this construct is used, null elements are allowed, but do
    //    not contribute to the count of elements present. That is,
    //    "(element), , (element) " is permitted, but counts as only two
    //    elements. Therefore, where at least one element is required, at
    //    least one non-null element MUST be present. Default values are 0
    //    and infinity so that "#element" allows any number, including zero;
    //    "1#element" requires at least one; and "1#2element" allows one or
    //    two.

    internal static class SubProtocolUtil {

        // RFC 2616, sec. 2.2:
        // LWS            = [CRLF] 1*( SP | HT )
        // We use a subset: _lwsTrimChars = SP | HT
        private static readonly char[] _lwsTrimChars = new char[] { ' ', '\t' };
        private static readonly char[] _splitChars = new char[] { ',' };

        // Returns a value stating whether the specified SubProtocol is valid
        public static bool IsValidSubProtocolName(string subprotocol) {
            return (!String.IsNullOrEmpty(subprotocol) && subprotocol.All(IsValidSubProtocolChar));
        }

        private static bool IsValidSubProtocolChar(char c) {
            return ('\u0021' <= c && c <= '\u007e' && !IsSeparatorChar(c));
        }

        // RFC 2616, sec. 2.2:
        // separators     = "(" | ")" | "<" | ">" | "@"
        //                | "," | ";" | ":" | "\" | <">
        //                | "/" | "[" | "]" | "?" | "="
        //                | "{" | "}" | SP | HT
        private static bool IsSeparatorChar(char c) {
            switch (c) {
                case '(':
                case ')':
                case '<':
                case '>':
                case '@':
                case ',':
                case ';':
                case ':':
                case '\\':
                case '"':
                case '/':
                case '[':
                case ']':
                case '?':
                case '=':
                case '{':
                case '}':
                case ' ':
                case '\t':
                    return true;

                default:
                    return false;
            }
        }

        // Returns a list of preferred subprotocols by parsing an incoming header value, or null if the incoming header was improperly formatted.
        public static List<string> ParseHeader(string headerValue) {
            if (headerValue == null) {
                // No incoming values
                return null;
            }

            List<string> subprotocols = new List<string>();
            foreach (string subprotocolCandidate in headerValue.Split(_splitChars)) {
                string subprotocolCandidateTrimmed = subprotocolCandidate.Trim(_lwsTrimChars); // remove LWS according to '#' rule

                // skip LWS between commas according to '#' rule
                if (subprotocolCandidateTrimmed.Length == 0) {
                    continue;
                }

                // reject improperly formatted header values
                if (!IsValidSubProtocolName(subprotocolCandidateTrimmed)) {
                    return null;
                }

                // otherwise this subprotocol is OK
                subprotocols.Add(subprotocolCandidateTrimmed);
            }

            if (subprotocols.Count == 0) {
                // header is improperly formatted (contained no usable values)
                return null;
            }

            if (subprotocols.Distinct(StringComparer.Ordinal).Count() != subprotocols.Count) {
                // header is improperly formatted (contained duplicate values)
                return null;
            }

            return subprotocols;
        }

    }
}