ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/svc/webget/message.b/
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 "";
}