B4J Question HTTPJOB with multithread?

juventino883

Member
Licensed User
Longtime User
Hi,
sorry maybe it is a silly question but... can I use an HTTPJOB in a multithread handler? im using this code in a handler:

B4X:
'Handler class
Sub Class_Globals
    Public AWS_URI_Is_Path_Style As Boolean
    Public AWS_Access_Key_ID As String
    Public AWS_Secret_Access_Key As String
    Public AWS_Region As String
    Public AWS_End_Point As String

    Public AWS_S3_Bucket_Name As String
    Public AWS_S3_File_Name As String
    Public AWS_S3_HttpMethod As String
    Public AWS_S3_Query_map As Map
    Public AWS_S3_Payload() As Byte
    Public AWS_S3_GMT_DateTime As Long
    Public AWS_S3_OtherHeader_map As Map
    Type KeyValueType(key As String, value As String)
    Dim ws As WebSocket
    Private aws_job As HttpJob
End Sub

Public Sub Initialize
   
End Sub

Sub Handle(req As ServletRequest, resp As ServletResponse)
    'get the callback module from the session (multiple modules can use this handler)
    Log("Helper2")
    Dim callback As Object = req.GetSession.GetAttribute("file_upload_sender")
       
    Try
        Dim data As Map = req.GetMultipartData(Main.TempDir,10000000)'10MB
        Dim filePart As Part = data.Get("file1")
        'S3_Upload(filePart)
        wait for (S3_Upload(filePart)) complete (result1 As Boolean)

    Catch
        Log("Catch Error")
        CallSubDelayed2(callback, "FileError", LastException.Message)
        resp.SendError(500, LastException.Message)
    End Try
   

End Sub



Sub S3_Upload (filepart As Part)
   
    AWS_URI_Is_Path_Style = True
    AWS_Access_Key_ID = "0021b7c45cae0a20000000004"
    AWS_Secret_Access_Key = "K0025WFMCY2jv9GJ9o3Og7FQruzJn0M"
    AWS_Region = "s3.us-west-002"
    AWS_End_Point = "s3.us-west-002.backblazeb2.com"
    AWS_S3_Bucket_Name = "B4Xtest"

    AWS_S3_File_Name = "FILE"&DateTime.Now&filepart.SubmittedFilename
    AWS_S3_HttpMethod = "PUT"

    AWS_S3_Query_map.Initialize
    AWS_S3_Query_map.Clear

    Private wrk_in As InputStream
   
    wrk_in = File.OpenInput("",filepart.TempFile)
    Private wrk_out As OutputStream
    wrk_out.InitializeToBytesArray(1000)
    File.Copy2(wrk_in, wrk_out)
    Log("aqui?")
    wrk_in.Close
    wrk_out.Close
    File.Delete("",filepart.TempFile)
    AWS_S3_Payload = wrk_out.ToBytesArray

    AWS_S3_GMT_DateTime = DateTime.Now

    AWS_S3_OtherHeader_map.Initialize
    AWS_S3_OtherHeader_map.Clear
    AWS_S3_OtherHeader_map.Put("Content-Type", "application/x-www-form-urlencoded")
    AWS_S3_OtherHeader_map.Put("Content-Length", NumberFormat2(AWS_S3_Payload.Length, 1, 0, 0, False))
    AWS_S3_OtherHeader_map.Put("x-amz-meta-author", "B4X")
    AWS_S3_OtherHeader_map.Put("Expect", "100-continue")
   
    Log(URI)

    'Set up httpjob
    aws_job.Initialize("AWS_job2", Me)
   
    aws_job.PutBytes(URI, AWS_S3_Payload)
    'Retrieve full header map
    Private wk_hdr_map As Map
    wk_hdr_map.Initialize
    wk_hdr_map = FullHeaderMap

    Private wrk_ptr As Int

    'For each key, value pair of full header map...
    For wrk_ptr = 0 To wk_hdr_map.Size - 1
 
        'If not host header...
        If wk_hdr_map.GetKeyAt(wrk_ptr) <> "host" Then
     
            'Add it to httpjob
            aws_job.GetRequest.SetHeader(wk_hdr_map.GetKeyAt(wrk_ptr), wk_hdr_map.GetValueAt(wrk_ptr))

            Log(wk_hdr_map.GetKeyAt(wrk_ptr) & ", " & wk_hdr_map.GetValueAt(wrk_ptr))

        End If

    Next
   
    Log(Authorization)

    'Add Authorization header to httpjob
    'AWS_job.GetRequest.SetContentType("application/x-www-form-urlencoded")
    aws_job.GetRequest.SetHeader("Authorization", Authorization)

   
    Log("start loop")
     StartMessageLoop
End Sub


Sub JobDone(Job As HttpJob)
   
    Log("JobName = " & Job.JobName & ", Success = " & Job.Success)
   
    If Job.Success = True Then
   
        Select Job.JobName
   
            Case "AWS_job"

                'Set up map to hold XML response
                Private XML_response_map As Map
                XML_response_map.Initialize
               
                'Suck XML response into XML_response_map
                Private xm As Xml2Map
                xm.Initialize
                XML_response_map = xm.Parse(Job.GetString)
                Log("XML: "&XML_response_map)
                Log("Job: "&Job.GetString)
                'Convert XML response to JSON
                Private JSON_response As JSONGenerator
                JSON_response.Initialize(XML_response_map)
               
                'Log JSON resonse
                Log(JSON_response.ToPrettyString(4))
           
            Case "AWS_job2"
         
                Log(">>>>>>>>>>>>>>>>>>>Uploaded<<<<<<<<<<<<<<<<<<<<")
                'Result.SetText("UPLOADED!")
               
        End Select
       
    Else
       
        Log("Error: " & Job.ErrorMessage)
        'Result.SetText("Error: " & Job.ErrorMessage)
    End If
   
    Job.Release
    'ws.Flush
    StopMessageLoop
   
