Skip to content
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

NoMethodError when making request to an invoker-proxied task #130

Closed
ysimonson opened this issue Apr 16, 2015 · 8 comments
Closed

NoMethodError when making request to an invoker-proxied task #130

ysimonson opened this issue Apr 16, 2015 · 8 comments

Comments

@ysimonson
Copy link

In my invoker.ini, I have an HTTP server. When making requests to it via the browser, everything works fine. When making requests via a node.js HTTP client, this happens:

/usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/balancer.rb:105:in `extract_host_from_domain': undefined method `match' for nil:NilClass (NoMethodError)
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/balancer.rb:111:in `select_backend_config'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/balancer.rb:68:in `headers_received'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/balancer.rb:51:in `block in install_callbacks'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/http_parser.rb:63:in `call'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/http_parser.rb:63:in `complete_headers_received'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/http_parser.rb:11:in `block in initialize'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/http_parser.rb:40:in `<<'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/http_parser.rb:40:in `<<'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/balancer.rb:84:in `append_for_http_parsing'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/balancer.rb:79:in `upstream_data'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/balancer.rb:53:in `block in install_callbacks'
    from /usr/local/lib/ruby/gems/2.2.0/gems/em-proxy-0.1.8/lib/em-proxy/connection.rb:20:in `call'
    from /usr/local/lib/ruby/gems/2.2.0/gems/em-proxy-0.1.8/lib/em-proxy/connection.rb:20:in `receive_data'
    from /usr/local/lib/ruby/gems/2.2.0/gems/eventmachine-1.0.4/lib/eventmachine.rb:187:in `run_machine'
    from /usr/local/lib/ruby/gems/2.2.0/gems/eventmachine-1.0.4/lib/eventmachine.rb:187:in `run'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/powerup.rb:13:in `run'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/powerup.rb:7:in `block in fork_and_start'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/powerup.rb:7:in `fork'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/powerup.rb:7:in `fork_and_start'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/process_manager.rb:72:in `run_power_server'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/commander.rb:39:in `start_manager'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/cli.rb:46:in `start'
    from /usr/local/lib/ruby/gems/2.2.0/gems/thor-0.19.1/lib/thor/command.rb:27:in `run'
    from /usr/local/lib/ruby/gems/2.2.0/gems/thor-0.19.1/lib/thor/invocation.rb:126:in `invoke_command'
    from /usr/local/lib/ruby/gems/2.2.0/gems/thor-0.19.1/lib/thor.rb:359:in `dispatch'
    from /usr/local/lib/ruby/gems/2.2.0/gems/thor-0.19.1/lib/thor/base.rb:440:in `start'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/cli.rb:12:in `start'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/bin/invoker:7:in `<top (required)>'
    from /usr/local/bin/invoker:23:in `load'
    from /usr/local/bin/invoker:23:in `<main>'

Here's the node.js script used to make the request:

var request = require('request');
request('http://web.dev', function (error, response, body) {
  if (!error && response.statusCode == 200) {
    console.log(body) // Show the HTML for the Google homepage. 
  }
})

It uses this lib

@ysimonson
Copy link
Author

This error happens when no Host header is set, due to this line: https://github.com/code-mancers/invoker/blob/master/lib/invoker/power/balancer.rb#L67

@ysimonson
Copy link
Author

That line was different from my installed version of invoker (the latest from rubygems, 1.3.2), so I tried re-installing invoker from the repo and got this error instead:

/usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/url_rewriter.rb:21:in `block in extract_host_from_domain': undefined method `match' for nil:NilClass (NoMethodError)
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/url_rewriter.rb:20:in `map'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/url_rewriter.rb:20:in `extract_host_from_domain'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/url_rewriter.rb:7:in `select_backend_config'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/balancer.rb:67:in `headers_received'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/balancer.rb:50:in `block in install_callbacks'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/http_parser.rb:63:in `call'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/http_parser.rb:63:in `complete_headers_received'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/http_parser.rb:11:in `block in initialize'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/http_parser.rb:40:in `<<'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/http_parser.rb:40:in `<<'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/balancer.rb:83:in `append_for_http_parsing'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/balancer.rb:78:in `upstream_data'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/balancer.rb:52:in `block in install_callbacks'
    from /usr/local/lib/ruby/gems/2.2.0/gems/em-proxy-0.1.8/lib/em-proxy/connection.rb:20:in `call'
    from /usr/local/lib/ruby/gems/2.2.0/gems/em-proxy-0.1.8/lib/em-proxy/connection.rb:20:in `receive_data'
    from /usr/local/lib/ruby/gems/2.2.0/gems/eventmachine-1.0.4/lib/eventmachine.rb:187:in `run_machine'
    from /usr/local/lib/ruby/gems/2.2.0/gems/eventmachine-1.0.4/lib/eventmachine.rb:187:in `run'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/powerup.rb:13:in `run'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/powerup.rb:7:in `block in fork_and_start'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/powerup.rb:7:in `fork'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/power/powerup.rb:7:in `fork_and_start'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/process_manager.rb:72:in `run_power_server'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/commander.rb:39:in `start_manager'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/cli.rb:46:in `start'
    from /usr/local/lib/ruby/gems/2.2.0/gems/thor-0.19.1/lib/thor/command.rb:27:in `run'
    from /usr/local/lib/ruby/gems/2.2.0/gems/thor-0.19.1/lib/thor/invocation.rb:126:in `invoke_command'
    from /usr/local/lib/ruby/gems/2.2.0/gems/thor-0.19.1/lib/thor.rb:359:in `dispatch'
    from /usr/local/lib/ruby/gems/2.2.0/gems/thor-0.19.1/lib/thor/base.rb:440:in `start'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/lib/invoker/cli.rb:12:in `start'
    from /usr/local/lib/ruby/gems/2.2.0/gems/invoker-1.3.2/bin/invoker:7:in `<top (required)>'
    from /usr/local/bin/invoker:23:in `load'
    from /usr/local/bin/invoker:23:in `<main>'

@gnufied
Copy link
Contributor

gnufied commented Apr 17, 2015

Invoker can't work if Host header field is not present in the request. Here is from http/1.1 specs:

A client MUST include a Host header field in all HTTP/1.1 request messages . If the requested URI does not include an Internet host name for the service being requested, then the Host header field MUST be given with an empty value. An HTTP/1.1 proxy MUST ensure that any request message it forwards does contain an appropriate Host header field that identifies the service being requested by the proxy. All Internet-based HTTP/1.1 servers MUST respond with a 400 (Bad Request) status code to any HTTP/1.1 request message which lacks a Host header field.

http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

In other words, I think the node.js http client should be fixed to include Host header. Invoker works bit like Apache Virtual hosts and absence of Host field in headers would be greatly confusing to it.

@gnufied
Copy link
Contributor

gnufied commented Apr 17, 2015

On the flipside, I will update Invoker to return 400 Bad request if Host field is missing. Thanks for the bug report!

@ysimonson
Copy link
Author

Great, thanks! The HTTP spec does allow for Host headers with an empty string value. I think that's what the request library is doing.

If the Host header is not absolutely required to resolve the backend, it might make sense to handle empty string values without a 400 response. But it seems like it is.

@gnufied
Copy link
Contributor

gnufied commented Apr 17, 2015

Fixed by #131

@gnufied gnufied closed this as completed Apr 17, 2015
@gnufied
Copy link
Contributor

gnufied commented Apr 17, 2015

It would be hard to return non-400 response if Host header is empty from requests, because then Invoker can't decide which backend Invoker should proxy the request to. Theoretically, Invoker can be made to work via other ways, But as per HTTP specs it seems right thing to do is - always include a valid Host field in requests. The empty Host field is really a special case and kinda last resort. I am sure, node http client can be easily fixed to insert that header manually. :-)

@ysimonson
Copy link
Author

Yep, makes sense and that's what I'm doing for my use case. Just want to prevent others from getting caught up on this issue in the future. Thanks again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants