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 > > > >