I've got an issue that when I run a HTTP POST via HttpClient. I used it upload firmware to ESP8266 Arduino's OTA firmware upgrade interface (see sample: https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266HTTPUpdateServer/examples), but it throws an exception saying
"WinHttpException: The server returned an invalid or unrecognized response"
When I upload the firmware image by cURL, it works totally fine with command below:
curl -F "[email protected]" 192.168.1.104/update
I've tried debugging with Fiddler, it told me I've got 504 Gateway Timeout. But it won't happen if I use cURL.
I guess it might be caused by some bugs inside ESP8266 Firmware OTA update API, and my program didn't generate a header which can "satisfy" the API, so it was somehow rejected. If possible, I would like to ask that is there any workaround for this issue? Thanks in advance!
Here is the cURL's raw result captured by Fiddler:
POST http://192.168.1.104/update HTTP/1.1
Host: 192.168.1.104
User-Agent: curl/7.51.0
Accept: */*
Connection: Keep-Alive
Content-Length: 335869
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------32e3208a349a700d
--------------------------32e3208a349a700d
Content-Disposition: form-data; name="image"; filename="firmware.bin"
Content-Type: application/octet-stream
.......(Firmware File Content).......
Here is my program's raw result:
POST http://192.168.1.104/update HTTP/1.1
Connection: Keep-Alive
Content-Type: multipart/form-data; boundary="----TwilightFirmware"
Accept-Encoding: gzip, deflate
Content-Length: 335857
Host: 192.168.1.104
------TwilightFirmware
Content-Type: application/octet-stream
Content-Disposition: form-data; name=update; filename=firmware.bin; filename*=utf-8''firmware.bin
.............(Firmware file Content)...........
Here is my (part of) C# code,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net.Http;
using System.Diagnostics;
namespace JasmineApp.Core
{
class LocalFirmwareUpdater
{
public async Task<bool> UploadLocalFirmware(string baseUrl, string filePath)
{
Debug.WriteLine("[Core.FirmwareUpdater] Base URL is: " + baseUrl);
HttpCommandHandler httpHandler = new HttpCommandHandler();
httpHandler.SetHttpBaseUrl(baseUrl);
byte[] firmwareContent = File.ReadAllBytes(filePath);
// HttpContent postContent = new StreamContent(firmwareStream);
HttpContent postContent = new ByteArrayContent(firmwareContent);
postContent.Headers.Add("Content-Type", "application/octet-stream");
var formData = new MultipartFormDataContent("----TwilightFirmware");
formData.Add(postContent, "update", "firmware.bin");
string result = await httpHandler.ExecutePostAsync("/update", formData);
// string result = await httpHandler.ExecutePostAsync("/update", postContent);
return (
result.Equals(string.Empty)
|| result == null
|| result.Contains("Fail")) ? false : true;
}
}
}
...and HTTP handler code is here:
public async Task<string> ExecutePostAsync(string pathAndQuery, MultipartFormDataContent content)
{
Debug.WriteLine("[Core.HttpHandler] Request POST URL: " + BaseUrl + pathAndQuery);
using(var client = this.getHttpClient())
{
client.Timeout = TimeSpan.FromSeconds(2000000);
var httpResponseMessage = client.PostAsync(pathAndQuery, content).Result;
if(httpResponseMessage != null)
{
string responseResult = await httpResponseMessage.Content.ReadAsStringAsync();
Debug.WriteLine("[Core.HttpCommandHandler] POST return message: " + httpResponseMessage.Content.ReadAsStringAsync().Result);
return responseResult;
}
else
{
// Return an empty string to avoid exceptions
// Don't need to worrry if it's empty.
return string.Empty;
}
}
By the way, you can have a look for my full code on GitHub if necessary. Here's the link: https://github.com/huming2207/JasmineApp.Windows