delphiindyidhttp

"=\r\n" inserted in "access_token" field value when using TIdHTTP to upload a file using TIdMultipartFormDataStream


I'm trying to upload a file using TIdHTTP. The problem is the access token gets changed when the request is sent to the server.

The access token that I'm using is fJNhDM6TlcpeVmD8h3jFuPJS71sxwZB8bZBXajTRB5TNAcRa6PNXfv4J7mPxIvMdMhjy7oKdTLbsRYthpBCCqGVkj4vlojJ4BRBkLAVIBJ1DZAnMZD

The API returns

HTTP/1.1 400 Bad Request OAuth "invalid_token" "Malformed access token fJNhDM6TlcpeVmD8h3jFu=\r\nPJS71sxwZB8bZBXajTRB5TNAcRa6PNXfv4J7mPxIvMdMhjy7oKdTLbsRYthpBCCqGVkj4v=\r\nlojJ4BRBkLAVIBJ1DZAnMZD"

There is =\r\n added to my token twice.

My code is:

function TFoo.Post(const AToken, guID, AMessage, AImageFileName: string): Boolean;
var
  lParam : TIdMultipartFormDataStream;
begin
  Result := False;
  if not FileExists(AImageFileName) then begin
    LastError := 'File not found ' + AImageFileName;
    Exit;
  end;
  ProxyCheck;
  lParam := TIdMultipartFormDataStream.Create;
  try
    lParam.AddFormField('message', AMessage);
    lParam.AddFormField('access_token', AToken);
    lParam.AddFile('source', AImageFileName);
    idHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
    try
      idHTTP.Post( UrlAPI + guID + '/photos', lParam);
      Result := True;
    except;
      LastError := idHTTP.ResponseText + sLineBreak + idHTTP.Response.WWWAuthenticate.Text;
    end;
  finally
    lParam.Free;
  end;
end;   �

What am I missing here ?


Solution

  • By default, AddFormField() sets the TIdFormDataField.ContentTransfer property to MIME's quoted-printable format. That is where the extra =\r\n is coming from. It is a "soft" line break being inserted by quoted-printable every 76 characters. Any server that supports quoted-printable should remove "soft" line breaks during decoding. But maybe your server does not.

    If you want to disable the quoted-printable behavior, you can set the ContentTransfer property to either:

    1. a blank string:

       lParam.AddFormField('access_token', AToken).ContentTransfer := '';
      
    2. '7bit' (since it does not contain any non-ASCII characters):

       lParam.AddFormField('access_token', AToken).ContentTransfer := '7bit';
      
    3. '8bit' or binary:

       lParam.AddFormField('access_token', AToken).ContentTransfer := '8bit';
      
       lParam.AddFormField('access_token', AToken).ContentTransfer := 'binary';
      

    In this case, I would suggest #1 (which will treat the transfer encoding as 7bit but will not send a Content-Transfer-Encoding header).


    On a side note, do not set the HTTP ContentType when posting a TIdMultipartFormDataStream. Not only are you using the wrong media type to begin with (it should be multipart/form-data instead), but the TIdMultipartFormDataStream version of TIdHTTP.Post() will simply overwrite it anyway.

    function TFoo.Post(const AToken, guID, AMessage, AImageFileName: string): Boolean;
    var
      lParam : TIdMultipartFormDataStream;
    begin
      Result := False;
      if not FileExists(AImageFileName) then begin
        LastError := 'File not found ' + AImageFileName;
        Exit;
      end;
      ProxyCheck;
      lParam := TIdMultipartFormDataStream.Create;
      try
        lParam.AddFormField('message', AMessage);
        lParam.AddFormField('access_token', AToken).ContentTransfer := '';
        lParam.AddFile('source', AImageFileName);
        try
          idHTTP.Post(UrlAPI + guID + '/photos', lParam);
          Result := True;
        except;
          LastError := idHTTP.ResponseText + sLineBreak + idHTTP.Response.WWWAuthenticate.Text;
        end;
      finally
        lParam.Free;
      end;
    end;