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.

[ Submitted by John on Tue, 2007-11-13 22:57. | | ]

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.

[ Submitted by John on Wed, 2007-11-07 17:00. | | ]

Leopard and SoftRAID

If you use a RAID 0 array created by SoftRAID 3.6.4 as the boot drive on your Intel Mac and you feel the urge to install Leopard on it, don't. Just be patient until SoftRAID 3.6.6 comes out. Also, get your head examined. You should not be running striped RAID as your boot drive! *grin*

[ Submitted by John on Tue, 2007-11-06 20:17. | | ]

Leopard firewall

OS X Server 10.4 has an excellent firewall. It has a nice UI, lets you rearrange firewall rules by dragging and dropping, and is basically a sweet interface to ipfw. I haven't checked if they've made any changes or improvements on OS X Server 10.5.

What I can tell you is that it is a crying shame that Apple didn't take that nice firewall interface and put it into OS X 10.5. I understand their motivation (make it easy for Aunt Betty to feel secure) but I want more control. More at Securosis.com and Leopard firewall takes one step forward, three steps back. Alternatives are WaterRoof and DoorStop.

[ Submitted by John on Tue, 2007-11-06 09:46. | | ]

Watching traffic on FreeBSD

Ben Wong at MostlyGeek pointed out that a great way to watch traffic on FreeBSD is the command systat -ifstat 1 which shows:

                    /0   /1   /2   /3   /4   /5   /6   /7   /8   /9   /10
     Load Average   ||   

      Interface           Traffic               Peak                Total


            lo0  in      0.000 KB/s          0.000 KB/s            4.122 MB
                 out     0.000 KB/s          0.000 KB/s            4.122 MB

           bge0  in      6.181 KB/s         36.944 KB/s            1.666 GB
                 out   110.521 KB/s        670.040 KB/s            3.444 GB
[ Submitted by John on Fri, 2007-11-02 10:53. | | ]

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.

[ Submitted by John on Fri, 2007-10-26 13:23. | | ]

IIS6.0, FastCGI Go Live, Windows Server 2003

With all the hoopla about FastCGI on IIS I thought I'd give it a whirl. Here's what I did.

Installed Windows Server 2003 R2 Standard Edition.

Selected Application Server role. This installs IIS 6.

Setup autologin since this is a disposable virtual machine.

Fought IE7's draconian security popups to download Firefox.

Downloaded mysql-essential-5.0.45-win32.msi.

Downloaded php-5.2.4-nts-Win32.zip. (This non-thread-safe version is recommended to use FastCGI).

Extracted the contents to C:\php.

Went to My Computer - Properties - Advanced - Environmental Variables and added ,C:\php to the PATH variable.

Renamed php.ini-dist to php.ini. I used php.ini-dist since this is just a development box.

In php.ini:

- uncommented fastcgi.impersonate = 1 since I plan to run it with IIS.
- set doc_root = "C:\Inetpub"
- set session.save_path = "C:\php\sessiondata"

Opened port 80 in the firewall. I can now send a request to IIS and get back a response in my web browser. The response is an Under Construction page since I haven't set a default page in IIS yet. Made a index.htm file and said Hello World.

Downloaded FastCGI Extension for IIS6.0.

The End-User License Agreement includes this: "You may not disclose the results of any benchmark tests of the software to any third party without Microsoft's prior written approval". Thus you will not see any benchmarks from me.

And this: "If you give feedback about the software to Microsoft, you give to Microsoft, without charge, the right to use, share and commercialize your feedback in any way and for any purpose." Thus you will not see any feedback about the Microsoft FastCGI Extension for IIS 5.1 and 6.0 Go Live software from me.

Configured FastCGI according to these instructions.

My C:\WINDOWS\system32\inetsrv\fcgiext.ini file contains the minimum:

[Types]
php=PHP
[PHP]
ExePath=C:\php\php-cgi.exe

I created a basic hello.php file containing

<?php
 
echo "Hello world from PHP."
?>
.

I can now do \php\php.exe \Inetpub\wwwroot\hello.php at the command line and it works.

But hitting it through IIS results in

FastCGI Error

The FastCGI Handler was unable to process the request.


Error Details:

  • The FastCGI process exited unexpectedly
  • Error Number: -2147467259 (0x80004005).
  • Error Description: Unspecified error

HTTP Error 500 - Server Error.
Internet Information Services (IIS)

Using Rick James' Fake FastCGI Web Server to say

