Drupal

Apache [client ::1] requests, broken pipes, virtual hosts and Drupal

I was puzzling over entries like this in Apache 2.2.3's error log:

[info] [client ::1] (32)Broken pipe: core_output_filter: writing data to the network

A check for which user agent was causing that revealed:

::1 - - [01/Dec/2007:04:08:53 +0000] "GET / HTTP/1.0" 200 85781 "-" "Apache/2.2.3 (CentOS) (internal dummy connection)"

So it's Apache's internal dummy connection throwing the errors. Oh, but what's this?

::1 - - [01/Dec/2007:04:08:53 +0000] "GET / HTTP/1.0" 200 <strong><span style="text-decoration: underline">85781</span></strong> "-" "Apache/2.2.3 (CentOS) (internal dummy connection)"

Yikes! Here's what is happening.

1. Apache opens an internal dummy connection with a request for /.
2. On this server, / turns out to be a Drupal page.
3. Drupal spends time and effort putting together the page and sends it...
4. ...to the dummy connection that ignores it resulting in the broken pipe.

It turned out that in httpd.conf, an include statement which pulled in a virtual host configuration was being encountered before the regular virtual host configuration...making the included virtual host configuration the default host for all requests, including internal Apache requests.

I solved this by making sure the default page of the first virtual host is not a dynamic page.

Topic: 

Blank white pages when submitting forms in Drupal

This has bitten me enough that I'm going to write a blog post about it so when I search for this issue, I will find my own blog post and say, "Duh!"

Recently we had one site in development that presented a white screen whenever a form was submitted. Other sites right next to it worked fine. The form was being processed. But no errors were appearing either on the screen or in the error log for Apache or PHP -- all we saw was a blank page.

Then I noticed that the output_buffering setting in php.ini was set to Off. I turned it on and it solved the problem. But I wanted to know why it solved the problem. Here's why, and why it doesn't really solve the problem.

When Drupal's form handler completes processing of a form submission, it normally does a 302 redirect. The code responsible for that is in drupal_goto() and looks like this:

  header('Location: '. $url, TRUE, $http_response_code);

  // The "Location" header sends a redirect status code to the HTTP daemon. In
  // some cases this can be wrong, so we make sure none of the code below the
  // drupal_goto() call gets executed upon redirection.
  exit();

Running the code through a debugger showed that indeed, we were reaching the exit() call.

If any of your code has sent anything back to the browser, PHP has already sent the headers to the browser. If output_buffering is Off, Drupal cannot take that back and say no, really we meant to send you a 302. But if output buffering is on, the headers are not really sent, so calling header() still works. That's why turning on output buffering solves the problem.

But that's just treating the symptom. The real problem is that somewhere in your code, something is being sent back to the browser when it shouldn't be. This is the problem being referred to when the Drupal coding standards say, Note that the final ?> should be omitted from all code files--modules, includes, etc. The closing delimiter is optional, and removing it helps prevent unwanted white space at the end of files which can cause problems elsewhere in the system.

In my case, a designer had put a drupal_add_js(..., 'inline') call directly into template.php. Moving the JavaScript to a .js file solved the real problem.

Topic: 

Installing Drupal on Mac OS X 10.5 Leopard

I thought I'd write up the steps I took to get Drupal running on a stock Leopard installation. You may wish to save some time and install MAMP instead. Especially if you need GD support (i.e., you're going to have Drupal do image resizing). Update 17-Dec-2007: In fact, I recommend using MAMP instead.

Step 1: Enable PHP

Uncomment line 114 in /etc/apache2/httpd.conf to enable Leopard's built-in PHP:

LoadModule php5_module        libexec/apache2/libphp5.so

Start Apache 2 by using the Sharing panel in Preferences or at the command line with the following:

sudo apachectl start

(If Apache was already running, use restart instead of start.)

Place a test document into the default htdocs root to see if php is running. I created /Library/WebServer/Documents/phpinfo.php with the following content:

<?php phpinfo(); ?>

Now going to http://localhost/phpinfo.php shows me the info page for PHP 5.2.4. Yay!

Step 2: Friendly Virtual Hosts in Apache

I don't like keeping my websites in /Library/WebServer/Documents. It's a cumbersome place; I'd much rather keep them in /Users/john/Sites. That's right in my home directory and when I copy or sync my home directory I get the sites I'm working on, too. But using Leopard's built-in URL support for my home directory is verbose, too:

http://localhost/~john/sitename

I'd much rather use a nice short URL like http://dev/sitename. So first I assigned the name dev to my computer by adding a line to /etc/hosts:

