AsHttp -> httpClient request to api returns "Bad Request"

Hi, as said in the title, I’m trying to make an API request to a local server, but it’s returning “Bad Request”.

I have tried the API request using curl and connecting to the server via my laptop, and this works fine.

Below is the curl command that works:

image

Below is the code that I’m using to send the request:

VAR
	httpClientInst : httpClient;
	response : STRING[1000];
	loginBody : STRING[255];
	stats : httpStatistics_t;
	requestHeader : httpRequestHeader_t;
END_VAR


PROGRAM _INIT
	loginBody := '{"name": "admin", "password": "781b9c2264383b453b40fdbf8a7e"}';
	
	requestHeader.contentType := 'application/json';
	requestHeader.protocol :='HTTP/1.1';
	requestHeader.host := '192.168.3.11';
	requestHeader.contentLength := SIZEOF(loginBody);
	
	httpClientInst.enable := TRUE;
	httpClientInst.pHost := ADR('192.168.3.11');
	httpClientInst.pResponseData := ADR(response);
	httpClientInst.responseDataSize := SIZEOF(response);
	httpClientInst.pRequestData := ADR(loginBody);
	httpClientInst.requestDataLen := SIZEOF(loginBody);
	httpClientInst.pRequestHeader := ADR(requestHeader); 
	httpClientInst.method := httpMETHOD_POST;
	httpClientInst.option := httpOPTION_HTTP_11;
	httpClientInst.pUri := ADR('/api/login');
	httpClientInst.pStatistics := ADR(stats);
END_PROGRAM

PROGRAM _CYCLIC
	httpClientInst();
END_PROGRAM

So at first glance everything looks the same as the curl request, but when sent with httpClient it’s not working for some reason.

Am I missing something?

Is there some hidden syntax that I need to use for the body of the request, e.g. in CMD a JSON payload is wrapped in escaped double quotes (\") so it’s parsed correctly.

Or maybe there is a way to see the complete POST packet before it’s sent out the “door”, without running it through a proxy and using Wireshark?

1 Like

Hi,

I’m not sure but I could imagine that the API is missing some request header information that is set automatically when using curl, like User-Agent or Referer.

As concrete example why I’m thinking about that, please see the following post:

To see all header information, you could use the rawHeader element of type httpRawHeader_t, which is an element of the request and response header structure.
Some more infos about header and raw header you can find in this conversation:

But to see the whole http conversation, I would propose to use wireshark.

Best regards.

Hi,

You can view the complete communication using API testing tools (I prefer Postman), including request and response bodies and headers, and adapt your request accordingly.

From my experience, the authentication data is typically part of the request header (it’s better to use the rawHeader instead of the user field), and it fully depends on the type of authorization (e.g., Basic or Digest).

Regards

Michal Malek

Hi Alexander,

Thank you for your response, i have sniffed the API request made with curl and indeed there is an extra header.

i added this to the header via the user line and via the added raw header, both without success.

i can’t see the conversation via Wireshark because the API is run on a second device on the same network.

Hi Michal,

Thank you for your reaction.

Postman cant be used locally i think, however i have access to the swagger page of the api, the swagger page gives me this curl command:

curl -X 'POST' \
  'http://192.168.3.11/api/login' \
  -H 'accept: text/plain' \
  -H 'Content-Type: application/json' \
  -d '{
  "password": "781b9c2264383b453b40fdbf8a7e",
  "name": "admin"
}'

As you can see there is no authentication, this route is supposed to retrieve the api key for the authentication.

Hi,

okay, thanks for the update.
What service is behind this api, is there any documentation available online?

In the swagger page, the accept header of the request is set to text/plain, have you tried to set this also, or maybe for test, accept: / could be also fine?

About sniffing your request: for testing, you could use the PLC simulation on your Automation Studio notebook, then you should be able to see request and response in your wireshark installation?

Best regards!

I fixed it!

The problem was that I was calling the httpClientInst.requestDataLen := SIZEOF(x), when it should be brsstrlen(ADR(x))

The content length was 256 instead of the actual USED content length(61 in my case), therefore asHttp function would put many “********” after the body to fill it to the content length of 256, which the API of course did not accept as a valid body.

Below this is the final code that works for me if any time travelers in the future need it:

VAR
	httpClientInst : httpClient;
	response : ARRAY[0..10] OF STRING[256];
	loginBody : STRING[255];
	stats : httpStatistics_t;
	requestHeader : httpRequestHeader_t;
	requestData : STRING[500];
	rawRequestHeader : httpRawHeader_t;
END_VAR
PROGRAM _INIT
	loginBody := '{"name": "admin", "password": "781b9c2264383b453b40fdbf8a7e"}';

	rawRequestHeader.pData := ADR('User-Agent: curl/8.14.1\r\n');
	rawRequestHeader.dataSize := SIZEOF('User-Agent: curl/8.14.1\r\n');
	rawRequestHeader.dataLen := brsstrlen(ADR('User-Agent: curl/8.14.1\r\n'));
	
	requestHeader.contentType := 'application/json';
	requestHeader.protocol :='HTTP/1.1';
	requestHeader.host := '192.168.3.11';
	requestHeader.contentLength := brsstrlen(ADR(loginBody));
	requestHeader.rawHeader := rawRequestHeader;
	requestHeader.keepAlive := 'timout=5, max=100';
	requestHeader.connection := 'Close';
	
	httpClientInst.enable := TRUE;
	httpClientInst.pHost := ADR('192.168.3.11');
	httpClientInst.pResponseData := ADR(response);
	httpClientInst.responseDataSize := SIZEOF(response);
	httpClientInst.pRequestData := ADR(loginBody);
	httpClientInst.requestDataLen := brsstrlen(ADR(loginBody));
	httpClientInst.pRequestHeader := ADR(requestHeader); 
	httpClientInst.method := httpMETHOD_POST;
	httpClientInst.option := httpOPTION_HTTP_11;
	httpClientInst.pUri := ADR('/api/login');
	httpClientInst.pStatistics := ADR(stats);
END_PROGRAM
2 Likes