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.
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/binFor 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.pidNow 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.sockInstalling 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 drupalChange 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/filesRejoicing
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.
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*
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.
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
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.
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.exeI 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 0I 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.
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.
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.
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.



