Related
I am attempting to send a POST request to an arduino from a PHP script that is called by a web page. I am using file_get_contents() to accomplish this. I have read the examples from the PHP manual here: Here. It appears that the Arduino is receiving the POST request but there is no content included in the data stream, only headers and content parameters. Here is my relevant php
// send database updates to the arduino
$url = 'http://'.$ip;
$options = array(
'http' => array(
'header' => "Content-type: application/x-www/form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($responseText)
),
);
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
currently on the Arduino I am just dumping the entire POST request to the terminal so that I can verify that it is being received. Here is the output of the Arduino terminal.
Ethernet WebServer Example
server is at 192.168.3.3
new client
POST / HTTP/1.0
Host: 192.168.3.3
Connection: close
Content-Length: 80
Content-type: application/x-www/form-urlencoded
client disconnected
As you can see the entire POST request is there accept the content. It shows the Content-Length but no actual content.
Here is the Arduino code that produced the terminal output. It is nothing more than the Web Server example that comes with the Arduino IDE.
/*
Web Server
A simple web server that shows the value of the analog input pins.
using an Arduino Wiznet Ethernet shield.
Circuit:
* Ethernet shield attached to pins 10, 11, 12, 13
* Analog inputs attached to pins A0 through A5 (optional)
created 18 Dec 2009
by David A. Mellis
modified 9 Apr 2012
by Tom Igoe
modified 02 Sept 2015
by Arturo Guadalupi
*/
#include <SPI.h>
#include <Ethernet.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 3, 3);
// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);
String postData;
void setup() {
// You can use Ethernet.init(pin) to configure the CS pin
Ethernet.init(10); // Most Arduino shields
//Ethernet.init(5); // MKR ETH shield
//Ethernet.init(0); // Teensy 2.0
//Ethernet.init(20); // Teensy++ 2.0
//Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet
//Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("Ethernet WebServer Example");
// start the Ethernet connection and the server:
Ethernet.begin(mac, ip);
// Check for Ethernet hardware present
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
while (true) {
delay(1); // do nothing, no point running without Ethernet hardware
}
}
if (Ethernet.linkStatus() == LinkOFF) {
Serial.println("Ethernet cable is not connected.");
}
// start the server
server.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
}
void loop() {
// listen for incoming clients
EthernetClient client = server.available();
if (client) {
Serial.println("new client");
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char c = client.read();
//postData = postData + c;
Serial.write(c);
// if you've gotten to the end of the line (received a newline
// character) and the line is blank, the http request has ended,
// so you can send a reply
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close"); // the connection will be closed after completion of the response
//client.println("Refresh: 5"); // refresh the page automatically every 5 sec
client.println();
/*client.println("<!DOCTYPE HTML>");
client.println("<html>");
// output the value of each analog input pin
for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
int sensorReading = analogRead(analogChannel);
client.print("analog input ");
client.print(analogChannel);
client.print(" is ");
client.print(sensorReading);
client.println("<br />");
}
client.println("</html>");*/
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
// give the web browser time to receive the data
delay(1);
Serial.println("client disconnected");
}
}
What is causing the content of the POST request to not be sent from the PHP?
After I let my brain rest for about half a day I determined the cause of my problem. It was not in the PHP at all. It was being caused by this IF block in the Arduino code
if (c == '\n' && currentLineIsBlank) {
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
//client.println("Refresh: 5");
client.println();
/*client.println("<!DOCTYPE HTML>");
client.println("<html>");
// output the value of each analog input pin
for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
int sensorReading = analogRead(analogChannel);
client.print("analog input ");
client.print(analogChannel);
client.print(" is ");
client.print(sensorReading);
client.println("<br />");
}
client.println("</html>");*/
break;
}
it was causing while loop that it is nested in to terminate at the blank line between the header and the content. My new working code for the Arduino is
#include <SPI.h>
#include <Ethernet.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 3, 3);
// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);
String postData;
void setup() {
// You can use Ethernet.init(pin) to configure the CS pin
Ethernet.init(10); // Most Arduino shields
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.println("Ethernet WebServer Example");
// start the Ethernet connection and the server:
Ethernet.begin(mac, ip);
// Check for Ethernet hardware present
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
while (true) {
delay(1); // do nothing, no point running without Ethernet hardware
}
}
if (Ethernet.linkStatus() == LinkOFF) {
Serial.println("Ethernet cable is not connected.");
}
// start the server
server.begin();
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
}
void loop() {
// listen for incoming clients
EthernetClient client = server.available();
if (client) {
Serial.println("new client");
while (client.connected()) {
while (client.available()) {
char c = client.read();
postData = postData + c;
// send a standard http response header
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");
client.println();
}
client.stop();
}
// give the web browser time to receive the data
delay(1);
Serial.println("\r\nclient disconnected");
Serial.println(postData);
Serial.println(postData.substring(postData.indexOf("status=")+7, postData.indexOf("status=")+8 ));
}
}
I'm trying to post a insert on a MySQL database using idHTTP and a PHP script. This is the PHP script to insert in the database:
$mysqli = new mysqli($servidor, $usuario, $senha, $banco);
// Caso algo tenha dado errado, exibe uma mensagem de erro
if (mysqli_connect_errno()) trigger_error(mysqli_connect_error());
$iduser = quoted_printable_decode($_POST['iduser']);
$nome = quoted_printable_decode($_POST['nome']);
$data = quoted_printable_decode($_POST['data']);
$hora = quoted_printable_decode($_POST['hora']);
$mensagem = quoted_printable_decode($_POST['mensagem']);
$latitude = quoted_printable_decode($_POST['latitude']);
$longitude = quoted_printable_decode($_POST['longitude']);
$imagem = $_FILES["imagem"]['tmp_name'];
$tamanho = $_FILES['imagem']['size'];
header($_SERVER["SERVER_PROTOCOL"] . " 200 OK");
header('Content-Type: text/plain; charset="utf-8"');
if ( $imagem != "none" )
{
$fp = fopen($imagem, "rb");
$conteudo = fread($fp, $tamanho);
$conteudo = addslashes($conteudo);
fclose($fp);
$queryInsercao = "INSERT INTO tabpainel (iduser, nome, data, hora, mensagem, latitude, longitude, imagem) VALUES ('$iduser', '$nome', '$data','$hora','$mensagem', '$latitude', '$longitude', '$conteudo')";
mysqli_query($mysqli,$queryInsercao) or die("Algo deu errado ao inserir o registro. Tente novamente.");
if (mysqli_affected_rows($mysqli) > 0)
include 'baixarpainel.php';
else
print utf8_encode("Não foi possível inserir o registro");
}
else
print utf8_encode("Não foi possível carregar a imagem.");
?>
And in Delphi, i'm using this:
FormPHP := TIdMultiPartFormDataStream.Create;
FormPHP.AddFile ('imagem', AImagem, 'image/jpeg');
FormPHP.AddFormField ('iduser', AIDUser, 'utf-8');
FormPHP.AddFormField ('nome', ANome, 'utf-8');
FormPHP.AddFormField ('data', AData, 'utf-8');
FormPHP.AddFormField ('hora', AHora, 'utf-8');
FormPHP.AddFormField ('mensagem', AMensagem, 'utf-8');
FormPHP.AddFormField ('latitude', '1');
FormPHP.AddFormField ('longitude', '1');
Response := TStringStream.Create('',TEncoding.UTF8);
HTTP:= TIdHTTP.Create(self);
HTTP.Post('http://addressexample.com/cadastro.php',FormPHP,Response);
It was working fine until a had to change the hosting company. With Hostinger was ok but with Hostgator it doesn't. With Hostgator the idHTTP raise an exception in the class EIdHTTPProtocalException with the message: "HTTP/1.1 406 Not Acceptable". The Hostgator support has already disabled the mod_security, that could cause the problem.
This exception only occurs on Android. Using the same app on Windows, it works fine.
UPDATE: I've tried another thing. The PHP script is this:
// Conecta-se ao banco de dados MySQL
$mysqli = new mysqli($servidor, $usuario, $senha, $banco);
// Caso algo tenha dado errado, exibe uma mensagem de erro
if (mysqli_connect_errno()) trigger_error(mysqli_connect_error());
# Instanciando o XMLWriter
$xml = new XMLWriter;
$xml->openMemory();
# Definindo o encoding do XML
$xml->startDocument( '1.0', 'UTF-8');
# Primeiro elemento do XML
$xml->startElement("DATAPACKET");
$xml->writeAttribute("version", "2.0");
$xml->StartElement("METADATA");
$xml->startElement("FIELDS");
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "id");
$xml->writeAttribute("fieldtype", "I4");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "iduser");
$xml->writeAttribute("fieldtype", "String");
$xml->writeAttribute("Width", "30");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "nome");
$xml->writeAttribute("fieldtype", "String");
$xml->writeAttribute("Width", "200");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "data");
$xml->writeAttribute("fieldtype", "String");
$xml->writeAttribute("Width", "8");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "hora");
$xml->writeAttribute("fieldtype", "String");
$xml->writeAttribute("Width", "5");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "mensagem");
$xml->writeAttribute("fieldtype", "String");
$xml->writeAttribute("Width", "3000");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "latitude");
$xml->writeAttribute("fieldtype", "r8");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "longitude");
$xml->writeAttribute("fieldtype", "r8");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "imagem");
$xml->writeAttribute("fieldtype", "bin.hex");
$xml->writeAttribute("subtype", "Binary");
$xml->endElement();
$xml->endElement(); //FIELDS
$xml->endElement(); //METADATA
$xml->StartElement("ROWDATA");
# Query na tabela escolhida
$rs_table = $mysqli->query("select * from tabpainel ORDER BY id DESC LIMIT 50");
while($table = $rs_table->fetch_array(MYSQLI_ASSOC))
{
# Transformando array em objeto
$table = (object)$table;
# Criando elemento tabela
$xml->StartElement("ROW");
# Setando os atributos
$xml->writeAttribute("id", "$table->id");
$xml->writeAttribute("iduser", "$table->iduser");
$xml->writeAttribute("nome", "$table->nome");
$xml->writeAttribute("data", "$table->data");
$xml->writeAttribute("hora", "$table->hora");
$xml->writeAttribute("mensagem", "$table->mensagem");
$xml->writeAttribute("latitude", "$table->latitude");
$xml->writeAttribute("longitude","$table->longitude");
$xml->writeAttribute("imagem", base64_encode("$table->imagem"));
$xml->endElement();
}
# Fechando o ROWDATA
$xml->endElement();
# Fechando o elemento DATAPACKET
$xml->endElement();
# Encerrando a conexao
//$con->close();
# Definindo cabecalho de saida
header("content-type: application/xml; charset=utf-8");
# Imprimindo a saida do XML
print $xml->outputMemory(true);
?>
And I used a http.get to receive the xml:
Http.HandleRedirects:= true;
Http.request.useragent := 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; MAAU)';
MS.Text:= Http.get('http://addressexample.com/baixarpainel.php');
MS.SaveToFile(FarquivoBaixado);
And this worked just fine on Android too. The problem remains only http.post on Android.
TIdHTTP works exactly the same way on all platforms, as Indy uses a single cross-platform codebase. So the generated HTTP request should be exactly the same on all platforms.
An HTTP 406 error happens when the HTTP request includes an Accept header that does not specify any media type that the server is capable of rendering the response in. Per RFC 2616 Section 14.1:
If no Accept header field is present, then it is assumed that the client accepts all media types. If an Accept header field is present, and if the server cannot send a response which is acceptable according to the combined Accept field value, then the server SHOULD send a 406 (not acceptable) response.
Your PHP script is sending a text/plain response, so if you send an Accept header that does not allow text/plain then that can cause a 406 error. It sounds like Hostgator is enforcing that more than Hostinger does.
By default, TIdHTTP sets its Request.Accept property to the following string value:
'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
Which technically allows all media types via */*, but just with a lower priority than some other media types. But that default should still be enough to allow a text/plain response, if the server implements Accept handling correctly.
You need to contact Hostgator and discuss the issue with them, as the problem is on their end, not yours.
That being said, since you know the server response is always text/plain, you could just add the following to your code before calling Post():
HTTP.Request.Accept := 'text/plain';
HTTP.Request.AcceptCharset := 'utf-8';
After long hours trying to solve, this is what happened.
On Windows, the application was working fine, and when I tried using HTTP.GET i imagined that couldn't be on server's end the problem, but on my request. So then, i started opening the headers of request and response both on Windows and Android using this code:
Astr.Add('Response text: ' + Http.Response.ResponseText);
Astr.Add(#13);
Astr.Add('Raw Headers Response: ' + HTTP.Response.RawHeaders.Text);
Astr.Add(#13);
Astr.Add('Raw Headers Request: ' + HTTP.Request.RawHeaders.Text);
This was the message i've got on Android:
Response text: HTTP/1.1 406 Not Acceptable
Raw Headers Response: Server: nginx/1.10.2
Date: ...
Content-Type: text/html;
charset=iso-8859-1
Content-Length:226
Connection: keep-alive
Raw Headers Request: Connection: keep-alive
Content-Type: multipart/form-data; boundary=------(somenumbers)
Content-Length: 0
Host: myhost
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Encoding: identity
User-Agent: ...
And this was the message on Windows
Response text: HTTP/1.1 200 OK
Raw Headers Request: Server: nginx/1.10.2
Date: ...
Content-Type: application/xml
Connection: close
Vary: Accept-Encoding,User-Agent
Raw Headers Request: Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------(numebrs)
Content-Length: 0
Host: myhost
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
User-Agent: ...
The same app on different platforms were sending different headers on Request. On Android, the idHTTP was sending an Accept-Encoding that on Windows was not. So I tried adding this: Http.Request.AcceptEncoding:= '*'; before the HTTP.POST and it worked! I received the xml just as expected.
I don't know why the idHTTP was changing the Request Accept-Encoding, but I had to specify another one and I chose this one because of MDN definition:
*
Matches any content encoding not already listed in the header. This is
the default value if the header is not present. It doesn't mean that
any algorithm is supported; merely that no preference is expressed.
I was able to resolve this issue by simple changing user agent string. I think remote machine(server) has some security installed.
I just set the Request -> UserAgent to:
Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0
and now it works !
I'm trying to post a insert on a MySQL database using idHTTP and a PHP script. This is the PHP script to insert in the database:
$mysqli = new mysqli($servidor, $usuario, $senha, $banco);
// Caso algo tenha dado errado, exibe uma mensagem de erro
if (mysqli_connect_errno()) trigger_error(mysqli_connect_error());
$iduser = quoted_printable_decode($_POST['iduser']);
$nome = quoted_printable_decode($_POST['nome']);
$data = quoted_printable_decode($_POST['data']);
$hora = quoted_printable_decode($_POST['hora']);
$mensagem = quoted_printable_decode($_POST['mensagem']);
$latitude = quoted_printable_decode($_POST['latitude']);
$longitude = quoted_printable_decode($_POST['longitude']);
$imagem = $_FILES["imagem"]['tmp_name'];
$tamanho = $_FILES['imagem']['size'];
header($_SERVER["SERVER_PROTOCOL"] . " 200 OK");
header('Content-Type: text/plain; charset="utf-8"');
if ( $imagem != "none" )
{
$fp = fopen($imagem, "rb");
$conteudo = fread($fp, $tamanho);
$conteudo = addslashes($conteudo);
fclose($fp);
$queryInsercao = "INSERT INTO tabpainel (iduser, nome, data, hora, mensagem, latitude, longitude, imagem) VALUES ('$iduser', '$nome', '$data','$hora','$mensagem', '$latitude', '$longitude', '$conteudo')";
mysqli_query($mysqli,$queryInsercao) or die("Algo deu errado ao inserir o registro. Tente novamente.");
if (mysqli_affected_rows($mysqli) > 0)
include 'baixarpainel.php';
else
print utf8_encode("Não foi possível inserir o registro");
}
else
print utf8_encode("Não foi possível carregar a imagem.");
?>
And in Delphi, i'm using this:
FormPHP := TIdMultiPartFormDataStream.Create;
FormPHP.AddFile ('imagem', AImagem, 'image/jpeg');
FormPHP.AddFormField ('iduser', AIDUser, 'utf-8');
FormPHP.AddFormField ('nome', ANome, 'utf-8');
FormPHP.AddFormField ('data', AData, 'utf-8');
FormPHP.AddFormField ('hora', AHora, 'utf-8');
FormPHP.AddFormField ('mensagem', AMensagem, 'utf-8');
FormPHP.AddFormField ('latitude', '1');
FormPHP.AddFormField ('longitude', '1');
Response := TStringStream.Create('',TEncoding.UTF8);
HTTP:= TIdHTTP.Create(self);
HTTP.Post('http://addressexample.com/cadastro.php',FormPHP,Response);
It was working fine until a had to change the hosting company. With Hostinger was ok but with Hostgator it doesn't. With Hostgator the idHTTP raise an exception in the class EIdHTTPProtocalException with the message: "HTTP/1.1 406 Not Acceptable". The Hostgator support has already disabled the mod_security, that could cause the problem.
This exception only occurs on Android. Using the same app on Windows, it works fine.
UPDATE: I've tried another thing. The PHP script is this:
// Conecta-se ao banco de dados MySQL
$mysqli = new mysqli($servidor, $usuario, $senha, $banco);
// Caso algo tenha dado errado, exibe uma mensagem de erro
if (mysqli_connect_errno()) trigger_error(mysqli_connect_error());
# Instanciando o XMLWriter
$xml = new XMLWriter;
$xml->openMemory();
# Definindo o encoding do XML
$xml->startDocument( '1.0', 'UTF-8');
# Primeiro elemento do XML
$xml->startElement("DATAPACKET");
$xml->writeAttribute("version", "2.0");
$xml->StartElement("METADATA");
$xml->startElement("FIELDS");
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "id");
$xml->writeAttribute("fieldtype", "I4");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "iduser");
$xml->writeAttribute("fieldtype", "String");
$xml->writeAttribute("Width", "30");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "nome");
$xml->writeAttribute("fieldtype", "String");
$xml->writeAttribute("Width", "200");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "data");
$xml->writeAttribute("fieldtype", "String");
$xml->writeAttribute("Width", "8");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "hora");
$xml->writeAttribute("fieldtype", "String");
$xml->writeAttribute("Width", "5");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "mensagem");
$xml->writeAttribute("fieldtype", "String");
$xml->writeAttribute("Width", "3000");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "latitude");
$xml->writeAttribute("fieldtype", "r8");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "longitude");
$xml->writeAttribute("fieldtype", "r8");
$xml->endElement();
$xml->startElement("FIELD");
$xml->writeAttribute("attrname", "imagem");
$xml->writeAttribute("fieldtype", "bin.hex");
$xml->writeAttribute("subtype", "Binary");
$xml->endElement();
$xml->endElement(); //FIELDS
$xml->endElement(); //METADATA
$xml->StartElement("ROWDATA");
# Query na tabela escolhida
$rs_table = $mysqli->query("select * from tabpainel ORDER BY id DESC LIMIT 50");
while($table = $rs_table->fetch_array(MYSQLI_ASSOC))
{
# Transformando array em objeto
$table = (object)$table;
# Criando elemento tabela
$xml->StartElement("ROW");
# Setando os atributos
$xml->writeAttribute("id", "$table->id");
$xml->writeAttribute("iduser", "$table->iduser");
$xml->writeAttribute("nome", "$table->nome");
$xml->writeAttribute("data", "$table->data");
$xml->writeAttribute("hora", "$table->hora");
$xml->writeAttribute("mensagem", "$table->mensagem");
$xml->writeAttribute("latitude", "$table->latitude");
$xml->writeAttribute("longitude","$table->longitude");
$xml->writeAttribute("imagem", base64_encode("$table->imagem"));
$xml->endElement();
}
# Fechando o ROWDATA
$xml->endElement();
# Fechando o elemento DATAPACKET
$xml->endElement();
# Encerrando a conexao
//$con->close();
# Definindo cabecalho de saida
header("content-type: application/xml; charset=utf-8");
# Imprimindo a saida do XML
print $xml->outputMemory(true);
?>
And I used a http.get to receive the xml:
Http.HandleRedirects:= true;
Http.request.useragent := 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; MAAU)';
MS.Text:= Http.get('http://addressexample.com/baixarpainel.php');
MS.SaveToFile(FarquivoBaixado);
And this worked just fine on Android too. The problem remains only http.post on Android.
TIdHTTP works exactly the same way on all platforms, as Indy uses a single cross-platform codebase. So the generated HTTP request should be exactly the same on all platforms.
An HTTP 406 error happens when the HTTP request includes an Accept header that does not specify any media type that the server is capable of rendering the response in. Per RFC 2616 Section 14.1:
If no Accept header field is present, then it is assumed that the client accepts all media types. If an Accept header field is present, and if the server cannot send a response which is acceptable according to the combined Accept field value, then the server SHOULD send a 406 (not acceptable) response.
Your PHP script is sending a text/plain response, so if you send an Accept header that does not allow text/plain then that can cause a 406 error. It sounds like Hostgator is enforcing that more than Hostinger does.
By default, TIdHTTP sets its Request.Accept property to the following string value:
'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
Which technically allows all media types via */*, but just with a lower priority than some other media types. But that default should still be enough to allow a text/plain response, if the server implements Accept handling correctly.
You need to contact Hostgator and discuss the issue with them, as the problem is on their end, not yours.
That being said, since you know the server response is always text/plain, you could just add the following to your code before calling Post():
HTTP.Request.Accept := 'text/plain';
HTTP.Request.AcceptCharset := 'utf-8';
After long hours trying to solve, this is what happened.
On Windows, the application was working fine, and when I tried using HTTP.GET i imagined that couldn't be on server's end the problem, but on my request. So then, i started opening the headers of request and response both on Windows and Android using this code:
Astr.Add('Response text: ' + Http.Response.ResponseText);
Astr.Add(#13);
Astr.Add('Raw Headers Response: ' + HTTP.Response.RawHeaders.Text);
Astr.Add(#13);
Astr.Add('Raw Headers Request: ' + HTTP.Request.RawHeaders.Text);
This was the message i've got on Android:
Response text: HTTP/1.1 406 Not Acceptable
Raw Headers Response: Server: nginx/1.10.2
Date: ...
Content-Type: text/html;
charset=iso-8859-1
Content-Length:226
Connection: keep-alive
Raw Headers Request: Connection: keep-alive
Content-Type: multipart/form-data; boundary=------(somenumbers)
Content-Length: 0
Host: myhost
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Encoding: identity
User-Agent: ...
And this was the message on Windows
Response text: HTTP/1.1 200 OK
Raw Headers Request: Server: nginx/1.10.2
Date: ...
Content-Type: application/xml
Connection: close
Vary: Accept-Encoding,User-Agent
Raw Headers Request: Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------(numebrs)
Content-Length: 0
Host: myhost
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
User-Agent: ...
The same app on different platforms were sending different headers on Request. On Android, the idHTTP was sending an Accept-Encoding that on Windows was not. So I tried adding this: Http.Request.AcceptEncoding:= '*'; before the HTTP.POST and it worked! I received the xml just as expected.
I don't know why the idHTTP was changing the Request Accept-Encoding, but I had to specify another one and I chose this one because of MDN definition:
*
Matches any content encoding not already listed in the header. This is
the default value if the header is not present. It doesn't mean that
any algorithm is supported; merely that no preference is expressed.
I was able to resolve this issue by simple changing user agent string. I think remote machine(server) has some security installed.
I just set the Request -> UserAgent to:
Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0
and now it works !
Could someone help me figure out this.
here is my arduino sketch
#include <Adafruit_CC3000.h>
#include <ccspi.h>
#include <SPI.h>
#include <string.h>
#include "utility/debug.h"
// Default Pin GizDuino CC3000
#define ADAFRUIT_CC3000_IRQ 3
#define ADAFRUIT_CC3000_VBAT 5
#define ADAFRUIT_CC3000_CS 10
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT,
SPI_CLOCK_DIV2);
//Router Specifics
#define WLAN_SSID "Cisco"
#define WLAN_PASS "nothingts"
//Router Security
#define WLAN_SECURITY WLAN_SEC_WPA2
//Web transactions on port 80
char server[] = "tmm.site50.net";
Adafruit_CC3000_Client client;
float tempPin = A1;
uint32_t ip;
void setup()
{
Serial.begin(115200);
Serial.println(F("Hello, Patient 1!\n"));
displayDriverMode();
Serial.print("Free RAM: "); Serial.println(getFreeRam(), DEC);
/* Initialise CC3000 */
Serial.println(F("\nInitialising the CC3000 ..."));
if (!cc3000.begin())
{
Serial.println(F("Unable to initialise the CC3000! Check your wiring?"));
while(1);
}
/* Setting MAC Address Spoof */
uint8_t macAddress[6] = { 0x08, 0x00, 0x28, 0x01, 0x79, 0xB7 };
if (!cc3000.setMacAddress(macAddress))
{
Serial.println(F("Failed trying to update the MAC address"));
while(1);
}
/* End MAC Spoof */
uint16_t firmware = checkFirmwareVersion();
if ((firmware != 0x113) && (firmware != 0x118)) {
Serial.println(F("Wrong firmware version!"));
for(;;);
}
displayMACAddress();
/* Cleanup ng Past Connection */
Serial.println(F("\nDeleting old connection profiles"));
if (!cc3000.deleteProfiles()) {
Serial.println(F("Failed!"));
while(1);
}
/* Connecting to Wi-Fi Router */
char *ssid = WLAN_SSID;
Serial.print(F("\nAttempting to connect to ")); Serial.println(ssid);
/* NOTE: Secure connections are not available in 'Tiny' mode! */
if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) {
Serial.println(F("Failed!"));
while(1);
}
Serial.println(F("Connected!"));
/* Wait for DHCP to complete */
Serial.println(F("Request DHCP"));
while (!cc3000.checkDHCP())
{
delay(100);
}
/* Display the IP address DNS, Gateway, etc. */
while (! displayConnectionDetails()) {
delay(1000);
}
}
void loop()
{
uint32_t ip = 0;
Serial.print(F("tmm.site50.net -> "));
while (ip == 0)
{
if (! cc3000.getHostByName("tmm.site50.net", &ip))
{
Serial.println(F("Couldn't resolve!"));
while(1){}
}
}
cc3000.printIPdotsRev(ip);
Serial.println(F(""));
Serial.println(F("Posting!"));
Adafruit_CC3000_Client client = cc3000.connectTCP(ip, 80);
if (client.connected())
{
{
client.println("POST /sample.php HTTP/1.1");
Serial.println("POST /sample.php HTTP/1.1");
client.println("Host: tmm.site50.net");
Serial.println("Host: tmm.site50.net");
client.println("From: your#email.tld");
Serial.println("From: your#email.tld");
client.println("User-Agent: gizDuinodev1/1.0");
Serial.println("User-Agent: gizDuinodev1/1.0");
client.println("Content-Type: Application/x-www-form-urlencoded");
Serial.println("Content-Type: Application/x-www-form-urlencoded");
client.println("Content-Length: 7"); // notice not println to not get a linebreak
Serial.println("Content-Length: 7");
client.println(""); // to get the empty line
Serial.println("");
client.println("temp=50");
Serial.println("temp=50");
Serial.println("POSTED!");
delay (1000);
// While we're connected, print out anything the server sends:
while (client.connected())
{
if (client.available())
{
char c = client.read();
Serial.print(c);
}
}
Serial.println();
}
}
else // If the connection failed, print a message:
{
Serial.println(F("Connection failed"));
}
delay(5000);
}
/**************************************************************************/
/*!
#brief Displays the driver mode (tiny of normal), and the buffer
size if tiny mode is not being used
#note The buffer size and driver mode are defined in cc3000_common.h
*/
/**************************************************************************/
void displayDriverMode(void)
{
#ifdef CC3000_TINY_DRIVER
Serial.println(F("CC3000 is configure in 'Tiny' mode"));
#else
Serial.print(F("RX Buffer : "));
Serial.print(CC3000_RX_BUFFER_SIZE);
Serial.println(F(" bytes"));
Serial.print(F("TX Buffer : "));
Serial.print(CC3000_TX_BUFFER_SIZE);
Serial.println(F(" bytes"));
#endif
}
/**************************************************************************/
/*!
#brief Tries to read the CC3000's internal firmware patch ID
*/
/**************************************************************************/
uint16_t checkFirmwareVersion(void)
{
uint8_t major, minor;
uint16_t version;
#ifndef CC3000_TINY_DRIVER
if(!cc3000.getFirmwareVersion(&major, &minor))
{
Serial.println(F("Unable to retrieve the firmware version!\r\n"));
version = 0;
}
else
{
Serial.print(F("Firmware V. : "));
Serial.print(major); Serial.print(F(".")); Serial.println(minor);
version = major; version <<= 8; version |= minor;
}
#endif
return version;
}
/**************************************************************************/
/*!
#brief Tries to read the 6-byte MAC address of the CC3000 module
*/
/**************************************************************************/
void displayMACAddress(void)
{
uint8_t macAddress[6];
if(!cc3000.getMacAddress(macAddress))
{
Serial.println(F("Unable to retrieve MAC Address!\r\n"));
}
else
{
Serial.print(F("MAC Address : "));
cc3000.printHex((byte*)&macAddress, 6);
}
}
/**************************************************************************/
/*!
#brief Tries to read the IP address and other connection details
*/
/**************************************************************************/
bool displayConnectionDetails(void)
{
uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;
if(!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
{
Serial.println(F("Unable to retrieve the IP Address!\r\n"));
return false;
}
else
{
Serial.print(F("\nIP Addr: ")); cc3000.printIPdotsRev(ipAddress);
Serial.print(F("\nNetmask: ")); cc3000.printIPdotsRev(netmask);
Serial.print(F("\nGateway: ")); cc3000.printIPdotsRev(gateway);
Serial.print(F("\nDHCPsrv: ")); cc3000.printIPdotsRev(dhcpserv);
Serial.print(F("\nDNSserv: ")); cc3000.printIPdotsRev(dnsserv);
Serial.println();
return true;
}
}
and here is the php
http://tmm.site50.net/sample.php
<html>
<body>
<label>Temperature:</label>
<input type="text" id="temp" name="temp" value="" />
</br>
<label>Device:</label>
<input type="text" id="module" name="module" value="" />
</body>
</html>
the thing is, when the device sent.. it seems that there is a bad header..
in arduino serial monitor it gives me this reply.
tmm.site50.net -> 31.170.162.243
Posting!
POST /sample.php HTTP/1.1
Host: 31.170.162.243
From: your#email.tld
User-Agent: gizDuinodev1/1.0
Content-Type: Application/x-www-form-urlencoded
Content-Length: 7
temp=50
POSTED!
HTTP/1.1 200 OK
Date: Fri, 30 Jan 2015 00:51:38 GMT
Server: Apache
X-Powered-By: PHP/5.2.17
Content-Length: 351
Connection: close
Content-Type: text/html
<html>
<body>
<label>Temperature:</label>
<input type="text" id="temp" name="temp" value="" />
</br>
<label>Device:</label>
<input type="text" id="module" name="module" value="" />
</body>
</html>
<!-- Hosting24 Analytics Code -->
<script type="text/javascript" src="http://stats.hosting24.com/count.php"></script>
<!-- End Of Analytics Code -->
this is what the server replies to me
The HTTP request
Correct format
A proper HTTP POST request looks something like this:
POST /url HTTP/1.1\r\n
Host example.com\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 7\r\n
\r\n
foo=bar
This will send bar as the value of foo. In a GET request you could add this to the URL (/url/?foo=bar), but POST requests don't work like this: you have to add a message body.
Content-Length should be the amount of characters in the body (here: foo=bar, so 7 characters). The Content-Type and Content-Length fields are required to do what you want. The Host field is required in any HTTP/1.1 request.
Note that linebreaks are \r\n. Fortunately, Arduino's println() adds both the carriage return \r and the newline feed \n. However, you should make sure that you don't use print() unless you want to continue on the same line. So here's a working example:
Working Arduino example
client.println("POST /sample.php HTTP/1.1");
client.println("Host: tmm.site50.net");
client.println("Content-Type: application/x-www-form-urlencoded");
client.println("Content-Length: 7");
client.println("");
client.println("temp=50");
This will send 50 as the temperature.
PHP back-end
Storing the data in a file
Then you probably want to save the sent data on the server. To do this, you fetch the information from the $_POST superglobal, and save it in a file:
<?php
$handle = fopen('temp.log', 'w');
fwrite($handle, date('d/m/Y H:i:s') . " - " . $_POST['temp']) . "\n";
fclose($handle);
?>
This opens temp.log in write-mode (see fopen()), then writes a timestamp and the current value to that file (see fwrite()), and closes the handle (see fclose()). The file then looks something like this:
30/01/2015 09:12:40 - 50
You can simply go to http://tmm.site50.net/temp.log to view the file.
Logging
Every time the Arduino sends a request, the old result will be overwritten. Alternatively, you may want to log the data, and add new lines instead of overwriting old data. Then, use the a (append) mode in fopen() instead of w (write):
$handle = fopen('temp.log', 'a');
This will automatically append new lines to the end of the file.
Permissions
Depending on the permissions on your server, you may need to create temp.log before PHP can write to it.
I'm trying to send a "post" reguest to my php file and get the info back, it works fine, but
it also print something before printing my response from the php file. this is what it print
first:
HTTP/1.1 200 OK
Date: Fri, 20 Apr 2012 10:19:12 GMT
Server: Apache/2.2.21 (Unix) mod_ssl/2.2.21 OpenSSL/0.9.8r DAV/2 PHP/5.3.6
X-Powered-By: PHP/5.3.6
Content-Length: 12
Connection: close
Content-Type: text/html
and then my response:
hello world
how can i only print what i'm getting from myphp code, without:
HTTP/1.1 200 OK
Date: Fri, 20 Apr 2012 10:19:12 GMT
Server: Apache/2.2.21 (Unix) mod_ssl/2.2.21 OpenSSL/0.9.8r DAV/2 PHP/5.3.6
X-Powered-By: PHP/5.3.6
Content-Length: 12
Connection: close
Content-Type: text/html
the code i'm using is:
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netdb.h>
#include <unistd.h>
#define SA struct sockaddr
#define MAXLINE 4096
#define MAXSUB 200
#define LISTENQ 1024
extern int h_errno;
ssize_t process_http(int sockfd, char *host, char *page, char *poststr)
{
char sendline[MAXLINE + 1], recvline[MAXLINE + 1];
ssize_t n;
snprintf(sendline, MAXSUB,
"POST %s HTTP/1.0\r\n"
"Host: %s\r\n"
"Content-type: application/x-www-form-urlencoded\r\n"
"Content-length: %d\r\n\r\n"
"%s", page, host, strlen(poststr), poststr);
write(sockfd, sendline, strlen(sendline));
while ((n = read(sockfd, recvline, MAXLINE)) > 0) {
recvline[n] = '\0';
printf("%s", recvline); // <-- this
}
return n;
}
int main(void)
{
int sockfd;
struct sockaddr_in servaddr;
char **pptr;
//********** You can change. Puy any values here *******
char *hname = "localhost";
char *page = "/mysql_update.php";
char *poststr = "server=dd sd sdsdt\n";
//*******************************************************
char str[50];
struct hostent *hptr;
if ((hptr = gethostbyname(hname)) == NULL) {
fprintf(stderr, " Server down error for host: %s: %s",
hname, hstrerror(h_errno));
exit(1);
}
printf("hostname: %s \n", hptr->h_name);
if (hptr->h_addrtype == AF_INET
&& (pptr = hptr->h_addr_list) != NULL) {
printf("address: %s\n",
inet_ntop(hptr->h_addrtype, *pptr, str,
sizeof(str)));
} else {
fprintf(stderr, "Error call inet_ntop \n");
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(80);
inet_pton(AF_INET, str, &servaddr.sin_addr);
connect(sockfd, (SA *) & servaddr, sizeof(servaddr));
process_http(sockfd, hname, page, poststr);
close(sockfd);
exit(0);
}
Please help me to fix this problem, step by step so' i can understand it, give me information and please if you can' show me a example. ( this will help me and others )
First part of the response you received is made up of HTTP version, Server Response Status Code and various HTTP response headers.
HTTP/1.1 200 OK Date: Fri, 20 Apr 2012 10:19:12 GMT Server:
Apache/2.2.21 (Unix) mod_ssl/2.2.21 OpenSSL/0.9.8r DAV/2 PHP/5.3.6
X-Powered-By: PHP/5.3.6 Content-Length: 12 Connection: close
Content-Type: text/html
After headers follows HTTP response body (which you need to extract):
hello world
HTTP headers and body are separated with the following sequence of characters: \r\n\r\n. So all you need to do is to search for it and extract everything that is behind it.
You can do this yourself (sockets, parsing...) but my advice is to use some of HTTP libraries: WinInet, WinHttp (both are Microsoft's) or libCurl (open source).
The responses header bit is separated from the content by a blank line.
Need to take into account different line endings. Basically ignore \rs.
So 1st read lines until you get a blank one then start printing them!
EDIT
You have a response as follows
HTTP/1.1 200 OK
Date: Fri, 20 Apr 2012 10:19:12 GMT
...
Content-Type: text/html
Hello World
Notice that there is a blank line between the HTTP headers and the response (HTML in this case).