Skip to content

Commit

Permalink
Setup working dir path in Caddyfile (#57)
Browse files Browse the repository at this point in the history
* Setup working dir path in Caddyfile

* Write header to 500 when there's a problem with an ASGI request

* Comment out macos deployment

* Revert "Comment out macos deployment"

This reverts commit 57d8966.

* FIXME: Remove macOS deployment
  • Loading branch information
mliezun authored Jan 13, 2025
1 parent c371ced commit 62b9996
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 81 deletions.
67 changes: 0 additions & 67 deletions .github/workflows/python-build-macos.yml

This file was deleted.

16 changes: 14 additions & 2 deletions caddysnake.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,19 @@ static PyTypeObject ResponseType = {
};

WsgiApp *WsgiApp_import(const char *module_name, const char *app_name,
const char *venv_path) {
const char *working_dir, const char *venv_path) {
WsgiApp *app = malloc(sizeof(WsgiApp));
if (app == NULL) {
return NULL;
}
PyGILState_STATE gstate = PyGILState_Ensure();

// Add working_dir into sys.path list
if (working_dir) {
PyObject *sysPath = PySys_GetObject("path");
PyList_Append(sysPath, PyUnicode_FromString(working_dir));
}

// Add venv_path into sys.path list
if (venv_path) {
PyObject *sysPath = PySys_GetObject("path");
Expand Down Expand Up @@ -461,7 +467,7 @@ struct AsgiApp {
};

AsgiApp *AsgiApp_import(const char *module_name, const char *app_name,
const char *venv_path) {
const char *working_dir, const char *venv_path) {
AsgiApp *app = malloc(sizeof(AsgiApp));
if (app == NULL) {
return NULL;
Expand All @@ -470,6 +476,12 @@ AsgiApp *AsgiApp_import(const char *module_name, const char *app_name,
app->lifespan_shutdown = NULL;
PyGILState_STATE gstate = PyGILState_Ensure();

// Add working_dir into sys.path list
if (working_dir) {
PyObject *sysPath = PySys_GetObject("path");
PyList_Append(sysPath, PyUnicode_FromString(working_dir));
}

// Add venv_path into sys.path list
if (venv_path) {
PyObject *sysPath = PySys_GetObject("path");
Expand Down
67 changes: 57 additions & 10 deletions caddysnake.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type CaddySnake struct {
ModuleWsgi string `json:"module_wsgi,omitempty"`
ModuleAsgi string `json:"module_asgi,omitempty"`
Lifespan string `json:"lifespan,omitempty"`
WorkingDir string `json:"working_dir,omitempty"`
VenvPath string `json:"venv_path,omitempty"`
logger *zap.Logger
app AppServer
Expand All @@ -71,6 +72,10 @@ func (f *CaddySnake) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
if !d.Args(&f.Lifespan) || (f.Lifespan != "on" && f.Lifespan != "off") {
return d.Errf("expected exactly one argument for lifespan: on|off")
}
case "working_dir":
if !d.Args(&f.WorkingDir) {
return d.Errf("expected exactly one argument for working_dir")
}
case "venv":
if !d.Args(&f.VenvPath) {
return d.Errf("expected exactly one argument for venv")
Expand Down Expand Up @@ -98,21 +103,22 @@ func (CaddySnake) CaddyModule() caddy.ModuleInfo {
func (f *CaddySnake) Provision(ctx caddy.Context) error {
f.logger = ctx.Logger(f)
if f.ModuleWsgi != "" {
w, err := NewWsgi(f.ModuleWsgi, f.VenvPath)
w, err := NewWsgi(f.ModuleWsgi, f.WorkingDir, f.VenvPath)
if err != nil {
return err
}
if f.Lifespan != "" {
f.logger.Warn("lifespan is only used in ASGI mode", zap.String("lifespan", f.Lifespan))
}
f.logger.Info("imported wsgi app", zap.String("module_wsgi", f.ModuleWsgi), zap.String("venv_path", f.VenvPath))
f.logger.Info("imported wsgi app", zap.String("module_wsgi", f.ModuleWsgi), zap.String("working_dir", f.WorkingDir), zap.String("venv_path", f.VenvPath))
f.app = w
} else if f.ModuleAsgi != "" {
var err error
f.app, err = NewAsgi(f.ModuleAsgi, f.VenvPath, f.Lifespan == "on")
f.app, err = NewAsgi(f.ModuleAsgi, f.WorkingDir, f.VenvPath, f.Lifespan == "on", f.logger)
if err != nil {
return err
}
f.logger.Info("imported asgi app", zap.String("module_asgi", f.ModuleAsgi), zap.String("working_dir", f.WorkingDir), zap.String("venv_path", f.VenvPath))
} else {
return errors.New("asgi or wsgi app needs to be specified")
}
Expand Down Expand Up @@ -200,6 +206,25 @@ func findSitePackagesInVenv(venvPath string) (string, error) {
return sitePackagesPath, nil
}

// findWorkingDirectory checks if the directory exists and returns the absolute path
func findWorkingDirectory(working_dir string) (string, error) {
working_dir_abs, err := filepath.Abs(working_dir)
if err != nil {
return "", err
}
fileInfo, err := os.Stat(working_dir_abs)
if err != nil {
if os.IsNotExist(err) {
return "", fmt.Errorf("working_dir directory does not exist in: %s", working_dir_abs)
}
return "", err
}
if !fileInfo.IsDir() {
return "", fmt.Errorf("working_dir is not a directory: %s", working_dir_abs)
}
return working_dir_abs, nil
}

// findPythonDirectory searches for a directory that matches "python3.*" inside the given libPath.
func findPythonDirectory(libPath string) (string, error) {
var pythonDir string
Expand Down Expand Up @@ -235,7 +260,7 @@ type Wsgi struct {
var wsgiapp_cache map[string]*Wsgi = map[string]*Wsgi{}

// NewWsgi imports a WSGI app
func NewWsgi(wsgi_pattern string, venv_path string) (*Wsgi, error) {
func NewWsgi(wsgi_pattern, working_dir, venv_path string) (*Wsgi, error) {
wsgi_lock.Lock()
defer wsgi_lock.Unlock()

Expand All @@ -262,9 +287,19 @@ func NewWsgi(wsgi_pattern string, venv_path string) (*Wsgi, error) {
defer C.free(unsafe.Pointer(packages_path))
}

var working_dir_path *C.char = nil
if working_dir != "" {
working_dir_abs, err := findWorkingDirectory(working_dir)
if err != nil {
return nil, err
}
working_dir_path = C.CString(working_dir_abs)
defer C.free(unsafe.Pointer(working_dir_path))
}

runtime.LockOSThread()
defer runtime.UnlockOSThread()
app := C.WsgiApp_import(module_name, app_name, packages_path)
app := C.WsgiApp_import(module_name, app_name, working_dir_path, packages_path)
if app == nil {
return nil, errors.New("failed to import module")
}
Expand Down Expand Up @@ -426,7 +461,7 @@ func (m *Wsgi) HandleRequest(w http.ResponseWriter, r *http.Request) error {
body_bytes := C.GoBytes(unsafe.Pointer(h.body), C.int(h.body_size))
w.Write(body_bytes)
} else if h.status_code == 500 {
w.Write([]byte("Interal Server Error"))
w.Write([]byte("Internal Server Error"))
}

return nil
Expand All @@ -452,12 +487,13 @@ func wsgi_write_response(request_id C.int64_t, status_code C.int, headers *C.Map
type Asgi struct {
app *C.AsgiApp
asgi_pattern string
logger *zap.Logger
}

var asgiapp_cache map[string]*Asgi = map[string]*Asgi{}

// NewAsgi imports a Python ASGI app
func NewAsgi(asgi_pattern string, venv_path string, lifespan bool) (*Asgi, error) {
func NewAsgi(asgi_pattern, working_dir, venv_path string, lifespan bool, logger *zap.Logger) (*Asgi, error) {
asgi_lock.Lock()
defer asgi_lock.Unlock()

Expand All @@ -484,9 +520,19 @@ func NewAsgi(asgi_pattern string, venv_path string, lifespan bool) (*Asgi, error
defer C.free(unsafe.Pointer(packages_path))
}

var working_dir_path *C.char = nil
if working_dir != "" {
working_dir_abs, err := findWorkingDirectory(working_dir)
if err != nil {
return nil, err
}
working_dir_path = C.CString(working_dir_abs)
defer C.free(unsafe.Pointer(working_dir_path))
}

runtime.LockOSThread()
defer runtime.UnlockOSThread()
app := C.AsgiApp_import(module_name, app_name, packages_path)
app := C.AsgiApp_import(module_name, app_name, working_dir_path, packages_path)
if app == nil {
return nil, errors.New("failed to import module")
}
Expand All @@ -500,7 +546,7 @@ func NewAsgi(asgi_pattern string, venv_path string, lifespan bool) (*Asgi, error
}
}

result := &Asgi{app, asgi_pattern}
result := &Asgi{app, asgi_pattern, logger}
asgiapp_cache[asgi_pattern] = result
return result, err
}
Expand Down Expand Up @@ -746,7 +792,8 @@ func (m *Asgi) HandleRequest(w http.ResponseWriter, r *http.Request) error {
runtime.UnlockOSThread()

if err := <-arh.done; err != nil {
return err
w.WriteHeader(500)
m.logger.Debug(err.Error())
}

return nil
Expand Down
4 changes: 2 additions & 2 deletions caddysnake.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ MapKeyVal *MapKeyVal_new(size_t);

// WSGI Protocol
typedef struct WsgiApp WsgiApp;
WsgiApp *WsgiApp_import(const char *, const char *, const char *);
WsgiApp *WsgiApp_import(const char *, const char *, const char *, const char *);
void WsgiApp_handle_request(WsgiApp *, int64_t, MapKeyVal *, const char *);
void WsgiApp_cleanup(WsgiApp *);

Expand All @@ -26,7 +26,7 @@ extern void wsgi_write_response(int64_t, int, MapKeyVal *, char *, size_t);

typedef struct AsgiApp AsgiApp;
typedef struct AsgiEvent AsgiEvent;
AsgiApp *AsgiApp_import(const char *, const char *, const char *);
AsgiApp *AsgiApp_import(const char *, const char *, const char *, const char *);
uint8_t AsgiApp_lifespan_startup(AsgiApp *);
uint8_t AsgiApp_lifespan_shutdown(AsgiApp *);
void AsgiApp_handle_request(AsgiApp *, uint64_t, MapKeyVal *, MapKeyVal *,
Expand Down

0 comments on commit 62b9996

Please # to comment.