Search code examples
delphi-7indy

TidHTTP error "A port is needed" when trying to Connect


I am attempting to connect to a secure server using a TIdHTTP component. I have assigned a TIdSSLIOHandlerSocketOpenSSL I/O Handler to the IOHandler property, and I have downloaded the OpenSSL files.

When I invoke Connect(), it generates an "A port is required" exception. Another post suggests that Host and Port should be assigned automatically for a TIdHTTP. Any suggestions as to what I am doing wrong?

If I don't invoke Connect(), the site responds with an error 406, because the connection is not secure.

Relevant bits of code are:

try
  IdHttp1.Connect ('https://test-api.service.hmrc.gov.uk');
  IdHttp1.Request.CustomHeaders.AddValue ('Accept', 'application/vnd.hmrc.1.0+json');

  result := IdHTTP1.Get('https://test-api.service.hmrc.gov.uk/hello/world');
except
  on E: Exception do
    memo1.lines.add (E.ClassName + ': ' + E.message);
end;

Solution

  • The "Port is required" error is because the Port property has not been assigned a value yet when Connect() is called.

    But, the thing is, unlike with other TCP client components, you are NOT supposed to call Connect() directly at all when using TIdHTTP. Its Get() method (and other request methods) will handle that internally for you. So, you need to remove the Connect() call from your code.

    HTTP is a stateless protocol. The underlying TCP connection may be persistent, or it may be disconnected, between multiple requests to the same server. Client and server negotiate that on a per-request basis. TIdHTTP handles that detail internally for you, by parsing URLs for Host and Port information (as I explained in the other post you mentioned) and disconnecting/reconnecting the TCP connection as needed.

    All you have to do is provide a valid URL to Get() (and others) and let TIdHTTP manage the TCP connection for you.

    On a separate note, you should not be using the Request.CustomHeaders property to set the Accept request header. Use the Request.Accept property instead.

    Try this:

    IdHttp1.Request.Accept := 'application/vnd.hmrc.1.0+json';
    try
      Result := IdHTTP1.Get('https://test-api.service.hmrc.gov.uk/hello/world');
    except
      on E: Exception do
        Memo1.Lines.Add(E.ClassName + ': ' + E.message);
    end;
    

    As for response code 406, the fact that you get any response at all means you have a valid HTTP connection to the server. But 406 DOES NOT mean the connection is unsecure, as you claim (in fact, it can't be, since you are requesting an HTTPS url). 406 actually means the server could not deliver its response data to you in a format that you explicitly requested as being acceptable. In this case, the response could not be given in the JSON format that you want.

    So, you need to figure out why the server is not sending JSON and fix that, or you need to ammend your Accept request header to include the format that the server wants to use instead.

    My guess is the server is probably trying to send back an error message in a non-JSON format (maybe in HTML instead), so try removing your Accept request header temporarily so you can look at the actual response, and then update your request code as needed.