Search code examples
httpraspberry-pitclwifiraspbian

Tcl http POST - payload gets separated from headers when sent over wlan0 on rPi


I am using ::http::geturl -query to issue an HTTP POST request with a small json payload to an ESP8266 (a 3rd party commercial device) from a rPi. It works when sent over eth0 but fails when sent over wlan0. tcpdump shows that sent over eth0, the message is sent as a single packet but when sent over wlan0 the payload is being split from the headers and sent in a second packet. The ESP8266 most likely due to having overly simple implementation of its packet receivers and/or http server doesn't appear to handle this splitting. It issues a 200 OK response after receiving the packet containing the headers and doesn't process the payload part of the request.

Experimentally I composed the same request message text being sent by ::http::geturl and sent it over wlan0 using nc; it was sent as a single packet and was successfully processed by the ESP8266.

Does anyone happen to know why sending the request using ::http over wlan0 is ending up with this split message, and what if anything can be done to prevent it?

Code fragment:

set s [::http::geturl http://$ip/con?com=cli -query $data -type application/json]
set r [::http::ncode $s]
::http::cleanup $s

Raspbian package versions:

tcl8.6 8.6.9+dfsg-2
tcllib 1.19-dfsg-2

tcl_platform(engine)        = Tcl
tcl_platform(machine)       = armv7l
tcl_platform(os)            = Linux
tcl_platform(osVersion)     = 5.4.79-v7+
$ ifconfig wlan0
wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
inet 192.168.0.101  netmask 255.255.255.0  broadcast 192.168.0.255
inet6 fe80::ed38:71ab:13af:ae30  prefixlen 64  scopeid 0x20<link>
ether b8:27:eb:26:bf:94  txqueuelen 1000  (Ethernet)

From /proc/cpuinfo:

Hardware    : BCM2835
Revision    : a020d3
Model       : Raspberry Pi 3 Model B Plus Rev 1.3
$ uname -a
Linux raspberrypi 5.4.79-v7+ #1373 SMP Mon Nov 23 13:22:33 GMT 2020 armv7l GNU/Linux

Solution

  • Tcl's http package flushes the headers to the socket (i.e., performs an actual write()/send()) between writing the headers and the body of a query. For any correct implementation of an HTTP server this is fine… but you're not working with that. For some reason, the wlan and eth drivers in the OS kernel have different policies for what to do with that case, with the eth driver deciding to wait a bit before sending; Tcl definitely doesn't configure this aspect of sockets at all, staying with the system defaults. (I don't know how to configure the OS defaults.)

    You can always take a copy of the http code and comment out the flush. It's this one:

    There's a Download button/link at the top of the page for that exact version of that file (it's changed only very slightly from the one in your version of Tcl and should be compatible provided you source the file explicitly before doing any package require calls).