Showing posts with label web services. Show all posts
Showing posts with label web services. Show all posts

Friday, May 15, 2009

Rshrtnr: The private URI shortener

Over the past couple of days, I've implemented a private URI shortener service for myself, which I have named "Rshrtnr". The derivation of the name is left as an exercise for the reader.

My main motivation for writing it was a criticism of public URI shortening services that I have been seeing in blogs for a long time: if the service has some downtime or suddenly disappears, all of the links that you have created with it are useless. With my approach, I regain some control of where my shortened links point to, and if the service has downtime and/or disappears, I have more options for restoring it.

The code itself is written in Python. SLOCCount says that the core module runs at around 100 SLOC. Most of my time, however, was taken up by working around problems relating to my webhost's Python installation. The supported Python version is 2.4.x, which is ridiculously old (for reference, Gentoo was the last major Linux distribution to switch from Python 2.4 to 2.5, around July 2008). Additionally, for some reason, if I attempt to change the sys.path variable (i.e., the "include path") to use locally installed modules (I am on a shared host), the entire script breaks with zero logged messages anywhere. It runs fine via the command line, but in FastCGI mode, the strangeness occurs.

The two third-party modules that I used were Paste and mysql-python. I store the URIs and their associated aliases in a simple SQL table, and I use Paste for various WSGI/HTTP-related utilities. I "manually" handle routing via parsing the PATH_INFO environment variable.

There are two ways to specify an alias: either explicitly send a custom one as a query parameter with the URI, or let the app make a random one for you. With the latter behavior, it hashes the URI to generate an eight character "unique" alias. Since there are (in theory) 64^8 possibilities, I don't think I'll run out of aliases any time soon, especially since custom aliases can be anywhere from 1 to 15 characters long.

In my opinion, the most interesting feature is that adding URIs requires one to send an OpenPGP-encoded query string, which needs a public key recognized by the app for the operation to succeed. To write this, I simply parsed the output from sending the OpenPGP message to the gpg binary.

Finally, mod_rewrite magic is used to prettify the shortened URIs. Nothing too exciting about that part.

I had thought about hosting a version of Rshrtnr on Google App Engine, but a key component is missing - OpenPGP support.

If anyone wants me to release it, please comment below. There's currently a bunch of webhost-specific things that I would need to abstract out before I release the code to the general public, and unless someone gives me a very good reason, it will be licensed under the AGPL version 3.

Sunday, February 22, 2009

The GNOME Platform, Awn, and the Cloud

Benjamin Otte recently wrote about desktop-web integration in the GNOME desktop. It's kind of interesting that he calls himself "not web enabled", given that he's the main developer of the swfdec library and associated applications. I agree with most of what he wrote, but there are a few comments I would like to make.

Benjamin asks:

Why does dconf (or GConf) not store my settings in the cloud so it uses the same settings on my university login?

As I understand it, this is one of the features of Conduit. There's a bug in Awn regarding config synchronization via Conduit. I'm probably going to look into how that works when I resume work on the config interface for libdesktop-agnostic.

There is an erroneous statement in his post:

We don't even have a http library that knows cookies.

libsoup has had (non-persistent) cookie support since 2.23.1, and persistent support will be in 2.26.0.

And then there's the main point:

[GNOME is] doing a very bad job at integrating the web into the desktop. Apart from epiphany (and the weather applet), what application does GNOME ship that speak http?

I believe there are two main reasons for this: One is GNOME developers are not "web-enabled". [...] The other, related reason is that we don't have the software to do this.

In Awn-land, we have several web-enabled applets:

  • arss
  • comics
  • digg
  • lastfm
  • meebo
  • pandora
  • rtm
  • weather
  • webapplet

In particular, webapplet is a work in progress framework, which will allow what is essentially an applet version of Mozilla Prism. It currently uses WebKit as the backend, although there is also a Gecko-based backend planned. The rest of the applets listed are written in Python. In particular, the digg, meebo, pandora, and rtm applets use the gtkmozembed Python bindings to view the respective websites. Here lies one of the problems of the web-enabling the GNOME platform. This library is acknowledged to be less than ideal. If you look at the source code of the applets, you'll see that there are some ugly hacks in order to make them work properly on Ubuntu systems. Webapplet is slated to replace that ugliness.

One of my side projects is a status aggregator applet. It's supposed to aggregate all of these social networking status feeds and also to sync all of your personal statuses. One of said social networking websites returns complex, site-specific HTML that obviously needs to be sanitized/canonicalized. The easiest (but not necessarily most memory-efficient) method of performing that task is to use the DOM. There is not currently a DOM library for the GNOME platform. There is a libgdom3 project (written in Vala) which I believe is being / will be used by the gnome2-globalmenu project, but it is unfinished. There is also an old, unmaintained library called gdome2 based on libxml2. I'm not even sure if anyone actually uses that library anymore (its last release was in 2003). I avoided using gtkmozembed and friends based on the experiences described above. I settled on a promising feature request for WebKit: a GObject/C DOM binding. (As an aside, the WebKit bug linked is a fascinating case study on several levels: conflicting coding standards, conflicting developer personalities, and some interesting coding/reviewing.) It's very nice - I can manipulate document fragments as if I were using JavaScript in a web page, among other things. I eagerly await that feature being committed to WebKit trunk.It is an important stepping stone when it comes to working with the web.

