B4J Question HttpJob when Proxy is active

udg

Expert
Licensed User
Longtime User
Hi all,
I'm trying to deploy one of my sw in a very strictly controlled LAN where everything should pass through a proxy.
They ask me to integrate the ability to make use of their proxy in my sw.
What should I do?

As an example of data transmission, I have:
B4X:
Dim pJob As HttpJob
    pJob.Initialize("", Me)
    pJob.PostBytes(wscsync, buffer)
    Wait For (pJob) JobDone(j As HttpJob)

wscsync is:
Public const wscsync As String = "https://<mydomain>:62238/mnog"

Proxy needs authentication (user/password). What they can give me is:
proxy name/address
proxy port
proxy user (allowed to access Internet)
proxy password for user

I found the following code snippets on this forum, but I don't know how to use them (and whether they could be part of the solution):
B4X:
    SetSystemProperty("http.proxyHost","webwasher.xxxx.com")
    SetSystemProperty("https.proxyHost", "webwasher.xxx.com")
    SetSystemProperty("http.proxyPort", 3128)
    SetSystemProperty("https.proxyPort", 3128)
    SetSystemProperty("https.proxyUser",  <username>)
    SetSystemProperty("https.proxyPassword", <pwd>")

Sub SetProxy (hc As OkHttpClient, Host As String, Port As String)
    Dim jo As JavaObject = hc
    Dim proxy, socketaddress As JavaObject
    socketaddress.InitializeNewInstance("java.net.InetSocketAddress", Array(Host, Port))
    proxy.InitializeNewInstance("java.net.Proxy", Array("HTTPS", socketaddress))
    jo.GetFieldJO("client").RunMethod("setProxy", Array(proxy))
End Sub

Please, note the use of HTTPS (original code was for HTTP).

TIA
 

udg

Expert
Licensed User
Longtime User
Thank you.
So I have to call Setup each time I need to execute a GET or POST command?
Is it ok to transform the following "regular" way of doing a GET to to the "advanced" one? (see below)

regular (for a GET command)
B4X:
   Dim j As HttpJob
    j.Initialize("", Me)
    j.GetRequest.Timeout = 5000
    j.Download(AppGlobals.wscdisp)                'Hello service
    Wait For (j) JobDone(j As HttpJob)
    If j.Success Then ....

advanced
B4X:
Sub testproxyGET
    Dim j1 As OkHttpRequest
    j1.InitializeGet(AppGlobals.wscdisp)
    j1.Timeout = 5000
    Dim j2 As OkHttpClient
    j2.Initialize("aaa7")
    SetProxy(j2,"webwasher...", 3128)
    j2.ExecuteCredentials(j1,1,<proxy user>, <proxy pwd)
...
End Sub

Private Sub aaa7_ResponseSuccess (Response As OkHttpResponse, TaskId As Int)
    Log(Response)
End Sub

Private Sub aaa7_ResponseError (Response As OkHttpResponse, Reason As String, StatusCode As Int, TaskId As Int)
    Log(Reason)
End Sub

If the above is correct, how do I replicate the "WaitFor JobDone"clause used in the regular version of the sub?

udg
 
Upvote 0

udg

Expert
Licensed User
Longtime User
Great. So, I setup things like you showed on post #4 above then I go the "regular" way. No need to change anything in code.
Why I'm not surprised that B4X makes it everything so simple? :)
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Great. So, I setup things like you showed on post #4 above then I go the "regular" way. No need to change anything in code.
Yes.

Why I'm not surprised that B4X makes it everything so simple?
I'm not saying anything until you confirm that it is working :)
 
Upvote 0

udg

Expert
Licensed User
Longtime User
Back to the proxy problem.
Executing the code on my machine in Release mode and outside of the LAN where the Proxy resides, I get the following error:
java.lang.RuntimeExecption: Method: setProxy not found in: okhttp3.OkHttpClient

The code in AppStart is the following (and the HU2_PUBLIC symbol is defined)
B4X:
Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1")
    MainForm.Show
    If HttpUtils2Service.hc.IsInitialized = False Then
        HttpUtils2Service.Service_Create
    End If
    Try
        SetProxy(HttpUtils2Service.hc, "webwasher....", 3128)
    Catch
        Label1.Text=LastException.Message
    End Try
End Sub

Sub SetProxy (hc As OkHttpClient, Host As String, Port As Int)
   Dim jo As JavaObject = hc
   Dim proxy, socketAddress As JavaObject
   socketAddress.InitializeNewInstance("java.net.InetSocketAddress", Array (Host, Port))
   proxy.InitializeNewInstance("java.net.Proxy", Array ("HTTP", socketAddress))
   jo.GetFieldJO("client").RunMethod("setProxy", Array(proxy))
End Sub

Note that I changedback to HTTP when doing proxy.InitializeNewInstance since Oracle doc shows no signs of HTTPS

Running the same jar on a machine residing inside the protecetd LAN, shows no error. I even tried to redirect output to a text file (which is created but with zero length); my idea was to write there the LastExecption.Message by using LogError.
 
Upvote 0

drgottjr

Well-Known Member
Licensed User
Longtime User
the error: java.lang.RuntimeExecption: Method: setProxy not found in: okhttp3.OkHttpClient
refers to the current version of okhttp. the setProxy() method is no longer used. proxies are now set as part of the okhttpclient.Builder.build process. i can use a proxy with b4a (and okhttputils2). it involves a small hack to bypass okhttp's defunct setProxy() method. unfortunately, there was an error with b4j. but i think erel will quickly see what is needed.

a side note: in addition to setting a proxy as part of building an okhttpclient, you can also authenticate it as part of the same process. so there is hope for that too.
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Try this:
B4X:
Sub SetProxy (Host As String, Port As Int)
    If HttpUtils2Service.hc.IsInitialized = False Then
        HttpUtils2Service.Service_Create
    End If
    Dim jo As JavaObject = HttpUtils2Service.hc
    Dim proxy, socketAddress As JavaObject
    socketAddress.InitializeNewInstance("java.net.InetSocketAddress", Array (Host, Port))
    proxy.InitializeNewInstance("java.net.Proxy", Array ("HTTP", socketAddress))
    Dim client As JavaObject = jo.RunMethod("sharedInit", Array("hc"))
    client.RunMethod("proxy", Array(proxy))
    jo.SetField("client", client.RunMethod("build", Null))
End Sub
 
Upvote 0

udg

Expert
Licensed User
Longtime User
I redirectd stderr output to a file and found something that makes me believe we're very close to a solution now:
java.io.IOException: failed to authenticate with proxy
Unfortunately I can't copy, mail or transmit by any means the error.txt file generated by stderr becauses the PC is strictly locked.
Tell me if I have to manually copy a few rows below the first one showed above.

Anyway, if I read it correctly, the proxy seems reachable. What it's missing are credentials.
Soon after SetProxy I call below code with a WaiFor statement. In it a set proxy credentials, but I don't know whether it's the right place or not (I suspect it isn't)

B4X:
Sub CheckInternet As ResumableSub
    LogColor("check Internet start", 0xFFFFFF00)
    Dim j As HttpJob
    j.Initialize("", Me)
    j.GetRequest.Timeout = 5000
    j.Download("https://beachbooking.eu:62238/hello2")                'Hello service
    j.Username = <user for proxy>
    j.Password =  <pwd for proxy>
    Wait For (j) JobDone(j As HttpJob)
    Dim res As Boolean = j.Success
    If j.Success Then
        Log(j.GetString)
    Else
        Log("Internet or remote server unavailable")
    End If
    j.Release
    LogColor("check Internet finish", 0xFFFFFF00)
    Return res
End Sub
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Step #3:
B4X:
Sub SetProxy (Host As String, Port As Int, Username As String, Password As String)
    If HttpUtils2Service.hc.IsInitialized = False Then
        HttpUtils2Service.Service_Create
    End If
    Dim jo As JavaObject = HttpUtils2Service.hc
    Dim proxy, socketAddress As JavaObject
    socketAddress.InitializeNewInstance("java.net.InetSocketAddress", Array (Host, Port))
    proxy.InitializeNewInstance("java.net.Proxy", Array ("HTTP", socketAddress))
    Dim client As JavaObject = jo.RunMethod("sharedInit", Array("hc"))
    client.RunMethod("proxy", Array(proxy))
    Dim pa As JavaObject
    pa.InitializeNewInstance(GetType(Me) & "$MyProxyAuthenticator", Array(Username, Password))
    client.RunMethod("proxyAuthenticator", Array(pa))
    jo.SetField("client", client.RunMethod("build", Null))
End Sub

#if Java
public static class MyProxyAuthenticator implements okhttp3.Authenticator {
    private final String username, password;
    public MyProxyAuthenticator(String username, String password) {
        this.username = username;
        this.password = password;
    }
    @Override 
    public okhttp3.Request authenticate(okhttp3.Route route, okhttp3.Response response) throws java.io.IOException {
         String credential = okhttp3.Credentials.basic(username, password);
         return response.request().newBuilder()
           .header("Proxy-Authorization", credential)
           .build();
    }
}
#End If

based on: https://stackoverflow.com/questions/35554380/okhttpclient-proxy-authentication-how-to
 
Upvote 0

udg

Expert
Licensed User
Longtime User
I feel we are very close to the solution. The new error is:
java.lang.ClassNotFoundException: java$lang$Class$MyProxyAuthenticator

As far as I can see, the class is defined in the #java section of the code. Its spelling looks correct.
What else could it be?
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Have you added it to a class (not module)?

You can simply change this line:
B4X:
 pa.InitializeNewInstance(GetType(Me) & "$MyProxyAuthenticator", Array(Username, Password))
To:
B4X:
 pa.InitializeNewInstance("your.packagename.main$MyProxyAuthenticator", Array(Username, Password))
based on the package name and current module.
 
Upvote 0

udg

Expert
Licensed User
Longtime User
Hi Erel,
using it in Main of a UI project for testing.
Production code will go to B4xMainPage.

So, for testing I've to use "mypackage.main" while for production the original "gettype(me)"; right?
 
Upvote 0

udg

Expert
Licensed User
Longtime User
Update: I used (for testing) "mypackage.main".
No more errors from the Authenticator, so that part should be ok now.
The new error is:
javax.net.ssl.SSLHAndshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
 
Upvote 0

udg

Expert
Licensed User
Longtime User
Thanks a lot. I asked them to check about it.
Fingers crossed for the next couple of hours..

Update: still no feed-back from them...
 
Last edited:
Upvote 0

hatzisn

Well-Known Member
Licensed User
Longtime User
Thanks a lot. I asked them to check about it.
Fingers crossed for the next couple of hours..

Update: still no feed-back from them...

Please if you get more info and especially if it works will you please write here what you did? (If anything extra is needed after the installation of the certificate)
 
Upvote 0

udg

Expert
Licensed User
Longtime User
Update: we still have problems about the certificate.
As a temporary solution we tried HTTP instead of HTTPS. It work for a simple GET service but fails when it comes to PostBytes.
What happens when lauching run_debug.bat is that it briefly appears the main form then the program shuts down.

On an older thread I found that @Erel specified that RAF Serializator has nothing to do with Ziplib. So where the error comes from and what to do?

Below the first rows of the error (manually copied do to their strict security policies).
B4X:
main$ResumableSub_SyncCVBase.resume (java line: -1)
java.util.zip.ZipException: incorrect header check
   at java.base/java.util.zip.InflaterInputStream.read(Unknown Source)
   at java.base/java.util.zip.InflaterInputStream.read(Unknown Source)
   at java.base/java.io.DataInputStream.readByte(Unknown Source)
at b4j/anywheresoftware.b4a.randomaccessfile.B4XSerilizator.readByte(Unknown Source)

And here you find the calling sub which maked the error arise
B4X:
'Type TDCmd (Cmd1 As Int, Payload() As Byte)
'Type TDRes (Err As Int, Payload() As Byte)

Sub SyncCVBase(ssl As Boolean) As ResumableSub
    LogColor("CVBase start", 0xFFFFFF00)
    Dim m2 As Map
    m2.Initialize
    m2.Put("cliente_id", 3)
    Dim m1 As Map
    m1.Initialize
    m1.Put("appid", 90)
    m1.Put("mensadata", m2)
    Dim buffer() As Byte
    buffer=PrepareCommand(m1, 6)
    Dim pJob As HttpJob
    pJob.Initialize("", Me)
    If ssl Then
        pJob.PostBytes(<myserver>:52238/mnog", buffer)
    Else
        pJob.PostBytes(<myserver>:52237/mnog", buffer)
    End If
    Wait For (pJob) JobDone(j As HttpJob)
    If j.Success Then
        Dim ser As B4XSerializator
        Dim res As TDRes = ser.ConvertBytesToObject(Bit.InputStreamToBytes(j.GetInputStream))
        Dim risp As Map = ser.ConvertBytesToObject(res.Payload)
        If res.Err = 0 Then
            Dim LOM As List = risp.Get("lom")
            Log($"${TAB}CVBase dwnl size: ${LOM.Size}"$)
            If LOM.Size > 0 Then
                Log("Data received correctly")
                Label1.Text = Label1.Text&CRLF&"Data received correctly"
                LogError(Label1.Text)                                    'log std error
            End If
        Else
            Log(risp.Get("errmsg"))
            Label1.Text = Label1.Text&CRLF&risp.Get("errmsg")
            LogError(Label1.Text)                                    'log std error
        End If
    Else
        Log(j.ErrorMessage)
        Label1.Text = Label1.Text&CRLF&j.ErrorMessage
        LogError(Label1.Text)                                            'log std error
    End If
    pJob.Release
    LogColor("CVBase stop", 0xFFFFFF00)
    Return LOM
End Sub

Public Sub PrepareCommand(Parameters As Object, Command As Int) As Byte()
    Dim ser As B4XSerializator
    Dim buffer() As Byte
    buffer=ser.ConvertObjectToBytes(Parameters)
    Dim cmnd As TDCmd
    cmnd.Initialize
    cmnd.Cmd1 = Command
    cmnd.Payload=buffer
    Dim buffer() As Byte
    buffer=ser.ConvertObjectToBytes(cmnd)
    Return buffer
End Sub
 
Last edited:
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
What happens when lauching run_debug.bat is that it briefly appears the main form then the program shuts down.
It opens a dos console with the logs. Check them.

On an older thread I found that @Erel specified that RAF Serializator has nothing to do with Ziplib. So where the error comes from and what to do?
B4XSerializator does use compression.

I recommend you to send simple messages and check what you receive. You are not receiving what you expect.
 
Upvote 0
Top