aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Peach <mrpeach@users.sourceforge.net>2011-01-13 18:54:34 +0000
committerMartin Peach <mrpeach@users.sourceforge.net>2011-01-13 18:54:34 +0000
commit7c6492599d64e3125af81e1c6442c1c1c43ad187 (patch)
tree3e47f7a9f2d218fca6f99c25aa4120ed38e7c947
parentec9960cb49988a010b76b537f7fd4f7391a208a5 (diff)
Two externals to process HTTP/1.1 requests and responses in conjunction with net exernals like [tcpclient] or
[slipenc]/[slipdec]. So far only GET requests work. svn path=/trunk/externals/mrpeach/; revision=14735
-rw-r--r--net/httpreceive-help.pd94
-rw-r--r--net/httpreceive.c214
-rw-r--r--net/httpreq-help.pd94
-rw-r--r--net/httpreq.c107
4 files changed, 509 insertions, 0 deletions
diff --git a/net/httpreceive-help.pd b/net/httpreceive-help.pd
new file mode 100644
index 0000000..1ea8800
--- /dev/null
+++ b/net/httpreceive-help.pd
@@ -0,0 +1,94 @@
+#N canvas 389 46 712 733 10;
+#X obj 119 76 httpreq;
+#X obj 73 15 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1
+-1;
+#X msg 91 31 GET http://132.205.142.12/index.php;
+#X obj 136 269 unpack 0 0 0 0;
+#X floatatom 136 292 3 0 0 0 - - -;
+#X floatatom 163 292 3 0 0 0 - - -;
+#X floatatom 190 292 3 0 0 0 - - -;
+#X floatatom 217 292 3 0 0 0 - - -;
+#X text 81 310 from;
+#X obj 119 217 tcpclient;
+#X obj 153 243 tgl 15 0 empty empty connected 18 7 0 8 -24198 -13381
+-1 0 1;
+#X text 190 216 tcpclient opens a tcp socket to send and receive bytes
+on;
+#X floatatom 270 302 9 0 0 0 - - -;
+#X floatatom 313 280 9 0 0 0 - - -;
+#X text 382 279 Size of the send buffer;
+#X obj 270 234 route sent buf blocked;
+#X text 340 301 Number of bytes sent;
+#X obj 356 258 print sender_blocked!;
+#X msg 85 166 connect 132.205.142.12 80;
+#X obj 119 410 httpreceive;
+#X floatatom 182 443 5 0 0 0 - - -;
+#X symbolatom 150 602 10 0 0 0 - - -;
+#X obj 150 580 prepend set;
+#X symbolatom 216 560 50 0 0 0 - - -;
+#X obj 216 538 prepend set;
+#X obj 150 486 route reason Date Content-Length Content-Type;
+#X symbolatom 350 528 50 0 0 0 - - -;
+#X obj 350 506 prepend set;
+#X floatatom 283 518 5 0 0 0 - - -;
+#X msg 111 51 GET http://132.205.142.12/nothing;
+#X msg 44 125 dump \$1;
+#X obj 44 99 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1
+;
+#X obj 37 288 tgl 15 0 empty empty empty 17 7 0 10 -4034 -257985 -1
+1 1;
+#X msg 37 311 verbosity \$1;
+#X obj 457 503 print more_status;
+#X obj 76 352 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1
+-1;
+#X text 175 115 So far only GET requests are supported;
+#X text 515 686 Author: Martin Peach;
+#X text 515 703 Date: 2011/01/13;
+#X text 224 441 The right outlet is the status code.;
+#X text 166 653 The left outlet is the message body as a list of bytes
+;
+#X text 154 465 The middle outlet is the status header as name/value
+pairs;
+#X obj 119 653 spigot;
+#X obj 119 683 print message_body;
+#X obj 152 631 tgl 15 0 empty empty printing_long_messages_can_hang_Pd
+17 7 0 10 -4034 -257985 -1 0 1;
+#X text 175 77 [httpreq] sends an HTTP/1.1 request as a list of bytes
+(actually float atoms) \, suitable for [tcpclient];
+#X text 195 404 [htpreceive] expects an HTTP/1.1 response as one or
+more lists of bytes.;
+#X text 187 517 message length:;
+#X connect 0 0 9 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 0;
+#X connect 3 0 4 0;
+#X connect 3 1 5 0;
+#X connect 3 2 6 0;
+#X connect 3 3 7 0;
+#X connect 9 0 19 0;
+#X connect 9 1 3 0;
+#X connect 9 2 10 0;
+#X connect 9 3 15 0;
+#X connect 15 0 12 0;
+#X connect 15 1 13 0;
+#X connect 15 2 17 0;
+#X connect 18 0 9 0;
+#X connect 19 0 42 0;
+#X connect 19 1 25 0;
+#X connect 19 2 20 0;
+#X connect 22 0 21 0;
+#X connect 24 0 23 0;
+#X connect 25 0 22 0;
+#X connect 25 1 24 0;
+#X connect 25 2 28 0;
+#X connect 25 3 27 0;
+#X connect 25 4 34 0;
+#X connect 27 0 26 0;
+#X connect 29 0 0 0;
+#X connect 30 0 9 0;
+#X connect 31 0 30 0;
+#X connect 32 0 33 0;
+#X connect 33 0 19 0;
+#X connect 35 0 19 0;
+#X connect 42 0 43 0;
+#X connect 44 0 42 1;
diff --git a/net/httpreceive.c b/net/httpreceive.c
new file mode 100644
index 0000000..6c9e5a2
--- /dev/null
+++ b/net/httpreceive.c
@@ -0,0 +1,214 @@
+/* httpreceive.c Started by Martin Peach 20110111 */
+/* httpreceive will process http 1.1 responses received as lists of bytes from something like [tcpclient] */
+/* See http://www.w3.org/Protocols/rfc2616/rfc2616.html */
+
+#include "m_pd.h"
+#include <stdio.h>
+#include <string.h>
+
+static t_class *httpreceive_class;
+
+#define STATUS_BUF_LEN 1024
+
+typedef struct _httpreceive
+{
+ t_object x_obj;
+ t_outlet *x_status_out;
+ t_outlet *x_message_out;
+ t_outlet *x_statuscode_out;
+ int x_state;
+ int x_remaining;
+ int x_verbosity;
+ char *x_status_buf;
+ size_t x_status_buf_len;
+ int x_status_buf_write_index;
+} t_httpreceive;
+
+#define MAX_GETSTRING 256
+
+static void httpreceive_bang(t_httpreceive *x);
+static void httpreceive_list(t_httpreceive *x, t_symbol *s, int argc, t_atom *argv);
+static void httpreceive_verbosity(t_httpreceive *x, t_floatarg verbosity);
+static void httpreceive_free (t_httpreceive *x);
+static void *httpreceive_new (void);
+void httpreceive_setup(void);
+
+static void httpreceive_bang(t_httpreceive *x)
+{
+ post("httpreceive_bang %p", x);
+}
+
+static void httpreceive_list(t_httpreceive *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int i, j, k, m, n, message_len = 0;
+ char buf[256];
+ t_atom status_list[2];
+
+ /* first check that all the atoms are integer floats on 0-255 */
+ for (i = 0; i < argc; ++i)
+ {
+ if (argv[i].a_type != A_FLOAT)
+ {
+ pd_error(x, "list element %d is not a float", i);
+ return;
+ }
+ j = argv[i].a_w.w_float;
+ if (j != argv[i].a_w.w_float)
+ {
+ pd_error(x, "list element %d is not an integer", i);
+ return;
+ }
+ if ((j < -128) || (j > 255))
+ {
+ pd_error(x, "list element %d is not on [0...255]", i);
+ return;
+ }
+ }
+ if (x->x_verbosity) post ("httpreceive_list %d elements, x_state %d x_remaining %d", i, x->x_state, x->x_remaining);
+
+
+ for (i = 0; ((i < argc) && (x->x_state == 0)); ++i)
+ {
+ j = argv[i].a_w.w_float;
+ x->x_status_buf[x->x_status_buf_write_index] = j;
+ /* status ends with CRLFCRLF */
+ if
+ (
+ (x->x_status_buf_write_index > 3)
+ && (j == 10) && (x->x_status_buf[x->x_status_buf_write_index-1] == 13)
+ && (x->x_status_buf[x->x_status_buf_write_index-2] == 10)
+ && (x->x_status_buf[x->x_status_buf_write_index-3] == 13)
+ )
+ {
+ x->x_state = 1;/* complete status header is in x->x_status_buf */
+ x->x_status_buf[x->x_status_buf_write_index+1] = 0;
+ if (x->x_verbosity) post("httpreceive_list: status: %s", x->x_status_buf);
+ /* get status code from first line */
+ if (1 != sscanf(x->x_status_buf, "HTTP/1.1 %d", &j))
+ {
+ pd_error(x, "httpreceive_list: malformed status line");
+ post("httpreceive_list: status: %s", x->x_status_buf);
+ }
+ else
+ {
+ outlet_float(x->x_statuscode_out, j);
+ if (x->x_verbosity) post("httpreceive_list: status code: %d", j);
+ for (j = 8; j < x->x_status_buf_write_index; ++j)
+ if (x->x_status_buf[j] >= 'A') break; /* skip to start of reason phrase */
+ for (k = 0; j < x->x_status_buf_write_index; ++j, ++k)
+ {
+ if (13 == x->x_status_buf[j]) break;
+ buf[k] = x->x_status_buf[j];/* copy reason phrase to buf */
+ }
+ buf[k] = 0;
+ /* make buf into a symbol */
+ SETSYMBOL(&status_list[0], gensym(buf));
+ /* output it through status outlet */
+ outlet_anything(x->x_status_out, gensym("reason"), 1, &status_list[0]);
+ /* loop through all the response header fields */
+
+ do
+ {
+ /* skip to first non-whitespace on next line: */
+ for (; j < x->x_status_buf_write_index; ++j)
+ if (x->x_status_buf[j] > 32) break;
+ /* copy the field name to buf */
+ for (k = 0; j < x->x_status_buf_write_index; ++j, ++k)
+ {
+ if (':' == x->x_status_buf[j]) break;
+ buf[k] = x->x_status_buf[j];
+ }
+ j++; /* skip the colon */
+ buf[k] = 0;
+ SETSYMBOL(&status_list[0], gensym(buf)); /* field name */
+ /* skip whitespace: */
+ for (; j < x->x_status_buf_write_index; ++j)
+ if (x->x_status_buf[j] != 32) break;
+ /* copy the token to buf */
+ for (k = 0; j < x->x_status_buf_write_index; ++j, ++k)
+ {
+ if (13 == x->x_status_buf[j]) break;
+ buf[k] = x->x_status_buf[j];
+ }
+ buf[k] = 0;
+ /* if the value is a number, set it to a float, else it's a symbol */
+ for (m = 0; m < k; ++m) if ((buf[m] < '0' || buf[m] > '9')) break;
+ if (m == k)
+ {
+ sscanf(buf, "%d", &n);
+ SETFLOAT(&status_list[1], n);
+ /* if this is Content-Length, we know the message_length */
+ if (atom_getsymbol(&status_list[0]) == gensym("Content-Length")) x->x_remaining = n;
+ }
+ else SETSYMBOL(&status_list[1], gensym(buf));
+ outlet_anything(x->x_status_out, status_list[0].a_w.w_symbol, 1, &status_list[1]);
+ } while (j < x->x_status_buf_write_index-3);
+
+ }
+ } // if end of status response
+ else x->x_status_buf_write_index++;
+ if (x->x_status_buf_write_index >= STATUS_BUF_LEN)
+ {
+ pd_error(x, "httpreceive_list: status buffer full");
+ x->x_status_buf_write_index = 0;
+ x->x_state = 0;
+ x->x_remaining = 0;
+ }
+ } // for each byte
+ if (1 == x->x_state)
+ {
+ /* any remaining atoms are the message body. For now just output them */
+ /* but if the incoming bytes are in more than one list this won't work, */
+ /*we'll need to cache them until we have Content-Length */
+ if (x->x_verbosity) post ("httpreceive_list: x->x_remaining is %d", x->x_remaining);
+ message_len = argc - i;
+ if (x->x_verbosity) post ("httpreceive_list: message_len is %d", message_len);
+ if (message_len <= x->x_remaining) x->x_remaining -= message_len;
+ outlet_list(x->x_message_out, &s_list, message_len, &argv[i]);
+ x->x_status_buf_write_index = 0;
+ if (0 == x->x_remaining) x->x_state = 0;
+ }
+}
+
+static void httpreceive_verbosity(t_httpreceive *x, t_float verbosity)
+{
+ x->x_verbosity = verbosity;
+ if (x->x_verbosity != 0) post ("httpreceive_verbosity %d", x->x_verbosity);
+}
+
+static void httpreceive_free (t_httpreceive *x)
+{
+ if ((NULL != x->x_status_buf)&&(0 != x->x_status_buf_len)) freebytes(x->x_status_buf, x->x_status_buf_len);
+}
+
+static void *httpreceive_new (void)
+{
+ t_httpreceive *x = (t_httpreceive *)pd_new(httpreceive_class);
+ if (NULL != x)
+ {
+ x->x_message_out = outlet_new(&x->x_obj, &s_anything);
+ x->x_status_out = outlet_new(&x->x_obj, &s_anything);
+ x->x_statuscode_out = outlet_new(&x->x_obj, &s_float); /* rightmost outlet */
+ x->x_state = 0; /* waiting for a list of bytes */
+ x->x_status_buf_len = STATUS_BUF_LEN;
+ if (NULL == (x->x_status_buf = getbytes(STATUS_BUF_LEN)))
+ {
+ pd_error(x, "httpreceive_new: no memory available for x_status_buf");
+ x->x_status_buf_len = 0;
+ }
+ x->x_status_buf_write_index = 0;
+ x->x_remaining = 0;
+ x->x_verbosity = 0;
+ }
+
+ return (void *)x;
+}
+
+void httpreceive_setup(void)
+{
+ httpreceive_class = class_new(gensym("httpreceive"), (t_newmethod)httpreceive_new, (t_method)httpreceive_free, sizeof(t_httpreceive), CLASS_DEFAULT, 0);
+ class_addbang(httpreceive_class, httpreceive_bang);
+ class_addlist (httpreceive_class, (t_method)httpreceive_list);
+ class_addmethod(httpreceive_class, (t_method)httpreceive_verbosity, gensym("verbosity"), A_FLOAT, 0);
+}
+/* fin httpreceive.c */
diff --git a/net/httpreq-help.pd b/net/httpreq-help.pd
new file mode 100644
index 0000000..1ea8800
--- /dev/null
+++ b/net/httpreq-help.pd
@@ -0,0 +1,94 @@
+#N canvas 389 46 712 733 10;
+#X obj 119 76 httpreq;
+#X obj 73 15 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1
+-1;
+#X msg 91 31 GET http://132.205.142.12/index.php;
+#X obj 136 269 unpack 0 0 0 0;
+#X floatatom 136 292 3 0 0 0 - - -;
+#X floatatom 163 292 3 0 0 0 - - -;
+#X floatatom 190 292 3 0 0 0 - - -;
+#X floatatom 217 292 3 0 0 0 - - -;
+#X text 81 310 from;
+#X obj 119 217 tcpclient;
+#X obj 153 243 tgl 15 0 empty empty connected 18 7 0 8 -24198 -13381
+-1 0 1;
+#X text 190 216 tcpclient opens a tcp socket to send and receive bytes
+on;
+#X floatatom 270 302 9 0 0 0 - - -;
+#X floatatom 313 280 9 0 0 0 - - -;
+#X text 382 279 Size of the send buffer;
+#X obj 270 234 route sent buf blocked;
+#X text 340 301 Number of bytes sent;
+#X obj 356 258 print sender_blocked!;
+#X msg 85 166 connect 132.205.142.12 80;
+#X obj 119 410 httpreceive;
+#X floatatom 182 443 5 0 0 0 - - -;
+#X symbolatom 150 602 10 0 0 0 - - -;
+#X obj 150 580 prepend set;
+#X symbolatom 216 560 50 0 0 0 - - -;
+#X obj 216 538 prepend set;
+#X obj 150 486 route reason Date Content-Length Content-Type;
+#X symbolatom 350 528 50 0 0 0 - - -;
+#X obj 350 506 prepend set;
+#X floatatom 283 518 5 0 0 0 - - -;
+#X msg 111 51 GET http://132.205.142.12/nothing;
+#X msg 44 125 dump \$1;
+#X obj 44 99 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1
+;
+#X obj 37 288 tgl 15 0 empty empty empty 17 7 0 10 -4034 -257985 -1
+1 1;
+#X msg 37 311 verbosity \$1;
+#X obj 457 503 print more_status;
+#X obj 76 352 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1
+-1;
+#X text 175 115 So far only GET requests are supported;
+#X text 515 686 Author: Martin Peach;
+#X text 515 703 Date: 2011/01/13;
+#X text 224 441 The right outlet is the status code.;
+#X text 166 653 The left outlet is the message body as a list of bytes
+;
+#X text 154 465 The middle outlet is the status header as name/value
+pairs;
+#X obj 119 653 spigot;
+#X obj 119 683 print message_body;
+#X obj 152 631 tgl 15 0 empty empty printing_long_messages_can_hang_Pd
+17 7 0 10 -4034 -257985 -1 0 1;
+#X text 175 77 [httpreq] sends an HTTP/1.1 request as a list of bytes
+(actually float atoms) \, suitable for [tcpclient];
+#X text 195 404 [htpreceive] expects an HTTP/1.1 response as one or
+more lists of bytes.;
+#X text 187 517 message length:;
+#X connect 0 0 9 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 0;
+#X connect 3 0 4 0;
+#X connect 3 1 5 0;
+#X connect 3 2 6 0;
+#X connect 3 3 7 0;
+#X connect 9 0 19 0;
+#X connect 9 1 3 0;
+#X connect 9 2 10 0;
+#X connect 9 3 15 0;
+#X connect 15 0 12 0;
+#X connect 15 1 13 0;
+#X connect 15 2 17 0;
+#X connect 18 0 9 0;
+#X connect 19 0 42 0;
+#X connect 19 1 25 0;
+#X connect 19 2 20 0;
+#X connect 22 0 21 0;
+#X connect 24 0 23 0;
+#X connect 25 0 22 0;
+#X connect 25 1 24 0;
+#X connect 25 2 28 0;
+#X connect 25 3 27 0;
+#X connect 25 4 34 0;
+#X connect 27 0 26 0;
+#X connect 29 0 0 0;
+#X connect 30 0 9 0;
+#X connect 31 0 30 0;
+#X connect 32 0 33 0;
+#X connect 33 0 19 0;
+#X connect 35 0 19 0;
+#X connect 42 0 43 0;
+#X connect 44 0 42 1;
diff --git a/net/httpreq.c b/net/httpreq.c
new file mode 100644
index 0000000..e0b764a
--- /dev/null
+++ b/net/httpreq.c
@@ -0,0 +1,107 @@
+/* httptreq.c Started by Martin Peach 20110111 */
+/* httpreq will generate http 1.1 requests as lists of bytes suitable for input to tcpclient */
+/* See http://www.w3.org/Protocols/rfc2616/rfc2616.html */
+
+#include "m_pd.h"
+#include <stdio.h>
+#include <string.h>
+
+static t_class *httpreq_class;
+
+typedef struct _httpreq
+{
+ t_object x_obj;
+ t_outlet *x_reqout;
+} t_httpreq;
+
+#define MAX_GETSTRING 256
+
+void httpreq_bang(t_httpreq *x)
+{
+ post("httpreq_bang %p", x);
+}
+
+static void httpreq_get(t_httpreq *x, t_symbol *s)
+{
+ unsigned int i, j, len;
+ char buf[MAX_GETSTRING];
+ char request_line[1024];
+ t_atom request_atoms[1024];
+
+ len = strlen (s->s_name);
+ if (len > MAX_GETSTRING)
+ {
+ pd_error(x, "httpreq_get: string too long (%d), should be less than %d", len, MAX_GETSTRING);
+ return;
+ }
+ for (i = 0; i < strlen(s->s_name); ++i) buf[i] = s->s_name[i];
+ buf[i] = 0;
+// post("httpreq_get %s (%s)", s->s_name, buf);
+ if (0 != strncmp("http://", buf, 7))
+ {
+ pd_error(x, "httpreq_get: url doesn't begin with 'http://' (%d)", len);
+ return;
+ }
+/*
+5.1 Request-Line
+The Request-Line begins with a method token,
+followed by the Request-URI and the protocol version,
+and ending with CRLF.
+The elements are separated by SP characters.
+No CR or LF is allowed except in the final CRLF sequence.
+
+Request-Line = Method SP Request-URI SP HTTP-Version CRLF
+*/
+ j = sprintf(request_line, "GET ");
+ for (i = 7; i < len; ++i)
+ { /* skip "http://" and the host name */
+ if ('/' == buf[i]) break;
+ }
+ for (; i < len; ++i, ++j)
+ {
+ if (buf[i] <= 0x20) break;
+ request_line[j] = buf[i];
+ }
+ j += sprintf(&request_line[j], " HTTP/1.1");
+ request_line[j++] = 0xD; // <CR>
+ request_line[j++] = 0xA; // <LF>
+ j += sprintf(&request_line[j], "Host: ");
+ for (i = 7; i < len; ++i, ++j)
+ { /* copy the host name */
+ if ('/' == buf[i]) break;
+ request_line[j] = buf[i];
+ }
+ request_line[j++] = 0xD; // <CR>
+ request_line[j++] = 0xA; // <LF>
+ request_line[j++] = 0xD; // <CR>
+ request_line[j++] = 0xA; // <LF>
+ request_line[j] = 0; // terminate string
+
+// output the request line as a list of floats
+ for (i = 0; i < j; ++i)
+ {
+ SETFLOAT(&request_atoms[i], request_line[i]);
+// post("%f", request_atoms[i].a_w.w_float);
+ }
+ post ("j is %d", j);
+ post("httpreq_get: %s", request_line);
+ outlet_list(x->x_reqout, &s_list, j, &request_atoms[0]);
+}
+
+static void *httpreq_new (void)
+{
+ t_httpreq *x = (t_httpreq *)pd_new(httpreq_class);
+ if (NULL != x)
+ {
+ x->x_reqout = outlet_new(&x->x_obj, &s_anything);
+ }
+ return (void *)x;
+}
+
+void httpreq_setup(void)
+{
+ httpreq_class = class_new(gensym("httpreq"), (t_newmethod)httpreq_new, 0, sizeof(t_httpreq), CLASS_DEFAULT, 0);
+ class_addbang(httpreq_class, httpreq_bang);
+ class_addmethod (httpreq_class, (t_method)httpreq_get, gensym ("GET"), A_DEFSYM, 0);
+}
+/* fin httpreq.c */