Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle staticfiles OSError exceptions #1220

Merged
merged 18 commits into from Oct 5, 2021
Merged

Handle staticfiles OSError exceptions #1220

merged 18 commits into from Oct 5, 2021

Conversation

aminalaee
Copy link
Member

@aminalaee aminalaee commented Jun 27, 2021

Closes #1123 Issue
And can also closes #1124 old PR

This will handle staticfiles exceptions:

  • If directory not found throws 404 (file not found was already handled)
  • if permission denied on file/folder returns 401
  • if any other OSError exceptions happen, returns 500


with mock.patch(
"starlette.staticfiles.StaticFiles.lookup_path"
) as mock_lookup_path:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen any usage of unittest.mock in the project, but I couldn't find any better way to create not-so-simple OSError exceptions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any OSError that might cause this? Maybe a broken symlink or would that just 404?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that would be a FileNotFound. I'll give it another try but all other unhandled OSError exceptions happened on writing a file or internal sys calls.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

statting a file you can't access gives you PermissionError:

>>> os.stat("/boot/bar")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
PermissionError: [Errno 13] Permission denied: '/boot/bar'

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah you already have a test for that

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JayH5 Changed that, please check again if that's what you meant.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks yes that is what I meant. Sorry for the slow response 😕

I've been thinking about this again and I'm not sure we actually want to return 500 from within the static files implementation. Should we not just allow/raise the exception since it is so unexpected? IMO, the ServerErrorMiddleware (and similar) should really be the only thing in Starlette that returns a 500 response.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JayH5
That makes sense. What about the other exceptions?
Like 404 and 401, can't ExceptionMiddleware handle those?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You do make a good point 🤔 I'm not sure why we don't raise an HTTPException in those cases. I'd be open to that change (if it's as straightforward as it seems) either in this PR or another.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JayH5 I played around this a bit and changed that.
I think we can raise HTTPException instead of returning PlaintTextResponses but there's a difference. If we raise HTTPException then we need to can't use StaticFiles alone as we need Starlette middlewares to handle the exception which StaticFiles doesn't have.

starlette/staticfiles.py Outdated Show resolved Hide resolved

with mock.patch(
"starlette.staticfiles.StaticFiles.lookup_path"
) as mock_lookup_path:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any OSError that might cause this? Maybe a broken symlink or would that just 404?

starlette/staticfiles.py Outdated Show resolved Hide resolved
@aminalaee aminalaee requested a review from a team August 11, 2021 18:39
Copy link
Member

@tomchristie tomchristie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aminalaee - Looks neatly done. Happy to leave this up to your judgement.

starlette/staticfiles.py Outdated Show resolved Hide resolved
starlette/staticfiles.py Show resolved Hide resolved
@aminalaee aminalaee merged commit 1db8b5b into encode:master Oct 5, 2021
@aminalaee aminalaee deleted the handle-staticfiles-os-errors branch October 5, 2021 06:01
@lambdaq
Copy link

lambdaq commented Jan 17, 2022

Heads up for anyone using lookup_path(): it's now changed to a sync method in starlette>=0.17.1

I am using await self.lookup_path in my code this PR breaks it lol.

I know it's my bad to use an undocumented internal method, but I just don't want to handle path on my own.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

StaticFiles causes Internal Server Error when user accesses existing files as directory
6 participants