implement Httpc; include "string.m"; str : String; include "sys.m"; sys : Sys; include "draw.m"; include "sslsession.m"; include "keyring.m"; include "asn1.m"; include "pkcs.m"; include "x509.m"; include "ssl3.m"; ssl3: SSL3; Context: import ssl3; include "httpc.m"; debug = 0; Request.header_bytes(r: self ref Request) : array of byte { txt := r.method + " " + r.uri + " HTTP/" + r.http_version + "\r\n" + "Host: " + r.host + "\r\n"; h : string; for(hdrs := r.headers; hdrs != nil; hdrs = tl hdrs) { h = hd hdrs; if(len h > 0) txt += h + "\r\n"; } if(r.method == "POST" && r.body != nil && len r.body > 0) txt += "Content-Length: " + string len r.body + "\r\n"; txt += "\r\n"; return array of byte txt; } Connection.send_request(c: self ref Connection, r : ref Request) : ref Response { hbytes := r.header_bytes(); if(c.ssl_connection != nil) c.ssl_connection.write(hbytes, len hbytes); else sys->write(c.c.dfd, hbytes, len hbytes); if(debug) sys->print("%s", string hbytes); if(r.method == "POST" && r.body != nil && len r.body > 0) { if(c.ssl_connection != nil) { c.ssl_connection.write(r.body, len r.body); } else { sys->write(c.c.dfd, r.body, len r.body); } if(debug) sys->print("%s\n", string r.body); } return c.read_response(); } Connection.gets(c: self ref Connection, delim : byte) : string { a := array[1] of byte; s := ""; if(c.ssl_connection != nil) { while(c.ssl_connection.read(a, 1) == 1) { s += string a; if(a[0] == delim) break; } } else { while(sys->read(c.c.dfd, a, 1) == 1) { s += string a; if(a[0] == delim) break; } } return s; } Connection.read_response(c: self ref Connection) : ref Response { header, nl: string; k, v : string; chunked := 0; (header, nl) = str->splitl(c.gets(byte '\n'), "\n\r"); if(header == "") return nil; (nil, bits) := sys->tokenize(header, " "); response := ref Response(bits, nil, "", nil, "", 0); (header, nl) = str->splitl(c.gets(byte '\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(c.gets(byte '\n'), "\n\r"); } # if(response.content_length > 0) if(chunked) { response.body = c.read_chunked(); } else { response.body = c.read_unchunked(response.content_length); } # else # response.body = nil; return response; } Response.body_string(r : self ref Response) : string { s := ""; blocks := r.body; while(blocks != nil) { s = string hd blocks + s; blocks = tl blocks; } return s; } Connection.read_unchunked(c: self ref Connection, block_size:int) : list of array of byte { blocks : list of array of byte; block := array[block_size] of byte; read : int; if(c.ssl_connection != nil) do { read = c.ssl_connection.read(block, len block); if(read > 0) blocks = block[0:read] :: blocks; } while (read > 0); else do { read = sys->read(c.c.dfd, block, len block); if(read > 0) blocks = block[0:read] :: blocks; } while (read > 0); return blocks; } Connection.read_chunked(c: self ref Connection) : list of array of byte { chunks : list of array of byte; (chunk_info, nil) := str->splitl(c.gets(byte '\n'), "\n\r"); (chunk_size, nil) := str->toint(chunk_info, 16); while(chunk_size > 0) { data := array[chunk_size] of byte; read, offset : int; offset = 0; while(chunk_size > 0) { if(c.ssl_connection != nil) read = c.ssl_connection.read(data[offset:], chunk_size); else read = sys->read(c.c.dfd, data[offset:], chunk_size); if(read < 1) break; chunk_size -= read; offset += read; } chunks = data :: chunks; c.gets(byte '\n'); (chunk_info, nil) = str->splitl(c.gets(byte '\n'), "\n\r"); (chunk_size, nil) = str->toint(chunk_info, 16); } return chunks; } 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" + r.body_string(); return s; } init() { sys = load Sys Sys->PATH; str = load String String->PATH; ssl3 = load SSL3 SSL3->PATH; ssl3->init(); } new_connection(dialstring : string) : ref Connection { (success, conn) := sys->dial(dialstring, nil); if (success == 0) { return ref Connection(ref conn, nil, 0); } return nil; } new_ssl_connection(dialstring : string) : ref Connection { err : string; plain := new_connection(dialstring); if(plain == nil) err = "Dial failed"; else err = ""; if(err == "") { plain.ssl_connection = ssl3->Context.new(); info := ref SSL3->Authinfo(ssl_suites, ssl_comprs, nil, 0, nil, nil, nil); vers := 3; err : string; (err, plain.ssl_vers) = plain.ssl_connection.client(plain.c.dfd, dialstring, vers, info); } if(err == "") return plain; 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; } ssl_suites := array [] of { # Inferno supported cipher suites: byte 0, byte 16r03, # RSA_EXPORT_WITH_RC4_40_MD5 byte 0, byte 16r04, # RSA_WITH_RC4_128_MD5 byte 0, byte 16r05, # RSA_WITH_RC4_128_SHA byte 0, byte 16r06, # RSA_EXPORT_WITH_RC2_CBC_40_MD5 byte 0, byte 16r07, # RSA_WITH_IDEA_CBC_SHA byte 0, byte 16r08, # RSA_EXPORT_WITH_DES40_CBC_SHA byte 0, byte 16r09, # RSA_WITH_DES_CBC_SHA byte 0, byte 16r0A, # RSA_WITH_3DES_EDE_CBC_SHA byte 0, byte 16r0B, # DH_DSS_EXPORT_WITH_DES40_CBC_SHA byte 0, byte 16r0C, # DH_DSS_WITH_DES_CBC_SHA byte 0, byte 16r0D, # DH_DSS_WITH_3DES_EDE_CBC_SHA byte 0, byte 16r0E, # DH_RSA_EXPORT_WITH_DES40_CBC_SHA byte 0, byte 16r0F, # DH_RSA_WITH_DES_CBC_SHA byte 0, byte 16r10, # DH_RSA_WITH_3DES_EDE_CBC_SHA byte 0, byte 16r11, # DHE_DSS_EXPORT_WITH_DES40_CBC_SHA byte 0, byte 16r12, # DHE_DSS_WITH_DES_CBC_SHA byte 0, byte 16r13, # DHE_DSS_WITH_3DES_EDE_CBC_SHA byte 0, byte 16r14, # DHE_RSA_EXPORT_WITH_DES40_CBC_SHA byte 0, byte 16r15, # DHE_RSA_WITH_DES_CBC_SHA byte 0, byte 16r16, # DHE_RSA_WITH_3DES_EDE_CBC_SHA byte 0, byte 16r17, # DH_anon_EXPORT_WITH_RC4_40_MD5 byte 0, byte 16r18, # DH_anon_WITH_RC4_128_MD5 byte 0, byte 16r19, # DH_anon_EXPORT_WITH_DES40_CBC_SHA byte 0, byte 16r1A, # DH_anon_WITH_DES_CBC_SHA byte 0, byte 16r1B, # DH_anon_WITH_3DES_EDE_CBC_SHA byte 0, byte 16r1C, # FORTEZZA_KEA_WITH_NULL_SHA byte 0, byte 16r1D, # FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA byte 0, byte 16r1E, # FORTEZZA_KEA_WITH_RC4_128_SHA }; ssl_comprs := array [] of {byte 0};