jhthorsen / mojolicious-plugin-openapi

OpenAPI / Swagger plugin for Mojolicious
54 stars 44 forks source link

Missing routing when using apache and plack #81

Closed KrisGray closed 6 years ago

KrisGray commented 6 years ago

In the apache devel.conf I have the following:

Listen 8000
<VirtualHost *:8000>
  ServerName localhost
  DocumentRoot "/var/www/src/reactjs/swagger-ui/"
  <Directory /var/www/src/reactjs/swagger-ui>
    Options +Indexes
    Order deny,allow
    Allow from all
  </Directory>
</VirtualHost>

Listen 8010
<VirtualHost *:8010>
  ServerName localhost

  PerlRequire  /var/www/src/perl5/pace_api/startup.pl

  # $ENV{MOJO_MODE} = 'deployment';
  <Perl>
    $ENV{PLACK_ENV} = 'production';
    $ENV{MOJO_HOME} = '/var/www/src/perl5/pace_api';
    $ENV{MOJO_MODE} = 'development';
  </Perl>

  <Location />
    SetHandler perl-script
    PerlResponseHandler Plack::Handler::Apache2
    PerlSetVar psgi_app /var/www/src/perl5/pace_api/script/pace_api
  </Location>
</VirtualHost>

The pace_api script has the following code within:

#!/usr/bin/env perl

use strict;
use warnings;
use FindBin;
BEGIN { 
  unshift @INC, "$FindBin::Bin/../lib";
}
use Mojolicious::Commands;

# warn Dumper \@INC;

# Start command line interface for application
print STDERR "HELLO WORLD\n";
Mojolicious::Commands->start_app('PaceAPI');

The PaceAPI has the following:

package PaceAPI;
use Mojo::Base "Mojolicious";
use Crypt::JWT qw(encode_jwt decode_jwt);
use Data::Dumper;

sub startup {
  my $self = shift;
  # Load the "api.yaml" specification from the public directory
  #

  # to get around COR
  $self->hook(before_dispatch => sub {
    my $c = shift;
    $c->res->headers->header('Access-Control-Allow-Origin' => '*');
    $c->res->headers->header('Access-Control-Allow-Methods' => 'POST, GET, DELETE, PATCH');
    $c->res->headers->header('Access-Control-Allow-Headers' => 'Authorization, Content-Type');
  });

  $self->plugin(OpenAPI => {
    url => $self->home->rel_file('public/pace_api.json'),
    security => {
      bearer => sub {
        my ($c, $definition, $scopes, $cb) = @_;
        if ($c->req->headers->authorization) {
          my ($jwt) = $c->req->headers->authorization =~ m/Bearer\s+(.+)/;
          return $c->$cb() if $jwt;
          return $c->$cb('Invalid Bearer token');
        }
        return $c->$cb('Authorization header not present');
      }
    }
  });
}

1;

The config OpenAPI file public/pace_api.json is as follows:

{
  "swagger" : "2.0",
  "info" : {
    "version" : "1.0",
    "title" : "HGNC's RESTful PACE API"
  },
  "basePath" : "/api",
  "tags" : [ {
    "name" : "wibble",
    "description" : "wibble"
  }, {
    "name" : "user",
    "description" : "Operations involved in the user CRUD for pace"
  } ],
  "schemes" : [ "http", "https" ],
  "securityDefinitions" : {
    "bearer" : {
      "type": "apiKey",
      "name": "Authorization",
      "in": "header"
    }
  },
  "paths" : {
    "/wibble/{w}" : {
      "get" : {
        "tags" : [ "wibble" ],
        "summary" : "wibble",
        "parameters" : [ {
          "name" : "w",
          "in" : "path",
          "description" : "wibble",
          "required" : true,
          "type" : "integer"
        } ],
        "responses" : {
          "200" : {
            "description" : "response",
            "schema" : {
              "$ref" : "#/definitions/wibble"
            }
          },
          "default" : {
            "description" : "Unexpected error",
            "schema" : {
              "$ref" : "#/definitions/error"
            }
          }
        },
        "x-mojo-to" : "wibble#wobble",
        "operationId": "getWibble",
        "x-mojo-name": "get_wibble"
      }
    },
    "/users" : {
      "get" : {
        "tags" : [ "user" ],
        "summary" : "Retrieve all users in the pace system",
        "parameters" : [ {
          "name" : "active",
          "in" : "query",
          "description" : "Only return active or inactive users",
          "required" : false,
          "type" : "boolean"
        } ],
        "responses" : {
          "200" : {
            "description" : "User response",
            "schema" : {
              "$ref" : "#/definitions/users"
            }
          },
          "401" : {
            "description" : "Authentication failure",
            "schema" : {
              "$ref" : "#/definitions/error"
            }
          },
          "default" : {
            "description" : "Unexpected error",
            "schema" : {
              "$ref" : "#/definitions/error"
            }
          }
        },
        "x-mojo-to" : "user#index",
        "operationId": "getUsers",
        "x-mojo-name": "get_users"
      }
    }
  },
  "definitions" : {
    "wibble" : {
      "type" : "object",
      "properties" : {
        "wibble" : {
          "type" : "string"
        }
      }
    },
    "users" : {
      "type" : "array",
      "items" : {
        "$ref" : "#/definitions/user"
      }
    },
    "user" : {
      "required" : [ "active", "id", "name" ],
      "properties" : {
        "id" : {
          "type" : "integer"
        },
        "name" : {
          "type" : "string"
        },
        "active" : {
          "type" : "boolean"
        },
        "fullName" : {
          "type" : "string"
        }
      },
      "example" : {
        "name" : "name",
        "active" : true,
        "fullName" : "fullName",
        "id" : 0
      }
    },
    "error" : {
      "type" : "object",
      "properties" : {
        "errors" : {
          "type" : "array",
          "items" : {
            "type" : "object",
            "properties" : {
              "message" : {
                "type" : "string"
              },
              "path" : {
                "type" : "string"
              }
            }
          }
        },
        "status" : {
          "type" : "string"
        }
      }
    }
  }
}

