Quarkus - Apache CXF - HTTPException - HTTP response 403 ... when comunicating with http

544 Views Asked by At

I'm having this problem when consuming a SOAP webservice from a Quarkus application.

The error I'm getting is:

Caused by: org.apache.cxf.transport.http.HTTPException: HTTP response '403: (POST http://mydevelopserver/XISOAPAdapter/MessageServlet?senderParty=&senderService=hidden01&receiverParty=&receiverService=&interface=SI_SAP_MaestroProveedores_sync_out&interfaceNamespace=hidden2) 403' when communicating with http://mydevelopserver/XISOAPAdapter/MessageServlet?senderParty=&senderService=hidden01&receiverParty=&receiverService=&interface=SI_SAP_MaestroProveedores_sync_out&interfaceNamespace=hidden2

        at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.doProcessResponseCode(HTTPConduit.java:1653)
        at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1660)
        at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1602)
        at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1396)
        at org.apache.cxf.ext.logging.LoggingOutputStream.postClose(LoggingOutputStream.java:53)    at org.apache.cxf.io.CachedOutputStream.close(CachedOutputStream.java:228)
        at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
        at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:696)
        at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:63)
        at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307)   at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:528)    :307)   at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:528)
        at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:439)
        at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:354)
        at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:312)
        at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
        at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:140)
        ... 39 more

I generated the classes from the WSDL with wsdl2java (actually Quarkus does this in the dev phase).

The service has basic authentication, witch I use it in the client:

import java.net.Authenticator;
import java.net.PasswordAuthentication;

import jakarta.enterprise.context.RequestScoped;

@RequestScoped
public class WSDLAuthentication extends Authenticator {
  private String user;
  private String password;

  public WSDLAuthentication() {
    super();

    // Config properties.
    this.user = "secret";
    this.password = "secret";

    Authenticator.setDefault(this);
  }

  @Override
  protected PasswordAuthentication getPasswordAuthentication() {
    return new PasswordAuthentication(this.user, this.password.toCharArray());
  }
}

And before executing the service I instantiate it:

@ApplicationScoped
public class UserDataWebService {
  private Config config;
  private String MANDANTE;

  @CXFClient("maestroProveedoresService")
  private SISAPMaestroProveedoresSyncOut maestroProveedoresService;

  @Inject
  private ZprovsMapper zprovsMapper;

  public UserDataWebService() {
    // WSDL Auth.
    new WSDLAuthentication();
  }

  public UserDataDTO getUserData(String userId) throws CustomException {
    ZFIMAESTROPROV zfimaestroprov = new ZFIMAESTROPROV();
    zfimaestroprov.setPMANDANTE('100');
    zfimaestroprov.setPTAXNUM('123456789');

    try {
      // Excecute SOAP ws.
      ZFIMAESTROPROVResponse zfimaestroprovResponse = maestroProveedoresService
          .siSAPMaestroProveedoresSyncOut(zfimaestroprov);

      Optional<String> errorOptional = Optional.ofNullable(zfimaestroprovResponse.getPERROR());
      switch (errorOptional.orElse(CustomTypes.EMPTY_FIELD.getLabel())) {
        case "E":
          System.err.println(CustomMessagesType.USER_NOT_FOUND.getLabel());
          throw new CustomException(404, CustomMessagesType.USER_NOT_FOUND.getLabel());
        default:
          ZPROVS userDataSAP = zfimaestroprovResponse.getSALIDA();
          UserDataDTO userData = zprovsMapper.zprovsToUserDataDTO(userDataSAP);

          return userData;
      }
    } catch (jakarta.xml.ws.WebServiceException e) {
      e.printStackTrace();
      throw new CustomException(503, CustomMessagesType.SERVER_UNAVAILABLE.getLabel());
    }
  }
}

I have also configured basic authentication in Quarkus properties (in fact, this works for getting the WSDL definition).

When I log the request, I can see the basic authentication on it:

