Failing SuperHTTP request because of missing ContentType

Hello,

We’ve run into an issue in our Delphi FMX app. We use TRONetSuperHTTPChannel to connect to a Remoting SDK SuperHTTP server endpoint as part of our login process.

On Android, iOS and macOS the ShHello request fails for one of our endpoints: we get an HTML “Request Rejected” response from a Web Application Firewall. On Windows the same request succeeds.

The issue seems to be that TRONetSuperHTTPChannel.DispatchHTTPRequest never sets a Content-Type on the post. On Android/iOS/macOS the platform HTTP stack then defaults to application/x-www-form-urlencoded, which the WAF rejects for the binary SuperHTTP body. Windows (WinHTTP) sends no Content-Type in that case, and the WAF allows that to go through.

As temporary workaround I’ve subclassed TRONetSuperHTTPChannel and have my own version of DispatchHTTPRequest and it works but would be nice if this could be fixed.

Thank you in advance!

Hi,

Will WAF accepts application/octet-stream value?

if yes, you can use

procedure TROBaseSuperHTTPChannel.DispatchHTTPRequest(aWaitingThread: Boolean; aRequest: TStream; out aResponse: TStream);
begin
  SetHeaders(aWaitingThread, id_ContentType, id_ContentType_application_octetstream); // <<< added

Thank you for your response. So application/octet-streamis accepted, but the SetHeaders call doesnt work. What worked for me is to set it like this:
l_request.SetHeaderValue(id_ContentType, id_ContentType_application_octetstream);

But that is actually also not the only thing I see now.

There’s also this

procedure TROBaseSuperHTTPChannel.DispatchHTTPRequest(aWaitingThread: Boolean; aRequest: TStream; out aResponse: TStream);
begin
  {$IFDEF LOG}
  if not aWaitingThread then
    Log('===== DispatchHTTPRequest (' + TargetUrl + ')')
  else
    Log('===== DispatchHTTPRequest[WAITING] (' + TargetUrl + ')');
  {$ENDIF}
  if fAllowGzipEncoding and not aWaitingThread then begin
    if (aemGZIP in fServerAcceptEncodingModes) and
      fCanUseContentEncoding and not IsEncryptionUsed and
      (aRequest.Size >= fMinSizeForGzipEncoding) then begin
      DoEncodeStream(aRequest);
      SetHeaders(aWaitingThread, id_ContentEncoding, id_gzip);
    end
    else begin
      SetHeaders(aWaitingThread, id_ContentEncoding, '');
    end;
  end
  else begin
    SetHeaders(aWaitingThread, id_ContentEncoding, '');
  end;
end;

The 2 SetHeaders(aWaitingThread, id_ContentEncoding, ''); calls are also contributing to the issue. In my subclass I’m skipping those. So skipping these 2 + the l_request.SetHeaderValue thing from before made it work for me.

Hi,

hmm, I don’t see any special processing at:

procedure TNetHTTPRequest.SetCustomHeaderValue(const AName, Value: string);
begin
  FCustomHeaders.Value[AName] := Value;
end;

so

SetHeaders(aWaitingThread, id_ContentType, id_ContentType_application_octetstream);

and

SetHeaders(aWaitingThread, id_ContentEncoding, '');

should work as expected

unless they wrote empty value as headername=

unless they wrote empty value as headername=

Yeah this seems to be the case for the encoding issue:
SetHeaders(aWaitingThread, id_ContentEncoding, '');

As for the content type I dont understand why, but SetHeaders(aWaitingThread, id_ContentType, id_ContentType_application_octetstream); does not work. I used Fiddler to verify, content type doesnt show up in the request headers if we use the SetHeaders call but does with l_request.SetHeaderValue.

Hi,

try this case:

procedure TROBaseSuperHTTPChannel.DispatchHTTPRequest(aWaitingThread: Boolean; aRequest: TStream; out aResponse: TStream);
begin
  SetHeaders(aWaitingThread, id_ContentType, id_ContentType_application_octetstream); // <<< added
procedure TRONetSuperHTTPChannel.SetHeaders(aWaitingThread: Boolean; const aName, aValue: string);
var
  l_client: THTTPClient;
begin
  if aWaitingThread then
    l_client := ClientWait
  else
    l_client := ClientRequest;

  if l_client.CustomHeaders[aName] <> aValue then
    l_client.CustomHeaders[aName] := aValue;
end;

as for me, it works as expected:

My bad about the content type part, that is working now. I must have been calling in the wrong place before.

The Content-Encoding part with the empty strings is still an issue on Android though.

Hi,

if value isn’t set, it shouldn’t be added.
at least on macos it works as expected:

  if l_client.CustomHeaders[aName] <> aValue then
    l_client.CustomHeaders[aName] := aValue;

Nevermind, you’re right. Thank you very much for your support!