Pages: [1]   Go Down
Author Topic: Using GET requests to run a php script?  (Read 585 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Jr. Member
**
Karma: 0
Posts: 56
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I dont know if I am doing this right, i have looked through many examples on how to do this but it still is not working for me.  Basically I have a php script on a server that is programmed to update a sql database
The script works when someone goes to the webpage:
http://cs14.sheridanc.on.ca/connect.php?var1=x&var2=x

(where x is a value to be written to the database)
it works just fine when i go there in a webbrowser, what i am doing in my code is replacing those x's with variables from my program and uploading them to the database when they change.
Here is my code below: (http stuff is closer to the bottom half)

Code:
#if defined(ARDUINO) && ARDUINO > 18
#include <SPI.h>
#endif
#include <Ethernet.h>
#include <EthernetDHCP.h>

// MAC Address
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

const char* ip_to_str(const uint8_t*);

// Initialize the Ethernet server library
//Server server(8080);

int var1 = 2;
int var2 = 4;
int var3 = 7;
int var4 = 8;
int var5 = 1;



byte serverip[] = { 142, 55, 49, 114 };
Client client(serverip, 80); //apache web server running on port 80

void setup()
{
  Serial.begin(9600);

  Serial.println("Attempting to obtain a DHCP lease...");
 
 
  EthernetDHCP.begin(mac);

  // Since we're here, it means that we now have a DHCP lease, so we print
  // out some information.
  const byte* ipAddr = EthernetDHCP.ipAddress();
  const byte* gatewayAddr = EthernetDHCP.gatewayIpAddress();
  const byte* dnsAddr = EthernetDHCP.dnsIpAddress();
 
  Serial.println("A DHCP lease has been obtained.");

  Serial.print("My IP address is ");
  Serial.println(ip_to_str(ipAddr));
 
  Serial.print("Gateway IP address is ");
  Serial.println(ip_to_str(gatewayAddr));
 
  Serial.print("DNS IP address is ");
  Serial.println(ip_to_str(dnsAddr));
 
  // Start the server
   Serial.print("Starting Server.");
   //server.begin();
}

void loop()
{

  EthernetDHCP.maintain();
 
  // listen for incoming clients
 // Client client = server.available();

  if (client) {
     Serial.println("Client Pass 1");
    // an http request ends with a blank line
    while (client.connected()) {
       Serial.println("Client Pass 2");
      if (client.available()) {
         Serial.println("client pass 3 ");
        char c = client.read();
       
        if (c == '\n') {
         
          //Posts values of var1 and var2 to the database using an sql script
          Serial.println("posting to db");
          client.print("GET http://cs14.sheridanc.on.ca/connect.php?var1=");
          client.print(var1);
          client.print("&var2=");
          client.print(var2);
         
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();
          client.println("<head>");
          client.println("<meta http-equiv=Content-Type content=text/html; charset=utf-8 />");
          client.println("<title>Test Status</title>");
          client.println("</head>");
          client.println("<body>");
          client.println("</body>");
     
          break;
        }

       
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
  }
}

// Just a utility function to nicely format an IP address.
const char* ip_to_str(const uint8_t* ipAddr)
{
  static char buf[16];
  sprintf(buf, "%d.%d.%d.%d\0", ipAddr[0], ipAddr[1], ipAddr[2], ipAddr[3]);
  return buf;
}
Logged

CO, USA
Offline Offline
God Member
*****
Karma: 4
Posts: 710
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I haven't used this, but it appears as if your script is sending out the response it's expecting to get back from the webserver after the get request is processed. the '200 OK' is an http response which would be coming back from the server. Try

Code:
client.println("HTTP/1.1");
without the 200 OK part. Then don't send the rest of the HTML code you have there.

Do you have access to your server logs? I'm going to guess you'll see some illegal request errors.

I suspect that the ethernet library has some methods for retrieving the response you get back, so I think it would be instructive to do that, and Serial.print it.
Logged

... it is poor civic hygiene to install technologies that could someday
facilitate a police state. -- Bruce Schneier

Offline Offline
Jr. Member
**
Karma: 0
Posts: 56
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I went into the apache error logs and this is what i found

Code:
[Wed Oct 05 23:53:46 2011] [error] [client 64.228.213.33] client sent HTTP/1.1 request without hostname (see RFC2616 section 14.23): /connect.php

does it mean i didnt pick a hostname or that the arduino needs a hostname?
Logged

0
Offline Offline
Tesla Member
***
Karma: 118
Posts: 8962
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The below connects part of the time.

Code:
//zoomkat 11-13-10
//simple ethernet client test code
//for use with IDE 0021 and W5100 ethernet shield
//modify the arduino lan ip address as needed
//open serial monitor to see what the arduino receives
//push the shield reset button to run client again

#include <SPI.h>
#include <Ethernet.h>
byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192,168,1,102 };
byte server[] = { 142, 55, 49, 114 }; //
Client client(server, 80);

void setup() {
  Ethernet.begin(mac, ip);
  Serial.begin(9600);
  delay(500);
  Serial.println("connecting...");
  if (client.connect()) {
    Serial.println("connected");
    client.print("GET /connect.php?var1=x&var2= HTTP/1.1\r\n");
    client.print("Host: cs14.sheridanc.on.ca\r\n");
    client.print("Connection: close\r\n");
    client.print("\r\n");

   }
  else {
    Serial.println("connection failed");
  }
}

void loop()
{
  if (client.available()) {
    char c = client.read();
    Serial.print(c);
  }
  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();
    for(;;)
      ;
  }
}
 
Logged

Consider the daffodil. And while you're doing that, I'll be over here, looking through your stuff.   smiley-cool

CO, USA
Offline Offline
God Member
*****
Karma: 4
Posts: 710
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I went into the apache error logs and this is what i found

Code:
[Wed Oct 05 23:53:46 2011] [error] [client 64.228.213.33] client sent HTTP/1.1 request without hostname (see RFC2616 section 14.23): /connect.php

does it mean i didnt pick a hostname or that the arduino needs a hostname?

I think it means the Apache server you're connecting to is set up to do virtual domain hosting. So without a hostname as part of the request, it doesn't know what virtual host to use to service it. Or, it could be more complicated.

See http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html and scroll down a bit, where you'll find the example:
Code:
For example, a client wishing to retrieve the resource above directly from the origin server would create a TCP connection to port 80 of the host "www.w3.org" and send the lines:

       GET /pub/WWW/TheProject.html HTTP/1.1
       Host: www.w3.org

Note that the GET request specifies a resource (in the form of a path), and the hostname is a seperate transmission immediately following that. I'm not an expert on this, but I think you'll be able to verify this by finding, in your Apache logs, a successful invocation from your web browser. I haven't looked at Apache logs for quite a while. I mostly remember that it's tedious without an analyzer.

Looking at http://www.jmarshall.com/easy/http/ I find:
Code:
HTTP 1.0 defines 16 headers, though none are required. HTTP 1.1 defines 46 headers, and one (Host:) is required in requests.
So, I'm not sure how you'd construct the request for HTTP/1.0, but that might be an easier possibility. And as I said, I haven't looked at the library, so I'm wondering why it doesn't do some of this stuff for you, given a complete URL -- maybe the processing involved in parsing is too much overhead for the Arduino, so you have to do all the heavy lifting yourself.

ETA: I see you've discovered the Host: header already. I'm afraid I'm out of ideas, at least for now.
Logged

... it is poor civic hygiene to install technologies that could someday
facilitate a police state. -- Bruce Schneier

0
Offline Offline
Tesla Member
***
Karma: 118
Posts: 8962
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This also works part of the time, but less frequently.

Code:
//zoomkat 11-13-10
//simple ethernet client test code
//for use with IDE 0021 and W5100 ethernet shield
//modify the arduino lan ip address as needed
//open serial monitor to see what the arduino receives
//push the shield reset button to run client again

#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 1, 102 }; // Arduino IP address
byte server[] = { 142, 55, 49, 114 }; // zoomkat's web site

Client client(server, 80);

void setup()
{
  Ethernet.begin(mac, ip);
  Serial.begin(9600);
  Serial.println("starting simple arduino client test");
  Serial.println();

  delay(1000);

  Serial.println("connecting...");

  if (client.connect()) {
    Serial.println("connected");
    client.println("GET /connect.php?var1=x&var2=x HTTP/1.0");
    client.println();
  } else {
    Serial.println("connection failed");
  }
}

void loop()
{
  if (client.available()) {
    char c = client.read();
    Serial.print(c);
  }

  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting.");
    Serial.println("==================================");
    Serial.println("");
    client.stop();
    for(;;);
  }
}
 
