View | Details | Raw Unified | Return to bug 52860
Collapse All | Expand All

(-)modules/filters/mod_deflate.c (-2 / +383 lines)
Lines 883-888 Link Here
883
    return APR_SUCCESS;
883
    return APR_SUCCESS;
884
}
884
}
885
885
886
static apr_status_t deflate_te_filter(ap_filter_t *f,
887
                                       apr_bucket_brigade *bb)
888
{
889
    apr_bucket *e;
890
    request_rec *r = f->r;
891
    deflate_ctx *ctx = f->ctx;
892
    int zRC;
893
    apr_size_t len = 0, blen;
894
    const char *data;
895
    deflate_filter_config *c;
896
897
    /* No need to compress an empty brigade */
898
    if (APR_BRIGADE_EMPTY(bb)) {
899
        return APR_SUCCESS;
900
    }
901
902
    c = ap_get_module_config(r->server->module_config,
903
                             &deflate_module);
904
905
906
    /* If we don't have a context, we need to ensure that it is okay to send
907
     * the deflated content.  If we have a context, that means we've done
908
     * this before and we liked it.
909
     * This could be not so nice if we always fail.  But, if we succeed,
910
     * we're in better shape.
911
     */
912
    if (!ctx) {
913
        char *token;
914
        const char *encoding;
915
916
        if (have_ssl_compression(r)) {
917
            ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
918
                          "Compression enabled at SSL level; not compressing "
919
                          "at HTTP level.");
920
            ap_remove_output_filter(f);
921
            return ap_pass_brigade(f->next, bb);
922
        }
923
924
        ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
925
926
        /*
927
         * Only work on main request, not subrequests,
928
         * that are not a 204 response with no content
929
         * and are not tagged with the no-gzip env variable
930
         * and not a partial response to a Range request.
931
         */
932
        if ((r->main != NULL) || (r->status == HTTP_NO_CONTENT) ||
933
            apr_table_get(r->subprocess_env, "no-gzip") ||
934
            apr_table_get(r->headers_out, "Content-Range")
935
           ) {
936
            if (APLOG_R_IS_LEVEL(r, APLOG_TRACE1)) {
937
                const char *reason =
938
                    (r->main != NULL)                           ? "subrequest" :
939
                    (r->status == HTTP_NO_CONTENT)              ? "no content" :
940
                    apr_table_get(r->subprocess_env, "no-gzip") ? "no-gzip" :
941
                    "content-range";
942
                ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
943
                              "Not compressing (%s)", reason);
944
            }
945
            ap_remove_output_filter(f);
946
            return ap_pass_brigade(f->next, bb);
947
        }
948
949
        /*
950
         * Don't add gzip transfer-encoding to responses that 
951
         * cannot be chunked.
952
         */
953
        if (!(r->proto_num >= HTTP_VERSION(1,1))) {
954
            ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
955
                          "GZIPTE ignoring non-HTTP/1.1 request");
956
            ap_remove_output_filter(f);
957
            return ap_pass_brigade(f->next, bb);
958
        }
959
960
        /* Some browsers might have problems with content types
961
         * other than text/html, so set gzip-only-text/html
962
         * (with browsermatch) for them
963
         */
964
        if (r->content_type == NULL
965
             || strncmp(r->content_type, "text/html", 9)) {
966
            const char *env_value = apr_table_get(r->subprocess_env,
967
                                                  "gzip-only-text/html");
968
            if ( env_value && (strcmp(env_value,"1") == 0) ) {
969
                ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
970
                              "Not compressing, (gzip-only-text/html)");
971
                ap_remove_output_filter(f);
972
                return ap_pass_brigade(f->next, bb);
973
            }
974
        }
975
976
        /* Let's see what our current Content-Encoding is.
977
         * If it's already encoded, don't compress again.
978
         * (We could, but let's not.)
979
         */
980
        encoding = apr_table_get(r->headers_out, "Content-Encoding");
981
        if (encoding) {
982
            const char *err_enc;
983
984
            err_enc = apr_table_get(r->err_headers_out, "Content-Encoding");
985
            if (err_enc) {
986
                encoding = apr_pstrcat(r->pool, encoding, ",", err_enc, NULL);
987
            }
988
        }
989
        else {
990
            encoding = apr_table_get(r->err_headers_out, "Content-Encoding");
991
        }
