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

io.undertow.server.handlers.proxy.mod_cluster.MCMPAdvertiseTask Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * 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 io.undertow.server.handlers.proxy.mod_cluster;

import io.undertow.UndertowLogger;
import io.undertow.util.NetworkUtils;
import org.xnio.OptionMap;
import org.xnio.Options;
import org.xnio.XnioWorker;
import org.xnio.channels.MulticastMessageChannel;

import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit;

/**
 * @author Emanuel Muckenhuber
 */
class MCMPAdvertiseTask implements Runnable {

    public static final String RFC_822_FMT = "EEE, d MMM yyyy HH:mm:ss Z";
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(RFC_822_FMT, Locale.US);
    private static final boolean linuxLike;
    private static final boolean windows;

    static {
        String value = System.getProperty("os.name");
        linuxLike = (value != null) && (value.toLowerCase().startsWith("linux") || value.toLowerCase().startsWith("mac") || value.toLowerCase().startsWith("hp"));
        windows = (value != null) && value.toLowerCase().contains("win");
    }

    private volatile int seq = 0;

    private final String protocol;
    private final String host;
    private final int port;
    private final String path;
    private final byte[] ssalt;
    private final MessageDigest md;
    private final InetSocketAddress address;
    private final ModClusterContainer container;
    private final MulticastMessageChannel channel;

    static void advertise(final ModClusterContainer container, final MCMPConfig.AdvertiseConfig config, final XnioWorker worker) throws IOException {
        InetSocketAddress bindAddress;
        final InetAddress address = InetAddress.getByName(config.getAdvertiseAddress());

        if (address == null) {
            bindAddress = new InetSocketAddress(config.getAdvertisePort());
        } else {
            bindAddress = new InetSocketAddress(address, config.getAdvertisePort());
        }

        MulticastMessageChannel channel;
        try {
            channel = worker.createUdpServer(bindAddress, OptionMap.EMPTY);
        } catch (IOException e) {
            if (address != null && (linuxLike || windows)) {
                //try again with no address
                //see UNDERTOW-454
                UndertowLogger.ROOT_LOGGER.potentialCrossTalking(address, (address instanceof Inet4Address) ? "IPv4" : "IPv6", e.getLocalizedMessage());
                bindAddress = new InetSocketAddress(config.getAdvertisePort());
                channel = worker.createUdpServer(bindAddress, OptionMap.EMPTY);
            } else {
                throw e;
            }
        }

        // multicast ttl can only be set after the channel has been created
        channel.setOption(Options.MULTICAST_TTL, config.getAdvertiseTtl());

        final MCMPAdvertiseTask task = new MCMPAdvertiseTask(container, config, channel);
        //execute immediately, so there is no delay before load balancing starts working
        channel.getIoThread().execute(task);
        channel.getIoThread().executeAtInterval(task, config.getAdvertiseFrequency(), TimeUnit.MILLISECONDS);
    }

    MCMPAdvertiseTask(final ModClusterContainer container, final MCMPConfig.AdvertiseConfig config, final MulticastMessageChannel channel) throws IOException {

        this.container = container;
        this.protocol = config.getProtocol();
        // MODCLUSTER-483 mod_cluster client does not yet support ipv6 addresses with zone indices so skip it
        String host = config.getManagementSocketAddress().getHostString();
        int zoneIndex = host.indexOf("%");
        this.host = (zoneIndex < 0) ? host : host.substring(0, zoneIndex);
        this.port = config.getManagementSocketAddress().getPort();
        this.path = config.getPath();
        this.channel = channel;

        final InetAddress group = InetAddress.getByName(config.getAdvertiseGroup());
        address = new InetSocketAddress(group, config.getAdvertisePort());
        try {
            md = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        final String securityKey = config.getSecurityKey();
        if (securityKey == null) {
            // Security key is not configured, so the result hash was zero bytes
            ssalt = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
        } else {
            md.reset();
            digestString(md, securityKey);
            ssalt = md.digest();
        }

        UndertowLogger.ROOT_LOGGER.proxyAdvertisementsStarted(address.toString(), config.getAdvertiseFrequency());
    }

    private static final String CRLF = "\r\n";

    /*
     * the messages to send are something like:
     *
     * HTTP/1.0 200 OK
     * Date: Thu, 13 Sep 2012 09:24:02 GMT
     * Sequence: 5
     * Digest: ae8e7feb7cd85be346134657de3b0661
     * Server: b58743ba-fd84-11e1-bd12-ad866be2b4cc
     * X-Manager-Address: 127.0.0.1:6666
     * X-Manager-Url: /b58743ba-fd84-11e1-bd12-ad866be2b4cc
     * X-Manager-Protocol: http
     * X-Manager-Host: 10.33.144.3
     *
     */
    @Override
    public void run() {
        try {
            /*
             * apr_uuid_get(&magd->suuid);
             * magd->srvid[0] = '/';
             * apr_uuid_format(&magd->srvid[1], &magd->suuid);
             * In fact we use the srvid on the 2 second byte [1]
             */
            final byte[] ssalt = this.ssalt;
            final String server = container.getServerID();
            final String date = DATE_FORMAT.format(new Date(System.currentTimeMillis()));
            final String seq = "" + this.seq++;

            final byte[] digest;
            synchronized (md) {
                md.reset();
                md.update(ssalt);
                digestString(md, date);
                digestString(md, seq);
                digestString(md, server);
                digest = md.digest();
            }
            final String digestString = bytesToHexString(digest);

            final StringBuilder builder = new StringBuilder();
            builder.append("HTTP/1.0 200 OK").append(CRLF)
                    .append("Date: ").append(date).append(CRLF)
                    .append("Sequence: ").append(seq).append(CRLF)
                    .append("Digest: ").append(digestString).append(CRLF)
                    .append("Server: ").append(server).append(CRLF)
                    .append("X-Manager-Address: ").append(NetworkUtils.formatPossibleIpv6Address(host)).append(":").append(port).append(CRLF)
                    .append("X-Manager-Url: ").append(path).append(CRLF)
                    .append("X-Manager-Protocol: ").append(protocol).append(CRLF)
                    .append("X-Manager-Host: ").append(host).append(CRLF);

            final String payload = builder.toString();
            final ByteBuffer byteBuffer = ByteBuffer.wrap(payload.getBytes(StandardCharsets.US_ASCII));
            UndertowLogger.ROOT_LOGGER.proxyAdvertiseMessagePayload(payload);
            channel.sendTo(address, byteBuffer);
        } catch (Exception e) {
            UndertowLogger.ROOT_LOGGER.proxyAdvertiseCannotSendMessage(e, address);
        }
    }

    private void digestString(MessageDigest md, String securityKey) {
        byte[] buf = securityKey.getBytes(StandardCharsets.UTF_8);
        md.update(buf);
    }

    private static final char[] TABLE = "0123456789abcdef".toCharArray();
    static String bytesToHexString(final byte[] bytes) {
        final StringBuilder builder = new StringBuilder(bytes.length * 2);
        for (byte b : bytes) {
            builder.append(TABLE[b >> 4 & 0x0f]).append(TABLE[b & 0x0f]);
        }
        return builder.toString();
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy