I have multiple bruises today, trying to learn two things at once... the API for Postmark and Rails HTTP requests.
Goal: Use Postmark add-on for Heroku to send production email.
I am trying to combine this article on HTTP requests... http://docs.ruby-lang.org/en/2.0.0/Net/HTTP.html ... with this API reference for Postmark... http://developer.postmarkapp.com/developer-send-api.html
Unfortunately, the examples from Postmark are done in curl and I have not succeeded in translating them into a HTTP request. I suspect the problem centers around the headers -- the parts of the transmission other than the body.
The rescue clause seen in the code below traps the error 'connection reset by peer'. At this point I don't know if I am even close to the right format for the headers that provide Postmark authentication.
I have the proper server token (in the config entry) and the From email has been given the required Postmark signature.
def send_production_email(email_address, subject, email_body)
# Use API to interact with Heroku add-on Postmark
# http://developer.postmarkapp.com/developer-send-api.html
uri = URI('https://api.postmarkapp.com/email')
# Form the request
req = Net::HTTP::Post.new(uri)
# Set request headers -- SUSPECT THIS IS WRONG
req['Accept'] = 'application/json'
req['Content-Type'] = 'application/json'
req['X-Postmark-Server-Token'] = Rails.application.config.postmark_token
rbody ={
'From' => 'Support <michael@mydomain.com>',
'To' => email_address,
'Subject' => subject,
'HtmlBody' => wrap_html(email_body),
'TextBody' => email_body
}.to_json
req.body = rbody
# Send the request, waiting for the response
begin
response = Net::HTTP.new(uri.host, uri.port).start {|http| http.request(req) }
rescue Exception => e
logthis("http request error: #{e.message}")
return
end
# ...parsing section omitted since I do not get that far...
end
A second attempt was formatted this way, but results in the same peer reset error:
rbody ={
'From' => 'Support <michael@disambiguator.com>', # TODO: replace email when domain is live
'To' => email_address,
'Subject' => subject,
'HtmlBody' => wrap_html(email_body),
'TextBody' => email_body
}.to_json
uri = URI('https://api.postmarkapp.com/email')
http = Net::HTTP.new(uri.host, uri.port)
# http.use_ssl = true
request = Net::HTTP::Post.new(uri.path, {'Content-Type' => 'application/json', 'Accept' => 'application/json', 'X-Postmark-Server-Token' => Rails.application.config.postmark_token})
request.body = rbody
# Send the request, waiting for the response
begin
response = http.request(request)
rescue Exception => e
logthis("http request error: #{e.message}")
return
end
I am grateful for any guidance!
I’m a Wildbit’s employee and the maintainer of the official Postmark Ruby gem.
The "connection reset by peer" error is the result of you trying to send an unencrypted HTTP request to an endpoint expecting secure communication via HTTPS. So, if you change this line:
Net::HTTP.new(uri.host, uri.port).start {|http| http.request(req) }
to:
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
response = http.start { |http| http.request(req) }
then you should be able to receive a response from the API. I see that you have this line in the second example, but it is commented. Since you’re doing this as an exercise, I’d like to add that when using net/http
you don’t usually have to work with the underlying classes like Net::HTTP::Post
. It’s generally simpler to use the higher level API provided by instances of the Net::HTTP
class. Here is an example of how your method could be simplified by using it:
def send_production_email(email_address, subject, email_body)
uri = URI('https://api.postmarkapp.com/email')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
headers = {'Accept' => 'application/json',
'Content-Type' => 'application/json',
'X-Postmark-Server-Token' => Rails.application.config.postmark_token}
payload = {'From' => 'tema@wildbit.com',
'To' => email_address,
'Subject' => subject,
'HtmlBody' => email_body,
'TextBody' => email_body}
http.post(uri.request_uri, payload.to_json, headers)
rescue => e
puts "http request error: #{e.message}"
end
And, if you’re interested in how net/http
is used in the official Postmark Ruby gem, check out the HttpClient class’ source.