C:>fakefcgi.exe c:\Inetpub\wwwroot\hello.php c:\php\php-cgi.exe

I get

Fake FastCGI web server
FCGI_PARAMS sent
FCGI_STDIN sent
Launching receive loop
FCGI_STDOUT: Status: 404
X-Powered-By: PHP/5.2.4
Content-type: text/html
No input file specified.
FCGI_END_REQUEST received
killing app
FastCGI process exited with 0

I installed procmon and discovered that the IIS user (NETWORK SERVICE) did not have Execute access to C:\php\php5.dll. Once that was granted, my hello.php ran.

The next problem was that I wanted to set up Drupal, so clearly I need to run MySQL. But PHP would not load the C:\php\ext\php_mysqli.dll file. Again, a permissions issue solved by giving NETWORK SERVICE access. Clearly I must have missed the permissions section of the PHP installation docs.

Thanks to Drew Robbins of Microsoft for debugging the extension permissions issues.

[ Submitted by John on Tue, 2007-10-23 14:42. | | ]

Mann pages

For a long time I've just used the man command to look up man pages inside Terminal. I find this frustrating. I downloaded a copy of ManOpen years ago but for whatever reason I don't use it. Mostly because I have to resize the window every time. Anyway, fast forward to today when I resolved to spend 10 minutes solving this problem. Here's my solution.

1. Download Bwana and drag it to your OS X Applications folder.
2. Add the following line to your /Users/username/.profile file:

mann() { open man:$@; }

This adds a bash function named mann that opens the man page in Safari, complete with hyperlinks and searchability. So now I can type, e.g., mann lookupd. Yay! And it has the following bonus: whenever I look up a unix command, I now think of Boris.

[ Submitted by John on Tue, 2007-10-09 13:22. | | ]

Solution to iMovie 6.0.3 consistent crashes

For a while now, my copy of iMovie HD has been crashing when I try to open it. I've been putting off finding the solution since I haven't needed to do much with iMovie, but today I needed it.

I first reinstalled the iMovie 6.0.3 update. No luck. Then reinstalled QuickTime 7.2. Still crashing. An examination of ~/Library/Logs/CrashReporter/iMovie HD.crash.log showed this thread as the culprit:

Thread 5 Crashed:
0   com.apple.CoreFoundation       	0x90811125 CFRetain + 56
1   com.apple.MediaBrowser         	0x67e01f56 -[MediaGrabberiTunes _loadMusicThreaded] + 733
2   com.apple.Foundation           	0x927f52e0 forkThreadForFunction + 123
3   libSystem.B.dylib              	0x90024227 _pthread_body + 84

That led me to a thread on iPhoto crashes on Apple's discussion forum, with solution posted by Terence Devlin: start up GarageBand.

I started up GarageBand, quit it, and now iMovie HD is working fine. Although Terence recommended saving something from GarageBand, I found that just starting the application was enough. Apparently something in GB's initialization routine fixes the problem.

[ Submitted by John on Tue, 2007-09-25 09:08. | | ]

Daily Paul moves to a VPS

In Drupal, Grassroots Political Activism and the Ron Paul Campaign I mentioned the site Daily Paul. Michael Nystrom, who runs the site, was seeing serious performance problems as the site became more popular. This is Michael's first site with Drupal, but he had made all the right moves: using throttle.module, turning on caching and CSS optimization, and even upgrading to a faster hosting plan.

I contacted Michael and we began going through various scenarios. Was the slowdown consistent? No. Were ping times consistent? Yes. What about the MySQL configuration? And so on. It turns out that Michael's host (Dreamhost) did not have the query cache turned on in MySQL, and refused to turn it on. That meant that every query for every noncached page had to be tediously run by the database server. When the server got busy, Michael's site suffered, badly, though the server that ran Apache and PHP was idling.

Since I had recently gotten a VPS from Rimuhosting but had not set it up yet, we used it as a testbed for Michael's site. Last weekend we switched DNS and the new site began to pick up traffic. After paring down Apache, tweaking PHP, and tuning MySQL, the speed increase was clear. Last night Ron Paul participated in a debate in Florida which drew traffic to the site, up to 30-40 simultaneous users. Load average on the server was about 0.50. The two big wins were (1) having MySQL on the same server and (2) enabling query caching.

Last night I handed the VPS off to Michael with the help of the good folks at Rimuhosting. It's just a little VPS, but it sure made a big difference.

Note: of course, a VPS has a serious downside: it must be maintained.

[ Submitted by John on Tue, 2007-09-18 20:54. | ]