Hum, CI is still broken. https://builds.apache.org/job/Qpid-proton-j/org.apache.qpid $proton-tests/683/testReport/org.apache.qpid.proton/JythonTest/test/
Somehow my trivial re-org of the URL tests in python has caused the entire Jython test harness to explode in flight. It works fine on my box in python and Jython, and it works on CI in regular python - so maybe another Java 6 issue? Will I lose my mind if I try to install Java 6 on Fedora and get proton to use it? On Thu, 2014-09-25 at 13:59 -0400, Alan Conway wrote: > On Thu, 2014-09-25 at 15:59 +0100, Robbie Gemmell wrote: > > On 25 September 2014 15:00, Alan Conway <acon...@redhat.com> wrote: > > > > > On Wed, 2014-09-24 at 12:19 +0100, Robbie Gemmell wrote: > > > > The tests are now running again, but a couple of the URL tests still > > > > seem > > > > to be failing on the CI job: > > > > > > > https://builds.apache.org/view/M-R/view/Qpid/job/Qpid-proton-j/lastBuild/org.apache.qpid$proton-tests/testReport/ > > > > > > > > > > They are all failing with: > > > Not a valid port number or service name: 'amqps' > > > > > > Could this be a configuration problem on the CI machine, i.e. missing an > > > 'amqps' entry in /etc/services? Can I get access to the CI machine to > > > poke around and see what's up? > > > > > > > Almost certainly no, only the core maintainers are allowed shell access as > > far as I've seen. You can ask on bui...@apache.org for those with access to > > check things out and report back and see what happens. > > > > I think the CI instances will be running Ubuntu 12.04 or 14.04 LTS. For > > giggles, I dug out an ooooold Ubuntu VM with Java 6 on it and tried the > > tests, which failed, and it indeed has no amqp[s] entry in /etc/services > > file so that could well be it. > > Thanks for checking that out! I have hacked the tests to skip tests for > 'amqps' if it is not recognized. Poke me if there are still failures. > > ------------------------------------------------------------------------ > r1627577 | aconway | 2014-09-25 13:59:17 -0400 (Thu, 25 Sep 2014) | 5 > lines > > NO-JIRA: Fix URL test to skip 'amqps' tests if 'amqps' is not recognized > as a service name. > > On some older Ubuntu with Java 6, 'amqps' is not recognized as a service > name so > skip those tests in that case. > > ------------------------------------------------------------------------ > > > > > > > The URL code uses socket.getservbyname() to look up service names. Is > > > there a more portable way to do it? > > > > > > > No idea I'm afraid. > > > > > > > > > > Cheers, > > > Alan. > > > > > > > As mentioned in my other post about a timeline for dropping Java6 > > > support, > > > > they seem to work on Java8 (havent tried Java7). > > > > > > > > Robbie > > > > > > > > On 22 September 2014 21:14, Alan Conway <acon...@redhat.com> wrote: > > > > > > > > > My bad, didn't run the java tests. Will fix ASAP and then give myself > > > > > a > > > > > flogging. > > > > > > > > > > On Mon, 2014-09-22 at 19:50 +0100, Robbie Gemmell wrote: > > > > > > This seems to have broken the Java test runs: > > > > > > > > > > > > https://builds.apache.org/view/M-R/view/Qpid/job/Qpid-proton-j/664/ > > > > > > > > > > > > > > > > > > > > > > > > On 19 September 2014 22:00, <acon...@apache.org> wrote: > > > > > > > > > > > > > Author: aconway > > > > > > > Date: Fri Sep 19 21:00:50 2014 > > > > > > > New Revision: 1626329 > > > > > > > > > > > > > > URL: http://svn.apache.org/r1626329 > > > > > > > Log: > > > > > > > PROTON-693: Python Url class to wrap C function pni_parse_url > > > > > > > > > > > > > > It was pointed out that pni_parse_url is an internal function and > > > the > > > > > > > interface > > > > > > > is not suitable for public API. > > > > > > > > > > > > > > Rewrote the URL parser as a proper swigable C API pn_url_*. This > > > gets > > > > > rid > > > > > > > of the > > > > > > > need for previous swig insanity and is cleaner all round. > > > > > > > > > > > > > > Internally still uses the pni_parse_url parser, we can clean that > > > up > > > > > later. > > > > > > > > > > > > > > Added: > > > > > > > qpid/proton/trunk/proton-c/include/proton/url.h > > > > > > > qpid/proton/trunk/proton-c/src/url.c > > > > > > > Modified: > > > > > > > qpid/proton/trunk/proton-c/CMakeLists.txt > > > > > > > qpid/proton/trunk/proton-c/bindings/perl/perl.i > > > > > > > qpid/proton/trunk/proton-c/bindings/php/php.i > > > > > > > qpid/proton/trunk/proton-c/bindings/python/cproton.i > > > > > > > qpid/proton/trunk/proton-c/bindings/python/proton.py > > > > > > > qpid/proton/trunk/proton-c/bindings/ruby/ruby.i > > > > > > > qpid/proton/trunk/proton-c/include/proton/cproton.i > > > > > > > qpid/proton/trunk/tests/python/proton_tests/url.py > > > > > > > > > > > > > > Modified: qpid/proton/trunk/proton-c/CMakeLists.txt > > > > > > > URL: > > > > > > > > > > > > > > > http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/CMakeLists.txt?rev=1626329&r1=1626328&r2=1626329&view=diff > > > > > > > > > > > > > > > > > > > > > > ============================================================================== > > > > > > > --- qpid/proton/trunk/proton-c/CMakeLists.txt (original) > > > > > > > +++ qpid/proton/trunk/proton-c/CMakeLists.txt Fri Sep 19 21:00:50 > > > 2014 > > > > > > > @@ -270,6 +270,7 @@ set (qpid-proton-core > > > > > > > src/object/iterator.c > > > > > > > > > > > > > > src/util.c > > > > > > > + src/url.c > > > > > > > src/error.c > > > > > > > src/buffer.c > > > > > > > src/parser.c > > > > > > > > > > > > > > Modified: qpid/proton/trunk/proton-c/bindings/perl/perl.i > > > > > > > URL: > > > > > > > > > > > > > > > http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/perl/perl.i?rev=1626329&r1=1626328&r2=1626329&view=diff > > > > > > > > > > > > > > > > > > > > > > ============================================================================== > > > > > > > --- qpid/proton/trunk/proton-c/bindings/perl/perl.i (original) > > > > > > > +++ qpid/proton/trunk/proton-c/bindings/perl/perl.i Fri Sep 19 > > > 21:00:50 > > > > > > > 2014 > > > > > > > @@ -8,6 +8,7 @@ > > > > > > > #include <proton/messenger.h> > > > > > > > #include <proton/ssl.h> > > > > > > > #include <proton/driver_extras.h> > > > > > > > +#include <proton/url.h> > > > > > > > %} > > > > > > > > > > > > > > %include <cstring.i> > > > > > > > > > > > > > > Modified: qpid/proton/trunk/proton-c/bindings/php/php.i > > > > > > > URL: > > > > > > > > > > > > > > > http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/php/php.i?rev=1626329&r1=1626328&r2=1626329&view=diff > > > > > > > > > > > > > > > > > > > > > > ============================================================================== > > > > > > > --- qpid/proton/trunk/proton-c/bindings/php/php.i (original) > > > > > > > +++ qpid/proton/trunk/proton-c/bindings/php/php.i Fri Sep 19 > > > 21:00:50 > > > > > 2014 > > > > > > > @@ -29,6 +29,7 @@ > > > > > > > %header %{ > > > > > > > /* Include the headers needed by the code in this wrapper file */ > > > > > > > #include <proton/types.h> > > > > > > > +#include <proton/url.h> > > > > > > > #include <proton/message.h> > > > > > > > #include <proton/driver.h> > > > > > > > #include <proton/driver_extras.h> > > > > > > > > > > > > > > Modified: qpid/proton/trunk/proton-c/bindings/python/cproton.i > > > > > > > URL: > > > > > > > > > > > > > > > http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/python/cproton.i?rev=1626329&r1=1626328&r2=1626329&view=diff > > > > > > > > > > > > > > > > > > > > > > ============================================================================== > > > > > > > --- qpid/proton/trunk/proton-c/bindings/python/cproton.i > > > > > > > (original) > > > > > > > +++ qpid/proton/trunk/proton-c/bindings/python/cproton.i Fri Sep > > > > > > > 19 > > > > > > > 21:00:50 2014 > > > > > > > @@ -23,6 +23,7 @@ > > > > > > > #include <winsock2.h> > > > > > > > #endif > > > > > > > #include <proton/engine.h> > > > > > > > +#include <proton/url.h> > > > > > > > #include <proton/message.h> > > > > > > > #include <proton/sasl.h> > > > > > > > #include <proton/driver.h> > > > > > > > @@ -280,41 +281,4 @@ int pn_ssl_get_peer_hostname(pn_ssl_t *s > > > > > > > } > > > > > > > %} > > > > > > > > > > > > > > - > > > > > > > -/** > > > > > > > - pni_parse_url(char* url, char **scheme, char **user, char > > > **pass, > > > > > char > > > > > > > **host, char **port, char **path) > > > > > > > - The following type maps convert this into a python function > > > that > > > > > taks > > > > > > > a URL string argument > > > > > > > - and returns a list of strings [scheme, user, pass, host, port, > > > > > path] > > > > > > > - This probably could be done more neatly. > > > > > > > -*/ > > > > > > > - > > > > > > > -// Typemap to copy the url string as it will be modified by > > > parse_url > > > > > > > -%typemap(in,noblock=1,fragment="SWIG_AsCharPtrAndSize") char *url > > > (int > > > > > > > res, char *t = 0, size_t n = 0, int alloc = 0) { > > > > > > > - res = SWIG_AsCharPtrAndSize($input, &t, &n, &alloc); > > > > > > > - if (!SWIG_IsOK(res)) { > > > > > > > - %argument_fail(res, "char *url", $symname, $argnum); > > > > > > > - } > > > > > > > - $1 = %new_array(n, $*1_ltype); > > > > > > > - memcpy($1,t,sizeof(char)*n); > > > > > > > - if (alloc == SWIG_NEWOBJ) %delete_array(t); > > > > > > > - $1[n-1] = 0; > > > > > > > -} > > > > > > > -%typemap(freearg,match="in") char *url "free($1);"; > > > > > > > -%typemap(argout) char *url ""; > > > > > > > - > > > > > > > -// Typemap for char** return strings. Don't free them. > > > > > > > -%typemap(in,numinputs=0) char **OUTSTR($*1_ltype temp = 0) "$1 = > > > > > &temp;"; > > > > > > > -%typemap(freearg,match="in") char **OUTSTR ""; > > > > > > > -%typemap(argout,noblock=1,fragment="SWIG_FromCharPtr") char > > > **OUTSTR { > > > > > > > - %append_output(SWIG_FromCharPtr(*$1)); > > > > > > > -} > > > > > > > - > > > > > > > -// Typemap to initialize result as empty list > > > > > > > -%typemap(out) void "$result = PyList_New(0);"; > > > > > > > - > > > > > > > - > > > > > > > -%apply char** OUTSTR {char **scheme, char **user, char **pass, > > > char > > > > > > > **host, char **port, char **path}; > > > > > > > -void pni_parse_url(char* url, char **scheme, char **user, char > > > **pass, > > > > > > > char **host, char **port, char **path); > > > > > > > -%ignore pni_parse_url; > > > > > > > - > > > > > > > %include "proton/cproton.i" > > > > > > > > > > > > > > Modified: qpid/proton/trunk/proton-c/bindings/python/proton.py > > > > > > > URL: > > > > > > > > > > > > > > > http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/python/proton.py?rev=1626329&r1=1626328&r2=1626329&view=diff > > > > > > > > > > > > > > > > > > > > > > ============================================================================== > > > > > > > --- qpid/proton/trunk/proton-c/bindings/python/proton.py > > > > > > > (original) > > > > > > > +++ qpid/proton/trunk/proton-c/bindings/python/proton.py Fri Sep > > > > > > > 19 > > > > > > > 21:00:50 2014 > > > > > > > @@ -3657,114 +3657,98 @@ __all__ = [ > > > > > > > > > > > > > > > > > > > > > class Url(object): > > > > > > > - """ > > > > > > > - Simple URL parser/constructor, handles URLs of the form: > > > > > > > + """ > > > > > > > + Simple URL parser/constructor, handles URLs of the form: > > > > > > > > > > > > > > - <scheme>://<user>:<password>@<host>:<port>/<path> > > > > > > > + <scheme>://<user>:<password>@<host>:<port>/<path> > > > > > > > > > > > > > > - All components can be None if not specifeid in the URL > > > > > > > string. > > > > > > > + All components can be None if not specifeid in the URL string. > > > > > > > > > > > > > > - The port can be specified as a service name, e.g. 'amqp' in > > > the > > > > > > > - URL string but Url.port always gives the integer value. > > > > > > > + The port can be specified as a service name, e.g. 'amqp' in the > > > > > > > + URL string but Url.port always gives the integer value. > > > > > > > + > > > > > > > + @ivar scheme: Url scheme e.g. 'amqp' or 'amqps' > > > > > > > + @ivar user: Username > > > > > > > + @ivar password: Password > > > > > > > + @ivar host: Host name, ipv6 literal or ipv4 dotted quad. > > > > > > > + @ivar port: Integer port. > > > > > > > + @ivar host_port: Returns host:port > > > > > > > + """ > > > > > > > + > > > > > > > + AMQPS = "amqps" > > > > > > > + AMQP = "amqp" > > > > > > > + > > > > > > > + class Port(int): > > > > > > > + """An integer port number that can be constructed from a > > > service > > > > > name > > > > > > > string""" > > > > > > > + > > > > > > > + def __new__(cls, value): > > > > > > > + port = super(Url.Port, cls).__new__(cls, > > > cls.port_int(value)) > > > > > > > + setattr(port, 'name', str(value)) > > > > > > > + return port > > > > > > > + > > > > > > > + def __eq__(self, x): return str(self) == x or int(self) == x > > > > > > > + def __ne__(self, x): return not self == x > > > > > > > + def __str__(self): return str(self.name) > > > > > > > + > > > > > > > + @staticmethod > > > > > > > + def port_int(value): > > > > > > > + """Convert service, an integer or a service name, into an > > > > > integer > > > > > > > port number.""" > > > > > > > + try: > > > > > > > + return int(value) > > > > > > > + except ValueError: > > > > > > > + try: > > > > > > > + return socket.getservbyname(value) > > > > > > > + except socket.error: > > > > > > > + raise ValueError("Not a valid port number or service > > > name: > > > > > > > '%s'" % value) > > > > > > > > > > > > > > - @ivar scheme: Url scheme e.g. 'amqp' or 'amqps' > > > > > > > - @ivar user: Username > > > > > > > - @ivar password: Password > > > > > > > - @ivar host: Host name, ipv6 literal or ipv4 dotted quad. > > > > > > > - @ivar port: Integer port. > > > > > > > - @ivar host_port: Returns host:port > > > > > > > + def __init__(self, url=None, **kwargs): > > > > > > > + """ > > > > > > > + @param url: URL string to parse. > > > > > > > + @param kwargs: scheme, user, password, host, port, path. > > > > > > > + If specified, replaces corresponding part in url string. > > > > > > > """ > > > > > > > + if url: > > > > > > > + self._url = pn_url_parse(str(url)) > > > > > > > + if not self._url: raise ValueError("Invalid URL '%s'" % > > > > > > > url) > > > > > > > + else: > > > > > > > + self._url = pn_url() > > > > > > > + for k in kwargs: # Let kwargs override values > > > parsed > > > > > from > > > > > > > url > > > > > > > + getattr(self, k) # Check for invalid kwargs > > > > > > > + setattr(self, k, kwargs[k]) > > > > > > > + > > > > > > > + class PartDescriptor(object): > > > > > > > + def __init__(self, part): > > > > > > > + self.getter = globals()["pn_url_%s" % part] > > > > > > > + self.setter = globals()["pn_url_set_%s" % part] > > > > > > > + def __get__(self, obj, type=None): return > > > self.getter(obj._url) > > > > > > > + def __set__(self, obj, value): return self.setter(obj._url, > > > > > > > str(value)) > > > > > > > + > > > > > > > + scheme = PartDescriptor('scheme') > > > > > > > + username = PartDescriptor('username') > > > > > > > + password = PartDescriptor('password') > > > > > > > + host = PartDescriptor('host') > > > > > > > + path = PartDescriptor('path') > > > > > > > + > > > > > > > + @property > > > > > > > + def port(self): > > > > > > > + portstr = pn_url_port(self._url) > > > > > > > + return portstr and Url.Port(portstr) > > > > > > > + > > > > > > > + @port.setter > > > > > > > + def port(self, value): > > > > > > > + if value is None: pn_url_set_port(self._url, None) > > > > > > > + else: pn_url_set_port(self._url, str(Url.Port(value))) > > > > > > > > > > > > > > - AMQPS = "amqps" > > > > > > > - AMQP = "amqp" > > > > > > > + def __str__(self): return pn_url_str(self._url) > > > > > > > > > > > > > > - class Port(int): > > > > > > > - """An integer port number that can also have an associated > > > > > service > > > > > > > name string""" > > > > > > > + def __repr__(self): return "Url(%r)" % str(self) > > > > > > > > > > > > > > - def __new__(cls, value): > > > > > > > - port = super(Url.Port, cls).__new__(cls, > > > cls.port_int(value)) > > > > > > > - setattr(port, 'name', str(value)) > > > > > > > - return port > > > > > > > - > > > > > > > - def __eq__(self, x): return str(self) == x or int(self) == > > > > > > > x > > > > > > > - def __ne__(self, x): return not self == x > > > > > > > - def __str__(self): return str(self.name) > > > > > > > - > > > > > > > - @staticmethod > > > > > > > - def port_int(value): > > > > > > > - """Convert service, an integer or a service name, into an > > > > > integer > > > > > > > port number.""" > > > > > > > - try: > > > > > > > - return int(value) > > > > > > > - except ValueError: > > > > > > > - try: > > > > > > > - return socket.getservbyname(value) > > > > > > > - except socket.error: > > > > > > > - raise ValueError("Not a valid port number or service > > > name: > > > > > > > '%s'" % value) > > > > > > > - > > > > > > > - def __init__(self, url=None, **kwargs): > > > > > > > - """ > > > > > > > - @param url: String or Url instance to parse or copy. > > > > > > > - @param kwargs: URL fields: scheme, user, password, host, > > > port, > > > > > > > path. > > > > > > > - If specified, replaces corresponding component in > > > > > > > url. > > > > > > > - """ > > > > > > > - > > > > > > > - fields = ['scheme', 'user', 'password', 'host', 'port', > > > > > 'path'] > > > > > > > - > > > > > > > - for f in fields: setattr(self, f, None) > > > > > > > - for k in kwargs: getattr(self, k) # Check for invalid > > > kwargs > > > > > > > - > > > > > > > - if isinstance(url, Url): # Copy from another Url > > > > > > > instance. > > > > > > > - self.__dict__.update(url.__dict__) > > > > > > > - elif url is not None: # Parse from url > > > > > > > - parts = pni_parse_url(str(url)) > > > > > > > - if not filter(None, parts): raise ValueError("Invalid > > > AMQP > > > > > > > URL: '%s'" % url) > > > > > > > - self.scheme, self.user, self.password, self.host, > > > port, > > > > > > > self.path = parts > > > > > > > - if not self.host: self.host = None > > > > > > > - self.port = port and self.Port(port) > > > > > > > - > > > > > > > - # Let kwargs override values previously set from url > > > > > > > - for field in fields: > > > > > > > - setattr(self, field, kwargs.get(field, getattr(self, > > > > > field))) > > > > > > > - > > > > > > > - def __repr__(self): > > > > > > > - return "Url(%r)" % str(self) > > > > > > > - > > > > > > > - def __str__(self): > > > > > > > - s = "" > > > > > > > - if self.scheme: > > > > > > > - s += "%s://" % self.scheme > > > > > > > - if self.user: > > > > > > > - s += self.user > > > > > > > - if self.password: > > > > > > > - s += ":%s" % self.password > > > > > > > - if self.user or self.password: > > > > > > > - s += '@' > > > > > > > - if self.host and ':' in self.host: > > > > > > > - s += "[%s]" % self.host > > > > > > > - elif self.host: > > > > > > > - s += self.host > > > > > > > - if self.port: > > > > > > > - s += ":%s" % self.port > > > > > > > - if self.path: > > > > > > > - s += "/%s" % self.path > > > > > > > - return s > > > > > > > - > > > > > > > - def __eq__(self, url): > > > > > > > - return \ > > > > > > > - self.scheme == url.scheme and \ > > > > > > > - self.user == url.user and self.password == > > > url.password > > > > > and \ > > > > > > > - self.host == url.host and self.port == url.port and \ > > > > > > > - self.path == url.path > > > > > > > - > > > > > > > - def __ne__(self, url): > > > > > > > - return not self.__eq__(url) > > > > > > > - > > > > > > > - def defaults(self): > > > > > > > - """ > > > > > > > - Fill in missing values with defaults > > > > > > > - @return: self > > > > > > > - """ > > > > > > > - self.scheme = self.scheme or self.AMQP > > > > > > > - self.host = self.host or '0.0.0.0' > > > > > > > - self.port = self.port or self.Port(self.scheme) > > > > > > > - return self > > > > > > > + def defaults(self): > > > > > > > + """ > > > > > > > + Fill in missing values (scheme, host or port) with defaults > > > > > > > + @return: self > > > > > > > + """ > > > > > > > + self.scheme = self.scheme or self.AMQP > > > > > > > + self.host = self.host or '0.0.0.0' > > > > > > > + self.port = self.port or self.Port(self.scheme) > > > > > > > + return self > > > > > > > > > > > > > > Modified: qpid/proton/trunk/proton-c/bindings/ruby/ruby.i > > > > > > > URL: > > > > > > > > > > > > > > > http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/ruby/ruby.i?rev=1626329&r1=1626328&r2=1626329&view=diff > > > > > > > > > > > > > > > > > > > > > > ============================================================================== > > > > > > > --- qpid/proton/trunk/proton-c/bindings/ruby/ruby.i (original) > > > > > > > +++ qpid/proton/trunk/proton-c/bindings/ruby/ruby.i Fri Sep 19 > > > 21:00:50 > > > > > > > 2014 > > > > > > > @@ -26,8 +26,8 @@ > > > > > > > #include <proton/messenger.h> > > > > > > > #include <proton/ssl.h> > > > > > > > #include <proton/driver_extras.h> > > > > > > > - > > > > > > > #include <proton/types.h> > > > > > > > +#include <proton/url.h> > > > > > > > > > > > > > > #include <uuid/uuid.h> > > > > > > > %} > > > > > > > > > > > > > > Modified: qpid/proton/trunk/proton-c/include/proton/cproton.i > > > > > > > URL: > > > > > > > > > > > > > > > http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/include/proton/cproton.i?rev=1626329&r1=1626328&r2=1626329&view=diff > > > > > > > > > > > > > > > > > > > > > > ============================================================================== > > > > > > > --- qpid/proton/trunk/proton-c/include/proton/cproton.i (original) > > > > > > > +++ qpid/proton/trunk/proton-c/include/proton/cproton.i Fri Sep 19 > > > > > > > 21:00:50 2014 > > > > > > > @@ -1394,3 +1394,6 @@ typedef unsigned long int uintptr_t; > > > > > > > pn_delivery_t *pn_cast_pn_delivery(void *x) { return > > > (pn_delivery_t > > > > > *) > > > > > > > x; } > > > > > > > pn_transport_t *pn_cast_pn_transport(void *x) { return > > > > > (pn_transport_t > > > > > > > *) x; } > > > > > > > %} > > > > > > > + > > > > > > > +%include "proton/url.h" > > > > > > > + > > > > > > > > > > > > > > Added: qpid/proton/trunk/proton-c/include/proton/url.h > > > > > > > URL: > > > > > > > > > > > > > > > http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/include/proton/url.h?rev=1626329&view=auto > > > > > > > > > > > > > > > > > > > > > > ============================================================================== > > > > > > > --- qpid/proton/trunk/proton-c/include/proton/url.h (added) > > > > > > > +++ qpid/proton/trunk/proton-c/include/proton/url.h Fri Sep 19 > > > 21:00:50 > > > > > > > 2014 > > > > > > > @@ -0,0 +1,83 @@ > > > > > > > +#ifndef PROTON_URL_H > > > > > > > +#define PROTON_URL_H > > > > > > > +/* > > > > > > > + * Licensed to the Apache Software Foundation (ASF) under one > > > > > > > + * or more contributor license agreements. See the NOTICE file > > > > > > > + * distributed with this work for additional information > > > > > > > + * regarding copyright ownership. The ASF licenses this file > > > > > > > + * to you under the Apache License, Version 2.0 (the > > > > > > > + * "License"); you may not use this file except in compliance > > > > > > > + * with the License. You may obtain a copy of the License at > > > > > > > + * > > > > > > > + * http://www.apache.org/licenses/LICENSE-2.0 > > > > > > > + * > > > > > > > + * Unless required by applicable law or agreed to in writing, > > > > > > > + * software distributed under the License is distributed on an > > > > > > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY > > > > > > > + * KIND, either express or implied. See the License for the > > > > > > > + * specific language governing permissions and limitations > > > > > > > + * under the License. > > > > > > > + */ > > > > > > > + > > > > > > > +#include <proton/import_export.h> > > > > > > > + > > > > > > > +/** @file > > > > > > > + * URL API for parsing URLs. > > > > > > > + * > > > > > > > + * @defgroup url URL > > > > > > > + * @{ > > > > > > > + */ > > > > > > > + > > > > > > > +/** A parsed URL */ > > > > > > > +typedef struct pn_url_t pn_url_t; > > > > > > > + > > > > > > > +/** Create an empty URL */ > > > > > > > +PN_EXTERN pn_url_t *pn_url(void); > > > > > > > + > > > > > > > +/** Parse a string URL as a pn_url_t. > > > > > > > + *@param[in] url A URL string. > > > > > > > + *@return The parsed pn_url_t or NULL if url is not a valid URL > > > > > string. > > > > > > > + */ > > > > > > > +PN_EXTERN pn_url_t *pn_url_parse(const char *url); > > > > > > > + > > > > > > > +/** Free a URL */ > > > > > > > +PN_EXTERN void pn_url_free(pn_url_t *url); > > > > > > > + > > > > > > > +/** Clear the contents of the URL. */ > > > > > > > +PN_EXTERN void pn_url_clear(pn_url_t *url); > > > > > > > + > > > > > > > +/** Return the string form of a URL. Owned by the pn_url_t.*/ > > > > > > > +PN_EXTERN const char *pn_url_str(pn_url_t *url); > > > > > > > + > > > > > > > +/** > > > > > > > + *@name Getters for parts of the URL. > > > > > > > + * > > > > > > > + *Values belong to the URL. May return NULL if the value is not > > > set. > > > > > > > + * > > > > > > > + *@{ > > > > > > > + */ > > > > > > > +PN_EXTERN const char *pn_url_scheme(pn_url_t *url); > > > > > > > +PN_EXTERN const char *pn_url_username(pn_url_t *url); > > > > > > > +PN_EXTERN const char *pn_url_password(pn_url_t *url); > > > > > > > +PN_EXTERN const char *pn_url_host(pn_url_t *url); > > > > > > > +PN_EXTERN const char *pn_url_port(pn_url_t *url); > > > > > > > +PN_EXTERN const char *pn_url_path(pn_url_t *url); > > > > > > > +///@} > > > > > > > + > > > > > > > +/** > > > > > > > + *@name Setters for parts of the URL. > > > > > > > + * > > > > > > > + *Values are copied. Value can be NULL to indicate the part is > > > > > > > not > > > > > set. > > > > > > > + * > > > > > > > + *@{ > > > > > > > + */ > > > > > > > +PN_EXTERN void pn_url_set_scheme(pn_url_t *url, const char > > > *scheme); > > > > > > > +PN_EXTERN void pn_url_set_username(pn_url_t *url, const char > > > > > *username); > > > > > > > +PN_EXTERN void pn_url_set_password(pn_url_t *url, const char > > > > > *password); > > > > > > > +PN_EXTERN void pn_url_set_host(pn_url_t *url, const char *host); > > > > > > > +PN_EXTERN void pn_url_set_port(pn_url_t *url, const char *port); > > > > > > > +PN_EXTERN void pn_url_set_path(pn_url_t *url, const char *path); > > > > > > > +///@} > > > > > > > + > > > > > > > +///@} > > > > > > > +#endif > > > > > > > > > > > > > > Added: qpid/proton/trunk/proton-c/src/url.c > > > > > > > URL: > > > > > > > > > > > > > > > http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/src/url.c?rev=1626329&view=auto > > > > > > > > > > > > > > > > > > > > > > ============================================================================== > > > > > > > --- qpid/proton/trunk/proton-c/src/url.c (added) > > > > > > > +++ qpid/proton/trunk/proton-c/src/url.c Fri Sep 19 21:00:50 2014 > > > > > > > @@ -0,0 +1,127 @@ > > > > > > > +/* > > > > > > > + * > > > > > > > + * Licensed to the Apache Software Foundation (ASF) under one > > > > > > > + * or more contributor license agreements. See the NOTICE file > > > > > > > + * distributed with this work for additional information > > > > > > > + * regarding copyright ownership. The ASF licenses this file > > > > > > > + * to you under the Apache License, Version 2.0 (the > > > > > > > + * "License"); you may not use this file except in compliance > > > > > > > + * with the License. You may obtain a copy of the License at > > > > > > > + * > > > > > > > + * http://www.apache.org/licenses/LICENSE-2.0 > > > > > > > + * > > > > > > > + * Unless required by applicable law or agreed to in writing, > > > > > > > + * software distributed under the License is distributed on an > > > > > > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY > > > > > > > + * KIND, either express or implied. See the License for the > > > > > > > + * specific language governing permissions and limitations > > > > > > > + * under the License. > > > > > > > + * > > > > > > > + */ > > > > > > > + > > > > > > > +#include <proton/url.h> > > > > > > > +#include <proton/util.h> > > > > > > > +#include <stdlib.h> > > > > > > > +#include <string.h> > > > > > > > +#include <stdio.h> > > > > > > > + > > > > > > > +static char* copy(const char* str) { > > > > > > > + if (str == NULL) return NULL; > > > > > > > + char *str2 = (char*)malloc(strlen(str)); > > > > > > > + if (str2) strcpy(str2, str); > > > > > > > + return str2; > > > > > > > +} > > > > > > > + > > > > > > > +struct pn_url_t { > > > > > > > + char *scheme; > > > > > > > + char *username; > > > > > > > + char *password; > > > > > > > + char *host; > > > > > > > + char *port; > > > > > > > + char *path; > > > > > > > + char *str; > > > > > > > +}; > > > > > > > + > > > > > > > +PN_EXTERN pn_url_t *pn_url() { > > > > > > > + pn_url_t *url = (pn_url_t*)malloc(sizeof(pn_url_t)); > > > > > > > + memset(url, 0, sizeof(*url)); > > > > > > > + return url; > > > > > > > +} > > > > > > > + > > > > > > > +/** Parse a string URL as a pn_url_t. > > > > > > > + *@param[in] url A URL string. > > > > > > > + *@return The parsed pn_url_t or NULL if url is not a valid URL > > > > > string. > > > > > > > + */ > > > > > > > +PN_EXTERN pn_url_t *pn_url_parse(const char *str) { > > > > > > > + if (!str || !*str) /* Empty string or NULL is > > > illegal. */ > > > > > > > + return NULL; > > > > > > > + > > > > > > > + pn_url_t *url = pn_url(); > > > > > > > + char *str2 = copy(str); /* FIXME aconway 2014-09-19: > > > > > clean up > > > > > > > */ > > > > > > > + pni_parse_url(str2, &url->scheme, &url->username, > > > &url->password, > > > > > > > &url->host, &url->port, &url->path); > > > > > > > + url->scheme = copy(url->scheme); > > > > > > > + url->username = copy(url->username); > > > > > > > + url->password = copy(url->password); > > > > > > > + url->host = (url->host && !*url->host) ? NULL : > > > copy(url->host); > > > > > > > + url->port = copy(url->port); > > > > > > > + url->path = copy(url->path); > > > > > > > + return url; > > > > > > > +} > > > > > > > + > > > > > > > +/** Free a URL */ > > > > > > > +PN_EXTERN void pn_url_free(pn_url_t *url) { > > > > > > > + pn_url_clear(url); > > > > > > > + free(url); > > > > > > > +} > > > > > > > + > > > > > > > +/** Clear the contents of the URL. */ > > > > > > > +PN_EXTERN void pn_url_clear(pn_url_t *url) { > > > > > > > + pn_url_set_username(url, NULL); > > > > > > > + pn_url_set_password(url, NULL); > > > > > > > + pn_url_set_host(url, NULL); > > > > > > > + pn_url_set_port(url, NULL); > > > > > > > + pn_url_set_path(url, NULL); > > > > > > > + free(url->str); url->str = NULL; > > > > > > > +} > > > > > > > + > > > > > > > +static inline int len(const char *str) { return str ? strlen(str) > > > : > > > > > 0; } > > > > > > > + > > > > > > > +/** Return the string form of a URL. */ > > > > > > > +PN_EXTERN const char *pn_url_str(pn_url_t *url) { > > > > > > > + int size = len(url->scheme) + len(url->username) + > > > > > len(url->password) > > > > > > > + + len(url->host) + len(url->port) + len(url->path) > > > > > > > + + len("s://u:p@[h]:p/p"); > > > > > > > + free(url->str); > > > > > > > + url->str = (char*)malloc(size); > > > > > > > + if (!url->str) return NULL; > > > > > > > + > > > > > > > + int i = 0; > > > > > > > + if (url->scheme) i += snprintf(url->str+i, size-i, "%s://", > > > > > > > url->scheme); > > > > > > > + if (url->username) i += snprintf(url->str+i, size-i, "%s", > > > > > > > url->username); > > > > > > > + if (url->password) i += snprintf(url->str+i, size-i, ":%s", > > > > > > > url->password); > > > > > > > + if (url->username || url->password) i += snprintf(url->str+i, > > > > > size-i, > > > > > > > "@"); > > > > > > > + if (url->host) { > > > > > > > + if (strchr(url->host, ':')) i += snprintf(url->str+i, > > > size-i, > > > > > > > "[%s]", url->host); > > > > > > > + else i += snprintf(url->str+i, size-i, "%s", url->host); > > > > > > > + } > > > > > > > + if (url->port) i += snprintf(url->str+i, size-i, ":%s", > > > > > url->port); > > > > > > > + if (url->path) i += snprintf(url->str+i, size-i, "/%s", > > > > > url->path); > > > > > > > + return url->str; > > > > > > > +} > > > > > > > + > > > > > > > +PN_EXTERN const char *pn_url_scheme(pn_url_t *url) { return > > > > > url->scheme; } > > > > > > > +PN_EXTERN const char *pn_url_username(pn_url_t *url) { return > > > > > > > url->username; } > > > > > > > +PN_EXTERN const char *pn_url_password(pn_url_t *url) { return > > > > > > > url->password; } > > > > > > > +PN_EXTERN const char *pn_url_host(pn_url_t *url) { return > > > url->host; } > > > > > > > +PN_EXTERN const char *pn_url_port(pn_url_t *url) { return > > > url->port; } > > > > > > > +PN_EXTERN const char *pn_url_path(pn_url_t *url) { return > > > url->path; } > > > > > > > + > > > > > > > +#define SET(part) free(url->part); url->part = copy(part) > > > > > > > +PN_EXTERN void pn_url_set_scheme(pn_url_t *url, const char > > > *scheme) { > > > > > > > SET(scheme); } > > > > > > > +PN_EXTERN void pn_url_set_username(pn_url_t *url, const char > > > > > *username) { > > > > > > > SET(username); } > > > > > > > +PN_EXTERN void pn_url_set_password(pn_url_t *url, const char > > > > > *password) { > > > > > > > SET(password); } > > > > > > > +PN_EXTERN void pn_url_set_host(pn_url_t *url, const char *host) { > > > > > > > SET(host); } > > > > > > > +PN_EXTERN void pn_url_set_port(pn_url_t *url, const char *port) { > > > > > > > SET(port); } > > > > > > > +PN_EXTERN void pn_url_set_path(pn_url_t *url, const char *path) { > > > > > > > SET(path); } > > > > > > > + > > > > > > > + > > > > > > > > > > > > > > Modified: qpid/proton/trunk/tests/python/proton_tests/url.py > > > > > > > URL: > > > > > > > > > > > > > > > http://svn.apache.org/viewvc/qpid/proton/trunk/tests/python/proton_tests/url.py?rev=1626329&r1=1626328&r2=1626329&view=diff > > > > > > > > > > > > > > > > > > > > > > ============================================================================== > > > > > > > --- qpid/proton/trunk/tests/python/proton_tests/url.py (original) > > > > > > > +++ qpid/proton/trunk/tests/python/proton_tests/url.py Fri Sep 19 > > > > > 21:00:50 > > > > > > > 2014 > > > > > > > @@ -28,9 +28,9 @@ class UrlTest(common.Test): > > > > > > > def assertNotEqual(self, a, b): > > > > > > > assert a != b, "%s == %s" % (a, b) > > > > > > > > > > > > > > - def assertUrl(self, u, scheme, user, password, host, port, > > > path): > > > > > > > - self.assertEqual((u.scheme, u.user, u.password, u.host, > > > > > u.port, > > > > > > > u.path), > > > > > > > - (scheme, user, password, host, port, > > > path)) > > > > > > > + def assertUrl(self, u, scheme, username, password, host, > > > > > > > port, > > > > > path): > > > > > > > + self.assertEqual((u.scheme, u.username, u.password, > > > u.host, > > > > > > > u.port, u.path), > > > > > > > + (scheme, username, password, host, port, > > > > > path)) > > > > > > > > > > > > > > def testUrl(self): > > > > > > > url = Url('amqp://me:secret@myhost:1234/foobar') > > > > > > > @@ -40,7 +40,7 @@ class UrlTest(common.Test): > > > > > > > > > > > > > > def testDefaults(self): > > > > > > > # Check that we allow None for scheme, port > > > > > > > - url = Url(user='me', password='secret', host='myhost', > > > > > > > path='foobar') > > > > > > > + url = Url(username='me', password='secret', > > > > > > > host='myhost', > > > > > > > path='foobar') > > > > > > > self.assertEqual(str(url), "me:secret@myhost/foobar") > > > > > > > self.assertUrl(url, None, 'me', 'secret', 'myhost', None, > > > > > > > 'foobar') > > > > > > > > > > > > > > @@ -97,21 +97,19 @@ class UrlTest(common.Test): > > > > > > > def testMissing(self): > > > > > > > self.assertUrl(Url(), None, None, None, None, None, None) > > > > > > > self.assertUrl(Url('amqp://'), 'amqp', None, None, None, > > > None, > > > > > > > None) > > > > > > > - self.assertUrl(Url('user@'), None, 'user', None, None, > > > None, > > > > > > > None) > > > > > > > + self.assertUrl(Url('username@'), None, 'username', None, > > > > > None, > > > > > > > None, None) > > > > > > > self.assertUrl(Url(':pass@'), None, '', 'pass', None, > > > None, > > > > > None) > > > > > > > self.assertUrl(Url('host'), None, None, None, 'host', > > > None, > > > > > None) > > > > > > > self.assertUrl(Url(':1234'), None, None, None, None, > > > > > > > 1234, > > > > > None) > > > > > > > self.assertUrl(Url('/path'), None, None, None, None, > > > > > > > None, > > > > > 'path') > > > > > > > > > > > > > > - for s in ['amqp://', 'user@', ':pass@', ':1234', > > > '/path']: > > > > > > > + for s in ['amqp://', 'username@', ':pass@', ':1234', > > > > > '/path']: > > > > > > > self.assertEqual(s, str(Url(s))) > > > > > > > > > > > > > > for s, full in [ > > > > > > > ('amqp://', 'amqp://0.0.0.0:amqp'), > > > > > > > - ('user@', 'amqp://user@0.0.0.0:amqp'), > > > > > > > + ('username@', 'amqp://username@0.0.0.0:amqp'), > > > > > > > (':pass@', 'amqp://:pass@0.0.0.0:amqp'), > > > > > > > (':1234', 'amqp://0.0.0.0:1234'), > > > > > > > ('/path', 'amqp://0.0.0.0:amqp/path')]: > > > > > > > self.assertEqual(str(Url(s).defaults()), full) > > > > > > > - > > > > > > > - self.assertRaises(ValueError, Url, '') > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > --------------------------------------------------------------------- > > > > > > > To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org > > > > > > > For additional commands, e-mail: commits-h...@qpid.apache.org > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > --------------------------------------------------------------------- > > > > > To unsubscribe, e-mail: dev-unsubscr...@qpid.apache.org > > > > > For additional commands, e-mail: dev-h...@qpid.apache.org > > > > > > > > > > > > > > > > > > > > > > --------------------------------------------------------------------- > > > To unsubscribe, e-mail: dev-unsubscr...@qpid.apache.org > > > For additional commands, e-mail: dev-h...@qpid.apache.org > > > > > > >