Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Some changes for better Nginx compatibility #26

Merged
merged 4 commits into from

2 participants

@sloonz

The three patches are independent, but I couldn’t find how to make three distinct pull requests in github…

Unit tests: Nginx doesn't accept PUT requests without request body. Because of that, some tests doesn't pass (all tests involving PUT requests). a4d61ad fixes that.

base_uri: In nginx, url rewriting using match/replace (^(.*)$ index.php/$1) is tricky not widely used. The best way to achieve pretty-urls is to use :

try_files $uri $uri/ index.php

Which is equivalent to the (growing popular) apache rule

 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteRule ^(.*)$ index.php [QSA,L]

However, this is not compatible with the current request_uri() implementation. 12955a3 fixes that.

Patch 5eaebb7 : $query_string is used in preference to $request_uri in request_uri(). This raises an important issue : assuming that the desired method by the webmaster/web developer to determine routing is request_uri (which is always the case in the example above), route http://example.com/app/route will work fine whereas http://example.com/app/route?foo=bar will not work. This is really bad IMO. We try to avoid this by not considering query_string being a valid route if it doesn't starts with '/'.

sloonz added some commits
@sloonz sloonz Fix for running unit tests on nginx a4d61ad
@sloonz sloonz Don't try to use query_string to determine request_uri() if
it doesn't looks like a proper route

Query string can be used to determine routing when used this
way :

 http://test/index.php?/route

However, this method consume GET arguments even if query string
is not intended to be used for routing. To fix that, we don't
even try to use query string for routing if query string doesn't
starts with /.
5eaebb7
@sloonz sloonz Allow to use base_uri option for url routing.
Right now, the following methods are used to determine request_uri(),
used in routing :

* u or uri GET params
* PATH_INFO (index.php/uri)
* QUERY_STRING (index.php?/uri)
* REQUEST_URI, which is just the equivalent of the two above methods
  but for webservers which doesn't set PATH_INFO or QUERY_STRING

The problem with this is that none is controllable via user
configuration.

This patch modify the last method to try to use option('base_uri') in
addition to the internally computed base_path. For example, if
REQUEST_URI is /app/myroute and option('base_uri') is '/app', then
request_uri() (if not other methods are used) will return '/myroute'.

The main usage of this new method will be to allow rewrite rule of
this form :

 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteRule ^(.*)$ index.php [QSA,L]

Which is the preferred method for "pretty"-urls in Nginx.
12955a3
@sofadesign
Owner

Thanks all for those good patches (I love Nginx!). Before I merge your pull request, can you confirm that tests are still passing ? Is there some changes/side effects that require a note in the README ?

@sloonz

Yes, the tests are still passing, at least on Nginx (no apache on my server ;))

Obviously no side effect for a4d61ad, or I think we have a serious problem ;)

For 5eaebb7 : prior to the patch, index.php?myroute was a synonym to index.php?/myroute. This is not the case anymore, only the index.php?/myroute form will be accepted as a valid route.

The description of the patch is bad, however. The issue it addresses is not that the query string is consumed, but that in the presence of a non-routing query string, the next part of the code is never called, which make the next patch (routing with request uri + base_uri option) pretty much useless.

For 12955a3 : since, if my understanding is right, this part of the code is rarely used (it just try to determine query_string/path_info from request_uri if the web server doesn't set them, but if that’s the case 1. that’s likely a configuration error, and php may have trouble running 2. php will try to build them itself if cgi.fix_pathinfo is on, which is the case by default), so it shouldn’t have any side-effect :)

For this last patch, note that I ran into an issue a few days ago : since query string is part of request uri, for /foo?bar the route is '/foo?bar' and not '/foo', which obviously leads to a 404 error when the request has a query string. I’ll probably push a fix for this at the begining of next week.

@sloonz

Like stated in my previous message, i fixed the issue with query string. The patch works fine for me now (it's in all my sites since monday)

@sofadesign sofadesign merged commit 2c15fc5 into sofadesign:master
@sofadesign
Owner

I just merged your patches. Thanks a lot for your contribution and your explainations !

@sloonz

Thanks for the merge :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 21, 2012
  1. @sloonz
Commits on Feb 28, 2012
  1. @sloonz

    Don't try to use query_string to determine request_uri() if

    sloonz authored
    it doesn't looks like a proper route
    
    Query string can be used to determine routing when used this
    way :
    
     http://test/index.php?/route
    
    However, this method consume GET arguments even if query string
    is not intended to be used for routing. To fix that, we don't
    even try to use query string for routing if query string doesn't
    starts with /.
  2. @sloonz

    Allow to use base_uri option for url routing.

    sloonz authored
    Right now, the following methods are used to determine request_uri(),
    used in routing :
    
    * u or uri GET params
    * PATH_INFO (index.php/uri)
    * QUERY_STRING (index.php?/uri)
    * REQUEST_URI, which is just the equivalent of the two above methods
      but for webservers which doesn't set PATH_INFO or QUERY_STRING
    
    The problem with this is that none is controllable via user
    configuration.
    
    This patch modify the last method to try to use option('base_uri') in
    addition to the internally computed base_path. For example, if
    REQUEST_URI is /app/myroute and option('base_uri') is '/app', then
    request_uri() (if not other methods are used) will return '/myroute'.
    
    The main usage of this new method will be to allow rewrite rule of
    this form :
    
     RewriteCond %{REQUEST_FILENAME} !-f
     RewriteRule ^(.*)$ index.php [QSA,L]
    
    Which is the preferred method for "pretty"-urls in Nginx.
Commits on Mar 14, 2012
  1. @sloonz
This page is out of date. Refresh to see the latest.
Showing with 9 additions and 3 deletions.
  1. +8 −2 lib/limonade.php
  2. +1 −1  lib/limonade/tests.php
View
10 lib/limonade.php
@@ -1044,7 +1044,7 @@ function request_uri($env = null)
$uri = $path_info;
}
// No PATH_INFO?... What about QUERY_STRING?
- elseif (trim($query_string, '/') != '')
+ elseif (trim($query_string, '/') != '' && $query_string[0] == '/')
{
$uri = $query_string;
$get = $env['GET'];
@@ -1058,11 +1058,17 @@ function request_uri($env = null)
}
elseif(array_key_exists('REQUEST_URI', $env['SERVER']) && !empty($env['SERVER']['REQUEST_URI']))
{
- $request_uri = rtrim(rawurldecode($env['SERVER']['REQUEST_URI']), '?/').'/';
+ $request_uri = rtrim($env['SERVER']['REQUEST_URI'], '?/').'/';
$base_path = $env['SERVER']['SCRIPT_NAME'];
if($request_uri."index.php" == $base_path) $request_uri .= "index.php";
$uri = str_replace($base_path, '', $request_uri);
+ if(option('base_uri') && strpos($uri, option('base_uri')) === 0) {
+ $uri = substr($uri, strlen(option('base_uri')));
+ }
+ if(strpos($uri, '?') !== false) {
+ $uri = substr($uri, 0, strpos($uri, '?')) . '/';
+ }
}
elseif($env['SERVER']['argc'] > 1 && trim($env['SERVER']['argv'][1], '/') != '')
{
View
2  lib/limonade/tests.php
@@ -334,7 +334,7 @@ function test_request($url, $method="GET", $include_header=false, $post_data=arr
curl_setopt($curl, CURLOPT_HTTPHEADER, $http_header);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
- if($method == 'POST')
+ if($method == 'POST' || $method == 'PUT')
{
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
Something went wrong with that request. Please try again.