Check-in [712dad911c]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:[LIBS] - Several improvements and fixes for our http client: 1) Select better data types for winhttp function parameters. 2) add more needed functions and constants. 3) Clarify some code in the client itself.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 712dad911c66a1d5ac6aee34a7e7f7e1c8a72944
User & Date: julcar 2021-08-04 13:58:29.625
Context
2021-08-04
20:51
[LIBS] - Check the upper bound to be sure we are not gonna crash. check-in: 9ca3d8fc29 user: julcar tags: trunk
13:58
[LIBS] - Several improvements and fixes for our http client: 1) Select better data types for winhttp function parameters. 2) add more needed functions and constants. 3) Clarify some code in the client itself. check-in: 712dad911c user: julcar tags: trunk
2021-08-03
08:59
[LIBS] - Make LastError a global var, delete debug code. check-in: 1c53722afc user: julcar tags: trunk
Changes
Side-by-Side Diff Ignore Whitespace Patch
Changes to libs/http-client.bas.
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
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








-
+












-







#INCLUDE "winhttp.bi"
#INCLUDE "http-client.bi"

'Implement friendly interfaces to interact with WinHTTP api

CONST AS STRING IguanaUserAgent = "IguanaCMS HTTP Client/1.0"

DIM SHARED AS ULONG PTR SessionHandler, ConnectionHandler, RequestHandler
DIM SHARED RequestMethod AS STRING, ResponseReceived AS LONG
DIM SHARED RequestMethod AS STRING, ResponseHeaders() AS STRING, ResponseReceived AS LONG

FUNCTION IsValidMethod(CurrentMethod AS STRING) AS BYTE
  DIM ArrayMethods(HTTP_Methods.Count - 1) AS STRING = { _
    "HEAD", "GET", "POST", "PUT", "DELETE" _
  }
  FOR i AS ULONG = 0 TO HTTP_Methods.Count - 1
    IF UCASE(CurrentMethod) = ArrayMethods(i) THEN
      IsValidMethod = -1
      EXIT FOR
    END IF
  NEXT
END FUNCTION


FUNCTION SplitUrl(FullUrl AS STRING, UrlPart AS URL_Properties) AS STRING
  IF FullUrl <> "" AND UrlPart < URL_Properties.Count THEN
    DIM Result AS STRING
    DIM AS ULONG ColonPos, DoubleSlashPos, PathStartPos
    ColonPos = INSTR(FullUrl, ":")
    DoubleSlashPos = INSTR(FullUrl, "//")
43
44
45
46
47
48
49



















































































50
51
52
53
54
55
56
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







      CASE URL_URI
        PathStartPos = INSTR(DoubleSlashPos + 2, FullUrl, "/")
        Result = MID(FullUrl, PathStartPos)
      'CASE URL_QueryString
      'CASE URL_Fragment
    END SELECT
    SplitUrl = Result
  END IF
END FUNCTION

SUB ParseResponseHeaders(HeadersContent AS STRING)
  IF HeadersContent <> "" THEN
    DIM Char AS UBYTE, LineCount AS ULONG
    DIM AS STRING CleanHeaders, NewLine = CHR(13, 10)
    FOR i AS ULONG = 1 TO LEN(HeadersContent)
      Char = ASC(MID(HeadersContent, i, 1))
      SELECT CASE Char
        CASE 0
          i += 2
        CASE 10
          CleanHeaders += CHR(Char)
          i += 1
          LineCount += 1
        CASE ELSE
          CleanHeaders += CHR(Char)
          i += 1
      END SELECT
    NEXT
    IF LineCount THEN
      DIM AS ULONG IndexPos, NextPos, StartPos = 1
      REDIM ResponseHeaders(LineCount - 1)
      NextPos = StartPos
      WHILE NextPos
        NextPos = INSTR(StartPos, CleanHeaders, NewLine)
        'PRINT MID(CleanHeaders, StartPos, NextPos - StartPos)
        ResponseHeaders(IndexPos) = MID(CleanHeaders, StartPos, NextPos - StartPos)
        IndexPos += 1
        StartPos = NextPos + 2
      WEND
    END IF
  END IF
END SUB

SUB GetResponseHeaders()
  'Partially based on http://www.jose.it-berater.org/winhttp/winhttpqueryheaders.htm
  IF RequestHandler THEN
      DIM ResponseContent AS STRING, ResponseLen AS ULONG, QueryFlag AS ULONG
      'Fail the first attemtp, but gain the buffer length
      IF NOT WinHttpQueryHeaders( _
        RequestHandler, _
        WINHTTP_QUERY_RAW_HEADERS_CRLF, _
        WINHTTP_HEADER_NAME_BY_INDEX, _
        WINHTTP_NO_OUTPUT_BUFFER, _
        VarPtr(ResponseLen), _
        WINHTTP_NO_HEADER_INDEX _
      ) THEN
        LastError = GetLastError()
        IF LastError = ERROR_INSUFFICIENT_BUFFER THEN
          ResponseContent = SPACE(ResponseLen)
          'Now we know the size, get full headers
          IF WinHttpQueryHeaders( _
            RequestHandler, _
            WINHTTP_QUERY_RAW_HEADERS_CRLF, _
            WINHTTP_HEADER_NAME_BY_INDEX, _
            ResponseContent, _
            VarPtr(ResponseLen), _
            WINHTTP_NO_HEADER_INDEX _
          ) THEN
            ParseResponseHeaders(ResponseContent)
          END IF
        END IF
      END IF
  END IF