Logged

Consider the daffodil. And while you're doing that, I'll be over here, looking through your stuff.   smiley-cool

Offline Offline
God Member
*****
Karma: 3
Posts: 813
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


There are three problems.

First, you are not creating a proper HTTP GET request. a GET request, in text, looks something like this:

Code:
GET /connect.php?var1=x&var2=y HTTP/1.1
Connection: close
Host: cs14.sheridanc.on.ca


Each line is terminated by both CR and LF, and the final blank line terminates the request (by terminating the headers of the GET).

Second, a delay(1) is probably not enough to make sure the server gets the data, a little bit depending on how far away it is, how lossy the network is, etc. If you need a delay to make sure the server gets the data, try adding a delay(500) instead -- or maybe even delay(5000), if that delay is necessary.
A better way would be to wait for the server to send back the "HTTP/1.1 200 OK" response before you close() the connection.

Third, and this is more subtle: You should not expect a GET request to be able to always write to a destination. GET requests are generally cacheable in the Internet infrastructure, and some proxy or server cache or whatever on the way may decide that it's already seen a "GET" with the parameters you're posting, and just return the cached response, rather than forwarding the request all the way to your PHP script. You should be using a PUT or a POST. The only difference, compared to a GET, is that you need to add a Content-Length header, which can be 0. Thus, a proper request should probably look like:

