Month: April 2010

Apache

Apache and PHP HTTP PUT Voodoo

While trying to work out the details for a PHP REST utility I kept running into a wall when it came to using HTTP PUT (and HTTP DELETE) with Apache 2.2 and PHP 5. There are plenty of scattered tidbits of information relating to this on forums about the web, many old, and many more incomplete or even unhelpful. [As a side note: if someone on a forum you frequent is asking for help with getting HTTP PUT to work in Apache, telling them “Don’t use PUT it lets the hax0rs put files on your server! N00b! Use POST LOL!!11!” is not helping, nor does it make you look intelligent.]

The first hint I came across was putting Script PUT put.php in your httpd.conf in the <Directory> section. (That is, of course, assuming that your script for handling PUT requests is called put.php.)

I tried that and on restarting Apache got the error “Invalid command ‘Script’, perhaps misspelled or defined by a module not included in the server configuration” – which lead to a short bit of research (thanks Google!) that pointed out that the Script directive requires mod_actions be enabled in Apache. I did that and then tried to hit my script with a PUT request, to which I got a 405 error: “The requested method PUT is not allowed for the URL /test/put.php”.

Well, that was certainly strange, so I added <Limit> and <LimitExcept> blocks to my <Directory> section, but to no avail. So I changed the <Directory> directive from <Directory /var/www/test> to <Directory /var/www/test/put.php>. It looked strange, but what the heck, worth a try. I could now do PUT requests, but only as long as the url was /test/put.php, and that is not what is wanted when putting together a RESTful application. Trying to do anything useful, like a PUT to /test/put.php/users/ resulted in more 405 errors, now saying “The requested method PUT is not allowed for the URL /test/put.php/users/”.

So, back to the httpd.conf to change the <Directory> back to the previous. And then on to the other method I saw in a few places, using mod_rewrite to forward PUT (and DELETE) requests to the script. Of course, everywhere I saw this listed it was claimed that this alone (without the Script directive) was enough to enable PUT. So, I commented out the Script directive and added some mod_rewrite statements to the .htaccess file (which is always preferable in development as you can make changes on the fly without reloading or restarting the server.) So I added a RewriteCond %{REQUEST_METHOD} (PUT|DELETE) and a RewriteRule .* put.php.

And, I went back to test it again and, big surprise, got a 405 error again. Now, even when pointing directly at /test/put.php I got a 405 error. So, I decided to try combining the two. I uncommented the lines in the httpd.conf and bumped the server and was pleasantly surprised that PUT (and DELETE) requests to the /test/ directory were properly handled by the script. Now I could do something useful, like add another mod_rewrite rule to send all traffic for /api/ to the /test/put.php and call /api/users/ with a PUT (or DELETE) request and it was properly handled!

So, putting it all together:

In Apache: enable mod_actions and mod_rewrite. In Gentoo: make sure the lines

LoadModule actions_module modules/mod_actions.so

and

LoadModule rewrite_module modules/mod_rewrite.so

in httpd.conf are not commented out. In Debian the commands

a2enmod actions

and

a2enmod rewrite

do the trick.

In the httpd.conf add the following:

<Directory /var/www/test>
    <Limit GET POST PUT DELETE HEAD OPTIONS>
        Order allow,deny
        # You might want something a little more secure here, this is a dev setup
        Allow from all
    </Limit>
    <LimitExcept GET POST PUT DELETE HEAD OPTIONS>
        Order deny,allow
        Deny from all
    </LimitExcept>
    Script PUT /var/www/test/put.php
    Script DELETE /var/www/test/put.php
</Directory>

And finally, in the .htaccess add the rewrite voodoo:

RewriteEngine On
RewriteBase /test
RewriteRule ^/?(api)/? put.php [NC]
RewriteCond %{REQUEST_METHOD} (PUT|DELETE)
RewriteRule .* put.php

Hopefully this works as well for you as it did for me. Now to get back to business of actually writing the code to deal with the request and dispatch it appropriately (which may be a post for another day, or you can have a look at how some others have done it.)

By the way, for testing I have found the Firefox plugin Poster to be immensely useful, as well as the Java based RESTClient.

Home

I need a break …

I don’t usually talk much about my day-to-day life here, but that doesn’t mean I never do. This is one of those times. If you just want more tech talk check out the end of this post. The rest is all me whinging anyway. ;)

I need a break. A real break. I mean, I am technically on a break right now from school but it doesn’t really feel that way. I finished out my first year of school a couple weeks ago (26 years after graduating from high school, no less) and I thought “wow, I have an entire month that I can use to rest, catch up on some personal stuff, maybe clean out the garage ….” Unfortunately it is not turning out that way.

Instead I am writing this at 5:54 in the morning as this has been the first chance I have had to pay any attention at all to the blog. So what has been keeping me busy? Well, first, there is work. I did use a little of what would have been study time to modify the script I use to generate weekly work reports from Trac so that it now shows the amount of change for hours on each ticket (which is set in a “custom” field). And holy cow, I put in 58.5 hours last week. At least 8 of that doesn’t really count, though. I messed my back up and spent some time trying to work while under the influence of cyclobenzaprine which means that I wrote, scrapped and rewrote one class method at least 6 times before finally giving up. (Programming and drugs that make you stupid don’t mix!)

Aside from work I have been putting some time into a project for a non-profit that is kicking off on May 12th. I’m not allowed to say too much about it ahead of launch, but I can say that it is about raising awareness about ME/CFS and how badly it has been mismanaged and patients marginalized for the past 25 years.

Finally, I upgraded the WordPress plugin Shorten2Ping which I will continue to pimp as long as it keeps working so well. Of course I like my post tweets to have some hashtag love, so I do a little editing of the shorten2ping.php.

Here is a diff:

--- shorten2ping/shorten2ping.php       2010-04-12 10:22:34.000000000 -0700
+++ shorten2ping.mine/shorten2ping.php  2010-04-19 06:47:58.000000000 -0700
@@ -119,6 +119,15 @@
     $post_url = get_permalink($post_id);
     $post_title = strip_tags($post->post_title);

+               // add some tag bits here
+               $tags = wp_get_post_tags($post_id);
+               $my_tag_list = '';
+               if (is_array($tags)) {
+                       foreach ($tags as $j=>$tag) {
+                               $my_tag_list .= '#'.$tag->slug.' ';
+                       }
+               }
+
     $short_url_exists = get_post_meta($post_id, 'short_url', true);

              if (empty($short_url_exists)) {
@@ -205,9 +214,19 @@

             //get message from settings and process title and link
             $message = $s2p_options['message'];
+                                               $message_bare_char_count = strlen(str_replace(array('[title]','[link]','[tags]'), '', $message));
+                                               $title_count = strlen($post_title);
+                                               $link_count = strlen($short_url);
+                                               $tag_count = strlen($my_tag_list);
+                                               $over = $message_bare_count + $title_count + $link_count + $tag_count - 140;
+                                               if ($over > 0 && $over <= $post_title/2) {
+                                                       // if the overage is more than half the post title then skip it and let tags get truncated
+                                                       $post_title = substr($post_title, 0, $title_count - $over);
+                                               }
             $message = str_replace('[title]', $post_title, $message);
                              $message = str_replace('[link]', $short_url, $message);
-
+                                               $message = str_replace('[tags]', $my_tag_list, $message);
+
             if ($s2p_options['ping_service'] == 'pingfm'){

                send_pingfm($pingfm_user_key,$post_id,$message);

(You can download the diff as well.)

Ooops! Draft Saved at 6:23:27 am. And it is now 8:10, and this would still be a draft if I wasn’t closing browser tabs.