##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1 dev
127.0.0.1 localhost
255.255.255.255 broadcasthost
...

Since Leopard caches DNS queries, we force it to reread /etc/hosts by using dscacheutil which replaces the lookupd utility that was in OS X 10.4.

dscacheutil -flushcache

I changed /etc/apache2/users/john.conf from

<Directory "/Users/john/Sites/">
    Options Indexes MultiViews
    AllowOverride None
    Order allow,deny
    Allow from all
</Directory>

to

<Directory "/Users/john/Sites/">
    Options Indexes MultiViews FollowSymLinks
    # Allow .htaccess files to override httpd.conf.
    AllowOverride All
    # No access allowed.
    Order deny,allow
    Deny from all
    # Except from this machine.
    Allow from 127.0.0.1
</Directory>

# Enable virtual hosts.
NameVirtualHost *:80

# Point virtual host to our directory.
<Virtualhost *:80>
    DocumentRoot /Users/john/Sites
    Servername dev
</Virtualhost>

You can test that everything works and you didn't make any typos by using

sudo apachectl configtest

which should tell you that the syntax of your Apache configuration files is OK (it will point you to the line containing the error otherwise). If all is OK, restart Apache to effect the changes:

sudo apachectl restart

Now you should be able to go to http://dev/ in your browser, and the file at /Users/john/Sites/index.html should be displayed.

Installing MySQL

There was no friendly installer for OS X 10.5 so I used the OS X package for 10.4. It ran fine.

It was unclear from my web searches whether the startup item for MySQL is working on Leopard. So I created the following file at /Library/LaunchDaemons/com.mysql.mysqld.plist to autostart MySQL (thanks Joannou Ng):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>KeepAlive</key>
	<true/>
	<key>Label</key>
	<string>com.mysql.mysqld</string>
	<key>Program</key>
	<string>/usr/local/mysql/bin/mysqld_safe</string>
	<key>RunAtLoad</key>
	<true/>
</dict>
</plist>

Before I restarted, I wanted to make sure that mysql would be in my PATH environment variable when I restart. So I created a file at /etc/paths.d/mysql containing

/usr/local/mysql/bin

For more information on this, type man path_helper.

Then I restarted to make sure that MySQL would launch. Yes, it's running:

ps -ax | grep my
   40 ??         0:00.01 /bin/sh /usr/local/mysql/bin/mysqld_safe
   88 ??         0:00.11 /usr/local/mysql/bin/mysqld --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data --user=mysql --pid-file=/usr/local/mysql/data/localhost.pid

Now to set it up securely.

mysql_secure_installation

This allows you to set a root password, disallow remote root logins, and generally tighten up MySQL security.

It's nice to be able to tweak MySQL parameters, so I created a my.cnf file:

sudo cp /usr/local/mysql/support-files/my-large.cnf /etc/my.cnf

The only thing I tweaked in the my.cnf file was to add one line under the [mysqld] heading to prevent MySQL from listening on port 3306 (I don't like unnecessary open ports):

skip-networking

Now I can create a database for Drupal to use:

mysql -uroot -p
mysql> CREATE DATABASE drupaldb;
Query OK, 1 row affected (0.00 sec)

One more thing. PHP and MySQL are confused about which socket to use (/tmp/mysql.sock vs. /var/mysql/mysql.sock). So let's provide a symlink so they can use either one:

sudo mkdir /var/mysql
sudo ln -s /tmp/mysql.sock /var/mysql/mysql.sock

Installing Drupal

Pull down a copy of Drupal 5 from the CVS repository:

cd ~/Sites
cvs -z9 -d:pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal checkout -r DRUPAL-5 drupal

Change permissions on the settings file so the installer can modify it:

chmod o+w drupal/sites/default/settings.php

Run the Drupal installer by going to http://dev/drupal.

Remove the permissions from the settings file:

chmod o-w drupal/sites/default/settings.php

And create Drupal's files directory:

mkdir /Users/john/Sites/drupal/files
sudo chown www /Users/john/Sites/drupal/files

Rejoicing

Now I've got an easy setup where I can create my Drupal sites under /Users/john/Sites and refer to them with short names like http://dev/foo. MySQL is running nicely. Public access to my dev sites is disabled by Apache. Life is good.

Drupal wallpaper

Surprisingly, a search for Drupal wallpaper turns up nothing.

Update: Vadim Barsukov pointed me to one by Steven. But we need more inspiring wallpaper. Or perhaps live wallpaper that gives graphical hourly updates of patch queues and such. And a pony.

Topic: 

Pages

Subscribe to RSS - Drupal