2 +--------------------------------------------------------------------+
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2005, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
19 #define HTTP_WANT_ZLIB
22 #include "php_http_api.h"
23 #include "php_http_encoding_api.h"
24 #include "php_http_send_api.h"
25 #include "php_http_headers_api.h"
27 ZEND_EXTERN_MODULE_GLOBALS(http
);
29 static inline int eol_match(char **line
, int *eol_len
)
33 while (0x20 == *ptr
) ++ptr
;
35 if (ptr
== http_locate_eol(*line
, eol_len
)) {
43 /* {{{ char *http_encoding_dechunk(char *, size_t, char **, size_t *) */
44 PHP_HTTP_API
const char *_http_encoding_dechunk(const char *encoded
, size_t encoded_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
48 const char *e_ptr
= encoded
;
51 *decoded
= ecalloc(1, encoded_len
);
53 while ((encoded
+ encoded_len
- e_ptr
) > 0) {
54 ulong chunk_len
= 0, rest
;
56 chunk_len
= strtoul(e_ptr
, &n_ptr
, 16);
58 /* we could not read in chunk size */
61 * if this is the first turn and there doesn't seem to be a chunk
62 * size at the begining of the body, do not fail on apparently
63 * not encoded data and return a copy
65 if (e_ptr
== encoded
) {
66 http_error(HE_NOTICE
, HTTP_E_ENCODING
, "Data does not seem to be chunked encoded");
67 memcpy(*decoded
, encoded
, encoded_len
);
68 *decoded_len
= encoded_len
;
69 return encoded
+ encoded_len
;
72 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Expected chunk size at pos %tu of %zu but got trash", n_ptr
- encoded
, encoded_len
);
79 /* move over '0' chunked encoding terminator */
80 while (*e_ptr
== '0') ++e_ptr
;
84 /* there should be CRLF after the chunk size, but we'll ignore SP+ too */
85 if (*n_ptr
&& !eol_match(&n_ptr
, &eol_len
)) {
87 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Expected CRLF at pos %tu of %zu but got 0x%02X 0x%02X", n_ptr
- encoded
, encoded_len
, *n_ptr
, *(n_ptr
+ 1));
89 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Expected LF at pos %tu of %zu but got 0x%02X", n_ptr
- encoded
, encoded_len
, *n_ptr
);
94 /* chunk size pretends more data than we actually got, so it's probably a truncated message */
95 if (chunk_len
> (rest
= encoded
+ encoded_len
- n_ptr
)) {
96 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Truncated message: chunk size %lu exceeds remaining data size %lu at pos %tu of %zu", chunk_len
, rest
, n_ptr
- encoded
, encoded_len
);
101 memcpy(*decoded
+ *decoded_len
, n_ptr
, chunk_len
);
102 *decoded_len
+= chunk_len
;
104 if (chunk_len
== rest
) {
105 e_ptr
= n_ptr
+ chunk_len
;
108 /* advance to next chunk */
109 e_ptr
= n_ptr
+ chunk_len
+ eol_len
;
117 #ifdef HTTP_HAVE_ZLIB
119 static const char http_encoding_gzip_header
[] = {
120 (const char) 0x1f, // fixed value
121 (const char) 0x8b, // fixed value
122 (const char) Z_DEFLATED
, // compression algorithm
123 (const char) 0, // none of the possible flags defined by the GZIP "RFC"
124 (const char) 0, // MTIME
125 (const char) 0, // =*=
126 (const char) 0, // =*=
127 (const char) 0, // =*=
128 (const char) 0, // two possible flag values for 9 compression levels? o_O
130 (const char) 0x0b // OS_CODE
132 (const char) 0x03 // OS_CODE
136 PHP_HTTP_API STATUS
_http_encoding_gzencode(int level
, int mtime
, const char *data
, size_t data_len
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
139 STATUS status
= Z_OK
;
141 if (!(data
&& data_len
)) {
147 memset(&Z
, 0, sizeof(z_stream
));
149 Z
.next_in
= (Bytef
*) data
;
150 Z
.avail_in
= data_len
;
151 Z
.avail_out
= HTTP_ENCODING_BUFLEN(data_len
) + HTTP_ENCODING_SAFPAD
- 1;
153 *encoded
= emalloc(HTTP_ENCODING_BUFLEN(data_len
) + sizeof(http_encoding_gzip_header
) + HTTP_ENCODING_SAFPAD
);
154 memcpy(*encoded
, http_encoding_gzip_header
, sizeof(http_encoding_gzip_header
));
157 (*encoded
)[4] = (char) (mtime
& 0xFF);
158 (*encoded
)[5] = (char) ((mtime
>> 8) & 0xFF);
159 (*encoded
)[6] = (char) ((mtime
>> 16) & 0xFF);
160 (*encoded
)[7] = (char) ((mtime
>> 24) & 0xFF);
163 Z
.next_out
= (Bytef
*) *encoded
+ sizeof(http_encoding_gzip_header
);
165 if (Z_OK
== (status
= deflateInit2(&Z
, level
, Z_DEFLATED
, -MAX_WBITS
, MAX_MEM_LEVEL
, Z_DEFAULT_STRATEGY
))) {
166 status
= deflate(&Z
, Z_FINISH
);
169 if (Z_STREAM_END
== status
) {
173 crc
= crc32(0L, Z_NULL
, 0);
174 crc
= crc32(crc
, (const Bytef
*) data
, data_len
);
176 trailer
= *encoded
+ sizeof(http_encoding_gzip_header
) + Z
.total_out
;
179 trailer
[0] = (char) (crc
& 0xFF);
180 trailer
[1] = (char) ((crc
>> 8) & 0xFF);
181 trailer
[2] = (char) ((crc
>> 16) & 0xFF);
182 trailer
[3] = (char) ((crc
>> 24) & 0xFF);
183 trailer
[4] = (char) ((Z
.total_in
) & 0xFF);
184 trailer
[5] = (char) ((Z
.total_in
>> 8) & 0xFF);
185 trailer
[6] = (char) ((Z
.total_in
>> 16) & 0xFF);
186 trailer
[7] = (char) ((Z
.total_in
>> 24) & 0xFF);
188 *encoded_len
= Z
.total_out
+ sizeof(http_encoding_gzip_header
) + 8;
189 (*encoded
)[*encoded_len
] = '\0';
194 STR_SET(*encoded
, NULL
);
195 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not gzencode data: %s", zError(status
));
199 PHP_HTTP_API STATUS
_http_encoding_gzdecode(const char *data
, size_t data_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
204 if ( (data
&& data_len
) &&
205 (SUCCESS
== http_encoding_gzencode_verify(data
, data_len
, &encoded
, &encoded_len
)) &&
206 (SUCCESS
== http_encoding_inflate(encoded
, encoded_len
, decoded
, decoded_len
))) {
207 http_encoding_gzdecode_verify(data
, data_len
, *decoded
, *decoded_len
);
214 PHP_HTTP_API STATUS
_http_encoding_deflate(int level
, int zhdr
, const char *data
, size_t data_len
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
217 STATUS status
= Z_OK
;
221 memset(&Z
, 0, sizeof(z_stream
));
223 Z
.data_type
= Z_UNKNOWN
;
224 Z
.next_in
= (Bytef
*) data
;
225 Z
.avail_in
= data_len
;
226 Z
.avail_out
= HTTP_ENCODING_BUFLEN(data_len
) - 1;
227 Z
.next_out
= emalloc(HTTP_ENCODING_BUFLEN(data_len
));
229 *encoded
= (char *) Z
.next_out
;
231 if (Z_OK
== (status
= deflateInit2(&Z
, level
, Z_DEFLATED
, zhdr
? MAX_WBITS
: -MAX_WBITS
, MAX_MEM_LEVEL
, Z_DEFAULT_STRATEGY
))) {
232 status
= deflate(&Z
, Z_FINISH
);
235 if (Z_STREAM_END
== status
) {
236 (*encoded
)[*encoded_len
= Z
.total_out
] = '\0';
241 STR_SET(*encoded
, NULL
);
242 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not deflate data: %s", zError(status
));
246 PHP_HTTP_API STATUS
_http_encoding_inflate(const char *data
, size_t data_len
, char **decoded
, size_t *decoded_len TSRMLS_DC
)
248 int max
= 0, wbits
= -MAX_WBITS
;
254 memset(&Z
, 0, sizeof(z_stream
));
258 *decoded_len
= data_len
* 2;
259 *decoded
= emalloc(*decoded_len
+ 1);
261 size_t new_len
= *decoded_len
<< 2;
262 char *new_ptr
= erealloc_recoverable(*decoded
, new_len
+ 1);
266 *decoded_len
= new_len
;
268 max
= INT_MAX
-1; /* avoid integer overflow on increment op */
273 Z
.next_in
= (Bytef
*) data
;
274 Z
.avail_in
= data_len
;
275 Z
.next_out
= (Bytef
*) *decoded
;
276 Z
.avail_out
= *decoded_len
;
278 if (Z_OK
== (status
= inflateInit2(&Z
, wbits
))) {
279 status
= inflate(&Z
, Z_FINISH
);
282 /* retry if it looks like we've got a zlib header */
283 if (wbits
== -MAX_WBITS
&& status
== Z_DATA_ERROR
) {
288 if (Z_STREAM_END
== status
) {
289 (*decoded
)[*decoded_len
= Z
.total_out
] = '\0';
293 } while (status
== Z_BUF_ERROR
&& ++max
< HTTP_ENCODING_MAXTRY
);
295 STR_SET(*decoded
, NULL
);
296 http_error_ex(HE_WARNING
, HTTP_E_ENCODING
, "Could not inflate data: %s", zError(status
));
300 PHP_HTTP_API STATUS
_http_encoding_gzencode_verify(const char *data
, size_t data_len
, const char **encoded
, size_t *encoded_len
, int error_level TSRMLS_DC
)
302 size_t offset
= sizeof(http_encoding_gzip_header
);
304 if (data_len
< offset
) {
305 goto really_bad_gzip_header
;
308 if (data
[0] != (const char) 0x1F || data
[1] != (const char) 0x8B) {
309 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Unrecognized GZIP header start: 0x%02X 0x%02X", (int) data
[0], (int) (data
[1] & 0xFF));
313 if (data
[2] != (const char) Z_DEFLATED
) {
314 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Unrecognized compression format (%d)", (int) (data
[2] & 0xFF));
315 /* still try to decode */
317 if ((data
[3] & 0x4) == 0x4) {
318 if (data_len
< offset
+ 2) {
319 goto really_bad_gzip_header
;
321 /* there are extra fields, the length follows the common header as 2 bytes LSB */
322 offset
+= (unsigned) ((data
[offset
] & 0xFF));
324 offset
+= (unsigned) ((data
[offset
] & 0xFF) << 8);
327 if ((data
[3] & 0x8) == 0x8) {
328 if (data_len
<= offset
) {
329 goto really_bad_gzip_header
;
331 /* there's a file name */
332 offset
+= strlen(&data
[offset
]) + 1 /*NUL*/;
334 if ((data
[3] & 0x10) == 0x10) {
335 if (data_len
<= offset
) {
336 goto really_bad_gzip_header
;
338 /* there's a comment */
339 offset
+= strlen(&data
[offset
]) + 1 /* NUL */;
341 if ((data
[3] & 0x2) == 0x2) {
342 /* there's a CRC16 of the header */
344 if (data_len
<= offset
) {
345 goto really_bad_gzip_header
;
349 cmp
= (unsigned) ((data
[offset
-2] & 0xFF));
350 cmp
+= (unsigned) ((data
[offset
-1] & 0xFF) << 8);
352 crc
= crc32(0L, Z_NULL
, 0);
353 crc
= crc32(crc
, (const Bytef
*) data
, sizeof(http_encoding_gzip_header
));
355 if (cmp
!= (crc
& 0xFFFF)) {
356 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "GZIP headers CRC checksums so not match (%lu, %lu)", cmp
, crc
& 0xFFFF);
362 if (data_len
< offset
+ 8) {
363 http_error(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Missing or truncated GZIP footer");
368 *encoded
= data
+ offset
;
371 *encoded_len
= data_len
- offset
- 8 /* size of the assumed GZIP footer */;
376 really_bad_gzip_header
:
377 http_error(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Missing or truncated GZIP header");
381 PHP_HTTP_API STATUS
_http_encoding_gzdecode_verify(const char *data
, size_t data_len
, const char *decoded
, size_t decoded_len
, int error_level TSRMLS_DC
)
383 STATUS status
= SUCCESS
;
386 crc
= crc32(0L, Z_NULL
, 0);
387 crc
= crc32(crc
, (const Bytef
*) decoded
, decoded_len
);
389 cmp
= (unsigned) ((data
[data_len
-8] & 0xFF));
390 cmp
+= (unsigned) ((data
[data_len
-7] & 0xFF) << 8);
391 cmp
+= (unsigned) ((data
[data_len
-6] & 0xFF) << 16);
392 cmp
+= (unsigned) ((data
[data_len
-5] & 0xFF) << 24);
393 len
= (unsigned) ((data
[data_len
-4] & 0xFF));
394 len
+= (unsigned) ((data
[data_len
-3] & 0xFF) << 8);
395 len
+= (unsigned) ((data
[data_len
-2] & 0xFF) << 16);
396 len
+= (unsigned) ((data
[data_len
-1] & 0xFF) << 24);
399 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Could not verify data integrity: CRC checksums do not match (%lu, %lu)", cmp
, crc
);
402 if (len
!= decoded_len
) {
403 http_error_ex(error_level TSRMLS_CC
, HTTP_E_ENCODING
, "Could not verify data integrity: data sizes do not match (%lu, %lu)", len
, decoded_len
);
409 #define HTTP_ENCODING_STREAM_ERROR(status, tofree) \
411 if (tofree) efree(tofree); \
412 http_error_ex(HE_WARNING, HTTP_E_ENCODING, "GZIP stream error: %s", zError(status)); \
416 PHP_HTTP_API STATUS
_http_encoding_stream_init(http_encoding_stream
*s
, int flags
, int level
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
419 int wbits
= (flags
& HTTP_ENCODING_STREAM_ZLIB_HEADER
) ? MAX_WBITS
: -MAX_WBITS
;
421 memset(s
, 0, sizeof(http_encoding_stream
));
422 if (Z_OK
!= (status
= deflateInit2(&s
->Z
, level
, Z_DEFLATED
, wbits
, MAX_MEM_LEVEL
, Z_DEFAULT_STRATEGY
))) {
423 HTTP_ENCODING_STREAM_ERROR(status
, NULL
);
426 s
->persistent
= (flags
& HTTP_ENCODING_STREAM_PERSISTENT
);
427 if ((s
->gzip
= (flags
& HTTP_ENCODING_STREAM_GZIP_HEADER
))) {
428 s
->crc
= crc32(0L, Z_NULL
, 0);
429 *encoded_len
= sizeof(http_encoding_gzip_header
);
430 *encoded
= pemalloc(*encoded_len
, s
->persistent
);
431 memcpy(*encoded
, http_encoding_gzip_header
, *encoded_len
);
440 PHP_HTTP_API STATUS
_http_encoding_stream_update(http_encoding_stream
*s
, const char *data
, size_t data_len
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
444 *encoded_len
= HTTP_ENCODING_BUFLEN(data_len
);
445 *encoded
= pemalloc(*encoded_len
, s
->persistent
);
447 s
->Z
.next_in
= (Bytef
*) data
;
448 s
->Z
.avail_in
= data_len
;
449 s
->Z
.next_out
= (Bytef
*) *encoded
;
450 s
->Z
.avail_out
= *encoded_len
;
452 status
= deflate(&s
->Z
, Z_SYNC_FLUSH
);
454 if (Z_OK
!= status
&& Z_STREAM_END
!= status
) {
455 HTTP_ENCODING_STREAM_ERROR(status
, *encoded
);
457 *encoded_len
-= s
->Z
.avail_out
;
460 s
->crc
= crc32(s
->crc
, (const Bytef
*) data
, data_len
);
466 PHP_HTTP_API STATUS
_http_encoding_stream_finish(http_encoding_stream
*s
, char **encoded
, size_t *encoded_len TSRMLS_DC
)
471 *encoded
= pemalloc(*encoded_len
, s
->persistent
);
473 s
->Z
.next_out
= (Bytef
*) *encoded
;
474 s
->Z
.avail_out
= *encoded_len
;
476 if (Z_STREAM_END
!= (status
= deflate(&s
->Z
, Z_FINISH
)) || Z_OK
!= (status
= deflateEnd(&s
->Z
))) {
477 HTTP_ENCODING_STREAM_ERROR(status
, *encoded
);
480 *encoded_len
-= s
->Z
.avail_out
;
482 if (s
->Z
.avail_out
< 8) {
483 *encoded
= perealloc(*encoded
, *encoded_len
+ 8, s
->persistent
);
485 (*encoded
)[(*encoded_len
)++] = (char) (s
->crc
& 0xFF);
486 (*encoded
)[(*encoded_len
)++] = (char) ((s
->crc
>> 8) & 0xFF);
487 (*encoded
)[(*encoded_len
)++] = (char) ((s
->crc
>> 16) & 0xFF);
488 (*encoded
)[(*encoded_len
)++] = (char) ((s
->crc
>> 24) & 0xFF);
489 (*encoded
)[(*encoded_len
)++] = (char) ((s
->Z
.total_in
) & 0xFF);
490 (*encoded
)[(*encoded_len
)++] = (char) ((s
->Z
.total_in
>> 8) & 0xFF);
491 (*encoded
)[(*encoded_len
)++] = (char) ((s
->Z
.total_in
>> 16) & 0xFF);
492 (*encoded
)[(*encoded_len
)++] = (char) ((s
->Z
.total_in
>> 24) & 0xFF);
498 #endif /* HTTP_HAVE_ZLIB */
500 PHP_HTTP_API zend_bool
_http_encoding_response_start(size_t content_length TSRMLS_DC
)
502 if ( php_ob_handler_used("ob_gzhandler" TSRMLS_CC
) ||
503 php_ob_handler_used("zlib output compression" TSRMLS_CC
)) {
504 HTTP_G(send
).gzip_encoding
= 0;
506 if (!HTTP_G(send
).gzip_encoding
) {
507 /* emit a content-length header */
508 if (content_length
) {
509 char cl_header_str
[128];
510 size_t cl_header_len
;
511 cl_header_len
= snprintf(cl_header_str
, lenof(cl_header_str
), "Content-Length: %zu", content_length
);
512 http_send_header_string_ex(cl_header_str
, cl_header_len
, 1);
515 #ifndef HTTP_HAVE_ZLIB
516 HTTP_G(send
).gzip_encoding
= 0;
517 php_start_ob_buffer_named("ob_gzhandler", 0, 0 TSRMLS_CC
);
522 INIT_PZVAL(&zsupported
);
523 array_init(&zsupported
);
524 add_next_index_stringl(&zsupported
, "gzip", lenof("gzip"), 1);
525 add_next_index_stringl(&zsupported
, "x-gzip", lenof("x-gzip"), 1);
526 add_next_index_stringl(&zsupported
, "deflate", lenof("deflate"), 1);
528 HTTP_G(send
).gzip_encoding
= 0;
530 if ((selected
= http_negotiate_encoding(&zsupported
))) {
532 char *encoding
= NULL
;
535 if (HASH_KEY_IS_STRING
== zend_hash_get_current_key(selected
, &encoding
, &idx
, 0) && encoding
) {
536 if (!strcmp(encoding
, "gzip") || !strcmp(encoding
, "x-gzip")) {
537 if (SUCCESS
== (hs
= http_send_header_string("Content-Encoding: gzip"))) {
538 HTTP_G(send
).gzip_encoding
= HTTP_ENCODING_GZIP
;
540 } else if (!strcmp(encoding
, "deflate")) {
541 if (SUCCESS
== (hs
= http_send_header_string("Content-Encoding: deflate"))) {
542 HTTP_G(send
).gzip_encoding
= HTTP_ENCODING_DEFLATE
;
546 http_send_header_string("Vary: Accept-Encoding");
550 zend_hash_destroy(selected
);
551 FREE_HASHTABLE(selected
);
554 zval_dtor(&zsupported
);
555 return HTTP_G(send
).gzip_encoding
;
567 * vim600: noet sw=4 ts=4 fdm=marker
568 * vim<600: noet sw=4 ts=4