mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-07-29 03:27:48 +00:00
Add a advanced example
It uses its own API which I packed between MTApi5 and MatLab. Possible that this is useless, but errors from the .NET assembly are more tradable.
This commit is contained in:
@@ -0,0 +1,371 @@
|
||||
function [output,extras] = urlread2(urlChar,method,body,headersIn,varargin)
|
||||
%urlread2 Makes HTTP requests and processes response
|
||||
%
|
||||
% [output,extras] = urlread2(urlChar, *method, *body, *headersIn, varargin)
|
||||
%
|
||||
% * indicates optional inputs that must be entered in place
|
||||
%
|
||||
% UNDOCUMENTED MATLAB VERSION
|
||||
%
|
||||
% EXAMPLE CALLING FORMS
|
||||
% ... = urlread2(urlChar)
|
||||
% ... = urlread2(urlChar,'GET','',[],prop1,value1,prop2,value2,etc)
|
||||
% ... = urlread2(urlChar,'POST',body,headers)
|
||||
%
|
||||
% FEATURES
|
||||
% =======================================================================
|
||||
% 1) Allows specification of any HTTP method
|
||||
% 2) Allows specification of any header. Very little is hard-coded
|
||||
% in for header handling.
|
||||
% 3) Returns response status and headers
|
||||
% 4) Should handle unicode properly ...
|
||||
%
|
||||
% OUTPUTS
|
||||
% =======================================================================
|
||||
% output : body of the response, either text or binary depending upon
|
||||
% CAST_OUTPUT property
|
||||
% extras : (structure)
|
||||
% .allHeaders - stucture, fields have cellstr values, HTTP headers may
|
||||
% may be repeated but will have a single field entry, with each
|
||||
% repeat's value another being another entry in the cellstr, for
|
||||
% example:
|
||||
% .Set_Cookie = {'first_value' 'second_value'}
|
||||
% .firstHeaders - (structure), variable fields, contains the first
|
||||
% string entry for each field in allHeaders, this
|
||||
% structure can be used to avoid dereferencing a cell
|
||||
% for fields you expect not to be repeated ...
|
||||
% EXAMPLE:
|
||||
% .Response : 'HTTP/1.1 200 OK'}
|
||||
% .Server : 'nginx'
|
||||
% .Date : 'Tue, 29 Nov 2011 02:23:16 GMT'
|
||||
% .Content_Type : 'text/html; charset=UTF-8'
|
||||
% .Content_Length : '109155'
|
||||
% .Connection : 'keep-alive'
|
||||
% .Vary : 'Accept-Encoding, User-Agent'
|
||||
% .Cache_Control : 'max-age=60, private'
|
||||
% .Set_Cookie : 'first_value'
|
||||
% .status - (structure)
|
||||
% .value : numeric value of status, ex. 200
|
||||
% .msg : message that goes along with status, ex. 'OK'
|
||||
% .url - eventual url that led to output, this can change from
|
||||
% the input with redirects, see FOLLOW_REDIRECTS
|
||||
% .isGood - (logical) I believe this is an indicator of the presence of 400
|
||||
% or 500 status codes (see status.value) but more
|
||||
% testing is needed. In other words, true if status.value < 400.
|
||||
% In code, set true if the response was obtainable without
|
||||
% resorting to checking the error stream.
|
||||
%
|
||||
% INPUTS
|
||||
% =======================================================================
|
||||
% urlChar : The full url, must include scheme (http, https)
|
||||
% method : examples: 'GET' 'POST' etc
|
||||
% body : (vector)(char, uint8 or int8) body to write, generally used
|
||||
% with POST or PUT, use of uint8 or int8 ensures that the
|
||||
% body input is not manipulated before sending, char is sent
|
||||
% via unicode2native function with ENCODING input (see below)
|
||||
% headersIn : (structure array), use empty [] or '' if no headers are needed
|
||||
% but varargin property/value pairs are, multiple headers
|
||||
% may be passed in as a structure array
|
||||
% .name - (string), name of the header, a name property is used
|
||||
% instead of a field because the name must match a valid
|
||||
% header
|
||||
% .value - (string), value to use
|
||||
%
|
||||
% OPTIONAL INPUTS (varargin, property/value pairs)
|
||||
% =======================================================================
|
||||
% CAST_OUTPUT : (default true) output is uint8, useful if the body
|
||||
% of the response is not text
|
||||
% ENCODING : (default ''), ENCODING input to function unicode2native
|
||||
% FOLLOW_REDIRECTS : (default true), if false 3xx status codes will
|
||||
% be returned and need to be handled by the user,
|
||||
% note this does not handle javascript or meta tag
|
||||
% redirects, just server based ones
|
||||
% READ_TIMEOUT : (default 0), 0 means no timeout, value is in
|
||||
% milliseconds
|
||||
%
|
||||
% EXAMPLES
|
||||
% =======================================================================
|
||||
% GET:
|
||||
% --------------------------------------------
|
||||
% url = 'http://www.mathworks.com/matlabcentral/fileexchange/';
|
||||
% query = 'urlread2';
|
||||
% params = {'term' query};
|
||||
% queryString = http_paramsToString(params,1);
|
||||
% url = [url '?' queryString];
|
||||
% [output,extras] = urlread2(url);
|
||||
%
|
||||
% POST:
|
||||
% --------------------------------------------
|
||||
% url = 'http://posttestserver.com/post.php';
|
||||
% params = {'testChars' char([2500 30000]) 'new code' '?'};
|
||||
% [paramString,header] = http_paramsToString(params,1);
|
||||
% [output,extras] = urlread2(url,'POST',paramString,header);
|
||||
%
|
||||
% From behind a firewall, use the Preferences to set your proxy server.
|
||||
%
|
||||
% See Also:
|
||||
% http_paramsToString
|
||||
% unicode2native
|
||||
% native2unicode
|
||||
%
|
||||
% Subfunctions:
|
||||
% fixHeaderCasing - small subfunction to fix case errors encountered in real
|
||||
% world, requires updating when casing doesn't match expected form, like
|
||||
% if someone sent the header content-Encoding instead of
|
||||
% Content-Encoding
|
||||
%
|
||||
% Based on original urlread code by Matthew J. Simoneau
|
||||
%
|
||||
% VERSION = 1.1
|
||||
|
||||
in.CAST_OUTPUT = true;
|
||||
in.FOLLOW_REDIRECTS = true;
|
||||
in.READ_TIMEOUT = 0;
|
||||
in.ENCODING = '';
|
||||
|
||||
%Input handling
|
||||
%---------------------------------------
|
||||
if ~isempty(varargin)
|
||||
for i = 1:2:numel(varargin)
|
||||
prop = upper(varargin{i});
|
||||
value = varargin{i+1};
|
||||
if isfield(in,prop)
|
||||
in.(prop) = value;
|
||||
else
|
||||
error('Unrecognized input to function: %s',prop)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ~exist('method','var') || isempty(method), method = 'GET'; end
|
||||
if ~exist('body','var'), body = ''; end
|
||||
if ~exist('headersIn','var'), headersIn = []; end
|
||||
|
||||
assert(usejava('jvm'),'Function requires Java')
|
||||
|
||||
import com.mathworks.mlwidgets.io.InterruptibleStreamCopier;
|
||||
com.mathworks.mlwidgets.html.HTMLPrefs.setProxySettings %Proxy settings need to be set
|
||||
|
||||
%Create a urlConnection.
|
||||
%-----------------------------------
|
||||
urlConnection = getURLConnection(urlChar);
|
||||
%For HTTP uses sun.net.www.protocol.http.HttpURLConnection
|
||||
%Might use ice.net.HttpURLConnection but this has more overhead
|
||||
|
||||
%SETTING PROPERTIES
|
||||
%-------------------------------------------------------
|
||||
urlConnection.setRequestMethod(upper(method));
|
||||
urlConnection.setFollowRedirects(in.FOLLOW_REDIRECTS);
|
||||
urlConnection.setReadTimeout(in.READ_TIMEOUT);
|
||||
|
||||
for iHeader = 1:length(headersIn)
|
||||
curHeader = headersIn(iHeader);
|
||||
urlConnection.setRequestProperty(curHeader.name,curHeader.value);
|
||||
end
|
||||
|
||||
if ~isempty(body)
|
||||
%Ensure vector?
|
||||
if size(body,1) > 1
|
||||
if size(body,2) > 1
|
||||
error('Input parameter to function: body, must be a vector')
|
||||
else
|
||||
body = body';
|
||||
end
|
||||
end
|
||||
|
||||
if ischar(body)
|
||||
%NOTE: '' defaults to Matlab's default encoding scheme
|
||||
body = unicode2native(body,in.ENCODING);
|
||||
elseif ~(strcmp(class(body),'uint8') || strcmp(class(body),'int8'))
|
||||
error('Function input: body, should be of class char, uint8, or int8, detected: %s',class(body))
|
||||
end
|
||||
|
||||
urlConnection.setRequestProperty('Content-Length',int2str(length(body)));
|
||||
urlConnection.setDoOutput(true);
|
||||
outputStream = urlConnection.getOutputStream;
|
||||
outputStream.write(body);
|
||||
outputStream.close;
|
||||
else
|
||||
urlConnection.setRequestProperty('Content-Length','0');
|
||||
end
|
||||
|
||||
%==========================================================================
|
||||
% Read the data from the connection.
|
||||
%==========================================================================
|
||||
%This should be done first because it tells us if things are ok or not
|
||||
%NOTE: If there is an error, functions below using urlConnection, notably
|
||||
%getResponseCode, will fail as well
|
||||
try
|
||||
inputStream = urlConnection.getInputStream;
|
||||
isGood = true;
|
||||
catch ME
|
||||
isGood = false;
|
||||
%NOTE: HTTP error codes will throw an error here, we'll allow those for now
|
||||
%We might also get another error in which case the inputStream will be
|
||||
%undefined, those we will throw here
|
||||
inputStream = urlConnection.getErrorStream;
|
||||
|
||||
if isempty(inputStream)
|
||||
msg = ME.message;
|
||||
I = strfind(msg,char([13 10 9])); %see example by setting timeout to 1
|
||||
%Should remove the barf of the stack, at ... at ... at ... etc
|
||||
%Likely that this could be improved ... (generate link with full msg)
|
||||
if ~isempty(I)
|
||||
msg = msg(1:I(1)-1);
|
||||
end
|
||||
fprintf(2,'Response stream is undefined\n below is a Java Error dump (truncated):\n');
|
||||
error(msg)
|
||||
end
|
||||
end
|
||||
|
||||
%POPULATING HEADERS
|
||||
%--------------------------------------------------------------------------
|
||||
allHeaders = struct;
|
||||
allHeaders.Response = {char(urlConnection.getHeaderField(0))};
|
||||
done = false;
|
||||
headerIndex = 0;
|
||||
|
||||
while ~done
|
||||
headerIndex = headerIndex + 1;
|
||||
headerValue = char(urlConnection.getHeaderField(headerIndex));
|
||||
if ~isempty(headerValue)
|
||||
headerName = char(urlConnection.getHeaderFieldKey(headerIndex));
|
||||
headerName = fixHeaderCasing(headerName); %NOT YET FINISHED
|
||||
|
||||
%Important, for name safety all hyphens are replace with underscores
|
||||
headerName(headerName == '-') = '_';
|
||||
if isfield(allHeaders,headerName)
|
||||
allHeaders.(headerName) = [allHeaders.(headerName) headerValue];
|
||||
else
|
||||
allHeaders.(headerName) = {headerValue};
|
||||
end
|
||||
else
|
||||
done = true;
|
||||
end
|
||||
end
|
||||
|
||||
firstHeaders = struct;
|
||||
fn = fieldnames(allHeaders);
|
||||
for iHeader = 1:length(fn)
|
||||
curField = fn{iHeader};
|
||||
firstHeaders.(curField) = allHeaders.(curField){1};
|
||||
end
|
||||
|
||||
status = struct(...
|
||||
'value', urlConnection.getResponseCode(),...
|
||||
'msg', char(urlConnection.getResponseMessage));
|
||||
|
||||
%PROCESSING OF OUTPUT
|
||||
%----------------------------------------------------------
|
||||
byteArrayOutputStream = java.io.ByteArrayOutputStream;
|
||||
% This StreamCopier is unsupported and may change at any time. OH GREAT :/
|
||||
isc = InterruptibleStreamCopier.getInterruptibleStreamCopier;
|
||||
isc.copyStream(inputStream,byteArrayOutputStream);
|
||||
inputStream.close;
|
||||
byteArrayOutputStream.close;
|
||||
|
||||
if in.CAST_OUTPUT
|
||||
charset = '';
|
||||
|
||||
%Extraction of character set from Content-Type header if possible
|
||||
if isfield(firstHeaders,'Content_Type')
|
||||
text = firstHeaders.Content_Type;
|
||||
%Always open to regexp improvements
|
||||
charset = regexp(text,'(?<=charset=)[^\s]*','match','once');
|
||||
end
|
||||
|
||||
if ~isempty(charset)
|
||||
output = native2unicode(typecast(byteArrayOutputStream.toByteArray','uint8'),charset);
|
||||
else
|
||||
output = char(typecast(byteArrayOutputStream.toByteArray','uint8'));
|
||||
end
|
||||
else
|
||||
%uint8 is more useful for later charecter conversions
|
||||
%uint8 or int8 is somewhat arbitary at this point
|
||||
output = typecast(byteArrayOutputStream.toByteArray','uint8');
|
||||
end
|
||||
|
||||
extras = struct;
|
||||
extras.allHeaders = allHeaders;
|
||||
extras.firstHeaders = firstHeaders;
|
||||
extras.status = status;
|
||||
%Gets eventual url even with redirection
|
||||
extras.url = char(urlConnection.getURL);
|
||||
extras.isGood = isGood;
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
function headerNameOut = fixHeaderCasing(headerName)
|
||||
%fixHeaderCasing Forces standard casing of headers
|
||||
%
|
||||
% headerNameOut = fixHeaderCasing(headerName)
|
||||
%
|
||||
% This is important for field access in a structure which
|
||||
% is case sensitive
|
||||
%
|
||||
% Not yet finished.
|
||||
% I've been adding to this function as problems come along
|
||||
|
||||
switch lower(headerName)
|
||||
case 'location'
|
||||
headerNameOut = 'Location';
|
||||
case 'content_type'
|
||||
headerNameOut = 'Content_Type';
|
||||
otherwise
|
||||
headerNameOut = headerName;
|
||||
end
|
||||
end
|
||||
|
||||
%==========================================================================
|
||||
%==========================================================================
|
||||
%==========================================================================
|
||||
|
||||
function urlConnection = getURLConnection(urlChar)
|
||||
%getURLConnection
|
||||
%
|
||||
% urlConnection = getURLConnection(urlChar)
|
||||
|
||||
% Determine the protocol (before the ":").
|
||||
protocol = urlChar(1:find(urlChar==':',1)-1);
|
||||
|
||||
|
||||
% Try to use the native handler, not the ice.* classes.
|
||||
try
|
||||
switch protocol
|
||||
case 'http'
|
||||
%http://www.docjar.com/docs/api/sun/net/www/protocol/http/HttpURLConnection.html
|
||||
handler = sun.net.www.protocol.http.Handler;
|
||||
case 'https'
|
||||
handler = sun.net.www.protocol.https.Handler;
|
||||
end
|
||||
catch ME
|
||||
handler = [];
|
||||
end
|
||||
|
||||
% Create the URL object.
|
||||
try
|
||||
if isempty(handler)
|
||||
url = java.net.URL(urlChar);
|
||||
else
|
||||
url = java.net.URL([],urlChar,handler);
|
||||
end
|
||||
catch ME
|
||||
error('Failure to parse URL or protocol not supported for:\nURL: %s',urlChar);
|
||||
end
|
||||
|
||||
% Get the proxy information using MathWorks facilities for unified proxy
|
||||
% preference settings.
|
||||
mwtcp = com.mathworks.net.transport.MWTransportClientPropertiesFactory.create();
|
||||
proxy = mwtcp.getProxy();
|
||||
|
||||
% Open a connection to the URL.
|
||||
if isempty(proxy)
|
||||
urlConnection = url.openConnection;
|
||||
else
|
||||
urlConnection = url.openConnection(proxy);
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user