www.gusucode.com > external 工具箱matlab源码程序 > external/interfaces/webservices/restful/+matlab/+internal/+webservices/HTTPConnector.m
%matlab.internal.webservices.HTTPConnector HTTP connector handle object % % FOR INTERNAL USE ONLY -- This class is intentionally undocumented and % is intended for use only within the scope of functions and classes in % toolbox/matlab/external/interfaces/webservices/restful. Its behavior % may change, or the class itself may be removed in a future release. % % matlab.internal.webservices.HTTPConnector properties (read-only): % URL - URL string % CharacterSet - Connection charset value % ContentType - Connection content type % % matlab.internal.webservices.HTTPConnector properties: % Username - User identifier % Password - User authentication password % KeyName - Name of key % KeyValue - Value of key % HeaderFields - n-by-2 string matrix or cellstr of header names and values % UserAgent - User agent identification % Timeout - Connection timeout % RequestMethod - Name of HTTP request method (GET or POST) % PostData - String data to post to service % MediaType - Media type of data to post to service % Debug - Print debug information % % matlab.internal.webservices.HTTPConnector methods: % HTTPConnector - Constructor % closeConnection - Close HTTP connection % copyContentToByteArray - Copy content to byte array % copyContentToFile - Copy content to file % delete - Delete object % openConnection - Open HTTP connection % Copyright 2014-2016 The MathWorks, Inc. classdef HTTPConnector < handle properties (SetAccess = 'protected', Dependent) URL end properties (SetAccess = 'protected') CharacterSet = '' ContentType = '' end properties Username = '' Password = '' KeyName = '' KeyValue = '' HeaderFields = [] UserAgent = '' Timeout = [] RequestMethod = 'GET' PostData = '' MediaType = 'application/x-www-form-urlencoded' CharacterEncoding end properties (Hidden) Debug = false end properties (Dependent) CertificateFilename = '' end properties (Hidden, Access = 'protected') Connection = [] % matlab.internal.webservices.HTTPConnectionAdapter (C++) ConnectionIsOpen = false Proxy = [] Protocol = '' OptionsContentType = '' NumberOfRedirects = 0 MaximumRedirects = 20 % Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html StatusCode = struct( ... 'MovedPermanently', 301, ... 'Found', 302, ... 'SeeOther', 303, ... 'TemporaryRedirect', 307, ... 'Unauthorized', 401) NumberOfUnauthorizedAttempts = 0 MaximumNumberOfUnauthorizedAttempts = 1 end properties (Access = 'private') pURL pCertificateFilename % The DefaultCertificateFilename is the location of the generated % file containing root certificates. If set, then the certificate % from the HTTP server is validated against the certificates in the % PEM file. The verification validates the host domain against the % domain in the certificate and also issues an error if the % certificate in the PEM file has expired. Since the current % version of rootcerts.pem does have an expired certificate, set % the property value to ''. Even with an empty root certificate % file, the domain verification is still performed. % DefaultCertificateFilename = fullfile(matlabroot,'sys','certificates','ca','rootcerts.pem'); DefaultCertificateFilename = '' MessageCount = 0 % used in log end methods function connector = HTTPConnector(url, options, connection) % Constructor for HTTPConnector class. % Create a connection object, if not passed as an argument. if ~exist('connection', 'var') connection = matlab.internal.webservices.HTTPConnectionAdapter; end connector.Connection = connection; % Set the CertificateFilename property. connector.CertificateFilename = connector.DefaultCertificateFilename; % Set the URL property value. connector.URL = url; % Set the HTTPConnector request properties. if ~exist('options', 'var') options = weboptions; end connector = setProperties(connector, options); connector.OptionsContentType = options.ContentType; end %------------------------------------------------------------------ function openConnection(connector) % Open the URL connection and set request properties. Follow URL % redirects. if ~connector.ConnectionIsOpen try connection = connector.Connection; % Set timeout. milliseconds = secondsToMilliseconds(connector.Timeout); connection.TimeoutInMilliseconds = milliseconds; % Set Username and Password property values. (Allow % an empty password to be passed to the server, but not % an empty username). [username, password] = ... checkRequestProperty(connector, 'Username', 'Password'); if ~isempty(username) connection.Username = username; connection.Password = password; end % Open the connection to the URL. if isempty(connector.Proxy.Host) connection.openConnection(); else connection.openProxyConnection(connector.Proxy); end % Set the request properties. setRequestProperties(connector); % Set the connection properties. setConnectionProperties(connector); % Check if redirecting. if any(strcmpi(connector.Protocol, {'http', 'https'})) if isRedirecting(connector) && ... connector.NumberOfRedirects < connector.MaximumRedirects if connector.Debug % For purposes of logging the body of the redirect % message, assume native encoding. We should really be % looking at the charset in the Content-Type header. try bytes = copyContentToByteArray(connector.Connection); catch % a redirect likely just throws an error in % RESTful mode, so copy no bytes bytes = ''; end connector.log(native2unicode(bytes)); end % Follow redirects. Increase count to prevent % indefinite recursion. connector.NumberOfRedirects = connector.NumberOfRedirects + 1; % Redirecting to a different URL. openRedirectConnection(connector); end end % Determine if compression input stream needs to be used. If % so, set the property on the connection. connection.Encoding = getEncoding(connector); % If an unauthorized code is returned by the server, % the request method is 'get', and the JVM is running, % then try again using the HTTPJavaConnectionAdapter. % This adapter uses Java to communicate to the server % and Java supports NTLM authentication (on Windows). % Do not attempt more times than allowed by % MaximumNumberOfUnauthorizedAttempts. if connector.Connection.ResponseCode == connector.StatusCode.Unauthorized && ... usejava('jvm') && ... strcmpi(connector.RequestMethod, 'get') && ... connector.NumberOfUnauthorizedAttempts < connector.MaximumNumberOfUnauthorizedAttempts connector.NumberOfUnauthorizedAttempts = connector.NumberOfUnauthorizedAttempts + 1; if connector.Debug try % Try to grab data from the unauthorized message. Need to pretend connection % is open to avoid trying to open it again. oldOpen = connector.ConnectionIsOpen; connector.ConnectionIsOpen = true; clean = onCleanup(@()connector.ConnectionIsOpen(oldOpen)); bytes = connector.copyContentToByteArray(); catch bytes = ''; end connector.log(char(bytes)'); end connector.Connection = matlab.internal.webservices.HTTPJavaConnectionAdapter(connector.URL); connector.Connection.Encoding = connection.Encoding; openConnection(connector); end % Connection is open. connector.ConnectionIsOpen = true; catch e throwAsCaller(e) end end end %------------------------------------------------------------------ function res = log(obj, data) % Return a log of the request and response messages, including the response % data. If no return value, print it. This function works only if the % matlab.net.http package is available. connection = obj.Connection; if isa(connection, 'matlab.internal.webservices.HTTPConnectionAdapter') import matlab.net.http.* import matlab.net.* uri = connection.getRequestURI(); method = connection.getRequestMethod(); requestLine = RequestLine(method, URI(uri,'literal'), ProtocolVersion('HTTP/1.1')); requestMessage = RequestMessage(requestLine); requestMessage = ... requestMessage.addFieldsNoCheck(connection.getRequestFields()); requestMessage.Completed = true; [version, status, reason] = connection.getStatusLine(); response = ResponseMessage(StatusLine(version, status, reason)); response = response.addFieldsNoCheck(connection.getResponseFields()); obj.MessageCount = obj.MessageCount + 1; res = sprintf('\nREQUEST %d to %s\n\n%s\n', obj.MessageCount, ... obj.URL, char(requestMessage)); res = [res sprintf('RESPONSE\n\n%s\n', char(response))]; if ~ischar(data) data = evalc('disp(data)'); end if length(data) > 1000 res = [res sprintf('<<%d bytes of data>>\n', length(data))]; else res = [res sprintf('%s\n', data)]; end res = [res sprintf('----------------------------\n')]; else res = sprintf('\nUsing Java\n'); end if nargout == 0 fprintf('%s',res); end end %------------------------------------------------------------------ function closeConnection(connector) % Close the connection. connection = connector.Connection; if ~isempty(connection) && ismethod(connection, 'closeConnection') connection.closeConnection; end connector.ConnectionIsOpen = false; end %------------------------------------------------------------------ function delete(connector) % Close the connection when deleting the object. closeConnection(connector) end %------------------------------------------------------------------ function byteArray = copyContentToByteArray(connector) % Copy the content from the Web service to a byte (uint8) array. openConnection(connector); closeObj = onCleanup(@()closeConnection(connector)); try byteArray = copyContentToByteArray(connector.Connection); catch e code = connector.Connection.ResponseCode; e = convertCopyContentToDataStreamException(e,code); throwAsCaller(e); end end %------------------------------------------------------------------ function copyContentToFile(connector, filename) % Copy the content from the Web service to a file. openConnection(connector); closeObj = onCleanup(@()closeConnection(connector)); try copyContentToFile(connector.Connection, filename); catch e code = connector.Connection.ResponseCode; e = convertCopyContentToDataStreamException(e,code); throwAsCaller(e); end end %------------------------- set/get methods ------------------------ function set.URL(connector, url) % Set the URL property value by storing the value in the private % copy. Set the Protocol, and Proxy property values. % Set private copy. connector.pURL = url; connector.Connection.URL = url; % Get the protocol (before the ":") from the URL. connector.Protocol = getProtocolFromURL(url); % Get the proxy information using the MATLAB proxy API % and set the property. connector.Proxy = getProxySettings(url); end function url = get.URL(connector) url = connector.pURL; end function set.CertificateFilename(connector, filename) filename = matlab.net.internal.validateCertificateFile(filename); connector.pCertificateFilename = filename; connector.Connection.CertificateFilename = filename; end function filename = get.CertificateFilename(connector) % Get CertificateFilename from private copy. filename = connector.pCertificateFilename; end end methods (Access = 'protected') function tf = isRedirecting(connector) % Return true if the connection indicates that the URL is being % redirected by examining the response code. try % For all requests, redirect the same request on Found, MovedPermanently and % TemporaryRedirect. For GET, also redirect on SeeOther: the response may not % be what the user expects, but there will at least be a response. Not % appropriate to redirect SeeOther for other request methods. (RFC 7231, % 6.4.4) code = connector.Connection.ResponseCode; tf = any(code == [ ... connector.StatusCode.Found ... connector.StatusCode.MovedPermanently ... connector.StatusCode.TemporaryRedirect]) || ... (strcmpi(connector.RequestMethod, 'GET') && ... code == connector.StatusCode.SeeOther); catch tf = false; end end %------------------------------------------------------------------ function contentType = getConnectionContentType(connector) % Get the content type from the connection. Return unknown if any % error occurs. Empty may be returned if content type cannot be % determined. Invoking this function causes content to be % downloaded from the server. try contentType = connector.Connection.ContentType; catch e throwAsCaller(e) end if isempty(contentType) % Some servers may not have the mime types setup for % spreadsheet data. Check the URL extension to see if a % match is found. tableExtensions = {'.xls' '.xlsx' '.xlsb' '.xlsm' '.xltm' '.xltx' '.ods'}; url = connector.URL; [~,~,ext] = fileparts(url); if any(strcmpi(ext, tableExtensions)) contentType = 'spreadsheet'; end end end %------------------------------------------------------------------ function setConnectionProperties(connector) % Set connection properties. These properties must be set after the % connection is established and will initiate data transfer from % the server. connection = connector.Connection; if ~isempty(connection) contentType = getConnectionContentType(connector); % Set ContentType and CharacterSet properties. connector.ContentType = getContentTypeFromConnection(contentType); connector.CharacterSet = getCharacterSetFromConnection(contentType); end end %------------------------------------------------------------------ function setRequestProperty(connector, name, value) % Set connection request property if name is not empty. connection = connector.Connection; if ~isempty(name) && ~isempty(connection) connection.setRequestProperty(name, value); end end %------------------------------------------------------------------ function setRequestProperties(connector) % Set the connector property values on the connection. % The set order is important. Certain property manipulations % will invoke the connect method of the connection. After % connection, setting certain properties, such as Accept, can % cause an exception. % Assign a local variable for the connection. connection = connector.Connection; % Set Request method. if any(strcmp(connector.Protocol, {'http', 'https'})) connection.RequestMethod = upper(connector.RequestMethod); % Set PostData and MediaType if RequestMethod is POST. if strcmpi(connector.RequestMethod, 'POST') connection.PostData = connector.PostData; % If CharacterEncoding has been specified and the % MediaType is not application/x-www-form-urlencoded, % then add a "charset=" parameter to MediaType with the % value set to the value of CharacterEncoding. charset % values are not needed for form-encoded data since the % data is already encoded. if isempty(connector.CharacterEncoding) || ... strcmp('auto',connector.CharacterEncoding) || ... strcmp('application/x-www-form-urlencoded',connector.MediaType) mediaType = connector.MediaType; else mediaType = [ ... connector.MediaType '; charset=' connector.CharacterEncoding]; end connection.MediaType = mediaType; end end % Set User-Agent, if not empty. userAgent = connector.UserAgent; if ~isempty(userAgent) setRequestProperty(connector, 'User-Agent', userAgent); end % Obtain KeyName and KeyValue values. [keyName, keyValue] = ... checkRequestProperty(connector, 'KeyName', 'KeyValue'); % Set the Accept request property if options.ContentType is % xmldom or json and KeyName is not Accept. if ~strcmp(keyName, 'Accept') setAcceptRequestProperty(connector); end % Set Key name and value, if KeyName is not empty. (Allow an % empty KeyValue to be passed to the server.) The key % name/value pair may override the Authorization value, if set. if ~isempty(keyName) if ~ischar(keyValue) keyValue = num2str(keyValue); end setRequestProperty(connector, keyName, keyValue); end % HeaderFields was already verified by weboptions to be an n-by-2 cellstr % or string matrix, so calling cellstr converts them to a cellstr. if ~isempty(connector.HeaderFields) headers = cellstr(connector.HeaderFields); cellfun(@(n,v)setRequestProperty(connector, n, v), ... headers(:,1), headers(:,2)); end end %------------------------------------------------------------------ function setAcceptRequestProperty(connector) % Set the Accept request property if options.ContentType is xmldom % or json. % Some RESTful Web servers send either XML or JSON responses. % Set the Accept header property, if either of these content % types are requested. optionsContentType = connector.OptionsContentType; index = strcmp(optionsContentType, {'auto', 'json', 'xmldom'}); if any(index) if index(1) || index(2) % JSON is requested, set the Accept header property to % application/json. contentType = 'application/json'; else % XML is requested, set the Accept header property to % text/xml contentType = 'text/xml'; end % Add all others as secondary types. contentType = [contentType ', */*']; % Set the request property. try setRequestProperty(connector, 'Accept', contentType); catch % Ignore this error. We are only trying to assist in % specifying the Accept value. In most cases, it is not % needed anyway. end end end %------------------------------------------------------------------ function openRedirectConnection(connector) % Open redirect connection if the redirect URL is valid. url = connector.Connection.RedirectURL; if ~isempty(url) % Reset URL to new location. Just in case the url contains non-ASCII % characters, process it using URI to get the encoded version. url = matlab.net.URI(url,'literal'); connector.URL = char(url); % Redirecting to a different URL. % Ensure connection is closed. closeConnection(connector); % Try again to open URL connection. openConnection(connector); else % Close the redirection attempt since the redirect URL is % not valid. connector.NumberOfRedirects = connector.MaximumRedirects + 1; end end %------------------------------------------------------------------ function encoding = getEncoding(connector) contentEncoding = connector.Connection.ContentEncoding; encoding = 0; if ~strcmp('binary', connector.OptionsContentType) switch lower(contentEncoding) case 'gzip' encoding = 1; case 'deflate' encoding = 2; end end end end end %-------------------------------------------------------------------------- function obj = setProperties(obj, options) % Generic function that sets the public property values of obj with the % matching properties of options only if obj and options are non-empty and % scalar objects. if isobject(obj) && isscalar(obj) && isobject(options) && isscalar(options) names = properties(options); mc = metaclass(obj); index = strcmp('public', {mc.PropertyList.SetAccess}); props = {mc.PropertyList.Name}; props = props(index); for k = 1:length(names) prop = find(strcmpi(names{k}, props),1); if ~isempty(prop) obj.(props{prop}) = options.(names{k}); end end % this property is hidden, so copy explicitly obj.Debug = options.Debug; end end %-------------------------------------------------------------------------- function proxy = getProxySettings(url) % Get proxy settings from MATLAB preferences panel. proxy = struct( ... 'Host', '', ... 'Port', [], ... 'Username', '', ... 'Password', ''); if usejava('jvm') % Get the proxy information using the MATLAB proxy API. % Ensure the Java proxy settings are set. com.mathworks.mlwidgets.html.HTMLPrefs.setProxySettings % Obtain the proxy information. url = java.net.URL(url); % This function goes to MATLAB's preference panel or (if not set and on % Windows) the system preferences. javaProxy = com.mathworks.webproxy.WebproxyFactory.findProxyForURL(url); if ~isempty(javaProxy) address = javaProxy.address; if isa(address,'java.net.InetSocketAddress') && ... javaProxy.type == javaMethod('valueOf','java.net.Proxy$Type','HTTP') proxy.Host = char(address.getHostName()); proxy.Port = address.getPort(); % If proxy information came from MATLAB settings, also get the % username and password. If not, we can only talk to unauthenticated % proxies. mwt = com.mathworks.net.transport.MWTransportClientPropertiesFactory.create(); if ~isempty(mwt.getProxyHost()) proxy.Username = char(mwt.getProxyUser()); proxy.Password = char(mwt.getProxyPassword()); end end end else % The Java JVM is not running. The MATLAB proxy information is obtained % from the MATLAB preferences using Java. Return the default structure % containing empty values. end end %-------------------------------------------------------------------------- function protocol = getProtocolFromURL(url) % Get protocol (http or https) from URL. protocol = url(1:find(url == ':', 1) -1); end %-------------------------------------------------------------------------- function contentType = getContentTypeFromConnection(connectionContentType) % Get content type from connection content type. The connection content % type may include the character set. index = find(connectionContentType == ';', 1) - 1; if ~isempty(index) index = index(1); else index = length(connectionContentType); end contentType = connectionContentType(1:index); end %-------------------------------------------------------------------------- function charSet = getCharacterSetFromConnection(connectionContentType) % Get character set from connection content type. The default value if not % found is left as empty. defaultCharacterSet = ''; index = find(connectionContentType == ';', 1) + 1; if isempty(index) charSet = defaultCharacterSet; else charSet = connectionContentType(index:end); if ~isempty(charSet) && ~all(isspace(charSet)) charsetMatch = regexpi(charSet,'charset=([a-z0-9\-\.:_])*','tokens','once'); if ~isempty(charsetMatch) charSet = charsetMatch{1}; else % The sub-string "charset=" was not found in connectionContentType. charSet = ''; end end end end %-------------------------------------------------------------------------- function milliseconds = secondsToMilliseconds(seconds) if ~isempty(seconds) % Convert to milliseconds and bound the value by a reasonable amount. % Use ceil to prevent the calculation from reaching 0. secondsToMilliseconds = 1000; milliseconds = round(ceil(seconds*secondsToMilliseconds)); % For bounding, assume a maximum microsecond value that will fit into a % long (35.791 minutes) microToMillisecond = 1/1000; maxValueInMilliseconds = fix(double(intmax*microToMillisecond)-1); milliseconds = min(maxValueInMilliseconds, milliseconds); else % The value is empty, set to the minimum. milliseconds = 1; end end %-------------------------------------------------------------------------- function [name, value] = checkRequestProperty(connector, propName, propValue) % Ensure that if propName is set, then propValue may be set or empty. % If propValue is set, then propName must also be set. name = connector.(propName); value = connector.(propValue); % If propValue is set, then propName must be set. propValueIsSet = ~isempty(value); if propValueIsSet && isempty(name) id = 'MATLAB:webservices:ExpectedNonempty'; error(message(id, ['options.' propName])); end end %-------------------------------------------------------------------------- function e = convertCopyContentToDataStreamException(e, responseCode) % Convert an MException with CopyContentToDataStream ID to an exception % with an ID that contains the HTTP status code. id = 'MATLAB:webservices:StatusError'; if strcmp(e.identifier, id) && ~isempty(responseCode) responseCode = num2str(responseCode); id = ['MATLAB:webservices:HTTP' responseCode 'StatusCodeError']; e = MException(id, '%s', e.message); end end