992
993
        if (r->content_encoding) {
994
            encoding = encoding ? apr_pstrcat(r->pool, encoding, ",",
995
                                              r->content_encoding, NULL)
996
                                : r->content_encoding;
997
        }
998
999
        if (encoding) {
1000
            const char *tmp = encoding;
1001
1002
            token = ap_get_token(r->pool, &tmp, 0);
1003
            while (token && *token) {
1004
                /* stolen from mod_negotiation: */
1005
                if (strcmp(token, "identity") && strcmp(token, "7bit") &&
1006
                    strcmp(token, "8bit") && strcmp(token, "binary")) {
1007
                    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
1008
                                  "Not recompressing (content-encoding already "
1009
                                  " set: %s)", token);
1010
                    ap_remove_output_filter(f);
1011
                    return ap_pass_brigade(f->next, bb);
1012
                }
1013
1014
                /* Otherwise, skip token */
1015
                if (*tmp) {
1016
                    ++tmp;
1017
                }
1018
                token = (*tmp) ? ap_get_token(r->pool, &tmp, 0) : NULL;
1019
            }
1020
        }
1021
1022
        /* Check the TE: header now */
1023
        const char *te_accepts;
1024
        te_accepts = apr_table_get(r->headers_in, "TE");
1025
        if (te_accepts == NULL) {
1026
            ap_remove_output_filter(f);
1027
            return ap_pass_brigade(f->next, bb);
1028
        }
1029
1030
        /* check whether the Connection: header lists "TE" and,
1031
         * if so, overlook the TE: header from the request.
1032
         * 
1033
         * Might be better done more generally, elsewhere in httpd.
1034
         */
1035
        const char *connection;
1036
        connection = apr_table_get(r->headers_in, "Connection");
1037
        if (!(connection == NULL)) {
1038
            token = ap_get_token(r->pool, &connection, 0);
1039
            while (token && token[0] && strcasecmp(token, "te")) {
1040
                /* retrieve next token */
1041
                if (*connection == ',') {
1042
                    ++connection;
1043
                }
1044
                token = (*connection) ? ap_get_token(r->pool, &connection, 0) : NULL;
1045
                if (0 == strcasecmp(token, "te")) {
1046
                    /* We will never see a suitable TE: header
1047
                     * because we know we must remove any such 
1048
                     * header that is present.
1049
                     */
1050
                     ap_remove_output_filter(f);
1051
                     return ap_pass_brigade(f->next, bb);
1052
                }
1053
            }
1054
        }
1055
1056
        token = ap_get_token(r->pool, &te_accepts, 0);
1057
        while (token && token[0] && strcasecmp(token, "gzip")) {
1058
            /* skip parameters, XXX: ;q=foo evaluation? */
1059
            while (*te_accepts == ';') {
1060
                ++te_accepts;
1061
                ap_get_token(r->pool, &te_accepts, 1);
1062
            }
1063
1064
            /* retrieve next token */
1065
            if (*te_accepts == ',') {
1066
                ++te_accepts;
1067
            }
1068
            token = (*te_accepts) ? ap_get_token(r->pool, &te_accepts, 0) : NULL;
1069
        }
1070
1071
        /* No acceptable token found. */
1072
        if (token == NULL || token[0] == '\0') {
1073
            ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
1074
                          "Not compressing (no TE: gzip)");
1075
            ap_remove_output_filter(f);
1076
            return ap_pass_brigade(f->next, bb);
1077
        }
1078
1079
        /* At this point we have decided to filter the content. Let's try to
1080
         * to initialize zlib (except for 304 responses, where we will only
1081
         * send out the headers).
1082
         */
1083
1084
        ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
1085
        ctx->buffer = apr_palloc(r->pool, c->bufferSize);
1086
        ctx->libz_end_func = deflateEnd;
1087
1088
        zRC = deflateInit2(&ctx->stream, c->compressionlevel, Z_DEFLATED,
1089
                           c->windowSize, c->memlevel,
1090
                           Z_DEFAULT_STRATEGY);
1091
1092
        if (zRC != Z_OK) {
1093
            deflateEnd(&ctx->stream);
1094
            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1095
                          "unable to init Zlib: "
1096
                          "deflateInit2 returned %d: URL %s",
1097
                          zRC, r->uri);
1098
            /*
1099
             * Remove ourselves as it does not make sense to return:
1100
             * We are not able to init libz and pass data down the chain
1101
             * uncompressed.
1102
             */
