Python: Model HTTP responses in aiohttp.web

This commit is contained in:
Rasmus Wriedt Larsen
2021-06-02 18:08:13 +02:00
parent 735df4597f
commit 2dbbf52903
3 changed files with 119 additions and 34 deletions

View File

@@ -391,4 +391,89 @@ module AiohttpWebModel {
this.(DataFlow::AttrRead).getAttributeName() in ["url", "rel_url"]
}
}
// ---------------------------------------------------------------------------
// aiohttp.web Response modeling
// ---------------------------------------------------------------------------
/**
* An instantiation of `aiohttp.web.Response`.
*
* Note that `aiohttp.web.HTTPException` (and it's subclasses) is a subclass of `aiohttp.web.Response`.
*
* See
* - https://docs.aiohttp.org/en/stable/web_reference.html#aiohttp.web.Response
* - https://docs.aiohttp.org/en/stable/web_quickstart.html#aiohttp-web-exceptions
*/
class AiohttpWebResponseInstantiation extends HTTP::Server::HttpResponse::Range,
DataFlow::CallCfgNode {
AiohttpWebResponseInstantiation() {
this = API::moduleImport("aiohttp").getMember("web").getMember("Response").getACall()
or
exists(string httpExceptionClassName |
httpExceptionClassName in [
"HTTPException", "HTTPSuccessful", "HTTPOk", "HTTPCreated", "HTTPAccepted",
"HTTPNonAuthoritativeInformation", "HTTPNoContent", "HTTPResetContent",
"HTTPPartialContent", "HTTPRedirection", "HTTPMultipleChoices", "HTTPMovedPermanently",
"HTTPFound", "HTTPSeeOther", "HTTPNotModified", "HTTPUseProxy", "HTTPTemporaryRedirect",
"HTTPPermanentRedirect", "HTTPError", "HTTPClientError", "HTTPBadRequest",
"HTTPUnauthorized", "HTTPPaymentRequired", "HTTPForbidden", "HTTPNotFound",
"HTTPMethodNotAllowed", "HTTPNotAcceptable", "HTTPProxyAuthenticationRequired",
"HTTPRequestTimeout", "HTTPConflict", "HTTPGone", "HTTPLengthRequired",
"HTTPPreconditionFailed", "HTTPRequestEntityTooLarge", "HTTPRequestURITooLong",
"HTTPUnsupportedMediaType", "HTTPRequestRangeNotSatisfiable", "HTTPExpectationFailed",
"HTTPMisdirectedRequest", "HTTPUnprocessableEntity", "HTTPFailedDependency",
"HTTPUpgradeRequired", "HTTPPreconditionRequired", "HTTPTooManyRequests",
"HTTPRequestHeaderFieldsTooLarge", "HTTPUnavailableForLegalReasons", "HTTPServerError",
"HTTPInternalServerError", "HTTPNotImplemented", "HTTPBadGateway",
"HTTPServiceUnavailable", "HTTPGatewayTimeout", "HTTPVersionNotSupported",
"HTTPVariantAlsoNegotiates", "HTTPInsufficientStorage", "HTTPNotExtended",
"HTTPNetworkAuthenticationRequired"
] and
this =
API::moduleImport("aiohttp").getMember("web").getMember(httpExceptionClassName).getACall()
)
}
override DataFlow::Node getBody() {
result in [this.getArgByName("text"), this.getArgByName("body")]
}
override DataFlow::Node getMimetypeOrContentTypeArg() {
result = this.getArgByName("content_type")
}
override string getMimetypeDefault() {
exists(this.getArgByName("text")) and
result = "text/plain"
or
not exists(this.getArgByName("text")) and
result = "application/octet-stream"
}
}
/**
* An instantiation of aiohttp.web HTTP redirect exception.
*
* See the part about redirects at https://docs.aiohttp.org/en/stable/web_quickstart.html#aiohttp-web-exceptions
*/
class AiohttpRedirectExceptionInstantiation extends AiohttpWebResponseInstantiation,
HTTP::Server::HttpRedirectResponse::Range {
AiohttpRedirectExceptionInstantiation() {
exists(string httpRedirectExceptionClassName |
httpRedirectExceptionClassName in [
"HTTPMultipleChoices", "HTTPMovedPermanently", "HTTPFound", "HTTPSeeOther",
"HTTPNotModified", "HTTPUseProxy", "HTTPTemporaryRedirect", "HTTPPermanentRedirect"
] and
this =
API::moduleImport("aiohttp")
.getMember("web")
.getMember(httpRedirectExceptionClassName)
.getACall()
)
}
override DataFlow::Node getRedirectLocation() {
result in [this.getArg(0), this.getArgByName("location")]
}
}
}

View File

