www.gusucode.com > external 工具箱matlab源码程序 > external/interfaces/webservices/http/+matlab/+net/+http/Message.m
classdef (Abstract, AllowedSubclasses={?matlab.net.http.RequestMessage, ... ?matlab.net.http.ResponseMessage}) Message < ... matlab.mixin.Heterogeneous % Message Abstract base class of HTTP messages % % Message properties: % % StartLine - the start line % Header - header of the message % Body - body of the message % Completed - true if message is completed % % Message methods: % % getFields - return header fields % addFields - add header fields % changeFields - change existing header fields % removeFields - remove header fields % string - return the contents of the message as a string % char - return the contents of the message as a character vector % show - return the message as a string with optional maximum length % % See also matlab.net.http.RequestMessage, matlab.net.http.ResponseMessage % Copyright 2015-2016 The MathWorks, Inc. properties % StartLine - start line of the message, a matlab.net.http.StartLine StartLine % It's really a matlab.net.http.StartLine but we can't declare an abstract type w/o initializing it % Header - header of the message, vector of matlab.net.http.HeaderField % When you set this property, the fields of the Header will be checked to % insure that they are appropriate for the type of message. % % See also HeaderField Header matlab.net.http.HeaderField % Body - body of the message, a matlab.net.http.MessageBody % You may set this property to a MessageBody or any value acceptable to the % MessageBody constructor. If this message has a Content-Type header field, % Body.ContentType will be set to the MediaType of that field, overriding % any previous value that might be set in that property. Otherwise it will % be unchanged. % % See also MessageBody, matlab.net.http.field.ContentTypeField Body matlab.net.http.MessageBody end properties (Transient) % Completed - true if this message was completed % This property means that the message contains all the headers sent or % received, and if there is a payload, the raw payload as well as possibly % converted data in Body. % % In a RequestMessage, methods that validate this message, such as % RequestMessage.send and RequestMessage.complete, set this property to true % once determining that the message is valid and completing any processing % such as adding required header fields and converting Body.Data to % Body.Paylod. If you set this to true prior to issuing send or complete, % these methods will not modify message headers, and the send method will % send the message without checking it for validity. Any change to a % property in a message resets this property back to false. % % In a ResponseMessage, this property is true if the message contains no % payload (Body or Body.Data are empty), or if Body.Payload contains the raw % data. % % Set this to true in a RequestMessage if you want to prevent the send % method from modifying the headers of the message, thereby letting you send % arbitrary headers. You can still use the complete method to % validate the message, but the send method will attempt to send it whether % or not it is valid. Even if Completed is set, the send method will fill % in any properties of the RequestLine that are empty, and may convert % Body.Data, but it will not reject the message due to invalid headers. % % If Body.Payload contains data and Completed is true, RequestMessage.send % will send the payload as is, without attempting to convert any information % in Body.Data. But if Body.Data is nonempty and Body.Payload is empty, % RequestMessage.send will always convert the data based on the content type % (obtained from the Content-Type header field or derived from the type of % Data), regardless of the value of Completed, as it needs to do so to send % the message. % % The Completed property set in a RequestMessage or ResponseMessage returned % by RequestMessage.send, or in the HISTORY returned by send, indicates that % the message contains exactly what was sent to or received from the server. % For messages containing a body, this includes the raw data in % Body.Payload. This raw data is not preserved in ReponseMessages unless % you set HTTPOptions.SavePayload or there was an error trying to convert the % payload, so ResponseMessages with a body will not normally have Completed % set. % % See also matlab.net.http.RequestMessage.send, % matlab.net.http.RequestMessage.complete, Body, MessageBody, % matlab.net.http.HTTPOptions.SavePayload, ResponseMessage Completed logical = false end methods function obj = set.Header(obj, value) if ~isempty(value) validateattributes(value, {'matlab.net.http.HeaderField'}, ... {'vector'}, mfilename, 'Header'); badField = obj.getInvalidFields(value); if isempty(badField) obj.Header = value; else if isempty(badField.Value) error(message('MATLAB:http:BadFieldNameInHeader', ... char(badField.Name), class(obj))); else error(message('MATLAB:http:BadFieldValueInHeader', ... char(badField.Name), char(badField.Value), class(obj))); end end obj.checkHeader(value); end obj.Header = value; obj.Completed = false; %#ok<MCSUP> because Completed transient if obj.shouldSetBodyContentType() % copy the ContentType header field into the body, if there is one ct = obj.getBodyContentType(); if ~isempty(ct) % note this does not call the set.Body method obj.Body.ContentType = ct; %#ok<MCSUP> because ContentType transient end end end function obj = set.Body(obj, value) obj.checkBody(value); if isa(value, 'matlab.net.http.MessageBody') obj.Body = value; else obj.Body = matlab.net.http.MessageBody(value); end if obj.shouldSetBodyContentType() % copy the ContentType header field into the body, if there is one ct = obj.getBodyContentType(); if ~isempty(ct) obj.Body.ContentType = ct; end end obj.Completed = false; %#ok<MCSUP> because Completed transient end function obj = set.StartLine(obj, value) if ~isempty(value) || ~isnumeric(value) validateattributes(value, {obj.getStartLineType() ... 'matlab.net.http.StartLine'}, {'scalar'}, mfilename, 'StartLine'); end obj.StartLine = value; obj.Completed = false; %#ok<MCSUP> because Completed transient end function obj = set.Completed(obj, value) validateattributes(value, {'logical'}, {'scalar'}, mfilename, 'Completed'); obj.Completed = value; end end methods (Sealed) % These methods are sealed so they can be called on a heterogeneous array function [fields, indices] = getFields(obj, varargin) % getFields Return fields from HTTP message headers % [FIELDS, INDICES] = getFields(MESSAGES, IDS) returns FIELDS, a vector of % HeaderField in the vector of MESSAGES that match the IDS, and a vector % of their INDICES. The IDS can be: % - a character vector, vector of strings, comma-separated list of % strings or character vectors, or cell array of character vectors % naming the fields to be returned. Names are not case-sensitive. % - a vector of HeaderField or comma separated list of them, whose names % are used to determine which fields to return. Names are not % case-sensitive and Values of the HeaderFields are ignored. % - a vector of meta.class that are subclasses of HeaderField, or % comma-separated list of them. For each class, this matches any % fields that are instances of that class (including its subclasses) % plus any fields whose names match the names explicitly supported by % the class or its subclasses. % % For example MediaRangeField has two subclasses, AcceptField and % ContentTypeField. If you specify an ID of % ?matlab.net.http.field.MediaRangeField it will match all fields of % of class MediaRangeField, AcceptField and ContentTypeField, plus any % fields with the Name 'Accept' or 'Content-Type' (such as % matlab.net.http.HeaderField or matlab.net.http.field.GenericField). % % If MESSAGES contains more then one message, INDICES is a cell array of % vectors, where INDICES{i} contains the indices of the matching fields % in MESSAGE(i). % % If there is no match, Fields is HeaderField.empty. % % Header field name matching is case-insensitive. % % If there is more than one match, FIELDS is a vector containing all the % matches. If they are all the same type, it is generally possible to % call convert(FIELDS) to obtain a vector of their converted values. % Except for the Set-Cookie field, multiple fields of the same type are % allowed in a message only if the field syntax supports a comma-separated % list of values. % % See also HeaderField, removeFields, addFields, changeFields, meta.class, % matlab.net.http.HeaderField.convert, matlab.net.http.field.SetCookieField, % matlab.net.http.field.MediaRangeField, matlab.net.http.field.AcceptField, % matlab.net.http.field.ContentTypeField fields = matlab.net.http.HeaderField.empty; if isempty(varargin) error(message('MATLAB:minrhs')); end if nargout > 1 if ~isscalar(obj) indices{length(obj)} = []; else indices = []; end end % look for HeaderField instances ids = getFieldsVector(varargin); if isempty(ids) % none; look for mata.class instances ids = getFieldsVector(varargin,'meta.class'); if isempty(ids) ids = getNameVector(varargin); end end names = getMatchingNames(ids); if isa(ids, 'meta.class') classes = ids; else classes = []; end for i = 1 : length(obj) msg = obj(i); idx = []; if ~isempty(msg.Header) hdrNames = [msg.Header.Name]; matches = zeros(1,length(hdrNames)); % first match the names for j = 1 : length(names) matches = matches | strcmpi(names(j), hdrNames); end if ~isempty(classes) % next match the classes, if any matches = matches | ... arrayfun(@(hdr) any(metaclass(hdr) <= classes), msg.Header); end if isempty(fields) fields = msg.Header(matches); else fields = [fields msg.Header(matches)]; %#ok<AGROW> end if nargout > 1 idx = find(matches); end end if nargout > 1 if iscell(indices) indices{i} = idx; else indices = idx; end end end if isempty(fields) % g1393498 workaround fields = matlab.net.http.HeaderField.empty; end end function obj = addFields(obj, varargin) % addFields Add fields to an HTTP message header % MSG = addFields(MSG,FIELDS) returns a copy of the message array MSG with % additional fields added to the end of the header of each message. % FIELDS is a vector of HeaderField objects or comma separated list of % them. There is no check for duplicate fields, but RequestMessage.send % and RequestMessage.complete may reject inappropriate duplicates. % % MSG = addFields(MSG,NAME1,VALUE1,...,NAMEn,VALUEn) adds fields with NAME % and VALUE to end of Header. A VALUE may be '' to use the default value % for the field (usually, but not necessarily, []). If the last VALUE % is missing, it is the same as specifying [] (see below). The type of % HeaderField object created depends on its NAME, and any VALUEs are % validated for that type. % % MSG = addFields(MSG,INDEX,___) inserts fields at the specified INDEX in % Header. Can be used in combination with any of above. Adds to end if % INDEX is greater than length of Header. If INDEX is negative, counts % from end of header, where 0 adds fields to the end and -1 inserts % fields before the last field. % % If the value of any header field is [], this indicates that the field % should not be automatically added when the message is sent or completed. % In a message already completed, a value of [] does not remove instances % of that field already present in the message. Fields with empty values % are remvoed from the message prior to sending. % % See also HeaderField, RequestMessage, getFields, changeFields, % removeFields, Completed, complete % Undocumented behavior for internal use only -- this may change in a future % release: % addFields(STRUCTS) - array of structures containing Name and Value % fields. obj = obj.addFieldsPrivate(true, varargin{:}); matlab.net.http.internal.nargoutWarning(nargout,mfilename,'addFields'); end function obj = changeFields(obj, varargin) % changeFields Change existing fields in an HTTP message header % MSG = changeFields(MSG,NAME1,VALUE1,...,NAMEn,VALUEn) returns a copy of % the message array MSG with changes to the values of existing fields in % each message. NAME is the name of the field and VALUE is the new value. % Specify a VALUE of '' to reset a field to its default value, or [] to % prevent the field from being automatically added when the message is % completed (e.g., by RequestMessage.send or RequestMessaget.complete). % The last VALUE, if missing, is assumed to be []. % % NAME matching is case-insensitive; however if the NAME you specify % differs in case from the existing name, then the field's name will be % changed to NAME. This usage will never change the class of an existing % field in MSG. % % MSG = changeFields(MSG,FIELDS) changes existing fields to the names, % values and types in FIELDS, a vector of HeaderFields or comma-separated % list of them. This may change the class of an existing field if it has % a case-insensitive match to a name in FIELDS. % % This function throws an error if all the specified fields are not already % in the header of each message, or if there is more than one match to a % specified field. % % See also HeaderField, addFields, getFields, removeFields % matlab.net.http.RequestMessage.send, matlab.net.http.RequestMessage.complete % Make array of generic HeaderField objects out of args and validate proposed % values. fields = getFieldsVector(varargin); if ~isempty(fields) useClasses = true; else useClasses = false; names = varargin(1:2:end); values = varargin(2:2:end); if length(values) < length(names) values{end+1} = []; % append empty to end if not enough values end % Call the HeaderField constructors on each. This will validate that the % values are valid for those names. fields = cellfun(@(n,v)matlab.net.http.HeaderField(n,v), ... names, values, 'UniformOutput', false); fields = [fields{:}]; % take HeaderFields out of their cells end for midx = 1 : length(obj) msg = obj(midx); msg.checkHeader(fields); % validate fields are appropriate % Now merge them in. for i = 1 : length(fields) name = fields(i).Name; % match is vector of matching fields if isempty(msg.Header) match = []; else match = find(strcmpi(name, [msg.Header.Name])); end if isempty(match) error(message('MATLAB:http:NoMatchingField',char(name))); elseif length(match) > 1 error(message('MATLAB:http:MoreThanOneField',char(name))); end % Copy name and value properties into existing field. In the NAME,VALUE usage % of this function, don't just store the whole fields(i) because we don't want % to change its class. if useClasses msg.Header(match) = fields(i); else msg.Header(match).Name = fields(i).Name; msg.Header(match).Value = fields(i).Value; end end obj(midx) = msg; end matlab.net.http.internal.nargoutWarning(nargout,mfilename,'changeFields'); end function obj = removeFields(obj, varargin) % removeFields Remove fields from an HTTP message header % MSG = removeFields(MSG, IDS) returns a copy of the message array MSG with % all header fields matching IDS removed. The IDS can be: % - a character vector, vector of strings, comma-separated list of % strings or character vectors, or cell array of character vectors % naming the fields to be removed. Names are not case-sensitive. % - a vector of HeaderField or comma-separated list of them, whose names % are used to determine which fields to remove. Names are not % case-sensitive and Values of the HeaderFields are ignored. % - a vector of meta.class that are subclasses of HeaderField, or % comma-separated list of them. For each class, this matches any fields % that are instances of that class (including its subclasses) plus any % fields whose names match the names explicitly supported by the class % or its subclasses. % % For example MediaRangeField has two subclasses, AcceptField and % ContentTypeField. If you specify an ID of % ?matlab.net.http.field.MediaRangeField it will match all fields of % class MediaRangeField, AcceptField and ContentTypeField, plus any fields % with the Name 'Accept' or 'Content-Type' (such as % matlab.net.http.HeaderField or matlab.net.http.field.GenericField). % % See also HeaderField, addFields, changeFields, getFields if isempty(varargin) return; end ids = getFieldsVector(varargin); if isempty(ids) ids = getFieldsVector(varargin,'meta.class'); if isempty(ids) ids = getNameVector(varargin); end end names = getMatchingNames(ids); if isa(ids, 'meta.class') classes = ids; else classes = []; end for midx = 1 : length(obj) header = obj(midx).Header; if isempty(header) continue; end % first remove matching names for i = 1 : length(names) header(strcmpi(names(i), [header.Name])) = []; end % next remove matching classes and their subclasses if ~isempty(classes) header(arrayfun(@(h) any(metaclass(h) <= classes), header)) = []; end obj(midx).Header = header; end matlab.net.http.internal.nargoutWarning(nargout,mfilename,'removeFields'); end function res = char(obj) % char Return an HTTP message as a character vector % CHR = char(MSG) converts the message MSG to a character vector. If MSG is % an array with more than one message, returns a cell array of character % vectors. % % For more information, see string. % % See also string, show num = length(obj); if num > 1 res = cell(1,num); end for i = 1 : num field = obj(i); sHeader = sprintf('%s\n', char(field.Header)); if isempty(field.Body) || ... (isempty(field.Body.Data) && isempty(field.Body.Payload)) str = sprintf('%s\n%s', char(field.StartLine), sHeader); else str = sprintf('%s\n%s%s\n', char(field.StartLine), ... sHeader, field.Body.char()); end if num > 1 res{i} = str; else res = str; end end end function str = string(obj) % string Return an HTTP Message as a string % STR = string(MSG) returns the whole message, including Header and Body, as % a string. If the Body contains binary data (as opposed to character data) % a single line appears indicating the length of the data. % % If MSG is an array of messages, returns an array of strings, one per % message. % % This method is intended for logging or debugging. % % See also show, char str = string(obj.char()); end function str = show(obj, varargin) % show Display or return a human-readable version of a message % show(MSG) displays the whole message, including Header and Body, in the % command window. If MSG is an array with more than one message, displays % all of the messages with dashed lines separating the messages. % % show(MSG, MAXLENGTH) displays at most MAXLENGTH characters of the Body. % If the body is longer than this, displays an indication of the total % length of the data in characters and the type of the data. % % STR = show(___) returns a string containing the information that would be % displayed. % % If the Body contains binary data that cannot be converted to characters, % show displays or returns a single line for the Body, indicating the % length of the data in bytes. % % This method is intended for diagnostics or debugging. % % See also string, char, Body num = length(obj); if nargout > 0 str(num) = string(''); end for i = 1 : length(obj) field = obj(i); if ~isempty(field.Header) sHeader = sprintf('%s\n', string(field.Header)); else sHeader = ''; end if i > 1 && nargout == 0 fprintf('------------------------------------------\n'); end if isempty(field.Body) || ... (isempty(field.Body.Data) && isempty(field.Body.Payload)) if nargout == 0 fprintf('%s\n%s', string(field.StartLine), sHeader); else str(i) = sprintf('%s\n%s', string(field.StartLine), sHeader); end else if nargout == 0 fprintf('%s\n%s\n%s\n', string(field.StartLine), ... sHeader, field.Body.show(varargin{:})); else str(i) = sprintf('%s\n%s\n%s\n', string(field.StartLine), ... sHeader, field.Body.show(varargin{:})); end end end end end methods (Access=?matlab.net.http.internal.HTTPConnector, Hidden) function obj = setBodyExternal(obj, data) % Calls the setBody method from other classes obj = setBody(obj, data); end end methods (Abstract, Static, Access=protected, Hidden) % checkHeader(header) - throw error if the header has any invalid fields % for this message type. Invoked from set.Header after verifying its type. checkHeader(header) % checkBody(value) - throw error if the value is not suitable for the body % of this message type. Invoked from set.Body prior to any verification. checkBody(value) % getStartLineType - get the specific type of the StartLine for this message. % Returns a string. type = getStartLineType() % getInvalidFields Check whether the fields are valid for this header % This function is intended to be implemented by subclasses to place % restrictions on fields that are not allowed for the subclass. It is % invoked by the set method of the Fields property after verifying that the % fields value is a vector of matlab.net.http.HeaderField. If an invalid % field is found, it returns the invalid field, and the infrastructure will % print the appropriate error message. If the problem is only with the % field's Name, the subclass should set the Value in the returned field to % [] in order to produce the appropriate message. badField = getInvalidFields(fields) % Return true if setting the ContentType header field in this message should % also set the ContentType in the Body. tf = shouldSetBodyContentType() end methods (Access=protected, Hidden) function res = getSingleField(obj, name) % getSingleField returns one header field % FIELD = getSingleField(obj, NAME) returns the HeaderField matching NAME. % Use this method to extract a field that you only expect to appear once in % a message header. If there is more than one instance of the field, this % errors out. If the field does not appear in the header, this returns []. % % See also getAllFields res = obj.getFields(name); if ~isempty(res) && length(res) > 1 error(message('MATLAB:http:MoreThanOneField', name)); end end end methods (Access=private, Sealed) function obj = addFieldsPrivate(obj, check, varargin) % Add fields to the header, with optional check of validity % obj - a message array % check - if set, any errors thrown by subclass constructors of HeaderField % will be thrown from this function. If false, a Generic HeaderField % object will be created if the subclass constructor fails. % varargin - an optional INDEX plus an array of HeaderField, an array of % structs containing Name/Value fields, or a list of name/value pairs. import matlab.net.http.HeaderField; if isempty(varargin) error(message('MATLAB:minrhs')); end for midx = 1 : length(obj) if isnumeric(varargin{1}) where = varargin{1}; validateattributes(where, {'numeric'}, {'scalar','integer'}, ... mfilename, 'index'); % set where to value in range 1:length(Header)+1 if where <= 0 % count from end where 0 is end+1 where = max(length(obj(midx).Header) + where + 1, 1); else % count from front where = min(length(obj(midx).Header) + 1, where); end varargin(1) = []; % shift varargs left else where = length(obj(midx).Header) + 1; end fields = getFieldsVector(varargin); if isempty(fields) if isstruct(varargin{1}) % if first vararg is a struct, create HeaderField objects from % Name/Value members structs = varargin{1}; assert(length(varargin) == 1 && isvector(structs)); if check fields = arrayfun(@(s) ... HeaderField.createHeaderField(s.Name, s.Value), structs, ... 'UniformOutput', false); else fields = arrayfun(@(s) createFieldSafe(s.Name,s.Value), ... structs, 'UniformOutput', false); end fields = [fields{:}]; else % else, assume varargs is list of name,value pairs npairs = ceil(length(varargin)/2); fields(npairs) = HeaderField(); for i = 1 : npairs idx = 2*i-1; name = varargin{idx}; if idx == length(varargin) value = []; else value = varargin{idx+1}; end if check fields(i) = HeaderField.createHeaderField(name, value); else fields(i) = createFieldSafe(name, value); end end end end obj(midx).Header = ... [obj(midx).Header(1:where-1) fields obj(midx).Header(where:end)]; end end end methods (Sealed, Access=protected, Hidden) function obj = addFieldsSilent(obj, varargin) % addFieldsSilent Add fields without throwing an error % Same as addFields but silently creates a % matlab.net.http.field.GenericField header field if the Value of a field % is inappropriate for its Name. % % varargin - an array of matlab.net.http.HeaderField, an array of structs % containing Name/Value fields, or a list of name/value pairs. % % See also matlab.net.http.HeaderField, addFields obj = obj.addFieldsPrivate(false, varargin{:}); end function ct = getBodyContentType(obj) % If this message contains a MessageBody, return the MediaType in the % Content-Type header field. If not, return []. This causes the ContentType % of Body to be set to the value of the Content-Type field. ct = []; if ~isempty(obj.Body) ctf = obj.getSingleField('Content-Type'); if ~isempty(ctf) ct = ctf.convert(); end end end end methods (Access={?matlab.net.http.RequestMessage, ... ?matlab.internal.webservices.HTTPConnector, ... ?matlab.net.http.internal.HTTPConnector}) function obj = addFieldsNoCheck(obj, varargin) % addFieldsSilent Add fields to end of header without checking for errors % This function is the same as addFields, except that it does not enforce % any constraints on the names of the fields or their values. If a % subclass of HeaderField with the specified NAME cannot be constructed due % to an invalid VALUE, it constructs a generic HeaderField object. % % See also matlab.net.http.Header.addFields, matlab.net.http.HeaderField obj = obj.addFieldsSilent(varargin{:}); end end end function field = createFieldSafe(name, varargin) % createFieldSafe Create a HeaderField of the appropriate subclass from the name and % value. If the subclass complains, create one of type matlab.net.http.field.Generic. try field = matlab.net.http.HeaderField.createHeaderField(name, varargin{:}); catch field = matlab.net.http.field.GenericField(name, varargin{:}); end end function names = getMatchingNames(ids) % Return vector of names in vector of ids: % strings or cell array of strings (names) % meta.class (use PackageMonitor.getNamesForClass) % HeaderField (use their names) % Error out if ids is not one of above. if ischar(ids) || isstring(ids) || iscell(ids) names = matlab.net.internal.getStringVector(ids, mfilename, 'IDS'); else % We want to error out if user specifies HeaderField.empty or a meta.class % that doesn't exist (which creates meta.class.empty) with a message that the % input was empty, so need to mention 'nonempty' first, before 'vector'. validateattributes(ids, {'cell', 'meta.class', 'matlab.net.http.HeaderField', 'string array'}, ... {'nonempty','vector'}, mfilename, 'IDS'); if isa(ids, 'meta.class') names = arrayfun(@(c)matlab.net.http.internal.PackageMonitor.getNamesForClass(c), ... ids, 'UniformOutput', false); names = [names{:}]; else % input was HeaderField array names = [ids.Name]; end end end function fields = getFieldsVector(args, type) % args is a cell array, the varargin from a call to a function. If it % contains all HeaderField objects, return them all in a vector. If a % mixture, error out. If no HeaderField objects, return % matlab.net.http.HeaderField.empty. % % if 'type' specified, its the name of a class other than HeaderField, to to % the above processing. if nargin == 1 type = 'matlab.net.http.HeaderField'; end if any(cellfun(@(a) isa(a, type), args)) % If any arg is type, try to collapse all args into type vector. try fields = reshape([args{:}], 1, []); catch e % didn't work, so error out error(message('MATLAB:http:ExpectedAllSameType', type)); end else fields = feval([type '.empty']); end end function args = getNameVector(args) % args is the varargin from a call to a function, which must not be empty. If % first arg is a nonscalar array other than char or cell array, it must be the % only argument, and we return args{1}, or we error out with "too many input % arguments". Otherwise we return args. firstArg = args{1}; if iscell(firstArg) || (~ischar(firstArg) && ~isscalar(firstArg)) if length(args) > 1 error(message('MATLAB:maxrhs')); end args = firstArg; end end