Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add GZip and Deflate support to the SocketProcessor.

This might be a little buggy - I had one problem with deflate that I
couldn't really explain, so be on the lookout.
  • Loading branch information...
commit 5e20c9f3b7c171e76b7a08ed6e6b3de25101671d 1 parent 3687139
@chrismeller chrismeller authored
Showing with 91 additions and 0 deletions.
  1. +91 −0 classes/socketrequestprocessor.php
View
91 classes/socketrequestprocessor.php
@@ -9,10 +9,19 @@ public function __construct ( ) {
$this->can_followlocation = false;
}
+ if ( !defined( 'FILE_CACHE_LOCATION' ) ) {
+ define( 'FILE_CACHE_LOCATION', HABARI_PATH . '/user/cache/' );
+ }
+
}
public function execute ( $method, $url, $headers, $body, $config ) {
+ // before we handle headers, see if we should add our compression headers
+ if ( $this->can_zlib() ) {
+ $headers['Accept-Encoding'] = 'gzip,deflate';
+ }
+
$merged_headers = array();
foreach ( $headers as $k => $v ) {
$merged_headers[] = $k . ': '. $v;
@@ -98,6 +107,18 @@ public function execute ( $method, $url, $headers, $body, $config ) {
}
+ // check to see if the response was compressed
+ if ( isset( $headers['Content-Encoding'] ) ) {
+ $encoding = trim( $headers['Content-Encoding'] );
+
+ if ( $encoding == 'gzip' ) {
+ $body = $this->gzdecode( $body );
+ }
+ else if ( $encoding == 'deflate' ) {
+ $body = gzinflate( $body );

I think this must be gzuncompress() rather than gzinflate(). The RFC's deflate does not map properly to the zlib function names.

@chrismeller Owner

The pairs of ZLIB functions should be:

gzuncompress - Uncompresses a string compressed with gzcompress
gzcompress - Compresses a string using the ZLIB format

gzdecode - Decodes a string encoded with gzencode
gzencode - Encodes a string using the GZIP format

gzinflate - Inflates a string compressed with gzdeflate
gzdeflate - Compresses a string using the DEFLATE format

So it should actually be gzinflate, if the PHP docs are to be believed, gzuncompress would be for ZLIB compression, which we don't handle.

I'm afraid you're mistaken: The name of the gz* functions has little to do with the name of HTTP's content encodings. As far as I can tell, the mapping is as follows:

HTTP Content Encoding compress in PHP expand in PHP
gzip / x-gzip gzencode() gzdecode()
bzip2 / x-bzip2 bzcompress() bzdecompress()
deflate gzcompress() gzuncompress()
compress n/a n/a

compress is actually the Lempel-Ziv-Welch algorithm which is (to my knowledge) not supported by zlib's bindings in PHP.

@chrismeller Owner

I wasn't going by the names, I was going by the PHP documentation's stated implementations:

And then of course the uncompress / inflate / decode functions all specify which function they expect input from.

In my testing everything seemed to work fine (I don't remember what the deflate issue I mentioned in the commit message was, though this would explain one). What makes you think these are incorrect? Do you have any example test cases you could put into a new issue for me to look at?

Hm, I need to consult the RFCs again. At least the deflate one isn't right. I'm currently working on my own http client library. Using gzinflate() for deflate encoded content resulted in nothing but errors for me.

Ok, so RFC 2616, scetion 3.5 brings some light into this:

  • compress is indeed Lempel-Ziv-Welch
  • gzip / x-gzip maps to RFC 1952 which is implemented though gzencode() / gzdecode()
  • deflate maps to RFC 1950 and 1951, which is gzcompress() / gzuncompress()
@chrismeller Owner

I've created an Issue 365 for this. I've got some test cases I want to post and they're a bit bulky for line notes, so let's jump over there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ }
+ }
+
$this->response_headers = $headers;
$this->response_body = $body;
$this->executed = true;
@@ -106,6 +127,76 @@ public function execute ( $method, $url, $headers, $body, $config ) {
}
+ private function can_zlib ( ) {
+
+ // make sure that the zlib extension is loaded
+ if ( !extension_loaded( 'zlib' ) ) {
+ return false;
+ }
+
+ // since we have to write to a temp file to de-gzip, make sure we can do that
+ $tmp = tempnam( FILE_CACHE_LOCATION, 'RRS' ); // RRS for RemoteRequestSocket, get it?
+
+ // if creating the temp file failed, that's it
+ if ( !$tmp ) {
+ return false;
+ }
+
+ $fh = @fopen( $tmp, 'w+b' );
+
+ // if actually opening the file for writing failed
+ if ( !$fh ) {
+ return false;
+ }
+
+ fclose( $fh );
+
+ // now we should be good to go, let's clean up after ourselves
+ if ( file_exists( $tmp ) ) {
+ unlink( $tmp );
+ }
+
+ // and we're good
+ return true;
+
+ }
+
+ private function gzdecode ( $body ) {
+
+ // create the temp file to write to
+ $tmp = tempnam( FILE_CACHE_LOCATION, 'RRS' );
+
+ if ( !$tmp ) {
+ throw new Exception( _t( 'Socket Error. Unable to create temporary file name.' ) );
+ }
+
+ $result = file_put_contents( $tmp, $body );
+
+ if ( $result === false ) {
+ throw new Exception( _t( 'Socket Error. Unable to write to temporary file.' ) );
+ }
+
+ // before we read it back in, try to free up as much memory as possible
+ unset( $body );
+
+ $zp = gzopen( $tmp, 'rb' );
+
+ $body = '';
+ while ( !gzeof( $zp ) ) {
+ $body .= gzread( $zp, 1024 );
+ }
+
+ gzclose( $zp );
+
+ // clean up the temp file
+ if ( file_exists( $tmp ) ) {
+ unlink( $tmp );
+ }
+
+ return $body;
+
+ }
+
}
?>
Please sign in to comment.
Something went wrong with that request. Please try again.