Mercurial > octave
changeset 27048:159402e52cfa
New implementation of RESTful web services as part of GSoC 2018 (patch #9795).
* scripts/web/weboptions.m: New function for RESTful web services.
* scripts/web/webread.m: New function for RESTful web services.
* scripts/web/webwrite.m: New function for RESTful web services.
* scripts/web/module.mk: Add new functions to build system.
* scripts/module.mk: Add new module to build system.
* NEWS: Announce new feature.
* libinterp/corefcn/urlwrite.cc (__restful_service__): New function.
* liboctave/util/url-transfer[.cc/.h] (struct weboptions, cookie_jar,
set_header_fields, form_data_post, set_weboptions, http_get, http_post,
http_action): New functions to support the RESTful webservices. Refactor
the functions http_get, http_post, http_action.
author | Sahil Yadav <yadavsahil5198@gmail.com> |
---|---|
date | Mon, 15 Apr 2019 14:03:58 +0900 |
parents | 758063af3d0c |
children | eb522480d44c |
files | NEWS libinterp/corefcn/urlwrite.cc liboctave/util/url-transfer.cc liboctave/util/url-transfer.h scripts/module.mk scripts/web/module.mk scripts/web/weboptions.m scripts/web/webread.m scripts/web/webwrite.m |
diffstat | 9 files changed, 914 insertions(+), 36 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Fri Apr 12 13:15:34 2019 -0700 +++ b/NEWS Mon Apr 15 14:03:58 2019 +0900 @@ -38,6 +38,11 @@ ### Matlab compatibility +- Complex RESTful web services can now be accessed by the `webread` and + `webwrite` functions alongside with the `weboptions` structure. One major + feature is the support for cookies to enable RESTful communication with the + web service. + - The interpreter now supports handles to nested functions. - The graphics properties `"LineWidth"` and `"MarkerSize"` are now
--- a/libinterp/corefcn/urlwrite.cc Fri Apr 12 13:15:34 2019 -0700 +++ b/libinterp/corefcn/urlwrite.cc Mon Apr 15 14:03:58 2019 +0900 @@ -47,6 +47,7 @@ #include "oct-map.h" #include "oct-refcount.h" #include "ov-cell.h" +#include "ov-classdef.h" #include "ovl.h" #include "pager.h" #include "unwind-prot.h" @@ -680,7 +681,8 @@ std::string target; if (nargin == 3 && ! args(2).isempty ()) - target = args(2).xstring_value ("__ftp_mget__: TARGET must be a string") + octave::sys::file_ops::dir_sep_str (); + target = args(2).xstring_value ("__ftp_mget__: TARGET must be a string") + + octave::sys::file_ops::dir_sep_str (); octave::url_handle_manager& uhm = interp.get_url_handle_manager (); @@ -738,3 +740,93 @@ return ovl (); } + +DEFUN (__restful_service__, args, nargout, + doc: /* -*- texinfo -*- +@deftypefn {} {@var{response} =} __restful_service__ (@var{url}, @var{param}, @var{weboptions}) +Undocumented internal function. +@end deftypefn */) +{ + int nargin = args.length (); + + if (nargin < 1) + print_usage (); + + std::string url = args(0).xstring_value ("__restful_service__: URL must be a string"); + + std::ostringstream content; + + octave::url_transfer url_xfer (url, content); + + if (! url_xfer.is_valid ()) + error ("support for URL transfers was disabled when Octave was built"); + + Array<std::string> param = args(1).cellstr_value (); + + std::string data, method; + + struct octave::weboptions options; + + octave::cdef_object object = args (nargin - 1).classdef_object_value () + -> get_object (); + + // We could've used object.map_value () instead to return a map but that + // shows a warning about about overriding access restrictions. + // Nevertheless, we are keeping checking that here if the keys are not + // equal to "delete" and "display", getting away with the warning. + string_vector keys = object.map_keys (); + + for (int i = 0; i < keys.numel (); i++) + { + if (keys(i) == "Timeout") + { + float timeout = object.get (keys(i)).float_value (); + options.Timeout = static_cast<long>(timeout * 1000); + } + + if (keys(i) == "HeaderFields") + { + options.HeaderFields = object.get (keys(i)).cellstr_value (); + } + + // FIXME: 'delete' and 'display', auto-generated, probably by cdef_object + // class? Remaining fields have already been adjusted elsewhere in the + // m-script. Set 'value' as the Value of the Key wherever it's a string. + if (keys(i) != "Timeout" && keys(i) != "HeaderFields" + && keys(i) != "delete" && keys(i) != "display") + { + std::string value = object.get (keys(i)).string_value (); + + if (keys(i) == "UserAgent") + options.UserAgent = value; + + if (keys(i) == "Username") + options.Username = value; + + if (keys(i) == "Password") + options.Password = value; + + if (keys(i) == "ContentReader") + // Unimplemented. Only for MATLAB compatibility. + options.ContentReader = ""; + + if (keys(i) == "RequestMethod") + method = value; + + if (keys(i) == "ArrayFormat") + options.ArrayFormat = value; + + if (keys(i) == "CertificateFilename") + options.CertificateFilename = ""; + } + } + + url_xfer.set_weboptions (options); + + url_xfer.http_action (param, method); + + if (nargout < 2 && ! url_xfer.good ()) + error ("__restful_service__: %s", url_xfer.lasterror ().c_str ()); + + return ovl (content.str ()); +}
--- a/liboctave/util/url-transfer.cc Fri Apr 12 13:15:34 2019 -0700 +++ b/liboctave/util/url-transfer.cc Mon Apr 15 14:03:58 2019 +0900 @@ -40,6 +40,7 @@ #include "oct-env.h" #include "unwind-prot.h" #include "url-transfer.h" +#include "version.h" #if defined (HAVE_CURL) # include <curl/curl.h> @@ -643,40 +644,154 @@ void http_get (const Array<std::string>& param) { - url = host_or_url; - - std::string query_string = form_query_string (param); - - if (! query_string.empty ()) - url += '?' + query_string; - - SETOPT (CURLOPT_URL, url.c_str ()); - - perform (); + http_action (param, "get"); } void http_post (const Array<std::string>& param) { - SETOPT (CURLOPT_URL, host_or_url.c_str ()); - - std::string query_string = form_query_string (param); - - SETOPT (CURLOPT_POSTFIELDS, query_string.c_str ()); - - perform (); + http_action (param, "post"); } void http_action (const Array<std::string>& param, const std::string& action) { + url = host_or_url; + + std::string query_string; + + query_string = form_query_string (param); + if (action.empty () || action == "get") - http_get (param); - else if (action == "post") - http_post (param); + { + if (! query_string.empty ()) + url += '?' + query_string; + + SETOPT (CURLOPT_URL, url.c_str ()); + } + else if (action == "post" || action == "put" || action == "delete") + { + SETOPT (CURLOPT_POSTFIELDS, query_string.c_str ()); + + if (action == "put") + { + SETOPT (CURLOPT_CUSTOMREQUEST, "PUT"); + } + + if (action == "delete") + { + SETOPT (CURLOPT_CUSTOMREQUEST, "DELETE"); + } + + SETOPT (CURLOPT_URL, url.c_str ()); + } else { ok = false; errmsg = "curl_transfer: unknown http action"; } + + if (ok) + perform (); + } + + void cookie_jar (const std::string& filename) + { + SETOPT (CURLOPT_COOKIEJAR, filename.c_str ()); + + SETOPT (CURLOPT_COOKIEFILE, filename.c_str ()); + } + + // Sets the header fields in a transfer. Input should be in the form + // of an array of strings with pairs of keys and values together + void set_header_fields (const Array<std::string>& param) + { + struct curl_slist *slist = nullptr; + + unwind_protect frame; + + frame.add_fcn (curl_slist_free_all, slist); + + if (param.numel () >= 2) + { + for (int i = 0; i < param.numel (); i += 2) + { + std::string header = param(i) + ": " + param(i+1); + + slist = curl_slist_append (slist, header.c_str ()); + } + + SETOPT (CURLOPT_HTTPHEADER, slist); + } + } + + // Sets and sends the form data associated with a transfer. + // Input should be an array of strings with each pair of strings + // corresponding to the fieldname and it's value. + // To attach a file, you should use 'file' as the fieldname with the + // path of the file as its value. + void form_data_post (const Array<std::string>& param) + { + struct curl_httppost *post = NULL, *last = NULL; + + SETOPT (CURLOPT_URL, host_or_url.c_str ()); + + unwind_protect frame; + + frame.add_fcn (curl_formfree, post); + + if (param.numel () >= 2) + { + for (int i = 0; i < param.numel (); i += 2) + { + std::string name = param(i); + std::string data = param(i+1); + + if (name == "file") + curl_formadd(&post, &last, CURLFORM_COPYNAME, name.c_str (), + CURLFORM_FILE, data.c_str (), CURLFORM_END); + else + curl_formadd(&post, &last, CURLFORM_COPYNAME, name.c_str (), + CURLFORM_COPYCONTENTS, data.c_str (), CURLFORM_END); + } + + SETOPT (CURLOPT_HTTPPOST, post); + } + + perform (); + } + + // Sets the various options specified by weboptions object. + void set_weboptions (const struct weboptions& options) + { + // Remove this after completing fixmes. + std::string temp = ""; + + set_header_fields (options.HeaderFields); + + SETOPT (CURLOPT_TIMEOUT, options.Timeout); + + if (! options.UserAgent.empty ()) + SETOPT (CURLOPT_USERAGENT, options.UserAgent.c_str ()); + + if (! options.Username.empty ()) + { + if (! options.Password.empty ()) + SETOPT (CURLOPT_USERPWD, (options.Username + ":" + options.Password) + .c_str ()); + else + SETOPT (CURLOPT_USERPWD, (options.Username + ":").c_str ()); + } + + // Unimplemented. Only for MATLAB complatiblity. + if (! options.ContentReader.empty ()) + temp = options.ContentReader; + + // Unimplemented. Only for MATLAB complatiblity. + if (! options.ArrayFormat.empty ()) + temp = options.ArrayFormat; + + // Unimplemented. Only for MATLAB complatiblity. + if (! options.CertificateFilename.empty ()) + temp = options.CertificateFilename; } private: @@ -736,6 +851,17 @@ // instead. SETOPT (CURLOPT_FTP_USE_EPSV, false); + // Set the user agent for the curl request + // Needed by mediaWiki API. + curl_version_info_data * data = curl_version_info(CURLVERSION_NOW); + const char *lib_ver = data->version; + std::string user_agent = + "GNU Octave/" + std::string (OCTAVE_VERSION) + + " (https://www.gnu.org/software/octave/ ; help@octave.org) libcurl/" + + std::string (lib_ver); + + SETOPT (CURLOPT_USERAGENT, user_agent.c_str ()); + SETOPT (CURLOPT_NOPROGRESS, true); SETOPT (CURLOPT_FAILONERROR, true); @@ -747,25 +873,26 @@ { std::ostringstream query; - for (int i = 0; i < param.numel (); i += 2) - { - std::string name = param(i); - std::string text = param(i+1); + if (param.numel () >= 2) + for (int i = 0; i < param.numel (); i += 2) + { + std::string name = param(i); + std::string text = param(i+1); - // Encode strings. - char *enc_name = curl_easy_escape (curl, name.c_str (), - name.length ()); - char *enc_text = curl_easy_escape (curl, text.c_str (), - text.length ()); + // Encode strings. + char *enc_name = curl_easy_escape (curl, name.c_str (), + name.length ()); + char *enc_text = curl_easy_escape (curl, text.c_str (), + text.length ()); - query << enc_name << '=' << enc_text; + query << enc_name << '=' << enc_text; - curl_free (enc_name); - curl_free (enc_text); + curl_free (enc_name); + curl_free (enc_text); - if (i < param.numel ()-1) - query << '&'; - } + if (i < param.numel ()-2) + query << '&'; + } query.flush ();
--- a/liboctave/util/url-transfer.h Fri Apr 12 13:15:34 2019 -0700 +++ b/liboctave/util/url-transfer.h Mon Apr 15 14:03:58 2019 +0900 @@ -41,6 +41,19 @@ namespace octave { + struct weboptions + { + std::string UserAgent; + long Timeout; + std::string Username; + std::string Password; + Array<std::string> HeaderFields; + std::string ContentReader; + std::string RequestMethod; + std::string ArrayFormat; + std::string CertificateFilename; + }; + class OCTAVE_API base_url_transfer @@ -142,6 +155,13 @@ virtual void http_action (const Array<std::string>& /* param */, const std::string& /* action */) { } + virtual void cookie_jar (const std::string& /* filename */) { } + + virtual void set_header_fields (const Array<std::string>& /* param */) { } + + virtual void form_data_post (const Array<std::string>& /* param */) { } + + virtual void set_weboptions (const struct weboptions& /* param */) { } protected: // Host for ftp transfers or full URL for http requests. @@ -255,6 +275,25 @@ rep->http_action (param, action); } + void cookie_jar (const std::string& filename) + { + rep->cookie_jar (filename); + } + + void set_header_fields (const Array<std::string>& param) + { + rep->set_header_fields (param); + } + + void form_data_post (const Array<std::string>& param) + { + rep->form_data_post (param); + } + + void set_weboptions (const struct weboptions& param) + { + rep->set_weboptions (param); + } private: std::shared_ptr<base_url_transfer> rep;
--- a/scripts/module.mk Fri Apr 12 13:15:34 2019 -0700 +++ b/scripts/module.mk Mon Apr 15 14:03:58 2019 +0900 @@ -39,6 +39,7 @@ include %reldir%/strings/module.mk include %reldir%/testfun/module.mk include %reldir%/time/module.mk +include %reldir%/web/module.mk ## include %reldir%/@ftp/module.mk ## The include above fails because Automake cannot process the '@' character.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/web/module.mk Mon Apr 15 14:03:58 2019 +0900 @@ -0,0 +1,16 @@ +FCN_FILE_DIRS += scripts/web + +%canon_reldir%_FCN_FILES = \ + %reldir%/weboptions.m \ + %reldir%/webread.m \ + %reldir%/webwrite.m + +%canon_reldir%dir = $(fcnfiledir)/web + +%canon_reldir%_DATA = $(%canon_reldir%_FCN_FILES) + +FCN_FILES += $(%canon_reldir%_FCN_FILES) + +PKG_ADD_FILES += %reldir%/PKG_ADD + +DIRSTAMP_FILES += %reldir%/$(octave_dirstamp) \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/web/weboptions.m Mon Apr 15 14:03:58 2019 +0900 @@ -0,0 +1,386 @@ +## Copyright (C) 2018-2019 Sahil Yadav <yadavsahil5198@gmail.com> +## +## This file is part of Octave. +## +## Octave is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## <https://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {} {@var{output} =} weboptions () +## @deftypefnx {} {@var{output} =} weboptions (@var{name1}, @var{value1},...) +## +## Specify parameters for RESTful web services. +## +## weboptions returns a default weboptions object to specify parameters for a +## request to a web service. A weboptions object can be an optional input +## argument to the webread, websave, and webwrite functions. +## +## You can specify several name and value pair arguments in any order as +## @var{name1}, @var{value1}, @var{name2}, @var{value2}, etc. +## +## The names should be EXACTLY the same as specified in the help text. +## +## The following options are available: +## @itemize @bullet +## @item +## @samp{CharacterEncoding} --- Specify the character encoding of the data: +## +## @samp{auto} (default), @samp{UTF-8}, @samp{US-ASCII} +## @samp{auto} takes the value by determining the content-type of the data. +## +## @item +## @samp{UserAgent} --- Specify the User agent for the connection. +## +## Default value is @samp{GNU Octave/version}, where @samp{version} is the +## current Octave's version of user. +## +## @item +## @samp{Timeout} --- Specify the timeout value for the connection in seconds. +## +## Default is 10 seconds. @samp{Inf} is not supported currently. +## +## @item +## @samp{Username} --- User identifier for a basic HTTP connection. +## +## Default is NULL. It must be a string +## +## @item +## @samp{Password} --- User authentication password for HTTP connection. +## +## Default is NULL. It must be a string or character vector. +## Programming Note: If you display a weboptions object with the Password +## property set, the value is displayed as a character vector containing ‘*’. +## However, the object stores the value of the Password property as plain text. +## +## @item +## @samp{KeyName} --- Specify the name of an additional key to be added to +## the HTTP request header. It should be coupled with @samp{KeyValue}. It +## must be a string or character vector. +## +## @item +## @samp{KeyValue} --- Specify the value of the key @samp{KeyName}. +## +## @samp{KeyName} must be present in order to assign this field. +## +## @item +## @samp{HeaderFields} --- Specify the header fields for the connection. +## +## Names and values of header fields, specified as an m-by-2 array of strings +## or cell array of character vectors to add to the HTTP request header. +## HeaderFields@{i,1@} is the name of a field and HeaderFields@{i,2@} is its +## value. +## @example +## @group +## weboptions ("HeaderFields", @{"Content-Length" "78";"Content-Type" "application/json"@}) +## creates a weboptions object that contains two header fields: Content-Length +## with value 78 and Content-Type with value application/json. +## @end group +## @end example +## +## @item +## @samp{ContentType} --- Specify the content type of the data. +## +## The following values are available: +## @samp{auto}, @samp{text}, @samp{json} +## +## Default is @samp{auto}. It automatically determines the content type. +## All other formats like @samp{audio}, @samp{binary}, etc. available in +## @sc{matlab} are not yet supported. +## +## @item +## @samp{ContentReader} --- Not yet implemented. Only for @sc{matlab} +## compatibility. +## +## @item +## @samp{MediaType} --- Not yet implemented. Only for @sc{matlab} +## compatibility. +## +## @item +## @samp{RequestMethod} --- Specifies the type of request to be made. +## +## The following methods are available: +## @samp{get}, @samp{put}, @samp{post}, @samp{delete}, @samp{patch} +## +## webread and websave use the HTTP GET method. webwrite uses the HTTP POST +## method as default. +## +## @item +## @samp{ArrayFormat} -- Not yet implemented. Only for @sc{matlab} +## compatibility. +## +## @item +## @samp{CertificateFilename} --- Not yet implemented. Only for @sc{matlab} +## compatibility. +## @end itemize +## +## @seealso{webwrite, webread, websave} +## @end deftypefn + +classdef weboptions < handle + properties + CharacterEncoding = "auto"; + UserAgent = ["GNU Octave/", version]; + Timeout = 10; + Username = ""; + Password = ""; + KeyName = ""; + KeyValue = ""; + HeaderFields = {}; + ContentType = "auto"; + ContentReader = ""; + MediaType = "auto"; + RequestMethod = "auto"; + ArrayFormat = "csv"; + CertificateFilename = ""; + endproperties + + methods + function f = weboptions (varargin) + if (rem (numel (varargin), 2) != 0) + error ("weboptions: Invalid number of arguments"); + else + h = cell2struct (varargin(2:2:end), varargin(1:2:end), 2); + if (numfields (h) > 14) + error ("weboptions: Invalid number of arguments"); + endif + + if (isfield (h, "CharacterEncoding")) + f.CharacterEncoding = h.CharacterEncoding; + h = rmfield (h, "CharacterEncoding"); + endif + + if (isfield (h, "UserAgent")) + f.UserAgent = h.UserAgent; + h = rmfield (h, "UserAgent"); + endif + + if (isfield (h, "Timeout")) + f.Timeout = h.Timeout; + h = rmfield (h, "Timeout"); + endif + + if (isfield (h, "Username")) + f.Username = h.Username; + h = rmfield (h, "Username"); + endif + + if (isfield (h, "Password")) + f.Password = h.Password; + h = rmfield (h, "Password"); + endif + + if (isfield (h, "KeyName")) + f.KeyName = h.KeyName; + h = rmfield (h, "KeyName"); + endif + + if (isfield (h, "KeyValue")) + f.KeyValue = h.KeyValue; + h = rmfield (h, "KeyValue"); + endif + + if (isfield (h, "HeaderFields")) + f.HeaderFields = h.HeaderFields; + h = rmfield (h, "HeaderFields"); + endif + + if (isfield (h, "ContentType")) + f.ContentType = h.ContentType; + h = rmfield (h, "ContentType"); + endif + + if (isfield (h, "ContentReader")) + f.ContentReader = h.ContentReader; + h = rmfield (h, "ContentReader"); + endif + + if (isfield (h, "MediaType")) + f.MediaType = h.MediaType; + h = rmfield (h, "MediaType"); + endif + + if (isfield (h, "RequestMethod")) + f.RequestMethod = h.RequestMethod; + h = rmfield (h, "RequestMethod"); + endif + + if (isfield (h, "ArrayFormat")) + f.ArrayFormat = h.ArrayFormat; + h = rmfield (h, "ArrayFormat"); + endif + + if (isfield (h, "CertificateFilename")) + f.CertificateFilename = h.CertificateFilename; + h = rmfield (h, "CertificateFilename"); + endif + + if (! isempty (fieldnames (h))) + field = fieldnames (h){1}; + error (["weboptions: Undefined field " field "."]); + endif + endif + endfunction + + function f = set.CharacterEncoding (f, value) + if (! any (strcmpi (value, {"UTF-8", 'US-ASCII', "auto"}))); + error ("weboptions: Invalid CharacterEncoding value"); + else + f.CharacterEncoding = value; + endif + endfunction + + function f = set.UserAgent (f, value) + if (! ischar (value) && ! isvector (value)); + error ("weboptions: UserAgent must be a String"); + else + f.UserAgent = value; + endif + endfunction + + function f = set.Timeout (f, value) + if (! isreal (value) || ! isscalar (value) + || floor(value) != value || value < 0) + error ("weboptions: Invalid Timeout value"); + else + f.Timeout = value; + endif + endfunction + + function f = set.Username (f, value) + if (! ischar (value) && ! isvector (value)) + error ("weboptions: Username must be a string"); + else + f.Username = value; + endif + endfunction + + function f = set.Password (f, value) + if (! ischar (value) && ! isvector (value)) + error ("weboptions: Password must be a string"); + else + f.Password = value; + endif + endfunction + + function f = set.KeyName (f, value) + if (! ischar (value) && ! isvector (value)) + error ("weboptions: Invalid KeyName value"); + else + f.KeyName = value; + endif + endfunction + + function f = set.KeyValue (f, value) + if (isempty (f.KeyName) && ! isempty (value)) + error ("weboptions: Field KeyName empty. Cannot set KeyValue."); + else + f.KeyValue = value; + endif + endfunction + + function f = set.HeaderFields (f, value) + if (! isempty (value)) + if (! iscellstr (value) || ! ismatrix (value)) + error ("weboptions: HeaderFields must be array of strings or a cell array"); + elseif (size (value)(2) != 2) + error ("weboptions: HeaderFields must be of size m-by-2"); + endif + endif + f.HeaderFields = value; + endfunction + + function f = set.ContentType (f, value) + if (! isempty (value)) + if (! any (strcmpi (value, {"json", "text", "auto"}))) + error ("weboptions: Invalid ContentType value"); + endif + endif + f.ContentType = value; + endfunction + + function f = set.ContentReader (f, value) + f.ContentReader = value; + endfunction + + function f = set.MediaType (f, value) + f.MediaType = value; + endfunction + + function f = set.RequestMethod (f, value) + if (! isempty (value)) + if (! any (strcmpi (value, {"auto", "get", "put", "post",... + "delete", "patch"}))) + error ("weboptions: Invalid RequestMethod value"); + endif + endif + f.RequestMethod = value; + endfunction + + function f = set.ArrayFormat (f, value) + if (! isempty (value)) + if (! any (strcmpi (value, {"csv", "json",... + "repeating", "php"}))) + error ("weboptions: Invalid ArrayFormat value"); + endif + endif + f.ArrayFormat = value; + endfunction + + function f = set.CertificateFilename (f, value) + f.CertificateFilename = value; + endfunction + + function display (f) + Timeout = int2str (f.Timeout); + Password = repmat ("*", 1, numel (num2str (f.Password))); + + if (! isempty (f.ContentReader)) + f.ContentReader + ContentReader = ['["', strjoin(f.ContentReader, '", "'), '"]']; + else + ContentReader = "[]"; + endif + + if (! isempty (f.HeaderFields)) + HeaderFields = ['{"', strjoin(f.HeaderFields, '", "'), '"}']; + else + HeaderFields = "{}"; + endif + + if (! isempty (f.KeyValue)) + KeyValue = num2str (f.KeyValue); + else + KeyValue = "\'\'"; + endif + + printf ("%s =", inputname (1)); + output = ["\n\n weboptions with properties: \n",... + "\n CharacterEncoding: \'", f.CharacterEncoding, "\'",... + "\n UserAgent: \'", f.UserAgent, "\'",... + "\n Timeout: " , Timeout, "",... + "\n Username: \'", f.Username, "\'",... + "\n Password: \'", Password, "\'",... + "\n KeyName: \'", f.KeyName, "\'",... + "\n KeyValue: " , KeyValue,... + "\n ContentType: \'", f.ContentType, "\'",... + "\n ContentReader: " , ContentReader,... + "\n MediaType: \'", f.MediaType, "\'",... + "\n RequestMethod: \'", f.RequestMethod, "\'",... + "\n ArrayFormat: \'", f.ArrayFormat, "\'",... + "\n HeaderFields: " , HeaderFields,... + "\n CertificateFilename: \'", f.CertificateFilename, "\'\n"]; + disp (output); + endfunction + endmethods +endclassdef
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/web/webread.m Mon Apr 15 14:03:58 2019 +0900 @@ -0,0 +1,104 @@ +## Copyright (C) 2018-2019 Sahil Yadav <yadavsahil5198@gmail.com> +## +## This file is part of Octave. +## +## Octave is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## <https://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {} {@var{response} =} webread (@var{url}) +## @deftypefnx {} {@var{response} =} webread (@var{url}, @var{name1}, @var{value1},...) +## @deftypefnx {} {@var{response} =} webread (..., @var{options}) +## +## Read content from RESTful web service. +## +## Reads content from the web service specified by @var{url} and returns the +## content in @var{response}. +## +## If the pairs @var{name1}, @var{value1}... are given, append these key-value +## pairs of query parameters to @var{url}. To put a query into the body of the +## message, use @code{webwrite}. The web service defines the query parameters. +## +## @var{options} is a @code{weboptions} object to be used to add other HTTP +## request options. You can use this option with any of the input arguments of +## the previous syntax. +## +## See help text for @code{weboptions} to see the complete list of supported +## HTTP operations. +## +## @seealso{weboptions, webwrite, websave} +## @end deftypefn + +function response = webread (url, varargin) + + if (nargin < 1) + print_usage(); + endif + + if (! (ischar (url) && isvector (url))) + error ("webread: URL must be a string"); + endif + + has_weboptions = false; + options = weboptions; + + if (numel (varargin) > 0) + if (isa (varargin{end}, "weboptions")) + has_weboptions = true; + options = varargin{end}; + endif + endif + + if (strcmp (options.MediaType, "auto")) + options.MediaType = "application/x-www-form-urlencoded"; + endif + + ## If MediaType is set by the user, append it to other headers. + if (! strcmp (options.CharacterEncoding, "auto")) + options.HeaderFields{end+1,1} = "Content-Type"; + options.HeaderFields{end,2} = [options.MediaType,... + "; charset=", options.CharacterEncoding]; + endif + + if (! isempty (options.KeyName)) + options.HeaderFields{end+1,1} = options.KeyName; + options.HeaderFields{end,2} = options.KeyValue; + endif + + if (strcmp (options.RequestMethod, "auto")) + options.RequestMethod = "get"; + endif + + ## Flatten the cell array because the internal processing takes place on + ## a flattened array. + options.HeaderFields = options.HeaderFields(:)'; + + if (nargin == 1 || (nargin == 2 && has_weboptions)) + response = __restful_service__ (url, cell, options); + elseif (rem (numel (varargin), 2) == 1 && has_weboptions) + if (iscellstr (varargin(1:end-1))) + response = __restful_service__ (url, varargin(1:end-1), options); + else + error ("webread: Keys and Values must be string."); + endif + elseif (rem (numel (varargin), 2) == 0 && ! has_weboptions) + if (iscellstr (varargin)) + response = __restful_service__ (url, varargin, options); + else + error ("webread: Keys and Values must be string."); + endif + else + error ("webread: Wrong input arguments"); + endif +endfunction
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scripts/web/webwrite.m Mon Apr 15 14:03:58 2019 +0900 @@ -0,0 +1,108 @@ +## Copyright (C) 2018-2019 Sahil Yadav <yadavsahil5198@gmail.com> +## +## This file is part of Octave. +## +## Octave is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## <https://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {} {@var{response} =} webwrite (@var{url}, @var{name1}, @var{value1},...) +## @deftypefnx {} {@var{response} =} webwrite (@var{url}, @var{data}) +## @deftypefnx {} {@var{response} =} webwrite (..., @var{options}) +## +## Write data to RESTful web services. +## +## Writes content to web service specified by @var{url} and returns the +## response in @var{response}. +## +## All pairs @var{name1}, @var{value1}... are given, adds these key-value +## pairs of query parameters to the body of request method (@code{get}, +## @code{post}, @code{put}, etc). +## +## @var{options} is a @code{weboptions} object to be used to add other HTTP +## request options. You can use this option with any of the input arguments of +## the previous syntax. +## +## See @code{help weboptions} for a complete list of supported HTTP options. +## +## @seealso{weboptions, webread, websave} +## @end deftypefn + +function response = webwrite (url, varargin) + + if (numel (varargin) < 1) + print_usage(); + endif + + if (! (ischar (url) && isvector (url))) + error ("webwrite: URL must be a string"); + endif + + options = weboptions; + has_weboptions = false; + + if (isa (varargin{end}, "weboptions")) + has_weboptions = true; + options = varargin{end}; + endif + + if (strcmp (options.MediaType, "auto")) + options.MediaType = "application/x-www-form-urlencoded"; + endif + + ## If MediaType is set by the user, append it to other headers. + if (! strcmp (options.CharacterEncoding, "auto")) + options.HeaderFields{end+1, 1} = "Content-Type"; + options.HeaderFields{end, 2} = [options.MediaType,... + "; charset=", options.CharacterEncoding]; + endif + + if (! isempty (options.KeyName)) + options.HeaderFields{end+1, 1} = options.KeyName; + options.HeaderFields{end, 2} = options.KeyValue; + endif + + if (strcmp (options.RequestMethod, "auto")) + options.RequestMethod = "post"; + endif + + ## Flatten the cell array because the internal processing takes place on + ## a flattened array. + options.HeaderFields = options.HeaderFields(:)'; + + if (numel (varargin) == 2) + if ((ischar (varargin{1}) && isvector (varargin{1})) && has_weboptions) + param = strsplit (varargin{1}, {"=", "&"}); + response = __restful_service__ (url, param, options); + elseif (! has_weboptions && iscellstr (varargin)) + response = __restful_service__ (url, varargin, options); + else + error ("webwrite: data should be a character array or string."); + endif + elseif (rem (numel (varargin), 2) == 1 && has_weboptions) + if (iscellstr (varargin(1:end-1))) + response = __restful_service__ (url, varargin(1:end-1), options); + else + error ("webwrite: Keys and Values must be string."); + endif + elseif (rem (numel (varargin), 2) == 0 && ! has_weboptions) + if (iscellstr (varargin)) + response = __restful_service__ (url, varargin, options); + else + error ("webwrite: Keys and Values must be string."); + endif + else + error ("webwrite: Wrong input arguments"); + endif +endfunction