-
Notifications
You must be signed in to change notification settings - Fork 140
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
Change the rewrite_response function signature to take a RewritableResponse object #301
Change the rewrite_response function signature to take a RewritableResponse object #301
Conversation
We could also support rewrite_response="response.body = response.body.replace(b'cat', b'dog')" but then there are potential security issues which I don't know enough how to address. Is there any potential privilege escalation here? (If you can modify the config file, can you already execute arbitrary code as the user running the server?) |
Thanks for working on this, @maresb! See my comment in #300 (comment) about a path forward. |
Ah, I see - as the objects passed in are mutated, you can just pass in the same objects to a bunch of different functions, and they will be able to do their modifications as a chain. This lets you compose these changes - is that right? |
Exactly @yuvipanda. I can do rewrite_response=[rewrite_function_a, rewrite_function_b] and then they run one after the other. |
Thanks @maresb ! In jupyter-rsession-proxy, I need access to the request's URI and not the proxied path, e.g. /user/so-and-so/rstudio on a hub, in order to rewrite a Location header. How could I do so if the RewritableRespone object only has attributes for |
Hi @ryanlovett, please give I still need to document things fully. |
Paging @minrk to the API design discussion as well. I love the idea of chaining, but modifying arguments passed in implicitly makes me feel bad. I don't know how to balance these feelings! Help, @minrk! Two things to think about:
My intuition is that I feel stronger about (1) than (2). Thank you for being awesome and pushing this forward, @maresb and @ryanlovett! |
Ah, sorry I missed that @maresb . I'll give that a try. |
This looks great! Like @yuvipanda, my thought is that if it's mutating the object, it can be (a copy of) the original object, rather than a custom class?
Modifying inputs for rewriting a response doesn't feel bad to me at all, so I have no problem with that. I don't think it's implicit when it's the sole purpose of the function. If @yuvipanda feels strongly about immutability, then I think a custom class is appropriate, and each step in the chain would get a new instance of the class. I'm not sure what problem that solves, though. |
@minrk and @yuvipanda, thanks so much for the feedback. I actually came to the same conclusion (that I could mutate a copy of the class). It took a little extra boilerplate (no |
There's no copy method because copy is in the standard library and works for most objects: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One suggestion about passing in the request object too, and I think we're good to go after that!
Thanks a lot, @maresb, @minrk and @ryanlovett!
I'm happy that you're convinced by my PR. Now that I know it has support, I think I should add a few quick tests before merging. (For example, to verify that chained rewrites work properly, and that the request object is accessible.) |
lambda host, port, path, response: response.body, | ||
non_service_rewrite_response = Union( | ||
default_value=tuple(), | ||
trait_types=[List(), Tuple(), Callable()], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason we should support both Lists and Tuples?
Hi @yuvipanda, @minrk and @ryanlovett, My PR seems to have stalled out. I'm waiting for feedback on whether or not to add extra parameters (besides I'll try and summarize the status here: @yuvipanda wants the Then there's @minrk's comment, for which I see two different and interesting interpretations, both of which I have already tried to address.
I originally interpreted "original" to mean the original where I interpret "original" to mean my original Now I realize that @minrk likely indended that I could eliminate my Any suggestions on if / how to move this PR forward? Thanks! |
Sorry, I lost track of @yuvipanda's comment in the previous PR regarding |
Ok, if I get the green light from @yuvipanda regarding the implementation, I'll write some tests. Thanks for your patience! |
f89c299
to
095d95f
Compare
# convert it to a function of only ``response`` by plugging in the | ||
# known values for all the other parameters. (This is called partial | ||
# evaluation.) | ||
def rewrite_pe(rewritable_response: RewritableResponse): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use functools.partial
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some style suggestions and a note about reducing the API surface further, but I am pretty happy with this! Thank you for working on this and incorporating my feedback, @maresb!!!!
I was very confused until i read this haha :D Am glad you found it. Thank you for coming back and pushing this PR to completion. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ryanlovett does this look enough to support our rstudio workflow?
@maresb this looks great to me! A couple tests to test setting status and headers, and am ready to merge this. THANK YOU SO MUCH!
Great! I didn't have as much time this weekend as I expected. I'll try to find some time within the next day or two to bring this over the finish line. |
@yuvipanda Yes, I have a branch of jupyter-rstudio-proxy that works with @maresb latest on a local hub and on mybinder. @maresb I'm also very thankful for this PR. Many downstream folks will be very happy! |
Created maresb#1 which adds tests for status and headers to this PR. |
Sorry for being the bottleneck on the release here. I think this is finally ready to go. Thanks everyone for all the support! ❤️ |
@maresb thank you for this wonderfully designed PR! |
I marked this as maintenance (reworks the function signature of the |
Building on #300, illustrating my idea of implementing a
RewritableResponse
class. I tried to use Traitlets to make my data class, since it's used throughout the Jupyter ecosystem, but I don't know it very well.I was originally thinking of having
RewritableResponse
be the return type as well as the input type, but I found this awkward. Now I haverewrite_response
mutating theRewritableResponse
object.But this makes the variable names a bit awkward... in order for the variable names to make sense, I now defineoriginal_response
runrewrite_response(original_response)
, definerewritten_response
asoriginal_response
and then deleteoriginal_response
. There must be a better way which I'm not seeing at the moment.