I am trying to subscribe to a pubsubhubbub (pshb) feed on superfeedr. The way the pshb protocol works is
you Send POST to the hub requesting to subscribe to a feed and provide a callback
hub sends a GET to your callback to verifying your intent to subscribe
you respond saying yes I do want to subscribe
hub responds with subscription verification
I am running the server locally on my dev machine. I have code that can successfully subscribe to a feed, I test it by executing it in the rails console directly. I have now created a Feed ActiveRecord model and want automactially subscribe every time a new Feed record is created. I have added a ActiveRecord callback in the Feed model as such
after_create :subscribe_feed
Now when I create an active record I see the correct HTTP request go out, then there is a long hang (about 5 seconds where nothing happens in the logs), then the response from the pshb server comes in saying my callback could not be reached.
Here are the logs (numbers added by me for reference points)
(1) (0.1ms) begin transaction
(2) SQL (4.4ms) INSERT INTO "feeds" ("created_at", "updated_at", "url") VALUES (?, ?, ?) [["created_at", Fri, 15 Nov 2013 23:08:39 UTC +00:00], ["updated_at", Fri, 15 Nov 2013 23:08:39 UTC +00:00], ["url", "http://push-pub.appspot.com/feed"]]
(3) pshb subscribe parameters: {:headers=>{"Accept"=>"application/json"}, :body=>{"hub.mode"=>"subscribe", "hub.verify"=>"sync", "hub.callback"=>"http://...myserver.../pub_sub/callback", "hub.topic"=>"http://push-pub.appspot.com/feed", "hub.verify_token"=>"superfeedtest", "format"=>"json"}}
(4) # ABOUT 5 SECOND WAIT
(5) Subscribe Response: #<HTTParty::Response:0x7fd2180cd978 parsed_response="Your callback couldn't be reached.\n", #response=#<Net::HTTPUnprocessableEntity 422 Unprocessable Entity readbody=true>, #headers={"server"=>["nginx/0.8.52"], "date"=>["Fri, 15 Nov 2013 23:08:44 GMT"], "content-type"=>["text/plain; charset=utf-8"], "connection"=>["close"], "status"=>["422 Unprocessable Entity"], "x-runtime"=>["10057"], "content-length"=>["35"], "set-cookie"=>["_superfeedr_session=BAh7BzoMdXNlcl9pZGkC%2BIY6D3Nlc3Npb25faWQiJTBiMDExZGYzNzU4Mjk0MTMxNjc4NmE0OTg3MDhlMjJk--85d29756ae4b5ae9464630741372af7656f3894b; path=/; HttpOnly"], "cache-control"=>["no-cache"], "pubsubhubbub-version"=>["0.3"]}>
(6) (7.2ms) commit transaction
Redirected to http://67.180.177.165/feeds/28
Completed 302 Found in 10695ms (ActiveRecord: 11.6ms)
(7) Started GET "/pub_sub/callback?hub.challenge=437c4b3b47aa1dbf4072a2d8abb5c39a&hub.lease_seconds=315360000&hub.mode=subscribe&hub.topic=http%3A%2F%2Fpush-pub.appspot.com%2Ffeed&hub.verify_token=superfeedtest" for 173.255.193.75 at 2013-11-15 15:08:50 -0800
You can see that after the response the commit transaction happens (6), and then the GET from the phsb server asking to verify comes in (7). So the hub can reach my callback, but for some reason the I don't receive the GET from the hub until after it has timed out?
I am new to rails so not sure how things work, but I'm guessing something is going on with the concurrency of requests in the middle of ActiveRecord creation. The subscription works on its own, but not during an ActiveRecord creation as I have set it up in after_create.
What is the correct way to handle HTTP request to 3rd party servers if you want to do it when an ActiveRecord entry is created?
NOTE** in the superfeedr api you can specify the verification intent to happen asynchronously. When I specify it to be async I do get a success response instead, but the subscription still does not seem to be crated on superfeedr's hub. I will contact superfeedr directly about this issue, but in general I want to know how to handle this situation for API's that do not have an async option.
The problem is a well known issue with Rails in the "devlopment" environment which only allows for one single concurrent HTTP request. Since you're executing your after_create :subscribe_feed during one HTTP request, when Superfeedr tries to verify your intent, it will issue a second request to your input during the first one. The verification will hang until the subscription request is done... triggering some kind of deadlock.
The solution is simple: use the hub.verify=async param when subscribing and Superfeedr will first close the subscription request (with a 202 status) and will then issue the verification of intent.
You could also drop the hub.verify param, as it's async by default.
Related
I am setting up a chat feature using ActionCable in a Rails API / React project, and trying to figure out the best solution to authenticate connections.
The project uses the devise-jwt gem, so my Authorization is passed as a header. Since Web Sockets does not allow headers, I've been searching for the best alternative. Solutions I have found suggest one of the following (with various caveats):
sending the JWT as a query parameter in the url
hijacking the HTTP_SEC_WEBSOCKET_PROTOCOL & sending it that way
Neither of these solutions feel particular nice, but I've tried to go with the second because sending sensitive information in the url (even an encoded JWT) just feels wrong.
Here is the code in my connection.rb:
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
private
def find_verified_user
begin
jwt = request.env['HTTP_SEC_WEBSOCKET_PROTOCOL']
decoded_jwt = JWT.decode(jwt, Rails.application.credentials.devise_jwt_secret_key)[0]
id = decoded_jwt['sub']
case decoded_jwt['scp']
when 'admin'
Admin.where(id: id).first
when 'hero'
Hero.where(id: id).first
when'villain'
Villain.where(id: id).first
else
reject_unauthorized_connection
end
rescue
reject_unauthorized_connection
end
end
end
end
I have three different User models: Admin, (and let's call them) Hero & Villain, because reasons.
Now, my actual probelm: In the server I can get the JWT, decode it, and find my Admin, but in the chrome console when I try to create the socket I get: WebSocket connection to 'ws://localhost:3000/cable' failed: Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received
That is in response to running the following code in the chrome console:
token = "jwt.etc.blah..."
socket = new WebSocket(url, [token]);
The server itself seems alright with things:
Started GET "/cable" for ::1 at 2020-07-22 18:26:02 +0100
Started GET "/cable/" [WebSocket] for ::1 at 2020-07-22 18:26:02 +0100
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
Finished "/cable/" [WebSocket] for ::1 at 2020-07-22 18:26:02 +0100
Admin Load (0.2ms) SELECT "admins".* FROM "admins" WHERE "admins"."id" = $1 ORDER BY "admins"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
↳ app/channels/application_cable/connection.rb:19:in `find_verified_user'
Registered connection (Z2lkOi8vYXBpNTUtbWFpcy9BZG1pbi8x)
The error seems to be telling that I need some kind of confirmation or information about the extra protocol that I've hackily snuck my JWT into... but I've no idea how to go about doing this.
Any help would be greatly appreciated! Is it even possible to send additional protocols with rails ActionCable? Or am I going to have to resort to using a query param? Or is there an even neater way that I can tackle this problem?
First time I've tried anything with web sockets, so thanks in advance for help!
Passing credentials via Sec-WebSocket-Protocol is a common workaround for WebSocket authentication for web based applications since browsers do not support sending custom headers while connection initiation.
If you are passing a value via Sec-WebSocket-Protocol header, your server has to return the same header/value pair back to the client.
Since Sec-WebSocket-Protocol header is not intended for passing credentials to the WebSocket server, you have to play by its rules and treat it like a protocol and fulfill its requirements.
I have a rails API-only app running in nginx with passenger. I'm trying to use Action Cable to set up a simple chat application in Flutter but I can't seem to get it to work correctly.
I am able to connect using the following:
channel = IOWebSocketChannel.connect("wss://my.domain/cable")
and see the following in my production.log when that happens:
Started GET "/cable" for [redacted] at 2019-03-14 19:44:09 +0000
Started GET "/cable" [WebSocket] for [redacted] at 2019-03-14 19:44:09 +0000
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: upgrade, HTTP_UPGRADE: websocket)
Doorkeeper::AccessToken Load (0.3ms) SELECT `oauth_access_tokens`.* FROM `oauth_access_tokens` WHERE `oauth_access_tokens`.`token` = '[redacted]' LIMIT 1
User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
Registered connection (Z2lkOi8vYWdlbnQyNHg3L1VzZXIvMg)
So the connection seems to be made and successfully upgraded
I am then sending a subscribe command
channel.sink.add(json.encode({
"command": "subscribe",
"identifier": {"channel": "ConversationChannel", "conversation_id": "${conversation.id}"}
}));
I'm not sure if I should be seeing anything in the log files after this command is executed, but I don't. Nothing happens and there is no indication of any error in my production.log or my nginx error log. I set up some logging in the subscribe method of the ConversationChannel so it would at least output something to the production log when it is called, so I could test if it's at least even calling the subscribe command
class ConversationChannel < ApplicationCable::Channel
def subscribed
Rails.logger.info "test conversation subscribe"
reject && return unless current_user
stream_from "conversation_#{params[:conversation_id]}_channel"
end
...
end
but this does not appear in production.log so it would seem that the subscription command is not getting to where it is supposed to go.
I then tried using dart WebSocket using the code located here, which came from a conversation on github regarding connection states, to see if I could determine if the connection was coming back okay.
When I run that code with my domain I see the same successful connection in the production.log, but the connection actually times out with onerror TimeoutException after 0:00:15.000000: Future not completed which seems to indicate that the connection actually doesn't succeed after all, despite what it says in the log file.
What am I doing wrong here?
If I remember, when you're attempting to connect to a channel, your identifier has to be JSON stringified, as well as your whole JSON object. Take a look at this link:
https://thoughtbot.com/blog/talking-to-actioncable-without-rails
In particular:
const msg = {
command: 'subscribe',
identifier: JSON.stringify({
channel: 'SomeChannel',
}),
};
socket.send(JSON.stringify(msg));
Rails does the whole JSON parsing in the backend. See if this helps you out. I've had similar issues like this before, when attempting to connect the WebSockets, without the usage of their ActionCable npm library.
I'm trying to use the PDT system on PayPal to manage payments on my site. My site is correctly receiving the transaction id which is sent back to paypal in order to receive the transaction data.
This is the code which I am using to post the transaction id to paypal and receive a response.
response = Net::HTTP.post_form(URI.parse("#{Rails.application.secrets.paypal_host}/cgi-bin/webscr?"), values)
puts response.body
I am receiving the correct response where response.body shows this in the terminal.
SUCCESS
transaction_subject=SPORTS+PACKAGE+%281+Week%29
payment_date=23%3A32%3A01+Jul+20%2C+2017+PDT
txn_type=subscr_payment
subscr_id=I-WHFVMBY57NX2
last_name=Lucas
residence_country=US
item_name=SPORTS+PACKAGE+%281+Week%29
payment_gross=
mc_currency=AUD
business=merchant-success%40puntsquad.com
payment_type=instant
protection_eligibility=Ineligible
payer_status=verified
payer_email=buyer-success%40puntsquad.com
txn_id=8M5887849L359363Y
receiver_email=merchant-success%40puntsquad.com
first_name=George
payer_id=667TSNBH7R7X4
receiver_id=WJYE8WGSREP98
payment_status=Completed
payment_fee=
mc_fee=2.00
mc_gross=50.00
charset=windows-1252
My problem is I am unsure of how I am able to access these values.
You can convert the response body to a hash as follows
hsh = CGI::parse(response.body)
puts hsh['transaction_subject']
I have updated my Remine on servers from 2.3.0 to 2.4.1 and one (and only one of them) stops sending mail. If I was switched it back to 2.3.0 version, all works fine.
I am going to try to debug the code and just wondering which file in source code will be a good starting point to it ?
I have found notified_users(), recipients(), each_notification() in Issue model, but where is a code line which send email ?
Updated:
When I edit issue I get next message on console:
Rendered mailer/_issue.html.erb (2.2ms)
Rendered mailer/issue_edit.html.erb within layouts/mailer (4.8ms)
Sent email "[Redmine - ÐапÑÐ¾Ñ #13757] test" (26ms)
to: mymail#gmcs.ru
Date: Thu, 05 Dec 2013 17:34:07 +0400
....
cG9ydC5nbWNzLnJ1L215L2FjY291bnQ8L2E+PC9wPjwvc3Bhbj4KPC9ib2R5
Pgo8L2h0bWw+Cg==
----==_mimepart_52a080cfa4564_af93f8d53ef7714733eb--
Email delivery error: wrong argument (NilClass)! (Expected kind of OpenSSL::SSL::SSLContext)
(5.9ms) COMMIT
Redirected to http://vm-mecomstracker:90/issues/13757
Completed 302 Found in 574.3ms (ActiveRecord: 33.5ms)
I think you should look at app/models/mailer.
For example notified_users is used here
If update breaks sending emails I suggest to review config files, for example config/configuration.yml.example and check fresh issues on redmine.org
I have some ajax that is POSTing data to a URL, and I can see Chrome sending off the data :
Request URL:http://ubuntu:3000/groups?authenticity_token=EjAsrE07jziAMLt918Mgid4PSpRFfjIaz%2Bd9ZCxmbTo%3D
Request Payload
{"object":{"name":"test","description":"testing","is_era":false,"unique_ui_identifier":"db3772f13e8c5ec958105c72f11b7b89"}}
I then look at the dev logs, and the server shows the following:
Started POST "/groups?authenticity_token=EjAsrE07jziAMLt918Mgid4PSpRFfjIaz%2Bd9ZCxmbTo%3D" for 192.168.222.1 at Mon Aug 26 10:46:19 +0930 2013
Processing by GroupsController#create as */*
Parameters: {"group"=>{}, "authenticity_token"=>"EjAsrE07jziAMLt918Mgid4PSpRFfjIaz+d9ZCxmbTo="}
So, the server doesn't seem to be receiving the POST data. i.e. the 'object' with name/description/etc.
A few things I find strange:
It seems to be receiving an empty 'group' hash, but I have no idea where this would be coming from.
Is this strange: 'Processing by GroupsController#create as /'...what is the / ?
If anyone can help me, I would be very appreciative.