HTTPSupport.Mod 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. MODULE HTTPSupport; (** AUTHOR "Luc Blaeser/cs"; PURPOSE "HTTP Webserver Support Module for HTTP-Request Handling";
  2. contains also code parts from "CSHTTPSupport" by "cs" *)
  3. (*PH2012 fix behaviour of wellformed POST with urlencoding in body and valid Content-Length*)
  4. IMPORT WebHTTP, Streams, Strings, TFClasses, KernelLog;
  5. TYPE
  6. HTTPVariable* = POINTER TO RECORD
  7. name*: ARRAY 1024 OF CHAR;
  8. value*: ARRAY 1024 OF CHAR;
  9. isUrlEncoded*: BOOLEAN (** true iff url encoded if the HTTP request *)
  10. END;
  11. (** encapsulates the HTTP request header and the variables from POST ord GET *)
  12. HTTPRequest* = OBJECT
  13. VAR
  14. header*: WebHTTP.RequestHeader;
  15. shortUri*: ARRAY 4096 OF CHAR; (** uri without variables *)
  16. variables*: TFClasses.List; (** List of HTTPVariable *)
  17. PROCEDURE &Init*(VAR requestHeader: WebHTTP.RequestHeader; bodyReader: Streams.Reader);
  18. VAR pos, restLength: SIZE; uriReader : Streams.StringReader; uriRest : Strings.String;
  19. contentType: ARRAY 40 OF CHAR;
  20. BEGIN
  21. NEW(variables);
  22. header := requestHeader;
  23. (* look for variables inURL *)
  24. pos := Strings.Pos("?", header.uri);
  25. IF (pos > 0) THEN
  26. Strings.Copy(header.uri, 0, pos, shortUri);
  27. restLength := Strings.Length(header.uri)-pos;
  28. NEW(uriRest, restLength);
  29. Strings.Copy(header.uri, pos+1, restLength, uriRest^);
  30. NEW(uriReader, restLength); uriReader.Set(uriRest^);
  31. ParseVariables(uriReader, TRUE)
  32. ELSE
  33. COPY(header.uri, shortUri)
  34. END;
  35. (* look for variables in body *)
  36. IF (requestHeader.method = WebHTTP.PostM) THEN
  37. IF (WebHTTP.GetAdditionalFieldValue(requestHeader.additionalFields, "Content-Type", contentType)) THEN
  38. IF (contentType = "application/x-www-form-urlencoded") & (bodyReader # NIL)THEN
  39. (* look for variables in body *)
  40. ParseVariables(bodyReader, FALSE)
  41. END
  42. END
  43. END
  44. END Init;
  45. PROCEDURE ParseVariables(r: Streams.Reader; isUrlEncoded : BOOLEAN);
  46. VAR var: HTTPVariable; ch: CHAR; pos, i, size: LONGINT; close: BOOLEAN; s: ARRAY 32 OF CHAR;
  47. enc: BOOLEAN; (* true iff encoded *)
  48. PROCEDURE Next;
  49. VAR c0, c1: CHAR; val : LONGINT;
  50. BEGIN
  51. ch := r.Get(); INC(pos); enc := FALSE;
  52. IF ch = "%" THEN (* next byte is encoded *)
  53. IF (HasMoreData()) THEN c0 := r.Get(); INC(pos) ELSE c0 := 0X END;
  54. IF (HasMoreData()) THEN c1 := r.Get(); INC(pos) ELSE c1 := 0X END;
  55. (* first nibble *)
  56. val := 0; IF (c0 >='0') & (c0 <='9') THEN val := (ORD(c0) - ORD('0')) * 16 END;
  57. IF (CAP(c0) >='A') & (CAP(c0) <='F') THEN val := (ORD(CAP(c0)) - ORD('A') + 10) * 16 END;
  58. (* second nibble *)
  59. IF (c1 >='0') & (c1 <='9') THEN val := val + ORD(c1) - ORD('0') END;
  60. IF (CAP(c1) >='A') & (CAP(c1) <='F') THEN val := val + ORD(CAP(c1)) - ORD('A')+10 END;
  61. ch := CHR(val); enc := TRUE
  62. ELSIF ch = '+' THEN ch := ' '
  63. END
  64. END Next;
  65. PROCEDURE HasMoreData() : BOOLEAN;
  66. BEGIN
  67. RETURN (close & (r.Available() > 0)) OR (~close & (pos < size)) (*PH 2012 fix behaviour of urlencoded POST with Content-Length *)
  68. END HasMoreData;
  69. BEGIN
  70. pos := 0;
  71. IF (~isUrlEncoded & WebHTTP.HasAdditionalField(header.additionalFields, "Content-Length")
  72. & WebHTTP.GetAdditionalFieldValue(header.additionalFields, "Content-Length", s))THEN
  73. Strings.StrToInt(s, size); close := FALSE;
  74. ELSE
  75. close := TRUE;
  76. END;
  77. WHILE (HasMoreData()) DO
  78. NEW(var); var.isUrlEncoded := isUrlEncoded;
  79. i := 0; Next;
  80. WHILE ((HasMoreData()) & (enc OR (ch # "=")) & (i < LEN(var.name)-1)) DO
  81. var.name[i] := ch; INC(i); Next
  82. END;
  83. IF (i >= LEN(var.name)-1) THEN
  84. KernelLog.String("Variable name too long in HTTP request."); KernelLog.Ln;
  85. WHILE ((HasMoreData()) & (enc OR (ch # "="))) DO Next END
  86. ELSIF (ch # "=") THEN
  87. var.name[i] := ch; INC(i)
  88. END;
  89. var.name[i] := 0X; (* Strings.LowerCase(var.name); What the hell... why case in-sensitive *)
  90. i := 0;
  91. IF (HasMoreData()) THEN Next END;
  92. WHILE ((HasMoreData()) & (enc OR (ch # "&")) & (i < LEN(var.value)-1)) DO
  93. var.value[i] := ch; INC(i); Next
  94. END;
  95. IF (i >= LEN(var.value)-1) THEN
  96. KernelLog.String("Variable value too long in HTTP request."); KernelLog.Ln;
  97. WHILE ((HasMoreData()) & (enc OR (ch # "&"))) DO Next END
  98. ELSIF (ch # "&") THEN
  99. var.value[i] := ch; INC(i)
  100. END;
  101. var.value[i] := 0X;
  102. variables.Add(var);
  103. END;
  104. END ParseVariables;
  105. (** returns NIL if variable is not present *)
  106. PROCEDURE GetVariableByName*(name: ARRAY OF CHAR) : HTTPVariable;
  107. VAR p: ANY; var: HTTPVariable; i: LONGINT;
  108. BEGIN
  109. variables.Lock;
  110. FOR i := 0 TO variables.GetCount()-1 DO
  111. p := variables.GetItem(i); var := p(HTTPVariable); (* var # NIL *)
  112. IF (var.name = name) THEN
  113. variables.Unlock;
  114. RETURN var
  115. END
  116. END;
  117. variables.Unlock;
  118. RETURN NIL
  119. END GetVariableByName;
  120. PROCEDURE WriteEncodedUri*(encUri: ARRAY OF CHAR);
  121. VAR encStr: ARRAY 1024 OF CHAR; p: ANY; var: HTTPVariable; i : LONGINT;
  122. BEGIN
  123. COPY(shortUri, encUri);
  124. variables.Lock;
  125. FOR i := 0 TO variables.GetCount()-1 DO
  126. p := variables.GetItem(i); var := p(HTTPVariable); (* var # NIL *)
  127. IF (i = 0) THEN
  128. Strings.Append(encUri, "?")
  129. ELSE
  130. Strings.Append(encUri, "&")
  131. END;
  132. HTTPEncode(var.name, encStr);
  133. Strings.Append(encUri, encStr);
  134. Strings.Append(encUri, "=");
  135. HTTPEncode(var.value, encStr);
  136. Strings.Append(encUri, encStr)
  137. END;
  138. variables.Unlock
  139. END WriteEncodedUri;
  140. END HTTPRequest;
  141. PROCEDURE RemoveVariablesFromURI*(olduri: ARRAY OF CHAR; VAR newuri: ARRAY OF CHAR);
  142. VAR pos: SIZE;
  143. BEGIN
  144. pos := Strings.Pos("?", olduri);
  145. IF (pos > 0) THEN
  146. Strings.Copy(olduri, 0, pos, newuri)
  147. ELSE
  148. COPY(olduri, newuri)
  149. END
  150. END RemoveVariablesFromURI;
  151. (** HTTPEncode in by escaping illegal chars , author: "cs" *)
  152. PROCEDURE HTTPEncode*(in: ARRAY OF CHAR; VAR enc: ARRAY OF CHAR);
  153. VAR i,o: LONGINT;
  154. ch: LONGINT;
  155. PROCEDURE ToHex(in: CHAR;VAR c1: CHAR; VAR c2: CHAR);
  156. VAR i: INTEGER;
  157. BEGIN
  158. i := ORD(in) DIV 16;
  159. IF i < 10 THEN
  160. c1 := CHR(30H + i)
  161. ELSE
  162. c1 := CHR(37H + i)
  163. END;
  164. i := ORD(in) MOD 16;
  165. IF i < 10 THEN
  166. c2 := CHR(30H + i)
  167. ELSE
  168. c2 := CHR(37H + i)
  169. END
  170. END ToHex;
  171. BEGIN
  172. o := 0;
  173. FOR i:= 0 TO Strings.Length(in)-1 DO
  174. ch := ORD(in[i]);
  175. (* RFC2396 lowalpha *)
  176. IF (ch >= 61H ) & (ch <= 7AH) OR
  177. (* RFC2396 upalpha *)
  178. (ch >= 41H) & (ch <= 5AH) OR
  179. (* RFC2396 digit *)
  180. (ch >= 30H) & (ch <= 39H) OR
  181. (ch = 2DH) OR (* - *)
  182. (ch = 5FH) OR (* underscore *)
  183. (ch = 2EH) OR (* . *)
  184. (ch = 21H) OR (* ! *)
  185. (ch = 7EH) OR (* ~ *)
  186. (ch = 2AH) OR (* * *)
  187. (ch = 27H) OR (* ' *)
  188. (ch = 28H) OR (* ( *)
  189. (ch = 29H) (* ) *)
  190. THEN
  191. enc[o]:= CHR(ch); (* transparent *)
  192. INC(o)
  193. ELSE (* encode hex *)
  194. enc[o] := 25X; (* % *)
  195. ToHex(CHR(ch),enc[o+1],enc[o+2]);
  196. INC(o,3)
  197. END
  198. END;
  199. enc[o] := 0X
  200. END HTTPEncode;
  201. END HTTPSupport.
  202. System.Free HTTPSupport ~
  203. [RFC3261] Section 25.1 defines the syntax for the WWW-Authenticate and Proxy-Authenticate header fields as follows.
  204. Proxy-Authenticate = "Proxy-Authenticate" HCOLON challenge
  205. WWW-Authenticate = "WWW-Authenticate" HCOLON challenge
  206. challenge = ("Digest" LWS digest-cln *(COMMA digest-cln))
  207. / other-challenge
  208. This protocol defines the following extensions.
  209. challenge = ("Digest" LWS digest-cln *(COMMA digest-cln))
  210. / "NTLM" LWS msspi-cln *(COMMA msspi-cln)
  211. / "Kerberos" LWS msspi-cln *(COMMA msspi-cln)
  212. / "TLS-DSK" LWS msspi-cln *(COMMA msspi-cln)
  213. / other-challenge
  214. digest-cln = realm / domain / nonce
  215. / opaque / stale / algorithm
  216. / qop-options / auth-param
  217. algorithm = "algorithm" EQUAL
  218. ( "MD5" / "MD5-sess"/ "SHA256-sess" / token )
  219. msspi-cln = realm / opaque
  220. / targetname / gssapi-data / version / sts-uri
  221. targetname = "targetname" EQUAL target-value
  222. target-value = DQUOTE ( ntlm-target-val
  223. / ( "sip/" kerberos-target-val)
  224. / tls-dsk-target-val ) DQUOTE
  225. ntlm-target-val = token
  226. kerberos-target-val = token
  227. tls-dsk-target-val = token
  228. gssapi-data = "gssapi-data" EQUAL gssapi-data-value
  229. gssapi-data-value = quoted-string
  230. version = "version" EQUAL version-value
  231. version-value = 1*DIGIT
  232. sts-uri = "sts-uri" EQUAL DQUOTE absoluteURI DQUOTE