File: HTTP.k

package info (click to toggle)
kaya 0.4.2-4
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 4,448 kB
  • ctags: 1,694
  • sloc: cpp: 9,536; haskell: 7,461; sh: 3,013; yacc: 910; makefile: 816; perl: 90
file content (235 lines) | stat: -rw-r--r-- 10,129 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/** -*-C-*-ish
    Kaya standard library
    Copyright (C) 2004, 2005 Edwin Brady

    This file is distributed under the terms of the GNU Lesser General
    Public Licence. See COPYING for licence.
*/
"<summary>HTTP requests</summary>
<prose>This module contains low-level functions for constructing and sending HTTP and HTTPS requests. If you are using HTTPS, then you should read the documentation for the <moduleref>TLS</moduleref> module about SSL certificates.</prose>"
module HTTP;

import WebCommon;
import Array;
import Net;
import Regex;
import Tuples;
import Strings;
import Builtins;

"<summary>The HTTP version</summary>
<prose>The HTTP version to use in communication. HTTP 1.1 (<code>HTTP11</code>) has advantages but may be harder to parse the responses of.</prose>"
public data HTTPversion = HTTP10 | HTTP11;

"<summary>Bad username for HTTP authentication</summary>
<prose>Usernames for HTTP authentication may not contain a ':'</prose>"
Exception BadUsername();

"<summary>Not a URL</summary>
<prose>The String for conversion did not appear to be a valid URL</prose>"
Exception NotAURL();


"<summary>URL information</summary>
<prose>This data type stores URL information.</prose>
<list>
<item><code>server</code> is the host to be contacted. Generally this should be the host name although for some sites the IP address may be used instead.</item>
<item><code>port</code> is the TCP port to use. Normally, 80 is used for HTTP and 443 for HTTPS</item>
<item><code>localpart</code> is the remainder of the URL (which must include the initial '/')</item>
<item><code>secure</code> is false for HTTP and true for HTTPS (note that it is possible for a HTTP server to run on port 443, so no assumptions will be made about this setting based on the port number)</item>
</list>
<related><functionref>getURL</functionref></related>
<related><functionref>parseURL</functionref></related>
<related><functionref>postURL</functionref></related>"
public data HTTPURL(String server, Int port, String localpart, Bool secure);

"<argument name='url'>The String representing the URL</argument>
<summary>Convert a String to a HTTP URL</summary>
<prose>Parse a String solely containing an absolute URL into a <dataref>HTTPURL</dataref>. The String must begin with the protocol http:// or https://</prose>
<related><functionref>getURL</functionref></related>
<related><functionref>postURL</functionref></related>"
public HTTPURL parseURL(String url) {
  re = compile("^https?://([^/]+)(/.*|$)",createArray(1));
  result = match(re,url);
  case result of {
    noMatch -> throw(NotAURL);
    | matches([_,server,local],_,_) -> secure = quickMatch("^https://",url);
    if (quickMatch(":",server)) {
      hdata = split(":",server);
      server = hdata[0];
      port = Int(hdata[1]);
    } else {
      if (secure) {
	port = 443;
      } else {
	port = 80;
      }
    }
    if (quickMatch("#",local)) {
      ldata = split("#",local);
      local = ldata[0];
    }
    return HTTPURL(server,port,local,secure);
    | _ -> throw(NotAURL); // shouldn't happen
  }
}

"<argument name='url'>A <dataref>HTTPURL</dataref>.</argument>
<argument name='headers'>A list of key/value pairs of extra HTTP headers (optional, defaulting to the empty list)</argument>
<argument name='version'>The HTTP version</argument>
<argument name='certfiles'>A list of SSL certificate files, each containing one or more PEM encoded certificates of trusted Certification Authorities, used for HTTPS connections. This list may be empty (the default), but this is much less secure. For HTTP connections, this parameter is ignored.</argument>
<summary>Retrieve a URL by HTTP.</summary>
<prose>Retrieve a URL by HTTP. The String returned will contain the HTTP response headers and response body exactly as sent by the server. Because Strings may not contain null bytes, this function is not suitable for retrieving binary data.</prose>
<related><functionref>basicAuthHeader</functionref></related>
<related><functionref>parseURL</functionref></related>
<related><functionref>postURL</functionref></related>"
public String getURL(HTTPURL url, 
		     [(String,String)] headers = createArray(1),
		     HTTPversion version = HTTP10,
		     [String] certfiles=[]) 
{
    server = url.server;
    dir = url.localpart;
    port = url.port;
    secure = url.secure;

    h = connect(TCP, server, port, secure, certfiles);
    getURL(h,url,headers,version);
    urldata = recv(h);
    try {
      closeConnection(h);
    } catch(CloseError) {
      // ignore this error
    }
    return urldata;
}


