Forum OpenACS Development: Re: POST Method can be broken when using host node map

Collapse
Posted by Gustaf Neumann on
I have not looked into the problem, but from the discussion, it is not clear to me, what the problem is: is it (a) the different host-name due to the host-node map, or (b) the redirection issue of a POST due to URL length limitations.

For (b) the only clean solution is to store the POST-data temporarily on the server and to refer to it in the redirection-URL, and resolve the reference in the request processor. It should not be hard to implement (famous last words), but there are a few hairy issues, and the likelihood to decrease the number of your friends by breaking their code is greater than 0.

Collapse
Posted by Eduardo Santos on
Hi Gustaf,

The problem to me seems more related to (b). Let me try to make myself more clear.

1 - Let's assume you have one subsite in yourdomain.com named subsite. So, the subsite URL will be http://yourdomain.com/subsite.

2 - Let's also assume that you have a host-node mapped do otherdomain.com pointing to your subsite. In such way, the subsite URL would be http://otherdomain.com

3 - We create a form under the URL add-edit. so this form has two URL's: http://yourdomain.com/subsite/add-edit and http://otherdomain.com/add-edit.

In the form code, the action link is allways going to be /subsite/add-edit, no matter how you access the site. This mean, if I type the URL http://otherdomain.com/add-edit and http://yourdomain.com/subsite/add-edit, in both ways the form action is going to be /subsite/add-edit, wich is going to be treated for request processor.

Ok, so we have this situation: the RP is going to execute this filter code:


# -------------------------------------------------------------------------
# Start of patch "hostname-based subsites"
# -------------------------------------------------------------------------
# 1. determine the root of the host and the requested URL
set root [root_of_host [ad_host]]
set url [ad_conn url]
# 2. handle special case: if the root is a prefix of the URL,
# remove this prefix from the URL, and redirect.
if { ![empty_string_p $root] } {
if { [regexp "^${root}(.*)$" $url match url] } {

if { [regexp {^GET [^\?]*\?(.*) HTTP} [ns_conn request] match vars] } {
append url ?$vars
}
if { [security::secure_conn_p] } {
# it's a secure connection.
ad_returnredirect https://[ad_host][ad_port]$url
return "filter_return"
} else {
ad_returnredirect http://[ad_host][ad_port]$url
return "filter_return"
}
}
# Normal case: Prepend the root to the URL.
# 3. set the intended URL
ad_conn -set url ${root}${url}

# 4. set urlv and urlc for consistency
set urlv [lrange [split $root /] 1 end]
ad_conn -set urlc [expr [ad_conn urlc]+[llength $urlv]]
ad_conn -set urlv [concat $urlv [ad_conn urlv]]
}
# -------------------------------------------------------------------------
# End of patch "hostname-based subsites"
# -------------------------------------------------------------------------

Ok, so here the URL is changed acording to the host. If you are using the host-node mapped subsite, it's going to cut the subsite URL (/subsite/add-edit) and deliver to the user the form URL (add-edit). It works fine for redirection, but there's a problem here: no matter how you access it, the POST is allways going to be:


POST /subsite/add-edit HTTP/1.1

When you are using the host-node mapped URL, the POST is going to be redirected to /add-edit, but the vars, that where in the POST body, are going to be lost, as they are associated to the /subsite/add-edit URL.

It seems like somebody have seen it before for the GET method, as we can see in these code lines:


if { [regexp {^GET [^\?]*\?(.*) HTTP} [ns_conn request] match vars] } {
append url ?$vars
}

So I proposed a similar patch, wich should work mostly:


--- openacs-4/packages/acs-tcl/tcl/request-processor-procs.tcl 2008-03-22 15:31:32.000000000 -0300
+++ acs-tcl/tcl/request-processor-procs.tcl 2008-04-16 11:01:58.000000000 -0300
@@ -525,12 +525,21 @@
set url [ad_conn url]
# 2. handle special case: if the root is a prefix of the URL,
# remove this prefix from the URL, and redirect.
- if { $root ne "" } {
+ if { ![empty_string_p $root] } {
if { [regexp "^${root}(.*)$" $url match url] } {

if { [regexp {^GET [^\?]*\?(.*) HTTP} [ns_conn request] match vars] } {
append url ?$vars
- }
+ }
+ # Handle POST bug: When the URL in POST request is different from the absolute one,
+ # the vars aren't sent to the page. So we past them as URL vars
+ if {[regexp {^POST (.*) HTTP} [ns_conn request] match post_url]} {
+ if {$post_url ne $url} {
+ set query [ns_getform]
+ set query "?[export_entire_form_as_url_vars]"
+ append url $query
+ }
+ }
if { [security::secure_conn_p] } {
# it's a secure connection.
ad_returnredirect https://[ad_host][ad_port]$url

However, this patch has a major issue: even that the absolute URL is not delivered to the user, as the form is going to be processed sometimes internally, if you are using some redirecting machine such as a proxy, you have to face Apache maximum character limit for GET method, wich is 4.000 as I could see. In that way, some forms are not going to be sent and the user is going to see the proxy error message.

So, you must be asking: why does it send a GET method with the vars? I'm still trying to figure this out, but I guess it has something to do with the ad_returnredirect in the patch. After all the filters are applied, it tries to redirect to the absolute page, without the subsite URL, and this is where the vars are lost.

I've tried some different approaches: when you detect a difference between the post url and redirect url, one could use this code:


util_httppost url formvars [ timeout ] [ depth ] [ http_referer ]

However, it also seems to fail. So, I ran out of options here. I don't want to lose any friends :) and neither want to break other people's code, and that's why I'm trying to find a better solution with you guys.

I am going to use this a lot more, and in order to do this I have to fix this issue.

So, if somebody can help, I'll be very thankfull.

:)