All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.gematik.rbellogger.modifier.RbelHttpResponseWriter Maven / Gradle / Ivy

/*
 * Copyright (c) 2022 gematik GmbH
 * 
 * Licensed under the Apache License, Version 2.0 (the License);
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.gematik.rbellogger.modifier;

import de.gematik.rbellogger.data.RbelElement;
import de.gematik.rbellogger.data.facet.RbelHttpHeaderFacet;
import de.gematik.rbellogger.data.facet.RbelHttpMessageFacet;
import de.gematik.rbellogger.data.facet.RbelHttpRequestFacet;
import de.gematik.rbellogger.data.facet.RbelHttpResponseFacet;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Locale;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import org.apache.commons.lang3.ArrayUtils;

public class RbelHttpResponseWriter implements RbelElementWriter {

    @Override
    public boolean canWrite(RbelElement oldTargetElement) {
        return oldTargetElement.hasFacet(RbelHttpResponseFacet.class)
            || oldTargetElement.hasFacet(RbelHttpRequestFacet.class);
    }

    @Override
    public byte[] write(RbelElement oldTargetElement, RbelElement oldTargetModifiedChild, byte[] newContent) {
        final Optional responseFacet = oldTargetElement.getFacet(RbelHttpResponseFacet.class);
        final Optional requestFacet = oldTargetElement.getFacet(RbelHttpRequestFacet.class);
        final RbelHttpMessageFacet messageFacet = oldTargetElement.getFacetOrFail(RbelHttpMessageFacet.class);
        final StringJoiner joiner = new StringJoiner("\r\n");

        joiner.add(buildTitleLine(oldTargetModifiedChild, newContent, responseFacet, requestFacet));

        byte[] body = getChunkedMapper(oldTargetElement)
            .apply(getBodyContent(messageFacet, oldTargetModifiedChild, newContent));
        if (messageFacet.getHeader() == oldTargetModifiedChild) {
            joiner.add(new String(newContent));
        } else {
            joiner.add(patchHeader(new String(messageFacet.getHeader().getRawContent()), body.length));
        }
        joiner.add("");
        joiner.add("");
        return ArrayUtils.addAll(joiner.toString().getBytes(oldTargetModifiedChild.getElementCharset()),
            body);
    }

    private UnaryOperator getChunkedMapper(RbelElement oldTargetElement) {
        if (isChunkedMessage(oldTargetElement)) {
            return array -> ArrayUtils.addAll((array.length + "\r\n").getBytes(oldTargetElement.getElementCharset()),
                ArrayUtils.addAll(array, ("\r\n0\r\n").getBytes(oldTargetElement.getElementCharset())));
        } else {
            return UnaryOperator.identity();
        }
    }

    private boolean isChunkedMessage(RbelElement oldTargetElement) {
        return oldTargetElement.getFacet(RbelHttpMessageFacet.class)
            .map(RbelHttpMessageFacet::getHeader)
            .flatMap(el -> el.getFacet(RbelHttpHeaderFacet.class))
            .stream()
            .flatMap(h -> h.getCaseInsensitiveMatches("Transfer-Encoding"))
            .map(RbelElement::getRawStringContent)
            .filter(value -> value.equalsIgnoreCase("chunked"))
            .findAny().isPresent();
    }

    private String patchHeader(String headerRaw, int length) {
        return Arrays.stream(headerRaw.split("\r\n"))
            .map(headerLine -> {
                if (headerLine.toLowerCase(Locale.ROOT).startsWith("content-length")) {
                    return "Content-Length: " + length;
                } else {
                    return headerLine;
                }
            })
            .collect(Collectors.joining("\r\n"));
    }

    private byte[] getBodyContent(RbelHttpMessageFacet messageFacet, RbelElement oldTargetModifiedChild,
        byte[] newContent) {
        if (messageFacet.getBody() == oldTargetModifiedChild) {
            return newContent;
        } else {
            return messageFacet.getBody().getRawContent();
        }
    }

    private byte[] getResponseCode(Optional responseFacet, RbelElement oldTargetModifiedChild,
        byte[] newContent) {
        if (responseFacet.get().getResponseCode() == oldTargetModifiedChild) {
            return newContent;
        } else {
            return responseFacet.get().getResponseCode().getRawContent();
        }
    }

    private Optional getReasonPhrase(Optional responseFacet,
        RbelElement oldTargetModifiedChild,
        byte[] newContent) {
        if (responseFacet.get().getReasonPhrase() == null) {
            return Optional.empty();
        }
        if (responseFacet.get().getReasonPhrase() == oldTargetModifiedChild) {
            return Optional.of(newContent);
        } else {
            return Optional.ofNullable(responseFacet.get().getReasonPhrase().getRawContent());
        }
    }

    private String buildTitleLine(RbelElement oldTargetModifiedChild, byte[] newContent,
        Optional responseFacet, Optional requestFacet) {
        StringBuilder builder = new StringBuilder();

        String request = buildRequest(oldTargetModifiedChild, new String(newContent), requestFacet, builder);
        if (request != null) {
            return request;
        }
        String responseCodeContent = new String(getResponseCode(responseFacet, oldTargetModifiedChild, newContent));

        if (getReasonPhrase(responseFacet, oldTargetModifiedChild, newContent).isPresent()
            && new String(getReasonPhrase(responseFacet, oldTargetModifiedChild, newContent).get()).trim().length() > 0) {
            String reasonPhraseContent =
                " " + new String(getReasonPhrase(responseFacet, oldTargetModifiedChild, newContent).get(),
                    StandardCharsets.UTF_8);
            return "HTTP/1.1 " + responseCodeContent + reasonPhraseContent;
        }

        return "HTTP/1.1 " + responseCodeContent;
    }

    private String buildRequest(RbelElement oldTargetModifiedChild, String newContent,
        Optional requestFacet, StringBuilder builder) {
        if (requestFacet.isPresent()) {
            if (requestFacet.get().getMethod() == oldTargetModifiedChild) {
                builder.append(newContent);
            } else {
                builder.append(requestFacet.get().getMethod().getRawStringContent());
            }
            builder.append(" ");
            if (requestFacet.get().getPath() == oldTargetModifiedChild) {
                builder.append(newContent);
            } else {
                builder.append(requestFacet.get().getPath().getRawStringContent());
            }
            builder.append(" HTTP/1.1");
            return builder.toString();
        }
        return null;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy