diff options
Diffstat (limited to 'appl/svc/webget/message.b')
| -rw-r--r-- | appl/svc/webget/message.b | 249 |
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 ""; +} |
