HttpUtil call with PUT ends up in a forbidden response

Hello,

I wanted to update my binding and wanted to add new functionality. The new functionality requires a “PUT” http call. For some reasons I receive always a “403 forbidden message” as repsonse of the call. I do know that my authentication token in the header is correct, as authentication is also required in the GET call which works as expected. Here are my code snippets. Anyone having an idea:

public String startStopBatteryCharging(String putData) {
        String result = "";
        String urlStr = "http://" + config.hostIP + "/api/v2/configurations";
        String urlStr2 = "http://" + config.hostIP + "/api/v2/setpoint/charge/"
                + Integer.toString(config.chargingPower);
        Properties httpHeader = new Properties();
        httpHeader = createHeader(config.authToken);
        try {
            // in putData there is 1 or 2 inside to turn on or off the manual mode of the battery
            // it will be executed by a change of the switch an either turn on or off the manual mode of the battery
            InputStream targetStream = new ByteArrayInputStream(putData.getBytes(StandardCharsets.UTF_8));
            String response = HttpUtil.executeUrl("PUT", urlStr, httpHeader, targetStream, "application/json", 10000);
            logger.debug("ChargingOperationMode = {}", response);
            if (response == null) {
                throw new IOException("HttpUtil.executeUrl returned null");
            }
            batteryData = gson.fromJson(response, SonnenJsonDataDTO.class);
            // if battery is put to manual mode
            if (config.chargingPower > 10000) {
                throw new IllegalArgumentException(
                        "Max battery charging power in watt needs to be in the range of greater 0 and smaller 10000.");
            }
            if (getBatteryData().getBatteryChargingLevel() == 1 && config.chargingPower > 0
                    && config.chargingPower <= 10000) {
                // start charging
                String response2 = ""; // = HttpUtil.executeUrl("POST", urlStr2, httpHeader, null, "application/json",
                                       // 10000);
                logger.debug("ChargingOperationMode = {}", response2);
                if (response2 == null) {
                    throw new IOException("HttpUtil.executeUrl returned null");
                }
            }
        } catch (IOException | JsonSyntaxException | IllegalArgumentException e) {
            logger.debug("Error processiong Put request {}:  {}", urlStr, e.getMessage());
            String message = e.getMessage();
            if (message != null && message.contains("WWW-Authenticate header")) {
                result = "Given token: " + config.authToken + " is not valid.";
            } else if (e.getCause() instanceof IllegalArgumentException) {
                result = "Max battery charging power needs to be in the range of greater 0 and smaller 10000. It cannot be: "
                        + config.chargingPower;
                logger.debug("Error in value for battery capacity: {}", e.getMessage());
            } else {
                result = "Cannot find service on given IP " + config.hostIP + ". Please verify the IP address!";
                logger.debug("Error in establishing connection: {}", e.getMessage());
            }
            batteryData = null;
        }
        return result;

content of the putData variable is created like this:

public void handleCommand(ChannelUID channelUID, Command command) {
        if (command == RefreshType.REFRESH) {
            updateBatteryData();
            updateChannel(channelUID.getId(), null);
        }
        if (channelUID.getId().equals(CHANNELBATTERYCHARGINGGRID)) {
            String putData = null;
            if (command.equals(OnOffType.ON)) {
                // Set battery to manual mode with 1
                putData = "{\"EM_OperationgMode\": \"1\"}";
            } else if (command.equals(OnOffType.OFF)) {
                // set battery to automatic mode with 2
                putData = "{\"EM_OperationgMode\": \"2\"}";
            }
            if (putData != null) {
                logger.debug("Executing {} command", CHANNELBATTERYCHARGINGGRID);
                updateChannel(channelUID.getId(), putData);
            }
        }
    }

Header is created like this:

 private Properties createHeader(String authToken) {
        Properties httpHeader = new Properties();
        httpHeader.setProperty("Host", config.hostIP);
        httpHeader.setProperty("Accept", "*/*");
        httpHeader.setProperty("Proxy-Connection", "keep-alive");
        httpHeader.setProperty("Auth-Token", authToken);
        httpHeader.setProperty("Accept-Encoding", "gzip;q=1.0, compress;q=0.5");
        return httpHeader;
    }

The appropriate curl call which works fine looks like this:

curl -X PUT -d EM_OperatingMode=1 --header 'Auth-Token: <APIToken>' http://<IP>/api/v2/configurations

The main difference I see is that in the curl request the data is in www-form format, but in the code you send it as json. If the api was following Convention this should be a 400 Bad request response though, but you never know…

Would need to see the actual requests that are sent (e.g. via a tcpdump) to compare and make sure they are exactly the same.

1 Like

Hello,

thanks for the hint. I think you are right. In the api the request body schema is: application/x-www-form-urlencoded.

So I will try to change the PUT request and see if this helps. I wasn’t sure if I may misuse the HttpUtil.executeUrl call incorrectly.

I will give it a try.

Thanks

Hello,

i can confirm this was that looking with Wiresharks to packages sent I was able to identify the root cause which was indeed a typo in the content which I send.

Thanks for the help.

1 Like