HTTPS Proxy Configuration (with HTTP Header Modification and URL shortening)

From ContikiWiki

Jump to: navigation, search

[edit] A simple HTTP proxy for connecting to a remote API server using Nginx

Let's consider the following real-world scenario:

  • your ISP doesn't provide IPv6 connectivity
  • you don't have a VPS that could be used for tunnelling
  • you only want to bind a single remote IPv4 address and local IPv6 address
  • what if the API host only accepts HTTPS connections ...
  • what if you can use plain HTTP, though you rather not use it ...

Additionally you may wish to:

  • avoid the entire chunk of HTTP headers going through to 6loWPAN node
  • avoid having to store the API key in the program memory of the node
  • shorten the URL suffix or rewrite it for your needs

[edit] Header optimisation

The idea is to avoid transfer of unnecessary information via the 6loWPAN segment.

It appears that Pachube server sends the following reply headers:

                 % curl -6 http://localhost:82/feeds/504.csv -v
                 * About to connect() to localhost port 82 (#0)
                 *   Trying ::1... connected
                 * Connected to localhost (::1) port 82 (#0)
                 > GET /feeds/504.csv HTTP/1.1
                 > User-Agent: curl/7.21.4 (x86_64-pc-linux-gnu) libcurl/7.21.4 GnuTLS/2.10.5 zlib/1.2.5 libidn/1.22
                 > Host: localhost:82
                 > Accept: */*
                 >
                 < HTTP/1.1 200 OK
                 < Server: nginx/1.0.6
                 < Date: Wed, 02 Nov 2011 18:07:11 GMT
                 < Content-Type: text/plain; charset=utf-8
                 < Connection: keep-alive
                 < Last-Modified: Wed, 02 Nov 2011 18:07:09 GMT
                 < X-Pachube-Logging-Key: logging.6hLvdI8DjdJP1gRr2X30
                 < X-PachubeRequestId: 903ed3dea9830689cac1544ac299f08cc9c36df6
                 < Cache-Control: max-age=5
                 < Content-Length: 232
                 < Age: 0
                 < Vary: Accept-Encoding
                 <
                 0,2011-11-02T18:07:09.396577Z,18
                 1,2011-11-02T18:07:09.396577Z,93
                 2,2011-11-02T18:07:09.396577Z,292
                 3,2011-11-02T18:07:09.396577Z,0
                 4,2011-11-02T18:07:09.396577Z,0
                 5,2011-11-02T18:07:09.396577Z,0
                 * Connection #0 to host localhost left intact
                 * Closing connection #0
                 6,2011-11-02T18:07:09.396577Z,-28307


The minimum header that it could be compressed to is:

                 < HTTP/1.1 200 OK
                 < Content-Type: text/plain; charset=utf-8
                 < Connection: keep-alive
                 < Content-Length: 232
                 <

However for HTTP/1.1 compliance this is what has to be there:

                 < HTTP/1.1 200 OK
                 < Server: nginx/1.0.6
                 < Date: Wed, 02 Nov 2011 21:47:05 GMT
                 < Content-Type: text/plain; charset=utf-8
                 < Connection: keep-alive
                 < Content-Length: 22
                 <

Let's presume that these details will never be of any use:

                 < Age: 0
                 < Vary: Accept-Encoding

There is hardly any use for this either:

                 < Cache-Control: max-age=5

As well as 'Last-Modified', because it is as you notice the same as the second column on the firs line of CSV:

                 < Last-Modified: Wed, 02 Nov 2011 18:07:09 GMT

I really don't think that this could be used by a node:

                 < X-Pachube-Logging-Key: logging.6hLvdI8DjdJP1gRr2X30
                 < X-PachubeRequestId: 903ed3dea9830689cac1544ac299f08cc9c36df6

[edit] Pachube example

This is multi-port configuration, hopefully it is quite self-explanatory. For up-to-date version please see pachube_proxy.conf

user nginx nginx; worker_processes 3;

error_log log/nginx/pachube-api.error_log debug;

events {
 worker_connections 1024;
 use epoll;
}

http {

 upstream pachube-ssl-proxy {
   server 173.203.98.29:443;
 }

 server {
   # curl https://api.pachube.com/v1/feeds/504.csv
   # curl -6 http://localhost:81/feeds/504.csv
   listen [::0]:81;
   location / {
     proxy_set_header host api.pachube.com;
     proxy_set_header X-PachubeApiKey COPY_YOUR_TOP_SECRET_STRING_OF_43_CHARCTERS;
     proxy_hide_header Age;
     proxy_hide_header Vary;
     proxy_hide_header Last-Modified;
     proxy_hide_header X-Pachube-Logging-Key;
     proxy_hide_header X-PachubeRequestId;
     proxy_hide_header Cache-Control;
     proxy_pass https://pachube-ssl-proxy/v1/;
   }
 }

 server {
   # curl https://api.pachube.com/v2/feeds/504.csv
   # curl -6 http://localhost:82/feeds/504.csv
   listen [::0]:82;
   location / {
     proxy_set_header host api.pachube.com;
     proxy_set_header X-PachubeApiKey COPY_YOUR_TOP_SECRET_STRING_OF_43_CHARCTERS;
     proxy_hide_header Age;
     proxy_hide_header Vary;
     proxy_hide_header Last-Modified;
     proxy_hide_header X-Pachube-Logging-Key;
     proxy_hide_header X-PachubeRequestId;
     proxy_hide_header Cache-Control;
     proxy_pass https://pachube-ssl-proxy/v2/;
   }
 }

 server {
   listen [::0]:80;
   location / {
     proxy_set_header host pachube.com;
     proxy_pass https://pachube-ssl-proxy;
   }
 }

 server {
   # curl -6 http://localhost:83/504/datastreams/0/history.csv
   listen [::0]:83;
   location / {
     proxy_set_header host pachube.com;
     proxy_pass https://pachube-ssl-proxy/feeds/;
   }
 }
}

It works well with this example (provided you have supplied the API key in the Nginx config).

#include "contiki-net.h"
#include <string.h>

static struct etimer timer;
static struct psock ps;
static char buffer[100];

PROCESS(example_psock_client_process, "Pachube test client");
AUTOSTART_PROCESSES(&example_psock_client_process);

/*---------------------------------------------------------------------------*/
static int
handle_connection(struct psock *p)
{
 PSOCK_BEGIN(p);

 PSOCK_SEND_STR(p, "GET /feeds/504.csv HTTP/1.0\r\n");
 PSOCK_SEND_STR(p, "User-Agent: econotag\r\n");
 PSOCK_SEND_STR(p, "\r\n");

 while(1) {
   PSOCK_READTO(p, '\n');
   printf("Got: %s", buffer);
 }

 PSOCK_END(p);
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(example_psock_client_process, ev, data)
{
 uip_ip6addr_t addr;

 PROCESS_BEGIN();

 /* Use nginx proxy that is mapping our local
  * `::0:82` to `https://api.pachub.com/v2/`
 */
 uip_ip6addr(&addr, 0xaaaa,0,0,0,0,0,0,1);
 tcp_connect(&addr, UIP_HTONS(82), NULL);

 printf("Trying...\n");
 PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event);

 if(uip_aborted() || uip_timedout() || uip_closed()) {
   printf("Failed!\n");
 } else if(uip_connected()) {
   printf("Connected.\n");

   PSOCK_INIT(&ps, buffer, sizeof(buffer));

   do {
     handle_connection(&ps);
     PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event);
   } while(!(uip_closed() || uip_aborted() || uip_timedout()));

   printf("\nClosed.\n");
 }
 PROCESS_END();
}
/*---------------------------------------------------------------------------*/

Personal tools