implement Http_client; include "string.m"; str : String; include "sys.m"; sys : Sys; include "draw.m"; include "bufio.m"; bufio : Bufio; Iobuf : import bufio; include "http_client.m"; POST(bio : ref Iobuf, host, uri, http_version : string, headers : list of string, body : array of byte) { bio.puts("POST " + uri + " HTTP/" + http_version + "\r\n" + "host: " + host + "\r\n"); h : string; for(hdrs := headers; hdrs != nil; hdrs = tl hdrs) { h = hd hdrs; if(len h > 0) bio.puts(h + "\r\n"); } if(body != nil) { bio.puts(sys->sprint("Content-Length: %d\r\n", len body)); bio.puts("\r\n"); bio.write(body, len body); } bio.puts("\r\n"); } Response.read(bio : ref Bufio->Iobuf) : ref Response { header, nl: string; k, v : string; chunked := 0; (header, nl) = str->splitl(bio.gets('\n'), "\n\r"); if(header == "") return nil; (nil, bits) := sys->tokenize(header, " "); response := ref Response(bits, nil, "", nil, "", 0); (header, nl) = str->splitl(bio.gets('\n'), "\n\r"); while(len header > 0) { (k, v) = str->splitl(header, ":"); v = str->drop(v, ": "); case k { "Content-Type" => response.content_type = v; "Content-Length" => (response.content_length, nil) = str->toint(v, 10); "Last-Modified" => response.last_modified = v; "Transfer-Encoding" => case v { "chunked" => chunked = 1; }; * => response.headers += header + "\n"; }; (header, nl) = str->splitl(bio.gets('\n'), "\n\r"); } if(chunked) { response.body = read_chunked(bio); } else { if(response.content_length > 0) { response.body = read_content(bio, response.content_length); } } return response; } read_content(bio : ref Bufio->Iobuf, content_length:int) : array of byte { block := array[content_length] of byte; bio.read(block, len block); return block; } build_array_from_rev_list(blocks : list of array of byte, array_size : int) : array of byte { body := array[array_size] of byte; s := array_size - len hd blocks; while(blocks != nil) { body[s:] = hd blocks; s -= len hd blocks; blocks = tl blocks; } return body; } read_unchunked(bio : ref Bufio->Iobuf, block_size:int) : array of byte { blocks : list of array of byte; block := array[block_size] of byte; total_blocksize := 0; for(read:= bio.read(block, len block) ; read > 0; read = bio.read(block, len block)) { total_blocksize += read; blocks = block :: blocks; } return build_array_from_rev_list(blocks, total_blocksize); } read_chunked(bio : ref Bufio->Iobuf) : array of byte { chunks : list of array of byte; total_chunksize := 0; for((chunksize, chunk) := read_chunk(bio); chunksize > 0; (chunksize, chunk) = read_chunk(bio)) { total_chunksize += chunksize; chunks = chunk :: chunks; } return build_array_from_rev_list(chunks, total_chunksize); } read_chunk(bio : ref Bufio->Iobuf) : (int, array of byte) { (chunk_info, nil) := str->splitl(bio.gets('\n'), "\n\r"); (chunk_size, nil) := str->toint(chunk_info, 16); if(chunk_size > 0) { data := array[chunk_size] of byte; i:= bio.read(data, chunk_size); return (i, data); } else { return (0, nil); } } Response.to_string(r: self ref Response) : string { s := hd r.status+ " " + hd tl r.status + "\n"; s += "Content-Type: " + r.content_type + "\n"; s += sys->sprint("Content-Length: %d\n", r.content_length); s += "Last-Modified: " + r.last_modified + "\n"; s += r.headers + "\n" + string r.body; return s; } Connection.read_response(c: self ref Connection) : ref Response { bufio = load Bufio Bufio->PATH; bio := bufio->fopen(c.c.dfd, Bufio->OREAD); return Response.read(bio); } new_connection(dialstring : string) : ref Connection { sys = load Sys Sys->PATH; str = load String String->PATH; bufio = load Bufio Bufio->PATH; (success, conn) := sys->dial(dialstring, nil); if (success) { return ref Connection(ref conn); } return nil; } urlencode(data: string) : string { c, length, status : int; plain := array[127] of { 45=>'-', 46=>'.', 48=>'0', 49=>'1', 50=>'2', 51=>'3', 52=>'4', 53=>'5', 54=>'6', 55=>'7', 56=>'8', 57=>'9', 65=>'A', 66=>'B', 67=>'C', 68=>'D', 69=>'E', 70=>'F', 71=>'G', 72=>'H', 73=>'I', 74=>'J', 75=>'K', 76=>'L', 77=>'M', 78=>'N', 79=>'O', 80=>'P', 81=>'Q', 82=>'R', 83=>'S', 84=>'T', 85=>'U', 86=>'V', 87=>'W', 88=>'X', 89=>'Y', 90=>'Z', 95=>'_', 97=>'a', 98=>'b', 99=>'c', 100=>'d', 101=>'e', 102=>'f', 103=>'g', 104=>'h', 105=>'i', 106=>'j', 107=>'k', 108=>'l', 109=>'m', 110=>'n', 111=>'o', 112=>'p', 113=>'q', 114=>'r', 115=>'s', 116=>'t', 117=>'u', 118=>'v', 119=>'w', 120=>'x', 121=>'y', 122=>'z' , * => 0}; bytes := array of byte data; encoded := ""; i := 0; status = 1; while(i < len bytes && status != 0) { (c, length, status) = sys->byte2char(bytes, i); if(status > 0) if (c < len plain && plain[c] > 0) encoded += sys->sprint("%c", plain[c]); else encoded += sys->sprint("%%%X", c); i += length; } return encoded; }