Rails 3.2 f.file_field raises a routing error

Tested on rails 3.2.12 and 3.2.11. In another rails project 3.2.11 I do not have this problem with f.file_field , but in the current I do and can not find the reason for this strange behavior, so here is my question.

I have a strange problem with the update action. Here are the relevant parts of the code:

routes:

 get "signup" => "users#new", :as => "signup" get "profile" => "users#profile", :as => "profile" resources :users do member do get :activate end end 

controller:

 def update @user = User.find(params[:id]) if @user.update_attributes(params[:user]) redirect_to user_path(@user), :notice => t('users_controller.update.updated') else render :edit end end 

in haml (simplified but has the same behavior):

 = form_for @user do |f| .field = f.label :first_name %br = f.text_field :first_name, :size => 40 .actions = f.submit 

So, after clicking the Refresh button, everything works as expected, and user attributes are updated. However, when I add the file field as follows:

 = form_for @user do |f| .field = f.label :first_name %br = f.text_field :first_name, :size => 40 .field = f.label :avatar %br = f.file_field :avatar .actions = f.submit 

and I click Refresh, then I get a routing error:

 No route matches [PUT] "/1" 

I donโ€™t understand why he is trying to reach path /1 using the PUT method. On the page displaying this routing error, I see /users/1 in the address bar of the browser.

html for the form is generated here:

  <form accept-charset="UTF-8" action="/users/1" class="edit_user" enctype="multipart/form-data" id="edit_user_1" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="&#x2713;" /><input name="_method" type="hidden" value="put" /><input name="authenticity_token" type="hidden" value="che8VLfDxDAoenma+TXwsA+0IQ7+/jbCIK+Q2xwr8uc=" /></div> <div class='field'> <label for="user_first_name">First name</label> <br> <input id="user_first_name" name="user[first_name]" size="40" type="text" value="Anton" /> </div> <div class='field'> <label for="user_avatar">Avatar</label> <br> <input id="user_avatar" name="user[avatar]" type="file" /> </div> <div class='actions'> <input name="commit" type="submit" value="Update User" /> </div> </form> 

So here is the fun part. When I change my form to this:

 = form_for @user do |f| .field = f.label :first_name %br = f.text_field :first_name, :size => 40 .field = f.label :avatar %br %input{:id => "user_avatar", :name => "user[avatar]", :type => "file"} .actions = f.submit 

then the generated html will be the same as in the previous case (the only difference I see is that single quotes instead of double quotes are used for file field attributes):

  <form accept-charset="UTF-8" action="/users/1" class="edit_user" id="edit_user_1" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="&#x2713;" /><input name="_method" type="hidden" value="put" /><input name="authenticity_token" type="hidden" value="che8VLfDxDAoenma+TXwsA+0IQ7+/jbCIK+Q2xwr8uc=" /></div> <div class='field'> <label for="user_first_name">First name</label> <br> <input id="user_first_name" name="user[first_name]" size="40" type="text" value="Anton" /> </div> <div class='field'> <label for="user_avatar">Avatar</label> <br> <input id='user_avatar' name='user[avatar]' type='file'> </div> <div class='actions'> <input name="commit" type="submit" value="Update User" /> </div> </form> 

But after submitting this form, there is no routing error, and everything works as it should.

UPDATE

In fact, it does not work as it should. I just looked at the params hash and saw that the :avatar key was present, but I missed this in the latter case, there is no enctype="multipart/form-data" attribute in the open open tag in html, so the file will not be uploaded. Adding the attribute enctype=multipart/form-data results in a repeated routing error.

I found that when adding the route put ":id" => "users#update" when trying redirect_to user_path(@user) after submitting a multi-page form (there is definitely no routing error for PUT with this route), then there is also a No route matches [GET] "/users/users/1" routing error No route matches [GET] "/users/users/1" .

Here is full of routes.rb :

 Myapp::Application.routes.draw do match "oauth/callback" => "oauths#callback" match "oauth/callback/:provider" => "oauths#callback" match "oauth/:provider" => "oauths#oauth", :as => :auth_at_provider resources :countries resources :categories resources :images resources :collections resources :items put ":id" => "users#update" get "signup" => "users#new", :as => "signup" get "profile" => "users#profile", :as => "profile" resources :users do member do get :activate end end get "signout" => "sessions#destroy", :as => "signout" get "signin" => "sessions#new", :as => "signin" resources :sessions get "site/index" root :to => "site#index" end 

