Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compress Outgoing Requests in Angular 2+

I want to use gzip or deflate compression on outgoing POST and PUT JSON requests to an API project from an Angular 4 application.

Presently, I'm using HttpClient to send the requests. I've tried using pako or zlib to generate the compressed content, but the server returns back responses indicating a bad implementation of the compression algorithm.

My POST TypeScript looks like the following:

public post(url: string, content: any): Observable < any > {
  const fullUrl: string = `${HttpService.baseUrl}/${url}`;

  Logger.debug(`Beginning HttpPost invoke to ${fullUrl}`, content);

  // Optionally, deflate the input
  const toSend: any = HttpService.compressInputIfNeeded(content);

  return Observable.create((obs: Observer < any > ) => {
    this.client.post(fullUrl, toSend, HttpService.getClientOptions()).subscribe(
      (r: any) => {
        Logger.debug(`HttpPost operation to ${fullUrl} completed`, r);

        // Send the response along to the invoker
        obs.next(r);
        obs.complete();
      },
      (err: any) => {
        Logger.error(`Error on HttpPost invoke to ${fullUrl}`, err);

        // Pass the error along to the client observer
        obs.error(err);
      }
    );
  });
}

private static getClientOptions(): {
  headers: HttpHeaders
} {
  return {
    headers: HttpService.getContentHeaders()
  };
}

private static getContentHeaders(): HttpHeaders {
  let headers: HttpHeaders = new HttpHeaders({
    'Content-Type': 'application/json; charset=utf-8'
  });

  // Headers are immutable, so any set operation needs to set our reference
  if (HttpService.deflate) {
    headers = headers.set('Content-Encoding', 'deflate');
  }
  if (HttpService.gzip) {
    headers = headers.set('Content-Encoding', 'gzip');
  }

  return headers;
}

private static compressInputIfNeeded(content: any): string {
  const json: string = JSON.stringify(content);

  Logger.debug('Pako Content', pako);

  if (HttpService.deflate) {
    const deflated: string = pako.deflate(json);
    Logger.debug(`Deflated content`, deflated);

    return deflated;
  }

  if (HttpService.gzip) {
    const zipped: string = pako.gzip(json);
    Logger.debug(`Zipped content`, zipped);

    return zipped;
  }

  return json;
}

I've tried various permutations of deflating and gzipping the content, but nothing seems to work. I've also inspected the outgoing requests in Fiddler and verified that Fiddler could not interpret the request JSON.

I've also verified that content is being sent with Content-Type: application/json; charset=UTF-8 and Content-Encoding: deflate with appropriate Accept-Encoding values.

At this point I'm sure I'm either doing something wrong I haven't figured out, or that I'm trying to do more than what HttpClient will allow me to do.

like image 731
Matt Eland Avatar asked Aug 21 '18 05:08

Matt Eland


1 Answers

I've just gotten this working myself.

I think the problem may be the way you are using pako.

pako.gzip(obj) does not return a string, unless you explicitly pass that option. It returns a byte array. (Uint8Array, specifically)

The default HttpClient will try to turn this into a json string, which is wrong. I did the following:

  const newHeaders: Headers = new Headers();
  newHeaders.append('Content-Encoding', 'gzip')
  newHeaders.set('Content-Type', 'application/octet-stream');

  var options = { headers: newHeaders, responseType: ResponseContentType.Json };

  var compressedBody = pako.gzip(JSON.stringify(body))

  client.post(url, compressedBody.buffer, options);

Note a few things:

  1. The Content-Type and Content-Encoding headers need to be set correctly for a zipped byte array.
  2. the use of the .buffer property on the compressedBody object. HttpClient needs it this way so that it knows it is dealing with a byte array.
  3. Your API will need to be smart enough to convert the byte array to json on the other end. This usually isn't handled by default.
like image 144
SouthShoreAK Avatar answered Sep 23 '22 08:09

SouthShoreAK