Step 2. However, when I submit an un-matching password, the form is redisplayed and URL is now http://example.com/wp-login.php?action=resetpass&key=xyz&login=zyx. Notice, that action has changed.
When I attempt this with an invalid key I get redirected to http://example.com/wp-login.php?action=lostpassword&error=invalidkey.
Anyway, the infinite loop is occurring because you are the condition in_array( $action, array('rp', 'resetpass') )
remains true for the url you are redirecting to, so you end up in a redirect loop.
The solution is to check that the requested url differs from the redirect url:
function pretty_login_urls_form_action_fix(){
$action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : '';
if( in_array( $action, array('rp', 'resetpass') ) ){
$requested_url = is_ssl() ? 'https://' : 'http://';
$requested_url .= $_SERVER['HTTP_HOST'];
$requested_url .= $_SERVER['REQUEST_URI'];
$redirect_url = home_url( '/password-restore?action='. $action .'&key=' . urlencode( $_GET['key'] ) . '&login=' . urlencode( $_GET['login'] ), 'login_post' );
if( $redirect_url !== $requested_url ){
wp_redirect( $redirect_url );
exit;
}
}
}