How to develop and deploy Catalyst fastcgi apps with nginx and custom URIs? - perl

Probably it's not so relevant, but I'll start with my environment: Linux OS, Perl 5.10, Catalyst 5.80032, nginx 1.0.11.
For the sake of the question, let's suppose I'm using mydomain.com on port 80 as an access point for the app's web page. Also let's use /var/www/mydomain as the physical location of my Catalyst application. In this case the static content is located at /var/www/mydomain/MyApp/root/.
I start the application as a fastcgi server (from MyApp/script):
> ./myapp_fastcgi.pl -l /tmp/myapp.socket -n 2 -p /tmp/myapp.pid -d
I start the nginx server with the following config:
server {
listen 127.0.0.1:80;
server_name mydomain.com;
location / {
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_pass unix:/tmp/myapp.socket;
}
location /static {
root /var/www/mydomain/MyApp/root;
}
}
and everything seems to work fine when I access the app as http://mydomain.com/ or http://mydomain.com/products as another page (handled by MyApp::Controller::Products).
Now the challenge and the question: how should be nginx configured so it could serve applications' pages with an URI prefix (for example /some/prefix)?
In this case rootpage should be accessed as http://mydomain.com/some/prefix/ and the second one as http://mydomain.com/some/prefix/products.
The second part of the question is: how should be the application code modified in order to have valid URIs for redirects and all the pages? i.e. how $c->uri_for() and similar methods should be (re)written to have the same behavior for prefixed paths?
I have tried the dummy straightforward adjustment
location /some/prefix {
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_pass unix:/tmp/myapp.socket;
}
but of course it doesn't work this way. So, I'm not able to get responses even to manually typed in URLs before moving to Perl code and play with redirects and URLs generation.

There is in nginx such directive like alias. It works this way:
If you have a url with value of http://my.domain.com/some/prefix/blabla.file and use alias directive with location directive as follows:
location /some/prefix {
alias /var/www/mydomain/MyApp/root;
}
The resulting url will be /var/www/mydomain/MyApp/blabla.file. Why? Because alias directive in location block use it's value (alias value) as temporary root for serving files and request and trim from request part matched in location so when request was /some/prefix/blabla.file here it will be only /blabla.file and when you add this to alias value then you will get what you want (if I understood you well).
According to our discusion belove in comments I can propose two things.
First, in nginx you can add such location block:
location ~ ^/some/prefix(.*)$ {
rewrite ^/some/prefix(.*)$ $1;
}
This directive will delete /some/prefix from /some/prefix/products and leave you only with /products. Next change your location block to looks like following one:
location / {
include fastcgi_params;
# Here you provide for your Catalys real uri which was orignally provided by user
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_pass unix:/tmp/myapp.socket;
}
So you set param REQUEST_URI for your Catalys to has value of user request without any modifications. In nginx are to variables $request_uri and $uri. Value of this first one never changes and of the second one changes everytime when it's, for example, rewrited by rewrite directive. Moreover this will work because location ~ has higher priority than just location / so request url will be always first trimed to only this what comes after /some/prefix pattern.
Will it be helpfull now?

Related

Files served with Aqueduct don't have a Content-Length header

I am writing a backend for my Flutter app using Aqueduct. I have Aqueduct set up so that Nginx proxies requests to it like this:
server {
root /home/web/my_server/web;
index index.html index.htm;
server_name example.com www.example.com;
location / {
try_files $uri $uri/ =404;
}
location /api {
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://127.0.0.1:8888;
proxy_http_version 1.1;
}
...
}
In Aqueduct I serve the files using a FileController:
router.route("/api/v1/app/*")
.link(() => LogController(context))
.link(() => FileController("public/app/")
..setContentTypeForExtension('apk', ContentType('application', 'vnd.android.package-archive')));
However, any files that it returns don't include the Content-Length header. That means that I can't show download progress.
I tried creating a custom FileController where I added the headers in manually:
final contentLengthValue = file.lengthSync();
return Response.ok(byteStream,
headers: {HttpHeaders.lastModifiedHeader: lastModifiedDateStringValue,
HttpHeaders.contentLengthHeader: contentLengthValue,
'x-decompressed-content-length': contentLengthValue,
HttpHeaders.cacheControlHeader: 'no-transform',
HttpHeaders.acceptRangesHeader: 'bytes'})
..cachePolicy = _policyForFile(file)
..encodeBody = false
..contentType = contentType;
The Content-Length header was still removed, but the x-decompressed-content-length header remained so this is a possible workaround. It just doesn't play nicely with some Flutter plugins that look for the Content-Length header and don't have a convenient way to check other headers.
Is this an Aqueduct problem or an Nginx problem? How do I solve it?
This solution works, but it skirts around the original problem. That it, it allows you to serve files that have the Content-Length in the header, but it doesn't explain why it was getting stripped in Aqueduct. Other answers are welcome.
Rather than have Aqueduct serve files, just have Nginx serve them directly.
If you can't change your API route, you can just give it an alias in the Nginx config location block. Add this before the /api location block.
location /api/v1/app/ {
alias /home/web/my_server/public/app/;
}
Now files in the app/ folder will get served by Nginx rather than Aqueduct. Nginx includes the Content-Length header in the files it returns.

