1

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

2 Answers 2

1

Hello jackson you should change esp software. you dont use https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266HTTPUpdateServer/examples because we uploaded server side. you can use https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WebServer/examples/WebUpdate/WebUpdate.ino this example and you will change this part

 server.on("/update", HTTP_POST, [](){
  server.sendHeader("Connection", "close");
  server.sendHeader("Access-Control-Allow-Methods", "GET, POST, PUT");
  server.sendHeader("Access-Control-Allow-Headers","Origin, X-Requested-With, Content-Type, Accept, Authorization");
  server.send(200, "text/plain", (Update.hasError())?"FAIL":"OK");
  ESP.restart();
}

I hope resolve your problem :) enjoy coding

Sign up to request clarification or add additional context in comments.

Comments

0

Try using the "image" name

formData.Add(postContent, "image", "firmware.bin");

1 Comment

I've tried it before, still remains the same actually

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.