Plan 9 from Bell Labs’s /usr/web/sources/contrib/maht/limbo/appl/lib/httpc.b

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


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};

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].