Code:
POST /connect.php HTTP/1.1
Host: your-host-name.com
Connection: close
Content-length: 14

?var1=x&var2=y

(The body is NOT terminated by CR LF here)
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 56
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

So this is the new code
Code:
Serial.println("posting to db");

          client.print("POST /connect.php HTTP/1.1");
          client.println("Host: cs14.sheridanc.on.ca");
          client.println("Connection: close");
          client.println("Content-length: 14");

          client.println("?var1=");
          client.print(var1);
          client.print("&var2=");
          client.print(var2);
          Serial.println("posted to db");

Still doesnt work apache shows this in the access logs
Code:
"POST /connect.php HTTP/1.1Host: cs14.sheridanc.on.ca" 400 368 "-" "-"

How do i add CF anf LF?
Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 553
Posts: 46281
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
How do i add CF anf LF?
Code:
client.println();

Quote
Still doesnt work apache shows this in the access logs
"POST /connect.php HTTP/1.1Host: cs14.sheridanc.on.ca" 400 368 "-" "-"
The 400 there is a clue. Look up what an HTTP error value of 400 means.
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 56
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Http 400 means a malformed request, so basically the formatting of the information I am sending is not right?
Logged

CO, USA
Offline Offline
God Member
*****
Karma: 4
Posts: 710
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Third, and this is more subtle: You should not expect a GET request to be able to always write to a destination. GET requests are generally cacheable in the Internet infrastructure, and some proxy or server cache or whatever on the way may decide that it's already seen a "GET" with the parameters you're posting, and just return the cached response, rather than forwarding the request all the way to your PHP script. You should be using a PUT or a POST.

There are a couple other ways of dealing with this. One would be to use the the cache control header
Code:
Cache-Control: no-cache
The other would be to add another parameter which is set to a random value, thus preventing the request from looking just like the previous ones. Might be that Pragma: no-cache would be better there.

Might be simpler for the OP to stay with GET requests until all that is working.

ETA: a 3rd variable (ignored by the script) could just be set to the value of millis(), no need to mess with generating a random value.
« Last Edit: October 06, 2011, 06:20:13 pm by justjed » Logged

