Absolute URL protocol

I’m having a lot of issues creating URL’s in SSL enviroments, everything would be easier if the method createAbsoluteUrl() by default would create url’s starting with //

so instead of http://mysite.com/param/param it would create //mysite.com/param/param

Why can’t you just use third parameter of this method ($schema)




public string createAbsoluteUrl(string $route, array $params=array ( ), string $schema='', string $ampersand='&')



$schema - schema to use (e.g. http, https). If empty, the schema used for the current request will be used.

That’s an option, but it requires extra unnecessary logic, using // by default is shorter, prettier and more reliable

furthermore, for debugging it’s easier to see what the purpose of the url is:

// -> use current protocol

http:// -> force non-SSL

https:// -> force SSL

Is there any information if such a URL is standards compliant? And if it’s recognized by all browsers?

Such a change could also break some code, e.g. say you have created URLs to be part of an email. I’m not sure, if email clients will like this format. So in this case you’d have to add the third parameter in the call to createAbsoluteUrl().

Yes, it’s standards compliant. Mentioned in one of RFCs. There’s a downside that prevents us from really using it: http://www.stevesouders.com/blog/2010/02/10/5a-missing-schema-double-download/

My own approach to this issue is a derived base-class from CController for my application’s controllers, and adding a property that allows each controller to specify whether it needs SSL or not:




/**

 * Base class for all controllers in the application

 */

class Controller extends CController

{

  /**

   * @var mixed if true, SSL is required - if false, SSL is not allowed - if null, both are acceptable

   */

  public $https = false;

  

  /**

   * Integrate GAuthorizationFilter

   */

  public function filters()

  {

    return array(

      array('GSchemaFilter', 'https'=>$this->https),

    );

  }

}


/**

 * This filter enforces HTTP or HTTPS protocol for the request, by aborting and redirecting

 * the request to the same URL, but with the correct protocol specifier applied.

 *

 * You can globally bypass this filter using define('NO_SSL', true) in your configuration

 * file - this is useful while developing/testing on a machine that does not have an SSL

 * certificate installed.

 */

class GSchemaFilter extends CFilter

{

  /**

   * @var mixed if true, SSL is required - if false, SSL is not allowed - if null, both are allowed

   */

  public $https=null;

  

  /**

   * Apply schema-based filtering

   */

  public function preFilter($filterChain)

  {

    if (defined('NO_SSL') && NO_SSL)

      return true;

    

    if ($this->https===false)

      $this->requireSchema('http');

    if ($this->https===true)

      $this->requireSchema('https');

    

    return true;

  }

  

  /**

   * Aborts the request, redirects and switches schema, if required.

   *

   * @param string the required schema for this request, e.g. 'http' or 'https'

   */

  protected function requireSchema($schema)

  {

    $secure = Yii::app()->getRequest()->getIsSecureConnection();

    

    $c = Yii::app()->getController();

    

    if (($schema==='http' && $secure) || ($schema==='https' && !$secure))

      $c->redirect($c->createAbsoluteUrl('', $_GET, $schema), true);

  }

}




This will automatically redirect and switch protocols as required by your controllers - so for example, your login or checkout controller in a store might have $https=true which would prevent them from loading insecurely on the server-side. I like this approach better than trying to make sure the URLs are correct.

Note that HTTP POST from a secure to a non-secure page (or vice-versa) will not work. This is by design, since this would cause a warning in most browsers anyway. And typically this doesn’t cause any problems, as this setting is per-controller. In other words, the first time you hit CheckoutController::actionIndex() it’ll redirect to switch to SSL - and if you go back to ProductController::actionIndex() it’ll redirect to switch it off again.

I’m using this on several live sites for some years now, and it works well :slight_smile:

For local development/testing without an SSL certificate, define(‘NO_SSL’,true) will disable this filter.