package org.keycloak.encoding;

import org.apache.commons.io.IOUtils;
import org.jboss.logging.Logger;
import org.keycloak.models.KeycloakSession;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.zip.GZIPOutputStream;

public class GzipResourceEncodingProvider implements ResourceEncodingProvider {

    private static final Logger logger = Logger.getLogger(ResourceEncodingProvider.class);

    private KeycloakSession session;
    private File cacheDir;

    public GzipResourceEncodingProvider(KeycloakSession session, File cacheDir) {
        this.session = session;
        this.cacheDir = cacheDir;
    }

    public InputStream getEncodedStream(StreamSupplier producer, String... path) {
        StringBuilder sb = new StringBuilder();
        sb.append(cacheDir.getAbsolutePath());
        for (String p : path) {
            sb.append(File.separatorChar);
            sb.append(p);
        }
        sb.append(".gz");

        String filePath = sb.toString();

        try {
            File encodedFile = new File(filePath);
            if (!encodedFile.getCanonicalPath().startsWith(cacheDir.getCanonicalPath())) {
                return null;
            }

            if (!encodedFile.exists()) {
                InputStream is = producer.getInputStream();
                if (is != null) {
                    File parent = encodedFile.getParentFile();
                    if (!parent.isDirectory()) {
                        parent.mkdirs();
                    }
                    FileOutputStream fos = new FileOutputStream(encodedFile);
                    GZIPOutputStream gos = new GZIPOutputStream(fos);
                    IOUtils.copy(is, gos);
                    gos.close();
                    is.close();
                } else {
                    encodedFile = null;
                }
            }

            return encodedFile != null ? new FileInputStream(encodedFile) : null;
        } catch (Exception e) {
            logger.warn("Failed to encode resource", e);
            return null;
        }
    }

    public String getEncoding() {
        return "gzip";
    }

}

@@ -25,16 +25,137 @@
 oding;%0A%0A
+import java.io.IOException;%0Aimport java.nio.file.Files;%0Aimport static java.nio.file.StandardCopyOption.REPLACE_EXISTING;%0A
 import o
@@ -1669,32 +1669,219 @@
               %7D%0A
+                    File tmpEncodedFile = File.createTempFile(%0A                            encodedFile.getName(),%0A                            %22tmp%22,%0A                            parent);%0A%0A
                 
@@ -1920,33 +1920,36 @@
 ileOutputStream(
-e
+tmpE
 ncodedFile);%0A   
@@ -2115,32 +2115,546 @@
     is.close();%0A
+                    try %7B%0A                        Files.move(%0A                                tmpEncodedFile.toPath(),%0A                                encodedFile.toPath(),%0A                                REPLACE_EXISTING);%0A                    %7D catch ( IOException io ) %7B%0A                        logger.warnf(%22Fail to move %25s  %25s%22, tmpEncodedFile.toString(), io);%0A                        if (!encodedFile.exists()) %7B%0A                            encodedFile = null;%0A                        %7D%0A                    %7D%0A
                 

Use temporary file to reduce the chance of serving partial gzipped resource (#14511)