... it is poor civic hygiene to install technologies that could someday
facilitate a police state. -- Bruce Schneier

Hamburg, Germany
Offline Offline
Full Member
***
Karma: 2
Posts: 191
Hello world!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

When POSTing you need to know and send the content length. I assume the parameters var1 and var2 are not always exactly one character/byte long so the "Content-length: 14" would be correct for "?var1=1&var2=2" (btw, the ? is the seperator for the query string in a URI, needed for GET requests but should not be used when POSTing form data in the http body) but wrong for "?var1=42&var2=23"

I agree with justjed, GET is easier to use and adequate for this task. In most default server configurations php automatically adds HTTP headers to prevent caching, so there should not be problems with proxies at all. Adding a random value is also a common practice to prevent caching.

Your request should look like:
Code:
GET /connect.php?var1=1&var2=2&23489 HTTP/1.0
Host: cs14.sheridanc.on.ca
Connection: close


And this is how I think it should work:

Code:
Serial.println("posting to db");
          client.print("GET /connect.php?var1=");     // Path only, not the complete URI
          client.print(var1);
          client.print("&var2=");
          client.print(var2);
          client.print("&");
          client.print( millis() );          // prevent caching, like justjed said a post before
          client.println(" HTTP/1.0");                // no need for 1.1
          client.println("Host: cs14.sheridanc.on.ca");
          client.println("Connection: close");
          client.println();

Logged


Offline Offline
Jr. Member
**
Karma: 0
Posts: 56
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

When POSTing you need to know and send the content length. I assume the parameters var1 and var2 are not always exactly one character/byte long so the "Content-length: 14" would be correct for "?var1=1&var2=2" (btw, the ? is the seperator for the query string in a URI, needed for GET requests but should not be used when POSTing form data in the http body) but wrong for "?var1=42&var2=23"

I agree with justjed, GET is easier to use and adequate for this task. In most default server configurations php automatically adds HTTP headers to prevent caching, so there should not be problems with proxies at all. Adding a random value is also a common practice to prevent caching.

Your request should look like:
Code:
GET /connect.php?var1=1&var2=2&23489 HTTP/1.0
Host: cs14.sheridanc.on.ca
Connection: close


And this is how I think it should work:

Code:
Serial.println("posting to db");
          client.print("GET /connect.php?var1=");     // Path only, not the complete URI
          client.print(var1);
          client.print("&var2=");
          client.print(var2);
          client.print("&");
          client.print( millis() );          // prevent caching, like justjed said a post before
          client.println(" HTTP/1.0");                // no need for 1.1
          client.println("Host: cs14.sheridanc.on.ca");
          client.println("Connection: close");
          client.println();



Oh wow this worked, thanks alot! Ive been working at this for so long trying to figure this out lol... This will make future work easier, thanks!
Logged

Offline Offline
God Member
*****
Karma: 3
Posts: 813
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
          client.print("POST /connect.php HTTP/1.1"); // <-- this line has a bug -- no CR/LF
          client.println("Host: cs14.sheridanc.on.ca");
          client.println("Connection: close");
          client.println("Content-length: 14");

          client.println("?var1="); // <-- this line has a bug -- you want an empty line before this line, not a linebreak in the middle
          client.print(var1);
          client.print("&var2=");
          client.print(var2);
          Serial.println("posted to db");

Still doesnt work apache shows this in the access logs
Code:
"POST /connect.php HTTP/1.1Host: cs14.sheridanc.on.ca" 400 368 "-" "-"

How do i add CF anf LF?

I annotated the code for you. Additionally, the character code for CR is 13, and the string value is "\r". The character code for LF is 10, and the string value is "\n".
Logged

Montreal
Offline Offline
Newbie
*
Karma: 0
Posts: 6
For all your IT Needs - 7-13 IT Solutions
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Would anyone know if the connection code will work with GSM Shield or only with an Ethernet Shield?

Thanks
Tony
Logged

Pages: [1]   Go Up
Jump to: