I'm trying to write a macro to do some querying from the web to update an Access database. For some reason, VBA refuses to play friendly with http, but is totally content to do https.
Here's my requesting function:
Function httpRequest(ByVal url As String, useProxy As Boolean) As String
Dim response As String
Dim proxy As String
Dim xhr As Object
'Make HTTP requester object
Set xhr = CreateObject("MSXML2.ServerXMLHTTP.6.0")
If useProxy Then
If Left(url, 5) = "https" Then
proxy = "proxyb1secure:8443"
Else
proxy = "proxyb1:8080"
End If
xhr.setProxy 2, proxy
End If
xhr.Open "GET", url, False
'send the request. THIS LINE TIMES OUT ON HTTP
xhr.Send
'fetch the whole page
response = xhr.responseText
'clean up
Set xhr = Nothing
'return
httpRequest = response
End Function
And my testing function:
Function testProxy()
'This one works
MsgBox (httpRequest("https://www.bing.com/search?q=stackoverflow", True))
'This one doesn't.
MsgBox (httpRequest("http://www.bing.com/search?q=stackoverflow", True))
End Function
I'm certain I'm going after the right name and port, because I've tested the same thing via Java, and it's content to do both flavors (i.e. everything works perfectly in the following code).
public static void main(String[] args) throws Exception {
URL url = new URL("http://www.bing.com/search?q=stackoverflow");
HttpURLConnection con = (HttpURLConnection) url.openConnection(getProxyForURL(url));
System.out.println(con.getResponseCode() + " " + con.getResponseMessage());
InputStream is = con.getInputStream();
int c;
StringBuilder sb = new StringBuilder();
while ((c = is.read()) != -1) {
sb.append((char) c);
}
String page = sb.toString();
System.out.println(page);
}
public static Proxy getProxyForURL(URL url) {
if (url.getProtocol().equals("https")) {
return new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxyb1secure", 8443));
} else {
return new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxyb1", 8080));
}
}
What trickery of VBA am I missing?
Mystery solved. Turns out this is a security feature revolving around the user agent (of all things...).
Java used these HTTP headers (which were successful):
GET http://www.bing.com/search?q=stackoverflow HTTP/1.1
Accept: */*
Accept-Language: en-us
Proxy-Connection: Keep-Alive
User-Agent: Java/1.7.0_79
Host: www.bing.com
and Access sent these (which were unsuccessful):
GET http://www.bing.com/search?q=stackoverflow HTTP/1.1
Accept: */*
Accept-Language: en-us
User-Agent: Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)
Proxy-Connection: Keep-Alive
Host: www.bing.com
by simply adding a
xhr.setRequestHeader "User-Agent", "PayMeNoAttention"
it magically goes through. And to confirm the theory, adding Access' user-agent to Java caused it to fail. So. That's definitely what's up.
This is likely an attempt by our brilliant network techs to block macro viruses from contacting malicious sites.
Related
Till now I've used RestSharp to perform POST/GET passing a JSON payload as parameter/body.
Now I've to perform a delete (you can see the example form documentation just here)
DELETE https://api.xxx.it/shipment
HTTP/1.1 Accept-Encoding: gzip,deflate
Content-Type: application/x-www-form-urlencoded
X-API-KEY: APIKEY123456789
Content-Length: 10 Host: api.xxx.it
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
id=1234567
and my code below.
public Task PerformShipmentDeleteAsync(ShipmentDeleteRequest objectRequest)
{
var client = new RestClient(settingsService.Endpoint);
var request = new RestRequest("shipment", DataFormat.Json);
request.AddHeader(Constants.XApiKey, settingsService.ApiXKey);
request.AddParameter( "text/plain",$"id={objectRequest.Id}", ParameterType.RequestBody);
var res = client.Delete(request);
return Task.CompletedTask;
}
and ShipmentDeleteRequest.cs
public class ShipmentDeleteRequest
{
[JsonProperty("id")]
public int Id { get;set; }
}
The only way I've found is to format the string in this way, but It's a hack.
How do I correctly pass the body as the example without passing a string but just the C# object?
Consider the following VB code:
Public Async Function someFunction(ByVal url As String, Optional ByVal methodPost As Boolean = False, Optional ByVal postContent As HttpContent = Nothing) As Threading.Tasks.Task(Of String)
Using client = New HttpClient
client.DefaultRequestHeaders.Authorization = makeAuthenticationHeader()
If methodPost Then
client.DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue("application/json"))
Dim Response = Await client.PostAsync(url, postContent)
Dim content As String = Await Response.Content.ReadAsStringAsync
Return content
Else
Return Await client.GetStringAsync(url)
End If
End Using
End Function
I want to set the request content type to application/json as well as the response content type to application/json.
If I add the following line of code:
client.DefaultRequestHeaders.Add("content-type", "application/json") then the system throws an exception Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects..
I've searched all over google for a way to set the requests header to JSON. Using fiddler (on the server) I can see that the request is sent as plain/text.
POST **URL REMOVED FOR SAFETY REASONS** HTTP/1.1
Authorization: Basic **HASHED AUTH DETAILS - REMOVED FOR SAFETY REASONS**
Accept: application/json
Content-Type: text/plain; charset=utf-8
Host: **HOST REMOVED FOR SAFETY REASONS**
Content-Length: 1532
Expect: 100-continue
Connection: Keep-Alive
Content-Type: text/plain; charset=utf-8 This is where I am having an issue. This needs to be set to a content type for JSON as the body of the request is JSON. How do I set this content-type to JSON in vb.net Code.
I found a solution, I don't know if it is the correct solution or if there is a better solution out there.
Basically you need to set the content-type header on the actual content that you are sending and not on the HTTP Client.
So basically adding content.Headers.ContentType = New MediaTypeWithQualityHeaderValue("application/json") to your code should set the REQUEST's content-type to JSON as well.
Public Async Function someDifferentFunction() As Threading.Tasks.Task(Of String)
Dim url As String = "http://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Dim content As HttpContent = New StringContent(txtRequestBody.Text)
content.Headers.ContentType = New MediaTypeWithQualityHeaderValue("application/json")
Return Await someFunction(url, True, content)
End Function
Regardless of whether I use WebClient or HttpWebRequest, loading this page times out. What am I doing wrong? It can't be https, since other https sites load just fine.
Below is my latest attempt, which adds all headers that I see in Firefox's inspector.
One interesting behavior is that I cannot monitor this with Fiddler, because everything works properly when Fiddler is running.
Using client As WebClient = New WebClient()
client.Headers(HttpRequestHeader.Accept) = "text/html, image/png, image/jpeg, image/gif, */*;q=0.1"
client.Headers(HttpRequestHeader.UserAgent) = "Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12"
client.Headers(HttpRequestHeader.AcceptLanguage) = "en-US;en;q=0.5"
client.Headers(HttpRequestHeader.AcceptEncoding) = "gzip, deflate, br"
client.Headers(HttpRequestHeader.Referer) = "http://www.torontohydro.com/sites/electricsystem/Pages/foryourhome.aspx"
client.Headers("DNT") = "1"
client.Headers(HttpRequestHeader.KeepAlive) = "keep-alive"
client.Headers(HttpRequestHeader.Upgrade) = "1"
client.Headers(HttpRequestHeader.CacheControl) = "max-age=0"
Dim x = New Uri("https://css.torontohydro.com/")
Dim data as string = client.DownloadString(x)
End Using
All of this is excess code. Boiling it down to just a couple of lines causes the same hang.
Using client as WebClient = New WebClient()
Dim data as string = client.DownloadString("https://css.torontohydro.com")
End Using
And this is the HttpWebRequest code, in a nutshell, which also hangs getting the response.
Dim getRequest As HttpWebRequest = CreateWebRequest("https://css.torontohydro.com/")
getRequest.CachePolicy = New Cache.RequestCachePolicy(Cache.RequestCacheLevel.BypassCache)
Using webResponse As HttpWebResponse = CType(getRequest.GetResponse(), HttpWebResponse)
'no need for any more code, since the above line is where things hang
So this ended up being due to the project still being in .NET 3.5. .NET was trying to load the site, being https, using SSL. Adding this line fixed the problem:
ServicePointManager.SecurityProtocol = 3072
I had to use 3072 since 3.5 does not contain a definition for SecurityProtocolType.Tls12.
If I send the http request through Postman it works and I get the result. But the same is not working and getting Unauthorized when I execute through RestSharp.
Below is the code snippet:
var client = new RestClient(
"http://Username:Password#localhost:port/_db/databaseName/_api/simple/all");
var request = new RestRequest(Method.PUT);
request.AddHeader("content-type", "application/json");
request.AddParameter("application/json",
"{\n \"collection\":\"collectionName\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
return response;
According to the restsharp wiki you cannot specify the authentication parameters via the URL:
var client = new RestClient("http://example.com");
client.Authenticator = new SimpleAuthenticator("username", "foo", "password", "bar");
var request = new RestRequest("resource", Method.GET);
client.Execute(request);
in general its a good idea in such a case to drop https, and use wireshark or ngrep to inspect whats going on on the wire:
GET /_db/_system/_api/version?details=true HTTP/1.1
Host: 127.0.0.1
Connection: Keep-Alive
User-Agent: ArangoDB
Accept-Encoding: deflate
Authrization: Basic cm9vdDo=
to inspect the actualy generated authentication headers.
var client = new RestClient("http://example.com");
client.Authenticator = new SimpleAuthenticator("admin","admin");
var request = new RestRequest(Method.GET);
client.Execute(request);
This works for me..
I am having difficulties interacting with a website which use basic authentication to authenticate the user.
I am working on visual basic and i have already tried to use
Dim req As HttpWebRequest = HttpWebRequest.Create("http://url.to.website.com")
adding the headers directly to the web request:
req.Headers.Add("Authorization: Basic " & Convert.ToBase64String(Encoding.Default.GetBytes("user" & ":" & "password")))
or using the network credentials:
req.Credentials = New Net.NetworkCredential("user", "password")
receiving always the same response code: 401 Unauthorized
Using Firefox developer tools i can analyze and resend some web requests and only using Firefox i am able to authenticate correctly.
Firefox report these headers:
Host: url.to.website.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: it-IT,it;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http.//url.to.website.com/portal/data/pub
DNT: 1
Authorization: Basic ZmFrZTpwYXNzd29yZA==
Connection: keep-alive
So i have tried to set it manaually this way:
req.Host = "url.to.website.com"
req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; rv:11.0) Gecko/20100101 Firefox/11.0"
req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
req.Referer = "https://url.to.website.com/some/path/to/file.jsf"
req.ContentType = "application/x-www-form-urlencoded"
req.KeepAlive = True
req.PreAuthenticate = True
req.Method = "POST"
req.Headers.Add("Authorization: Basic " & Convert.ToBase64String(Encoding.Default.GetBytes("user" & ":" & "password")))
with no success (receiving always the same response code: 401 Unauthorized)
Another try was with a web-browser:
WebBrowser1.Navigate("url", Nothing, Nothing, "Authorization: Basic " & Convert.ToBase64String(Encoding.Default.GetBytes(AUTH_USER & ":" & AUTH_PASSWORD)))
My objective is to authenticate, then query some pages and collect responses in order to parse them and use it later in the application.
How can i solve the issue about authentication?
The website is written using JSF and i have no control over it.
Update:
My problem is about authentication, not yet about the jsf application.
While using Firefox all work fine (I can send a request to the website and it will authenticate me right) but while using the HttpWebRequest the authentication fails, even if I set the same headers, as Written before .
I have to figure out the difference between the two requests
I had to get this working for Dukes Forest Java EE Tutorial Port to Wildfly. The code was already written, but the header was case sensitive. Anyway, the code used there is as follows:
/* Client filter for basic HTTP auth */
class AuthClientRequestFilter implements ClientRequestFilter {
private final String user;
private final String password;
public AuthClientRequestFilter(String user, String password) {
this.user = user;
this.password = password;
}
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
try {
requestContext.getHeaders().add(
"Authorization",
"Basic " + DatatypeConverter.printBase64Binary(
(user+":"+password).getBytes("UTF-8"))
);
} catch (UnsupportedEncodingException ex) { }
}
}
The DatatypeConverter is imported from javax.xml.bind. This code was called from the following routine, which has the HTTPClient:
Client client = ClientBuilder.newClient();
client.register(new AuthClientRequestFilter("jack#example.com", "1234"));
Response resp = client.target(ENDPOINT)
.request(MediaType.APPLICATION_XML)
.post(Entity.entity(order, MediaType.APPLICATION_XML), Response.class);
int status = resp.getStatus();
if (status == 200) {
success = true;
}
logger.log(Level.INFO, "[PaymentHandler] Response status {0}", status);
client.close();
return success;
This client code posts to a RESTful service.