Tiny web server, Firefox compatibility issue

Discuss how to use and promote Web standards with the Mozilla Gecko engine.
Posts: 4
Joined: August 2nd, 2016, 12:48 am

Post Posted August 2nd, 2016, 1:23 am

Hello folks, good $time!

I'm writing a tiny web server running from an microcontroller.
I'm having issues serving pages on not standard ports (e.g. 8080, or whatever).
When the server is running normally at port 80, no problems at all but if I change it the page wont load, instead, firefox screen show the actual page's code but no rendering any elements just page´s code text.

I'm confused because:
1- Internet Explorer opens the page on same situation (page hosted on the tiny web server, alternate port)
2- Chrome also opens it (using the mobile version | didnt tested on desktop)
2- If I host the same page on Apache, on some alternate port on localhost, firefox opens it normally from e.g 'localhost:8080'

Page is hosted on the LAN (e.g., I've tried '' on address bar also '' but no deal.

I fear this is probably not a Firefox related issue but it works on other browsers.
Maybe I'm missing specs Firefox is not relaxed for? Like, expecting a server's signature/description I'm not sending? Anyway.. Stuff do work if using the standard port 80 so I'm lost about it.
Any clue or direction to make this Firefox compatible is greatly appreciated.
Testing this on Firefox 47.0.1 - Windows 7 64bit

The example page code I'm using:

Code: Select all
function setcookie() {
var status = "none";
document.cookie = "SID=; expires=1970";
if(status=="checked") {
   document.cookie = "SID=947";
else if(status=="wrong") { alert("Senha incorreta");
<!DOCTYPE html>
<meta charset="iso-8859-1">
<title>testing page</title>
<body onload="setcookie()"; style="background-color: #dbe7aa">

<div id="lgs" class="ma">

<div id="ls" class="co">
<div id="lc" align="center"><img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAoHBwgHBgoICAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9Pjv/2wBDAQoLCw4NDhwQEBw7KCIoOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozv/wAARCABdAFADASIAAhEBAxEB/8QAGgABAQEBAQEBAAAAAAAAAAAAAAYHBQQDAf/EADcQAAEDAwEFBQYDCQAAAAAAAAEAAgMEBRFBBhIhMWETFFGB0SIjQpGhwRVysSQlMjNDUmNx4v/EABkBAQEAAwEAAAAAAAAAAAAAAAABAgMEBf/EABwRAQEBAQEAAwEAAAAAAAAAAAABAhEDEiEiQv/aAAwDAQACEQMRAD8Au0RF5iCIiApO7bZvoLtJTU9PHNFFhriSQS7XC7l9ubbTaZqnI7TG5EDq88vXyWWOc5zi5xLnOOSTqfFdHj5zX3VXdNt5QPwKimmhPi3Dh9lR01THWUzKiEkxyDLS5pBx/orO9mLGbvXb8oPdYSDIf7jo1aSAGgBoAAGAByCx9c5zeRBERaQREQERc++XNtptUtTwMmN2Jp1ceXr5KydvII7bO6d8ugpI3ZipeHA8C48/RcSgoprjWx0lO3L5DjoBqT0C+BLnvJJLnOOSTzJWjbLWL8Jo+2nb+1Tj2882N0b6rt1Z54V07db4bZQx0kA9lg4nVx1K9SIuG3v2giIgIiICz7bO698uYpI3ZipeBxq88/RWN8uYtNqlquBkxuxA6uPL18lljnOe8ue4uc45JPMnxXT4Z/pVVsbYu8TC6VLPdRn3LT8TvHyVNXbR2q35bNVtdIP6cXtu+nLzwomCm2hvMLIomTmma0Bo/lxAfQH6rw3W1SWipbTTSRvl3A5wj5NzpkrO4m9fqijrNvJHncoKMDPAOlOT8gqu3Nqm0EXfZN+oc3ek4YwTpjooPY+1d/u3byNzDS4ec6u+Effy6rRVp9ZnP5iCIi0giIg5V3sMd6mhNTUSNgiziJnDJPMkr60VhtdBgwUce8PjeN53zK6CLL5a5zo/HvDWl7jgAZJKye5Vjrjc56rn2ryWjpoFfbX1/crFIxpxJUnsm9Af4j8uHmFLbHWrv91FTI3MNL7Rzq/4R9/IeK3+P5zdVVjs/bBarRFARiVw35fzHTy5Lpoi57e3tQREUBERAXkr7pS2zsTVv7Nkz9wP0aevgOq9aldvj+76Qf5T+izxma1JRydtbgKy7R00Tg9kDBjByC53H0VdYLWLTaYqcjErhvy/mPpyWZUs0lNVRTQgGSNwcwFu8M6cNVqNofcpKFslzEbZn8QxjcFo69Vu9Z8cyRXuREXMgiIgIiIClNvQ59LQsaCXOldgAczgKrXzkp4ZZoppIw58OTGT8JPM/RZY18ddE9szsu23tbW1zA6qPFjDxEX/AF+ipURNaur2giIsQREQf//Z" width="80" height="120"
alt="logo" /><br><br><span style="color: #090"><strong><em>Page</em></strong></span>
<div id="lr" class="co">

<div id="bt" align="center"><span style="color: #090"><strong>Login:</strong></span>
<form action="logi" method="post" id="bts" enctype="application/x-www-form-urlencoded">
<div id="bt2" align="left" ><strong>Senha:</strong>
  <input type="password" name="password" id="s1">
  <input type="checkbox" id="ch" onclick="chk()">
  <label for="checkbox">Mostrar </label>
 <input type="hidden" name="h" value="ÿ" maxlength="1" >

<div id="bt3" align="center" >
  <input type="button" name="button" id="button" value="Entrar" onclick="validsession();">

function chk() {
var a=document.getElementById("ch");
var b=document.getElementById("s1");
if(a.checked)  b.type = "text"; else  b.type = "password";


function validsession() {



   float: left;
   height: 30px;
   width: 95%;   
   //border: 1px solid #879E2E;
   padding-top: 15px;
   padding-left: 15px;
   float: left;
   height: 50px;
   width: 80%;   
   //border: 1px solid #879E2E;
   padding-top: 27px;
   padding-left: 45px;
   float: left;
   height: 25px;
   width: 99%;   
   //border: 1px solid #879E2E;
.ma {
   font-family: Verdana, Geneva, sans-serif;
   float: left;
width: 33%;   
height: 190px;
//border: 1px solid #879E2E;      
#lc {
height: 175px;
width: 32%;   
float: left;
padding-top: 15px;
//border: 1px solid #879E2E;   

#lgs {

overflow: auto;
   height: 370px;
   width: 400px;
 position: fixed;
  border-radius: 5px;
 top: 50%;
 left: 50%;
  border: 1px solid #879E2E;
 transform: translate(-50%, -50%);      



User avatar
Posts: 123522
Joined: September 23rd, 2004, 8:57 pm
Location: Somewhere on the right coast

Post Posted August 2nd, 2016, 5:18 am

Moving to Web Development.

Posts: 270
Joined: January 20th, 2015, 12:29 pm

Post Posted August 2nd, 2016, 7:08 am

It sounds like the server might be giving the wrong Content-Type for the response (or no Content-Type at all).
You can check this in devtools: go to the Network tab, find the request/response for the page in the list, then look under Headers > Response headers > Content-Type.


Posts: 6
Joined: July 3rd, 2016, 2:21 pm

Post Posted August 2nd, 2016, 9:21 am

I have also a tiny just-barely-working httpd on a custom port. As few server response lines as possible, probably breaking standard practices (it's just sudoku, not anything serious).
Firefox/47 does render the html ok.
Do you close the connection after every response ?

tcpdump shows (IP and TCP stuff deleted)
Code: Select all
19:01:44.231254 IP localhost.31488 > localhost.gps
GET /location HTTP/1.1
Host: localhost:3001
User-Agent: Mozilla/5.0 (X11; FreeBSD i386; rv:47.0) Gecko/20100101 Firefox/47.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cache-Control: max-age=0

19:01:44.231294 IP localhost.gps > localhost.31488
HTTP/1.1 200 OK
content-type: text/html
content-length: 17



Posts: 4
Joined: August 2nd, 2016, 12:48 am

Post Posted August 2nd, 2016, 11:11 pm

Thanks DanRaisch for moving this into correct place.
Thanks oh5nxo for the example.

Thanks libertyernie. You were right! It ends that... I was not sending a Header at all! ](*,) It was just a matter of inserting the missing header data and it started working despite of alternate ports. Now I think I can be a bit _less_ pretentious about calling it a "web server", just a step forward.
I'm not experienced and I really thought everything need by browsers were information included in <head> and meta tags.

Leaving here some references for google juice, to help other people searching for the same "problem" I had: ... --net-8039


Posts: 6
Joined: July 3rd, 2016, 2:21 pm

Post Posted August 3rd, 2016, 8:40 am

There's a really nice old algorithm you might find useful if you ever have to create Date: or similar headers.
If you have limited time-handling on your uC libraries, or whatever. Dates must contain the (IMHO pointless) weekday name.


Code: Select all
/* Posted by Tomohiko Sakamoto on the comp.lang.c Usenet newsgroup in 1993,
 * it is accurate for any Gregorian date,
 * returns 0, 1. ... for Sun, Mon, ... */

static int
dayofweek(int y, int m, int d) /* 1 <= m <= 12,  y > 1752 (in the U.K.) */
        static int t[] = { 0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4 };

        y -= m < 3;

        return ((y + y/4 - y/100 + y/400 + t[m-1] + d) % 7);

Posts: 4
Joined: August 2nd, 2016, 12:48 am

Post Posted August 3rd, 2016, 12:37 pm

Hello Juha,

Many thanks. It will be useful for sure. I will need this type of conversion for other purposes as well.
I'm wondering about not sending the 'Date:' field but I'm unsure how bad is not sending it, that's something I must research.

As I'm unsure how to deal with the more modern Chunked transfer encoding, also not sure if I can even do it with the available TCP/IP stack, I ended with this static header that's working, at least for now:

Code: Select all
  HTTP/1.0 200 OK
      Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
      Connection: close
      Content-Type: text/html; charset=iso-8859-1
   //   Keep-Alive: timeout=5, max=100 --> not used. Not meaningful against 'Connection: close' ?
      Pragma: no-cache
      Server: EnerControl_TinnyWeb/1.0 (Havard RISC)
      X-Powered-By: Atmel/Microchip

What is happening here is: Atmega328, ESP01(ESP8266) for wifi, and mixed avr-gcc/arduino libraries on AtmelStudio. This is the actual mess:

As for time: Purpose is getting the most cost effective solution for a non-critical appliance. So not inserting an RTC to this. Instead, getting epoch from NTP. I leave here the code Im using in case you find it useful. I managed to change to "local" strings in the above example so you can understand what's being sent, it´s going originally from PROGMEM so I hope theres not any typo in those transcriptions. Function ECSP is just sending stuff to ESP over hardware serial. timeset function and timecontrol variable is just a timer interrupt based delay to avoid getting stuck inside while in case of unsuccessful connections. This is not taking into account network delays correction and other NTP details for the most accurate timing but useful already for some objectives. Quite long code... Im sure it can be optimized but it show the principle.

Code: Select all
unsigned long getEpoch() {
byte ntpData[48];
memset(ntpData, 0, 48); // Construir estes a partir da EEPROM, testar se economiza espaco de FLASH
  ntpData[0] = 227;   // LI, Version, Mode
  ntpData[1] = 0;     // Stratum, or type of clock
  ntpData[2] = 6;     // Polling Interval
  ntpData[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  ntpData[12]  = 49;
  ntpData[13]  = 78;
  ntpData[14]  = 49;
  ntpData[15]  = 52;
String CIP = "AT+CIPSTART=0,\"UDP\",\"\",123\r\n";

ECSP(CIP, 3000, false,"");
ECSP(CR("AT+CIPSEND=0,48"), 3000, false,">");
String query;

for(byte fa=0;fa<48;fa++) query+=(char)ntpData[fa];
while(timeoutcontrol) {
  if(Serial.find(":")) timeoutcontrol=0;
byte bb[4];

byte counter=0;
byte bcount=0;
while(timeoutcontrol) {
while(Serial.available())    { 
byte c =;
if(counter>39 && counter<44) { bb[bcount] = c; bcount++; } // Get just epoch bytes from response
if(counter==47) timeoutcontrol=0;



unsigned long secsSince1900;
secsSince1900 =  (unsigned long)bb[0] << 24;
secsSince1900 |= (unsigned long)bb[1] << 16;
secsSince1900 |= (unsigned long)bb[2] << 8;
secsSince1900 |= (unsigned long)bb[3];

unsigned long epoch = secsSince1900 - 2208988800UL;
  return epoch; }
  else return 0;


Posts: 6
Joined: July 3rd, 2016, 2:21 pm

Post Posted August 4th, 2016, 6:39 am

Ah, ok. I thought that you might have your hw clock as separate year, m, d, h, m and seconds.

Chunked transfers are no more complicated than the traditional way. You just add %x\r\n (size of the piece) before and \r\n after each piece of data. 0\r\n\r\n after last piece.

The wifi-module looks nice, btw.

Posts: 4
Joined: August 2nd, 2016, 12:48 am

Post Posted August 4th, 2016, 1:12 pm

Thanks I will try it.

Yeah the module is nice, it's being very popular among makers. The chip is quite impressive, a 32 bit RISC with TCP/IP/Wireless stack packed small. It's from a relatively new Chinese company, support still so so... stuf like endurance, long term reliability are still unknown I think. Consumption average is around 200mAh but it seems that really huge current spikes around average is the normal operation, probably from TX bursts. Definitely needs a good tank capacitor and noise filtering around it.


Return to Web Development / Standards Evangelism

Who is online

Users browsing this forum: No registered users and 1 guest