INFO  [org.apa.cxf.ser.SI_.REQ_OUT] (executor-thread-1) REQ_OUT     
    Address: http://mydeveloperservice/XISOAPAdapter/MessageServlet?senderParty=&senderService=hidden&receiverParty=&receiverService=&interface=SI_SAP_MaestroProveedores_sync_out&interfaceNamespace=hidden2
    HttpMethod: POST
    Content-Type: text/xml
    ExchangeId: 1ce8c3ce-036f-4c87-b4d7-e94f2704ca73
    ServiceName: SI_SAP_MaestroProveedores_sync_outService
    PortName: HTTPS_Port
    PortTypeName: SI_SAP_MaestroProveedores_sync_out
    Headers: {Authorization=Basic supperhidden, SOAPAction="http://sap.com/xi/WebService/soap1.1", Accept=*/*}
    Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:ZFI_MAESTRO_PROV xmlns:ns2="urn:sap-com:document:sap:rfc:functions"><P_MANDANTE>100</P_MANDANTE><P_TAXNUM>123456789</P_TAXNUM></ns2:ZFI_MAESTRO_PROV></soap:Body></soap:Envelope>

The service is under HTTP, and with wireshark I can see this traces:

enter image description here

In fact, in the key request, this is the trace:

Hypertext Transfer Protocol
    POST /XISOAPAdapter/MessageServlet?senderParty=&senderService=hidden&receiverParty=&receiverService=&interface=SI_SAP_MaestroProveedores_sync_out&interfaceNamespace=hidden2 HTTP/1.1\r\n
        [ [truncated]Expert Info (Chat/Sequence): POST /XISOAPAdapter/MessageServlet?senderParty=&senderService=hidden&receiverParty=&receiverService=&interface=SI_SAP_MaestroProveedores_sync_out&interfaceNamespace=hidden2]
            [POST /XISOAPAdapter/MessageServlet?senderParty=&senderService=hidden&receiverParty=&receiverService=&interface=SI_SAP_MaestroProveedores_sync_out&interfaceNamespace=hidden2 HTTP/1.1\r\n]
            [Severity level: Chat]
            [Group: Sequence]
        Request Method: POST
        Request URI: /XISOAPAdapter/MessageServlet?senderParty=&senderService=hidden&receiverParty=&receiverService=&interface=SI_SAP_MaestroProveedores_sync_out&interfaceNamespace=hidden2
            Request URI Path: /XISOAPAdapter/MessageServlet
            Request URI Query: senderParty=&senderService=hidden&receiverParty=&receiverService=&interface=SI_SAP_MaestroProveedores_sync_out&interfaceNamespace=hidden2
                Request URI Query Parameter: senderParty=
                Request URI Query Parameter: senderService=hidden
                Request URI Query Parameter: receiverParty=
                Request URI Query Parameter: receiverService=
                Request URI Query Parameter: interface=SI_SAP_MaestroProveedores_sync_out
                Request URI Query Parameter: interfaceNamespace=hidden2
        Request Version: HTTP/1.1
    Connection: Upgrade, HTTP2-Settings\r\n
    Content-Length: 266\r\n
        [Content length: 266]
    Host: mydeveloperserverr\n
    HTTP2-Settings: AAEAAEAAAAIAAAABAAMAAABkAAQBAAAAAAUAAEAA\r\n
    Upgrade: h2c\r\n
    Accept: */*\r\n
    Authorization: Basic superhidden
    Content-Type: text/xml; charset=UTF-8\r\n
    SOAPAction: "http://sap.com/xi/WebService/soap1.1"\r\n
    User-Agent: Apache-CXF/4.0.1\r\n
    \r\n
    [Full request URI [truncated]: http://mydeveloperservice/XISOAPAdapter/MessageServlet?senderParty=&senderService=hidden&receiverParty=&receiverService=&interface=SI_SAP_MaestroProveedores_sync_out&interfaceNamespace=http:]
    [HTTP request 1/1]
    [Response in frame: 468]
    File Data: 266 bytes

In witch I can identify that the client is sending the header Connection: Upgrade, HTTP2-Settings. I think that this is the problem.

Any ways I can make the request without this header?

Stacks:

  • POM:
    <properties>
      <compiler-plugin.version>3.11.0</compiler-plugin.version>
      <maven.compiler.release>17</maven.compiler.release>
      <org.mapstruct.version>1.5.5.Final</org.mapstruct.version>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
      <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
      <quarkus.platform.version>3.1.1.Final</quarkus.platform.version>
      <skipITs>true</skipITs>
      <surefire-plugin.version>3.0.0</surefire-plugin.version>
    </properties>
    <dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>${quarkus.platform.group-id}</groupId>
          <artifactId>${quarkus.platform.artifact-id}</artifactId>
          <version>${quarkus.platform.version}</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
        <dependency>
          <groupId>${quarkus.platform.group-id}</groupId>
          <artifactId>quarkus-cxf-bom</artifactId>
          <version>${quarkus.platform.version}</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
        <dependency>
          <groupId>${quarkus.platform.group-id}</groupId>
          <artifactId>quarkus-camel-bom</artifactId>
          <version>${quarkus.platform.version}</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
      </dependencies>
    </dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>io.quarkiverse.cxf</groupId>
        <artifactId>quarkus-cxf</artifactId>
      </dependency>
      <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-hibernate-orm</artifactId>
      </dependency>
      <dependency>
        <groupId>org.apache.camel.quarkus</groupId>
        <artifactId>camel-quarkus-cxf-soap</artifactId>
      </dependency>
      <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-hibernate-validator</artifactId>
      </dependency>
      <dependency>
        <groupId>org.apache.camel.quarkus</groupId>
        <artifactId>camel-quarkus-soap</artifactId>
      </dependency>
      <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-oidc</artifactId>
      </dependency>
      <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-resteasy</artifactId>
      </dependency>
      <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-jdbc-oracle</artifactId>
      </dependency>
      <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-arc</artifactId>
      </dependency>
      <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-smallrye-health</artifactId>
      </dependency>
      <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-hibernate-orm-panache</artifactId>
      </dependency>
      <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-resteasy-jsonb</artifactId>
      </dependency>
      <dependency>
        <groupId>io.quarkiverse.cxf</groupId>
        <artifactId>quarkus-cxf-rt-ws-security</artifactId>
      </dependency>
      <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-junit5</artifactId>
        <scope>test</scope>
      </dependency>
      <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>rest-assured</artifactId>
        <scope>test</scope>
      </dependency>
      <dependency>
        <groupId>io.quarkiverse.cxf</groupId>
        <artifactId>quarkus-cxf-rt-features-logging</artifactId>
      </dependency>
      <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
        <scope>provided</scope>
      </dependency>
      <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
        <version>${org.mapstruct.version}</version>
        <scope>provided</scope>
      </dependency>
    </dependencies>
    
  • SAP PI/PO 7.5

Note: I did some search in SAP, and the only thing that I found was this note.

Sorry for the long post.

Thanks.

I tried to search how to disable Connection: Upgrade, HTTP2-Settings in Apache CXF, but coudn't find anything.

1

There are 1 best solutions below

0
brunomaso1 On

The main problem is that SAP PO has a bug (reported in the note in the question above). The request is via HTTP 1.1 with the upgrade protocol, and SAP PO is the one that should interpret (accept or dismiss the header) this request, which in this case is not happening and sending a 403 error.

So, a workaround is sending the request via HTTP 1.1 without the upgrade header. Implementing this configuration in a class achieves that:

import io.quarkus.runtime.StartupEvent;
import jakarta.enterprise.event.Observes;

import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transport.http.HTTPConduitConfigurer;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;

public class ConduitConfigurer {

    void onStart(@Observes StartupEvent ev) {
        HTTPConduitConfigurer httpConduitConfigurer = (String name, String address, HTTPConduit c) -> {
            HTTPClientPolicy hTTPClientPolicy = new HTTPClientPolicy();
            hTTPClientPolicy.setVersion("1.1");

            c.setClient(hTTPClientPolicy);
        };

        final Bus bus = BusFactory.getThreadDefaultBus();
        bus.setExtension(httpConduitConfigurer, HTTPConduitConfigurer.class);
    }
}

This configures the CFX client to use HTTP 1.1 and not try to upgrade to HTTP/2 protocol.

For more information check this sources: Quarkus Github issues, CXF issues.