Moved to SliceHost

I’ve moved this blog to SliceHost! Now this site is running on a snappy Gentoo 2006.1 VPS, with Apache2 and PHP5 serving the article that you’re reading. In the course of migrating the site, the following links helped immensely:

  • Check your DNS: http://pingability.com/zoneinfo.jsp
  • Configuring Postfix as your MTA: http://rimuhosting.com/support/settingupemail.jsp?mta=postfix
  • More Postfix goodness: http://www.gentoo.org/doc/en/virt-mail-howto.xml
  • Installing MySQL: http://www.gentoo.org/doc/en/mysql-howto.xml
  • Wordpress, Gentoo style: http://mormanski.net/?p=5

3 Comments

Installing SSHFS on MacPorts

I came across SSHFS in an article by Paul Gross and thought of trying it out. Unfortunately, I had to take a slightly more complicated route to have it installed with MacPorts on my laptop.

Running

sudo port install sshfs

caused this particular error to appear:

--->  Building fusefs

Error: Target org.macports.build returned: shell command "cd "/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_fuse_fusefs/work/fusefs" && xcodebuild  -target "fusefs" -configuration Release build OBJROOT=build/ SYMROOT=build/ OBJROOT=build/ SYMROOT=build/" returned error 1

Command output: === BUILDING NATIVE TARGET fusefs WITH CONFIGURATION Release ===Checking Dependencies...

Cpp build/Release/fusefs.kext/Contents/Info.plist build/fusefs.build/Release/fusefs.build/Info.plist

    cd /opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_fuse_fusefs/work/fusefs

    /usr/bin/gcc -E -P -x c -Wno-trigraphs /opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_fuse_fusefs/work/fusefs/build/fusefs.build/Release/fusefs.build/Info.plist -o /opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_fuse_fusefs/work/fusefs/build/Release/fusefs.kext/Contents/Info.plist

/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_fuse_fusefs/work/fusefs/build/fusefs.build/Release/fusefs.build/Info.plist:1:33: error: common/fuse_version.h: No such file or directory

/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_fuse_fusefs/work/fusefs/build/fusefs.build/Release/fusefs.build/Info.plist:2:31: error: common/fuse_param.h: No such file or directory

** BUILD FAILED **

Error: The following dependencies failed to build: fusefs libfuse

Error: Status 1 encountered during processing.

A quick spot of googling found the answer to this pretty quickly. Hans-Göran (comment #24) mentioned that he managed to install fusefs after copying the files that the error was complaining were missing.

At first glance, it seems that the file in question (common/fuse_param.h) is indeed present in the build directory. It appears that the error is being triggered from the property list file for the build.

/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_fuse_fusefs/work/fusefs/build/fusefs.build/Release/fusefs.build/Info.plist

Taking a look at its contents reveals that it’s including 2 header files from its containing directory

#include "common/fuse_version.h"

#include "common/fuse_param.h"

So I copied over the missing directory and re-ran the sshfs install.

cd /opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_fuse_fusefs/work/fusefs

sudo cp -r common build/fusefs.build/Release/fusefs.build/

sudo port install sshfs
--->  Fetching libfuse
--->  Attempting to fetch fuse-2.6.5-macosx.patch from http://macfuse.googlecode.com/svn/tags/macfuse-0.4.0/libfuse/
--->  Attempting to fetch fuse-2.6.5.tar.gz from http://downloads.sourceforge.net/fuse
--->  Verifying checksum(s) for libfuse
--->  Extracting libfuse
--->  Applying patches to libfuse
--->  Configuring libfuse
--->  Building libfuse with target all
--->  Staging libfuse into destroot
--->  Installing libfuse 2.6.5_1
--->  Activating libfuse 2.6.5_1
--->  Cleaning libfuse
--->  Fetching sshfs
--->  Attempting to fetch sshfs-fuse-1.7-macosx.patch from http://macfuse.googlecode.com/svn/tags/macfuse-0.4.0/filesystems/sshfs
--->  Attempting to fetch sshfs-fuse-1.7.tar.gz from http://downloads.sourceforge.net/fuse
--->  Verifying checksum(s) for sshfs
--->  Extracting sshfs
--->  Applying patches to sshfs
--->  Configuring sshfs
--->  Building sshfs with target all
--->  Staging sshfs into destroot
--->  Installing sshfs 1.7_5
--->  Activating sshfs 1.7_5
--->  Cleaning sshfs

Now sshfs is installed!

Comments

Raising URI::InvalidURIError from a perfectly valid URI

I was puzzled by URI::parse raising an URI::InvalidURIError on a perfectly well formed URI recently.

RUBY:
  1. URI::InvalidURIError: bad URI(is not URI?): http://practicalguile.com/articles?query=latest
  2. from /opt/local/lib/ruby/1.8/uri/common.rb:436:in `split'
  3. from /opt/local/lib/ruby/1.8/uri/common.rb:485:in `parse'
  4. from (irb):2
  5. from :0

What's not apparent in this exception message is that the url contained a trailing space and this was causing URI.parse to fail. The following specifications demonstrate how it can trigger this particular exception.

uri.spec.rb

RUBY:
  1. require 'rubygems'
  2. require 'spec'
  3. require 'uri'
  4.  
  5. describe URI do
  6. it "should raise an InvalidURIException with leading whitespace in url" do
  7. lambda{ URI.parse(' http://www.ruby-lang.org') }.should raise_error(URI::InvalidURIError)
  8. end
  9.  
  10. it "should raise an InvalidURIException with trailing whitespace in url" do
  11. lambda{ URI.parse('http://www.ruby-lang.org ') }.should raise_error(URI::InvalidURIError)
  12. end
  13. end

Running the spec will get you the result below.

ruby uri.spec.rb

..Finished in 0.030051 seconds

2 examples, 0 failures

Looking at the stacktrace in the exception, it's being raised by URI.split after URI.parse is invoked with the offending URL.

RUBY_INSTALL/1.8/uri/common.rb

RUBY:
  1. def self.parse(uri)
  2. scheme, userinfo, host, port,
  3. registry, path, opaque, query, fragment = self.split(uri)
  4.  
  5. if scheme && @@schemes.include?(scheme.upcase)
  6. @@schemes[scheme.upcase].new(scheme, userinfo, host, port,
  7. registry, path, opaque, query,
  8. fragment)
  9. else
  10. Generic.new(scheme, userinfo, host, port,
  11. registry, path, opaque, query,
  12. fragment)
  13. end
  14. end

Nothing weird happening in URI.parse, its a straightforward call to URI.split. So I'll go into URI.split, comments removed for brevity.

RUBY:
  1. def self.split(uri)
  2. case uri
  3. when ''
  4. when ABS_URI
  5. scheme, opaque, userinfo, host, port,
  6. registry, path, query, fragment = $~[1..-1]
  7.  
  8. if !scheme
  9. raise InvalidURIError,
  10. "bad URI(absolute but no scheme): #{uri}"
  11. end
  12. if !opaque && (!path && (!host && !registry))
  13. raise InvalidURIError,
  14. "bad URI(absolute but no path): #{uri}"
  15. end
  16. when REL_URI
  17. scheme = nil
  18. opaque = nil
  19.  
  20. userinfo, host, port, registry,
  21. rel_segment, abs_path, query, fragment = $~[1..-1]
  22. if rel_segment && abs_path
  23. path = rel_segment + abs_path
  24. elsif rel_segment
  25. path = rel_segment
  26. elsif abs_path
  27. path = abs_path
  28. end
  29. else
  30. raise InvalidURIError, "bad URI(is not URI?): #{uri}"
  31. end
  32.  
  33. path = '' if !path && !opaque # (see RFC2396 Section 5.2)
  34. ret = [
  35. scheme,
  36. userinfo, host, port,         # X
  37. registry,                        # X
  38. path,                         # Y
  39. opaque,                        # Y
  40. query,
  41. fragment
  42. ]
  43. return ret
  44. end

URI.split is matching the incoming url with an empty string as well as regular expressions for absolute and relative URIs. It's obvious from the specifications earlier that urls with leading/trailing whitespace do not match any of these and the case statement raises InvalidURIError, with the rather misleading message.

The regexes used for matching absolute and relative URIs is shown below, if you really want to know.

RUBY:
  1. require 'uri'
  2. include URI::REGEXP
  3.  
  4. ABS_URI
  5. /^
  6. ([a-zA-Z][-+.a-zA-Z\d]*):                     (?# 1: scheme)
  7. (?:
  8. ((?:[-_.!~*'()a-zA-Z\d;?:@&=+$,]|%[a-fA-F\d]{2})(?:[-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*)              (?# 2: opaque)
  9. |
  10. (?:(?:
  11. \/\/(?:
  12. (?:(?:((?:[-_.!~*'()a-zA-Z\d;:&=+$,]|%[a-fA-F\d]{2})*)@)?  (?# 3: userinfo)
  13. (?:((?:(?:(?:[a-zA-Z\d](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.)*(?:[a-zA-Z](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:(?:[a-fA-F\d]{1,4}:)*[a-fA-F\d]{1,4})?::(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?)\]))(?::(\d*))?))?(?# 4: host, 5: port)               |
  14. ((?:[-_.!~*'()a-zA-Z\d$,;+@&=+]|%[a-fA-F\d]{2})+)           (?# 6: registry)
  15. )
  16. |
  17. (?!\/\/))                              (?# XXX: '\/\/' is the mark for hostport)
  18. (\/(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*(?:;(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*)*(?:\/(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*(?:;(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*)*)*)?              (?# 7: path)
  19. )(?:\?((?:[-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*))?           (?# 8: query)
  20. )
  21. (?:\#((?:[-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*))?            (?# 9: fragment)
  22. $/xn
  23.  
  24. REL_URI
  25. /^
  26. (?:
  27. (?:
  28. \/\/
  29. (?:
  30. (?:((?:[-_.!~*'()a-zA-Z\d;:&=+$,]|%[a-fA-F\d]{2})*)@)?       (?# 1: userinfo)
  31. ((?:(?:(?:[a-zA-Z\d](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.)*(?:[a-zA-Z](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:(?:[a-fA-F\d]{1,4}:)*[a-fA-F\d]{1,4})?::(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?)\]))?(?::(\d*))?  (?# 2: host, 3: port)
  32. |
  33. ((?:[-_.!~*'()a-zA-Z\d$,;+@&=+]|%[a-fA-F\d]{2})+)             (?# 4: registry)
  34. )
  35. )
  36. |
  37. ((?:[-_.!~*'()a-zA-Z\d;@&=+$,]|%[a-fA-F\d]{2})+)              (?# 5: rel_segment)
  38. )?
  39. (\/(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*(?:;(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*)*(?:\/(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*(?:;(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*)*)*)?                  (?# 6: abs_path)
  40. (?:\?((?:[-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*))?              (?# 7: query)
  41. (?:\#((?:[-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*))?           (?# 8: fragment)
  42. $/xn

Looks rather intimidating, doesn't it? However, we're more interested in the beginning and end of the regular expressions so its safe to ignore all the stuff in between. Narrowing our focus down to the regex anchors (^ and $), we can see that there is no matching of whitespace, thus preventing a valid URI from being matched in URI.split.

This all means that URI.split has a undocumented pre-condition on the uri parameter being stripped of any whitespace around it.

2 Comments