I'm having problems with my server in ruby I set the request as the client or session .gets so request = session.gets
but when the sent request is something like:
GET / HTTP/1.1\r\n
HOST: 192.168.2.4\r\n
User-Agent: Mozilla/5.0\r\n
Accept: text/xml,text/html,text/plain,image/jpeg\r\n
Accept-Language: en-us,en\r\n
Then request only returns the first line. If I do:
request1 = session.gets
request2 = session.gets
request3 = session.gets
request4 = session.gets
Then it'll work but if I put one too many session.gets then the client will not enter any more lines and will stop the server from running
Any ideas on what I could do to fix this?
Related
Final Edit: This works with no semantic errors:
+ Request
+ Headers
Accept: application/json
Content-Type: application/json
X-Auth-Client: Your Client Id
X-Auth-Token: Your Token
+ Body
+ Attributes (ProductPost)
+ Response 200
+ Headers
Content-Encoding: Entity header is used to compress the media-type.
Content-Type: application/json
Date: The date the response was sent.
Transfer-Encoding: Header specifies the form of encoding used to safely transfer the entity to the user.
Vary: HTTP response header determines how to match future request headers to decide whether a cached response can be used rather than requesting a fresh one from the origin server. We use Accept Encoding
X-Rate-Limit-Requests-Left: Header details how many remaining requests your client can make in the current window before being rate-limited.
X-Rate-Limit-Requests-Quota: Header shows how many API requests are allowed in the current window for your client
X-Rate-Limit-Time-Reset-Ms: Header shows how many milliseconds are remaining in the window.
X-Rate-Limit-Time-Window-Ms: Header shows the size of your current rate-limiting window
+ Body
+ Attributes (ProductResponse)
Edit: The header section is rendering, but now the Body section is just showing the text " + Attributes (ProductPost)"
+ Request
+ Headers
Accept: application/json
Content-Type: application/json
X-Auth-Client: Your Client Id
X-Auth-Token: Your Token
+ Response 200
+ Headers
Content-Encoding: Entity header is used to compress the media-type.
Content-Type: application/json
Date: The date the response was sent.
Transfer-Encoding: Header specifies the form of encoding used to safely transfer the entity to the user.
Vary: HTTP response header determines how to match future request headers to decide whether a cached response can be used rather than requesting a fresh one from the origin server. We use Accept Encoding
X-Rate-Limit-Requests-Left: Header details how many remaining requests your client can make in the current window before being rate-limited.
X-Rate-Limit-Requests-Quota: Header shows how many API requests are allowed in the current window for your client
X-Rate-Limit-Time-Reset-Ms: Header shows how many milliseconds are remaining in the window.
X-Rate-Limit-Time-Window-Ms: Header shows the size of your current rate-limiting window
+ Body
+ Attributes (ProductCollectionResponse)
I am trying to define the Request Body and after reading this:
https://help.apiary.io/api_101/apib-authentication/ &
https://github.com/apiaryio/api-blueprint/blob/master/API%20Blueprint%20Specification.md#def-headers-section
It seemed like I could split them into sections. But the Attributes section is not being recognized. This is a /GET request.
Any ideas why?
+ Request (application/json)
+ Headers
+ Attributes (RequestHeaders)
+ Body
+ Attributes (ProductPost)
Headers section can't contain Attributes, you need to define them explicitly. Just replace:
+ Attributes (RequestHeaders)
with definition of RequestHeaders.
Also try to align Body and Attributes at the same column:
+ Body
+ Attributes (ProductPost)
I'm trying to setup a webhook on Asana with the following:
token = <user_token>
uri = URI.parse("https://app.asana.com/api/1.0/webhooks")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{token}"
request.set_form_data(
"resource" => "219668070168571",
"target" => "https://myserveraddress/api/webhooks/asana/1234",
)
req_options = {
use_ssl: uri.scheme == "https",
}
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
puts response.body
puts response.code
And this is my controller handling the response:
response.set_header("X-Hook-Secret", request.headers["X-Hook-Secret"])
head 200
And when I do a curl request such as:
curl -i --header "X-Hook-Secret: 12356789" https://myserveraddress/api/webhooks/asana/1234
I get the following answer:
HTTP/1.1 200 OK
X-Hook-Secret: 12356789
Content-Type: text/html
Cache-Control: no-cache
X-Request-Id: fd8ec280-9ef1-426c-9cb5-58309f835ccf
X-Runtime: 0.045875
Vary: Origin
Transfer-Encoding: chunked
But when I try to setup the webhook I get this response from Asana:
{"errors":[{"message":"Could not complete activation handshake with target URL. Please ensure that the receiving server is accepting connections and supports SSL","help":"For more information on API status codes and how to handle them, read the docs on errors: https://asana.com/developers/documentation/getting-started/errors"}]}
What am I missing here?
Hey there #WagnerMatosUK,
Your response looks good to me, so that's probably not the problem. I have a suspicion of what's happening, though...
The ordering of the webhook handshake goes like this:
client server
|-- POST /api/1.0/webhooks --->|
|
|<--- POST {yourcallback} ---- |
| |
|--- 200 OK [with secret] ---> |
|
<--------- 200 OK -------------|
That is, the callback happens inside of the original request before the original request returns.
This is great for getting assurance that the webhook is established, because the return from the first request will be either success or failure depending on what happened with the handshake. However, this also means that your server needs to be able to respond to the incoming handshake request before the first request returns.
This has implications for single-threaded servers that block on the return of the first request - namely, they are blocked! They can't respond to the second request until that first request returns.
The solution is to kick off the first request in a thread, which frees up the second thread to occur, or to run multiple servers in parallel somehow (like Unicorn does) so that a server in process 2 can handle the handshake while process 1 is blocked.
Hopefully this makes sense and solves the issue! Cheers!
I want to get the contents of 'a.com/a.html' and 'a.com/b.html' with the same request
And my code is
uri = URI.parse("http://www.sample.com/sample1.html")
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Get.new(uri.request_uri)
# request.initialize_http_header({"User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36"})
result = http.request(request).body
should i change the path of the request?or any other idea?
You can't fetch multiple resources at once, but you can reuse a HTTP connection to fetch multiple resources from the same server (one after another):
require 'net/http'
Net::HTTP.start('a.com') do |http|
result_a = http.get('/a.html').body
result_b = http.get('/b.html').body
end
From the docs:
::start immediately creates a connection to an HTTP server which is kept open for the duration of the block. The connection will remain open for multiple requests in the block if the server indicates it supports persistent connections.
I've written a short snippet which sends a GET request, performs auth and checks if there is a 200 OK response (when auth success). Now, one thing I saw with this specific GET request, is that the response is always 200 irrespective of whether auth success or not.
The diff is in the HTTP response. That is when auth fails, the first response is 200 OK, just the same as when auth success, and after this then there is a second step. The page gets redirected again to the login page.
I am just trying to make a quick script which can check my login user and pass on my web application and tell me which auth passed and which didn't.
How should I check this? The sample code is like this:
def funcA(u, p)
print_A("#{ip} - '#{u}' : '#{p}' - Pass")
end
def try_login(u, p)
path = '/index.php?uuser=#{u}&ppass=#{p}'
r = send_request_raw({
'URI' => 'path',
'method' => 'GET'
})
if (r and r.code.to_i == 200)
check = true
end
if check == true
funcA(u, p)
else
out = "#{ip} - '#{u} - Fail"
print_B(out)
end
return check, r
end
end
Update:
I also tried adding a new check for matching a 'Success/Fail' keyword coming in HTTP response. It didn't work either. But I now noticed that the response coming back seems to be in a different form. The Content-Type in response is text/html;charset=utf-8 though. And I am not doing any parsing so it is failing.
Success Response is in form of:
{"param1":1,"param2"="Auth Success","menu":0,"userdesc":"My User","user":"uuser","pass":"ppass","check":"success"}
Fail response is in form of:
{"param1":-1,"param2"="Auth Fail","check":"fail"}
So now I need some pointers on how to parse this response.
Many Thanks.
I do this with with "net/http"
require 'net/http'
uri = URI(url)
connection = Net::HTTP.start(uri.host, uri.port)
#response = Net::HTTP.get_response(URI(url))
#httpStatusCode = #response.code
connection.finish
If there's a redirect from a 200 then it must be a javascript or meta redirect. So just look for that in the response body.
I'm trying to do some digest authorization to a server, then parse the resulting HTML with nokogiri. I'm using the net-http-digest_auth gem (https://github.com/drbrain/net-http-digest_auth) to do the url connection. All is fine up until I start the digest_auth code (line 20); it throws an 'unknown algorithm ""MD5"" error'..
The full error message from the console:
~/.rvm/gems/ruby-1.9.3-p194#rails32/gems/net-http-digest_auth-1.2.1/lib/net/http/digest_auth.rb:105:in 'auth_header': unknown algorithm ""MD5"" (Net::HTTP::DigestAuth::Error)
from ./server_connection.rb:20:in '<main>'
Line 20 is the auth line:
auth = digest_auth.auth_header uri, res['www-authenticate'], 'GET'
Here's my complete code (almost completely verbatim from the sample code used at the github link):
#!/usr/bin/env ruby
require 'uri'
require 'net/http'
require 'net/http/digest_auth'
digest_auth = Net::HTTP::DigestAuth.new
uri = URI.parse 'http://url/controlpage?name=_internal_variables_&asList=1&useJS=True'
uri.user = 'username'
uri.password = 'password'
h = Net::HTTP.new uri.host, uri.port
req = Net::HTTP::Get.new uri.request_uri
res = h.request req
# res is a 401 response with a WWW-Authenticate header
auth = digest_auth.auth_header uri, res['www-authenticate'], 'GET'
# create a new request with the Authorization header
req = Net::HTTP::Get.new uri.request_uri
req.add_field 'Authorization', auth
# re-issue request with Authorization
res = h.request req
if res.code == "200"
page = Nokogiri::HTML(res)
isDaylight = page.css('.controlTitle:contains("isDaylight") ~ .controlValue');
puts isDaylight.content
end
Updated this question to include the request headers via Chrome's dev tools:
GET /_getupdatedcontrols?name=_internal_variables_&asList=True&folderFilter=0&changeCount=479&serverState=idle HTTP/1.1
Host: url
Connection: keep-alive
Cache-Control: no-cache
Authorization: Digest username="username", realm="Indigo Control Server", nonce="71079e9f29f7210325ae451d0f423f07", uri="/_getupdatedcontrols?name=_internal_variables_&asList=True&folderFilter=0&changeCount=479&serverState=idle", algorithm=MD5, response="bc056cc472d35f7967973cb51c5b1a65", qop=auth, nc=00005649, cnonce="18dfcf3e4a7b809d"
X-Indigo-Web-Server-Version: 1
X-Prototype-Version: 1.6.0.3
X-Requested-With: XMLHttpRequest
Pragma: no-cache
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.17 Safari/536.11
Accept: text/javascript, text/html, application/xml, text/xml, */*
Referer: http://url/controlpage?name=_internal_variables_&asList=1&useJS=True
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
I ended up using the httpclient gem to accomplish the same thing.
The final code to do exactly what i was after:
#!/usr/bin/env ruby
require 'httpclient'
require 'nokogiri'
c = HTTPClient.new
c.debug_dev = STDOUT
c.set_auth("http://domain.com", "username", "password")
doc = Nokogiri::HTML(c.get_content("http://domain.com"))
isDaylight = "";
doc.css('.controlTitle:contains("isDaylight") ~ .controlValue').each do |var|
isDaylight = var.content
end
if (!isDaylight)
system("curl -X PUT --digest -u username:password -d isOn=1 http://domain.com")
else
system("curl -X PUT --digest -u username:password -d isOn=0 http://domain.com")
end
I hope this helps others that may be working with a home automation server and needing to easily do digest-based authentication.
Seth,
I ran into this same issue while working on a script in ruby. I am new to ruby but after a few google searches and some Charles Proxy showing me what was going on, I see that it is common for HTTP implementations to include quotes in the algorithm="MD5" portion of the Auth header, which is incorrect according to spec (it should be algorithm=MD5, with out quotes). Your updated header logs from Chrome devtools shows that your server response is honoring the spec, but the ruby library is NOT when it interprets that response string. This can be seen by
You server's 401 response included:
note the algorithm=MD5
Authorization: Digest username="username", realm="Indigo Control Server", nonce="71079e9f29f7210325ae451d0f423f07", uri="/_getupdatedcontrols?name=_internal_variables_&asList=True&folderFilter=0&changeCount=479&serverState=idle", algorithm=MD5, response="bc056cc472d35f7967973cb51c5b1a65", qop=auth, nc=00005649, cnonce="18dfcf3e4a7b809d"
But the console output of the initial request using this Ruby library shows:
note the algorithm=\"MD5\"
<- "GET /some/request HTTP/1.1\r\nAccept: */*\r\nUser-Agent: Ruby\r\nConnection: close\r\nHost: 10.1.0.15\r\n\r\n"
-> "HTTP/1.1 401 Unauthorized\r\n"
-> "Content-Length: 530\r\n"
-> "Server: SomeServer/5.0\r\n"
-> "Allow: GET, HEAD, POST, PUT\r\n"
-> "Date: Sun, 27 Jan 2013 00:29:23 GMT\r\n"
-> "Content-Type: text/html;charset=utf-8\r\n"
-> "Www-Authenticate: Digest realm=\"Some Realm\", nonce=\"5a8b8b46cfb84466431baf454eb9ddb9\", algorithm=\"MD5\", qop=\"auth\"\r\n"
For the script example in the original post, I would insert the following two lines:
www_auth_response = res['www-authenticate']
www_auth_response["algorithm=\"MD5\""] = "algorithm=MD5"
And Modify the third line:
auth = digest_auth.auth_header uri, www_auth_response, 'GET'
As follows:
...
res = h.request req
# res is a 401 response with a WWW-Authenticate header
www_auth_response = res['www-authenticate']
www_auth_response["algorithm=\"MD5\""] = "algorithm=MD5"
auth = digest_auth.auth_header uri, www_auth_response, 'GET'
# create a new request with the Authorization header
req = Net::HTTP::Get.new uri.request_uri
req.add_field 'Authorization', auth
...
The important thing that is going on here is that we are modifying the www-authenticate string that is coming back from your initial unauthorized 401 request (as interpreted by this ruby library). Sending the modified header string (www_auth_response) to the digest_auth.auth_header method produces no errors. At least that worked for me in my script!
I hope that helps!
Matt