Categories
REST

Decompressing the TRESTResponse

RAD Studio, Delphi and C++ 10 Seattle has a fantastic REST Client Library. If you are unfamiliar with it, check out my 5 Minute REST Client video.

The TRESTRequest component has an AcceptEncoding property where you can specify the type of compression you would like applied to the response. The TRESTResponse component has a ContentEncoding property that specifies the the encoding that the server chose. The most common compression encoding is gzip, followed by deflate. The others are rarely used (although brotli is a new compression that looks promising for the future.)

By default it is not encrypted if you leave AcceptEncoding blank. However when you use the Stack Overflow API it always compresses with gzip if nothing is specified.

The TRESTResponse has two ways to access the response data directly, the RawBytes property and the Content property. Both are read-only. So we can read the compressed response data, but cannot update the TRESTResponse with the decompressed data. Updating the TRESTReponse allows it to work with the TRESTResponseDataSetAdapter, which is a fantastic feature.

Luckily we can use the protected mode workaround to update the value. Here is a unit that you can include in your project to easily decompress theĀ TRESTResponse and continue to use it with the Data Set Adapter.

unit RestDecompress;

interface

uses
  System.SysUtils, System.Types, System.Classes, IPPeerClient, REST.Client;

procedure DecodeRestResponse(ARestResponse: TRESTResponse);

implementation

uses
  System.Zlib,
  IdBaseComponent, IdException, IdZLibCompressorBase, IdCompressorZLib;

  type // protected mode work around
  TProtectedRESTResponse = class(TRESTResponse)
  end;


procedure DecodeRestResponse(ARestResponse: TRESTResponse);
var
  LCompressed: TMemoryStream;
  LDecompressed: TStringStream;
  LDecompress: TIdCompressorZLib;
begin
  if Length(ARestResponse.ContentEncoding) = 0 then exit;

  LCompressed := nil;
  LDecompressed := nil;
  LDecompress := nil;
  try
    LCompressed := TMemoryStream.Create;
    LDecompressed := TStringStream.Create;

    LCompressed.WriteData(ARESTResponse.RawBytes, Length(ARESTResponse.RawBytes));
    LCompressed.Position := 0;

    // Use the Indy decompression libraries because the HTTP stream doesn't
    //   have the proper headers that System.ZLib looks for.

    LDecompress :=TIdCompressorZLib.Create();

    if ARestResponse.ContentEncoding = 'gzip' then
      LDecompress.DecompressGZipStream(LCompressed, LDecompressed)
    else if ARestResponse.ContentEncoding = 'deflate' then
    begin
      // Due to variations in the deflate server side implementations,
      //   this rarely works, but is here for completeness and just in case
      LDecompress.DecompressHTTPDeflate(LCompressed, LDecompressed);
    end;

    TProtectedRESTResponse(ARESTResponse).SetContent(LDecompressed.DataString);
  finally
    LDecompressed.Free;
    LCompressed.Free;
    LDecompress.Free;
  end;
end;

end.

Simply use this unit, and then add a call to DecodeRestResponse(RESTResponse) to RESTResponse’s OnAfterExecute event handler. It checks the ContentEncoding and then uses the correct decompression and updates the content.

You’ll notice it uses the Indy TIdCompressorZLib component instead of the new System.ZLib library. The reason is a GZip encoded HTTP response doesn’t include the full headers expected by the ZLib library. There is a way to work around this, but no need to do that since the Indy library works fine.

Categories
REST Tools

REST and the Kimono

I love the new TRESTClient components in XE5. Especially the ability to visual bind a REST datasource with the use of the TRESTResponseDataSetAdapter. Now I find I’m always on the look out for new REST datasources. The REST Debugger makes the whole process really easy too.

The other day I hit the mother-load of REST datasources with Kimono Labs. It is a creative web service that makes it easy to scrape a web site and turn it into a REST data source. It looks for repeating data on the page. Their free service is enough to get you started. I created a simple REST datasource of San Francisco 49ers games from their schedule on their web site.

kimonolabs.comA few tips for working with the Kimonolabs REST API.

  • Make sure the web data doesn’t include hyperlinks – if it does, then the REST data will include objects containing the href and the text, which doesn’t map to a grid well.
  • UseĀ results.collection1 as your root element in the TRESTResponseDataSetAdapter and you are off to the races.
  • It doesn’t work with every web page because of malformed pages, but it works with a lot of them.
  • There is no way to edit your API once you’ve finished it (yet), so you end up deleting and recreating it a few times.

They have a lot of videos and tutorials on their site walking you through how to use their service. Take a look and I’m looking forward to your REST enabled apps!