1103
            ap_remove_output_filter(f);
1104
            return ap_pass_brigade(f->next, bb);
1105
        }
1106
1107
        /*
1108
         * Register a cleanup function to ensure that we cleanup the internal
1109
         * libz resources.
1110
         */
1111
        apr_pool_cleanup_register(r->pool, ctx, deflate_ctx_cleanup,
1112
                                  apr_pool_cleanup_null);
1113
1114
        /* Set the filter init flag so subsequent invocations know we are
1115
         * active.
1116
         */
1117
        ctx->filter_init = 1;
1118
1119
        ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
1120
                      "Applying gzip Transfer-Encoding");
1121
1122
        /*
1123
         * Zlib initialization worked, so we can now change the important
1124
         * response metadata before sending the response out.
1125
         */
1126
1127
        apr_table_mergen(r->headers_out, "Transfer-Encoding", "gzip");
1128
        apr_table_unset(r->headers_out, "Content-Length");
1129
1130
        /* add immortal gzip header */
1131
        e = apr_bucket_immortal_create(gzip_header, sizeof gzip_header,
1132
                                       f->c->bucket_alloc);
1133
        APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
1134
1135
        /* initialize deflate output buffer */
1136
        ctx->stream.next_out = ctx->buffer;
1137
        ctx->stream.avail_out = c->bufferSize;
1138
    } else if (!ctx->filter_init) {
1139
        /* Hmm.  We've run through the filter init before as we have a ctx,
1140
         * but we never initialized.  We probably have a dangling ref.  Bail.
1141
         */
1142
        return ap_pass_brigade(f->next, bb);
1143
    }
1144
1145
    while (!APR_BRIGADE_EMPTY(bb))
1146
    {
1147
        apr_bucket *b;
1148
1149
        e = APR_BRIGADE_FIRST(bb);
1150
1151
        if (APR_BUCKET_IS_EOS(e)) {
1152
            char *buf;
1153
1154
            ctx->stream.avail_in = 0; /* should be zero already anyway */
1155
            /* flush the remaining data from the zlib buffers */
1156
            flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate, Z_FINISH,
1157
                              NO_UPDATE_CRC);
1158
1159
            buf = apr_palloc(r->pool, VALIDATION_SIZE);
1160
            putLong((unsigned char *)&buf[0], ctx->crc);
1161
            putLong((unsigned char *)&buf[4], ctx->stream.total_in);
1162
1163
            b = apr_bucket_pool_create(buf, VALIDATION_SIZE, r->pool,
1164
                                       f->c->bucket_alloc);
1165
            APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
1166
            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
1167
                          "Zlib: Compressed %ld to %ld : URL %s",
1168
                          ctx->stream.total_in, ctx->stream.total_out, r->uri);
1169
1170
            deflateEnd(&ctx->stream);
1171
            /* No need for cleanup any longer */
1172
            apr_pool_cleanup_kill(r->pool, ctx, deflate_ctx_cleanup);
1173
1174
            /* Remove EOS from the old list, and insert into the new. */
1175
            APR_BUCKET_REMOVE(e);
1176
            APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
1177
1178
            /* Okay, we've seen the EOS.
1179
             * Time to pass it along down the chain.
1180
             */
1181
            return ap_pass_brigade(f->next, ctx->bb);
1182
        }
1183
1184
        if (APR_BUCKET_IS_FLUSH(e)) {
1185
            apr_status_t rv;
1186
1187
            /* flush the remaining data from the zlib buffers */
1188
            zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate,
1189
                                    Z_SYNC_FLUSH, NO_UPDATE_CRC);
1190
            if (zRC != Z_OK) {
1191
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1192
                              "Zlib error %d flushing zlib output buffer (%s)",
1193
                              zRC, ctx->stream.msg);
1194
                return APR_EGENERAL;
1195
            }
1196
1197
            /* Remove flush bucket from old brigade anf insert into the new. */
1198
            APR_BUCKET_REMOVE(e);
1199
            APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
1200
            rv = ap_pass_brigade(f->next, ctx->bb);
1201
            if (rv != APR_SUCCESS) {
1202
                return rv;
1203
            }
1204
            continue;
1205
        }
