≡

wincent.dev

  • Products
  • Blog
  • Wiki
  • Issues
You are viewing an historical archive of past issues. Please report new issues to the appropriate project issue tracker on GitHub.
Home » Issues » Feature request #1568

Feature request #1568: Ability to serve static HTML files under the "products" namespace

Kind feature request
Product wincent.dev
When Created 2010-06-13T13:44:17Z, updated 2010-07-24T12:20:52Z
Status closed
Reporter Greg Hurrell
Tags no tags

Description

Would be nice to be able to upload static HTML files somewhere "under" the "products" namespace.

For example, the generated RDoc pages for the Wikitext extension could be accessed at:

  • https://wincent.dev/products/wikitext/rdoc

Or similar.

Comments

  1. Greg Hurrell 2010-06-14T13:38:24Z

    I was going to say that perhaps the existing page model could be abused for this purpose.

    We currently have the ability to pull wikitext markup or HTML out of the database for the product pages.

    A third option could be to somehow reference the content on the disk (sendfile style or something, ugh).

    But really, the whole point of having this kind of static content is to allow nginx to serve it without Rails ever being touched.

    Not really sure though if the rewrite trickery that would be required is worth it.

  2. Greg Hurrell 2010-06-20T13:23:00Z

    nginx notes

    Will need to check this, but I believe nginx will only hit Rails if the requested filename does not exist:

    if (-f $request_filename) {
      break;
    }

    ie. if the directory exists, but no file does, it will still hit Rails.

    This is actually what we want.

    That is, given these directories /public/products/synergy/, we don't want nginx to try to serve those itself and effectively mask the products#show action for URLs like https://wincent.dev/products/synergy.

    But we do want it to serve a static file like /public/products/synergy/docs/index.html if one is uploaded.

    Still, like I said, must test it. The config does have this set up:

    index index.html;

    So I don't know whether the presence of the directory alone will be enough to make it look for index.html and if it can't find it, return a 404 rather than forwarding the request to Rails.

    Static page types

    There are really two types of static content that we might wish to serve:

    • stand-alone HTML pages: we want these to be served by nginx without hitting Rails at all if possible
    • HTML fragments, to be embedded in the existing site layout: by necessity, these need to be mediated by Rails itself

    The alternative

    If the nginx thing doesn't work out for the first page type, there is an alternative.

    We can use the "asset host" trick to give us URLs which at least in part look similar to the product URLs on the main site; eg.

    • use: http://assets.example.com/products/synergy/doc (or whatever other subdomain may seem more appropriate than "assets")
    • matching: https://wincent.dev/products/synergy/

    So will do some more investigation and see which option we have to pursue.

  3. Greg Hurrell 2010-06-20T16:17:51Z

    Ah, silly me. I just remembered that the way page caching works under nginx, I already know the answer to my doubts above.

    For example, we have a bunch of static cache files like:

    • /twitter.html
    • /twitter.atom
    • /twitter/1.html

    So, yes, in the absence of an /twitter/index.html file, requests for /twitter still wind up at the Rails twitter#index action whenever the cache files are cleared.

    The only question remaining, then, for this type of static file, is how do we preserve such files across deployments (following the Capistrano pattern, everything in public is blown away (including all the cache files) except for stuff under public/system which is actually a symlink elsewhere.

    The question about what to do about the other type of static file (the one we want to render embedded in the Rails-provided layout) still remains. But I have to pop out now, so no time to think it through.

  4. Greg Hurrell 2010-06-20T17:13:11Z

    Thinking about it, switching to an "assets" subdomain would have a pretty big downside: would lose the ability to enter "relative" URLs in links (that is, /products/foo/baz rather than http://assets.example.com/products/foo/baz).

  5. Greg Hurrell 2010-07-23T13:54:36Z

    One idea to make the deployments easier would be to have a parallel hierarchy which nginx checks first. This would slow it down, but the thing is so fast that it might not even be measurable.

    ie. given Rails app at /rails/current and static files at /rails/static, nginx would do something like:

    # check static area first
    if (-f $static_root/$request_filename) {
      break;
    }
    
    # serve static content without hitting Rails
    location ~ ^/(images|javascripts|stylesheets)/ {
      expires 10y;
    }
    if (-f $request_filename) {
      break;
    }
    
    # show maintenance page (added by deploy script) if present
    if (-f $document_root/system/maintenance.html) {
      rewrite ^(.*)$ /system/maintenance.html last;
      break;
    }
    
    # cached pages: only for GET requests
    set $cache_extension '';
    if ($request_method = GET) {
      set $cache_extension '.html';
    }
    
    # the above is a hack because nginx doesn't allow nested or ANDed ifs
    if (-f $request_filename$cache_extension) {
      rewrite (.*) $1.html break;
    }
    
    # everything else goes to Unicorn
    if (!-f $request_filename) {
      proxy_pass http://unicorn;
      break;
    }

    The above is based on quite an old config, and I suspect it could be simplified at least in part by using the try_files directive.

  6. Greg Hurrell 2010-07-24T11:59:50Z

    Ok, have a first cut at this working now:

    location / {
      set $cache_extension '';
      if ($request_method = GET) {
        set $cache_extension '.html';
      }
    
      if (-f $request_filename) {
        break;
      }
    
      if (-f $request_filename$cache_extension) {
        rewrite (.*) $1.html break;
      }
    
      if (-f $static_root$uri) {
        root $static_root;
        break;
      }
    
      if (-f $static_root$uri$cache_extension) {
        root $static_root;
        rewrite (.*) $1.html break;
      }
    
      if (!-f $request_filename) {
        proxy_pass http://unicorn;
        break;
      }
    }

    My initial stab at this didn't work because I tried to incorporate try_files and that seems to interact horribly with if blocks (see "If Is Evil" on the nginx wiki). So it seems there is no choice but to have this string of if blocks testing one thing after another.

    Note that this will give us access to, for example, /products/wikitext/rdoc/index.html but not /products/wikitext/rdoc (with automatic fallback to index.html). I am not sure whether this can be done safely:

    1. test for directory at $static_root$uri
    2. if $uri is just /, then test succeeds (not confirmed whether trailing slash matters yet)
    3. if we then set root, we rule out the possibility of a match happening in the real root (although, not sure I'll ever want an index.html there anyway)

    Perhaps I could just add another test:

    if (-f $static_root$uri/index.html) {
      root $static_root;
      rewrite $static_root$uri/index.html break;
    }

    Again, want to find out what happens with trailing slashes here.

    And looking at it, can probably drop the $cache_extension block when looking under $static_root.

  7. Greg Hurrell 2010-07-24T12:20:52Z

    Status changed:

    • From: new
    • To: closed
Add a comment

Comments are now closed for this issue.

  • contact
  • legal

Menu

  • Blog
  • Wiki
  • Issues
  • Snippets