Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nginx map accept header to subdirectory for api strange behaviour

I've been fiddling around with this for quite some time now, and I can't really get a grasp on how nginx+hhvm maps my requests.

Basiclly, I have an API on api.example.com that I would like to call with the Accept: application/vnd.com.example.api.v1+json for version 1 and application/vnd.com.example.api.v2+json for version 2. The API itself is a PHP application that I'll be running using a fresh install of HHVM. All requests will be handeled by index.php.

The folder structure looks like this:

api.example.com/
  index.php (content: fail)
  v1/
    index.php (content: v1)
  v2/
    index.php (content: v2)

Whenever I use my REST client to access api.example.com/test with the v1 accept header I get the v1 response back. When I the use the accept header for v2, it shows the v2. So everything is correct. If I don't supply any accept header I get redirected to example.com

The NGINX configuration looks like this

map $http_accept $api_version {
        default 0;
        "application/vnd.com.example.api.v1+json" 1;
        "application/vnd.com.example.api.v2+json" 2;
}

server {
        # listen to :80 is already implied.

        # root directory
        root /var/www/api.example.com/;
        index index.html;

        server_name api.example.com;
        include hhvm.conf;

        location / {
                if ($api_version = 0) {
                        # redirect to example.com if applicable
                        # Accept-header is missing
                        return 307 http://example.com;
                }

                try_files /v$api_version/$uri /v$api_version/$uri/ /v$api_version/index.php?$args;
        }

        # Prevent access to hidden files
        location ~ /\. {
                deny all;
        }
}

the hhvm.conf file is included below. It is derivated or somewhat exact functionality of the default hhvm.conf included with hhvm.

location ~ \.(hh|php)$ {
    fastcgi_keep_conn on;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include        fastcgi_params;
}

This is my problem

If I try to access api.example.com/index.php I get the "fail" response, even if I am expecting v1 for the v1 accept header and v2 for the v2 accept header. Everything else seems to work fine, even index.html maps correctly to it's subdirectory.

What I've tried

I've tried using

root /var/www/api.example.com/v$api_version/; 

in the configuration, but that only gives me 404 errors from NGINX. I believe what I'm looking for is actually changing the root path, but I haven't got my head around how to make it work. I've also tried removing index parameters in both the nginx configuration and the hhvm.conf, but that does not seem to help. I've also tried a ton of different configurations and I've had at least 20-30 tabs of stackoverflow open to solve this, but I'm clearly missing something (probably rather simple) here. I've also tried moving the hhvm include inside the location block.

The setup

Debian 7, nginx/1.2.1, hhvm 3.2.0

Oh, and it's my first time actually asking a question here. :) hope I've formatted everything correctly.

like image 461
Carl Hannes Avatar asked Sep 19 '14 09:09

Carl Hannes


1 Answers

What are the contents of hhvm.conf ?

I'm assuming Fast CGI is being used to proxy requests to the HHVM server. So your hhvm.conf might look something like this:

root /var/www/api.example.com;
index index.php;
fastcgi_pass   127.0.0.1:9000;
fastcgi_index  index.php;
fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
include        fastcgi_params;

Which should be wrapped by a location directive.

So based on your config shown, what I think is happening is you must be matching php scripts to the HHVM location directive, which is fine, but in doing so your try_files setting, which seems to be in charge of doing the API version to file system mapping, is not being processed.

Without your hhvm.conf it's hard to say what to do next, but I suspect you need to focus on the root value inside the location directive that contains the HHVM fastcgi settings.

UPDATE

So I have the concept of an API version derived from the header mapping to a filesystem working for me on nginx + HHVM. Here is my nginx config for HHVM:

location / {
    root /var/www/html/hh/v$api_version;
    index index.php;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include        fastcgi_params;
}

Really though, the location of / is no better than your one - in fact, yours is probably better as you probably don't want HHVM serving static files etc. But this is working for me - combined with the map you have in your original post, when I curl -H 'Accept: application/vnd.com.example.api.v2+json' localhost, I get the expected response from an index.php file inside the version's directory.

I think what you need to do is update your HHVM nginx config with a dynamically generated root declaration like mine above. If you're still getting a 404, try this: in /etc/init.d/hhvm, find the ADDITIONAL_ARGS= var, make it ADDITIONAL_ARGS="-vServer.FixPathInfo=true". I'm not sure exactly what it does, but I've come across it before and it had fixed a weird 404 problem I was having in the past (where the 404 was coming from HHVM, not Apache/nginx).

like image 85
ndavison Avatar answered Nov 15 '22 16:11

ndavison