Another side project that I'm currently working on is a developer dashboard applet. It's kind of like the previous applet, except as applied to software projects. I was originally going to write it in Vala, which meant that I would have to write an interface to (at least) the Launchpad API, which meant implementing at least three specifications in Vala: OAuth, URI templates, and WADL. I finished the first two (I haven't yet decided whether to release my URI templates implementation as a separate library - the implementation plus the test app is 451 source lines of code), and WADL is a very complex specification. So, I decided to postpone working on the WADL library and instead am currently working on a prototype applet using Python and launchpadlib. Implementations for those three specifications and many others (including AtomPub) should be included in the GNOME platform if it wants to be web-enabled.

Wednesday, February 18, 2009

On Webhooks, or The Push Revolution

Mike Rooney (Awn Extras developer, among other things) posted an article on this "webhooks" idea. As I understand it, it's essentially a customizable, web-based version of the "Subscribe to future XYZ via email" features (e.g., blog comments) that are currently around. The key phrase in this sort of thinking is "push technology". Mike asks:

So will webhooks replace the current paradigm that I'm using here, or complement it?

I believe that it will complement the current paradigm. We need to have a transitional period (à la the old, rigid deadline for the US digital television transition) between the current "polling" techniques and the "new and shiny" (and arguably bandwidth-saving) push techniques. Take, for example, Twitter [1]. Let's assume that they didn't shut down their XMPP service, and they built upon it an (XML-based) API so that a client (for the purposes of this thought experiment, let's say my currently-in-vaporware Status Aggregator Awn applet) could connect to a specific JID (AKA user name + domain + "resource", or specific client) and listen for any new tweets, responses to my tweets, etc., replacing those messy timeout callbacks with messy async socket callbacks. The main benefit that I see is a savings in bandwidth for both the consumer and the producer. It avoids sending network requests every X minutes, which would add up, given the number of services/feeds that a user subscribes to (including mail). This actually leads me to my answer to Mike's other question:

Are webhooks the next step of this evolution, or something else entirely?

As evidenced by my thought experiment, I'd like to see XMPP as the next step, or at minimum, the step after webhooks. While I love HTTP, and am a big fan of the whole REST concept, it's hard for me to see it used as a facilitator for pushing data, as opposed to pulling it. In fact, given the way that ETags and the like are designed, HTTP is inherently a pull technology. The Comet model feels like a big kludge to me for that reason. XMPP, on the other hand, is designed to be a push technology, and its supporters are actively marketing it as such. It's also scalable, as servers like ejabberd and services like Google Talk can attest. I suppose the bottleneck here is a catch-22: you need both services and apps to buy into this particular implementation. Maybe when I finish one of the myriad projects I have going at once, I'll take a crack at adding a push-based web service client. Ideally, for configuration, all a user would have to do is set their app-specific JID (e.g. foo@example.com/bar_app) in both the web app and the client app, and it would "just work" (well, you would also have to set the JID password somehow as well, but that's beside the point).


Update (2009/02/18): It seems that I was subconsciously channelling a presentation on XMPP PubSub that I read over six months ago.


[1]I'm focusing on the one I actually use. Yes, I should be using identi.ca, since I am a supporter of free and open services/protocols. It even has the hallowed XMPP interface to microblogging. One of these days, I'll do what all the cool kids™ are doing and post to both. It'll probably happen when I (continue) work on the vaporware [2] mentioned above.
[2]It's vaporware until I push the source code onto a public server.

Sunday, February 15, 2009

Announcement: OAuth Client Library

For the past week or so, I've been working on various web services-related libraries, in the hopes of writing some sort of Launchpad dashboard applet for Awn. I think it would be nice to have new/recently changed bugs in Awn/Awn Extras at a glance in the dock, among other things. The first somewhat complete component is an OAuth client library. I finished the initial implementation (plaintext signature only) in under nine hours, and after the Awn 0.3.2 release (and a bit of frustration), I added HMAC-SHA1 signature support with the help of RFCs 2104 and 2202 (using GLib's checksum API). I've tested it with both the OAuth example Python server and the Launchpad API.

Also included in the source are two small test programs: one tests the HMAC-SHA1 implementation, and the other tests the OAuth implementation in its entirety. The latter uses .ini-style config files to define keys/secrets/URIs/etc. for a service.

There are a few things that still need to be done, in order for it to be a "complete" implementation. I probably want to replace my handwritten HMAC-SHA1 implementation with a libgcrypt-based one (although I'd have to figure out how to create bindings for Vala), which would also enable me to add RSA-SHA1 signature support. Additionally, I should probably add an asynchronous equivalent to the current synchronous API. Finally, it would be nice if it integrated better with libsoup's existing authentication structure. Currently, I just generate an Authorization: header to be manually added.

The next component I'm working on is a WADL dispatcher library. I don't like the idea of a library generating code (as I presume wadl2java does). We'll see how this turns out.