@@ -20,80 +20,94 @@ import java.util.concurrent.atomic.AtomicInteger
20
20
21
21
class CronetHttpPlugin : FlutterPlugin , Messages .HttpApi {
22
22
private lateinit var flutterPluginBinding: FlutterPlugin .FlutterPluginBinding
23
- private lateinit var cronetEngine: CronetEngine
24
23
24
+ private val engineIdToEngine = HashMap <String , CronetEngine >()
25
25
private val executor = Executors .newCachedThreadPool()
26
26
private val mainThreadHandler = Handler (Looper .getMainLooper())
27
27
private val channelId = AtomicInteger (0 )
28
+ private val engineId = AtomicInteger (0 )
28
29
29
30
override fun onAttachedToEngine (
30
31
@NonNull flutterPluginBinding : FlutterPlugin .FlutterPluginBinding
31
32
) {
32
33
Messages .HttpApi .setup(flutterPluginBinding.binaryMessenger, this )
33
34
this .flutterPluginBinding = flutterPluginBinding
34
- val context = flutterPluginBinding.getApplicationContext()
35
-
36
- // TODO: Move the Cronet initialization elsewhere so that failures (e.g. because the device
37
- // doesn't have Google Play services) can be communicated to the Dart client.
38
- val builder = CronetEngine .Builder (context)
39
- cronetEngine = builder.build()
40
35
}
41
36
42
37
override fun onDetachedFromEngine (@NonNull binding : FlutterPlugin .FlutterPluginBinding ) {
43
38
Messages .HttpApi .setup(binding.binaryMessenger, null )
44
39
}
45
40
46
- override fun start (startRequest : Messages .StartRequest ): Messages .StartResponse {
47
- // Create a unique channel to communicate Cronet events to the Dart client code with.
48
- val channelName = " plugins.flutter.io/cronet_event/" + channelId.incrementAndGet()
49
- val eventChannel = EventChannel (flutterPluginBinding.binaryMessenger, channelName)
50
- lateinit var eventSink: EventChannel .EventSink
51
- var numRedirects = 0
41
+ override fun createEngine (createRequest : Messages .CreateEngineRequest ): Messages .CreateEngineResponse {
42
+ try {
43
+ val builder = CronetEngine .Builder (flutterPluginBinding.getApplicationContext())
52
44
53
- val cronetRequest =
54
- cronetEngine.newUrlRequestBuilder(
55
- startRequest.url,
56
- object : UrlRequest .Callback () {
57
- override fun onRedirectReceived (
58
- request : UrlRequest ,
59
- info : UrlResponseInfo ,
60
- newLocationUrl : String
61
- ) {
62
- if (! startRequest.getFollowRedirects()) {
63
- request.cancel()
64
- mainThreadHandler.post({
65
- eventSink.success(
66
- Messages .EventMessage .Builder ()
67
- .setType(Messages .EventMessageType .responseStarted)
68
- .setResponseStarted(
69
- Messages .ResponseStarted .Builder ()
70
- .setStatusCode(info.getHttpStatusCode().toLong())
71
- .setHeaders(info.getAllHeaders())
72
- .setIsRedirect(true )
73
- .build()
74
- )
75
- .build()
76
- .toMap()
77
- )
78
- })
79
- }
80
- ++ numRedirects
81
- if (numRedirects <= startRequest.getMaxRedirects()) {
82
- request.followRedirect()
83
- } else {
84
- request.cancel()
85
- mainThreadHandler.post({
86
- eventSink.success(
87
- Messages .EventMessage .Builder ()
88
- .setType(Messages .EventMessageType .tooManyRedirects)
89
- .build()
90
- .toMap()
91
- )
92
- })
93
- }
94
- }
45
+ if (createRequest.getStoragePath() != null ) {
46
+ builder.setStoragePath(createRequest.getStoragePath()!! )
47
+ }
48
+
49
+ if (createRequest.getCacheMode() == Messages .CacheMode .disabled) {
50
+ builder.enableHttpCache(createRequest.getCacheMode()!! .ordinal, 0 )
51
+ } else if (createRequest.getCacheMode() != null && createRequest.getCacheMaxSize() != null ) {
52
+ builder.enableHttpCache(createRequest.getCacheMode()!! .ordinal, createRequest.getCacheMaxSize()!! )
53
+ }
54
+
55
+ if (createRequest.getEnableBrotli() != null ) {
56
+ builder.enableBrotli(createRequest.getEnableBrotli()!! )
57
+ }
58
+
59
+ if (createRequest.getEnableHttp2() != null ) {
60
+ builder.enableHttp2(createRequest.getEnableHttp2()!! )
61
+ }
62
+
63
+ if (createRequest.getEnablePublicKeyPinningBypassForLocalTrustAnchors() != null ) {
64
+ builder.enablePublicKeyPinningBypassForLocalTrustAnchors(createRequest.getEnablePublicKeyPinningBypassForLocalTrustAnchors()!! )
65
+ }
95
66
96
- override fun onResponseStarted (request : UrlRequest ? , info : UrlResponseInfo ) {
67
+ if (createRequest.getEnableQuic() != null ) {
68
+ builder.enableQuic(createRequest.getEnableQuic()!! )
69
+ }
70
+
71
+ if (createRequest.getUserAgent() != null ) {
72
+ builder.setUserAgent(createRequest.getUserAgent()!! )
73
+ }
74
+
75
+ val engine = builder.build()
76
+ val engineName = " cronet_engine_" + engineId.incrementAndGet()
77
+ engineIdToEngine.put(engineName, engine)
78
+ return Messages .CreateEngineResponse .Builder ()
79
+ .setEngineId(engineName)
80
+ .build()
81
+ } catch (e: IllegalArgumentException ) {
82
+ return Messages .CreateEngineResponse .Builder ()
83
+ .setErrorString(e.message)
84
+ .setErrorType(Messages .ExceptionType .illegalArgumentException)
85
+ .build()
86
+ } catch (e: Exception ) {
87
+ return Messages .CreateEngineResponse .Builder ()
88
+ .setErrorString(e.message)
89
+ .setErrorType(Messages .ExceptionType .otherException)
90
+ .build()
91
+ }
92
+ }
93
+
94
+ override fun freeEngine (engineId : String ) {
95
+ engineIdToEngine.remove(engineId)
96
+ }
97
+
98
+ private fun createRequest (startRequest : Messages .StartRequest , cronetEngine : CronetEngine , eventSink : EventChannel .EventSink ): UrlRequest {
99
+ var numRedirects = 0
100
+
101
+ val cronetRequest = cronetEngine.newUrlRequestBuilder(
102
+ startRequest.url,
103
+ object : UrlRequest .Callback () {
104
+ override fun onRedirectReceived (
105
+ request : UrlRequest ,
106
+ info : UrlResponseInfo ,
107
+ newLocationUrl : String
108
+ ) {
109
+ if (! startRequest.getFollowRedirects()) {
110
+ request.cancel()
97
111
mainThreadHandler.post({
98
112
eventSink.success(
99
113
Messages .EventMessage .Builder ()
@@ -102,51 +116,84 @@ class CronetHttpPlugin : FlutterPlugin, Messages.HttpApi {
102
116
Messages .ResponseStarted .Builder ()
103
117
.setStatusCode(info.getHttpStatusCode().toLong())
104
118
.setHeaders(info.getAllHeaders())
105
- .setIsRedirect(false )
119
+ .setIsRedirect(true )
106
120
.build()
107
121
)
108
122
.build()
109
123
.toMap()
110
124
)
111
125
})
112
- request?.read(ByteBuffer .allocateDirect(1024 * 1024 ))
113
126
}
114
-
115
- override fun onReadCompleted (
116
- request : UrlRequest ,
117
- info : UrlResponseInfo ,
118
- byteBuffer : ByteBuffer
119
- ) {
120
- byteBuffer.flip()
121
- val b = ByteArray (byteBuffer.remaining())
122
- byteBuffer.get(b)
127
+ ++ numRedirects
128
+ if (numRedirects <= startRequest.getMaxRedirects()) {
129
+ request.followRedirect()
130
+ } else {
131
+ request.cancel()
123
132
mainThreadHandler.post({
124
133
eventSink.success(
125
134
Messages .EventMessage .Builder ()
126
- .setType(Messages .EventMessageType .readCompleted)
127
- .setReadCompleted(Messages .ReadCompleted .Builder ().setData(b).build())
135
+ .setType(Messages .EventMessageType .tooManyRedirects)
128
136
.build()
129
137
.toMap()
130
138
)
131
139
})
132
- byteBuffer.clear()
133
- request?.read(byteBuffer)
134
140
}
141
+ }
135
142
136
- override fun onSucceeded (request : UrlRequest , info : UrlResponseInfo ? ) {
137
- mainThreadHandler.post({ eventSink.endOfStream() })
138
- }
143
+ override fun onResponseStarted (request : UrlRequest ? , info : UrlResponseInfo ) {
144
+ mainThreadHandler.post({
145
+ eventSink.success(
146
+ Messages .EventMessage .Builder ()
147
+ .setType(Messages .EventMessageType .responseStarted)
148
+ .setResponseStarted(
149
+ Messages .ResponseStarted .Builder ()
150
+ .setStatusCode(info.getHttpStatusCode().toLong())
151
+ .setHeaders(info.getAllHeaders())
152
+ .setIsRedirect(false )
153
+ .build()
154
+ )
155
+ .build()
156
+ .toMap()
157
+ )
158
+ })
159
+ request?.read(ByteBuffer .allocateDirect(1024 * 1024 ))
160
+ }
139
161
140
- override fun onFailed (
141
- request : UrlRequest ,
142
- info : UrlResponseInfo ,
143
- error : CronetException
144
- ) {
145
- mainThreadHandler.post({ eventSink.error(" CronetException" , error.toString(), null ) })
146
- }
147
- },
148
- executor
149
- )
162
+ override fun onReadCompleted (
163
+ request : UrlRequest ,
164
+ info : UrlResponseInfo ,
165
+ byteBuffer : ByteBuffer
166
+ ) {
167
+ byteBuffer.flip()
168
+ val b = ByteArray (byteBuffer.remaining())
169
+ byteBuffer.get(b)
170
+ mainThreadHandler.post({
171
+ eventSink.success(
172
+ Messages .EventMessage .Builder ()
173
+ .setType(Messages .EventMessageType .readCompleted)
174
+ .setReadCompleted(Messages .ReadCompleted .Builder ().setData(b).build())
175
+ .build()
176
+ .toMap()
177
+ )
178
+ })
179
+ byteBuffer.clear()
180
+ request?.read(byteBuffer)
181
+ }
182
+
183
+ override fun onSucceeded (request : UrlRequest , info : UrlResponseInfo ? ) {
184
+ mainThreadHandler.post({ eventSink.endOfStream() })
185
+ }
186
+
187
+ override fun onFailed (
188
+ request : UrlRequest ,
189
+ info : UrlResponseInfo ,
190
+ error : CronetException
191
+ ) {
192
+ mainThreadHandler.post({ eventSink.error(" CronetException" , error.toString(), null ) })
193
+ }
194
+ },
195
+ executor
196
+ )
150
197
151
198
if (startRequest.getBody().size > 0 ) {
152
199
cronetRequest.setUploadDataProvider(
@@ -158,16 +205,24 @@ class CronetHttpPlugin : FlutterPlugin, Messages.HttpApi {
158
205
for ((key, value) in startRequest.getHeaders()) {
159
206
cronetRequest.addHeader(key, value)
160
207
}
208
+ return cronetRequest.build()
209
+ }
210
+
211
+ override fun start (startRequest : Messages .StartRequest ): Messages .StartResponse {
212
+ // Create a unique channel to communicate Cronet events to the Dart client code with.
213
+ val channelName = " plugins.flutter.io/cronet_event/" + channelId.incrementAndGet()
214
+ val eventChannel = EventChannel (flutterPluginBinding.binaryMessenger, channelName)
161
215
162
216
// Don't start the Cronet request until the Dart client code is listening for events.
163
217
val streamHandler =
164
218
object : EventChannel .StreamHandler {
165
219
override fun onListen (arguments : Any? , events : EventChannel .EventSink ) {
166
- eventSink = events
167
220
try {
168
- cronetRequest.build().start()
221
+ val cronetEngine = engineIdToEngine.getValue(startRequest.engineId)
222
+ val cronetRequest = createRequest(startRequest, cronetEngine, events)
223
+ cronetRequest.start()
169
224
} catch (e: Exception ) {
170
- mainThreadHandler.post({ eventSink .error(" CronetException" , e.toString(), null ) })
225
+ mainThreadHandler.post({ events .error(" CronetException" , e.toString(), null ) })
171
226
}
172
227
}
173
228
0 commit comments