and rake routes

  oauth_callback /oauth/callback(.:format) oauths#callback /oauth/callback/:provider(.:format) oauths#callback auth_at_provider /oauth/:provider(.:format) oauths#oauth countries GET /countries(.:format) countries#index POST /countries(.:format) countries#create new_country GET /countries/new(.:format) countries#new edit_country GET /countries/:id/edit(.:format) countries#edit country GET /countries/:id(.:format) countries#show PUT /countries/:id(.:format) countries#update DELETE /countries/:id(.:format) countries#destroy categories GET /categories(.:format) categories#index POST /categories(.:format) categories#create new_category GET /categories/new(.:format) categories#new edit_category GET /categories/:id/edit(.:format) categories#edit category GET /categories/:id(.:format) categories#show PUT /categories/:id(.:format) categories#update DELETE /categories/:id(.:format) categories#destroy images GET /images(.:format) images#index POST /images(.:format) images#create new_image GET /images/new(.:format) images#new edit_image GET /images/:id/edit(.:format) images#edit image GET /images/:id(.:format) images#show PUT /images/:id(.:format) images#update DELETE /images/:id(.:format) images#destroy collections GET /collections(.:format) collections#index POST /collections(.:format) collections#create new_collection GET /collections/new(.:format) collections#new edit_collection GET /collections/:id/edit(.:format) collections#edit collection GET /collections/:id(.:format) collections#show PUT /collections/:id(.:format) collections#update DELETE /collections/:id(.:format) collections#destroy items GET /items(.:format) items#index POST /items(.:format) items#create new_item GET /items/new(.:format) items#new edit_item GET /items/:id/edit(.:format) items#edit item GET /items/:id(.:format) items#show PUT /items/:id(.:format) items#update DELETE /items/:id(.:format) items#destroy PUT /:id(.:format) users#update signup GET /signup(.:format) users#new profile GET /profile(.:format) users#profile activate_user GET /users/:id/activate(.:format) users#activate users GET /users(.:format) users#index POST /users(.:format) users#create new_user GET /users/new(.:format) users#new edit_user GET /users/:id/edit(.:format) users#edit user GET /users/:id(.:format) users#show PUT /users/:id(.:format) users#update DELETE /users/:id(.:format) users#destroy signout GET /signout(.:format) sessions#destroy signin GET /signin(.:format) sessions#new sessions GET /sessions(.:format) sessions#index POST /sessions(.:format) sessions#create new_session GET /sessions/new(.:format) sessions#new edit_session GET /sessions/:id/edit(.:format) sessions#edit session GET /sessions/:id(.:format) sessions#show PUT /sessions/:id(.:format) sessions#update DELETE /sessions/:id(.:format) sessions#destroy site_index GET /site/index(.:format) site#index root / 

Does anyone have an idea?

UPDATE2

Identifying the problem using multipart forms helped to find this post about the same problem - Routing error with Post / Put messages (passenger headers) , but, unfortunately, there is no solution ...

Update3

I found something interesting. There is a method in /path_to_gemset_here/gem/journey-1.0.4/lib/journey/router.rb :

 def find_routes env req = request_class.new env routes = filter_routes(req.path_info) + custom_routes.find_all { |r| r.path.match(req.path_info) } routes.sort_by(&:precedence).find_all { |r| r.constraints.all? { |k,v| v === req.send(k) } && r.verb === req.request_method }.reject { |r| req.ip && !(r.ip === req.ip) }.map { |r| match_data = r.path.match(req.path_info) match_names = match_data.names.map { |n| n.to_sym } match_values = match_data.captures.map { |v| v && Utils.unescape_uri(v) } info = Hash[match_names.zip(match_values).find_all { |_,y| y }] [match_data, r.defaults.merge(info), r] } end 

I checked env for both asymmetric and multidisciplinary queries and found this:

not frequent:

 "REQUEST_URI"=>"/users/1", "SCRIPT_NAME"=>"", "PATH_INFO"=>"/users/1" 

multi-part:

 "REQUEST_URI"=>"/users/1", "SCRIPT_NAME"=>"/users", "PATH_INFO"=>"/1", "SCRIPT_FILENAME"=>"/path_to_project_folder_here/public/users", - there is no such variable in a non-multipart request 

So here is the problem. As I see in the method definition:

 match_data = r.path.match(req.path_info) 

PATH_INFO used to find the route for processing the request, but in the latter case it is completely erroneous due to something divides REQUEST_URI into two parts. Unfortunately, I donโ€™t have time to finish my investigation today, I hope that I can do it tomorrow.

If someone has enough curiosity to find the origin of the problem faster than me, please:

UPDATE4 (edited)

So, here is the continuation of the study.

method: parse_native_request in the file: /path_to_gemset_here/gems/passenger-3.0.17/lib/phusion_passenger/abstract_request_handler.rb

the headers_data variable after this call:

 headers_data = channel.read_scalar(buffer, MAX_HEADER_SIZE) 