Redirect/rewrite nginx location to .sock file without prefix

I have one server that has several APIs running on it. One of them is users-DB The following gets down to gunicorn just fine:
location /usersDB/ {
include proxy_params;
proxy_pass http://unix:/home/ubuntu/projects/UsersDB-api/app.sock;
}
Except when I try to access the usersDB API's /helloWorld route, and look in the logs at gunicorn.err I see:
GET /usersDB/helloWorld
I was hoping to see:
GET /helloWorld
Of course, gunicorn returns 404s and that is what I see in my browser. I've tried rewrite rules:
location /usersDB/ {
rewrite /usersDB/(.*) /$1 last;
include proxy_params;
proxy_pass http://unix:/home/ubuntu/projects/UsersDB-api/app.sock;
}
But the above results in the requests making their way to /var/www/htmlhelloWorld instead of app.sock.
I know that if you use a url for the proxy_pass you just add a trailing /, but I'm not sure what to do in the case of a sock file.
How do I get rid of the /usersDB/ suffix that is now included on all routes in nginx?
Use a separating :. For example:
proxy_pass http://unix:/home/ubuntu/projects/UsersDB-api/app.sock:/;
See this document for details.

Nginx config for single page app with HTML5 App Cache

I'm trying to build a single page app that utilizes HTML5 App Cache, which will cache a whole new version of the app for every distinct URL, thus I must redirect everyone to / and have my app route them afterward (this is the solution used on devdocs.io).
Here's my nginx config. I want all requests to send a file if it exists, redirect to my API at /auth and /api, and redirect all other requests to index.html. Why is the following configuration causing my browser to say that there is a redirect loop? If the user hits location block #2 and his route doesn't match a static file, he's sent to location block #3, which will redirect him to "/" which should hit location block #1 and serve index.html, correct? What is causing the redirect loop here? Is there a better way to accomplish this?
root /files/whatever/public;
index index.html;
# If the location is exactly "/", send index.html.
location = / {
try_files $uri /index.html;
}
location / {
try_files $uri #redirectToIndex;
}
# Set the cookie of the initialPath and redirect to "/".
location #redirectToIndex {
add_header Set-Cookie "initialPath=$request_uri; path=/";
return 302 $scheme://$host/;
}
# Proxy requests to "/auth" and "/api" to the server.
location ~* (^\/auth)|(^\/api) {
proxy_pass http://application_upstream;
proxy_redirect off;
}
That loop message suggests that /files/whatever/public/index.html doesn't exist, so the try_files in location / doesn't find $uri when it's equal to /index.html, so the try_files always internally redirects those requests to the # location which does the external redirect.
Unless you have a more complicated setup than you've outlined, I don't think you need to do so much. You shouldn't need external redirects (or even internal redirects) or server-side cookie sending for a one-file js app. The regex match for app and api wasn't quite right, either.
root /files/whatever/public;
index index.html;
location / {
try_files $uri /index.html =404;
}
# Proxy requests to "/auth" and "/api" to the server.
location ~ ^/(auth|api) {
proxy_pass http://application_upstream;
proxy_redirect off;
}

dotCloud nginx.conf: how to get the "index index.php" directive to work?

I got auth to work by "pushing" an nginx.conf file in the application directory, so I know the file works, but /app will not trigger /app/php.index. I can't get nginx working on my vista laptop with php and I can't edit the /etc/nginx/nginx.conf in the dotCloud instance as dotCloud makes life difficult by not giving root.
(Note that the .htpasswd is relative to the ngnix.conf file location, nice).
server {
location / {
index index.php;
}
location /admin {
auth_basic "enter password";
auth_basic_user_file .htpasswd;
index index.php;
}
}
You may need an nginx directive to map requests to your dynamic content. This controller can can then route them appropriately.
try_files $uri $uri/ /index.php;
For an example project, see the CakePHP tutorial.

with nginx, how to redirect part of request to apache

Some solutions about redirection can be found. But what I want to do is redirecting part of requests to another server, specifically, only when a request url includes string "service". For example:
http://localhost/service/image-------------> http://localhost:8080/service/image
http://localhost/service/image/upload------> http://localhost:8080/service/image/upload
http://localhost/service/blog--------------> http://localhost:8080/service/blog
.....
.................................................................................................................................................................
but blow will still served by ngnix,cause no "service" included within url
http://localhost/wiki/....
http://localhost/video/....
How to do this?
You need to use location regex matching together with proxy_pass, example:
upstream apache {
server 127.0.0.1:8080;
}
# in your server block:
server{
# location matching is prioritized by accuracy and order of definition
location ~* ^/service {
proxy_pass http://apache;
proxy_redirect off;
}
}
^/service will match any request beginning with /service and forward it to Apache.
proxy_pass is transparent for the user, i.e. it will forward the request to Apache and return the output to the user.
For more info on location matching, checkout http://wiki.nginx.org/HttpCoreModule#location
If I understood you correctly:
location / {
if ($request_uri ~* "^/service/.*") {
rewrite ^ http://localhost:8080$request_uri permanent;
}
}
P.S. did not check