org.eclipse.milo.opcua.stack.client.transport.http.OpcClientHttpCodec.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.milo.opcua.stack.client.transport.http.OpcClientHttpCodec.java

Source

/*
 * Copyright (c) 2019 the Eclipse Milo Authors
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 */

package org.eclipse.milo.opcua.stack.client.transport.http;

import java.util.List;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.AttributeKey;
import org.eclipse.milo.opcua.stack.client.UaStackClientConfig;
import org.eclipse.milo.opcua.stack.client.transport.UaTransportRequest;
import org.eclipse.milo.opcua.stack.core.StatusCodes;
import org.eclipse.milo.opcua.stack.core.UaException;
import org.eclipse.milo.opcua.stack.core.serialization.OpcUaBinaryStreamDecoder;
import org.eclipse.milo.opcua.stack.core.serialization.OpcUaBinaryStreamEncoder;
import org.eclipse.milo.opcua.stack.core.serialization.UaResponseMessage;
import org.eclipse.milo.opcua.stack.core.transport.TransportProfile;
import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription;
import org.eclipse.milo.opcua.stack.core.util.EndpointUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OpcClientHttpCodec extends MessageToMessageCodec<HttpResponse, UaTransportRequest> {

    private static final AttributeKey<UaTransportRequest> KEY_PENDING_REQUEST = AttributeKey
            .newInstance("pendingRequest");

    private static final String UABINARY_CONTENT_TYPE = HttpHeaderValues.APPLICATION_OCTET_STREAM.toString();

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final EndpointDescription endpoint;
    private final TransportProfile transportProfile;

    private final UaStackClientConfig config;

    OpcClientHttpCodec(UaStackClientConfig config) {
        this.config = config;

        endpoint = config.getEndpoint();
        transportProfile = TransportProfile.fromUri(endpoint.getTransportProfileUri());
    }

    @Override
    protected void encode(ChannelHandlerContext ctx, UaTransportRequest transportRequest, List<Object> out)
            throws Exception {

        logger.debug("encoding: " + transportRequest.getRequest());

        ctx.channel().attr(KEY_PENDING_REQUEST).set(transportRequest);

        ByteBuf content = Unpooled.buffer();

        switch (transportProfile) {
        case HTTPS_UABINARY: {
            OpcUaBinaryStreamEncoder encoder = new OpcUaBinaryStreamEncoder(content);
            encoder.writeMessage(null, transportRequest.getRequest());
            break;
        }

        case HTTPS_UAXML: {
            // TODO put document into a SOAP message.
            throw new UaException(StatusCodes.Bad_InternalError, "no encoder for transport: " + transportProfile);
        }

        default:
            throw new UaException(StatusCodes.Bad_InternalError, "no encoder for transport: " + transportProfile);
        }

        String endpointUrl = endpoint.getEndpointUrl();

        FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST,
                EndpointUtil.getPath(endpointUrl), content);

        httpRequest.headers().set(HttpHeaderNames.HOST, EndpointUtil.getHost(endpointUrl));
        httpRequest.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        httpRequest.headers().set(HttpHeaderNames.CONTENT_TYPE, UABINARY_CONTENT_TYPE);
        httpRequest.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
        httpRequest.headers().set("OPCUA-SecurityPolicy", config.getEndpoint().getSecurityPolicyUri());

        out.add(httpRequest);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, HttpResponse httpResponse, List<Object> out) throws Exception {

        logger.trace("channelRead0: " + httpResponse);

        UaTransportRequest transportRequest = ctx.channel().attr(KEY_PENDING_REQUEST).getAndSet(null);

        if (httpResponse instanceof FullHttpResponse) {
            String contentType = httpResponse.headers().get(HttpHeaderNames.CONTENT_TYPE);

            FullHttpResponse fullHttpResponse = (FullHttpResponse) httpResponse;
            ByteBuf content = fullHttpResponse.content();

            UaResponseMessage responseMessage;

            switch (transportProfile) {
            case HTTPS_UABINARY: {
                if (!UABINARY_CONTENT_TYPE.equalsIgnoreCase(contentType)) {
                    throw new UaException(StatusCodes.Bad_DecodingError, "unexpected content-type: " + contentType);
                }

                OpcUaBinaryStreamDecoder decoder = new OpcUaBinaryStreamDecoder(content);
                responseMessage = (UaResponseMessage) decoder.readMessage(null);
                break;
            }

            case HTTPS_UAXML: {
                // TODO extract document from SOAP message body
                throw new UaException(StatusCodes.Bad_InternalError,
                        "no decoder for transport: " + transportProfile);
            }

            default:
                throw new UaException(StatusCodes.Bad_InternalError,
                        "no decoder for transport: " + transportProfile);
            }

            transportRequest.getFuture().complete(responseMessage);
        } else {
            HttpResponseStatus status = httpResponse.status();

            if (status.equals(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE)) {
                transportRequest.getFuture()
                        .completeExceptionally(new UaException(StatusCodes.Bad_ResponseTooLarge));
            } else {
                transportRequest.getFuture().completeExceptionally(new UaException(StatusCodes.Bad_UnexpectedError,
                        String.format("%s: %s", status.code(), status.reasonPhrase())));
            }
        }
    }

}