contains:

 "SERVER_SOFTWARE\x00Apache/2.2.22 (Ubuntu)\x00 SERVER_PROTOCOL\x00HTTP/1.1\x00 SERVER_NAME\x00myapp.loc\x00 SERVER_ADMIN\x00[no address given]\x00 SERVER_ADDR\x00127.0.0.1\x00 SERVER_PORT\x0080\x00 REMOTE_ADDR\x00127.0.0.1\x00 REMOTE_PORT\x0033199\x00 REQUEST_METHOD\x00POST\x00 QUERY_STRING\x00\x00 CONTENT_TYPE\x00multipart/form-data; boundary=----WebKitFormBoundary8HlzQxocoOROMfRV\x00 DOCUMENT_ROOT\x00/path_to_project_folder_here/public\x00 REQUEST_URI\x00/users/1\x00 SCRIPT_NAME\x00\x00 PATH_INFO\x00/users/1\x00 HTTP_HOST\x00myapp.loc\x00 HTTP_CONNECTION\x00keep-alive\x00 HTTP_CONTENT_LENGTH\x00748\x00 HTTP_CACHE_CONTROL\x00max-age=0\x00 HTTP_ACCEPT\x00text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\x00 HTTP_ORIGIN\x00http://myapp.loc\x00 HTTP_USER_AGENT\x00Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22\x00 HTTP_CONTENT_TYPE\x00multipart/form-data; boundary=----WebKitFormBoundary8HlzQxocoOROMfRV\x00 HTTP_REFERER\x00http://myapp.loc/profile\x00 HTTP_ACCEPT_ENCODING\x00gzip,deflate,sdch\x00 HTTP_ACCEPT_LANGUAGE\x00en-US,en;q=0.8\x00 HTTP_ACCEPT_CHARSET\x00ISO-8859-1,utf-8;q=0.7,*;q=0.3\x00 HTTP_COOKIE\x00_myapp_session=BAh7CEkiDHVzZXJfaWQGOgZFRmkGSSIPc2Vzc2lvbl9pZAY7AEZJIiVhMjU2ZjU5N2VmMTE0YTJiOGEwNGJiYzUyYjM2NDg0OQY7AFRJIhBfY3NyZl90b2tlbgY7AEZJIjFjaGU4VkxmRHhEQW9lbm1hK1RYd3NBKzBJUTcrL2piQ0lLK1EyeHdyOHVjPQY7AEY%3D--a6e5daff1334c083e54b2bcafba43b32e546af9c\x00 UNIQUE_ID\x00UTXfEX8AAQEAACWVEMoAAAAB\x00 GATEWAY_INTERFACE\x00CGI/1.1\x00 >>>> here seems to start a kind of redirect <<<< SERVER_PROTOCOL\x00HTTP/1.1\x00 REQUEST_METHOD\x00POST\x00 QUERY_STRING\x00\x00 REQUEST_URI\x00/users/1\x00 SCRIPT_NAME\x00/users\x00 PATH_INFO\x00/1\x00 PATH_TRANSLATED\x00/path_to_project_folder_here/public/1\x00 HTTP_HOST\x00myapp.loc\x00 HTTP_CONNECTION\x00keep-alive\x00 CONTENT_LENGTH\x00748\x00HTTP_CACHE_CONTROL\x00max-age=0\x00 HTTP_ACCEPT\x00text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\x00 HTTP_ORIGIN\x00http://myapp.loc\x00 HTTP_USER_AGENT\x00Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22\x00CONTENT_TYPE\x00multipart/form-data; boundary=----WebKitFormBoundary8HlzQxocoOROMfRV\x00 HTTP_REFERER\x00http://myapp.loc/profile\x00 HTTP_ACCEPT_ENCODING\x00gzip,deflate,sdch\x00 HTTP_ACCEPT_LANGUAGE\x00en-US,en;q=0.8\x00 HTTP_ACCEPT_CHARSET\x00ISO-8859-1,utf-8;q=0.7,*;q=0.3\x00 HTTP_COOKIE\x00_myapp_session=BAh7CEkiDHVzZXJfaWQGOgZFRmkGSSIPc2Vzc2lvbl9pZAY7AEZJIiVhMjU2ZjU5N2VmMTE0YTJiOGEwNGJiYzUyYjM2NDg0OQY7AFRJIhBfY3NyZl90b2tlbgY7AEZJIjFjaGU4VkxmRHhEQW9lbm1hK1RYd3NBKzBJUTcrL2piQ0lLK1EyeHdyOHVjPQY7AEY%3D--a6e5daff1334c083e54b2bcafba43b32e546af9c\x00 PATH\x00/usr/local/bin:/usr/bin:/bin\x00 SERVER_SIGNATURE\x00<address>Apache/2.2.22 (Ubuntu) Server at myapp.loc Port 80</address>\n\x00 SERVER_SOFTWARE\x00Apache/2.2.22 (Ubuntu)\x00 SERVER_NAME\x00myapp.loc\x00 SERVER_ADDR\x00127.0.0.1\x00 SERVER_PORT\x0080\x00 REMOTE_ADDR\x00127.0.0.1\x00 DOCUMENT_ROOT\x00/path_to_project_folder_here/public\x00 SERVER_ADMIN\x00[no address given]\x00 SCRIPT_FILENAME\x00/path_to_project_folder_here/public/users\x00 REMOTE_PORT\x0033199\x00 PATH_TRANSLATED\x00/bin/runAV\x00 REDIRECT_STATUS\x00302\x00 PASSENGER_CONNECT_PASSWORD\x00EElt7wIBLlliWGCYJJoezPvecsB2brraBWdiIbD4nul\x00_\x00_\x00" a6e5daff1334c083e54b2bcafba43b32e546af9c \ x00 "SERVER_SOFTWARE\x00Apache/2.2.22 (Ubuntu)\x00 SERVER_PROTOCOL\x00HTTP/1.1\x00 SERVER_NAME\x00myapp.loc\x00 SERVER_ADMIN\x00[no address given]\x00 SERVER_ADDR\x00127.0.0.1\x00 SERVER_PORT\x0080\x00 REMOTE_ADDR\x00127.0.0.1\x00 REMOTE_PORT\x0033199\x00 REQUEST_METHOD\x00POST\x00 QUERY_STRING\x00\x00 CONTENT_TYPE\x00multipart/form-data; boundary=----WebKitFormBoundary8HlzQxocoOROMfRV\x00 DOCUMENT_ROOT\x00/path_to_project_folder_here/public\x00 REQUEST_URI\x00/users/1\x00 SCRIPT_NAME\x00\x00 PATH_INFO\x00/users/1\x00 HTTP_HOST\x00myapp.loc\x00 HTTP_CONNECTION\x00keep-alive\x00 HTTP_CONTENT_LENGTH\x00748\x00 HTTP_CACHE_CONTROL\x00max-age=0\x00 HTTP_ACCEPT\x00text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\x00 HTTP_ORIGIN\x00http://myapp.loc\x00 HTTP_USER_AGENT\x00Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22\x00 HTTP_CONTENT_TYPE\x00multipart/form-data; boundary=----WebKitFormBoundary8HlzQxocoOROMfRV\x00 HTTP_REFERER\x00http://myapp.loc/profile\x00 HTTP_ACCEPT_ENCODING\x00gzip,deflate,sdch\x00 HTTP_ACCEPT_LANGUAGE\x00en-US,en;q=0.8\x00 HTTP_ACCEPT_CHARSET\x00ISO-8859-1,utf-8;q=0.7,*;q=0.3\x00 HTTP_COOKIE\x00_myapp_session=BAh7CEkiDHVzZXJfaWQGOgZFRmkGSSIPc2Vzc2lvbl9pZAY7AEZJIiVhMjU2ZjU5N2VmMTE0YTJiOGEwNGJiYzUyYjM2NDg0OQY7AFRJIhBfY3NyZl90b2tlbgY7AEZJIjFjaGU4VkxmRHhEQW9lbm1hK1RYd3NBKzBJUTcrL2piQ0lLK1EyeHdyOHVjPQY7AEY%3D--a6e5daff1334c083e54b2bcafba43b32e546af9c\x00 UNIQUE_ID\x00UTXfEX8AAQEAACWVEMoAAAAB\x00 GATEWAY_INTERFACE\x00CGI/1.1\x00 >>>> here seems to start a kind of redirect <<<< SERVER_PROTOCOL\x00HTTP/1.1\x00 REQUEST_METHOD\x00POST\x00 QUERY_STRING\x00\x00 REQUEST_URI\x00/users/1\x00 SCRIPT_NAME\x00/users\x00 PATH_INFO\x00/1\x00 PATH_TRANSLATED\x00/path_to_project_folder_here/public/1\x00 HTTP_HOST\x00myapp.loc\x00 HTTP_CONNECTION\x00keep-alive\x00 CONTENT_LENGTH\x00748\x00HTTP_CACHE_CONTROL\x00max-age=0\x00 HTTP_ACCEPT\x00text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\x00 HTTP_ORIGIN\x00http://myapp.loc\x00 HTTP_USER_AGENT\x00Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22\x00CONTENT_TYPE\x00multipart/form-data; boundary=----WebKitFormBoundary8HlzQxocoOROMfRV\x00 HTTP_REFERER\x00http://myapp.loc/profile\x00 HTTP_ACCEPT_ENCODING\x00gzip,deflate,sdch\x00 HTTP_ACCEPT_LANGUAGE\x00en-US,en;q=0.8\x00 HTTP_ACCEPT_CHARSET\x00ISO-8859-1,utf-8;q=0.7,*;q=0.3\x00 HTTP_COOKIE\x00_myapp_session=BAh7CEkiDHVzZXJfaWQGOgZFRmkGSSIPc2Vzc2lvbl9pZAY7AEZJIiVhMjU2ZjU5N2VmMTE0YTJiOGEwNGJiYzUyYjM2NDg0OQY7AFRJIhBfY3NyZl90b2tlbgY7AEZJIjFjaGU4VkxmRHhEQW9lbm1hK1RYd3NBKzBJUTcrL2piQ0lLK1EyeHdyOHVjPQY7AEY%3D--a6e5daff1334c083e54b2bcafba43b32e546af9c\x00 PATH\x00/usr/local/bin:/usr/bin:/bin\x00 SERVER_SIGNATURE\x00<address>Apache/2.2.22 (Ubuntu) Server at myapp.loc Port 80</address>\n\x00 SERVER_SOFTWARE\x00Apache/2.2.22 (Ubuntu)\x00 SERVER_NAME\x00myapp.loc\x00 SERVER_ADDR\x00127.0.0.1\x00 SERVER_PORT\x0080\x00 REMOTE_ADDR\x00127.0.0.1\x00 DOCUMENT_ROOT\x00/path_to_project_folder_here/public\x00 SERVER_ADMIN\x00[no address given]\x00 SCRIPT_FILENAME\x00/path_to_project_folder_here/public/users\x00 REMOTE_PORT\x0033199\x00 PATH_TRANSLATED\x00/bin/runAV\x00 REDIRECT_STATUS\x00302\x00 PASSENGER_CONNECT_PASSWORD\x00EElt7wIBLlliWGCYJJoezPvecsB2brraBWdiIbD4nul\x00_\x00_\x00" a6e5daff1334c083e54b2bcafba43b32e546af9c \ x00 "SERVER_SOFTWARE\x00Apache/2.2.22 (Ubuntu)\x00 SERVER_PROTOCOL\x00HTTP/1.1\x00 SERVER_NAME\x00myapp.loc\x00 SERVER_ADMIN\x00[no address given]\x00 SERVER_ADDR\x00127.0.0.1\x00 SERVER_PORT\x0080\x00 REMOTE_ADDR\x00127.0.0.1\x00 REMOTE_PORT\x0033199\x00 REQUEST_METHOD\x00POST\x00 QUERY_STRING\x00\x00 CONTENT_TYPE\x00multipart/form-data; boundary=----WebKitFormBoundary8HlzQxocoOROMfRV\x00 DOCUMENT_ROOT\x00/path_to_project_folder_here/public\x00 REQUEST_URI\x00/users/1\x00 SCRIPT_NAME\x00\x00 PATH_INFO\x00/users/1\x00 HTTP_HOST\x00myapp.loc\x00 HTTP_CONNECTION\x00keep-alive\x00 HTTP_CONTENT_LENGTH\x00748\x00 HTTP_CACHE_CONTROL\x00max-age=0\x00 HTTP_ACCEPT\x00text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\x00 HTTP_ORIGIN\x00http://myapp.loc\x00 HTTP_USER_AGENT\x00Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22\x00 HTTP_CONTENT_TYPE\x00multipart/form-data; boundary=----WebKitFormBoundary8HlzQxocoOROMfRV\x00 HTTP_REFERER\x00http://myapp.loc/profile\x00 HTTP_ACCEPT_ENCODING\x00gzip,deflate,sdch\x00 HTTP_ACCEPT_LANGUAGE\x00en-US,en;q=0.8\x00 HTTP_ACCEPT_CHARSET\x00ISO-8859-1,utf-8;q=0.7,*;q=0.3\x00 HTTP_COOKIE\x00_myapp_session=BAh7CEkiDHVzZXJfaWQGOgZFRmkGSSIPc2Vzc2lvbl9pZAY7AEZJIiVhMjU2ZjU5N2VmMTE0YTJiOGEwNGJiYzUyYjM2NDg0OQY7AFRJIhBfY3NyZl90b2tlbgY7AEZJIjFjaGU4VkxmRHhEQW9lbm1hK1RYd3NBKzBJUTcrL2piQ0lLK1EyeHdyOHVjPQY7AEY%3D--a6e5daff1334c083e54b2bcafba43b32e546af9c\x00 UNIQUE_ID\x00UTXfEX8AAQEAACWVEMoAAAAB\x00 GATEWAY_INTERFACE\x00CGI/1.1\x00 >>>> here seems to start a kind of redirect <<<< SERVER_PROTOCOL\x00HTTP/1.1\x00 REQUEST_METHOD\x00POST\x00 QUERY_STRING\x00\x00 REQUEST_URI\x00/users/1\x00 SCRIPT_NAME\x00/users\x00 PATH_INFO\x00/1\x00 PATH_TRANSLATED\x00/path_to_project_folder_here/public/1\x00 HTTP_HOST\x00myapp.loc\x00 HTTP_CONNECTION\x00keep-alive\x00 CONTENT_LENGTH\x00748\x00HTTP_CACHE_CONTROL\x00max-age=0\x00 HTTP_ACCEPT\x00text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\x00 HTTP_ORIGIN\x00http://myapp.loc\x00 HTTP_USER_AGENT\x00Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22\x00CONTENT_TYPE\x00multipart/form-data; boundary=----WebKitFormBoundary8HlzQxocoOROMfRV\x00 HTTP_REFERER\x00http://myapp.loc/profile\x00 HTTP_ACCEPT_ENCODING\x00gzip,deflate,sdch\x00 HTTP_ACCEPT_LANGUAGE\x00en-US,en;q=0.8\x00 HTTP_ACCEPT_CHARSET\x00ISO-8859-1,utf-8;q=0.7,*;q=0.3\x00 HTTP_COOKIE\x00_myapp_session=BAh7CEkiDHVzZXJfaWQGOgZFRmkGSSIPc2Vzc2lvbl9pZAY7AEZJIiVhMjU2ZjU5N2VmMTE0YTJiOGEwNGJiYzUyYjM2NDg0OQY7AFRJIhBfY3NyZl90b2tlbgY7AEZJIjFjaGU4VkxmRHhEQW9lbm1hK1RYd3NBKzBJUTcrL2piQ0lLK1EyeHdyOHVjPQY7AEY%3D--a6e5daff1334c083e54b2bcafba43b32e546af9c\x00 PATH\x00/usr/local/bin:/usr/bin:/bin\x00 SERVER_SIGNATURE\x00<address>Apache/2.2.22 (Ubuntu) Server at myapp.loc Port 80</address>\n\x00 SERVER_SOFTWARE\x00Apache/2.2.22 (Ubuntu)\x00 SERVER_NAME\x00myapp.loc\x00 SERVER_ADDR\x00127.0.0.1\x00 SERVER_PORT\x0080\x00 REMOTE_ADDR\x00127.0.0.1\x00 DOCUMENT_ROOT\x00/path_to_project_folder_here/public\x00 SERVER_ADMIN\x00[no address given]\x00 SCRIPT_FILENAME\x00/path_to_project_folder_here/public/users\x00 REMOTE_PORT\x0033199\x00 PATH_TRANSLATED\x00/bin/runAV\x00 REDIRECT_STATUS\x00302\x00 PASSENGER_CONNECT_PASSWORD\x00EElt7wIBLlliWGCYJJoezPvecsB2brraBWdiIbD4nul\x00_\x00_\x00" 

This is followed by this call:

 headers = split_by_null_into_hash(headers_data) 

and headers contains:

 {"SERVER_SOFTWARE"=>"Apache/2.2.22 (Ubuntu)", "SERVER_PROTOCOL"=>"HTTP/1.1", "SERVER_NAME"=>"myapp.loc", "SERVER_ADMIN"=>"[no address given]", "SERVER_ADDR"=>"127.0.0.1", "SERVER_PORT"=>"80", "REMOTE_ADDR"=>"127.0.0.1", "REMOTE_PORT"=>"33243", "REQUEST_METHOD"=>"POST", "QUERY_STRING"=>"", "CONTENT_TYPE"=>"multipart/form-data; boundary=----WebKitFormBoundary8HlzQxocoOROMfRV", "DOCUMENT_ROOT"=>"/path_to_project_folder_here/public", "REQUEST_URI"=>"/users/1", "SCRIPT_NAME"=>"/users", "PATH_INFO"=>"/1", "HTTP_HOST"=>"myapp.loc", "HTTP_CONNECTION"=>"keep-alive", "HTTP_CONTENT_LENGTH"=>"748", "HTTP_CACHE_CONTROL"=>"max-age=0", "HTTP_ACCEPT"=>"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "HTTP_ORIGIN"=>"http://myapp.loc", "HTTP_USER_AGENT"=>"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22", "HTTP_CONTENT_TYPE"=>"multipart/form-data; boundary=----WebKitFormBoundary8HlzQxocoOROMfRV", "HTTP_REFERER"=>"http://myapp.loc/profile", "HTTP_ACCEPT_ENCODING"=>"gzip,deflate,sdch", "HTTP_ACCEPT_LANGUAGE"=>"en-US,en;q=0.8", "HTTP_ACCEPT_CHARSET"=>"ISO-8859-1,utf-8;q=0.7,*;q=0.3", "HTTP_COOKIE"=>"_myapp_session=BAh7CEkiDHVzZXJfaWQGOgZFRmkGSSIPc2Vzc2lvbl9pZAY7AEZJIiVhMjU2ZjU5N2VmMTE0YTJiOGEwNGJiYzUyYjM2NDg0OQY7AFRJIhBfY3NyZl90b2tlbgY7AEZJIjFjaGU4VkxmRHhEQW9lbm1hK1RYd3NBKzBJUTcrL2piQ0lLK1EyeHdyOHVjPQY7AEY%3D--a6e5daff1334c083e54b2bcafba43b32e546af9c", "UNIQUE_ID"=>"UTXjXn8AAQEAACceEdgAAAAA", "GATEWAY_INTERFACE"=>"CGI/1.1", "PATH_TRANSLATED"=>"/bin/runAV", "CONTENT_LENGTH"=>"748", "PATH"=>"/usr/local/bin:/usr/bin:/bin", "SERVER_SIGNATURE"=>"<address>Apache/2.2.22 (Ubuntu) Server at myapp.loc Port 80</address>\n", "SCRIPT_FILENAME"=>"/path_to_project_folder_here/public/users", "REDIRECT_STATUS"=>"302", "PASSENGER_CONNECT_PASSWORD"=>"GgEqWssAcbBETWnFI7xzBfWRGibgB34OhfFSUVyOhPn", "_"=>"_"} = BAh7CEkiDHVzZXJfaWQGOgZFRmkGSSIPc2Vzc2lvbl9pZAY7AEZJIiVhMjU2ZjU5N2VmMTE0YTJiOGEwNGJiYzUyYjM2NDg0OQY7AFRJIhBfY3NyZl90b2tlbgY7AEZJIjFjaGU4VkxmRHhEQW9lbm1hK1RYd3NBKzBJUTcrL2piQ0lLK1EyeHdyOHVjPQY7AEY% 3D - a6e5daff1334c083e54b2bcafba43b32e546af9c", {"SERVER_SOFTWARE"=>"Apache/2.2.22 (Ubuntu)", "SERVER_PROTOCOL"=>"HTTP/1.1", "SERVER_NAME"=>"myapp.loc", "SERVER_ADMIN"=>"[no address given]", "SERVER_ADDR"=>"127.0.0.1", "SERVER_PORT"=>"80", "REMOTE_ADDR"=>"127.0.0.1", "REMOTE_PORT"=>"33243", "REQUEST_METHOD"=>"POST", "QUERY_STRING"=>"", "CONTENT_TYPE"=>"multipart/form-data; boundary=----WebKitFormBoundary8HlzQxocoOROMfRV", "DOCUMENT_ROOT"=>"/path_to_project_folder_here/public", "REQUEST_URI"=>"/users/1", "SCRIPT_NAME"=>"/users", "PATH_INFO"=>"/1", "HTTP_HOST"=>"myapp.loc", "HTTP_CONNECTION"=>"keep-alive", "HTTP_CONTENT_LENGTH"=>"748", "HTTP_CACHE_CONTROL"=>"max-age=0", "HTTP_ACCEPT"=>"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "HTTP_ORIGIN"=>"http://myapp.loc", "HTTP_USER_AGENT"=>"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.97 Safari/537.22", "HTTP_CONTENT_TYPE"=>"multipart/form-data; boundary=----WebKitFormBoundary8HlzQxocoOROMfRV", "HTTP_REFERER"=>"http://myapp.loc/profile", "HTTP_ACCEPT_ENCODING"=>"gzip,deflate,sdch", "HTTP_ACCEPT_LANGUAGE"=>"en-US,en;q=0.8", "HTTP_ACCEPT_CHARSET"=>"ISO-8859-1,utf-8;q=0.7,*;q=0.3", "HTTP_COOKIE"=>"_myapp_session=BAh7CEkiDHVzZXJfaWQGOgZFRmkGSSIPc2Vzc2lvbl9pZAY7AEZJIiVhMjU2ZjU5N2VmMTE0YTJiOGEwNGJiYzUyYjM2NDg0OQY7AFRJIhBfY3NyZl90b2tlbgY7AEZJIjFjaGU4VkxmRHhEQW9lbm1hK1RYd3NBKzBJUTcrL2piQ0lLK1EyeHdyOHVjPQY7AEY%3D--a6e5daff1334c083e54b2bcafba43b32e546af9c", "UNIQUE_ID"=>"UTXjXn8AAQEAACceEdgAAAAA", "GATEWAY_INTERFACE"=>"CGI/1.1", "PATH_TRANSLATED"=>"/bin/runAV", "CONTENT_LENGTH"=>"748", "PATH"=>"/usr/local/bin:/usr/bin:/bin", "SERVER_SIGNATURE"=>"<address>Apache/2.2.22 (Ubuntu) Server at myapp.loc Port 80</address>\n", "SCRIPT_FILENAME"=>"/path_to_project_folder_here/public/users", "REDIRECT_STATUS"=>"302", "PASSENGER_CONNECT_PASSWORD"=>"GgEqWssAcbBETWnFI7xzBfWRGibgB34OhfFSUVyOhPn", "_"=>"_"} 

So the problem, obviously, is that the headers are packed in a hash - for PATH_INFO (and for other headers) there are two values, and the last (incorrect) overwrites the first (indeed, the problem is the reason that these headers are sent, but I donโ€™t know how to handle this). Hash packing occurs in the split_by_null_into_hash(headers_data) method. Now let's go there.

file: /path_to_gemset_here/gems/passenger-3.0.17/lib/phusion_passenger/utils.rb

The Utils module contains this code:

 if defined?(PhusionPassenger::NativeSupport) # Split the given string into an hash. Keys and values are obtained by splitting the # string using the null character as the delimitor. def split_by_null_into_hash(data) return PhusionPassenger::NativeSupport.split_by_null_into_hash(data) end else NULL = "\0".freeze def split_by_null_into_hash(data) args = data.split(NULL, -1) args.pop return Hash[*args] end end 

In my case, the if condition is satisfied, so now the problem goes to

 PhusionPassenger::NativeSupport.split_by_null_into_hash(data) 

and this seems to lead us to the file: /path_to_gemset_here/gems/passenger-3.0.17/ext/ruby/passenger_native_support.c

to continue...

UPDATE5

In fact, I decided not to deal with this C -file debugging addek, because I believe that this file is compiled during the installation of the passenger and debug it. I will need to reinstall and reinstall the passenger again and again. So I decided to switch to using the else -part condition, since it seems to achieve exactly the same goal, but obviously a little slower than the precompiled C code. But in my case it does not really matter. So I tried the method definition by including the file in the /path_to_project_folder_here/lib folder with this code:

 module PhusionPassenger module Utils protected NULL = "\0".freeze def split_by_null_into_hash(data) args = data.split(NULL, -1) args.pop return Hash[*args] end end end 

I canโ€™t change the behavior of Hash[*args] (more precisely, I can override the ::[] method, but I donโ€™t want it exactly), so Iโ€™ll change the code a bit:

 module PhusionPassenger module Utils protected NULL = "\0".freeze def split_by_null_into_hash(data) args = data.split(NULL, -1) args.pop headers_hash = Hash.new args.each_slice(2).to_a.each do |pair| headers_hash[pair.first] = pair.last unless headers_hash.keys.include? pair.first end return headers_hash end end end 

And bingo! Now it works.

However, I am not sure that I did not violate any other functions by doing this, so I cannot advise anyone to use this approach. I will use it until I encounter any problem associated with this modification. If so, I will try to find another way to solve the problem.

And the main question still is why those wrong headers are being sent.

+3
source share
1 answer

Create passenger_extension.rb in the lib folder using this code:

Passenger 3

 module PhusionPassenger module Utils protected NULL = "\0".freeze def split_by_null_into_hash(data) args = data.split(NULL, -1) args.pop headers_hash = Hash.new args.each_slice(2).to_a.each do |pair| headers_hash[pair.first] = pair.last unless headers_hash.keys.include? pair.first end return headers_hash end end end 

Passenger 5

 module PhusionPassenger module Utils # Utility functions that can potentially be accelerated by native_support functions. module NativeSupportUtils extend self NULL = "\0".freeze class ProcessTimes < Struct.new(:utime, :stime) end def split_by_null_into_hash(data) args = data.split(NULL, -1) args.pop headers_hash = Hash.new args.each_slice(2).to_a.each do |pair| headers_hash[pair.first] = pair.last unless headers_hash.keys.include? pair.first end return headers_hash end def process_times times = Process.times return ProcessTimes.new((times.utime * 1_000_000).to_i, (times.stime * 1_000_000).to_i) end end end # module Utils end # module PhusionPassenger 

And then in 'config / application.rb' do:

 class Application < Rails::Application ... config.autoload_paths += %W(#{config.root}/lib) require 'passenger_extension' end 

Then restart the web server.

NOTE: I am not sure that this does not violate any other functions, so use it at your own risk, and please let me know if you find any harm from this approach.

+3
source

All Articles