End Sub

and everithing works as expected when I use it as a singlethread handler, but when I want to use it as a multithread handler it fails, I get this error:

B4X:
javax.net.ssl.SSLProtocolException: Connection reset by peer: socket write error

I made some tests with wireshark and it seems like the error is because B4J is closing the connection too early, so maybe I'm not using OkHttpUtils2 properly? httpjobs should be used only in singlethread handlers? or I'm missing something here about Handlers? ,
If someone can help me clarify these doubts, I would appreciate it very much.

http PUT error.png
 

DonManfred

Expert
Licensed User
Longtime User
 
Upvote 0

juventino883

Member
Licensed User
Longtime User
Hi @DonManfred and @picenainformatica thanks for yor answers,

@DonManfred I have changed the code accordingly to that link but I'm still getting the same issue.

@picenainformatica yes it seems that, but why is it working when the Handler works as a singlethread? if it's an TLS issue it shouldn't work in both cases, rigth?

this is the log with the error:

B4X:
https://s3.us-west-002.backblazeb2.com/B4Xtest/C15989599429131.png
x-amz-content-sha256, ab2e7847775dcc58b3495c6527700d29e9297e9bee10a3a59bd89e81eac95fd0
x-amz-date, 20200901T073222Z
Content-Type, application/x-www-form-urlencoded
Content-Length, 2250233
x-amz-meta-author, B4X
Expect, 100-continue
AWS4-HMAC-SHA256 Credential=0021b7c45cae0a20000000004/20200901/s3.us-west-002/s3/aws4_request,SignedHeaders=content-length;content-type;expect;host;x-amz-content-sha256;x-amz-date;x-amz-meta-author,Signature=7fd32bca939f14ff28ff48aa4382e9ef9d61d457600cb0ff69a0490e8942b5d1
start loop
javax.net.ssl.SSLProtocolException: Connection reset by peer: socket write error
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:126)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:259)
    at java.base/sun.security.ssl.SSLSocketImpl$AppOutputStream.write(SSLSocketImpl.java:988)
    at okio.Okio$1.write(Okio.java:78)
    at okio.AsyncTimeout$1.write(AsyncTimeout.java:179)
    at okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:171)
    at okio.RealBufferedSink.write(RealBufferedSink.java:41)
    at okhttp3.internal.http1.Http1Codec$FixedLengthSink.write(Http1Codec.java:287)
    at okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:171)
    at okio.RealBufferedSink.write(RealBufferedSink.java:85)
    at anywheresoftware.b4h.okhttp.OkHttpClientWrapper$PostPayload.writeTo(OkHttpClientWrapper.java:538)
    at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:48)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:179)
    at okhttp3.RealCall.execute(RealCall.java:63)
    at anywheresoftware.b4h.okhttp.OkHttpClientWrapper.executeWithTimeout(OkHttpClientWrapper.java:156)
    at anywheresoftware.b4h.okhttp.OkHttpClientWrapper.access$0(OkHttpClientWrapper.java:153)
    at anywheresoftware.b4h.okhttp.OkHttpClientWrapper$ExecuteHelper.run(OkHttpClientWrapper.java:201)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.net.SocketException: Connection reset by peer: socket write error
    at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
    at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
    at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
    at java.base/sun.security.ssl.SSLSocketOutputRecord.deliver(SSLSocketOutputRecord.java:320)
    at java.base/sun.security.ssl.SSLSocketImpl$AppOutputStream.write(SSLSocketImpl.java:983)
    ... 31 more
ResponseError. Reason: javax.net.ssl.SSLProtocolException: Connection reset by peer: socket write error, Response:
JobName = AWS_job2, Success = false
Error: javax.net.ssl.SSLProtocolException: Connection reset by peer: socket write error
 
Upvote 0

juventino883

Member
Licensed User
Longtime User
Hi @Erel thank you for your answer, I had that suspicion about OkHttpUtils2.

When you said add a class are you refering about a code module or just add another handler? if I implement the httpjob with a code module it will be like a bottle neck, I'm sending put instructions to a S3 bucket to pass files that users upload to my server to the bucket, so if everithing is uploaded for the same thread a user will have to wait that the files from other users are uploaded before his file beguins to upload, for now to avoid that, I'm uploading the files to my server like in the websocketupload example, and I have a background worker that in a seccond moment sends the files to the bucket. Now my concern is that this approach can work with few users, but what if I have more users?.

Sincerelly I'm a bit confused about how to doing threading safe handlers, maybe, do you have some basic example of what you are meaning with CallSubDelayed?

regards!
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User

1. Not a class handler. It should be a code module or a class initialized from the main module.

2. It will not be a real bottleneck because the communication itself is done in the background. Only the management needs to be done on the same thread.
 
Upvote 0
Top