To begin with everything works as expected but as soon as all the apache children have ran the pace_api script and starts to reuse the child apache processes while using a different GET method call. Mojolicious seems to forget about the OpenAPI plugin and the routes it has created.

With debug on I see the following:

[Thu Jul 19 14:07:41 2018] [notice] suEXEC mechanism enabled (wrapper: /usr/sbin/suexec)
[Thu Jul 19 14:07:41 2018] [notice] Digest: generating secret for digest authentication ...
[Thu Jul 19 14:07:41 2018] [notice] Digest: done
[Thu Jul 19 14:07:41 2018] [notice] Apache/2.2.15 (Unix) DAV/2 mod_ssl/2.2.15 OpenSSL/1.0.1e-fips mod_apreq2-20090110/2.8.0 mod_perl/2.0.4 Perl/v5.10.1 configured -- resuming normal operations
HELLO WORLD
[Thu Jul 19 14:08:00 2018] [debug] GET "/api/wibble/682" (3501a6b5)
[Thu Jul 19 14:08:00 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:01 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:01 2018] [debug] Your secret passphrase needs to be changed
[Thu Jul 19 14:08:01 2018] [debug] 200 OK (0.639258s, 1.564/s)
HELLO WORLD
HELLO WORLD
HELLO WORLD
[Thu Jul 19 14:08:04 2018] [debug] GET "/api/wibble/682" (b7d0d407)
[Thu Jul 19 14:08:04 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:04 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:04 2018] [debug] 200 OK (0.0038s, 263.158/s)
HELLO WORLD
HELLO WORLD
[Thu Jul 19 14:08:04 2018] [debug] GET "/api/wibble/682" (f3bd9713)
[Thu Jul 19 14:08:04 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:04 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:04 2018] [debug] 200 OK (0.006495s, 153.965/s)
HELLO WORLD
[Thu Jul 19 14:08:04 2018] [debug] GET "/api/wibble/682" (82cc440b)
[Thu Jul 19 14:08:04 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:04 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:04 2018] [debug] 200 OK (0.003947s, 253.357/s)
[Thu Jul 19 14:08:04 2018] [debug] GET "/api/wibble/682" (54ef1b57)
[Thu Jul 19 14:08:04 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:04 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:04 2018] [debug] 200 OK (0.012766s, 78.333/s)
HELLO WORLD
[Thu Jul 19 14:08:05 2018] [debug] GET "/api/wibble/682" (d7c88beb)
[Thu Jul 19 14:08:05 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:05 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:05 2018] [debug] 200 OK (0.00434s, 230.415/s)
[Thu Jul 19 14:08:05 2018] [debug] GET "/api/wibble/682" (383fd821)
[Thu Jul 19 14:08:05 2018] [debug] Routing to a callback
HELLO WORLD
HELLO WORLD
[Thu Jul 19 14:08:05 2018] [debug] GET "/api/wibble/682" (9fa27793)
[Thu Jul 19 14:08:05 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:05 2018] [debug] GET "/api/wibble/682" (21823615)
[Thu Jul 19 14:08:05 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:06 2018] [debug] GET "/api/wibble/682" (8cf5e037)
[Thu Jul 19 14:08:06 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:06 2018] [debug] GET "/api/wibble/682" (d878328e)
[Thu Jul 19 14:08:06 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:06 2018] [debug] GET "/api/wibble/682" (085bb8e5)
[Thu Jul 19 14:08:06 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:06 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:06 2018] [debug] Your secret passphrase needs to be changed
[Thu Jul 19 14:08:06 2018] [debug] 200 OK (1.818217s, 0.550/s)
[Thu Jul 19 14:08:07 2018] [debug] GET "/api/wibble/682" (0ae0c5a6)
[Thu Jul 19 14:08:07 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:07 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:07 2018] [debug] Your secret passphrase needs to be changed
[Thu Jul 19 14:08:07 2018] [debug] 200 OK (1.356336s, 0.737/s)
[Thu Jul 19 14:08:07 2018] [debug] GET "/api/wibble/682" (2292082a)
[Thu Jul 19 14:08:07 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:07 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:07 2018] [debug] Your secret passphrase needs to be changed
[Thu Jul 19 14:08:07 2018] [debug] 200 OK (1.627971s, 0.614/s)
[Thu Jul 19 14:08:07 2018] [debug] GET "/api/wibble/682" (084cb41f)
[Thu Jul 19 14:08:07 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:07 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:07 2018] [debug] Your secret passphrase needs to be changed
[Thu Jul 19 14:08:07 2018] [debug] 200 OK (1.083936s, 0.923/s)
[Thu Jul 19 14:08:08 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:08 2018] [debug] Your secret passphrase needs to be changed
[Thu Jul 19 14:08:08 2018] [debug] 200 OK (1.227977s, 0.814/s)
[Thu Jul 19 14:08:08 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:08 2018] [debug] Your secret passphrase needs to be changed
[Thu Jul 19 14:08:08 2018] [debug] 200 OK (1.147097s, 0.872/s)
[Thu Jul 19 14:08:08 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:08 2018] [debug] Your secret passphrase needs to be changed
[Thu Jul 19 14:08:08 2018] [debug] 200 OK (1.016924s, 0.983/s)
[Thu Jul 19 14:08:08 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:08 2018] [debug] Your secret passphrase needs to be changed
[Thu Jul 19 14:08:08 2018] [debug] 200 OK (0.884144s, 1.131/s)
[Thu Jul 19 14:08:08 2018] [debug] Routing to controller "PaceAPI::Controller::Wibble" and action "wobble"
[Thu Jul 19 14:08:08 2018] [debug] Your secret passphrase needs to be changed
[Thu Jul 19 14:08:08 2018] [debug] 200 OK (0.80315s, 1.245/s)
HELLO WORLD
[Thu Jul 19 14:08:18 2018] [debug] GET "/api/users" (007d96d6)
[Thu Jul 19 14:08:18 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:19 2018] [debug] Routing to controller "PaceAPI::Controller::User" and action "index"
[Thu Jul 19 14:08:19 2018] [debug] Your secret passphrase needs to be changed
[Thu Jul 19 14:08:19 2018] [debug] 200 OK (0.612915s, 1.632/s)
[Thu Jul 19 14:08:21 2018] [debug] GET "/api/users" (28e979ff)
[Thu Jul 19 14:08:21 2018] [debug] Routing to a callback
[Thu Jul 19 14:08:21 2018] [debug] Controller "PaceAPI::User" does not exist
[Thu Jul 19 14:08:21 2018] [debug] Using default_handler to render data since 'openapi' was not found in stash. Set 'handler' in stash to avoid this message.
[Thu Jul 19 14:08:21 2018] [debug] Template "user/index.html.ep" not found
[Thu Jul 19 14:08:21 2018] [debug] 501 Not Implemented (0.005766s, 173.430/s)

