www.gusucode.com > external 工具箱matlab源码程序 > external/interfaces/webservices/http/+matlab/+net/+http/+field/SetCookieField.m

    classdef (Sealed) SetCookieField < matlab.net.http.HeaderField
    %SetCookieField a Set-Cookie HTTP header field
    %   This field appears in a ResponseMessage created by the server.  There may be
    %   one or more SetCookieFields in a message.  The value in each field may be
    %   extracted as a CookieInfo object using the convert method.
    %
    %   Example of obtaining CookieInfos from all Set-Cookie fields in a message:
    %
    %     uri = matlab.net.URI('http://www.mathworks.com');
    %     opts = matlab.net.http.HTTPOptions('MaxRedirects',1);
    %     resp = matlab.net.http.RequestMessage().send(uri);
    %     setCookieFields = resp.getFields('Set-Cookie');
    %     if ~isempty(setCookieFields)
    %        cookieInfos = setCookieFields.convert(uri);
    %     end   
    %
    %   SetCookieField methods:
    %     convert   - return CookieInfo object
    %
    % See also matlab.net.http.Cookie, matlab.net.http.CookieInfo,
    % matlab.net.http.ResponseMessage, datetime, convert
    
    % Copyright 2015-2016 The MathWorks, Inc.
    methods (Static)
        function names = getSupportedNames
            names = string('Set-Cookie');
        end
    end
    
    methods
        function obj = SetCookieField(value)
        % SetCookieField creates an HTTP Set-Cookie header field
        %   This constructor is provided for test purposes only.  You normally obtain
        %   a SetCookieField from a ResponseMessage created by the server, so you
        %   would not create one of these for use in a message. Use the CookieField
        %   constructor to insert a Cookie field in a RequestMessage.
        %
        %   This constructor accepts either a string or a CookieInfo object.  It does
        %   not validate that the string has valid syntax for a Set-Cookie field.
        %
        % See also matlab.net.http.ResponseMessage, CookieField,
        % matlab.net.http.RequestMessage, matlab.net.http.CookieInfo
            if nargin == 0
                value = [];
            end
            obj = obj@matlab.net.http.HeaderField('Set-Cookie', value);
        end
        
        function value = convert(obj, uri)
        % convert returns CookieInfo objects for a vector of SetCookieFields
        %   VALUE = convert(FIELDS, URI) where FIELDS is a SetCookieField vector and
        %   URI is an optional URI.
        %
        %   A SetCookieField contains information about just one cookie.  If the
        %   server sends multiple cookies, there are multiple SetCookieFields.  This
        %   function returns a vector of CookieInfo objects corresponding to the
        %   SetCookieFields in vector FIELDS.  
        %
        %   The URI is that of the request, used to compare against or initialize the
        %   Domain property of the CookieInfos.  If a SetCookieField contains no
        %   Domain attribute, the Domain property of its CookieInfo is set to
        %   URI.Host.  If there is a Domain attribute, its value must "domain-match"
        %   the URI.Host or no CookieInfo is returned for that SetCookieField.  If it
        %   matches, the Domain property is set to the Domain attribute.  For more
        %   information on domain matching, see RFC 6265 <a href="http://tools.ietf.org/html/rfc6265#section-5.1.3">section 5.1.3</a>
        %   and <a href="http://tools.ietf.org/html/rfc6265#section-5.3">section 5.3</a>, step 6.
        %
        %   If you do not specify a URI, then the CookieInfo is always returned with the
        %   Domain attribute neither set nor checked.  This is acceptable if you do
        %   not intend to use the Domain attribute to manage your cookie store.
        %
        % See also matlab.net.http.Cookie, matlab.net.http.CookieInfo, CookieField,
        % matlab.net.URI
        
            if isempty(obj)
                value = matlab.net.http.CookieInfo.empty;
            else
                if nargin < 2
                    uri = matlab.net.URI.empty;
                else
                    validateattributes(uri, {'matlab.net.URI'}, {'scalar'}, 'CookieField.convert');
                    if isempty(uri.Host)
                        error(message('MATLAB:http:URIMustNameHost', char(uri)));
                    end
                end
                value = arrayfun(@(o)convertScalar(o,uri), obj, 'UniformOutput',false);
                value = [value{:}];
                if nargin > 1
                    value = value(arrayfun(@(d) domainMatch(uri.Host, d), [value.Domain]));
                end
            end
        end
        
    end
    
    methods (Static, Access=protected, Hidden)
        function tf = allowsArray()
            tf = false;
        end
        
        function tokens = getTokenExtents(~, ~, ~)
        % Overridden not to do any escape or quote processing, because strings we get
        % are always the stringified result of CookieInfo which has already done
        % this, or have been validated by CookieInfo.
            tokens = [];
        end
    end
    
    methods (Access=protected, Hidden)
        function str = scalarToString(~, value, varargin)
        % Allow only CookieInfo or strings
            if isa(value, 'matlab.net.http.CookieInfo')
                str = string(value);
            else
                validateattributes(value, {'CookieInfo','string','char'}, {}, mfilename, 'value');
                str = matlab.net.internal.getString(value, mfilename, 'value');
            end
        end
    end                
    
    methods (Access=private)
        function info = convertScalar(obj, uri)
        % Parse the field and return CookieInfo object
        % uri is empty if user didn't specify a URI.  This says we don't use any
        % information in the uri to set the CookieInfo properties.
        % Use RFC 6265 parsing rules for each of the attributes
            if isempty(obj.Value) 
                info = [];
                return;
            end
            % The cookie name=value must be first
            cookieName = '^\s*([^\x00-\x1f\x7f()<>@,;:\\"/\[\]?={} \x09]*)';
            cookieOctets ='[\x21\x23-\x2b\x2d-\x3a\x3c-\x5b\x5d-\x7e]*';
            cookieValue = ['(' cookieOctets '|"' cookieOctets '")'];
            anyNonCtl = '[^\x00-\x1f\x7f;]';
            % The other name=value fields appear in any order, but each has its own
            % syntax.
            % Path can be any non-control characters
            pathValue = ['(' anyNonCtl '+)'];
            % A rather loose description of a date, but we'll parse it later.  Note
            % it can contain spaces.
            date = '([-\w ,:]+\w)';
            % generic attribute value
            attrValue = ['(' anyNonCtl '*)'];
            extensionAv = ['\s*(' anyNonCtl '+)'];
            % This is the ; separator between attributes, except after the last
            sep = '(?:;\s*|$)|';
            % In this expression, res{i} contains a 1- or 2-element cell containing
            % name and optional value of each attribute.  First one is cookie.
            str = [cookieName '=' cookieValue sep ...
                '(Expires)=' date sep ...
                '(Domain)=\.?([-a-zA-Z_0-9./]+)' sep ...  % note leading '.' ignored, if present; we allow / even though RFC disallows
                '(Path)=' pathValue sep ... 
                '(Max-Age)=' attrValue sep ...
                '(Secure)' sep '(HttpOnly)' sep extensionAv sep];
            res = regexpi(obj.Value, str, 'tokens');
            if isempty(res)
                % Return empty CookieInfo object if above parse returns nothing
                info = matlab.net.http.CookieInfo;
                return;
            end
            cookieName = '';
            cookieValue = '';
            if ~isempty(res)
                cookieData = res{1};
                if ~isempty(cookieData)
                    cookieName = cookieData{1};
                    if length(cookieData) > 1
                        cookieValue = cookieData{2};
                    end
                end
            end
            % The first token contains the cookie name and value
            % Get the rest of the tokens as name and optional value
            names = cell(1,length(res)); % preallocate
            values = cell(1,length(res));
            names{1} = 'Cookie';
            values{1} = matlab.net.http.Cookie(cookieName, cookieValue);
            % Loren's conditional if
            iif = @(varargin) varargin{2*find([varargin{1:2:end}], 1)}();
            % First element of each res{i} is the name
            names(2:end) = cellfun(@(r) r{1}, res(2:end), ...
                                   'UniformOutput', false);
            % Second element of each res{i} is the value, if present, or []
            values(2:end) = cellfun(@(r) iif(length(r) < 2, [], true, @()r{2}), ... 
                                    res(2:end), 'UniformOutput', false);
            % Normalize known attribute names
            names(2:end) = regexprep(names(2:end), ...
                {'expires','max-age','domain','path','secure','httponly'}, ...
                {'Expires','MaxAge', 'Domain','Path','Secure','HttpOnly'}, 'ignorecase');
            % names has an array of all the attribute names we know about, plus the
            % contents of any extension-av attributes
            % values has the values of all the attributes we know about, but if 
            % values{i} is [] then names{i} is an extension-av
            info = matlab.net.http.CookieInfo;
            import matlab.net.http.internal.HTTPDatetime
            for i = 1 : length(names)
                name = names{i};
                value = values{i};
                if isempty(value) && isnumeric(value)
                    % value is [] means name is extension-av
                    info.Extensions(end+1) = name;
                else
                    % If the same attribute appears more than once, this saves the
                    % last one.  This is consistent with RFC 6265, section 5.3 that
                    % says to use the last value for known attributes.
                    switch name
                        case 'Expires'
                            % Expires attribute field is set by servers to an
                            % rfc1123-date (RFC 2616 section 3.3.1) as documented in
                            % RFC 6265, section 4.1.1, but clients must implement
                            % more liberal parsing as per RFC 6265, section 5.1.1 for
                            % backwards compatibility.
                            tv = HTTPDatetime.getDatetime(value);
                            if isempty(tv)
                                % TBD: The above only allows a date in the exact
                                % format of an rfc-1123-date.  We try some alternate
                                % parsings here, but this still doesn't implement
                                % fully the formats allowed by 5.1.1 of RFC 6265.  To
                                % do that requires "manual" parsing rather than using
                                % datetime.  Hopefully these cover reasonable
                                % options; mathworks.com implements the 1st one.
                                % (g1352119)
                                formats = {'e, d-M-y H:m:s z' 'e d-M-y H:m:s z' ...
                                           'd-M-y H:m:s z' 'd M y H:m:s z'};
                                for j = 1 : length(formats)
                                    try
                                        tv = datetime(value, 'TimeZone', 'GMT', ...
                                            'InputFormat', formats{j}, 'Format', ...
                                            HTTPDatetime.Format, 'Locale', HTTPDatetime.Locale);
                                        break;
                                    catch
                                        if j == length(formats)
                                            tv = NaT;
                                        end
                                    end
                                end
                            end
                            value = tv;
                        case 'MaxAge'
                            value = str2double(value);
                            value = duration(0,0,value); % could be NaN
                        case {'Secure','HttpOnly'}
                            value = true;
                    end
                    info.(name) = value;
                end
            end
            
            % Always set the ExpirationTime based on the Expires and MaxAge
            % attributes.
            age = info.MaxAge;
            if ~isempty(age) && ~isnan(age)
                % If MaxAge appears, use it instead of Expires
                info.ExpirationTime = datetime('now') + age;
            else
                % Otherwise ExpirationTime is same as Expires
                info.ExpirationTime = datetime('Inf'); % default if neither appear
                if ~isempty(info.Expires) 
                    % RFC says use Expires first
                    info.ExpirationTime = info.Expires;
                end
            end
            
            % Set HostOnly and Domain as per RFC 6265, section 5.3, step 6.
            % Also normalize Domain to lowercase
            info.HostOnly = isempty(info.Domain);
            if info.HostOnly && ~isempty(uri)
                % get Domain from uri.Host if uri and HostOnly specified
                info.Domain = lower(uri.Host);
            else
                info.Domain = lower(info.Domain);
            end
            
            % If Path not specified or doesn't begin with '/', use default-path,
            % which is Path from URI minus any trailing segment, as per 5.1.4 of RFC
            % 6265.
            if isempty(info.Path)
                % Ignore if URI is empty.  This means convert was invoked without
                % a URI, so we don't know what Path to put in.
                if ~isempty(uri) && ...
                   (info.Path=='' || ~startsWith(info.Path,'/'))
                    info.Path = regexprep(uri.EncodedPath, '/[^/]*$', '');
                    if info.Path=='' || ~startsWith(info.Path,'/')
                        info.Path = '/';
                    end
                end
            else
                info.HasPath = true;
            end
        end
    end
    
end

function tf = domainMatch(str, domain)
% Implement RFC 6265 section 5.1.3 Domain Matching
    tf = str == domain || str.endsWith('.' + domain);
end