END SUB

FUNCTION HTTP_GetResponseHeader(HeaderName AS STRING) AS STRING
  IF HeaderName <> "" THEN
    FOR i AS ULONG = 0 TO UBOUND(ResponseHeaders)
      IF LCASE(HeaderName) = "status" THEN
        IF INSTR(ResponseHeaders(i), "HTTP") THEN
          HTTP_GetResponseHeader = MID(ResponseHeaders(i), LEN("HTTP/1.1") + 1)
          EXIT FOR
        END IF
      ELSE
        IF LCASE(HeaderName) = LCASE(RTRIM(MID(ResponseHeaders(i), 1, INSTR(ResponseHeaders(i), ":") - 1))) THEN
          HTTP_GetResponseHeader = LTRIM(MID(ResponseHeaders(i), INSTR(ResponseHeaders(i), ":") + 1))
          EXIT FOR
        END IF
      END IF
    NEXT
  END IF
END FUNCTION

SUB HTTP_Open(Method AS STRING, Url AS STRING)
  DIM AS ULONG ServerPort, RequestFlags
  DIM AS STRING ServerProtocol
  IF Method <> "" AND IsValidMethod(Method) THEN
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
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



-
+

-
+

-
-







-
+





+
+
+







        WINHTTP_NO_REFERER, _
        WINHTTP_DEFAULT_ACCEPT_TYPES, _
        RequestFlags _
      )
    END IF
  END IF
END SUB

SUB HTTP_SetRequestHeader(HeaderName AS STRING, HeaderValue AS STRING)
  IF RequestHandler THEN
    IF HeaderName <> "" AND HeaderValue <> "" THEN
      DIM CompleteHeader AS STRING
      CompleteHeader = HeaderName + ": " + HeaderValue
      IF NOT WinHttpAddRequestHeaders( _
          RequestHandler, _
          WSTR(CompleteHeader), _
          LEN(WSTR(CompleteHeader)), _
          0 _
        ) THEN
        LastError = GetLastError()
      END IF
    END IF
  END IF
END SUB

SUB HTTP_Send(RequestBody AS STRING = "")
  IF RequestHandler THEN
    DIM RequestFlags AS ULONG, ReTry AS LONG, RequestLen AS ULONG, RequestData AS WSTRING PTR
    DIM RequestFlags AS ULONG, ReTry AS LONG, RequestLen AS ULONG, RequestData AS STRING
    IF RequestBody <> "" AND RequestMethod = "POST" OR RequestMethod = "PUT" THEN
      RequestData[0] = WSTR(RequestBody)
      RequestData = RequestBody
      RequestLen = LEN(RequestData)
    ELSE
      RequestData = WINHTTP_NO_REQUEST_DATA
    END IF
    DO
      ReTry = 0
      IF WinHttpSendRequest( _
        RequestHandler, _
        WINHTTP_NO_ADDITIONAL_HEADERS, _
        0, _
        VarPtr(RequestData), _
        StrPtr(RequestData), _
        RequestLen, _
        RequestLen, _
        0 _
      ) THEN
        ResponseReceived = WinHttpReceiveResponse(RequestHandler)
        IF ResponseReceived THEN
          GetResponseHeaders()
        END IF
      ELSE
        LastError = GetLastError()
        IF LastError THEN
          SELECT CASE LastError
            CASE ERROR_WINHTTP_SECURE_INVALID_CA
              RequestFlags = _
                SECURITY_FLAG_IGNORE_UNKNOWN_CA OR _
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
247
248
249
250
251
252
253
254

255
256
257
258




259
260
261
262
263
264
265
266

267
268
269
270
271
272
273
274
275
276
277
278







+
-
+



-
-
-
-
+
+
+
+
+
+
+
+
-












      END IF
    LOOP WHILE ReTry
  END IF
END SUB

FUNCTION HTTP_GetResponse() AS STRING
  IF ResponseReceived THEN
    DIM AS STRING ResponseText, TempResponse
    DIM ResponseLen() AS UBYTE, ResponseText AS STRING, SavedLen AS ULONG, CharsLeft AS ULONG
    DIM AS ULONG SavedLen, CharsLeft
    DO
      CharsLeft = 0
      IF WinHttpQueryDataAvailable(RequestHandler, VarPtr(CharsLeft)) THEN
        REDIM ResponseLen(CharsLeft + 1)
        IF WinHttpReadData(RequestHandler, VarPtr(ResponseLen(0)), CharsLeft, VarPtr(SavedLen)) THEN
          FOR i AS ULONG = 0 TO UBOUND(ResponseLen)
            ResponseText += CHR(ResponseLen(i))
        TempResponse = SPACE(CharsLeft)
        IF WinHttpReadData( _
            RequestHandler, _
            StrPtr(TempResponse), _
            CharsLeft, _
            VarPtr(SavedLen) _
          ) THEN
          ResponseText += TempResponse
          NEXT
        END IF
      END IF
    LOOP UNTIL CharsLeft = 0
    HTTP_GetResponse = ResponseText
  END IF
