summaryrefslogtreecommitdiff
path: root/appl/svc/webget/message.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/svc/webget/message.b')
-rw-r--r--appl/svc/webget/message.b249
1 files changed, 249 insertions, 0 deletions
diff --git a/appl/svc/webget/message.b b/appl/svc/webget/message.b
new file mode 100644
index 00000000..3bf7778b
--- /dev/null
+++ b/appl/svc/webget/message.b
@@ -0,0 +1,249 @@
+implement Message;
+
+include "sys.m";
+ sys: Sys;
+
+include "string.m";
+ S : String;
+
+include "bufio.m";
+ B : Bufio;
+ Iobuf: import B;
+
+include "message.m";
+ msg: Message;
+
+msglog: ref Sys->FD;
+
+init(bufio: Bufio, smod: String)
+{
+ sys = load Sys Sys->PATH;
+ S = smod;
+ B = bufio;
+}
+
+sptab : con " \t";
+crlf : con "\r\n";
+
+Msg.newmsg() : ref Msg
+{
+ return ref Msg("", nil, nil, nil, 0);
+}
+
+# Read a message header from fd and return a Msg
+# the header fields.
+# If withprefix is true, read one line first and put it
+# in the prefixline field of the Msg (without terminating \r\n)
+# Return nil if there is a read error or eof before the
+# header is completely read.
+Msg.readhdr(io: ref Iobuf, withprefix: int) : (ref Msg, string)
+{
+ m := Msg.newmsg();
+ l : list of Nameval = nil;
+ needprefix := withprefix;
+ for(;;) {
+ line := getline(io);
+ n := len line;
+ if(n == 0) {
+ if(withprefix && m.prefixline != "")
+ break;
+ return(nil, "msg read hdr error: no header");
+ }
+ if(line[n-1] != '\n')
+ return (m, "msg read hdr error: incomplete header");
+ if(n >= 2 && line[n-2] == '\r')
+ line = line[0:n-2];
+ else
+ line = line[0:n-1];
+ if(needprefix) {
+ m.prefixline = line;
+ needprefix = 0;
+ }
+ else {
+ if(line == "")
+ break;
+ if(S->in(line[0], sptab)) {
+ if(l == nil)
+ continue;
+ nv := hd l;
+ l = Nameval(nv.name, nv.value + " " + S->drop(line, sptab)) :: tl l;
+ }
+ else {
+ (nam, val) := S->splitl(line, ":");
+ if(val == nil)
+ continue; # no colon
+ l = Nameval(S->tolower(nam), S->drop(val[1:], sptab)) :: l;
+ }
+ }
+ }
+ nh := len l;
+ if(nh > 0) {
+ m.fields = array[nh] of Nameval;
+ for(i := nh-1; i >= 0; i--) {
+ m.fields[i] = hd l;
+ l = tl l;
+ }
+ }
+ return (m, "");
+}
+
+glbuf := array[300] of byte;
+
+# Like io.gets('\n'), but assume Latin-1 instead of UTF encoding
+getline(io: ref Iobuf): string
+{
+ imax := len glbuf - 1;
+ for(i := 0; i < imax; ) {
+ c := io.getb();
+ if(c < 0)
+ break;
+ if(c < 128)
+ glbuf[i++] = byte c;
+ else
+ i += sys->char2byte(c, glbuf, i);
+ if(c == '\n')
+ break;
+ if(i == imax) {
+ imax += 100;
+ if(imax > 1000)
+ break; # Header lines aren't supposed to be > 1000
+ newglbuf := array[imax] of byte;
+ newglbuf[0:] = glbuf[0:i];
+ glbuf = newglbuf;
+ }
+ }
+ ans := string glbuf[0:i];
+ return ans;
+}
+
+Bbufsize: con 8000;
+
+# Read the body of the message, assuming the header has been processed.
+# If content-length has been specified, read exactly that many bytes
+# or until eof; else read until done.
+# Return "" if all is OK, else return an error string.
+Msg.readbody(m: self ref Msg, io: ref Iobuf) : string
+{
+ (clfnd, cl) := m.fieldval("content-length");
+ if(clfnd) {
+ clen := int cl;
+ if(clen > 0) {
+ m.body = array[clen] of byte;
+ n := B->io.read(m.body, clen);
+ m.bodylen = n;
+ if(n != clen)
+ return "short read";
+ }
+ }
+ else {
+ m.body = array[Bbufsize] of byte;
+ curlen := 0;
+ for(;;) {
+ avail := len m.body - curlen;
+ if(avail <= 0) {
+ newa := array[len m.body + Bbufsize] of byte;
+ if(curlen > 0)
+ newa[0:] = m.body[0:curlen];
+ m.body = newa;
+ avail = Bbufsize;
+ }
+ n := B->io.read(m.body[curlen:], avail);
+ if(n < 0)
+ return sys->sprint("readbody error %r");
+ if(n == 0)
+ break;
+ else
+ curlen += n;
+ }
+ m.bodylen = curlen;
+ }
+ return "";
+}
+
+# Look for name (lowercase) in the fields of m
+# and (1, field value) if found, or (0,"") if not.
+# If multiple fields with the same name exist,
+# the value is defined as the comma separated list
+# of all such values.
+Msg.fieldval(m: self ref Msg, name: string) : (int, string)
+{
+ n := len m.fields;
+ ans := "";
+ found := 0;
+ for(i := 0; i < n; i++) {
+ if(m.fields[i].name == name) {
+ v := m.fields[i].value;
+ if(found)
+ ans = ans + ", " + v;
+ else
+ ans = v;
+ found = 1;
+ }
+ }
+ return (found, ans);
+}
+
+Msg.addhdrs(m: self ref Msg, hdrs: list of Nameval)
+{
+ nh := len hdrs;
+ if(nh == 0)
+ return;
+ onh := len m.fields;
+ newa := array[nh + onh] of Nameval;
+ newa[0:] = m.fields;
+ i := onh;
+ while(hdrs != nil) {
+ newa[i++] = hd hdrs;
+ hdrs = tl hdrs;
+ }
+ m.fields = newa;
+}
+
+Msg.update(m: self ref Msg, name, value: string)
+{
+ for(i := 0; i < len m.fields; i++)
+ if(m.fields[i].name == name) {
+ m.fields[i] = Nameval(name, value);
+ return;
+ }
+ m.addhdrs(Nameval(name, value) :: nil);
+}
+
+Msg.header(m: self ref Msg) : string
+{
+ s := "";
+ for(i := 0; i < len m.fields; i++) {
+ nv := m.fields[i];
+ s += nv.name + ": " + nv.value + "\n";
+ }
+ return s;
+}
+
+Msg.writemsg(m: self ref Msg, io: ref Iobuf) : string
+{
+ n := 0;
+ if(m.prefixline != nil) {
+ n = B->io.puts(m.prefixline);
+ if(n >= 0)
+ n = B->io.puts(crlf);
+ }
+ for(i := 0; i < len m.fields; i++) {
+ nv := m.fields[i];
+ if(n >= 0)
+ n = B->io.puts(nv.name);
+ if(n >= 0)
+ n = B->io.puts(": ");
+ if(n >= 0)
+ n = B->io.puts(nv.value);
+ if(n >= 0)
+ n = B->io.puts(crlf);
+ }
+ if(n >= 0)
+ n = B->io.puts(crlf);
+ if(n >= 0 && m.bodylen > 0)
+ n = B->io.write(m.body, m.bodylen);
+ if(n < 0)
+ return sys->sprint("msg write error: %r");
+ B->io.flush();
+ return "";
+}