@@ -6,28 +6,28 @@ routes = web.RouteTableDef()
@routes.get("/raw_text") # $ routeSetup="/raw_text"
async def raw_text(request): # $ requestHandler
return web.Response(text="foo") # $ MISSING: HttpResponse
return web.Response(text="foo") # $ HttpResponse mimetype=text/plain responseBody="foo"
@routes.get("/raw_body") # $ routeSetup="/raw_body"
async def raw_body(request): # $ requestHandler
return web.Response(body=b"foo") # $ MISSING: HttpResponse
return web.Response(body=b"foo") # $ HttpResponse mimetype=application/octet-stream responseBody=b"foo"
@routes.get("/html_text") # $ routeSetup="/html_text"
async def html_text(request): # $ requestHandler
return web.Response(text="foo", content_type="text/html") # $ MISSING: HttpResponse
return web.Response(text="foo", content_type="text/html") # $ HttpResponse mimetype=text/html responseBody="foo"
@routes.get("/html_body") # $ routeSetup="/html_body"
async def html_body(request): # $ requestHandler
return web.Response(body=b"foo", content_type="text/html") # $ MISSING: HttpResponse
return web.Response(body=b"foo", content_type="text/html") # $ HttpResponse mimetype=text/html responseBody=b"foo"
@routes.get("/html_body_set_later") # $ routeSetup="/html_body_set_later"
async def html_body_set_later(request): # $ requestHandler
resp = web.Response(body=b"foo") # $ MISSING: HttpResponse
resp.content_type = "text/html"
resp = web.Response(body=b"foo") # $ HttpResponse mimetype=application/octet-stream responseBody=b"foo"
resp.content_type = "text/html" # $ MISSING: mimetype=text/html
return resp
# Each HTTP status code has an exception
@@ -35,35 +35,35 @@ async def html_body_set_later(request): # $ requestHandler
@routes.get("/through_200_exception") # $ routeSetup="/through_200_exception"
async def through_200_exception(request): # $ requestHandler
raise web.HTTPOk(text="foo") # $ MISSING: HttpResponse
raise web.HTTPOk(text="foo") # $ HttpResponse mimetype=text/plain responseBody="foo"
@routes.get("/through_200_exception_html") # $ routeSetup="/through_200_exception_html"
async def through_200_exception(request): # $ requestHandler
exception = web.HTTPOk(text="foo") # $ MISSING: HttpResponse
exception.content_type = "text/html"
exception = web.HTTPOk(text="foo") # $ HttpResponse mimetype=text/plain responseBody="foo"
exception.content_type = "text/html" # $ MISSING: mimetype=text/html
raise exception
@routes.get("/through_404_exception") # $ routeSetup="/through_404_exception"
async def through_404_exception(request): # $ requestHandler
raise web.HTTPNotFound(text="foo") # $ MISSING: HttpResponse
raise web.HTTPNotFound(text="foo") # $ HttpResponse mimetype=text/plain responseBody="foo"
@routes.get("/redirect_301") # $ routeSetup="/redirect_301"
async def redirect_301(request): # $ requestHandler
if not "kwarg" in request.url.query:
raise web.HTTPMovedPermanently("/login") # $ MISSING: HttpResponse HttpRedirectResponse
raise web.HTTPMovedPermanently("/login") # $ HttpResponse HttpRedirectResponse mimetype=application/octet-stream redirectLocation="/login"
else:
raise web.HTTPMovedPermanently(location="/logout") # $ MISSING: HttpResponse HttpRedirectResponse
raise web.HTTPMovedPermanently(location="/logout") # $ HttpResponse HttpRedirectResponse mimetype=application/octet-stream redirectLocation="/logout"
@routes.get("/redirect_302") # $ routeSetup="/redirect_302"
async def redirect_302(request): # $ requestHandler
if not "kwarg" in request.url.query:
raise web.HTTPFound("/login") # $ MISSING: HttpResponse HttpRedirectResponse
raise web.HTTPFound("/login") # $ HttpResponse HttpRedirectResponse mimetype=application/octet-stream redirectLocation="/login"
else:
raise web.HTTPFound(location="/logout") # $ MISSING: HttpResponse HttpRedirectResponse
raise web.HTTPFound(location="/logout") # $ HttpResponse HttpRedirectResponse mimetype=application/octet-stream redirectLocation="/logout"
if __name__ == "__main__":

View File