END FUNCTION

SUB HTTP_Clean()
  WinHttpCloseHandle(RequestHandler)
  WinHttpCloseHandle(ConnectionHandler)
  WinHttpCloseHandle(SessionHandler)
END SUB
Changes to libs/http-client.bi.
16
17
18
19
20
21
22


23
24
25
26
16
17
18
19
20
21
22
23
24
25
26
27
28







+
+




  HTTP_DELETE
  Count
END ENUM

DECLARE FUNCTION SplitUrl(FullUrl AS STRING, UrlPart AS URL_Properties) AS STRING
DECLARE SUB HTTP_Open(Method AS STRING, Url AS STRING)
DECLARE SUB HTTP_Send(RequestBody AS STRING = "")
DECLARE SUB HTTP_SetRequestHeader(HeaderName AS STRING, HeaderValue AS STRING)
DECLARE FUNCTION HTTP_GetResponseHeader(HeaderName AS STRING) AS STRING
DECLARE FUNCTION HTTP_GetResponse() AS STRING
DECLARE SUB HTTP_Clean()

COMMON SHARED LastError AS ULONG
Changes to libs/winhttp.bi.
20
21
22
23
24
25
26









27
28
29
30
31
32
33
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42







+
+
+
+
+
+
+
+
+







CONST WINHTTP_OPTION_SECURITY_FLAGS = 31

CONST SECURITY_FLAG_IGNORE_UNKNOWN_CA = &H100
CONST SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE = &H200
CONST SECURITY_FLAG_IGNORE_CERT_CN_INVALID = &H1000
CONST SECURITY_FLAG_IGNORE_CERT_DATE_INVALID = &H2000

CONST WINHTTP_HEADER_NAME_BY_INDEX = 0
CONST WINHTTP_NO_OUTPUT_BUFFER = 0
CONST WINHTTP_NO_HEADER_INDEX = 0

CONST WINHTTP_QUERY_RAW_HEADERS = 21
CONST WINHTTP_QUERY_RAW_HEADERS_CRLF = 22

CONST ERROR_INSUFFICIENT_BUFFER = 122

ENUM
  WINHTTP_ERROR_BASE = 12000
  ERROR_WINHTTP_OUT_OF_HANDLES = WINHTTP_ERROR_BASE + 1
  ERROR_WINHTTP_TIMEOUT = WINHTTP_ERROR_BASE + 2
  ERROR_WINHTTP_INTERNAL_ERROR = WINHTTP_ERROR_BASE + 4
  ERROR_WINHTTP_INVALID_URL = WINHTTP_ERROR_BASE + 5
  ERROR_WINHTTP_UNRECOGNIZED_SCHEME = WINHTTP_ERROR_BASE + 6
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
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







+
+
+
+
+
+
+
+
+



-
+











-
+

-
+







    dwFlags AS ULONG _
  ) AS ULONG PTR
  
  DECLARE FUNCTION WinHttpQueryDataAvailable( _
    hRequest AS ULONG PTR, _
    lpdwNumberOfBytesAvailable AS ULONG PTR _
  ) AS LONG
  
  DECLARE FUNCTION WinHttpQueryHeaders( _
    hRequest AS ULONG PTR, _
    dwInfoLevel AS ULONG, _
    pwszName AS CONST ZSTRING PTR, _
    lpBuffer AS ZSTRING PTR, _
    lpdwBufferLength AS ULONG PTR, _
    lpdwIndex AS ULONG PTR _
  ) AS LONG
  
  DECLARE FUNCTION WinHttpReadData( _
    hRequest AS ULONG PTR, _
    lpBuffer AS ULONG PTR, _
    lpBuffer AS ZSTRING PTR, _
    dwNumberOfBytesToRead AS ULONG, _
    lpdwNumberOfBytesRead AS ULONG PTR _
  ) AS LONG
  
  DECLARE FUNCTION WinHttpReceiveResponse( _
    hRequest AS ULONG PTR, _
    lpReserved AS LONG = 0 _
  ) AS LONG
  
  DECLARE FUNCTION WinHttpSendRequest( _
    hRequest AS ULONG PTR, _
    lpszHeaders AS CONST WSTRING PTR, _
    lpszHeaders AS CONST ZSTRING PTR, _
    dwHeadersLength AS ULONG, _
    lpOptional AS ULONG PTR, _
    lpOptional AS CONST ZSTRING PTR, _
    dwOptionalLength AS ULONG, _
    dwTotalLength AS ULONG, _
    dwContext AS ULONG PTR _
  ) AS LONG
  
  DECLARE FUNCTION WinHttpSetOption( _
    hInternet AS ULONG PTR, _