ruby-on-railshttpherokupostmark

Rails email using Postmark API on Heroku -- connection reset by peer


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!


Solution

  • 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.