123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- MODULE HTTPSupport; (** AUTHOR "Luc Blaeser/cs"; PURPOSE "HTTP Webserver Support Module for HTTP-Request Handling";
- contains also code parts from "CSHTTPSupport" by "cs" *)
- (*PH2012 fix behaviour of wellformed POST with urlencoding in body and valid Content-Length*)
- IMPORT WebHTTP, Streams, Strings, TFClasses, KernelLog;
- TYPE
- HTTPVariable* = POINTER TO RECORD
- name*: ARRAY 1024 OF CHAR;
- value*: ARRAY 1024 OF CHAR;
- isUrlEncoded*: BOOLEAN (** true iff url encoded if the HTTP request *)
- END;
- (** encapsulates the HTTP request header and the variables from POST ord GET *)
- HTTPRequest* = OBJECT
- VAR
- header*: WebHTTP.RequestHeader;
- shortUri*: ARRAY 4096 OF CHAR; (** uri without variables *)
- variables*: TFClasses.List; (** List of HTTPVariable *)
- PROCEDURE &Init*(VAR requestHeader: WebHTTP.RequestHeader; bodyReader: Streams.Reader);
- VAR pos, restLength: SIZE; uriReader : Streams.StringReader; uriRest : Strings.String;
- contentType: ARRAY 40 OF CHAR;
- BEGIN
- NEW(variables);
- header := requestHeader;
- (* look for variables inURL *)
- pos := Strings.Pos("?", header.uri);
- IF (pos > 0) THEN
- Strings.Copy(header.uri, 0, pos, shortUri);
- restLength := Strings.Length(header.uri)-pos;
- NEW(uriRest, restLength);
- Strings.Copy(header.uri, pos+1, restLength, uriRest^);
- NEW(uriReader, restLength); uriReader.Set(uriRest^);
- ParseVariables(uriReader, TRUE)
- ELSE
- COPY(header.uri, shortUri)
- END;
- (* look for variables in body *)
- IF (requestHeader.method = WebHTTP.PostM) THEN
- IF (WebHTTP.GetAdditionalFieldValue(requestHeader.additionalFields, "Content-Type", contentType)) THEN
- IF (contentType = "application/x-www-form-urlencoded") & (bodyReader # NIL)THEN
- (* look for variables in body *)
- ParseVariables(bodyReader, FALSE)
- END
- END
- END
- END Init;
- PROCEDURE ParseVariables(r: Streams.Reader; isUrlEncoded : BOOLEAN);
- VAR var: HTTPVariable; ch: CHAR; pos, i, size: LONGINT; close: BOOLEAN; s: ARRAY 32 OF CHAR;
- enc: BOOLEAN; (* true iff encoded *)
- PROCEDURE Next;
- VAR c0, c1: CHAR; val : LONGINT;
- BEGIN
- ch := r.Get(); INC(pos); enc := FALSE;
- IF ch = "%" THEN (* next byte is encoded *)
- IF (HasMoreData()) THEN c0 := r.Get(); INC(pos) ELSE c0 := 0X END;
- IF (HasMoreData()) THEN c1 := r.Get(); INC(pos) ELSE c1 := 0X END;
- (* first nibble *)
- val := 0; IF (c0 >='0') & (c0 <='9') THEN val := (ORD(c0) - ORD('0')) * 16 END;
- IF (CAP(c0) >='A') & (CAP(c0) <='F') THEN val := (ORD(CAP(c0)) - ORD('A') + 10) * 16 END;
- (* second nibble *)
- IF (c1 >='0') & (c1 <='9') THEN val := val + ORD(c1) - ORD('0') END;
- IF (CAP(c1) >='A') & (CAP(c1) <='F') THEN val := val + ORD(CAP(c1)) - ORD('A')+10 END;
- ch := CHR(val); enc := TRUE
- ELSIF ch = '+' THEN ch := ' '
- END
- END Next;
- PROCEDURE HasMoreData() : BOOLEAN;
- BEGIN
- RETURN (close & (r.Available() > 0)) OR (~close & (pos < size)) (*PH 2012 fix behaviour of urlencoded POST with Content-Length *)
- END HasMoreData;
- BEGIN
- pos := 0;
- IF (~isUrlEncoded & WebHTTP.HasAdditionalField(header.additionalFields, "Content-Length")
- & WebHTTP.GetAdditionalFieldValue(header.additionalFields, "Content-Length", s))THEN
- Strings.StrToInt(s, size); close := FALSE;
- ELSE
- close := TRUE;
- END;
- WHILE (HasMoreData()) DO
- NEW(var); var.isUrlEncoded := isUrlEncoded;
- i := 0; Next;
- WHILE ((HasMoreData()) & (enc OR (ch # "=")) & (i < LEN(var.name)-1)) DO
- var.name[i] := ch; INC(i); Next
- END;
- IF (i >= LEN(var.name)-1) THEN
- KernelLog.String("Variable name too long in HTTP request."); KernelLog.Ln;
- WHILE ((HasMoreData()) & (enc OR (ch # "="))) DO Next END
- ELSIF (ch # "=") THEN
- var.name[i] := ch; INC(i)
- END;
- var.name[i] := 0X; (* Strings.LowerCase(var.name); What the hell... why case in-sensitive *)
- i := 0;
- IF (HasMoreData()) THEN Next END;
- WHILE ((HasMoreData()) & (enc OR (ch # "&")) & (i < LEN(var.value)-1)) DO
- var.value[i] := ch; INC(i); Next
- END;
- IF (i >= LEN(var.value)-1) THEN
- KernelLog.String("Variable value too long in HTTP request."); KernelLog.Ln;
- WHILE ((HasMoreData()) & (enc OR (ch # "&"))) DO Next END
- ELSIF (ch # "&") THEN
- var.value[i] := ch; INC(i)
- END;
- var.value[i] := 0X;
- variables.Add(var);
- END;
- END ParseVariables;
- (** returns NIL if variable is not present *)
- PROCEDURE GetVariableByName*(name: ARRAY OF CHAR) : HTTPVariable;
- VAR p: ANY; var: HTTPVariable; i: LONGINT;
- BEGIN
- variables.Lock;
- FOR i := 0 TO variables.GetCount()-1 DO
- p := variables.GetItem(i); var := p(HTTPVariable); (* var # NIL *)
- IF (var.name = name) THEN
- variables.Unlock;
- RETURN var
- END
- END;
- variables.Unlock;
- RETURN NIL
- END GetVariableByName;
- PROCEDURE WriteEncodedUri*(encUri: ARRAY OF CHAR);
- VAR encStr: ARRAY 1024 OF CHAR; p: ANY; var: HTTPVariable; i : LONGINT;
- BEGIN
- COPY(shortUri, encUri);
- variables.Lock;
- FOR i := 0 TO variables.GetCount()-1 DO
- p := variables.GetItem(i); var := p(HTTPVariable); (* var # NIL *)
- IF (i = 0) THEN
- Strings.Append(encUri, "?")
- ELSE
- Strings.Append(encUri, "&")
- END;
- HTTPEncode(var.name, encStr);
- Strings.Append(encUri, encStr);
- Strings.Append(encUri, "=");
- HTTPEncode(var.value, encStr);
- Strings.Append(encUri, encStr)
- END;
- variables.Unlock
- END WriteEncodedUri;
- END HTTPRequest;
- PROCEDURE RemoveVariablesFromURI*(olduri: ARRAY OF CHAR; VAR newuri: ARRAY OF CHAR);
- VAR pos: SIZE;
- BEGIN
- pos := Strings.Pos("?", olduri);
- IF (pos > 0) THEN
- Strings.Copy(olduri, 0, pos, newuri)
- ELSE
- COPY(olduri, newuri)
- END
- END RemoveVariablesFromURI;
- (** HTTPEncode in by escaping illegal chars , author: "cs" *)
- PROCEDURE HTTPEncode*(in: ARRAY OF CHAR; VAR enc: ARRAY OF CHAR);
- VAR i,o: LONGINT;
- ch: LONGINT;
- PROCEDURE ToHex(in: CHAR;VAR c1: CHAR; VAR c2: CHAR);
- VAR i: INTEGER;
- BEGIN
- i := ORD(in) DIV 16;
- IF i < 10 THEN
- c1 := CHR(30H + i)
- ELSE
- c1 := CHR(37H + i)
- END;
- i := ORD(in) MOD 16;
- IF i < 10 THEN
- c2 := CHR(30H + i)
- ELSE
- c2 := CHR(37H + i)
- END
- END ToHex;
- BEGIN
- o := 0;
- FOR i:= 0 TO Strings.Length(in)-1 DO
- ch := ORD(in[i]);
- (* RFC2396 lowalpha *)
- IF (ch >= 61H ) & (ch <= 7AH) OR
- (* RFC2396 upalpha *)
- (ch >= 41H) & (ch <= 5AH) OR
- (* RFC2396 digit *)
- (ch >= 30H) & (ch <= 39H) OR
- (ch = 2DH) OR (* - *)
- (ch = 5FH) OR (* underscore *)
- (ch = 2EH) OR (* . *)
- (ch = 21H) OR (* ! *)
- (ch = 7EH) OR (* ~ *)
- (ch = 2AH) OR (* * *)
- (ch = 27H) OR (* ' *)
- (ch = 28H) OR (* ( *)
- (ch = 29H) (* ) *)
- THEN
- enc[o]:= CHR(ch); (* transparent *)
- INC(o)
- ELSE (* encode hex *)
- enc[o] := 25X; (* % *)
- ToHex(CHR(ch),enc[o+1],enc[o+2]);
- INC(o,3)
- END
- END;
- enc[o] := 0X
- END HTTPEncode;
- END HTTPSupport.
- System.Free HTTPSupport ~
- [RFC3261] Section 25.1 defines the syntax for the WWW-Authenticate and Proxy-Authenticate header fields as follows.
- Proxy-Authenticate = "Proxy-Authenticate" HCOLON challenge
- WWW-Authenticate = "WWW-Authenticate" HCOLON challenge
- challenge = ("Digest" LWS digest-cln *(COMMA digest-cln))
- / other-challenge
- This protocol defines the following extensions.
- challenge = ("Digest" LWS digest-cln *(COMMA digest-cln))
- / "NTLM" LWS msspi-cln *(COMMA msspi-cln)
- / "Kerberos" LWS msspi-cln *(COMMA msspi-cln)
- / "TLS-DSK" LWS msspi-cln *(COMMA msspi-cln)
- / other-challenge
- digest-cln = realm / domain / nonce
- / opaque / stale / algorithm
- / qop-options / auth-param
- algorithm = "algorithm" EQUAL
- ( "MD5" / "MD5-sess"/ "SHA256-sess" / token )
- msspi-cln = realm / opaque
- / targetname / gssapi-data / version / sts-uri
- targetname = "targetname" EQUAL target-value
- target-value = DQUOTE ( ntlm-target-val
- / ( "sip/" kerberos-target-val)
- / tls-dsk-target-val ) DQUOTE
- ntlm-target-val = token
- kerberos-target-val = token
- tls-dsk-target-val = token
- gssapi-data = "gssapi-data" EQUAL gssapi-data-value
- gssapi-data-value = quoted-string
- version = "version" EQUAL version-value
- version-value = 1*DIGIT
- sts-uri = "sts-uri" EQUAL DQUOTE absoluteURI DQUOTE
|