As you can see in the log for some reason the "Routing to a callback" starts to use "PaceAPI::User" rather than "PaceAPI::Controller::User". I have tried for a couple of days to solve this issue but I am not getting anywhere. It would be so appreciated if you could give me any feedback into why this is happening and how to fix the issue.

Many thank,

Kris

KrisGray commented 6 years ago

Just for completeness here is the dir structure

/var/www/src/perl5/pace_api/
├── lib/
│   ├── PaceAPI/
│   │   ├── Controller/
│   │   │   ├── User.pm
│   │   │   └── Wibble.pm
│   │   │  
│   │   ├── Model/
│   │   │   ├── User.pm
│   │   │   └── Wibble.pm
│   │   │
│   │   ├── Service/
│   │   │   ├── User.pm
│   │   │   └── Wibble.pm
│   │   │
│   │   ├── Controller.pm
│   │   └── Service.pm
│   │
│   └── PaceAPI.pm
│
├── public/
│   └── pace_api.json
│
└── script/
    └── pace_api
jhthorsen commented 6 years ago

It seems like you have some code that mess up $app->routes->namespace. There’s no code in OpenAPI that changes that value.

https://mojolicious.org/perldoc/Mojolicious/Routes#namespaces

KrisGray commented 6 years ago

Thanks for the quick reply. I don’t see anything in my code that messes around with the namespace. I have solved the issue however by preloading the app in a startup script using Plack::Handler::Apache2.