This repository was archived by the owner on Mar 4, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWebClient.cls
763 lines (667 loc) · 24 KB
/
WebClient.cls
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
236
237
238
239
240
241
242
243
244
245
246
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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
Persistable = 0 'NotPersistable
DataBindingBehavior = 0 'vbNone
DataSourceBehavior = 0 'vbNone
MTSTransactionMode = 0 'NotAnMTSObject
END
Attribute VB_Name = "WebClient"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
''
' WebClient v4.1.6
' (c) Tim Hall - https://github.com/VBA-tools/VBA-Web
'
' `WebClient` executes requests and handles response and is responsible for functionality shared between requests,
' such as authentication, proxy configuration, and security.
'
' Usage:
'
' ```VB.net
' Dim Client As New WebClient
' Client.BaseUrl = "https://www.example.com/api/"
'
' Dim Auth As New HttpBasicAuthenticator
' Auth.Setup Username, Password
' Set Client.Authenticator = Auth
'
' Dim Request As New WebRequest
' Dim Response As WebResponse
' ' Setup WebRequest...
'
' Set Response = Client.Execute(Request)
' ' -> Uses Http Basic authentication and appends Request.Resource to BaseUrl
' ```
'
' Errors:
' 11010 / 80042b02 / -2147210494 - cURL error in Execute
' 11011 / 80042b03 / -2147210493 - Error in Execute
' 11012 / 80042b04 / -2147210492 - Error preparing http request
' 11013 / 80042b05 / -2147210491 - Error preparing cURL request
'
' @class WebClient
' @author [email protected]
' @license MIT (http://www.opensource.org/licenses/mit-license.php)
'' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '
Option Explicit
' --------------------------------------------- '
' Constants and Private Variables
' --------------------------------------------- '
Private Const web_DefaultTimeoutMs As Long = 5000
Private Const web_HttpRequest_SetCredentials_ForServer = 0
Private Const web_HttpRequest_SetCredentials_ForProxy = 1
Private Const web_HttpRequest_ProxySetting_Default = 0
Private Const web_HttpRequest_ProxySetting_PreConfig = 0
Private Const web_HttpRequest_ProxySetting_Direct = 1
Private Const web_HttpRequest_ProxySetting_Proxy = 2
Private Enum web_WinHttpRequestOption
web_WinHttpRequestOption_UserAgentString = 0
web_WinHttpRequestOption_URL = 1
web_WinHttpRequestOption_URLCodePage = 2
web_WinHttpRequestOption_EscapePercentInURL = 3
web_WinHttpRequestOption_SslErrorIgnoreFlags = 4
web_WinHttpRequestOption_SelectCertificate = 5
web_WinHttpRequestOption_EnableRedirects = 6
web_WinHttpRequestOption_UrlEscapeDisable = 7
web_WinHttpRequestOption_UrlEscapeDisableQuery = 8
web_WinHttpRequestOption_SecureProtocols = 9
web_WinHttpRequestOption_EnableTracing = 10
web_WinHttpRequestOption_RevertImpersonationOverSsl = 11
web_WinHttpRequestOption_EnableHttpsToHttpRedirects = 12
web_WinHttpRequestOption_EnablePassportAuthentication = 13
web_WinHttpRequestOption_MaxAutomaticRedirects = 14
web_WinHttpRequestOption_MaxResponseHeaderSize = 15
web_WinHttpRequestOption_MaxResponseDrainSize = 16
web_WinHttpRequestOption_EnableHttp1_1 = 17
web_WinHttpRequestOption_EnableCertificateRevocationCheck = 18
End Enum
Private web_pProxyServer As String
Private web_pAutoProxyDomain As String
' --------------------------------------------- '
' Properties
' --------------------------------------------- '
''
' Set the base url that is shared by all requests
' and that the request `Resource` is appended to.
'
' @example
' ```VB.net
' ' Desired URLs
' ' https://api.example.com/v1/messages
' ' https://api.example.com/v1/users/id
' ' BaseUrl <- ^ -> Resource
'
' Dim Client As New WebClient
' Client.BaseUrl = "https://api.example.com/v1/"
'
' Dim Request As New WebRequest
' Request.Resource = "messages"
' Request.Resource = "users/{id}"
' ```
'
' @property BaseUrl
' @type String
''
Public BaseUrl As String
''
' Attach an authenticator to the client for authentication requests.
'
' @example
' ```VB.net
' Dim Client As New WebClient
' Dim Auth As New OAuth1Authenticator
' Auth.Setup ...
'
' Set Client.Authenticator = Auth
' ' -> All requests use Auth to add "Authorization" header
' ```
'
' @property Authenticator
' @type IWebAuthenticator
''
Public Authenticator As IWebAuthenticator
''
' Timeout (in milliseconds) to wait for timeout in each request phase
' (Resolve, Connect, Send, Receive).
'
' @property TimeoutMs
' @type Long
' @default 5000
''
Public TimeoutMs As Long
''
' Comma separated list of domains to bypass the proxy.
'
' @property ProxyBypassList
' @type String
''
Public ProxyBypassList As String
''
' Username for proxy.
'
' @property ProxyUsername
' @type String
''
Public ProxyUsername As String
''
' Password for proxy.
'
' @property ProxyPassword
' @type String
''
Public ProxyPassword As String
''
' Load proxy server and bypass list automatically (`False` by default).
'
' @property EnableAutoProxy
' @type Boolean
' @default False
''
Public EnableAutoProxy As Boolean
''
' Turn off SSL validation (`False` by default).
' Useful for self-signed certificates and should only be used with trusted servers.
'
' @property Insecure
' @type Boolean
' @default False
''
Public Insecure As Boolean
''
' Follow redirects (301, 302, 307) using Location header
'
' @property FollowRedirects
' @type Boolean
' @default True
''
Public FollowRedirects As Boolean
''
' Proxy server to pass requests through (except for those that match `ProxyBypassList`).
'
' @property ProxyServer
' @type String
''
Public Property Get ProxyServer() As String
ProxyServer = web_pProxyServer
End Property
Public Property Let ProxyServer(Value As String)
Me.EnableAutoProxy = False
web_pProxyServer = Value
End Property
' ============================================= '
' Public Methods
' ============================================= '
''
' Execute the given request
' (append the request's `FormattedResource` to the `BaseUrl`)
' and return the response.
'
' @example
' ```VB.net
' Dim Client As New WebClient
' Client.BaseUrl = "https://api.example.com/v1/"
'
' Dim Request As New WebRequest
' Request.Resource = "messages/{id}"
' Request.AddUrlSegment "id", 123
'
' ' Add querystring, body, headers, cookies, etc. for request
'
' Dim Response As WebResponse
' Set Response = Client.Execute(Request)
'
' ' -> GET https://api.example/com/v1/messages/123
' ' headers, cookies, and body...
' ```
'
' @method Execute
' @param {WebRequest} request The request to execute
' @return {WebResponse} Wrapper of server response for request
' @throws 11010 / 80042b02 / -2147210494 - cURL error in Execute
' @throws 11011 / 80042b03 / -2147210493 - Error in Execute
''
Public Function Execute(Request As WebRequest) As WebResponse
Dim web_Http As Object
Dim web_Response As New WebResponse
On Error GoTo web_ErrorHandling
#If Mac Then
Dim web_Curl As String
Dim web_Result As ShellResult
web_Curl = Me.PrepareCurlRequest(Request)
web_Result = WebHelpers.ExecuteInShell(web_Curl)
' Handle cURL errors
'
' Map to WinHttp error number, as possible
' https://msdn.microsoft.com/en-us/library/aa383770(VS.85).aspx
If web_Result.ExitCode > 0 Then
Dim web_ErrorNumber As Long
Dim web_ErrorMessage As String
Dim web_ErrorDetails As String
web_ErrorNumber = web_Result.ExitCode / 256
Select Case web_ErrorNumber
Case 1
' 1 = CURLE_UNSUPPORTED_PROTOCOL
' 12006 = ERROR_WINHTTP_UNRECOGNIZED_SCHEME
Err.Raise 12006 + &H30000 + vbObjectError, "The URL does not use a recognized protocol (1: CURLE_UNSUPPORTED_PROTOCOL)" & vbNewLine & _
"URL: " & Me.GetFullUrl(Request) & vbNewLine & _
"Protocol: " & WebHelpers.GetUrlParts(Me.GetFullUrl(Request))("Protocol")
Case 3
' 3 = CURLE_URL_MALFORMAT
' 12005 = ERROR_WINHTTP_INVALID_URL
Err.Raise 12005 + &H30000 + vbObjectError, "The URL is invalid (3: CURLE_URL_MALFORMAT)" & _
"URL: " & Me.GetFullUrl(Request)
Case 5, 6
' 5 = CURLE_COULDNT_RESOLVE_PROXY
' 6 = CURLE_COULDNT_RESOLVE_HOST
' 12007 = ERROR_WINHTTP_NAME_NOT_RESOLVED
If web_ErrorNumber = 5 Then
web_ErrorDetails = "(5: CURLE_COULDNT_RESOLVE_PROXY)"
Else
web_ErrorDetails = "(6: CURLE_COULDNT_RESOLVE_HOST)"
End If
Err.Raise 12007 + &H30000 + vbObjectError, "WebClient.Execute", "The server name or address could not be resolved " & web_ErrorDetails
Case 7
' 7 = CURLE_COULDNT_CONNECT
' 12029 = ERROR_WINHTTP_CANNOT_CONNECT
Err.Raise 12029 + &H30000 + vbObjectError, "WebClient.Execute", "A connection with the server could not be established (7: CURLE_COULDNT_CONNECT)"
Case 12, 28
' 12 = CURLE_FTP_ACCEPT_TIMEOUT
' 28 = CURLE_OPERATION_TIMEDOUT
' 12002 = ERROR_WINHTTP_TIMEOUT
If web_ErrorNumber = 12 Then
web_ErrorDetails = "(12: CURLE_FTP_ACCEPT_TIMEOUT)"
Else
web_ErrorDetails = "(28: CURLE_OPERATION_TIMEDOUT)"
End If
Err.Raise 12002 + &H30000 + vbObjectError, "WebClient.Execute", "The operation timed out " & web_ErrorDetails
Case 47
' 47 = CURLE_TOO_MANY_REDIRECTS
' 12156 = ERROR_WINHTTP_REDIRECT_FAILED
Err.Raise 12156 + &H30000 + vbObjectError, "WebClient.Execute", "Too many redirects (47: CURLE_TOO_MANY_REDIRECTS)"
Case Else
Err.Raise 11010 + vbObjectError, "WebClient.Execute", "An unknown cURL error occured, #" & web_ErrorNumber & vbNewLine & _
"Find details at http://curl.haxx.se/libcurl/c/libcurl-errors.html"
End Select
End If
web_Response.CreateFromCurl Me, Request, web_Result.Output
#Else
Set web_Http = Me.PrepareHttpRequest(Request)
web_Http.Send Request.Body
Do While Not web_Http.WaitForResponse(0.025)
VBA.DoEvents
Loop
web_Response.CreateFromHttp Me, Request, web_Http
#End If
WebHelpers.LogResponse Me, Request, web_Response
If Not Me.Authenticator Is Nothing Then
Me.Authenticator.AfterExecute Me, Request, web_Response
End If
Set web_Http = Nothing
Set Execute = web_Response
Exit Function
web_ErrorHandling:
Set web_Http = Nothing
Dim web_ErrorDescription As String
' Check lower 16 bits from error
' (e.g. 80072EE2 -> 2EE2 -> 12002)
Select Case Err.Number And 65535
Case 12002, 12007, 12029
' Treat timeout-related errors as 408: timeout, name not resolved, cannot connect
web_Response.StatusCode = WebStatusCode.RequestTimeout
web_Response.StatusDescription = "Request Timeout: " & Err.Description
WebHelpers.LogResponse Me, Request, web_Response
Set Execute = web_Response
Err.Clear
Case Else
' Error
web_ErrorDescription = "An error occurred during execute" & vbNewLine & _
Err.Number & VBA.IIf(Err.Number < 0, " (" & VBA.LCase$(VBA.Hex$(Err.Number)) & ")", "") & ": " & Err.Description
WebHelpers.LogError web_ErrorDescription, "WebClient.Execute", 11011 + vbObjectError
Err.Raise 11011 + vbObjectError, "WebClient.Execute", web_ErrorDescription
End Select
End Function
''
' Get JSON from the given URL
' (with options for Headers, Cookies, QuerystringParams, and UrlSegments).
'
' @example
' ```VB.net
' Dim Client As New WebClient
' Dim Url As String
' Url = "https://api.example.com/v1/messages/1"
'
' Dim Response As WebResponse
' Set Response = Client.GetJson(Url)
'
' Dim Headers As New Collection
' Headers.Add WebHelpers.CreateKeyValue("Authorization", "Bearer ...")
'
' Dim Options As New Dictionary
' Options.Add "Headers", Headers
'
' Set Response = Client.GetJson(Url, Options)
' ```
'
' @method GetJson
' @param {String} Url (appended to `BaseUrl`, if set)
' @param {Dictionary} [Options]
' @param {Collection} [Options.Headers] Collection of `KeyValue`
' @param {Collection} [Options.Cookies] Collection of `KeyValue`
' @param {Collection} [Options.QuerystringParams] Collection of `KeyValue`
' @param {Dictionary} [Options.UrlSegments]
' @return {WebResponse} Response
''
Public Function GetJson(Url As String, Optional Options As Dictionary) As WebResponse
Dim web_Request As New WebRequest
web_Request.CreateFromOptions Options
web_Request.Resource = Url
web_Request.Format = WebFormat.JSON
web_Request.method = WebMethod.HttpGet
Set GetJson = Me.Execute(web_Request)
End Function
''
' Post JSON Body (`Array`, `Collection`, `Dictionary`) to the given URL
' (with options for Headers, Cookies, QuerystringParams, and UrlSegments).
'
' @example
' ```VB.net
' Dim Client As New WebClient
' Dim Url As String
' Url = "https://api.example.com/v1/messages/1"
'
' ' Body
' ' Array, Collection, or Dictionary
' Dim Body As New Dictionary
' Body.Add "message", "Howdy!"
'
' Dim Response As WebResponse
' Set Response = Client.PostJson(Url, Body)
'
' Dim Headers As New Collection
' Headers.Add WebHelpers.CreateKeyValue("Authorization", "Bearer ...")
'
' Dim Options As New Dictionary
' Options.Add "Headers", Headers
'
' Set Response = Client.PostJson(Url, Body, Options)
' ```
'
' @method PostJson
' @param {String} Url (appended to `BaseUrl`, if set)
' @param {Dictionary} Body
' @param {Dictionary} [Options]
' @param {Collection} [Options.Headers] Collection of `KeyValue`
' @param {Collection} [Options.Cookies] Collection of `KeyValue`
' @param {Collection} [Options.QuerystringParams] Collection of `KeyValue`
' @param {Dictionary} [Options.UrlSegments]
' @return {WebResponse} Response
''
Public Function PostJson(Url As String, Body As Variant, Optional Options As Dictionary) As WebResponse
Dim web_Request As New WebRequest
web_Request.CreateFromOptions Options
web_Request.Resource = Url
web_Request.Format = WebFormat.JSON
web_Request.method = WebMethod.HttpPost
If VBA.IsObject(Body) Then
Set web_Request.Body = Body
Else
web_Request.Body = Body
End If
Set PostJson = Me.Execute(web_Request)
End Function
''
' Set proxy for all requests
'
' @example
' ```VB.net
' Dim Client As New RestClient
'
' ' Just Server
' Client.SetProxy "proxy_server:80"
'
' ' Server + Username and Password
' Client.SetProxy "proxy_server:80", "Tim", "Password"
'
' ' Server + Username and Password + BypassList
' Client.SetProxy "proxy_server:80", "Tim", "Password", "<local>,*.bypass.com"
' ```
'
' @method SetProxy
' @param {String} ProxyServer Proxy server to pass requests through
' @param {String} [Username=""] Username for proxy
' @param {String} [Password=""] Password for proxy
' @param {String} [BypassList=""] Comma-separated list of servers that should bypass proxy
''
Public Sub SetProxy(ProxyServer As String, _
Optional Username As String = "", Optional Password As String = "", Optional BypassList As String = "")
Me.ProxyServer = ProxyServer
Me.ProxyUsername = Username
Me.ProxyPassword = Password
Me.ProxyBypassList = BypassList
End Sub
''
' Get full url by joining given `WebRequest.FormattedResource` and `BaseUrl`.
'
' @method GetFullUrl
' @param {WebRequest} Request
' @return {String}
''
Public Function GetFullUrl(Request As WebRequest) As String
GetFullUrl = WebHelpers.JoinUrl(Me.BaseUrl, Request.FormattedResource)
End Function
''
' Prepare Http request for given WebRequest
'
' @internal
' @method PrepareHttpRequest
' @param {WebRequest} Request
' @return {WinHttpRequest}
' @throws 11012 / 80042b04 / -2147210492 - Error preparing http request
''
Public Function PrepareHttpRequest(Request As WebRequest, Optional Async As Boolean = True) As Object
Dim web_Http As Object
Dim web_KeyValue As Dictionary
On Error GoTo web_ErrorHandling
Set web_Http = CreateObject("WinHttp.WinHttpRequest.5.1")
' Prepare request (before open)
web_BeforeExecute Request
' Open http request
web_Http.Open WebHelpers.MethodToName(Request.method), Me.GetFullUrl(Request), Async
' Set timeouts
web_Http.SetTimeouts Me.TimeoutMs, Me.TimeoutMs, Me.TimeoutMs, Me.TimeoutMs
' Load auto-proxy (if needed)
If Me.EnableAutoProxy Then
web_LoadAutoProxy Request
End If
' Setup proxy
' See http://msdn.microsoft.com/en-us/library/windows/desktop/aa384059(v=vs.85).aspx for details
If Me.ProxyServer <> "" Then
WebHelpers.LogDebug "SetProxy: " & Me.ProxyServer, "WebClient.PrepareHttpRequest"
web_Http.SetProxy web_HttpRequest_ProxySetting_Proxy, Me.ProxyServer, Me.ProxyBypassList
If Me.ProxyUsername <> "" Then
WebHelpers.LogDebug "SetProxyCredentials: " & Me.ProxyUsername & ", " & WebHelpers.Obfuscate(Me.ProxyPassword), "WebClient.PrepareHttpRequest"
web_Http.SetCredentials Me.ProxyUsername, Me.ProxyPassword, web_HttpRequest_SetCredentials_ForProxy
End If
Else
' Attempt to get proxy setup with Proxycfg.exe, otherwise direct
web_Http.SetProxy web_HttpRequest_ProxySetting_PreConfig
End If
' Setup security
If Me.Insecure Then
' - Disable certifcate revocation check
' - Ignore all SSL errors
' Unknown certification authority (CA) or untrusted root, 0x0100
' Wrong usage, 0x0200
' Invalid common name (CN), 0x1000
' Invalid date or certificate expired, 0x2000
' = 0x3300 = 13056
' - Enable https-to-http redirects
web_Http.Option(web_WinHttpRequestOption.web_WinHttpRequestOption_EnableCertificateRevocationCheck) = False
web_Http.Option(web_WinHttpRequestOption.web_WinHttpRequestOption_SslErrorIgnoreFlags) = 13056
web_Http.Option(web_WinHttpRequestOption.web_WinHttpRequestOption_EnableHttpsToHttpRedirects) = True
Else
' By default:
' - Enable certificate revocation check (especially useful after HeartBleed)
' - Ignore no SLL erros
' - Disable https-to-http redirects
web_Http.Option(web_WinHttpRequestOption.web_WinHttpRequestOption_EnableCertificateRevocationCheck) = True
web_Http.Option(web_WinHttpRequestOption.web_WinHttpRequestOption_SslErrorIgnoreFlags) = 0
web_Http.Option(web_WinHttpRequestOption.web_WinHttpRequestOption_EnableHttpsToHttpRedirects) = False
End If
' Setup redirects
web_Http.Option(web_WinHttpRequestOption.web_WinHttpRequestOption_EnableRedirects) = Me.FollowRedirects
' Set headers on http request (after open)
For Each web_KeyValue In Request.Headers
web_Http.SetRequestHeader web_KeyValue("Key"), web_KeyValue("Value")
Next web_KeyValue
For Each web_KeyValue In Request.Cookies
web_Http.SetRequestHeader "Cookie", web_KeyValue("Key") & "=" & web_KeyValue("Value")
Next web_KeyValue
' Give authenticator opportunity to update Http
If Not Me.Authenticator Is Nothing Then
Me.Authenticator.PrepareHttp Me, Request, web_Http
End If
' Log request and return
WebHelpers.LogRequest Me, Request
Set PrepareHttpRequest = web_Http
Exit Function
web_ErrorHandling:
Set web_Http = Nothing
Err.Raise 11012 + vbObjectError, "WebClient.PrepareHttpRequest", _
"An error occurred while preparing http request" & vbNewLine & _
Err.Number & VBA.IIf(Err.Number < 0, " (" & VBA.LCase$(VBA.Hex$(Err.Number)) & ")", "") & ": " & Err.Description
End Function
''
' Prepare cURL request for given WebRequest
'
' @internal
' @method PrepareCurlRequest
' @param {WebRequest} Request
' @return {String}
' @throws 11013 / 80042b05 / -2147210491 - Error preparing cURL request
''
Public Function PrepareCurlRequest(Request As WebRequest) As String
Dim web_Curl As String
Dim web_KeyValue As Dictionary
Dim web_CookieString As String
On Error GoTo web_ErrorHandling
web_Curl = "curl -i"
' Setup authenticator
web_BeforeExecute Request
' Set timeouts
' (max time = resolve + sent + receive)
web_Curl = web_Curl & " --connect-timeout " & Me.TimeoutMs / 1000
web_Curl = web_Curl & " --max-time " & 3 * Me.TimeoutMs / 1000
' Setup proxy
If Me.ProxyServer <> "" Then
web_Curl = web_Curl & " --proxy " & Me.ProxyServer
If Me.ProxyBypassList <> "" Then
web_Curl = web_Curl & " --noproxy " & Me.ProxyBypassList
End If
If Me.ProxyUsername <> "" Then
web_Curl = web_Curl & " --proxy-user " & Me.ProxyUsername & ":" & Me.ProxyPassword
End If
End If
' Setup security
If Me.Insecure Then
web_Curl = web_Curl & " --insecure"
End If
' Setup redirects
If Me.FollowRedirects Then
web_Curl = web_Curl & " --location"
End If
' Set headers and cookies
For Each web_KeyValue In Request.Headers
web_Curl = web_Curl & " -H '" & web_KeyValue("Key") & ": " & web_KeyValue("Value") & "'"
Next web_KeyValue
For Each web_KeyValue In Request.Cookies
web_CookieString = web_CookieString & web_KeyValue("Key") & "=" & web_KeyValue("Value") & ";"
Next web_KeyValue
If web_CookieString <> "" Then
web_Curl = web_Curl & " --cookie '" & web_CookieString & "'"
End If
' Add method, data, and url
web_Curl = web_Curl & " -X " & WebHelpers.MethodToName(Request.method)
web_Curl = web_Curl & " -d '" & Request.Body & "'"
web_Curl = web_Curl & " '" & Me.GetFullUrl(Request) & "'"
' Give authenticator opportunity to update cURL
If Not Me.Authenticator Is Nothing Then
Me.Authenticator.PrepareCurl Me, Request, web_Curl
End If
' Log request and return
WebHelpers.LogRequest Me, Request
PrepareCurlRequest = web_Curl
Exit Function
web_ErrorHandling:
Err.Raise 11013 + vbObjectError, "WebClient.PrepareCURLRequest", _
"An error occurred while preparing cURL request" & vbNewLine & _
Err.Number & VBA.IIf(Err.Number < 0, " (" & VBA.LCase$(VBA.Hex$(Err.Number)) & ")", "") & ": " & Err.Description
End Function
''
' Clone client
'
' @internal
' @method Clone
' @return {WebClient}
''
Public Function Clone() As WebClient
Set Clone = New WebClient
Clone.BaseUrl = Me.BaseUrl
Clone.ProxyServer = Me.ProxyServer
Clone.ProxyBypassList = Me.ProxyBypassList
Clone.ProxyUsername = Me.ProxyUsername
Clone.ProxyPassword = Me.ProxyPassword
Clone.EnableAutoProxy = Me.EnableAutoProxy
Clone.TimeoutMs = Me.TimeoutMs
Clone.Insecure = Me.Insecure
Set Clone.Authenticator = Me.Authenticator
End Function
' ============================================= '
' Private Methods
' ============================================= '
Private Sub web_BeforeExecute(web_Request As WebRequest)
If Not Me.Authenticator Is Nothing Then
Me.Authenticator.BeforeExecute Me, web_Request
End If
' Preparing request includes adding headers
' -> Needs to happen after BeforeExecute in case headers were changed
web_Request.Prepare
End Sub
Private Sub web_LoadAutoProxy(web_Request As WebRequest)
#If Win32 Or Win64 Then
On Error GoTo web_ErrorHandling
Dim web_Parts As Dictionary
Dim web_Domain As String
Dim web_ProxyServer As String
Dim web_ProxyBypassList As String
Set web_Parts = WebHelpers.GetUrlParts(Me.GetFullUrl(web_Request))
web_Domain = VBA.IIf(web_Parts("Protocol") <> "", web_Parts("Protocol") & "://", "") & _
web_Parts("Host") & ":" & web_Parts("Port")
' Cache auto-proxy by domain
If web_Domain <> web_pAutoProxyDomain Then
' Cache first to store error as no proxy
web_pAutoProxyDomain = web_Domain
WebHelpers.GetAutoProxy web_Domain, web_ProxyServer, web_ProxyBypassList
WebHelpers.LogDebug "Loaded auto-proxy for " & web_Domain & ":" & vbNewLine & _
"Server = " & web_ProxyServer & vbNewLine & _
"Bypass List = " & web_ProxyBypassList
' Store proxy server in underlying to avoid turning off auto-proxy
web_pProxyServer = web_ProxyServer
Me.ProxyBypassList = web_ProxyBypassList
End If
Exit Sub
web_ErrorHandling:
WebHelpers.LogError "An error occurred while loading auto-proxy" & vbNewLine & _
Err.Number & VBA.IIf(Err.Number < 0, " (" & VBA.LCase$(VBA.Hex$(Err.Number)) & ")", "") & ": " & Err.Description, _
"WebClient.LoadAutoProxy", Err.Number
#End If
End Sub
Private Sub Class_Initialize()
Me.TimeoutMs = web_DefaultTimeoutMs
Me.EnableAutoProxy = False
Me.Insecure = False
Me.FollowRedirects = True
End Sub