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.write(c: self ref Connection, b: array of byte) : int
{
if(c.ssl_connection == nil)
return sys->write(c.c.dfd, b, len b);
return c.ssl_connection.write(b, len b);
}
Connection.read(c: self ref Connection, n: int) : array of byte
{
if(n < 0) raise "trying to read < 0 bytes";
a := array[n] of byte;
r := 0;
if(c.ssl_connection == nil)
r = sys->read(c.c.dfd, a, n);
else
r = c.ssl_connection.read(a, n);
return a[0:r];
}
Connection.send_request(c: self ref Connection, r : ref Request) : ref Response
{
hbytes := r.header_bytes();
c.write(hbytes);
if(r.method == "POST" && r.body != nil && len r.body > 0)
c.write(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(sys->read(c.c.dfd, a, 1) == 1) {
s += string a;
if(a[0] == delim) break;
}
} else {
while(c.ssl_connection.read(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};
|