1206
1207
        if (APR_BUCKET_IS_METADATA(e)) {
1208
            /*
1209
             * Remove meta data bucket from old brigade and insert into the
1210
             * new.
1211
             */
1212
            APR_BUCKET_REMOVE(e);
1213
            APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
1214
            continue;
1215
        }
1216
1217
        /* read */
1218
        apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
1219
1220
        /* This crc32 function is from zlib. */
1221
        ctx->crc = crc32(ctx->crc, (const Bytef *)data, len);
1222
1223
        /* write */
1224
        ctx->stream.next_in = (unsigned char *)data; /* We just lost const-ness,
1225
                                                      * but we'll just have to
1226
                                                      * trust zlib */
1227
        ctx->stream.avail_in = len;
1228
1229
        while (ctx->stream.avail_in != 0) {
1230
            if (ctx->stream.avail_out == 0) {
1231
                apr_status_t rv;
1232
1233
                ctx->stream.next_out = ctx->buffer;
1234
                len = c->bufferSize - ctx->stream.avail_out;
1235
1236
                b = apr_bucket_heap_create((char *)ctx->buffer, len,
1237
                                           NULL, f->c->bucket_alloc);
1238
                APR_BRIGADE_INSERT_TAIL(ctx->bb, b);
1239
                ctx->stream.avail_out = c->bufferSize;
1240
                /* Send what we have right now to the next filter. */
1241
                rv = ap_pass_brigade(f->next, ctx->bb);
1242
                if (rv != APR_SUCCESS) {
1243
                    return rv;
1244
                }
1245
            }
1246
1247
            zRC = deflate(&(ctx->stream), Z_NO_FLUSH);
1248
1249
            if (zRC != Z_OK) {
1250
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1251
                              "Zlib error %d deflating data (%s)", zRC,
1252
                              ctx->stream.msg);
1253
                return APR_EGENERAL;
1254
            }
1255
        }
1256
1257
        apr_bucket_delete(e);
1258
    }
1259
1260
    apr_brigade_cleanup(bb);
1261
    return APR_SUCCESS;
1262
}
1263
1264
886
/* This is the deflate input filter (inflates).  */
1265
/* This is the deflate input filter (inflates).  */
887
static apr_status_t deflate_in_filter(ap_filter_t *f,
1266
static apr_status_t deflate_in_filter(ap_filter_t *f,
888
                                      apr_bucket_brigade *bb,
1267
                                      apr_bucket_brigade *bb,
Lines 1503-1509 Link Here
1503
    return OK;
1882
    return OK;
1504
}
1883
}
1505
1884
1506
1507
#define PROTO_FLAGS AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH
1885
#define PROTO_FLAGS AP_FILTER_PROTO_CHANGE|AP_FILTER_PROTO_CHANGE_LENGTH
1508
static void register_hooks(apr_pool_t *p)
1886
static void register_hooks(apr_pool_t *p)
1509
{
1887
{
Lines 1511-1519 Link Here
1511
                              AP_FTYPE_CONTENT_SET);
1889
                              AP_FTYPE_CONTENT_SET);
1512
    ap_register_output_filter("INFLATE", inflate_out_filter, NULL,
1890
    ap_register_output_filter("INFLATE", inflate_out_filter, NULL,
1513
                              AP_FTYPE_RESOURCE-1);
1891
                              AP_FTYPE_RESOURCE-1);
1892
    ap_register_output_filter("GZIPTE", deflate_te_filter, NULL,
1893
                              AP_FTYPE_TRANSCODE);
1514
    ap_register_input_filter(deflateFilterName, deflate_in_filter, NULL,
1894
    ap_register_input_filter(deflateFilterName, deflate_in_filter, NULL,
1515
                              AP_FTYPE_CONTENT_SET);
1895
                              AP_FTYPE_CONTENT_SET);
1516
    ap_hook_post_config(mod_deflate_post_config, NULL, NULL, APR_HOOK_MIDDLE);
1517
}
1896
}
1518
1897
1519
static const command_rec deflate_filter_cmds[] = {
1898
static const command_rec deflate_filter_cmds[] = {
Lines 1539-1541 Link Here
1539
    deflate_filter_cmds,          /* command table */
1918
    deflate_filter_cmds,          /* command table */
1540
    register_hooks                /* register hooks */
1919
    register_hooks                /* register hooks */
1541
};
1920
};
1921
1922

Return to bug 52860