@@ -16,13 +16,13 @@ if True:
# `app.add_routes` with list
async def foo(request): # $ requestHandler
return web.Response(text="foo")
return web.Response(text="foo") # $ HttpResponse
async def foo2(request): # $ requestHandler
return web.Response(text="foo2")
return web.Response(text="foo2") # $ HttpResponse
async def foo3(request): # $ requestHandler
return web.Response(text="foo3")
return web.Response(text="foo3") # $ HttpResponse
app.add_routes([
web.get("/foo", foo), # $ routeSetup="/foo"
@@ -36,32 +36,32 @@ if True:
@routes.get("/bar") # $ routeSetup="/bar"
async def bar(request): # $ requestHandler
return web.Response(text="bar")
return web.Response(text="bar") # $ HttpResponse
@routes.route("*", "/bar2") # $ routeSetup="/bar2"
async def bar2(request): # $ requestHandler
return web.Response(text="bar2")
return web.Response(text="bar2") # $ HttpResponse
@routes.get(path="/bar3") # $ routeSetup="/bar3"
async def bar3(request): # $ requestHandler
return web.Response(text="bar3")
return web.Response(text="bar3") # $ HttpResponse
app.add_routes(routes)
# `app.router.add_get` / `app.router.add_route`
async def baz(request): # $ requestHandler
return web.Response(text="baz")
return web.Response(text="baz") # $ HttpResponse
app.router.add_get("/baz", baz) # $ routeSetup="/baz"
async def baz2(request): # $ requestHandler
return web.Response(text="baz2")
return web.Response(text="baz2") # $ HttpResponse
app.router.add_route("*", "/baz2", baz2) # $ routeSetup="/baz2"
async def baz3(request): # $ requestHandler
return web.Response(text="baz3")
return web.Response(text="baz3") # $ HttpResponse
app.router.add_get(path="/baz3", handler=baz3) # $ routeSetup="/baz3"
@@ -73,7 +73,7 @@ if True:
class MyCustomHandlerClass:
async def foo_handler(self, request): # $ MISSING: requestHandler
return web.Response(text="MyCustomHandlerClass.foo")
return web.Response(text="MyCustomHandlerClass.foo") # $ HttpResponse
my_custom_handler = MyCustomHandlerClass()
app.router.add_get("/MyCustomHandlerClass/foo", my_custom_handler.foo_handler) # $ routeSetup="/MyCustomHandlerClass/foo"
@@ -84,7 +84,7 @@ if True:
# `app.add_routes` with list
class MyWebView1(web.View):
async def get(self): # $ requestHandler
return web.Response(text="MyWebView1.get")
return web.Response(text="MyWebView1.get") # $ HttpResponse
app.add_routes([
web.view("/MyWebView1", MyWebView1) # $ routeSetup="/MyWebView1"
@@ -97,7 +97,7 @@ if True:
@routes.view("/MyWebView2") # $ routeSetup="/MyWebView2"
class MyWebView2(web.View):
async def get(self): # $ requestHandler
return web.Response(text="MyWebView2.get")
return web.Response(text="MyWebView2.get") # $ HttpResponse
app.add_routes(routes)
@@ -105,20 +105,20 @@ if True:
# `app.router.add_view`
class MyWebView3(web.View):
async def get(self): # $ requestHandler
return web.Response(text="MyWebView3.get")
return web.Response(text="MyWebView3.get") # $ HttpResponse
app.router.add_view("/MyWebView3", MyWebView3) # $ routeSetup="/MyWebView3"
# no route-setup
class MyWebViewNoRoute(web.View):
async def get(self): # $ requestHandler
return web.Response(text="MyWebViewNoRoute.get")
return web.Response(text="MyWebViewNoRoute.get") # $ HttpResponse
if len(__name__) < 0: # avoid running, but fool analysis to not consider dead code
# no explicit-view subclass (but route-setup)
class MyWebViewNoSubclassButRoute(somelib.someclass):
async def get(self): # $ requestHandler
return web.Response(text="MyWebViewNoSubclassButRoute.get")
return web.Response(text="MyWebViewNoSubclassButRoute.get") # $ HttpResponse
app.router.add_view("/MyWebViewNoSubclassButRoute", MyWebViewNoSubclassButRoute) # $ routeSetup="/MyWebViewNoSubclassButRoute"
@@ -127,14 +127,14 @@ if True:
# for `add_get` only being for async functions.
if True:
async def no_rules(request): # $ requestHandler
return web.Response(text="no_rules")
return web.Response(text="no_rules") # $ HttpResponse
app.router.add_view("/no_rules", no_rules) # $ routeSetup="/no_rules"
class NoRulesView(web.View):
async def get(self): # $ requestHandler
return web.Response(text="NoRulesView.get")
return web.Response(text="NoRulesView.get") # $ HttpResponse
app.router.add_get("/NoRulesView", NoRulesView) # $ routeSetup="/NoRulesView"
@@ -149,7 +149,7 @@ if True:
async def matching(request: web.Request): # $ requestHandler
name = request.match_info['name']
number = request.match_info['number']
return web.Response(text="matching name={} number={}".format(name, number))
return web.Response(text="matching name={} number={}".format(name, number)) # $ HttpResponse
app.router.add_get(r"/matching/{name}/{number:\d+}", matching) # $ routeSetup="/matching/{name}/{number:\d+}"
@@ -161,7 +161,7 @@ if True:
subapp = web.Application()
async def subapp_handler(request): # $ requestHandler
return web.Response(text="subapp_handler")
return web.Response(text="subapp_handler") # $ HttpResponse
subapp.router.add_get("/subapp_handler", subapp_handler) # $ routeSetup="/subapp_handler"
@@ -177,7 +177,7 @@ if True:
if True:
async def manual_dispatcher_instance(request): # $ requestHandler
return web.Response(text="manual_dispatcher_instance")
return web.Response(text="manual_dispatcher_instance") # $ HttpResponse
url_dispatcher = web.UrlDispatcher()
url_dispatcher.add_get("/manual_dispatcher_instance", manual_dispatcher_instance) # $ routeSetup="/manual_dispatcher_instance"