"<argument name='h'>An established network connection.</argument>
<argument name='url'>A <dataref>HTTPURL</dataref>.</argument>
<argument name='headers'>A list of key/value pairs of extra HTTP headers (optional, defaulting to the empty list)</argument>
<argument name='version'>The HTTP version</argument>
<summary>Retrieve a URL by HTTP using an existing connection.</summary>
<prose>Retrieve a URL by HTTP using an existing connection. The connection will then be ready to receive response data.</prose>
<related><functionref>basicAuthHeader</functionref></related>
<related><functionref>parseURL</functionref></related>
<related><functionref>getURL</functionref></related>
<related><functionref>postURL</functionref></related>"
public Void getURL(NetHandle h,
		   HTTPURL url, 
		   [(String,String)] headers = createArray(1),
		   HTTPversion version = HTTP10) {
    server = url.server;
    dir = url.localpart;
    port = url.port;
    
    case version of {
      HTTP10 -> hver = "HTTP/1.0";
      | HTTP11 -> hver = "HTTP/1.1";
    }
    send(h,"GET "+dir+" "+hver+"\nConnection: close\n");
    for x in headers {
	hd = x.fst;
	hv = x.snd;
	send(h,hd+": "+hv+"\n");
    }

    send(h,"Host:"+server+":"+port+"\n\n");

}



"<argument name='post'>The data to post, encoded as application/x-www-form-urlencoded data</argument>
<argument name='url'>A <dataref>HTTPURL</dataref>.</argument>
<argument name='headers'>A list of key/value pairs of extra HTTP headers (optional, defaulting to the empty list)</argument>
<argument name='version'>The HTTP version</argument>
<argument name='certfiles'>A list of SSL certificate files, each containing one or more PEM encoded certificates of trusted Certification Authorities, used for HTTPS connections. This list may be empty (the default), but this is much less secure. For HTTP connections, this parameter is ignored.</argument>
<summary>Post data to a URL by HTTP.</summary>
<prose>Post data to a URL by HTTP. The String returned will contain the HTTP response headers and response body exactly as sent by the server. Because Strings may not contain null bytes, this function is not suitable for requests that may return binary data.</prose>
<related><functionref>basicAuthHeader</functionref></related>
<related><functionref>getURL</functionref></related>
<related><functionref>parseURL</functionref></related>"
public String postURL(String post, HTTPURL url,
		      [(String,String)] headers = createArray(1),
		      HTTPversion version = HTTP10,
		      [String] certfiles = []) 
{
    server = url.server;
    dir = url.localpart;
    port = url.port;
    secure = url.secure;

    h = connect(TCP, server, port, secure, certfiles);
    postURL(h,post,url,headers,version);
    urldata = recv(h);
    try {
      closeConnection(h);
    } catch(CloseError) {
      // ignore this error
    }
    return urldata;
}

"<argument name='h'>An established network connection.</argument>
<argument name='post'>The data to post, encoded as application/x-www-form-urlencoded data</argument>
<argument name='url'>A <dataref>HTTPURL</dataref>.</argument>
<argument name='headers'>A list of key/value pairs of extra HTTP headers (optional, defaulting to the empty list)</argument>
<argument name='version'>The HTTP version</argument>
<summary>Post data to a URL by HTTP using an existing connection.</summary>
<prose>Post data to a URL by HTTP.  The connection will then be ready to receive response data.</prose>
<related><functionref>basicAuthHeader</functionref></related>
<related><functionref>getURL</functionref></related>
<related><functionref>parseURL</functionref></related>"
public Void postURL(NetHandle h, String post, HTTPURL url,
		    [(String,String)] headers = createArray(1),
		    HTTPversion version = HTTP10) {

    server = url.server;
    dir = url.localpart;
    port = url.port;

    case version of {
      HTTP10 -> hver = "HTTP/1.0";
      | HTTP11 -> hver = "HTTP/1.1";
    }
    send(h,"POST "+dir+" "+hver+"\nConnection: close\n");
    for x in headers {
	hd = x.fst;
	hv = x.snd;
	send(h,hd+": "+hv+"\n");
    }
    len = length(post);
    send(h,"Content-length: "+len+"\n");
    send(h,"Content-type: application/x-www-form-urlencoded\n");
    send(h,"Host:"+server+":"+port+"\n\n");
    send(h,post+"\n");
}

"<argument name='user'>The username</argument>
<argument name='pwd'>The password</argument>
<summary>Create a Basic authentication header</summary>
<prose>Creates an authentication header suitable for HTTP Basic Auth. The username may not contain a ':' character. Remember that this form of authentication is very insecure, and so should only be used over secure connections.</prose>
<related><functionref>getURL</functionref></related>
<related><functionref>postURL</functionref></related>"
public (String,String) basicAuthHeader(String user, String pwd) {
  if (quickMatch(":",user)) {
    throw(BadUsername);
  }
  userpass = user+":"+pwd;
  upenc = base64Encode(userpass);
  return ("Authorization","Basic "+upenc);
}

"<argument name='cookies'>A list of pairs (in name,value order) of the cookies to send to the server with this request</argument>
<summary>Generate a cookie header</summary>
<prose>Generates a HTTP header for sending cookies.</prose>
<related><functionref>getURL</functionref></related>
<related><functionref>postURL</functionref></related>"
public (String,String) cookieHeader([(String,String)] cookies) {
  vals = [];
  for cookie in cookies {
    push(vals,urlEncode(cookie.fst)+"="+urlEncode(cookie.snd));
  }
  return ("Cookie",join(vals,";"));
}