Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
cordova-custom
Cordova Plugin Advanced Http
Commits
09c88efe
Commit
09c88efe
authored
Mar 26, 2024
by
Chok
Browse files
chok: init commit
parent
02cfb854
Pipeline
#18
failed with stages
in 0 seconds
Changes
74
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
src/android/com/silkimen/cordovahttp/CordovaHttpOperation.java
0 → 100644
View file @
09c88efe
package
com.silkimen.cordovahttp
;
import
javax.net.ssl.HostnameVerifier
;
import
javax.net.ssl.SSLSocketFactory
;
import
com.silkimen.http.TLSConfiguration
;
import
org.json.JSONObject
;
class
CordovaHttpOperation
extends
CordovaHttpBase
{
public
CordovaHttpOperation
(
String
method
,
String
url
,
String
serializer
,
Object
data
,
JSONObject
headers
,
int
connectTimeout
,
int
readTimeout
,
boolean
followRedirects
,
String
responseType
,
TLSConfiguration
tlsConfiguration
,
CordovaObservableCallbackContext
callbackContext
)
{
super
(
method
,
url
,
serializer
,
data
,
headers
,
connectTimeout
,
readTimeout
,
followRedirects
,
responseType
,
tlsConfiguration
,
callbackContext
);
}
public
CordovaHttpOperation
(
String
method
,
String
url
,
JSONObject
headers
,
int
connectTimeout
,
int
readTimeout
,
boolean
followRedirects
,
String
responseType
,
TLSConfiguration
tlsConfiguration
,
CordovaObservableCallbackContext
callbackContext
)
{
super
(
method
,
url
,
headers
,
connectTimeout
,
readTimeout
,
followRedirects
,
responseType
,
tlsConfiguration
,
callbackContext
);
}
}
src/android/com/silkimen/cordovahttp/CordovaHttpPlugin.java
0 → 100644
View file @
09c88efe
package
com.silkimen.cordovahttp
;
import
java.security.KeyStore
;
import
java.util.HashMap
;
import
java.util.Observable
;
import
java.util.Observer
;
import
java.util.concurrent.Future
;
import
com.silkimen.http.TLSConfiguration
;
import
org.apache.cordova.CallbackContext
;
import
org.apache.cordova.CordovaInterface
;
import
org.apache.cordova.CordovaPlugin
;
import
org.apache.cordova.CordovaWebView
;
import
org.json.JSONArray
;
import
org.json.JSONException
;
import
org.json.JSONObject
;
import
android.content.Context
;
import
android.net.ConnectivityManager
;
import
android.net.NetworkInfo
;
import
android.util.Log
;
import
android.util.Base64
;
import
javax.net.ssl.TrustManagerFactory
;
public
class
CordovaHttpPlugin
extends
CordovaPlugin
implements
Observer
{
private
static
final
String
TAG
=
"Cordova-Plugin-HTTP"
;
private
TLSConfiguration
tlsConfiguration
;
private
HashMap
<
Integer
,
Future
<?>>
reqMap
;
private
final
Object
reqMapLock
=
new
Object
();
@Override
public
void
initialize
(
CordovaInterface
cordova
,
CordovaWebView
webView
)
{
super
.
initialize
(
cordova
,
webView
);
this
.
tlsConfiguration
=
new
TLSConfiguration
();
this
.
reqMap
=
new
HashMap
<
Integer
,
Future
<?>>();
try
{
KeyStore
store
=
KeyStore
.
getInstance
(
"AndroidCAStore"
);
String
tmfAlgorithm
=
TrustManagerFactory
.
getDefaultAlgorithm
();
TrustManagerFactory
tmf
=
TrustManagerFactory
.
getInstance
(
tmfAlgorithm
);
store
.
load
(
null
);
tmf
.
init
(
store
);
this
.
tlsConfiguration
.
setHostnameVerifier
(
null
);
this
.
tlsConfiguration
.
setTrustManagers
(
tmf
.
getTrustManagers
());
if
(
this
.
preferences
.
contains
(
"androidblacklistsecuresocketprotocols"
))
{
this
.
tlsConfiguration
.
setBlacklistedProtocols
(
this
.
preferences
.
getString
(
"androidblacklistsecuresocketprotocols"
,
""
).
split
(
","
)
);
}
}
catch
(
Exception
e
)
{
Log
.
e
(
TAG
,
"An error occured while loading system's CA certificates"
,
e
);
}
}
@Override
public
boolean
execute
(
String
action
,
final
JSONArray
args
,
final
CallbackContext
callbackContext
)
throws
JSONException
{
if
(
action
==
null
)
{
return
false
;
}
if
(
"setServerTrustMode"
.
equals
(
action
))
{
return
this
.
setServerTrustMode
(
args
,
callbackContext
);
}
else
if
(
"setClientAuthMode"
.
equals
(
action
))
{
return
this
.
setClientAuthMode
(
args
,
callbackContext
);
}
else
if
(
"abort"
.
equals
(
action
))
{
return
this
.
abort
(
args
,
callbackContext
);
}
if
(!
isNetworkAvailable
())
{
CordovaHttpResponse
response
=
new
CordovaHttpResponse
();
response
.
setStatus
(-
6
);
response
.
setErrorMessage
(
"No network connection available"
);
callbackContext
.
error
(
response
.
toJSON
());
return
true
;
}
if
(
"get"
.
equals
(
action
))
{
return
this
.
executeHttpRequestWithoutData
(
action
,
args
,
callbackContext
);
}
else
if
(
"head"
.
equals
(
action
))
{
return
this
.
executeHttpRequestWithoutData
(
action
,
args
,
callbackContext
);
}
else
if
(
"delete"
.
equals
(
action
))
{
return
this
.
executeHttpRequestWithoutData
(
action
,
args
,
callbackContext
);
}
else
if
(
"options"
.
equals
(
action
))
{
return
this
.
executeHttpRequestWithoutData
(
action
,
args
,
callbackContext
);
}
else
if
(
"post"
.
equals
(
action
))
{
return
this
.
executeHttpRequestWithData
(
action
,
args
,
callbackContext
);
}
else
if
(
"put"
.
equals
(
action
))
{
return
this
.
executeHttpRequestWithData
(
action
,
args
,
callbackContext
);
}
else
if
(
"patch"
.
equals
(
action
))
{
return
this
.
executeHttpRequestWithData
(
action
,
args
,
callbackContext
);
}
else
if
(
"uploadFiles"
.
equals
(
action
))
{
return
this
.
uploadFiles
(
args
,
callbackContext
);
}
else
if
(
"downloadFile"
.
equals
(
action
))
{
return
this
.
downloadFile
(
args
,
callbackContext
);
}
else
{
return
false
;
}
}
private
boolean
executeHttpRequestWithoutData
(
final
String
method
,
final
JSONArray
args
,
final
CallbackContext
callbackContext
)
throws
JSONException
{
String
url
=
args
.
getString
(
0
);
JSONObject
headers
=
args
.
getJSONObject
(
1
);
int
connectTimeout
=
args
.
getInt
(
2
)
*
1000
;
int
readTimeout
=
args
.
getInt
(
3
)
*
1000
;
boolean
followRedirect
=
args
.
getBoolean
(
4
);
String
responseType
=
args
.
getString
(
5
);
Integer
reqId
=
args
.
getInt
(
6
);
CordovaObservableCallbackContext
observableCallbackContext
=
new
CordovaObservableCallbackContext
(
callbackContext
,
reqId
);
CordovaHttpOperation
request
=
new
CordovaHttpOperation
(
method
.
toUpperCase
(),
url
,
headers
,
connectTimeout
,
readTimeout
,
followRedirect
,
responseType
,
this
.
tlsConfiguration
,
observableCallbackContext
);
startRequest
(
reqId
,
observableCallbackContext
,
request
);
return
true
;
}
private
boolean
executeHttpRequestWithData
(
final
String
method
,
final
JSONArray
args
,
final
CallbackContext
callbackContext
)
throws
JSONException
{
String
url
=
args
.
getString
(
0
);
Object
data
=
args
.
get
(
1
);
String
serializer
=
args
.
getString
(
2
);
JSONObject
headers
=
args
.
getJSONObject
(
3
);
int
connectTimeout
=
args
.
getInt
(
4
)
*
1000
;
int
readTimeout
=
args
.
getInt
(
5
)
*
1000
;
boolean
followRedirect
=
args
.
getBoolean
(
6
);
String
responseType
=
args
.
getString
(
7
);
Integer
reqId
=
args
.
getInt
(
8
);
CordovaObservableCallbackContext
observableCallbackContext
=
new
CordovaObservableCallbackContext
(
callbackContext
,
reqId
);
CordovaHttpOperation
request
=
new
CordovaHttpOperation
(
method
.
toUpperCase
(),
url
,
serializer
,
data
,
headers
,
connectTimeout
,
readTimeout
,
followRedirect
,
responseType
,
this
.
tlsConfiguration
,
observableCallbackContext
);
startRequest
(
reqId
,
observableCallbackContext
,
request
);
return
true
;
}
private
boolean
uploadFiles
(
final
JSONArray
args
,
final
CallbackContext
callbackContext
)
throws
JSONException
{
String
url
=
args
.
getString
(
0
);
JSONObject
headers
=
args
.
getJSONObject
(
1
);
JSONArray
filePaths
=
args
.
getJSONArray
(
2
);
JSONArray
uploadNames
=
args
.
getJSONArray
(
3
);
int
connectTimeout
=
args
.
getInt
(
4
)
*
1000
;
int
readTimeout
=
args
.
getInt
(
5
)
*
1000
;
boolean
followRedirect
=
args
.
getBoolean
(
6
);
String
responseType
=
args
.
getString
(
7
);
Integer
reqId
=
args
.
getInt
(
8
);
CordovaObservableCallbackContext
observableCallbackContext
=
new
CordovaObservableCallbackContext
(
callbackContext
,
reqId
);
CordovaHttpUpload
upload
=
new
CordovaHttpUpload
(
url
,
headers
,
filePaths
,
uploadNames
,
connectTimeout
,
readTimeout
,
followRedirect
,
responseType
,
this
.
tlsConfiguration
,
this
.
cordova
.
getActivity
().
getApplicationContext
(),
observableCallbackContext
);
startRequest
(
reqId
,
observableCallbackContext
,
upload
);
return
true
;
}
private
boolean
downloadFile
(
final
JSONArray
args
,
final
CallbackContext
callbackContext
)
throws
JSONException
{
String
url
=
args
.
getString
(
0
);
JSONObject
headers
=
args
.
getJSONObject
(
1
);
String
filePath
=
args
.
getString
(
2
);
int
connectTimeout
=
args
.
getInt
(
3
)
*
1000
;
int
readTimeout
=
args
.
getInt
(
4
)
*
1000
;
boolean
followRedirect
=
args
.
getBoolean
(
5
);
Integer
reqId
=
args
.
getInt
(
6
);
CordovaObservableCallbackContext
observableCallbackContext
=
new
CordovaObservableCallbackContext
(
callbackContext
,
reqId
);
CordovaHttpDownload
download
=
new
CordovaHttpDownload
(
url
,
headers
,
filePath
,
connectTimeout
,
readTimeout
,
followRedirect
,
this
.
tlsConfiguration
,
observableCallbackContext
);
startRequest
(
reqId
,
observableCallbackContext
,
download
);
return
true
;
}
private
void
startRequest
(
Integer
reqId
,
CordovaObservableCallbackContext
observableCallbackContext
,
CordovaHttpBase
request
)
{
synchronized
(
reqMapLock
)
{
observableCallbackContext
.
setObserver
(
this
);
Future
<?>
task
=
cordova
.
getThreadPool
().
submit
(
request
);
this
.
addReq
(
reqId
,
task
,
observableCallbackContext
);
}
}
private
boolean
setServerTrustMode
(
final
JSONArray
args
,
final
CallbackContext
callbackContext
)
throws
JSONException
{
CordovaServerTrust
runnable
=
new
CordovaServerTrust
(
args
.
getString
(
0
),
this
.
cordova
.
getActivity
(),
this
.
tlsConfiguration
,
callbackContext
);
cordova
.
getThreadPool
().
execute
(
runnable
);
return
true
;
}
private
boolean
setClientAuthMode
(
final
JSONArray
args
,
final
CallbackContext
callbackContext
)
throws
JSONException
{
byte
[]
pkcs
=
args
.
isNull
(
2
)
?
null
:
Base64
.
decode
(
args
.
getString
(
2
),
Base64
.
DEFAULT
);
CordovaClientAuth
runnable
=
new
CordovaClientAuth
(
args
.
getString
(
0
),
args
.
isNull
(
1
)
?
null
:
args
.
getString
(
1
),
pkcs
,
args
.
getString
(
3
),
this
.
cordova
.
getActivity
(),
this
.
cordova
.
getActivity
().
getApplicationContext
(),
this
.
tlsConfiguration
,
callbackContext
);
cordova
.
getThreadPool
().
execute
(
runnable
);
return
true
;
}
private
boolean
abort
(
final
JSONArray
args
,
final
CallbackContext
callbackContext
)
throws
JSONException
{
int
reqId
=
args
.
getInt
(
0
);
boolean
result
=
false
;
// NOTE no synchronized (reqMapLock), since even if the req was already removed from reqMap,
// the worst that would happen calling task.cancel(true) is a result of false
// (i.e. same result as locking & not finding the req in reqMap)
Future
<?>
task
=
this
.
reqMap
.
get
(
reqId
);
if
(
task
!=
null
&&
!
task
.
isDone
())
{
result
=
task
.
cancel
(
true
);
}
callbackContext
.
success
(
new
JSONObject
().
put
(
"aborted"
,
result
));
return
true
;
}
private
void
addReq
(
final
Integer
reqId
,
final
Future
<?>
task
,
final
CordovaObservableCallbackContext
observableCallbackContext
)
{
synchronized
(
reqMapLock
)
{
if
(!
task
.
isDone
()){
this
.
reqMap
.
put
(
reqId
,
task
);
}
}
}
private
void
removeReq
(
final
Integer
reqId
)
{
synchronized
(
reqMapLock
)
{
this
.
reqMap
.
remove
(
reqId
);
}
}
@Override
public
void
update
(
Observable
o
,
Object
arg
)
{
synchronized
(
reqMapLock
)
{
CordovaObservableCallbackContext
c
=
(
CordovaObservableCallbackContext
)
arg
;
if
(
c
.
getCallbackContext
().
isFinished
())
{
removeReq
(
c
.
getRequestId
());
}
}
}
private
boolean
isNetworkAvailable
()
{
ConnectivityManager
connectivityManager
=
(
ConnectivityManager
)
cordova
.
getContext
().
getSystemService
(
Context
.
CONNECTIVITY_SERVICE
);
NetworkInfo
activeNetworkInfo
=
connectivityManager
.
getActiveNetworkInfo
();
return
activeNetworkInfo
!=
null
&&
activeNetworkInfo
.
isConnected
();
}
}
src/android/com/silkimen/cordovahttp/CordovaHttpResponse.java
0 → 100644
View file @
09c88efe
package
com.silkimen.cordovahttp
;
import
java.nio.ByteBuffer
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
org.json.JSONException
;
import
org.json.JSONObject
;
import
android.text.TextUtils
;
import
android.util.Log
;
import
android.util.Base64
;
class
CordovaHttpResponse
{
private
int
status
;
private
String
url
;
private
Map
<
String
,
List
<
String
>>
headers
;
private
String
body
;
private
byte
[]
rawData
;
private
JSONObject
fileEntry
;
private
boolean
hasFailed
;
private
boolean
isFileOperation
;
private
boolean
isRawResponse
;
private
String
error
;
public
void
setStatus
(
int
status
)
{
this
.
status
=
status
;
}
public
void
setUrl
(
String
url
)
{
this
.
url
=
url
;
}
public
void
setHeaders
(
Map
<
String
,
List
<
String
>>
headers
)
{
this
.
headers
=
headers
;
}
public
void
setBody
(
String
body
)
{
this
.
body
=
body
;
}
public
void
setData
(
byte
[]
rawData
)
{
this
.
isRawResponse
=
true
;
this
.
rawData
=
rawData
;
}
public
void
setFileEntry
(
JSONObject
entry
)
{
this
.
isFileOperation
=
true
;
this
.
fileEntry
=
entry
;
}
public
void
setErrorMessage
(
String
message
)
{
this
.
hasFailed
=
true
;
this
.
error
=
message
;
}
public
boolean
hasFailed
()
{
return
this
.
hasFailed
;
}
public
JSONObject
toJSON
()
throws
JSONException
{
JSONObject
json
=
new
JSONObject
();
json
.
put
(
"status"
,
this
.
status
);
json
.
put
(
"url"
,
this
.
url
);
if
(
this
.
headers
!=
null
&&
!
this
.
headers
.
isEmpty
())
{
json
.
put
(
"headers"
,
new
JSONObject
(
getFilteredHeaders
()));
}
if
(
this
.
hasFailed
)
{
json
.
put
(
"error"
,
this
.
error
);
}
else
if
(
this
.
isFileOperation
)
{
json
.
put
(
"file"
,
this
.
fileEntry
);
}
else
if
(
this
.
isRawResponse
)
{
json
.
put
(
"data"
,
Base64
.
encodeToString
(
this
.
rawData
,
Base64
.
DEFAULT
));
}
else
{
json
.
put
(
"data"
,
this
.
body
);
}
return
json
;
}
private
Map
<
String
,
String
>
getFilteredHeaders
()
throws
JSONException
{
Map
<
String
,
String
>
filteredHeaders
=
new
HashMap
<
String
,
String
>();
for
(
Map
.
Entry
<
String
,
List
<
String
>>
entry
:
this
.
headers
.
entrySet
())
{
String
key
=
entry
.
getKey
();
List
<
String
>
value
=
entry
.
getValue
();
if
((
key
!=
null
)
&&
(!
value
.
isEmpty
()))
{
filteredHeaders
.
put
(
key
.
toLowerCase
(),
TextUtils
.
join
(
", "
,
value
));
}
}
return
filteredHeaders
;
}
}
src/android/com/silkimen/cordovahttp/CordovaHttpUpload.java
0 → 100644
View file @
09c88efe
package
com.silkimen.cordovahttp
;
import
android.content.ContentResolver
;
import
android.content.Context
;
import
android.database.Cursor
;
import
android.net.Uri
;
import
android.provider.OpenableColumns
;
import
android.webkit.MimeTypeMap
;
import
com.silkimen.http.HttpRequest
;
import
com.silkimen.http.TLSConfiguration
;
import
java.io.File
;
import
java.io.FileNotFoundException
;
import
java.io.InputStream
;
import
java.net.URI
;
import
java.net.URISyntaxException
;
import
javax.net.ssl.HostnameVerifier
;
import
javax.net.ssl.SSLSocketFactory
;
import
org.json.JSONArray
;
import
org.json.JSONException
;
import
org.json.JSONObject
;
class
CordovaHttpUpload
extends
CordovaHttpBase
{
private
JSONArray
filePaths
;
private
JSONArray
uploadNames
;
private
Context
applicationContext
;
public
CordovaHttpUpload
(
String
url
,
JSONObject
headers
,
JSONArray
filePaths
,
JSONArray
uploadNames
,
int
connectTimeout
,
int
readTimeout
,
boolean
followRedirects
,
String
responseType
,
TLSConfiguration
tlsConfiguration
,
Context
applicationContext
,
CordovaObservableCallbackContext
callbackContext
)
{
super
(
"POST"
,
url
,
headers
,
connectTimeout
,
readTimeout
,
followRedirects
,
responseType
,
tlsConfiguration
,
callbackContext
);
this
.
filePaths
=
filePaths
;
this
.
uploadNames
=
uploadNames
;
this
.
applicationContext
=
applicationContext
;
}
@Override
protected
void
sendBody
(
HttpRequest
request
)
throws
JSONException
,
URISyntaxException
,
FileNotFoundException
{
for
(
int
i
=
0
;
i
<
this
.
filePaths
.
length
();
++
i
)
{
String
uploadName
=
this
.
uploadNames
.
getString
(
i
);
String
filePath
=
this
.
filePaths
.
getString
(
i
);
Uri
fileUri
=
Uri
.
parse
(
filePath
);
// File Scheme
if
(
ContentResolver
.
SCHEME_FILE
.
equals
(
fileUri
.
getScheme
()))
{
File
file
=
new
File
(
new
URI
(
filePath
));
String
fileName
=
file
.
getName
().
trim
();
String
mimeType
=
this
.
getMimeTypeFromFileName
(
fileName
);
request
.
part
(
uploadName
,
fileName
,
mimeType
,
file
);
}
// Content Scheme
if
(
ContentResolver
.
SCHEME_CONTENT
.
equals
(
fileUri
.
getScheme
()))
{
InputStream
inputStream
=
this
.
applicationContext
.
getContentResolver
().
openInputStream
(
fileUri
);
String
fileName
=
this
.
getFileNameFromContentScheme
(
fileUri
,
this
.
applicationContext
).
trim
();
String
mimeType
=
this
.
getMimeTypeFromFileName
(
fileName
);
request
.
part
(
uploadName
,
fileName
,
mimeType
,
inputStream
);
}
}
}
private
String
getFileNameFromContentScheme
(
Uri
contentSchemeUri
,
Context
applicationContext
)
{
Cursor
returnCursor
=
applicationContext
.
getContentResolver
().
query
(
contentSchemeUri
,
null
,
null
,
null
,
null
);
if
(
returnCursor
==
null
||
!
returnCursor
.
moveToFirst
())
{
return
null
;
}
int
nameIndex
=
returnCursor
.
getColumnIndex
(
OpenableColumns
.
DISPLAY_NAME
);
String
fileName
=
returnCursor
.
getString
(
nameIndex
);
returnCursor
.
close
();
return
fileName
;
}
private
String
getMimeTypeFromFileName
(
String
fileName
)
{
if
(
fileName
==
null
||
!
fileName
.
contains
(
"."
))
{
return
null
;
}
MimeTypeMap
mimeTypeMap
=
MimeTypeMap
.
getSingleton
();
int
extIndex
=
fileName
.
lastIndexOf
(
'.'
)
+
1
;
String
extension
=
fileName
.
substring
(
extIndex
).
toLowerCase
();
return
mimeTypeMap
.
getMimeTypeFromExtension
(
extension
);
}
}
src/android/com/silkimen/cordovahttp/CordovaObservableCallbackContext.java
0 → 100644
View file @
09c88efe
package
com.silkimen.cordovahttp
;
import
org.apache.cordova.CallbackContext
;
import
org.json.JSONObject
;
import
java.util.Observer
;
public
class
CordovaObservableCallbackContext
{
private
CallbackContext
callbackContext
;
private
Integer
requestId
;
private
Observer
observer
;
public
CordovaObservableCallbackContext
(
CallbackContext
callbackContext
,
Integer
requestId
)
{
this
.
callbackContext
=
callbackContext
;
this
.
requestId
=
requestId
;
}
public
void
success
(
JSONObject
message
)
{
this
.
callbackContext
.
success
(
message
);
this
.
notifyObserver
();
}
public
void
error
(
JSONObject
message
)
{
this
.
callbackContext
.
error
(
message
);
this
.
notifyObserver
();
}
public
Integer
getRequestId
()
{
return
this
.
requestId
;
}
public
CallbackContext
getCallbackContext
()
{
return
callbackContext
;
}
public
Observer
getObserver
()
{
return
observer
;
}
protected
void
notifyObserver
()
{
if
(
this
.
observer
!=
null
){
this
.
observer
.
update
(
null
,
this
);
}
}
/**
* Set an observer that is notified, when {@link #success(JSONObject)}
* or {@link #error(JSONObject)} are called.
*
* NOTE the observer is notified with
* <pre>observer.update(null, cordovaObservableCallbackContext)</pre>
* @param observer
*/
public
void
setObserver
(
Observer
observer
)
{
this
.
observer
=
observer
;
}
}
src/android/com/silkimen/cordovahttp/CordovaServerTrust.java
0 → 100644
View file @
09c88efe
package
com.silkimen.cordovahttp
;
import
java.io.IOException
;
import
java.security.GeneralSecurityException
;
import
java.security.KeyStore
;
import
java.security.cert.CertificateFactory
;
import
java.security.cert.X509Certificate
;
import
com.silkimen.http.TLSConfiguration
;
import
org.apache.cordova.CallbackContext
;
import
android.app.Activity
;
import
android.util.Log
;
import
android.content.res.AssetManager
;
import
javax.net.ssl.HostnameVerifier
;
import
javax.net.ssl.SSLSession
;
import
javax.net.ssl.TrustManager
;
import
javax.net.ssl.TrustManagerFactory
;
import
javax.net.ssl.X509TrustManager
;
class
CordovaServerTrust
implements
Runnable
{
private
static
final
String
TAG
=
"Cordova-Plugin-HTTP"
;
private
final
TrustManager
[]
noOpTrustManagers
;
private
String
mode
;
private
Activity
activity
;
private
TLSConfiguration
tlsConfiguration
;
private
CallbackContext
callbackContext
;
public
CordovaServerTrust
(
final
String
mode
,
final
Activity
activity
,
final
TLSConfiguration
configContainer
,
final
CallbackContext
callbackContext
)
{
this
.
mode
=
mode
;
this
.
activity
=
activity
;
this
.
tlsConfiguration
=
configContainer
;
this
.
callbackContext
=
callbackContext
;
this
.
noOpTrustManagers
=
new
TrustManager
[]
{
new
X509TrustManager
()
{
public
X509Certificate
[]
getAcceptedIssuers
()
{
return
new
X509Certificate
[
0
];
}
public
void
checkClientTrusted
(
X509Certificate
[]
chain
,
String
authType
)
{
// intentionally left blank
}
public
void
checkServerTrusted
(
X509Certificate
[]
chain
,
String
authType
)
{
// intentionally left blank
}
}
};
}
@Override
public
void
run
()
{
try
{
if
(
"legacy"
.
equals
(
this
.
mode
))
{
this
.
tlsConfiguration
.
setHostnameVerifier
(
null
);
this
.
tlsConfiguration
.
setTrustManagers
(
null
);
}
else
if
(
"nocheck"
.
equals
(
this
.
mode
))
{
this
.
tlsConfiguration
.
setHostnameVerifier
(
null
);
this
.
tlsConfiguration
.
setTrustManagers
(
this
.
noOpTrustManagers
);
}
else
if
(
"pinned"
.
equals
(
this
.
mode
))
{
this
.
tlsConfiguration
.
setHostnameVerifier
(
null
);
this
.
tlsConfiguration
.
setTrustManagers
(
this
.
getTrustManagers
(
this
.
getCertsFromBundle
(
"www/certificates"
)));
}
else
{
this
.
tlsConfiguration
.
setHostnameVerifier
(
null
);
this
.
tlsConfiguration
.
setTrustManagers
(
this
.
getTrustManagers
(
this
.
getCertsFromKeyStore
(
"AndroidCAStore"
)));
}
callbackContext
.
success
();
}
catch
(
Exception
e
)
{
Log
.
e
(
TAG
,
"An error occured while configuring SSL cert mode"
,
e
);
callbackContext
.
error
(
"An error occured while configuring SSL cert mode"
);
}
}
private
TrustManager
[]
getTrustManagers
(
KeyStore
store
)
throws
GeneralSecurityException
{
String
tmfAlgorithm
=
TrustManagerFactory
.
getDefaultAlgorithm
();
TrustManagerFactory
tmf
=
TrustManagerFactory
.
getInstance
(
tmfAlgorithm
);
tmf
.
init
(
store
);
return
tmf
.
getTrustManagers
();
}
private
KeyStore
getCertsFromBundle
(
String
path
)
throws
GeneralSecurityException
,
IOException
{
AssetManager
assetManager
=
this
.
activity
.
getAssets
();
String
[]
files
=
assetManager
.
list
(
path
);
CertificateFactory
cf
=
CertificateFactory
.
getInstance
(
"X.509"
);
String
keyStoreType
=
KeyStore
.
getDefaultType
();
KeyStore
keyStore
=
KeyStore
.
getInstance
(
keyStoreType
);
keyStore
.
load
(
null
,
null
);
for
(
int
i
=
0
;
i
<
files
.
length
;
i
++)
{
int
index
=
files
[
i
].
lastIndexOf
(
'.'
);
if
(
index
==
-
1
||
!
files
[
i
].
substring
(
index
).
equals
(
".cer"
))
{
continue
;
}
keyStore
.
setCertificateEntry
(
"CA"
+
i
,
cf
.
generateCertificate
(
assetManager
.
open
(
path
+
"/"
+
files
[
i
])));
}
return
keyStore
;
}
private
KeyStore
getCertsFromKeyStore
(
String
storeType
)
throws
GeneralSecurityException
,
IOException
{
KeyStore
store
=
KeyStore
.
getInstance
(
storeType
);
store
.
load
(
null
);
return
store
;
}
}
src/android/com/silkimen/http/HttpBodyDecoder.java
0 → 100644
View file @
09c88efe
package
com.silkimen.http
;
import
java.nio.ByteBuffer
;
import
java.nio.charset.CharacterCodingException
;
import
java.nio.charset.Charset
;
import
java.nio.charset.CharsetDecoder
;
import
java.nio.charset.CodingErrorAction
;
import
java.nio.charset.MalformedInputException
;
public
class
HttpBodyDecoder
{
private
static
final
String
[]
ACCEPTED_CHARSETS
=
new
String
[]
{
"UTF-8"
,
"ISO-8859-1"
};
public
static
String
decodeBody
(
byte
[]
body
,
String
charsetName
)
throws
CharacterCodingException
,
MalformedInputException
{
return
decodeBody
(
ByteBuffer
.
wrap
(
body
),
charsetName
);
}
public
static
String
decodeBody
(
ByteBuffer
body
,
String
charsetName
)
throws
CharacterCodingException
,
MalformedInputException
{
if
(
charsetName
==
null
)
{
return
tryDecodeByteBuffer
(
body
);
}
else
{
return
decodeByteBuffer
(
body
,
charsetName
);
}
}
private
static
String
tryDecodeByteBuffer
(
ByteBuffer
buffer
)
throws
CharacterCodingException
,
MalformedInputException
{
for
(
int
i
=
0
;
i
<
ACCEPTED_CHARSETS
.
length
-
1
;
i
++)
{
try
{
return
decodeByteBuffer
(
buffer
,
ACCEPTED_CHARSETS
[
i
]);
}
catch
(
MalformedInputException
e
)
{
continue
;
}
catch
(
CharacterCodingException
e
)
{
continue
;
}
}
return
decodeBody
(
buffer
,
ACCEPTED_CHARSETS
[
ACCEPTED_CHARSETS
.
length
-
1
]);
}
private
static
String
decodeByteBuffer
(
ByteBuffer
buffer
,
String
charsetName
)
throws
CharacterCodingException
,
MalformedInputException
{
return
createCharsetDecoder
(
charsetName
).
decode
(
buffer
).
toString
();
}
private
static
CharsetDecoder
createCharsetDecoder
(
String
charsetName
)
{
return
Charset
.
forName
(
charsetName
).
newDecoder
().
onMalformedInput
(
CodingErrorAction
.
REPORT
)
.
onUnmappableCharacter
(
CodingErrorAction
.
REPORT
);
}
}
src/android/com/silkimen/http/HttpRequest.java
0 → 100644
View file @
09c88efe
/*
* Copyright (c) 2014 Kevin Sawicki <kevinsawicki@gmail.com>
* modified by contributors of cordova-plugin-advanced-http
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
package
com.silkimen.http
;
import
android.util.Log
;
import
static
java
.
net
.
HttpURLConnection
.
HTTP_BAD_REQUEST
;
import
static
java
.
net
.
HttpURLConnection
.
HTTP_CREATED
;
import
static
java
.
net
.
HttpURLConnection
.
HTTP_INTERNAL_ERROR
;
import
static
java
.
net
.
HttpURLConnection
.
HTTP_NO_CONTENT
;
import
static
java
.
net
.
HttpURLConnection
.
HTTP_NOT_FOUND
;
import
static
java
.
net
.
HttpURLConnection
.
HTTP_NOT_MODIFIED
;
import
static
java
.
net
.
HttpURLConnection
.
HTTP_OK
;
import
static
java
.
net
.
Proxy
.
Type
.
HTTP
;
import
java.io.BufferedInputStream
;
import
java.io.BufferedOutputStream
;
import
java.io.BufferedReader
;
import
java.io.ByteArrayInputStream
;
import
java.io.ByteArrayOutputStream
;
import
java.io.Closeable
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.FileNotFoundException
;
import
java.io.FileOutputStream
;
import
java.io.Flushable
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
import
java.io.OutputStream
;
import
java.io.OutputStreamWriter
;
import
java.io.PrintStream
;
import
java.io.Reader
;
import
java.io.UnsupportedEncodingException
;
import
java.io.Writer
;
import
java.net.HttpURLConnection
;
import
java.net.InetSocketAddress
;
import
java.net.MalformedURLException
;
import
java.net.Proxy
;
import
java.net.URI
;
import
java.net.URISyntaxException
;
import
java.net.URL
;
import
java.net.URLEncoder
;
import
java.nio.ByteBuffer
;
import
java.nio.CharBuffer
;
import
java.nio.charset.Charset
;
import
java.nio.charset.CharsetEncoder
;
import
java.security.AccessController
;
import
java.security.GeneralSecurityException
;
import
java.security.PrivilegedAction
;
import
java.security.SecureRandom
;
import
java.security.cert.X509Certificate
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.Iterator
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map.Entry
;
import
java.util.concurrent.Callable
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
java.util.concurrent.atomic.AtomicReference
;
import
java.util.zip.GZIPInputStream
;
import
javax.net.ssl.HostnameVerifier
;
import
javax.net.ssl.HttpsURLConnection
;
import
javax.net.ssl.SSLContext
;
import
javax.net.ssl.SSLSession
;
import
javax.net.ssl.SSLSocketFactory
;
import
javax.net.ssl.TrustManager
;
import
javax.net.ssl.X509TrustManager
;
/**
* A fluid interface for making HTTP requests using an underlying
* {@link HttpURLConnection} (or sub-class).
* <p>
* Each instance supports making a single request and cannot be reused for
* further requests.
*/
public
class
HttpRequest
{
/**
* 'UTF-8' charset name
*/
public
static
final
String
CHARSET_UTF8
=
"UTF-8"
;
/**
* 'application/x-www-form-urlencoded' content type header value
*/
public
static
final
String
CONTENT_TYPE_FORM
=
"application/x-www-form-urlencoded"
;
/**
* 'application/json' content type header value
*/
public
static
final
String
CONTENT_TYPE_JSON
=
"application/json"
;
/**
* 'gzip' encoding header value
*/
public
static
final
String
ENCODING_GZIP
=
"gzip"
;
/**
* 'Accept' header name
*/
public
static
final
String
HEADER_ACCEPT
=
"Accept"
;
/**
* 'Accept-Charset' header name
*/
public
static
final
String
HEADER_ACCEPT_CHARSET
=
"Accept-Charset"
;
/**
* 'Accept-Encoding' header name
*/
public
static
final
String
HEADER_ACCEPT_ENCODING
=
"Accept-Encoding"
;
/**
* 'Authorization' header name
*/
public
static
final
String
HEADER_AUTHORIZATION
=
"Authorization"
;
/**
* 'Cache-Control' header name
*/
public
static
final
String
HEADER_CACHE_CONTROL
=
"Cache-Control"
;
/**
* 'Content-Encoding' header name
*/
public
static
final
String
HEADER_CONTENT_ENCODING
=
"Content-Encoding"
;
/**
* 'Content-Length' header name
*/
public
static
final
String
HEADER_CONTENT_LENGTH
=
"Content-Length"
;
/**
* 'Content-Type' header name
*/
public
static
final
String
HEADER_CONTENT_TYPE
=
"Content-Type"
;
/**
* 'Date' header name
*/
public
static
final
String
HEADER_DATE
=
"Date"
;
/**
* 'ETag' header name
*/
public
static
final
String
HEADER_ETAG
=
"ETag"
;
/**
* 'Expires' header name
*/
public
static
final
String
HEADER_EXPIRES
=
"Expires"
;
/**
* 'If-None-Match' header name
*/
public
static
final
String
HEADER_IF_NONE_MATCH
=
"If-None-Match"
;
/**
* 'Last-Modified' header name
*/
public
static
final
String
HEADER_LAST_MODIFIED
=
"Last-Modified"
;
/**
* 'Location' header name
*/
public
static
final
String
HEADER_LOCATION
=
"Location"
;
/**
* 'Proxy-Authorization' header name
*/
public
static
final
String
HEADER_PROXY_AUTHORIZATION
=
"Proxy-Authorization"
;
/**
* 'Referer' header name
*/
public
static
final
String
HEADER_REFERER
=
"Referer"
;
/**
* 'Server' header name
*/
public
static
final
String
HEADER_SERVER
=
"Server"
;
/**
* 'User-Agent' header name
*/
public
static
final
String
HEADER_USER_AGENT
=
"User-Agent"
;
/**
* 'DELETE' request method
*/
public
static
final
String
METHOD_DELETE
=
"DELETE"
;
/**
* 'GET' request method
*/
public
static
final
String
METHOD_GET
=
"GET"
;
/**
* 'HEAD' request method
*/
public
static
final
String
METHOD_HEAD
=
"HEAD"
;
/**
* 'OPTIONS' options method
*/
public
static
final
String
METHOD_OPTIONS
=
"OPTIONS"
;
/**
* 'POST' request method
*/
public
static
final
String
METHOD_POST
=
"POST"
;
/**
* 'PUT' request method
*/
public
static
final
String
METHOD_PUT
=
"PUT"
;
/**
* 'TRACE' request method
*/
public
static
final
String
METHOD_TRACE
=
"TRACE"
;
/**
* 'charset' header value parameter
*/
public
static
final
String
PARAM_CHARSET
=
"charset"
;
private
static
final
String
BOUNDARY
=
"00content0boundary00"
;
private
static
final
String
CONTENT_TYPE_MULTIPART
=
"multipart/form-data; boundary="
+
BOUNDARY
;
private
static
final
String
CRLF
=
"\r\n"
;
private
static
final
String
[]
EMPTY_STRINGS
=
new
String
[
0
];
private
static
String
getValidCharset
(
final
String
charset
)
{
if
(
charset
!=
null
&&
charset
.
length
()
>
0
)
return
charset
;
else
return
CHARSET_UTF8
;
}
private
static
StringBuilder
addPathSeparator
(
final
String
baseUrl
,
final
StringBuilder
result
)
{
// Add trailing slash if the base URL doesn't have any path segments.
//
// The following test is checking for the last slash not being part of
// the protocol to host separator: '://'.
if
(
baseUrl
.
indexOf
(
':'
)
+
2
==
baseUrl
.
lastIndexOf
(
'/'
))
result
.
append
(
'/'
);
return
result
;
}
private
static
StringBuilder
addParamPrefix
(
final
String
baseUrl
,
final
StringBuilder
result
)
{
// Add '?' if missing and add '&' if params already exist in base url
final
int
queryStart
=
baseUrl
.
indexOf
(
'?'
);
final
int
lastChar
=
result
.
length
()
-
1
;
if
(
queryStart
==
-
1
)
result
.
append
(
'?'
);
else
if
(
queryStart
<
lastChar
&&
baseUrl
.
charAt
(
lastChar
)
!=
'&'
)
result
.
append
(
'&'
);
return
result
;
}
private
static
StringBuilder
addParam
(
final
Object
key
,
Object
value
,
final
StringBuilder
result
)
{
if
(
value
!=
null
&&
value
.
getClass
().
isArray
())
value
=
arrayToList
(
value
);
if
(
value
instanceof
Iterable
<?>)
{
Iterator
<?>
iterator
=
((
Iterable
<?>)
value
).
iterator
();
while
(
iterator
.
hasNext
())
{
result
.
append
(
key
);
result
.
append
(
"[]="
);
Object
element
=
iterator
.
next
();
if
(
element
!=
null
)
result
.
append
(
element
);
if
(
iterator
.
hasNext
())
result
.
append
(
"&"
);
}
}
else
{
result
.
append
(
key
);
result
.
append
(
"="
);
if
(
value
!=
null
)
result
.
append
(
value
);
}
return
result
;
}
/**
* Creates {@link HttpURLConnection HTTP connections} for {@link URL urls}.
*/
public
interface
ConnectionFactory
{
/**
* Open an {@link HttpURLConnection} for the specified {@link URL}.
*
* @throws IOException
*/
HttpURLConnection
create
(
URL
url
)
throws
IOException
;
/**
* Open an {@link HttpURLConnection} for the specified {@link URL} and
* {@link Proxy}.
*
* @throws IOException
*/
HttpURLConnection
create
(
URL
url
,
Proxy
proxy
)
throws
IOException
;
/**
* A {@link ConnectionFactory} which uses the built-in
* {@link URL#openConnection()}
*/
ConnectionFactory
DEFAULT
=
new
ConnectionFactory
()
{
public
HttpURLConnection
create
(
URL
url
)
throws
IOException
{
return
(
HttpURLConnection
)
url
.
openConnection
();
}
public
HttpURLConnection
create
(
URL
url
,
Proxy
proxy
)
throws
IOException
{
return
(
HttpURLConnection
)
url
.
openConnection
(
proxy
);
}
};
}
private
static
ConnectionFactory
CONNECTION_FACTORY
=
ConnectionFactory
.
DEFAULT
;
/**
* Specify the {@link ConnectionFactory} used to create new requests.
*/
public
static
void
setConnectionFactory
(
final
ConnectionFactory
connectionFactory
)
{
if
(
connectionFactory
==
null
)
CONNECTION_FACTORY
=
ConnectionFactory
.
DEFAULT
;
else
CONNECTION_FACTORY
=
connectionFactory
;
}
/**
* Callback interface for reporting upload progress for a request.
*/
public
interface
UploadProgress
{
/**
* Callback invoked as data is uploaded by the request.
*
* @param uploaded The number of bytes already uploaded
* @param total The total number of bytes that will be uploaded or -1 if the
* length is unknown.
*/
void
onUpload
(
long
uploaded
,
long
total
);
UploadProgress
DEFAULT
=
new
UploadProgress
()
{
public
void
onUpload
(
long
uploaded
,
long
total
)
{
}
};
}
/**
* <p>
* Encodes and decodes to and from Base64 notation.
* </p>
* <p>
* I am placing this code in the Public Domain. Do with it as you will. This
* software comes with no guarantees or warranties but with plenty of
* well-wishing instead! Please visit
* <a href="http://iharder.net/base64">http://iharder.net/base64</a>
* periodically to check for updates or to contribute improvements.
* </p>
*
* @author Robert Harder
* @author rob@iharder.net
* @version 2.3.7
*/
public
static
class
Base64
{
/** The equals sign (=) as a byte. */
private
final
static
byte
EQUALS_SIGN
=
(
byte
)
'='
;
/** Preferred encoding. */
private
final
static
String
PREFERRED_ENCODING
=
"US-ASCII"
;
/** The 64 valid Base64 values. */
private
final
static
byte
[]
_STANDARD_ALPHABET
=
{
(
byte
)
'A'
,
(
byte
)
'B'
,
(
byte
)
'C'
,
(
byte
)
'D'
,
(
byte
)
'E'
,
(
byte
)
'F'
,
(
byte
)
'G'
,
(
byte
)
'H'
,
(
byte
)
'I'
,
(
byte
)
'J'
,
(
byte
)
'K'
,
(
byte
)
'L'
,
(
byte
)
'M'
,
(
byte
)
'N'
,
(
byte
)
'O'
,
(
byte
)
'P'
,
(
byte
)
'Q'
,
(
byte
)
'R'
,
(
byte
)
'S'
,
(
byte
)
'T'
,
(
byte
)
'U'
,
(
byte
)
'V'
,
(
byte
)
'W'
,
(
byte
)
'X'
,
(
byte
)
'Y'
,
(
byte
)
'Z'
,
(
byte
)
'a'
,
(
byte
)
'b'
,
(
byte
)
'c'
,
(
byte
)
'd'
,
(
byte
)
'e'
,
(
byte
)
'f'
,
(
byte
)
'g'
,
(
byte
)
'h'
,
(
byte
)
'i'
,
(
byte
)
'j'
,
(
byte
)
'k'
,
(
byte
)
'l'
,
(
byte
)
'm'
,
(
byte
)
'n'
,
(
byte
)
'o'
,
(
byte
)
'p'
,
(
byte
)
'q'
,
(
byte
)
'r'
,
(
byte
)
's'
,
(
byte
)
't'
,
(
byte
)
'u'
,
(
byte
)
'v'
,
(
byte
)
'w'
,
(
byte
)
'x'
,
(
byte
)
'y'
,
(
byte
)
'z'
,
(
byte
)
'0'
,
(
byte
)
'1'
,
(
byte
)
'2'
,
(
byte
)
'3'
,
(
byte
)
'4'
,
(
byte
)
'5'
,
(
byte
)
'6'
,
(
byte
)
'7'
,
(
byte
)
'8'
,
(
byte
)
'9'
,
(
byte
)
'+'
,
(
byte
)
'/'
};
/** Defeats instantiation. */
private
Base64
()
{
}
/**
* <p>
* Encodes up to three bytes of the array <var>source</var> and writes the
* resulting four Base64 bytes to <var>destination</var>. The source and
* destination arrays can be manipulated anywhere along their length by
* specifying <var>srcOffset</var> and <var>destOffset</var>. This method does
* not check to make sure your arrays are large enough to accomodate
* <var>srcOffset</var> + 3 for the <var>source</var> array or
* <var>destOffset</var> + 4 for the <var>destination</var> array. The actual
* number of significant bytes in your array is given by <var>numSigBytes</var>.
* </p>
* <p>
* This is the lowest level of the encoding methods with all possible
* parameters.
* </p>
*
* @param source the array to convert
* @param srcOffset the index where conversion begins
* @param numSigBytes the number of significant bytes in your array
* @param destination the array to hold the conversion
* @param destOffset the index where output will be put
* @return the <var>destination</var> array
* @since 1.3
*/
private
static
byte
[]
encode3to4
(
byte
[]
source
,
int
srcOffset
,
int
numSigBytes
,
byte
[]
destination
,
int
destOffset
)
{
byte
[]
ALPHABET
=
_STANDARD_ALPHABET
;
int
inBuff
=
(
numSigBytes
>
0
?
((
source
[
srcOffset
]
<<
24
)
>>>
8
)
:
0
)
|
(
numSigBytes
>
1
?
((
source
[
srcOffset
+
1
]
<<
24
)
>>>
16
)
:
0
)
|
(
numSigBytes
>
2
?
((
source
[
srcOffset
+
2
]
<<
24
)
>>>
24
)
:
0
);
switch
(
numSigBytes
)
{
case
3
:
destination
[
destOffset
]
=
ALPHABET
[(
inBuff
>>>
18
)];
destination
[
destOffset
+
1
]
=
ALPHABET
[(
inBuff
>>>
12
)
&
0x3f
];
destination
[
destOffset
+
2
]
=
ALPHABET
[(
inBuff
>>>
6
)
&
0x3f
];
destination
[
destOffset
+
3
]
=
ALPHABET
[(
inBuff
)
&
0x3f
];
return
destination
;
case
2
:
destination
[
destOffset
]
=
ALPHABET
[(
inBuff
>>>
18
)];
destination
[
destOffset
+
1
]
=
ALPHABET
[(
inBuff
>>>
12
)
&
0x3f
];
destination
[
destOffset
+
2
]
=
ALPHABET
[(
inBuff
>>>
6
)
&
0x3f
];
destination
[
destOffset
+
3
]
=
EQUALS_SIGN
;
return
destination
;
case
1
:
destination
[
destOffset
]
=
ALPHABET
[(
inBuff
>>>
18
)];
destination
[
destOffset
+
1
]
=
ALPHABET
[(
inBuff
>>>
12
)
&
0x3f
];
destination
[
destOffset
+
2
]
=
EQUALS_SIGN
;
destination
[
destOffset
+
3
]
=
EQUALS_SIGN
;
return
destination
;
default
:
return
destination
;
}
}
/**
* Encode string as a byte array in Base64 annotation.
*
* @param string
* @return The Base64-encoded data as a string
*/
public
static
String
encode
(
String
string
)
{
byte
[]
bytes
;
try
{
bytes
=
string
.
getBytes
(
PREFERRED_ENCODING
);
}
catch
(
UnsupportedEncodingException
e
)
{
bytes
=
string
.
getBytes
();
}
return
encodeBytes
(
bytes
);
}
/**
* Encodes a byte array into Base64 notation.
*
* @param source The data to convert
* @return The Base64-encoded data as a String
* @throws NullPointerException if source array is null
* @throws IllegalArgumentException if source array, offset, or length are
* invalid
* @since 2.0
*/
public
static
String
encodeBytes
(
byte
[]
source
)
{
return
encodeBytes
(
source
,
0
,
source
.
length
);
}
/**
* Encodes a byte array into Base64 notation.
*
* @param source The data to convert
* @param off Offset in array where conversion should begin
* @param len Length of data to convert
* @return The Base64-encoded data as a String
* @throws NullPointerException if source array is null
* @throws IllegalArgumentException if source array, offset, or length are
* invalid
* @since 2.0
*/
public
static
String
encodeBytes
(
byte
[]
source
,
int
off
,
int
len
)
{
byte
[]
encoded
=
encodeBytesToBytes
(
source
,
off
,
len
);
try
{
return
new
String
(
encoded
,
PREFERRED_ENCODING
);
}
catch
(
UnsupportedEncodingException
uue
)
{
return
new
String
(
encoded
);
}
}
/**
* Similar to {@link #encodeBytes(byte[], int, int)} but returns a byte array
* instead of instantiating a String. This is more efficient if you're working
* with I/O streams and have large data sets to encode.
*
*
* @param source The data to convert
* @param off Offset in array where conversion should begin
* @param len Length of data to convert
* @return The Base64-encoded data as a String if there is an error
* @throws NullPointerException if source array is null
* @throws IllegalArgumentException if source array, offset, or length are
* invalid
* @since 2.3.1
*/
public
static
byte
[]
encodeBytesToBytes
(
byte
[]
source
,
int
off
,
int
len
)
{
if
(
source
==
null
)
throw
new
NullPointerException
(
"Cannot serialize a null array."
);
if
(
off
<
0
)
throw
new
IllegalArgumentException
(
"Cannot have negative offset: "
+
off
);
if
(
len
<
0
)
throw
new
IllegalArgumentException
(
"Cannot have length offset: "
+
len
);
if
(
off
+
len
>
source
.
length
)
throw
new
IllegalArgumentException
(
String
.
format
(
"Cannot have offset of %d and length of %d with array of length %d"
,
off
,
len
,
source
.
length
));
// Bytes needed for actual encoding
int
encLen
=
(
len
/
3
)
*
4
+
(
len
%
3
>
0
?
4
:
0
);
byte
[]
outBuff
=
new
byte
[
encLen
];
int
d
=
0
;
int
e
=
0
;
int
len2
=
len
-
2
;
for
(;
d
<
len2
;
d
+=
3
,
e
+=
4
)
encode3to4
(
source
,
d
+
off
,
3
,
outBuff
,
e
);
if
(
d
<
len
)
{
encode3to4
(
source
,
d
+
off
,
len
-
d
,
outBuff
,
e
);
e
+=
4
;
}
if
(
e
<=
outBuff
.
length
-
1
)
{
byte
[]
finalOut
=
new
byte
[
e
];
System
.
arraycopy
(
outBuff
,
0
,
finalOut
,
0
,
e
);
return
finalOut
;
}
else
return
outBuff
;
}
}
/**
* HTTP request exception whose cause is always an {@link IOException}
*/
public
static
class
HttpRequestException
extends
RuntimeException
{
private
static
final
long
serialVersionUID
=
-
1170466989781746231L
;
/**
* Create a new HttpRequestException with the given cause
*
* @param cause
*/
public
HttpRequestException
(
final
IOException
cause
)
{
super
(
cause
);
}
/**
* Get {@link IOException} that triggered this request exception
*
* @return {@link IOException} cause
*/
@Override
public
IOException
getCause
()
{
return
(
IOException
)
super
.
getCause
();
}
}
/**
* Operation that handles executing a callback once complete and handling nested
* exceptions
*
* @param <V>
*/
protected
static
abstract
class
Operation
<
V
>
implements
Callable
<
V
>
{
/**
* Run operation
*
* @return result
* @throws HttpRequestException
* @throws IOException
*/
protected
abstract
V
run
()
throws
HttpRequestException
,
IOException
;
/**
* Operation complete callback
*
* @throws IOException
*/
protected
abstract
void
done
()
throws
IOException
;
public
V
call
()
throws
HttpRequestException
{
boolean
thrown
=
false
;
try
{
return
run
();
}
catch
(
HttpRequestException
e
)
{
throw
e
;
}
catch
(
IOException
e
)
{
thrown
=
true
;
throw
new
HttpRequestException
(
e
);
}
finally
{
try
{
done
();
}
catch
(
IOException
e
)
{
}
}
}
}
/**
* Class that ensures a {@link Closeable} gets closed with proper exception
* handling.
*
* @param <V>
*/
protected
static
abstract
class
CloseOperation
<
V
>
extends
Operation
<
V
>
{
private
final
Closeable
closeable
;
private
final
boolean
ignoreCloseExceptions
;
/**
* Create closer for operation
*
* @param closeable
* @param ignoreCloseExceptions
*/
protected
CloseOperation
(
final
Closeable
closeable
,
final
boolean
ignoreCloseExceptions
)
{
this
.
closeable
=
closeable
;
this
.
ignoreCloseExceptions
=
ignoreCloseExceptions
;
}
@Override
protected
void
done
()
throws
IOException
{
if
(
closeable
instanceof
Flushable
)
((
Flushable
)
closeable
).
flush
();
if
(
ignoreCloseExceptions
)
try
{
closeable
.
close
();
}
catch
(
IOException
e
)
{
Log
.
e
(
"Error closing resource"
,
"An error occurred while closing the resource"
,
e
);
}
else
closeable
.
close
();
}
}
/**
* Class that and ensures a {@link Flushable} gets flushed with proper exception
* handling.
*
* @param <V>
*/
protected
static
abstract
class
FlushOperation
<
V
>
extends
Operation
<
V
>
{
private
final
Flushable
flushable
;
/**
* Create flush operation
*
* @param flushable
*/
protected
FlushOperation
(
final
Flushable
flushable
)
{
this
.
flushable
=
flushable
;
}
@Override
protected
void
done
()
throws
IOException
{
flushable
.
flush
();
}
}
/**
* Request output stream
*/
public
static
class
RequestOutputStream
extends
BufferedOutputStream
{
private
final
CharsetEncoder
encoder
;
/**
* Create request output stream
*
* @param stream
* @param charset
* @param bufferSize
*/
public
RequestOutputStream
(
final
OutputStream
stream
,
final
String
charset
,
final
int
bufferSize
)
{
super
(
stream
,
bufferSize
);
encoder
=
Charset
.
forName
(
getValidCharset
(
charset
)).
newEncoder
();
}
/**
* Write string to stream
*
* @param value
* @return this stream
* @throws IOException
*/
public
RequestOutputStream
write
(
final
String
value
)
throws
IOException
{
final
ByteBuffer
bytes
=
encoder
.
encode
(
CharBuffer
.
wrap
(
value
));
super
.
write
(
bytes
.
array
(),
0
,
bytes
.
limit
());
return
this
;
}
}
/**
* Represents array of any type as list of objects so we can easily iterate over
* it
*
* @param array of elements
* @return list with the same elements
*/
private
static
List
<
Object
>
arrayToList
(
final
Object
array
)
{
if
(
array
instanceof
Object
[])
return
Arrays
.
asList
((
Object
[])
array
);
List
<
Object
>
result
=
new
ArrayList
<
Object
>();
// Arrays of the primitive types can't be cast to array of Object, so this:
if
(
array
instanceof
int
[])
for
(
int
value
:
(
int
[])
array
)
result
.
add
(
value
);
else
if
(
array
instanceof
boolean
[])
for
(
boolean
value
:
(
boolean
[])
array
)
result
.
add
(
value
);
else
if
(
array
instanceof
long
[])
for
(
long
value
:
(
long
[])
array
)
result
.
add
(
value
);
else
if
(
array
instanceof
float
[])
for
(
float
value
:
(
float
[])
array
)
result
.
add
(
value
);
else
if
(
array
instanceof
double
[])
for
(
double
value
:
(
double
[])
array
)
result
.
add
(
value
);
else
if
(
array
instanceof
short
[])
for
(
short
value
:
(
short
[])
array
)
result
.
add
(
value
);
else
if
(
array
instanceof
byte
[])
for
(
byte
value
:
(
byte
[])
array
)
result
.
add
(
value
);
else
if
(
array
instanceof
char
[])
for
(
char
value
:
(
char
[])
array
)
result
.
add
(
value
);
return
result
;
}
/**
* Encode the given URL as an ASCII {@link String}
* <p>
* This method ensures the path and query segments of the URL are properly
* encoded such as ' ' characters being encoded to '%20' or any UTF-8 characters
* that are non-ASCII. No encoding of URLs is done by default by the
* {@link HttpRequest} constructors and so if URL encoding is needed this method
* should be called before calling the {@link HttpRequest} constructor.
*
* @param url
* @return encoded URL
* @throws HttpRequestException
*/
public
static
String
encode
(
final
CharSequence
url
)
throws
HttpRequestException
{
URL
parsed
;
try
{
parsed
=
new
URL
(
url
.
toString
());
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
String
host
=
parsed
.
getHost
();
int
port
=
parsed
.
getPort
();
if
(
port
!=
-
1
)
host
=
host
+
':'
+
Integer
.
toString
(
port
);
try
{
String
encoded
=
new
URI
(
parsed
.
getProtocol
(),
host
,
parsed
.
getPath
(),
parsed
.
getQuery
(),
null
).
toASCIIString
();
int
paramsStart
=
encoded
.
indexOf
(
'?'
);
if
(
paramsStart
>
0
&&
paramsStart
+
1
<
encoded
.
length
())
encoded
=
encoded
.
substring
(
0
,
paramsStart
+
1
)
+
encoded
.
substring
(
paramsStart
+
1
).
replace
(
"+"
,
"%2B"
);
return
encoded
;
}
catch
(
URISyntaxException
e
)
{
IOException
io
=
new
IOException
(
"Parsing URI failed"
);
io
.
initCause
(
e
);
throw
new
HttpRequestException
(
io
);
}
}
/**
* Append given map as query parameters to the base URL
* <p>
* Each map entry's key will be a parameter name and the value's
* {@link Object#toString()} will be the parameter value.
*
* @param url
* @param params
* @return URL with appended query params
*/
public
static
String
append
(
final
CharSequence
url
,
final
Map
<?,
?>
params
)
{
final
String
baseUrl
=
url
.
toString
();
if
(
params
==
null
||
params
.
isEmpty
())
return
baseUrl
;
final
StringBuilder
result
=
new
StringBuilder
(
baseUrl
);
addPathSeparator
(
baseUrl
,
result
);
addParamPrefix
(
baseUrl
,
result
);
Entry
<?,
?>
entry
;
Iterator
<?>
iterator
=
params
.
entrySet
().
iterator
();
entry
=
(
Entry
<?,
?>)
iterator
.
next
();
addParam
(
entry
.
getKey
().
toString
(),
entry
.
getValue
(),
result
);
while
(
iterator
.
hasNext
())
{
result
.
append
(
'&'
);
entry
=
(
Entry
<?,
?>)
iterator
.
next
();
addParam
(
entry
.
getKey
().
toString
(),
entry
.
getValue
(),
result
);
}
return
result
.
toString
();
}
/**
* Append given name/value pairs as query parameters to the base URL
* <p>
* The params argument is interpreted as a sequence of name/value pairs so the
* given number of params must be divisible by 2.
*
* @param url
* @param params name/value pairs
* @return URL with appended query params
*/
public
static
String
append
(
final
CharSequence
url
,
final
Object
...
params
)
{
final
String
baseUrl
=
url
.
toString
();
if
(
params
==
null
||
params
.
length
==
0
)
return
baseUrl
;
if
(
params
.
length
%
2
!=
0
)
throw
new
IllegalArgumentException
(
"Must specify an even number of parameter names/values"
);
final
StringBuilder
result
=
new
StringBuilder
(
baseUrl
);
addPathSeparator
(
baseUrl
,
result
);
addParamPrefix
(
baseUrl
,
result
);
addParam
(
params
[
0
],
params
[
1
],
result
);
for
(
int
i
=
2
;
i
<
params
.
length
;
i
+=
2
)
{
result
.
append
(
'&'
);
addParam
(
params
[
i
],
params
[
i
+
1
],
result
);
}
return
result
.
toString
();
}
/**
* Start a 'GET' request to the given URL
*
* @param url
* @return request
* @throws HttpRequestException
*/
public
static
HttpRequest
get
(
final
CharSequence
url
)
throws
HttpRequestException
{
return
new
HttpRequest
(
url
,
METHOD_GET
);
}
/**
* Start a 'GET' request to the given URL
*
* @param url
* @return request
* @throws HttpRequestException
*/
public
static
HttpRequest
get
(
final
URL
url
)
throws
HttpRequestException
{
return
new
HttpRequest
(
url
,
METHOD_GET
);
}
/**
* Start a 'GET' request to the given URL along with the query params
*
* @param baseUrl
* @param params The query parameters to include as part of the baseUrl
* @param encode true to encode the full URL
*
* @see #append(CharSequence, Map)
* @see #encode(CharSequence)
*
* @return request
*/
public
static
HttpRequest
get
(
final
CharSequence
baseUrl
,
final
Map
<?,
?>
params
,
final
boolean
encode
)
{
String
url
=
append
(
baseUrl
,
params
);
return
get
(
encode
?
encode
(
url
)
:
url
);
}
/**
* Start a 'GET' request to the given URL along with the query params
*
* @param baseUrl
* @param encode true to encode the full URL
* @param params the name/value query parameter pairs to include as part of the
* baseUrl
*
* @see #append(CharSequence, Object...)
* @see #encode(CharSequence)
*
* @return request
*/
public
static
HttpRequest
get
(
final
CharSequence
baseUrl
,
final
boolean
encode
,
final
Object
...
params
)
{
String
url
=
append
(
baseUrl
,
params
);
return
get
(
encode
?
encode
(
url
)
:
url
);
}
/**
* Start a 'POST' request to the given URL
*
* @param url
* @return request
* @throws HttpRequestException
*/
public
static
HttpRequest
post
(
final
CharSequence
url
)
throws
HttpRequestException
{
return
new
HttpRequest
(
url
,
METHOD_POST
);
}
/**
* Start a 'POST' request to the given URL
*
* @param url
* @return request
* @throws HttpRequestException
*/
public
static
HttpRequest
post
(
final
URL
url
)
throws
HttpRequestException
{
return
new
HttpRequest
(
url
,
METHOD_POST
);
}
/**
* Start a 'POST' request to the given URL along with the query params
*
* @param baseUrl
* @param params the query parameters to include as part of the baseUrl
* @param encode true to encode the full URL
*
* @see #append(CharSequence, Map)
* @see #encode(CharSequence)
*
* @return request
*/
public
static
HttpRequest
post
(
final
CharSequence
baseUrl
,
final
Map
<?,
?>
params
,
final
boolean
encode
)
{
String
url
=
append
(
baseUrl
,
params
);
return
post
(
encode
?
encode
(
url
)
:
url
);
}
/**
* Start a 'POST' request to the given URL along with the query params
*
* @param baseUrl
* @param encode true to encode the full URL
* @param params the name/value query parameter pairs to include as part of the
* baseUrl
*
* @see #append(CharSequence, Object...)
* @see #encode(CharSequence)
*
* @return request
*/
public
static
HttpRequest
post
(
final
CharSequence
baseUrl
,
final
boolean
encode
,
final
Object
...
params
)
{
String
url
=
append
(
baseUrl
,
params
);
return
post
(
encode
?
encode
(
url
)
:
url
);
}
/**
* Start a 'PUT' request to the given URL
*
* @param url
* @return request
* @throws HttpRequestException
*/
public
static
HttpRequest
put
(
final
CharSequence
url
)
throws
HttpRequestException
{
return
new
HttpRequest
(
url
,
METHOD_PUT
);
}
/**
* Start a 'PUT' request to the given URL
*
* @param url
* @return request
* @throws HttpRequestException
*/
public
static
HttpRequest
put
(
final
URL
url
)
throws
HttpRequestException
{
return
new
HttpRequest
(
url
,
METHOD_PUT
);
}
/**
* Start a 'PUT' request to the given URL along with the query params
*
* @param baseUrl
* @param params the query parameters to include as part of the baseUrl
* @param encode true to encode the full URL
*
* @see #append(CharSequence, Map)
* @see #encode(CharSequence)
*
* @return request
*/
public
static
HttpRequest
put
(
final
CharSequence
baseUrl
,
final
Map
<?,
?>
params
,
final
boolean
encode
)
{
String
url
=
append
(
baseUrl
,
params
);
return
put
(
encode
?
encode
(
url
)
:
url
);
}
/**
* Start a 'PUT' request to the given URL along with the query params
*
* @param baseUrl
* @param encode true to encode the full URL
* @param params the name/value query parameter pairs to include as part of the
* baseUrl
*
* @see #append(CharSequence, Object...)
* @see #encode(CharSequence)
*
* @return request
*/
public
static
HttpRequest
put
(
final
CharSequence
baseUrl
,
final
boolean
encode
,
final
Object
...
params
)
{
String
url
=
append
(
baseUrl
,
params
);
return
put
(
encode
?
encode
(
url
)
:
url
);
}
/**
* Start a 'DELETE' request to the given URL
*
* @param url
* @return request
* @throws HttpRequestException
*/
public
static
HttpRequest
delete
(
final
CharSequence
url
)
throws
HttpRequestException
{
return
new
HttpRequest
(
url
,
METHOD_DELETE
);
}
/**
* Start a 'DELETE' request to the given URL
*
* @param url
* @return request
* @throws HttpRequestException
*/
public
static
HttpRequest
delete
(
final
URL
url
)
throws
HttpRequestException
{
return
new
HttpRequest
(
url
,
METHOD_DELETE
);
}
/**
* Start a 'DELETE' request to the given URL along with the query params
*
* @param baseUrl
* @param params The query parameters to include as part of the baseUrl
* @param encode true to encode the full URL
*
* @see #append(CharSequence, Map)
* @see #encode(CharSequence)
*
* @return request
*/
public
static
HttpRequest
delete
(
final
CharSequence
baseUrl
,
final
Map
<?,
?>
params
,
final
boolean
encode
)
{
String
url
=
append
(
baseUrl
,
params
);
return
delete
(
encode
?
encode
(
url
)
:
url
);
}
/**
* Start a 'DELETE' request to the given URL along with the query params
*
* @param baseUrl
* @param encode true to encode the full URL
* @param params the name/value query parameter pairs to include as part of the
* baseUrl
*
* @see #append(CharSequence, Object...)
* @see #encode(CharSequence)
*
* @return request
*/
public
static
HttpRequest
delete
(
final
CharSequence
baseUrl
,
final
boolean
encode
,
final
Object
...
params
)
{
String
url
=
append
(
baseUrl
,
params
);
return
delete
(
encode
?
encode
(
url
)
:
url
);
}
/**
* Start a 'HEAD' request to the given URL
*
* @param url
* @return request
* @throws HttpRequestException
*/
public
static
HttpRequest
head
(
final
CharSequence
url
)
throws
HttpRequestException
{
return
new
HttpRequest
(
url
,
METHOD_HEAD
);
}
/**
* Start a 'HEAD' request to the given URL
*
* @param url
* @return request
* @throws HttpRequestException
*/
public
static
HttpRequest
head
(
final
URL
url
)
throws
HttpRequestException
{
return
new
HttpRequest
(
url
,
METHOD_HEAD
);
}
/**
* Start a 'HEAD' request to the given URL along with the query params
*
* @param baseUrl
* @param params The query parameters to include as part of the baseUrl
* @param encode true to encode the full URL
*
* @see #append(CharSequence, Map)
* @see #encode(CharSequence)
*
* @return request
*/
public
static
HttpRequest
head
(
final
CharSequence
baseUrl
,
final
Map
<?,
?>
params
,
final
boolean
encode
)
{
String
url
=
append
(
baseUrl
,
params
);
return
head
(
encode
?
encode
(
url
)
:
url
);
}
/**
* Start a 'GET' request to the given URL along with the query params
*
* @param baseUrl
* @param encode true to encode the full URL
* @param params the name/value query parameter pairs to include as part of the
* baseUrl
*
* @see #append(CharSequence, Object...)
* @see #encode(CharSequence)
*
* @return request
*/
public
static
HttpRequest
head
(
final
CharSequence
baseUrl
,
final
boolean
encode
,
final
Object
...
params
)
{
String
url
=
append
(
baseUrl
,
params
);
return
head
(
encode
?
encode
(
url
)
:
url
);
}
/**
* Start an 'OPTIONS' request to the given URL
*
* @param url
* @return request
* @throws HttpRequestException
*/
public
static
HttpRequest
options
(
final
CharSequence
url
)
throws
HttpRequestException
{
return
new
HttpRequest
(
url
,
METHOD_OPTIONS
);
}
/**
* Start an 'OPTIONS' request to the given URL
*
* @param url
* @return request
* @throws HttpRequestException
*/
public
static
HttpRequest
options
(
final
URL
url
)
throws
HttpRequestException
{
return
new
HttpRequest
(
url
,
METHOD_OPTIONS
);
}
/**
* Start a 'TRACE' request to the given URL
*
* @param url
* @return request
* @throws HttpRequestException
*/
public
static
HttpRequest
trace
(
final
CharSequence
url
)
throws
HttpRequestException
{
return
new
HttpRequest
(
url
,
METHOD_TRACE
);
}
/**
* Start a 'TRACE' request to the given URL
*
* @param url
* @return request
* @throws HttpRequestException
*/
public
static
HttpRequest
trace
(
final
URL
url
)
throws
HttpRequestException
{
return
new
HttpRequest
(
url
,
METHOD_TRACE
);
}
/**
* Set the 'http.keepAlive' property to the given value.
* <p>
* This setting will apply to all requests.
*
* @param keepAlive
*/
public
static
void
keepAlive
(
final
boolean
keepAlive
)
{
setProperty
(
"http.keepAlive"
,
Boolean
.
toString
(
keepAlive
));
}
/**
* Set the 'http.maxConnections' property to the given value.
* <p>
* This setting will apply to all requests.
*
* @param maxConnections
*/
public
static
void
maxConnections
(
final
int
maxConnections
)
{
setProperty
(
"http.maxConnections"
,
Integer
.
toString
(
maxConnections
));
}
/**
* Set the 'http.proxyHost' and 'https.proxyHost' properties to the given host
* value.
* <p>
* This setting will apply to all requests.
*
* @param host
*/
public
static
void
proxyHost
(
final
String
host
)
{
setProperty
(
"http.proxyHost"
,
host
);
setProperty
(
"https.proxyHost"
,
host
);
}
/**
* Set the 'http.proxyPort' and 'https.proxyPort' properties to the given port
* number.
* <p>
* This setting will apply to all requests.
*
* @param port
*/
public
static
void
proxyPort
(
final
int
port
)
{
final
String
portValue
=
Integer
.
toString
(
port
);
setProperty
(
"http.proxyPort"
,
portValue
);
setProperty
(
"https.proxyPort"
,
portValue
);
}
/**
* Set the 'http.nonProxyHosts' property to the given host values.
* <p>
* Hosts will be separated by a '|' character.
* <p>
* This setting will apply to all requests.
*
* @param hosts
*/
public
static
void
nonProxyHosts
(
final
String
...
hosts
)
{
if
(
hosts
!=
null
&&
hosts
.
length
>
0
)
{
StringBuilder
separated
=
new
StringBuilder
();
int
last
=
hosts
.
length
-
1
;
for
(
int
i
=
0
;
i
<
last
;
i
++)
separated
.
append
(
hosts
[
i
]).
append
(
'|'
);
separated
.
append
(
hosts
[
last
]);
setProperty
(
"http.nonProxyHosts"
,
separated
.
toString
());
}
else
setProperty
(
"http.nonProxyHosts"
,
null
);
}
/**
* Set property to given value.
* <p>
* Specifying a null value will cause the property to be cleared
*
* @param name
* @param value
* @return previous value
*/
private
static
String
setProperty
(
final
String
name
,
final
String
value
)
{
final
PrivilegedAction
<
String
>
action
;
if
(
value
!=
null
)
action
=
new
PrivilegedAction
<
String
>()
{
public
String
run
()
{
return
System
.
setProperty
(
name
,
value
);
}
};
else
action
=
new
PrivilegedAction
<
String
>()
{
public
String
run
()
{
return
System
.
clearProperty
(
name
);
}
};
return
AccessController
.
doPrivileged
(
action
);
}
private
HttpURLConnection
connection
=
null
;
private
final
URL
url
;
private
final
String
requestMethod
;
private
RequestOutputStream
output
;
private
boolean
multipart
;
private
boolean
form
;
private
boolean
ignoreCloseExceptions
=
true
;
private
boolean
uncompress
=
false
;
private
int
bufferSize
=
8192
;
private
long
totalSize
=
-
1
;
private
long
totalWritten
=
0
;
private
String
httpProxyHost
;
private
int
httpProxyPort
;
private
UploadProgress
progress
=
UploadProgress
.
DEFAULT
;
/**
* Create HTTP connection wrapper
*
* @param url Remote resource URL.
* @param method HTTP request method (e.g., "GET", "POST").
* @throws HttpRequestException
*/
public
HttpRequest
(
final
CharSequence
url
,
final
String
method
)
throws
HttpRequestException
{
try
{
this
.
url
=
new
URL
(
url
.
toString
());
}
catch
(
MalformedURLException
e
)
{
throw
new
HttpRequestException
(
e
);
}
this
.
requestMethod
=
method
;
}
/**
* Create HTTP connection wrapper
*
* @param url Remote resource URL.
* @param method HTTP request method (e.g., "GET", "POST").
* @throws HttpRequestException
*/
public
HttpRequest
(
final
URL
url
,
final
String
method
)
throws
HttpRequestException
{
this
.
url
=
url
;
this
.
requestMethod
=
method
;
}
private
Proxy
createProxy
()
{
return
new
Proxy
(
HTTP
,
new
InetSocketAddress
(
httpProxyHost
,
httpProxyPort
));
}
private
HttpURLConnection
createConnection
()
{
try
{
final
HttpURLConnection
connection
;
if
(
httpProxyHost
!=
null
)
connection
=
CONNECTION_FACTORY
.
create
(
url
,
createProxy
());
else
connection
=
CONNECTION_FACTORY
.
create
(
url
);
connection
.
setRequestMethod
(
requestMethod
);
return
connection
;
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
}
@Override
public
String
toString
()
{
return
method
()
+
' '
+
url
();
}
/**
* Get underlying connection
*
* @return connection
*/
public
HttpURLConnection
getConnection
()
{
if
(
connection
==
null
)
connection
=
createConnection
();
return
connection
;
}
/**
* Set whether or not to ignore exceptions that occur from calling
* {@link Closeable#close()}
* <p>
* The default value of this setting is <code>true</code>
*
* @param ignore
* @return this request
*/
public
HttpRequest
ignoreCloseExceptions
(
final
boolean
ignore
)
{
ignoreCloseExceptions
=
ignore
;
return
this
;
}
/**
* Get whether or not exceptions thrown by {@link Closeable#close()} are ignored
*
* @return true if ignoring, false if throwing
*/
public
boolean
ignoreCloseExceptions
()
{
return
ignoreCloseExceptions
;
}
/**
* Get the status code of the response
*
* @return the response code
* @throws HttpRequestException
*/
public
int
code
()
throws
HttpRequestException
{
try
{
closeOutput
();
return
getConnection
().
getResponseCode
();
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
}
/**
* Set the value of the given {@link AtomicInteger} to the status code of the
* response
*
* @param output
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
code
(
final
AtomicInteger
output
)
throws
HttpRequestException
{
output
.
set
(
code
());
return
this
;
}
/**
* Is the response code a 200 OK?
*
* @return true if 200, false otherwise
* @throws HttpRequestException
*/
public
boolean
ok
()
throws
HttpRequestException
{
return
HTTP_OK
==
code
();
}
/**
* Is the response code a 201 Created?
*
* @return true if 201, false otherwise
* @throws HttpRequestException
*/
public
boolean
created
()
throws
HttpRequestException
{
return
HTTP_CREATED
==
code
();
}
/**
* Is the response code a 204 No Content?
*
* @return true if 204, false otherwise
* @throws HttpRequestException
*/
public
boolean
noContent
()
throws
HttpRequestException
{
return
HTTP_NO_CONTENT
==
code
();
}
/**
* Is the response code a 500 Internal Server Error?
*
* @return true if 500, false otherwise
* @throws HttpRequestException
*/
public
boolean
serverError
()
throws
HttpRequestException
{
return
HTTP_INTERNAL_ERROR
==
code
();
}
/**
* Is the response code a 400 Bad Request?
*
* @return true if 400, false otherwise
* @throws HttpRequestException
*/
public
boolean
badRequest
()
throws
HttpRequestException
{
return
HTTP_BAD_REQUEST
==
code
();
}
/**
* Is the response code a 404 Not Found?
*
* @return true if 404, false otherwise
* @throws HttpRequestException
*/
public
boolean
notFound
()
throws
HttpRequestException
{
return
HTTP_NOT_FOUND
==
code
();
}
/**
* Is the response code a 304 Not Modified?
*
* @return true if 304, false otherwise
* @throws HttpRequestException
*/
public
boolean
notModified
()
throws
HttpRequestException
{
return
HTTP_NOT_MODIFIED
==
code
();
}
/**
* Get status message of the response
*
* @return message
* @throws HttpRequestException
*/
public
String
message
()
throws
HttpRequestException
{
try
{
closeOutput
();
return
getConnection
().
getResponseMessage
();
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
}
/**
* Disconnect the connection
*
* @return this request
*/
public
HttpRequest
disconnect
()
{
getConnection
().
disconnect
();
return
this
;
}
/**
* Set chunked streaming mode to the given size
*
* @param size
* @return this request
*/
public
HttpRequest
chunk
(
final
int
size
)
{
getConnection
().
setChunkedStreamingMode
(
size
);
return
this
;
}
/**
* Set the size used when buffering and copying between streams
* <p>
* This size is also used for send and receive buffers created for both char and
* byte arrays
* <p>
* The default buffer size is 8,192 bytes
*
* @param size
* @return this request
*/
public
HttpRequest
bufferSize
(
final
int
size
)
{
if
(
size
<
1
)
throw
new
IllegalArgumentException
(
"Size must be greater than zero"
);
bufferSize
=
size
;
return
this
;
}
/**
* Get the configured buffer size
* <p>
* The default buffer size is 8,192 bytes
*
* @return buffer size
*/
public
int
bufferSize
()
{
return
bufferSize
;
}
/**
* Set whether or not the response body should be automatically uncompressed
* when read from.
* <p>
* This will only affect requests that have the 'Content-Encoding' response
* header set to 'gzip'.
* <p>
* This causes all receive methods to use a {@link GZIPInputStream} when
* applicable so that higher level streams and readers can read the data
* uncompressed.
* <p>
* Setting this option does not cause any request headers to be set
* automatically so {@link #acceptGzipEncoding()} should be used in conjunction
* with this setting to tell the server to gzip the response.
*
* @param uncompress
* @return this request
*/
public
HttpRequest
uncompress
(
final
boolean
uncompress
)
{
this
.
uncompress
=
uncompress
;
return
this
;
}
/**
* Create byte array output stream
*
* @return stream
*/
protected
ByteArrayOutputStream
byteStream
()
{
final
int
size
=
contentLength
();
if
(
size
>
0
)
return
new
ByteArrayOutputStream
(
size
);
else
return
new
ByteArrayOutputStream
();
}
/**
* Get response as {@link String} in given character set
* <p>
* This will fall back to using the UTF-8 character set if the given charset is
* null
*
* @param charset
* @return string
* @throws HttpRequestException
*/
public
String
body
(
final
String
charset
)
throws
HttpRequestException
{
final
ByteArrayOutputStream
output
=
byteStream
();
try
{
copy
(
buffer
(),
output
);
return
output
.
toString
(
getValidCharset
(
charset
));
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
}
/**
* Get response as {@link String} using character set returned from
* {@link #charset()}
*
* @return string
* @throws HttpRequestException
*/
public
String
body
()
throws
HttpRequestException
{
return
body
(
charset
());
}
/**
* Get the response body as a {@link String} and set it as the value of the
* given reference.
*
* @param output
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
body
(
final
AtomicReference
<
String
>
output
)
throws
HttpRequestException
{
output
.
set
(
body
());
return
this
;
}
/**
* Get the response body as a {@link String} and set it as the value of the
* given reference.
*
* @param output
* @param charset
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
body
(
final
AtomicReference
<
String
>
output
,
final
String
charset
)
throws
HttpRequestException
{
output
.
set
(
body
(
charset
));
return
this
;
}
/**
* Is the response body empty?
*
* @return true if the Content-Length response header is 0, false otherwise
* @throws HttpRequestException
*/
public
boolean
isBodyEmpty
()
throws
HttpRequestException
{
return
contentLength
()
==
0
;
}
/**
* Get response as byte array
*
* @return byte array
* @throws HttpRequestException
*/
public
byte
[]
bytes
()
throws
HttpRequestException
{
final
ByteArrayOutputStream
output
=
byteStream
();
try
{
copy
(
buffer
(),
output
);
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
return
output
.
toByteArray
();
}
/**
* Get response in a buffered stream
*
* @see #bufferSize(int)
* @return stream
* @throws HttpRequestException
*/
public
BufferedInputStream
buffer
()
throws
HttpRequestException
{
return
new
BufferedInputStream
(
stream
(),
bufferSize
);
}
/**
* Get stream to response body
*
* @return stream
* @throws HttpRequestException
*/
public
InputStream
stream
()
throws
HttpRequestException
{
InputStream
stream
;
if
(
code
()
<
HTTP_BAD_REQUEST
)
try
{
stream
=
getConnection
().
getInputStream
();
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
else
{
stream
=
getConnection
().
getErrorStream
();
if
(
stream
==
null
)
try
{
stream
=
getConnection
().
getInputStream
();
}
catch
(
IOException
e
)
{
if
(
contentLength
()
>
0
)
throw
new
HttpRequestException
(
e
);
else
stream
=
new
ByteArrayInputStream
(
new
byte
[
0
]);
}
}
if
(!
uncompress
||
!
ENCODING_GZIP
.
equals
(
contentEncoding
()))
return
stream
;
else
try
{
return
new
GZIPInputStream
(
stream
);
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
}
/**
* Get reader to response body using given character set.
* <p>
* This will fall back to using the UTF-8 character set if the given charset is
* null
*
* @param charset
* @return reader
* @throws HttpRequestException
*/
public
InputStreamReader
reader
(
final
String
charset
)
throws
HttpRequestException
{
try
{
return
new
InputStreamReader
(
stream
(),
getValidCharset
(
charset
));
}
catch
(
UnsupportedEncodingException
e
)
{
throw
new
HttpRequestException
(
e
);
}
}
/**
* Get reader to response body using the character set returned from
* {@link #charset()}
*
* @return reader
* @throws HttpRequestException
*/
public
InputStreamReader
reader
()
throws
HttpRequestException
{
return
reader
(
charset
());
}
/**
* Get buffered reader to response body using the given character set r and the
* configured buffer size
*
*
* @see #bufferSize(int)
* @param charset
* @return reader
* @throws HttpRequestException
*/
public
BufferedReader
bufferedReader
(
final
String
charset
)
throws
HttpRequestException
{
return
new
BufferedReader
(
reader
(
charset
),
bufferSize
);
}
/**
* Get buffered reader to response body using the character set returned from
* {@link #charset()} and the configured buffer size
*
* @see #bufferSize(int)
* @return reader
* @throws HttpRequestException
*/
public
BufferedReader
bufferedReader
()
throws
HttpRequestException
{
return
bufferedReader
(
charset
());
}
/**
* Stream response body to file
*
* @param file
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
receive
(
final
File
file
)
throws
HttpRequestException
{
final
OutputStream
output
;
try
{
output
=
new
BufferedOutputStream
(
new
FileOutputStream
(
file
),
bufferSize
);
}
catch
(
FileNotFoundException
e
)
{
throw
new
HttpRequestException
(
e
);
}
return
new
CloseOperation
<
HttpRequest
>(
output
,
ignoreCloseExceptions
)
{
@Override
protected
HttpRequest
run
()
throws
HttpRequestException
,
IOException
{
return
receive
(
output
);
}
}.
call
();
}
/**
* Stream response to given output stream
*
* @param output
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
receive
(
final
OutputStream
output
)
throws
HttpRequestException
{
try
{
return
copy
(
buffer
(),
output
);
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
}
/**
* Stream response to given print stream
*
* @param output
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
receive
(
final
PrintStream
output
)
throws
HttpRequestException
{
return
receive
((
OutputStream
)
output
);
}
/**
* Receive response into the given appendable
*
* @param appendable
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
receive
(
final
Appendable
appendable
)
throws
HttpRequestException
{
final
BufferedReader
reader
=
bufferedReader
();
return
new
CloseOperation
<
HttpRequest
>(
reader
,
ignoreCloseExceptions
)
{
@Override
public
HttpRequest
run
()
throws
IOException
{
final
CharBuffer
buffer
=
CharBuffer
.
allocate
(
bufferSize
);
int
read
;
while
((
read
=
reader
.
read
(
buffer
))
!=
-
1
)
{
buffer
.
rewind
();
appendable
.
append
(
buffer
,
0
,
read
);
buffer
.
rewind
();
}
return
HttpRequest
.
this
;
}
}.
call
();
}
/**
* Receive response into the given writer
*
* @param writer
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
receive
(
final
Writer
writer
)
throws
HttpRequestException
{
final
BufferedReader
reader
=
bufferedReader
();
return
new
CloseOperation
<
HttpRequest
>(
reader
,
ignoreCloseExceptions
)
{
@Override
public
HttpRequest
run
()
throws
IOException
{
return
copy
(
reader
,
writer
);
}
}.
call
();
}
/**
* Set read timeout on connection to given value
*
* @param timeout
* @return this request
*/
public
HttpRequest
readTimeout
(
final
int
timeout
)
{
getConnection
().
setReadTimeout
(
timeout
);
return
this
;
}
/**
* Set connect timeout on connection to given value
*
* @param timeout
* @return this request
*/
public
HttpRequest
connectTimeout
(
final
int
timeout
)
{
getConnection
().
setConnectTimeout
(
timeout
);
return
this
;
}
/**
* Set header name to given value
*
* @param name
* @param value
* @return this request
*/
public
HttpRequest
header
(
final
String
name
,
final
String
value
)
{
getConnection
().
setRequestProperty
(
name
,
value
);
return
this
;
}
/**
* Set header name to given value
*
* @param name
* @param value
* @return this request
*/
public
HttpRequest
header
(
final
String
name
,
final
Number
value
)
{
return
header
(
name
,
value
!=
null
?
value
.
toString
()
:
null
);
}
/**
* Set all headers found in given map where the keys are the header names and
* the values are the header values
*
* @param headers
* @return this request
*/
public
HttpRequest
headers
(
final
Map
<
String
,
String
>
headers
)
{
if
(!
headers
.
isEmpty
())
for
(
Entry
<
String
,
String
>
header
:
headers
.
entrySet
())
header
(
header
);
return
this
;
}
/**
* Set header to have given entry's key as the name and value as the value
*
* @param header
* @return this request
*/
public
HttpRequest
header
(
final
Entry
<
String
,
String
>
header
)
{
return
header
(
header
.
getKey
(),
header
.
getValue
());
}
/**
* Get a response header
*
* @param name
* @return response header
* @throws HttpRequestException
*/
public
String
header
(
final
String
name
)
throws
HttpRequestException
{
closeOutputQuietly
();
return
getConnection
().
getHeaderField
(
name
);
}
/**
* Get all the response headers
*
* @return map of response header names to their value(s)
* @throws HttpRequestException
*/
public
Map
<
String
,
List
<
String
>>
headers
()
throws
HttpRequestException
{
closeOutputQuietly
();
return
getConnection
().
getHeaderFields
();
}
/**
* Get a date header from the response falling back to returning -1 if the
* header is missing or parsing fails
*
* @param name
* @return date, -1 on failures
* @throws HttpRequestException
*/
public
long
dateHeader
(
final
String
name
)
throws
HttpRequestException
{
return
dateHeader
(
name
,
-
1L
);
}
/**
* Get a date header from the response falling back to returning the given
* default value if the header is missing or parsing fails
*
* @param name
* @param defaultValue
* @return date, default value on failures
* @throws HttpRequestException
*/
public
long
dateHeader
(
final
String
name
,
final
long
defaultValue
)
throws
HttpRequestException
{
closeOutputQuietly
();
return
getConnection
().
getHeaderFieldDate
(
name
,
defaultValue
);
}
/**
* Get an integer header from the response falling back to returning -1 if the
* header is missing or parsing fails
*
* @param name
* @return header value as an integer, -1 when missing or parsing fails
* @throws HttpRequestException
*/
public
int
intHeader
(
final
String
name
)
throws
HttpRequestException
{
return
intHeader
(
name
,
-
1
);
}
/**
* Get an integer header value from the response falling back to the given
* default value if the header is missing or if parsing fails
*
* @param name
* @param defaultValue
* @return header value as an integer, default value when missing or parsing
* fails
* @throws HttpRequestException
*/
public
int
intHeader
(
final
String
name
,
final
int
defaultValue
)
throws
HttpRequestException
{
closeOutputQuietly
();
return
getConnection
().
getHeaderFieldInt
(
name
,
defaultValue
);
}
/**
* Get all values of the given header from the response
*
* @param name
* @return non-null but possibly empty array of {@link String} header values
*/
public
String
[]
headers
(
final
String
name
)
{
final
Map
<
String
,
List
<
String
>>
headers
=
headers
();
if
(
headers
==
null
||
headers
.
isEmpty
())
return
EMPTY_STRINGS
;
final
List
<
String
>
values
=
headers
.
get
(
name
);
if
(
values
!=
null
&&
!
values
.
isEmpty
())
return
values
.
toArray
(
new
String
[
values
.
size
()]);
else
return
EMPTY_STRINGS
;
}
/**
* Get parameter with given name from header value in response
*
* @param headerName
* @param paramName
* @return parameter value or null if missing
*/
public
String
parameter
(
final
String
headerName
,
final
String
paramName
)
{
return
getParam
(
header
(
headerName
),
paramName
);
}
/**
* Get all parameters from header value in response
* <p>
* This will be all key=value pairs after the first ';' that are separated by a
* ';'
*
* @param headerName
* @return non-null but possibly empty map of parameter headers
*/
public
Map
<
String
,
String
>
parameters
(
final
String
headerName
)
{
return
getParams
(
header
(
headerName
));
}
/**
* Get parameter values from header value
*
* @param header
* @return parameter value or null if none
*/
protected
Map
<
String
,
String
>
getParams
(
final
String
header
)
{
if
(
header
==
null
||
header
.
length
()
==
0
)
return
Collections
.
emptyMap
();
final
int
headerLength
=
header
.
length
();
int
start
=
header
.
indexOf
(
';'
)
+
1
;
if
(
start
==
0
||
start
==
headerLength
)
return
Collections
.
emptyMap
();
int
end
=
header
.
indexOf
(
';'
,
start
);
if
(
end
==
-
1
)
end
=
headerLength
;
Map
<
String
,
String
>
params
=
new
LinkedHashMap
<
String
,
String
>();
while
(
start
<
end
)
{
int
nameEnd
=
header
.
indexOf
(
'='
,
start
);
if
(
nameEnd
!=
-
1
&&
nameEnd
<
end
)
{
String
name
=
header
.
substring
(
start
,
nameEnd
).
trim
();
if
(
name
.
length
()
>
0
)
{
String
value
=
header
.
substring
(
nameEnd
+
1
,
end
).
trim
();
int
length
=
value
.
length
();
if
(
length
!=
0
)
if
(
length
>
2
&&
'"'
==
value
.
charAt
(
0
)
&&
'"'
==
value
.
charAt
(
length
-
1
))
params
.
put
(
name
,
value
.
substring
(
1
,
length
-
1
));
else
params
.
put
(
name
,
value
);
}
}
start
=
end
+
1
;
end
=
header
.
indexOf
(
';'
,
start
);
if
(
end
==
-
1
)
end
=
headerLength
;
}
return
params
;
}
/**
* Get parameter value from header value
*
* @param value
* @param paramName
* @return parameter value or null if none
*/
protected
String
getParam
(
final
String
value
,
final
String
paramName
)
{
if
(
value
==
null
||
value
.
length
()
==
0
)
return
null
;
final
int
length
=
value
.
length
();
int
start
=
value
.
indexOf
(
';'
)
+
1
;
if
(
start
==
0
||
start
==
length
)
return
null
;
int
end
=
value
.
indexOf
(
';'
,
start
);
if
(
end
==
-
1
)
end
=
length
;
while
(
start
<
end
)
{
int
nameEnd
=
value
.
indexOf
(
'='
,
start
);
if
(
nameEnd
!=
-
1
&&
nameEnd
<
end
&&
paramName
.
equals
(
value
.
substring
(
start
,
nameEnd
).
trim
()))
{
String
paramValue
=
value
.
substring
(
nameEnd
+
1
,
end
).
trim
();
int
valueLength
=
paramValue
.
length
();
if
(
valueLength
!=
0
)
if
(
valueLength
>
2
&&
'"'
==
paramValue
.
charAt
(
0
)
&&
'"'
==
paramValue
.
charAt
(
valueLength
-
1
))
return
paramValue
.
substring
(
1
,
valueLength
-
1
);
else
return
paramValue
;
}
start
=
end
+
1
;
end
=
value
.
indexOf
(
';'
,
start
);
if
(
end
==
-
1
)
end
=
length
;
}
return
null
;
}
/**
* Get 'charset' parameter from 'Content-Type' response header
*
* @return charset or null if none
*/
public
String
charset
()
{
return
parameter
(
HEADER_CONTENT_TYPE
,
PARAM_CHARSET
);
}
/**
* Set the 'User-Agent' header to given value
*
* @param userAgent
* @return this request
*/
public
HttpRequest
userAgent
(
final
String
userAgent
)
{
return
header
(
HEADER_USER_AGENT
,
userAgent
);
}
/**
* Set the 'Referer' header to given value
*
* @param referer
* @return this request
*/
public
HttpRequest
referer
(
final
String
referer
)
{
return
header
(
HEADER_REFERER
,
referer
);
}
/**
* Set value of {@link HttpURLConnection#setUseCaches(boolean)}
*
* @param useCaches
* @return this request
*/
public
HttpRequest
useCaches
(
final
boolean
useCaches
)
{
getConnection
().
setUseCaches
(
useCaches
);
return
this
;
}
/**
* Set the 'Accept-Encoding' header to given value
*
* @param acceptEncoding
* @return this request
*/
public
HttpRequest
acceptEncoding
(
final
String
acceptEncoding
)
{
return
header
(
HEADER_ACCEPT_ENCODING
,
acceptEncoding
);
}
/**
* Set the 'Accept-Encoding' header to 'gzip'
*
* @see #uncompress(boolean)
* @return this request
*/
public
HttpRequest
acceptGzipEncoding
()
{
return
acceptEncoding
(
ENCODING_GZIP
);
}
/**
* Set the 'Accept-Charset' header to given value
*
* @param acceptCharset
* @return this request
*/
public
HttpRequest
acceptCharset
(
final
String
acceptCharset
)
{
return
header
(
HEADER_ACCEPT_CHARSET
,
acceptCharset
);
}
/**
* Get the 'Content-Encoding' header from the response
*
* @return this request
*/
public
String
contentEncoding
()
{
return
header
(
HEADER_CONTENT_ENCODING
);
}
/**
* Get the 'Server' header from the response
*
* @return server
*/
public
String
server
()
{
return
header
(
HEADER_SERVER
);
}
/**
* Get the 'Date' header from the response
*
* @return date value, -1 on failures
*/
public
long
date
()
{
return
dateHeader
(
HEADER_DATE
);
}
/**
* Get the 'Cache-Control' header from the response
*
* @return cache control
*/
public
String
cacheControl
()
{
return
header
(
HEADER_CACHE_CONTROL
);
}
/**
* Get the 'ETag' header from the response
*
* @return entity tag
*/
public
String
eTag
()
{
return
header
(
HEADER_ETAG
);
}
/**
* Get the 'Expires' header from the response
*
* @return expires value, -1 on failures
*/
public
long
expires
()
{
return
dateHeader
(
HEADER_EXPIRES
);
}
/**
* Get the 'Last-Modified' header from the response
*
* @return last modified value, -1 on failures
*/
public
long
lastModified
()
{
return
dateHeader
(
HEADER_LAST_MODIFIED
);
}
/**
* Get the 'Location' header from the response
*
* @return location
*/
public
String
location
()
{
return
header
(
HEADER_LOCATION
);
}
/**
* Set the 'Authorization' header to given value
*
* @param authorization
* @return this request
*/
public
HttpRequest
authorization
(
final
String
authorization
)
{
return
header
(
HEADER_AUTHORIZATION
,
authorization
);
}
/**
* Set the 'Proxy-Authorization' header to given value
*
* @param proxyAuthorization
* @return this request
*/
public
HttpRequest
proxyAuthorization
(
final
String
proxyAuthorization
)
{
return
header
(
HEADER_PROXY_AUTHORIZATION
,
proxyAuthorization
);
}
/**
* Set the 'If-Modified-Since' request header to the given value
*
* @param ifModifiedSince
* @return this request
*/
public
HttpRequest
ifModifiedSince
(
final
long
ifModifiedSince
)
{
getConnection
().
setIfModifiedSince
(
ifModifiedSince
);
return
this
;
}
/**
* Set the 'If-None-Match' request header to the given value
*
* @param ifNoneMatch
* @return this request
*/
public
HttpRequest
ifNoneMatch
(
final
String
ifNoneMatch
)
{
return
header
(
HEADER_IF_NONE_MATCH
,
ifNoneMatch
);
}
/**
* Set the 'Content-Type' request header to the given value
*
* @param contentType
* @return this request
*/
public
HttpRequest
contentType
(
final
String
contentType
)
{
return
contentType
(
contentType
,
null
);
}
/**
* Set the 'Content-Type' request header to the given value and charset
*
* @param contentType
* @param charset
* @return this request
*/
public
HttpRequest
contentType
(
final
String
contentType
,
final
String
charset
)
{
if
(
charset
!=
null
&&
charset
.
length
()
>
0
)
{
final
String
separator
=
"; "
+
PARAM_CHARSET
+
'='
;
return
header
(
HEADER_CONTENT_TYPE
,
contentType
+
separator
+
charset
);
}
else
return
header
(
HEADER_CONTENT_TYPE
,
contentType
);
}
/**
* Get the 'Content-Type' header from the response
*
* @return response header value
*/
public
String
contentType
()
{
return
header
(
HEADER_CONTENT_TYPE
);
}
/**
* Get the 'Content-Length' header from the response
*
* @return response header value
*/
public
int
contentLength
()
{
return
intHeader
(
HEADER_CONTENT_LENGTH
);
}
/**
* Set the 'Content-Length' request header to the given value
*
* @param contentLength
* @return this request
*/
public
HttpRequest
contentLength
(
final
String
contentLength
)
{
return
contentLength
(
Integer
.
parseInt
(
contentLength
));
}
/**
* Set the 'Content-Length' request header to the given value
*
* @param contentLength
* @return this request
*/
public
HttpRequest
contentLength
(
final
int
contentLength
)
{
getConnection
().
setFixedLengthStreamingMode
(
contentLength
);
return
this
;
}
/**
* Set the 'Accept' header to given value
*
* @param accept
* @return this request
*/
public
HttpRequest
accept
(
final
String
accept
)
{
return
header
(
HEADER_ACCEPT
,
accept
);
}
/**
* Set the 'Accept' header to 'application/json'
*
* @return this request
*/
public
HttpRequest
acceptJson
()
{
return
accept
(
CONTENT_TYPE_JSON
);
}
/**
* Copy from input stream to output stream
*
* @param input
* @param output
* @return this request
* @throws IOException
*/
protected
HttpRequest
copy
(
final
InputStream
input
,
final
OutputStream
output
)
throws
IOException
{
return
new
CloseOperation
<
HttpRequest
>(
input
,
ignoreCloseExceptions
)
{
@Override
public
HttpRequest
run
()
throws
IOException
{
final
byte
[]
buffer
=
new
byte
[
bufferSize
];
int
read
;
while
((
read
=
input
.
read
(
buffer
))
!=
-
1
)
{
output
.
write
(
buffer
,
0
,
read
);
totalWritten
+=
read
;
progress
.
onUpload
(
totalWritten
,
totalSize
);
}
return
HttpRequest
.
this
;
}
}.
call
();
}
/**
* Copy from reader to writer
*
* @param input
* @param output
* @return this request
* @throws IOException
*/
protected
HttpRequest
copy
(
final
Reader
input
,
final
Writer
output
)
throws
IOException
{
return
new
CloseOperation
<
HttpRequest
>(
input
,
ignoreCloseExceptions
)
{
@Override
public
HttpRequest
run
()
throws
IOException
{
final
char
[]
buffer
=
new
char
[
bufferSize
];
int
read
;
while
((
read
=
input
.
read
(
buffer
))
!=
-
1
)
{
output
.
write
(
buffer
,
0
,
read
);
totalWritten
+=
read
;
progress
.
onUpload
(
totalWritten
,
-
1
);
}
return
HttpRequest
.
this
;
}
}.
call
();
}
/**
* Set the UploadProgress callback for this request
*
* @param callback
* @return this request
*/
public
HttpRequest
progress
(
final
UploadProgress
callback
)
{
if
(
callback
==
null
)
progress
=
UploadProgress
.
DEFAULT
;
else
progress
=
callback
;
return
this
;
}
private
HttpRequest
incrementTotalSize
(
final
long
size
)
{
if
(
totalSize
==
-
1
)
totalSize
=
0
;
totalSize
+=
size
;
return
this
;
}
/**
* Close output stream
*
* @return this request
* @throws HttpRequestException
* @throws IOException
*/
protected
HttpRequest
closeOutput
()
throws
IOException
{
progress
(
null
);
if
(
output
==
null
)
return
this
;
if
(
multipart
)
output
.
write
(
CRLF
+
"--"
+
BOUNDARY
+
"--"
+
CRLF
);
if
(
ignoreCloseExceptions
)
try
{
output
.
close
();
}
catch
(
IOException
ignored
)
{
// Ignored
}
else
output
.
close
();
output
=
null
;
return
this
;
}
/**
* Call {@link #closeOutput()} and re-throw a caught {@link IOException}s as an
* {@link HttpRequestException}
*
* @return this request
* @throws HttpRequestException
*/
protected
HttpRequest
closeOutputQuietly
()
throws
HttpRequestException
{
try
{
return
closeOutput
();
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
}
/**
* Open output stream
*
* @return this request
* @throws IOException
*/
protected
HttpRequest
openOutput
()
throws
IOException
{
if
(
output
!=
null
)
return
this
;
getConnection
().
setDoOutput
(
true
);
final
String
charset
=
getParam
(
getConnection
().
getRequestProperty
(
HEADER_CONTENT_TYPE
),
PARAM_CHARSET
);
output
=
new
RequestOutputStream
(
getConnection
().
getOutputStream
(),
charset
,
bufferSize
);
return
this
;
}
/**
* Start part of a multipart
*
* @return this request
* @throws IOException
*/
protected
HttpRequest
startPart
()
throws
IOException
{
if
(!
multipart
)
{
multipart
=
true
;
contentType
(
CONTENT_TYPE_MULTIPART
).
openOutput
();
output
.
write
(
"--"
+
BOUNDARY
+
CRLF
);
}
else
output
.
write
(
CRLF
+
"--"
+
BOUNDARY
+
CRLF
);
return
this
;
}
/**
* Write part header
*
* @param name
* @param filename
* @return this request
* @throws IOException
*/
protected
HttpRequest
writePartHeader
(
final
String
name
,
final
String
filename
)
throws
IOException
{
return
writePartHeader
(
name
,
filename
,
null
);
}
/**
* Write part header
*
* @param name
* @param filename
* @param contentType
* @return this request
* @throws IOException
*/
protected
HttpRequest
writePartHeader
(
final
String
name
,
final
String
filename
,
final
String
contentType
)
throws
IOException
{
final
StringBuilder
partBuffer
=
new
StringBuilder
();
partBuffer
.
append
(
"form-data; name=\""
).
append
(
name
);
if
(
filename
!=
null
)
partBuffer
.
append
(
"\"; filename=\""
).
append
(
filename
);
partBuffer
.
append
(
'"'
);
partHeader
(
"Content-Disposition"
,
partBuffer
.
toString
());
if
(
contentType
!=
null
)
partHeader
(
HEADER_CONTENT_TYPE
,
contentType
);
return
send
(
CRLF
);
}
/**
* Write part of a multipart request to the request body
*
* @param name
* @param part
* @return this request
*/
public
HttpRequest
part
(
final
String
name
,
final
String
part
)
{
return
part
(
name
,
null
,
part
);
}
/**
* Write part of a multipart request to the request body
*
* @param name
* @param filename
* @param part
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
part
(
final
String
name
,
final
String
filename
,
final
String
part
)
throws
HttpRequestException
{
return
part
(
name
,
filename
,
null
,
part
);
}
/**
* Write part of a multipart request to the request body
*
* @param name
* @param filename
* @param contentType value of the Content-Type part header
* @param part
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
part
(
final
String
name
,
final
String
filename
,
final
String
contentType
,
final
String
part
)
throws
HttpRequestException
{
try
{
startPart
();
writePartHeader
(
name
,
filename
,
contentType
);
output
.
write
(
part
);
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
return
this
;
}
/**
* Write part of a multipart request to the request body
*
* @param name
* @param part
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
part
(
final
String
name
,
final
Number
part
)
throws
HttpRequestException
{
return
part
(
name
,
null
,
part
);
}
/**
* Write part of a multipart request to the request body
*
* @param name
* @param filename
* @param part
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
part
(
final
String
name
,
final
String
filename
,
final
Number
part
)
throws
HttpRequestException
{
return
part
(
name
,
filename
,
part
!=
null
?
part
.
toString
()
:
null
);
}
/**
* Write part of a multipart request to the request body
*
* @param name
* @param part
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
part
(
final
String
name
,
final
File
part
)
throws
HttpRequestException
{
return
part
(
name
,
null
,
part
);
}
/**
* Write part of a multipart request to the request body
*
* @param name
* @param filename
* @param part
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
part
(
final
String
name
,
final
String
filename
,
final
File
part
)
throws
HttpRequestException
{
return
part
(
name
,
filename
,
null
,
part
);
}
/**
* Write part of a multipart request to the request body
*
* @param name
* @param filename
* @param contentType value of the Content-Type part header
* @param part
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
part
(
final
String
name
,
final
String
filename
,
final
String
contentType
,
final
File
part
)
throws
HttpRequestException
{
final
InputStream
stream
;
try
{
stream
=
new
BufferedInputStream
(
new
FileInputStream
(
part
));
incrementTotalSize
(
part
.
length
());
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
return
part
(
name
,
filename
,
contentType
,
stream
);
}
/**
* Write part of a multipart request to the request body
*
* @param name
* @param part
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
part
(
final
String
name
,
final
InputStream
part
)
throws
HttpRequestException
{
return
part
(
name
,
null
,
null
,
part
);
}
/**
* Write part of a multipart request to the request body
*
* @param name
* @param filename
* @param contentType value of the Content-Type part header
* @param part
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
part
(
final
String
name
,
final
String
filename
,
final
String
contentType
,
final
InputStream
part
)
throws
HttpRequestException
{
try
{
startPart
();
writePartHeader
(
name
,
filename
,
contentType
);
copy
(
part
,
output
);
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
return
this
;
}
/**
* Write a multipart header to the response body
*
* @param name
* @param value
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
partHeader
(
final
String
name
,
final
String
value
)
throws
HttpRequestException
{
return
send
(
name
).
send
(
": "
).
send
(
value
).
send
(
CRLF
);
}
/**
* Write contents of file to request body
*
* @param input
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
send
(
final
File
input
)
throws
HttpRequestException
{
final
InputStream
stream
;
try
{
stream
=
new
BufferedInputStream
(
new
FileInputStream
(
input
));
incrementTotalSize
(
input
.
length
());
}
catch
(
FileNotFoundException
e
)
{
throw
new
HttpRequestException
(
e
);
}
return
send
(
stream
);
}
/**
* Write byte array to request body
*
* @param input
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
send
(
final
byte
[]
input
)
throws
HttpRequestException
{
if
(
input
!=
null
)
incrementTotalSize
(
input
.
length
);
return
send
(
new
ByteArrayInputStream
(
input
));
}
/**
* Write stream to request body
* <p>
* The given stream will be closed once sending completes
*
* @param input
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
send
(
final
InputStream
input
)
throws
HttpRequestException
{
try
{
openOutput
();
copy
(
input
,
output
);
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
return
this
;
}
/**
* Write reader to request body
* <p>
* The given reader will be closed once sending completes
*
* @param input
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
send
(
final
Reader
input
)
throws
HttpRequestException
{
try
{
openOutput
();
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
final
Writer
writer
=
new
OutputStreamWriter
(
output
,
output
.
encoder
.
charset
());
return
new
FlushOperation
<
HttpRequest
>(
writer
)
{
@Override
protected
HttpRequest
run
()
throws
IOException
{
return
copy
(
input
,
writer
);
}
}.
call
();
}
/**
* Write char sequence to request body
* <p>
* The charset configured via {@link #contentType(String)} will be used and
* UTF-8 will be used if it is unset.
*
* @param value
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
send
(
final
CharSequence
value
)
throws
HttpRequestException
{
try
{
openOutput
();
output
.
write
(
value
.
toString
());
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
return
this
;
}
/**
* Create writer to request output stream
*
* @return writer
* @throws HttpRequestException
*/
public
OutputStreamWriter
writer
()
throws
HttpRequestException
{
try
{
openOutput
();
return
new
OutputStreamWriter
(
output
,
output
.
encoder
.
charset
());
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
}
/**
* Write the values in the map as form data to the request body
* <p>
* The pairs specified will be URL-encoded in UTF-8 and sent with the
* 'application/x-www-form-urlencoded' content-type
*
* @param values
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
form
(
final
Map
<?,
?>
values
)
throws
HttpRequestException
{
return
form
(
values
,
CHARSET_UTF8
);
}
/**
* Write the key and value in the entry as form data to the request body
* <p>
* The pair specified will be URL-encoded in UTF-8 and sent with the
* 'application/x-www-form-urlencoded' content-type
*
* @param entry
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
form
(
final
Entry
<?,
?>
entry
)
throws
HttpRequestException
{
return
form
(
entry
,
CHARSET_UTF8
);
}
/**
* Write the key and value in the entry as form data to the request body
* <p>
* The pair specified will be URL-encoded and sent with the
* 'application/x-www-form-urlencoded' content-type
*
* @param entry
* @param charset
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
form
(
final
Entry
<?,
?>
entry
,
final
String
charset
)
throws
HttpRequestException
{
return
form
(
entry
.
getKey
(),
entry
.
getValue
(),
charset
);
}
/**
* Write the name/value pair as form data to the request body
* <p>
* The pair specified will be URL-encoded in UTF-8 and sent with the
* 'application/x-www-form-urlencoded' content-type
*
* @param name
* @param value
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
form
(
final
Object
name
,
final
Object
value
)
throws
HttpRequestException
{
return
form
(
name
,
value
,
CHARSET_UTF8
);
}
/**
* Write the name/value pair as form data to the request body
* <p>
* The values specified will be URL-encoded and sent with the
* 'application/x-www-form-urlencoded' content-type
*
* @param name
* @param value
* @param charset
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
form
(
final
Object
name
,
final
Object
value
,
String
charset
)
throws
HttpRequestException
{
final
boolean
first
=
!
form
;
if
(
first
)
{
contentType
(
CONTENT_TYPE_FORM
,
charset
);
form
=
true
;
}
charset
=
getValidCharset
(
charset
);
try
{
openOutput
();
if
(!
first
)
output
.
write
(
'&'
);
output
.
write
(
URLEncoder
.
encode
(
name
.
toString
(),
charset
));
output
.
write
(
'='
);
if
(
value
!=
null
)
output
.
write
(
URLEncoder
.
encode
(
value
.
toString
(),
charset
));
}
catch
(
IOException
e
)
{
throw
new
HttpRequestException
(
e
);
}
return
this
;
}
/**
* Write the values in the map as encoded form data to the request body
*
* @param values
* @param charset
* @return this request
* @throws HttpRequestException
*/
public
HttpRequest
form
(
final
Map
<?,
?>
values
,
final
String
charset
)
throws
HttpRequestException
{
if
(!
values
.
isEmpty
())
for
(
Entry
<?,
?>
entry
:
values
.
entrySet
())
form
(
entry
,
charset
);
return
this
;
}
public
HttpRequest
setSSLSocketFactory
(
SSLSocketFactory
socketFactory
)
throws
HttpRequestException
{
final
HttpURLConnection
connection
=
getConnection
();
if
(
connection
instanceof
HttpsURLConnection
)
((
HttpsURLConnection
)
connection
).
setSSLSocketFactory
(
socketFactory
);
return
this
;
}
public
HttpRequest
setHostnameVerifier
(
HostnameVerifier
verifier
)
{
final
HttpURLConnection
connection
=
getConnection
();
if
(
connection
instanceof
HttpsURLConnection
)
((
HttpsURLConnection
)
connection
).
setHostnameVerifier
(
verifier
);
return
this
;
}
/**
* Get the {@link URL} of this request's connection
*
* @return request URL
*/
public
URL
url
()
{
return
getConnection
().
getURL
();
}
/**
* Get the HTTP method of this request
*
* @return method
*/
public
String
method
()
{
return
getConnection
().
getRequestMethod
();
}
public
HttpRequest
useProxy
(
final
String
proxyHost
,
final
int
proxyPort
)
{
if
(
connection
!=
null
)
throw
new
IllegalStateException
(
"The connection has already been created. This method must be called before reading or writing to the request."
);
this
.
httpProxyHost
=
proxyHost
;
this
.
httpProxyPort
=
proxyPort
;
return
this
;
}
/**
* Set whether or not the underlying connection should follow redirects in the
* response.
*
* @param followRedirects - true fo follow redirects, false to not.
* @return this request
*/
public
HttpRequest
followRedirects
(
final
boolean
followRedirects
)
{
getConnection
().
setInstanceFollowRedirects
(
followRedirects
);
return
this
;
}
}
src/android/com/silkimen/http/JsonUtils.java
0 → 100644
View file @
09c88efe
package
com.silkimen.http
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.Iterator
;
import
org.json.JSONArray
;
import
org.json.JSONException
;
import
org.json.JSONObject
;
public
class
JsonUtils
{
public
static
HashMap
<
String
,
String
>
getStringMap
(
JSONObject
object
)
throws
JSONException
{
HashMap
<
String
,
String
>
map
=
new
HashMap
<
String
,
String
>();
if
(
object
==
null
)
{
return
map
;
}
Iterator
<?>
i
=
object
.
keys
();
while
(
i
.
hasNext
())
{
String
key
=
(
String
)
i
.
next
();
map
.
put
(
key
,
object
.
getString
(
key
));
}
return
map
;
}
public
static
HashMap
<
String
,
Object
>
getObjectMap
(
JSONObject
object
)
throws
JSONException
{
HashMap
<
String
,
Object
>
map
=
new
HashMap
<
String
,
Object
>();
if
(
object
==
null
)
{
return
map
;
}
Iterator
<?>
i
=
object
.
keys
();
while
(
i
.
hasNext
())
{
String
key
=
(
String
)
i
.
next
();
Object
value
=
object
.
get
(
key
);
if
(
value
instanceof
JSONArray
)
{
map
.
put
(
key
,
getObjectList
((
JSONArray
)
value
));
}
else
{
map
.
put
(
key
,
object
.
get
(
key
));
}
}
return
map
;
}
public
static
ArrayList
<
Object
>
getObjectList
(
JSONArray
array
)
throws
JSONException
{
ArrayList
<
Object
>
list
=
new
ArrayList
<
Object
>();
for
(
int
i
=
0
;
i
<
array
.
length
();
i
++)
{
list
.
add
(
array
.
get
(
i
));
}
return
list
;
}
}
src/android/com/silkimen/http/KeyChainKeyManager.java
0 → 100644
View file @
09c88efe
package
com.silkimen.http
;
import
android.content.Context
;
import
android.security.KeyChain
;
import
java.net.Socket
;
import
java.security.Principal
;
import
java.security.PrivateKey
;
import
java.security.cert.CertificateException
;
import
java.security.cert.X509Certificate
;
import
javax.net.ssl.X509ExtendedKeyManager
;
public
class
KeyChainKeyManager
extends
X509ExtendedKeyManager
{
private
final
String
alias
;
private
final
X509Certificate
[]
chain
;
private
final
PrivateKey
key
;
public
KeyChainKeyManager
(
String
alias
,
PrivateKey
key
,
X509Certificate
[]
chain
)
{
this
.
alias
=
alias
;
this
.
key
=
key
;
this
.
chain
=
chain
;
}
@Override
public
String
chooseClientAlias
(
String
[]
keyTypes
,
Principal
[]
issuers
,
Socket
socket
)
{
return
this
.
alias
;
}
@Override
public
X509Certificate
[]
getCertificateChain
(
String
alias
)
{
return
chain
;
}
@Override
public
PrivateKey
getPrivateKey
(
String
alias
)
{
return
key
;
}
@Override
public
final
String
chooseServerAlias
(
String
keyType
,
Principal
[]
issuers
,
Socket
socket
)
{
// not a client SSLSocket callback
throw
new
UnsupportedOperationException
();
}
@Override
public
final
String
[]
getClientAliases
(
String
keyType
,
Principal
[]
issuers
)
{
// not a client SSLSocket callback
throw
new
UnsupportedOperationException
();
}
@Override
public
final
String
[]
getServerAliases
(
String
keyType
,
Principal
[]
issuers
)
{
// not a client SSLSocket callback
throw
new
UnsupportedOperationException
();
}
}
src/android/com/silkimen/http/TLSConfiguration.java
0 → 100644
View file @
09c88efe
package
com.silkimen.http
;
import
java.io.IOException
;
import
java.security.GeneralSecurityException
;
import
java.security.SecureRandom
;
import
javax.net.ssl.HostnameVerifier
;
import
javax.net.ssl.KeyManager
;
import
javax.net.ssl.SSLContext
;
import
javax.net.ssl.SSLSocketFactory
;
import
javax.net.ssl.TrustManager
;
import
com.silkimen.http.TLSSocketFactory
;
public
class
TLSConfiguration
{
private
TrustManager
[]
trustManagers
=
null
;
private
KeyManager
[]
keyManagers
=
null
;
private
HostnameVerifier
hostnameVerifier
=
null
;
private
String
[]
blacklistedProtocols
=
{};
private
SSLSocketFactory
socketFactory
;
public
void
setHostnameVerifier
(
HostnameVerifier
hostnameVerifier
)
{
this
.
hostnameVerifier
=
hostnameVerifier
;
}
public
void
setKeyManagers
(
KeyManager
[]
keyManagers
)
{
this
.
keyManagers
=
keyManagers
;
this
.
socketFactory
=
null
;
}
public
void
setTrustManagers
(
TrustManager
[]
trustManagers
)
{
this
.
trustManagers
=
trustManagers
;
this
.
socketFactory
=
null
;
}
public
void
setBlacklistedProtocols
(
String
[]
protocols
)
{
this
.
blacklistedProtocols
=
protocols
;
this
.
socketFactory
=
null
;
}
public
HostnameVerifier
getHostnameVerifier
()
{
return
this
.
hostnameVerifier
;
}
public
SSLSocketFactory
getTLSSocketFactory
()
throws
IOException
{
if
(
this
.
socketFactory
!=
null
)
{
return
this
.
socketFactory
;
}
try
{
SSLContext
context
=
SSLContext
.
getInstance
(
"TLSv1.2"
);
context
.
init
(
this
.
keyManagers
,
this
.
trustManagers
,
new
SecureRandom
());
this
.
socketFactory
=
new
TLSSocketFactory
(
context
,
this
.
blacklistedProtocols
);
return
this
.
socketFactory
;
}
catch
(
GeneralSecurityException
e
)
{
IOException
ioException
=
new
IOException
(
"Security exception occured while configuring TLS context"
);
ioException
.
initCause
(
e
);
throw
ioException
;
}
}
}
src/android/com/silkimen/http/TLSSocketFactory.java
0 → 100644
View file @
09c88efe
package
com.silkimen.http
;
import
java.io.IOException
;
import
java.net.InetAddress
;
import
java.net.Socket
;
import
java.net.UnknownHostException
;
import
java.util.ArrayList
;
import
java.util.List
;
import
javax.net.ssl.SSLContext
;
import
javax.net.ssl.SSLSocket
;
import
javax.net.ssl.SSLSocketFactory
;
public
class
TLSSocketFactory
extends
SSLSocketFactory
{
private
SSLSocketFactory
delegate
;
private
List
<
String
>
blacklistedProtocols
;
public
TLSSocketFactory
(
SSLContext
context
,
String
[]
blacklistedProtocols
)
{
this
.
delegate
=
context
.
getSocketFactory
();
this
.
blacklistedProtocols
=
new
ArrayList
();
for
(
int
i
=
0
;
i
<
blacklistedProtocols
.
length
;
++
i
)
{
this
.
blacklistedProtocols
.
add
(
blacklistedProtocols
[
i
].
trim
());
}
}
@Override
public
String
[]
getDefaultCipherSuites
()
{
return
delegate
.
getDefaultCipherSuites
();
}
@Override
public
String
[]
getSupportedCipherSuites
()
{
return
delegate
.
getSupportedCipherSuites
();
}
@Override
public
Socket
createSocket
(
Socket
socket
,
String
host
,
int
port
,
boolean
autoClose
)
throws
IOException
{
return
enableTLSOnSocket
(
delegate
.
createSocket
(
socket
,
host
,
port
,
autoClose
));
}
@Override
public
Socket
createSocket
(
String
host
,
int
port
)
throws
IOException
,
UnknownHostException
{
return
enableTLSOnSocket
(
delegate
.
createSocket
(
host
,
port
));
}
@Override
public
Socket
createSocket
(
String
host
,
int
port
,
InetAddress
localHost
,
int
localPort
)
throws
IOException
,
UnknownHostException
{
return
enableTLSOnSocket
(
delegate
.
createSocket
(
host
,
port
,
localHost
,
localPort
));
}
@Override
public
Socket
createSocket
(
InetAddress
host
,
int
port
)
throws
IOException
{
return
enableTLSOnSocket
(
delegate
.
createSocket
(
host
,
port
));
}
@Override
public
Socket
createSocket
(
InetAddress
address
,
int
port
,
InetAddress
localAddress
,
int
localPort
)
throws
IOException
{
return
enableTLSOnSocket
(
delegate
.
createSocket
(
address
,
port
,
localAddress
,
localPort
));
}
private
Socket
enableTLSOnSocket
(
Socket
socket
)
{
if
(
socket
==
null
||
!(
socket
instanceof
SSLSocket
))
{
return
socket
;
}
String
[]
supported
=
((
SSLSocket
)
socket
).
getSupportedProtocols
();
List
<
String
>
filtered
=
new
ArrayList
();
for
(
int
i
=
0
;
i
<
supported
.
length
;
++
i
)
{
if
(!
this
.
blacklistedProtocols
.
contains
(
supported
[
i
]))
{
filtered
.
add
(
supported
[
i
]);
}
}
((
SSLSocket
)
socket
).
setEnabledProtocols
(
filtered
.
toArray
(
new
String
[
0
]));
return
socket
;
}
}
src/browser/cordova-http-plugin.js
0 → 100644
View file @
09c88efe
var
pluginId
=
module
.
id
.
slice
(
0
,
module
.
id
.
lastIndexOf
(
'
.
'
));
var
cordovaProxy
=
require
(
'
cordova/exec/proxy
'
);
var
jsUtil
=
require
(
pluginId
+
'
.js-util
'
);
var
reqMap
=
{};
function
serializeJsonData
(
data
)
{
try
{
return
JSON
.
stringify
(
data
);
}
catch
(
err
)
{
return
null
;
}
}
function
serializePrimitive
(
key
,
value
)
{
if
(
value
===
null
||
value
===
undefined
)
{
return
encodeURIComponent
(
key
)
+
'
=
'
;
}
return
encodeURIComponent
(
key
)
+
'
=
'
+
encodeURIComponent
(
value
);
}
function
serializeArray
(
key
,
values
)
{
return
values
.
map
(
function
(
value
)
{
return
encodeURIComponent
(
key
)
+
'
[]=
'
+
encodeURIComponent
(
value
);
}).
join
(
'
&
'
);
}
function
serializeParams
(
params
)
{
if
(
params
===
null
)
return
''
;
return
Object
.
keys
(
params
).
map
(
function
(
key
)
{
if
(
jsUtil
.
getTypeOf
(
params
[
key
])
===
'
Array
'
)
{
return
serializeArray
(
key
,
params
[
key
]);
}
return
serializePrimitive
(
key
,
params
[
key
]);
}).
join
(
'
&
'
);
}
function
decodeB64
(
dataString
)
{
var
binaryString
=
atob
(
dataString
);
var
bytes
=
new
Uint8Array
(
binaryString
.
length
);
for
(
var
i
=
0
;
i
<
binaryString
.
length
;
++
i
)
{
bytes
[
i
]
=
binaryString
.
charCodeAt
(
i
);
}
return
bytes
.
buffer
;
}
function
processMultipartData
(
data
)
{
if
(
!
data
)
return
null
;
var
fd
=
new
FormData
();
for
(
var
i
=
0
;
i
<
data
.
buffers
.
length
;
++
i
)
{
var
buffer
=
data
.
buffers
[
i
];
var
name
=
data
.
names
[
i
];
var
fileName
=
data
.
fileNames
[
i
];
var
type
=
data
.
types
[
i
];
if
(
fileName
)
{
fd
.
append
(
name
,
new
Blob
([
decodeB64
(
buffer
)],
{
type
:
type
}),
fileName
);
}
else
{
// we assume it's plain text if no filename was given
fd
.
append
(
name
,
atob
(
buffer
));
}
}
return
fd
;
}
function
deserializeResponseHeaders
(
headers
)
{
var
headerMap
=
{};
var
arr
=
headers
.
trim
().
split
(
/
[\r\n]
+/
);
arr
.
forEach
(
function
(
line
)
{
var
parts
=
line
.
split
(
'
:
'
);
var
header
=
parts
.
shift
().
toLowerCase
();
var
value
=
parts
.
join
(
'
:
'
);
headerMap
[
header
]
=
value
;
});
return
headerMap
;
}
function
getResponseData
(
xhr
)
{
if
(
xhr
.
responseType
!==
'
text
'
||
jsUtil
.
getTypeOf
(
xhr
.
responseText
)
!==
'
String
'
)
{
return
xhr
.
response
;
}
return
xhr
.
responseText
;
}
function
createXhrSuccessObject
(
xhr
)
{
return
{
url
:
xhr
.
responseURL
,
status
:
xhr
.
status
,
data
:
getResponseData
(
xhr
),
headers
:
deserializeResponseHeaders
(
xhr
.
getAllResponseHeaders
())
};
}
function
createXhrFailureObject
(
xhr
)
{
var
obj
=
{};
obj
.
headers
=
xhr
.
getAllResponseHeaders
();
obj
.
error
=
getResponseData
(
xhr
);
obj
.
error
=
obj
.
error
||
'
advanced-http: please check browser console for error messages
'
;
if
(
xhr
.
responseURL
)
obj
.
url
=
xhr
.
responseURL
;
if
(
xhr
.
status
)
obj
.
status
=
xhr
.
status
;
return
obj
;
}
function
injectRequestIdHandler
(
reqId
,
cb
)
{
return
function
(
response
)
{
delete
reqMap
[
reqId
];
cb
(
response
);
}
}
function
getHeaderValue
(
headers
,
headerName
)
{
let
result
=
null
;
Object
.
keys
(
headers
).
forEach
(
function
(
key
)
{
if
(
key
.
toLowerCase
()
===
headerName
.
toLowerCase
())
{
result
=
headers
[
key
];
}
});
return
result
;
}
function
setDefaultContentType
(
headers
,
contentType
)
{
if
(
getHeaderValue
(
headers
,
'
Content-Type
'
)
===
null
)
{
headers
[
'
Content-Type
'
]
=
contentType
;
}
}
function
setHeaders
(
xhr
,
headers
)
{
Object
.
keys
(
headers
).
forEach
(
function
(
key
)
{
if
(
key
.
toLowerCase
()
===
'
cookie
'
)
return
;
xhr
.
setRequestHeader
(
key
,
headers
[
key
]);
});
}
function
sendRequest
(
method
,
withData
,
opts
,
success
,
failure
)
{
var
data
,
serializer
,
headers
,
readTimeout
,
followRedirect
,
responseType
,
reqId
;
var
url
=
opts
[
0
];
if
(
withData
)
{
data
=
opts
[
1
];
serializer
=
opts
[
2
];
headers
=
opts
[
3
];
// connect timeout not applied
// connectTimeout = opts[4];
readTimeout
=
opts
[
5
];
followRedirect
=
opts
[
6
];
responseType
=
opts
[
7
];
reqId
=
opts
[
8
];
}
else
{
headers
=
opts
[
1
];
// connect timeout not applied
// connectTimeout = opts[2];
readTimeout
=
opts
[
3
];
followRedirect
=
opts
[
4
];
responseType
=
opts
[
5
];
reqId
=
opts
[
6
];
}
var
onSuccess
=
injectRequestIdHandler
(
reqId
,
success
);
var
onFail
=
injectRequestIdHandler
(
reqId
,
failure
);
var
processedData
=
null
;
var
xhr
=
new
XMLHttpRequest
();
reqMap
[
reqId
]
=
xhr
;
xhr
.
open
(
method
,
url
);
if
(
headers
.
Cookie
&&
headers
.
Cookie
.
length
>
0
)
{
return
onFail
(
'
advanced-http: custom cookies not supported on browser platform
'
);
}
if
(
!
followRedirect
)
{
return
onFail
(
'
advanced-http: disabling follow redirect not supported on browser platform
'
);
}
switch
(
serializer
)
{
case
'
json
'
:
setDefaultContentType
(
headers
,
'
application/json; charset=utf8
'
);
processedData
=
serializeJsonData
(
data
);
if
(
processedData
===
null
)
{
return
onFail
(
'
advanced-http: failed serializing data
'
);
}
break
;
case
'
utf8
'
:
setDefaultContentType
(
headers
,
'
text/plain; charset=utf8
'
);
processedData
=
data
.
text
;
break
;
case
'
urlencoded
'
:
setDefaultContentType
(
headers
,
'
application/x-www-form-urlencoded
'
);
processedData
=
serializeParams
(
data
);
break
;
case
'
multipart
'
:
const
contentType
=
getHeaderValue
(
headers
,
'
Content-Type
'
);
// intentionally don't set a default content type
// it's set by the browser together with the content disposition string
if
(
contentType
)
{
headers
[
'
Content-Type
'
]
=
contentType
;
}
processedData
=
processMultipartData
(
data
);
break
;
case
'
raw
'
:
setDefaultContentType
(
headers
,
'
application/octet-stream
'
);
processedData
=
data
;
break
;
}
// requesting text instead of JSON because it's parsed in the response handler
xhr
.
responseType
=
responseType
===
'
json
'
?
'
text
'
:
responseType
;
// we can't set connect timeout and read timeout separately on browser platform
xhr
.
timeout
=
readTimeout
*
1000
;
try
{
setHeaders
(
xhr
,
headers
);
}
catch
(
error
)
{
return
onFail
({
status
:
-
1
,
error
:
error
,
url
:
url
,
headers
:
headers
});
}
xhr
.
onerror
=
function
()
{
return
onFail
(
createXhrFailureObject
(
xhr
));
};
xhr
.
onabort
=
function
()
{
return
onFail
({
status
:
-
8
,
error
:
'
Request was aborted
'
,
url
:
url
,
headers
:
{}
});
};
xhr
.
ontimeout
=
function
()
{
return
onFail
({
status
:
-
4
,
error
:
'
Request timed out
'
,
url
:
url
,
headers
:
{}
});
};
xhr
.
onload
=
function
()
{
if
(
xhr
.
readyState
!==
xhr
.
DONE
)
return
;
if
(
xhr
.
status
<
200
||
xhr
.
status
>
299
)
{
return
onFail
(
createXhrFailureObject
(
xhr
));
}
return
onSuccess
(
createXhrSuccessObject
(
xhr
));
};
xhr
.
send
(
processedData
);
}
function
abort
(
opts
,
success
,
failure
)
{
var
reqId
=
opts
[
0
];
var
result
=
false
;
var
xhr
=
reqMap
[
reqId
];
if
(
xhr
&&
xhr
.
readyState
!==
xhr
.
DONE
){
xhr
.
abort
();
result
=
true
;
}
success
({
aborted
:
result
});
}
var
browserInterface
=
{
get
:
function
(
success
,
failure
,
opts
)
{
return
sendRequest
(
'
get
'
,
false
,
opts
,
success
,
failure
);
},
head
:
function
(
success
,
failure
,
opts
)
{
return
sendRequest
(
'
head
'
,
false
,
opts
,
success
,
failure
);
},
delete
:
function
(
success
,
failure
,
opts
)
{
return
sendRequest
(
'
delete
'
,
false
,
opts
,
success
,
failure
);
},
post
:
function
(
success
,
failure
,
opts
)
{
return
sendRequest
(
'
post
'
,
true
,
opts
,
success
,
failure
);
},
put
:
function
(
success
,
failure
,
opts
)
{
return
sendRequest
(
'
put
'
,
true
,
opts
,
success
,
failure
);
},
patch
:
function
(
success
,
failure
,
opts
)
{
return
sendRequest
(
'
patch
'
,
true
,
opts
,
success
,
failure
);
},
abort
:
function
(
success
,
failure
,
opts
)
{
return
abort
(
opts
,
success
,
failure
);
},
uploadFile
:
function
(
success
,
failure
,
opts
)
{
return
failure
(
'
advanced-http: function "uploadFile" not supported on browser platform
'
);
},
uploadFiles
:
function
(
success
,
failure
,
opts
)
{
return
failure
(
'
advanced-http: function "uploadFiles" not supported on browser platform
'
);
},
downloadFile
:
function
(
success
,
failure
,
opts
)
{
return
failure
(
'
advanced-http: function "downloadFile" not supported on browser platform
'
);
},
setServerTrustMode
:
function
(
success
,
failure
,
opts
)
{
return
failure
(
'
advanced-http: function "setServerTrustMode" not supported on browser platform
'
);
},
setClientAuthMode
:
function
(
success
,
failure
,
opts
)
{
return
failure
(
'
advanced-http: function "setClientAuthMode" not supported on browser platform
'
);
}
};
module
.
exports
=
browserInterface
;
cordovaProxy
.
add
(
'
CordovaHttpPlugin
'
,
browserInterface
);
src/ios/BinaryRequestSerializer.h
0 → 100644
View file @
09c88efe
#import <Foundation/Foundation.h>
#import "SM_AFURLRequestSerialization.h"
@interface
BinaryRequestSerializer
:
SM_AFHTTPRequestSerializer
+
(
instancetype
)
serializer
;
@end
src/ios/BinaryRequestSerializer.m
0 → 100644
View file @
09c88efe
#import "BinaryRequestSerializer.h"
@implementation
BinaryRequestSerializer
+
(
instancetype
)
serializer
{
BinaryRequestSerializer
*
serializer
=
[[
self
alloc
]
init
];
return
serializer
;
}
#pragma mark - SM_AFURLRequestSerialization
-
(
NSURLRequest
*
)
requestBySerializingRequest
:(
NSURLRequest
*
)
request
withParameters
:(
id
)
parameters
error
:(
NSError
*
__autoreleasing
*
)
error
{
NSParameterAssert
(
request
);
if
([
self
.
HTTPMethodsEncodingParametersInURI
containsObject
:[[
request
HTTPMethod
]
uppercaseString
]])
{
return
[
super
requestBySerializingRequest
:
request
withParameters
:
parameters
error
:
error
];
}
NSMutableURLRequest
*
mutableRequest
=
[
request
mutableCopy
];
[
self
.
HTTPRequestHeaders
enumerateKeysAndObjectsUsingBlock
:
^
(
id
field
,
id
value
,
BOOL
*
__unused
stop
)
{
if
(
!
[
request
valueForHTTPHeaderField
:
field
])
{
[
mutableRequest
setValue
:
value
forHTTPHeaderField
:
field
];
}
}];
if
(
parameters
)
{
if
(
!
[
mutableRequest
valueForHTTPHeaderField
:
@"Content-Type"
])
{
[
mutableRequest
setValue
:
@"application/octet-stream"
forHTTPHeaderField
:
@"Content-Type"
];
}
[
mutableRequest
setHTTPBody
:
parameters
];
}
return
mutableRequest
;
}
#pragma mark - NSSecureCoding
-
(
instancetype
)
initWithCoder
:(
NSCoder
*
)
decoder
{
self
=
[
super
initWithCoder
:
decoder
];
if
(
!
self
)
{
return
nil
;
}
return
self
;
}
@end
src/ios/BinaryResponseSerializer.h
0 → 100644
View file @
09c88efe
#import <Foundation/Foundation.h>
#import "SM_AFURLResponseSerialization.h"
@interface
BinaryResponseSerializer
:
SM_AFHTTPResponseSerializer
+
(
instancetype
)
serializer
;
@end
src/ios/BinaryResponseSerializer.m
0 → 100644
View file @
09c88efe
#import "BinaryResponseSerializer.h"
static
NSError
*
AFErrorWithUnderlyingError
(
NSError
*
error
,
NSError
*
underlyingError
)
{
if
(
!
error
)
{
return
underlyingError
;
}
if
(
!
underlyingError
||
error
.
userInfo
[
NSUnderlyingErrorKey
])
{
return
error
;
}
NSMutableDictionary
*
mutableUserInfo
=
[
error
.
userInfo
mutableCopy
];
mutableUserInfo
[
NSUnderlyingErrorKey
]
=
underlyingError
;
return
[[
NSError
alloc
]
initWithDomain
:
error
.
domain
code
:
error
.
code
userInfo
:
mutableUserInfo
];
}
static
BOOL
AFErrorOrUnderlyingErrorHasCodeInDomain
(
NSError
*
error
,
NSInteger
code
,
NSString
*
domain
)
{
if
([
error
.
domain
isEqualToString
:
domain
]
&&
error
.
code
==
code
)
{
return
YES
;
}
else
if
(
error
.
userInfo
[
NSUnderlyingErrorKey
])
{
return
AFErrorOrUnderlyingErrorHasCodeInDomain
(
error
.
userInfo
[
NSUnderlyingErrorKey
],
code
,
domain
);
}
return
NO
;
}
@implementation
BinaryResponseSerializer
+
(
instancetype
)
serializer
{
BinaryResponseSerializer
*
serializer
=
[[
self
alloc
]
init
];
return
serializer
;
}
-
(
instancetype
)
init
{
self
=
[
super
init
];
if
(
!
self
)
{
return
nil
;
}
self
.
acceptableContentTypes
=
nil
;
return
self
;
}
-
(
NSString
*
)
decodeResponseData
:(
NSData
*
)
rawResponseData
withEncoding
:(
CFStringEncoding
)
cfEncoding
{
NSStringEncoding
nsEncoding
;
NSString
*
decoded
=
nil
;
if
(
cfEncoding
!=
kCFStringEncodingInvalidId
)
{
nsEncoding
=
CFStringConvertEncodingToNSStringEncoding
(
cfEncoding
);
}
NSStringEncoding
supportedEncodings
[
6
]
=
{
NSUTF8StringEncoding
,
NSWindowsCP1252StringEncoding
,
NSISOLatin1StringEncoding
,
NSISOLatin2StringEncoding
,
NSASCIIStringEncoding
,
NSUnicodeStringEncoding
};
for
(
int
i
=
0
;
i
<
sizeof
(
supportedEncodings
)
/
sizeof
(
NSStringEncoding
)
&&
!
decoded
;
++
i
)
{
if
(
cfEncoding
==
kCFStringEncodingInvalidId
||
nsEncoding
==
supportedEncodings
[
i
])
{
decoded
=
[[
NSString
alloc
]
initWithData
:
rawResponseData
encoding
:
supportedEncodings
[
i
]];
}
}
return
decoded
;
}
-
(
CFStringEncoding
)
getEncoding
:(
NSURLResponse
*
)
response
{
CFStringEncoding
encoding
=
kCFStringEncodingInvalidId
;
if
(
response
.
textEncodingName
)
{
encoding
=
CFStringConvertIANACharSetNameToEncoding
((
CFStringRef
)
response
.
textEncodingName
);
}
return
encoding
;
}
#pragma mark -
-
(
BOOL
)
validateResponse
:(
NSHTTPURLResponse
*
)
response
data
:(
NSData
*
)
data
error
:(
NSError
*
__autoreleasing
*
)
error
{
if
(
response
&&
[
response
isKindOfClass
:[
NSHTTPURLResponse
class
]])
{
if
(
self
.
acceptableStatusCodes
&&
!
[
self
.
acceptableStatusCodes
containsIndex
:(
NSUInteger
)
response
.
statusCode
]
&&
[
response
URL
])
{
NSMutableDictionary
*
mutableUserInfo
=
[@{
NSLocalizedDescriptionKey:
[
NSString
stringWithFormat
:
NSLocalizedStringFromTable
(
@"Request failed: %@ (%ld)"
,
@"SM_AFNetworking"
,
nil
),
[
NSHTTPURLResponse
localizedStringForStatusCode
:
response
.
statusCode
],
(
long
)
response
.
statusCode
],
NSURLErrorFailingURLErrorKey:
[
response
URL
],
SM_AFNetworkingOperationFailingURLResponseErrorKey:
response
,
}
mutableCopy
];
if
(
data
)
{
mutableUserInfo
[
SM_AFNetworkingOperationFailingURLResponseDataErrorKey
]
=
data
;
// trying to decode error message in body
mutableUserInfo
[
SM_AFNetworkingOperationFailingURLResponseBodyErrorKey
]
=
[
self
decodeResponseData
:
data
withEncoding
:[
self
getEncoding
:
response
]];
}
if
(
error
)
{
*
error
=
[
NSError
errorWithDomain
:
SM_AFURLResponseSerializationErrorDomain
code
:
NSURLErrorBadServerResponse
userInfo
:
mutableUserInfo
];
}
return
NO
;
}
}
return
YES
;
}
#pragma mark - SM_AFURLResponseSerialization
-
(
id
)
responseObjectForResponse
:(
NSURLResponse
*
)
response
data
:(
NSData
*
)
data
error
:(
NSError
*
__autoreleasing
*
)
error
{
if
(
!
[
self
validateResponse
:(
NSHTTPURLResponse
*
)
response
data
:
data
error
:
error
])
{
if
(
!
error
||
AFErrorOrUnderlyingErrorHasCodeInDomain
(
*
error
,
NSURLErrorCannotDecodeContentData
,
SM_AFURLResponseSerializationErrorDomain
))
{
return
nil
;
}
}
return
[
data
base64EncodedStringWithOptions
:
0
];
}
@end
src/ios/CordovaHttpPlugin.h
0 → 100644
View file @
09c88efe
#import <Foundation/Foundation.h>
#import <Cordova/CDVPlugin.h>
@interface
CordovaHttpPlugin
:
CDVPlugin
-
(
void
)
setServerTrustMode
:(
CDVInvokedUrlCommand
*
)
command
;
-
(
void
)
setClientAuthMode
:(
CDVInvokedUrlCommand
*
)
command
;
-
(
void
)
post
:(
CDVInvokedUrlCommand
*
)
command
;
-
(
void
)
put
:(
CDVInvokedUrlCommand
*
)
command
;
-
(
void
)
patch
:(
CDVInvokedUrlCommand
*
)
command
;
-
(
void
)
get
:(
CDVInvokedUrlCommand
*
)
command
;
-
(
void
)
delete
:(
CDVInvokedUrlCommand
*
)
command
;
-
(
void
)
head
:(
CDVInvokedUrlCommand
*
)
command
;
-
(
void
)
options
:(
CDVInvokedUrlCommand
*
)
command
;
-
(
void
)
uploadFiles
:(
CDVInvokedUrlCommand
*
)
command
;
-
(
void
)
downloadFile
:(
CDVInvokedUrlCommand
*
)
command
;
-
(
void
)
abort
:(
CDVInvokedUrlCommand
*
)
command
;
@end
src/ios/CordovaHttpPlugin.m
0 → 100644
View file @
09c88efe
#import "CordovaHttpPlugin.h"
#import "CDVFile.h"
#import "BinaryRequestSerializer.h"
#import "BinaryResponseSerializer.h"
#import "TextResponseSerializer.h"
#import "TextRequestSerializer.h"
#import "SM_AFHTTPSessionManager.h"
#import "SDNetworkActivityIndicator.h"
@interface
CordovaHttpPlugin
()
-
(
void
)
addRequest
:(
NSNumber
*
)
reqId
forTask
:(
NSURLSessionDataTask
*
)
task
;
-
(
void
)
removeRequest
:(
NSNumber
*
)
reqId
;
-
(
void
)
setRequestHeaders
:(
NSDictionary
*
)
headers
forManager
:(
SM_AFHTTPSessionManager
*
)
manager
;
-
(
void
)
handleSuccess
:(
NSMutableDictionary
*
)
dictionary
withResponse
:(
NSHTTPURLResponse
*
)
response
andData
:(
id
)
data
;
-
(
void
)
handleError
:(
NSMutableDictionary
*
)
dictionary
withResponse
:(
NSHTTPURLResponse
*
)
response
error
:(
NSError
*
)
error
;
-
(
NSNumber
*
)
getStatusCode
:(
NSError
*
)
error
;
-
(
NSMutableDictionary
*
)
copyHeaderFields
:(
NSDictionary
*
)
headerFields
;
-
(
void
)
setTimeout
:(
NSTimeInterval
)
timeout
forManager
:(
SM_AFHTTPSessionManager
*
)
manager
;
-
(
void
)
setRedirect
:(
bool
)
redirect
forManager
:(
SM_AFHTTPSessionManager
*
)
manager
;
@end
@implementation
CordovaHttpPlugin
{
SM_AFSecurityPolicy
*
securityPolicy
;
NSURLCredential
*
x509Credential
;
NSMutableDictionary
*
reqDict
;
}
-
(
void
)
pluginInitialize
{
securityPolicy
=
[
SM_AFSecurityPolicy
policyWithPinningMode
:
AFSSLPinningModeNone
];
reqDict
=
[
NSMutableDictionary
dictionary
];
}
-
(
void
)
addRequest
:(
NSNumber
*
)
reqId
forTask
:(
NSURLSessionDataTask
*
)
task
{
[
reqDict
setObject
:
task
forKey
:
reqId
];
}
-
(
void
)
removeRequest
:(
NSNumber
*
)
reqId
{
[
reqDict
removeObjectForKey
:
reqId
];
}
-
(
void
)
setRequestSerializer
:(
NSString
*
)
serializerName
forManager
:(
SM_AFHTTPSessionManager
*
)
manager
{
if
([
serializerName
isEqualToString
:
@"json"
])
{
manager
.
requestSerializer
=
[
SM_AFJSONRequestSerializer
serializer
];
}
else
if
([
serializerName
isEqualToString
:
@"utf8"
])
{
manager
.
requestSerializer
=
[
TextRequestSerializer
serializer
];
}
else
if
([
serializerName
isEqualToString
:
@"raw"
])
{
manager
.
requestSerializer
=
[
BinaryRequestSerializer
serializer
];
}
else
{
manager
.
requestSerializer
=
[
SM_AFHTTPRequestSerializer
serializer
];
}
}
-
(
void
)
setupAuthChallengeBlock
:(
SM_AFHTTPSessionManager
*
)
manager
{
[
manager
setSessionDidReceiveAuthenticationChallengeBlock
:
^
NSURLSessionAuthChallengeDisposition
(
NSURLSession
*
_Nonnull
session
,
NSURLAuthenticationChallenge
*
_Nonnull
challenge
,
NSURLCredential
*
_Nullable
__autoreleasing
*
_Nullable
credential
)
{
if
([
challenge
.
protectionSpace
.
authenticationMethod
isEqualToString
:
NSURLAuthenticationMethodServerTrust
])
{
*
credential
=
[
NSURLCredential
credentialForTrust
:
challenge
.
protectionSpace
.
serverTrust
];
if
(
!
[
self
->
securityPolicy
evaluateServerTrust
:
challenge
.
protectionSpace
.
serverTrust
forDomain
:
challenge
.
protectionSpace
.
host
])
{
return
NSURLSessionAuthChallengeRejectProtectionSpace
;
}
if
(
credential
)
{
return
NSURLSessionAuthChallengeUseCredential
;
}
}
if
([
challenge
.
protectionSpace
.
authenticationMethod
isEqualToString
:
NSURLAuthenticationMethodClientCertificate
]
&&
self
->
x509Credential
)
{
*
credential
=
self
->
x509Credential
;
return
NSURLSessionAuthChallengeUseCredential
;
}
return
NSURLSessionAuthChallengePerformDefaultHandling
;
}];
}
-
(
void
)
setRequestHeaders
:(
NSDictionary
*
)
headers
forManager
:(
SM_AFHTTPSessionManager
*
)
manager
{
[
headers
enumerateKeysAndObjectsUsingBlock
:
^
(
id
key
,
id
obj
,
BOOL
*
stop
)
{
[
manager
.
requestSerializer
setValue
:
obj
forHTTPHeaderField
:
key
];
}];
}
-
(
void
)
setRedirect
:(
bool
)
followRedirect
forManager
:(
SM_AFHTTPSessionManager
*
)
manager
{
[
manager
setTaskWillPerformHTTPRedirectionBlock
:
^
NSURLRequest
*
_Nonnull
(
NSURLSession
*
_Nonnull
session
,
NSURLSessionTask
*
_Nonnull
task
,
NSURLResponse
*
_Nonnull
response
,
NSURLRequest
*
_Nonnull
request
)
{
if
(
followRedirect
)
{
return
request
;
}
else
{
return
nil
;
}
}];
}
-
(
void
)
setTimeout
:(
NSTimeInterval
)
timeout
forManager
:(
SM_AFHTTPSessionManager
*
)
manager
{
[
manager
.
requestSerializer
setTimeoutInterval
:
timeout
];
}
-
(
void
)
setResponseSerializer
:(
NSString
*
)
responseType
forManager
:(
SM_AFHTTPSessionManager
*
)
manager
{
if
([
responseType
isEqualToString
:
@"text"
]
||
[
responseType
isEqualToString
:
@"json"
])
{
manager
.
responseSerializer
=
[
TextResponseSerializer
serializer
];
}
else
{
manager
.
responseSerializer
=
[
BinaryResponseSerializer
serializer
];
}
}
-
(
void
)
handleSuccess
:(
NSMutableDictionary
*
)
dictionary
withResponse
:(
NSHTTPURLResponse
*
)
response
andData
:(
id
)
data
{
if
(
response
!=
nil
)
{
[
dictionary
setValue
:
response
.
URL
.
absoluteString
forKey
:
@"url"
];
[
dictionary
setObject
:[
NSNumber
numberWithInt
:(
int
)
response
.
statusCode
]
forKey
:
@"status"
];
[
dictionary
setObject
:[
self
copyHeaderFields
:
response
.
allHeaderFields
]
forKey
:
@"headers"
];
}
if
(
data
!=
nil
)
{
[
dictionary
setObject
:
data
forKey
:
@"data"
];
}
}
-
(
void
)
handleError
:(
NSMutableDictionary
*
)
dictionary
withResponse
:(
NSHTTPURLResponse
*
)
response
error
:(
NSError
*
)
error
{
bool
aborted
=
error
.
code
==
NSURLErrorCancelled
;
if
(
aborted
){
[
dictionary
setObject
:[
NSNumber
numberWithInt
:
-
8
]
forKey
:
@"status"
];
[
dictionary
setObject
:
@"Request was aborted"
forKey
:
@"error"
];
}
if
(
response
!=
nil
)
{
[
dictionary
setValue
:
response
.
URL
.
absoluteString
forKey
:
@"url"
];
[
dictionary
setObject
:[
self
copyHeaderFields
:
response
.
allHeaderFields
]
forKey
:
@"headers"
];
if
(
!
aborted
){
[
dictionary
setObject
:[
NSNumber
numberWithInt
:(
int
)
response
.
statusCode
]
forKey
:
@"status"
];
if
(
error
.
userInfo
[
SM_AFNetworkingOperationFailingURLResponseBodyErrorKey
])
{
[
dictionary
setObject
:
error
.
userInfo
[
SM_AFNetworkingOperationFailingURLResponseBodyErrorKey
]
forKey
:
@"error"
];
}
}
}
else
if
(
!
aborted
)
{
[
dictionary
setObject
:[
self
getStatusCode
:
error
]
forKey
:
@"status"
];
[
dictionary
setObject
:[
error
localizedDescription
]
forKey
:
@"error"
];
}
}
-
(
void
)
handleException
:(
NSException
*
)
exception
withCommand
:(
CDVInvokedUrlCommand
*
)
command
{
CordovaHttpPlugin
*
__weak
weakSelf
=
self
;
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
[
dictionary
setValue
:
exception
.
userInfo
forKey
:
@"error"
];
[
dictionary
setObject
:[
NSNumber
numberWithInt
:
-
1
]
forKey
:
@"status"
];
CDVPluginResult
*
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_ERROR
messageAsDictionary
:
dictionary
];
[
weakSelf
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
}
-
(
NSNumber
*
)
getStatusCode
:(
NSError
*
)
error
{
switch
([
error
code
])
{
case
-
1001
:
// timeout
return
[
NSNumber
numberWithInt
:
-
4
];
case
-
1002
:
// unsupported URL
return
[
NSNumber
numberWithInt
:
-
5
];
case
-
1003
:
// server not found
return
[
NSNumber
numberWithInt
:
-
3
];
case
-
1009
:
// no connection
return
[
NSNumber
numberWithInt
:
-
6
];
case
-
1200
:
// secure connection failed
case
-
1201
:
// certificate has bad date
case
-
1202
:
// certificate untrusted
case
-
1203
:
// certificate has unknown root
case
-
1204
:
// certificate is not yet valid
// configuring SSL failed
return
[
NSNumber
numberWithInt
:
-
2
];
default:
return
[
NSNumber
numberWithInt
:
-
1
];
}
}
-
(
NSMutableDictionary
*
)
copyHeaderFields
:(
NSDictionary
*
)
headerFields
{
NSMutableDictionary
*
headerFieldsCopy
=
[[
NSMutableDictionary
alloc
]
initWithCapacity
:
headerFields
.
count
];
NSString
*
headerKeyCopy
;
for
(
NSString
*
headerKey
in
headerFields
.
allKeys
)
{
headerKeyCopy
=
[[
headerKey
mutableCopy
]
lowercaseString
];
[
headerFieldsCopy
setValue
:[
headerFields
objectForKey
:
headerKey
]
forKey
:
headerKeyCopy
];
}
return
headerFieldsCopy
;
}
-
(
void
)
executeRequestWithoutData
:(
CDVInvokedUrlCommand
*
)
command
withMethod
:(
NSString
*
)
method
{
SM_AFHTTPSessionManager
*
manager
=
[
SM_AFHTTPSessionManager
manager
];
NSString
*
url
=
[
command
.
arguments
objectAtIndex
:
0
];
NSDictionary
*
headers
=
[
command
.
arguments
objectAtIndex
:
1
];
NSTimeInterval
connectTimeout
=
[[
command
.
arguments
objectAtIndex
:
2
]
doubleValue
];
NSTimeInterval
readTimeout
=
[[
command
.
arguments
objectAtIndex
:
3
]
doubleValue
];
bool
followRedirect
=
[[
command
.
arguments
objectAtIndex
:
4
]
boolValue
];
NSString
*
responseType
=
[
command
.
arguments
objectAtIndex
:
5
];
NSNumber
*
reqId
=
[
command
.
arguments
objectAtIndex
:
6
];
[
self
setRequestSerializer
:
@"default"
forManager
:
manager
];
[
self
setupAuthChallengeBlock
:
manager
];
[
self
setRequestHeaders
:
headers
forManager
:
manager
];
[
self
setTimeout
:
readTimeout
forManager
:
manager
];
[
self
setRedirect
:
followRedirect
forManager
:
manager
];
[
self
setResponseSerializer
:
responseType
forManager
:
manager
];
CordovaHttpPlugin
*
__weak
weakSelf
=
self
;
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
startActivity
];
@try
{
void
(
^
onSuccess
)(
NSURLSessionTask
*
,
id
)
=
^
(
NSURLSessionTask
*
task
,
id
responseObject
)
{
[
weakSelf
removeRequest
:
reqId
];
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
// no 'body' for HEAD request, omitting 'data'
if
([
method
isEqualToString
:
@"HEAD"
])
{
[
self
handleSuccess
:
dictionary
withResponse
:(
NSHTTPURLResponse
*
)
task
.
response
andData
:
nil
];
}
else
{
[
self
handleSuccess
:
dictionary
withResponse
:(
NSHTTPURLResponse
*
)
task
.
response
andData
:
responseObject
];
}
CDVPluginResult
*
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_OK
messageAsDictionary
:
dictionary
];
[
weakSelf
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
[
manager
invalidateSessionCancelingTasks
:
YES
];
};
void
(
^
onFailure
)(
NSURLSessionTask
*
,
NSError
*
)
=
^
(
NSURLSessionTask
*
task
,
NSError
*
error
)
{
[
weakSelf
removeRequest
:
reqId
];
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
[
self
handleError
:
dictionary
withResponse
:(
NSHTTPURLResponse
*
)
task
.
response
error
:
error
];
CDVPluginResult
*
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_ERROR
messageAsDictionary
:
dictionary
];
[
weakSelf
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
[
manager
invalidateSessionCancelingTasks
:
YES
];
};
NSURLSessionDataTask
*
task
=
[
manager
downloadTaskWithHTTPMethod
:
method
URLString
:
url
parameters
:
nil
progress
:
nil
success
:
onSuccess
failure
:
onFailure
];
[
self
addRequest
:
reqId
forTask
:
task
];
}
@catch
(
NSException
*
exception
)
{
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
[
self
handleException
:
exception
withCommand
:
command
];
}
}
-
(
void
)
executeRequestWithData
:(
CDVInvokedUrlCommand
*
)
command
withMethod
:(
NSString
*
)
method
{
SM_AFHTTPSessionManager
*
manager
=
[
SM_AFHTTPSessionManager
manager
];
NSString
*
url
=
[
command
.
arguments
objectAtIndex
:
0
];
NSDictionary
*
data
=
[
command
.
arguments
objectAtIndex
:
1
];
NSString
*
serializerName
=
[
command
.
arguments
objectAtIndex
:
2
];
NSDictionary
*
headers
=
[
command
.
arguments
objectAtIndex
:
3
];
NSTimeInterval
connectTimeout
=
[[
command
.
arguments
objectAtIndex
:
4
]
doubleValue
];
NSTimeInterval
readTimeout
=
[[
command
.
arguments
objectAtIndex
:
5
]
doubleValue
];
bool
followRedirect
=
[[
command
.
arguments
objectAtIndex
:
6
]
boolValue
];
NSString
*
responseType
=
[
command
.
arguments
objectAtIndex
:
7
];
NSNumber
*
reqId
=
[
command
.
arguments
objectAtIndex
:
8
];
[
self
setRequestSerializer
:
serializerName
forManager
:
manager
];
[
self
setupAuthChallengeBlock
:
manager
];
[
self
setRequestHeaders
:
headers
forManager
:
manager
];
[
self
setTimeout
:
readTimeout
forManager
:
manager
];
[
self
setRedirect
:
followRedirect
forManager
:
manager
];
[
self
setResponseSerializer
:
responseType
forManager
:
manager
];
CordovaHttpPlugin
*
__weak
weakSelf
=
self
;
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
startActivity
];
@try
{
void
(
^
constructBody
)(
id
<
AFMultipartFormData
>
)
=
^
(
id
<
AFMultipartFormData
>
formData
)
{
NSArray
*
buffers
=
[
data
mutableArrayValueForKey
:
@"buffers"
];
NSArray
*
fileNames
=
[
data
mutableArrayValueForKey
:
@"fileNames"
];
NSArray
*
names
=
[
data
mutableArrayValueForKey
:
@"names"
];
NSArray
*
types
=
[
data
mutableArrayValueForKey
:
@"types"
];
NSError
*
error
;
for
(
int
i
=
0
;
i
<
[
buffers
count
];
++
i
)
{
NSData
*
decodedBuffer
=
[[
NSData
alloc
]
initWithBase64EncodedString
:[
buffers
objectAtIndex
:
i
]
options
:
0
];
NSString
*
fileName
=
[
fileNames
objectAtIndex
:
i
];
NSString
*
partName
=
[
names
objectAtIndex
:
i
];
NSString
*
partType
=
[
types
objectAtIndex
:
i
];
if
(
!
[
fileName
isEqual
:[
NSNull
null
]])
{
[
formData
appendPartWithFileData
:
decodedBuffer
name
:
partName
fileName
:
fileName
mimeType
:
partType
];
}
else
{
[
formData
appendPartWithFormData
:
decodedBuffer
name
:[
names
objectAtIndex
:
i
]];
}
}
if
(
error
)
{
[
weakSelf
removeRequest
:
reqId
];
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
[
dictionary
setObject
:[
NSNumber
numberWithInt
:
400
]
forKey
:
@"status"
];
[
dictionary
setObject
:
@"Could not add part to multipart request body."
forKey
:
@"error"
];
CDVPluginResult
*
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_ERROR
messageAsDictionary
:
dictionary
];
[
weakSelf
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
return
;
}
};
void
(
^
onSuccess
)(
NSURLSessionTask
*
,
id
)
=
^
(
NSURLSessionTask
*
task
,
id
responseObject
)
{
[
weakSelf
removeRequest
:
reqId
];
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
[
self
handleSuccess
:
dictionary
withResponse
:(
NSHTTPURLResponse
*
)
task
.
response
andData
:
responseObject
];
CDVPluginResult
*
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_OK
messageAsDictionary
:
dictionary
];
[
weakSelf
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
[
manager
invalidateSessionCancelingTasks
:
YES
];
};
void
(
^
onFailure
)(
NSURLSessionTask
*
,
NSError
*
)
=
^
(
NSURLSessionTask
*
task
,
NSError
*
error
)
{
[
weakSelf
removeRequest
:
reqId
];
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
[
self
handleError
:
dictionary
withResponse
:(
NSHTTPURLResponse
*
)
task
.
response
error
:
error
];
CDVPluginResult
*
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_ERROR
messageAsDictionary
:
dictionary
];
[
weakSelf
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
[
manager
invalidateSessionCancelingTasks
:
YES
];
};
NSURLSessionDataTask
*
task
;
if
([
serializerName
isEqualToString
:
@"multipart"
])
{
task
=
[
manager
uploadTaskWithHTTPMethod
:
method
URLString
:
url
parameters
:
nil
constructingBodyWithBlock
:
constructBody
progress
:
nil
success
:
onSuccess
failure
:
onFailure
];
}
else
{
task
=
[
manager
uploadTaskWithHTTPMethod
:
method
URLString
:
url
parameters
:
data
progress
:
nil
success
:
onSuccess
failure
:
onFailure
];
}
[
self
addRequest
:
reqId
forTask
:
task
];
}
@catch
(
NSException
*
exception
)
{
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
[
self
handleException
:
exception
withCommand
:
command
];
}
}
-
(
void
)
setServerTrustMode
:(
CDVInvokedUrlCommand
*
)
command
{
NSString
*
certMode
=
[
command
.
arguments
objectAtIndex
:
0
];
if
([
certMode
isEqualToString
:
@"default"
]
||
[
certMode
isEqualToString
:
@"legacy"
])
{
securityPolicy
=
[
SM_AFSecurityPolicy
policyWithPinningMode
:
AFSSLPinningModeNone
];
securityPolicy
.
allowInvalidCertificates
=
NO
;
securityPolicy
.
validatesDomainName
=
YES
;
}
else
if
([
certMode
isEqualToString
:
@"nocheck"
])
{
securityPolicy
=
[
SM_AFSecurityPolicy
policyWithPinningMode
:
AFSSLPinningModeNone
];
securityPolicy
.
allowInvalidCertificates
=
YES
;
securityPolicy
.
validatesDomainName
=
NO
;
}
else
if
([
certMode
isEqualToString
:
@"pinned"
])
{
securityPolicy
=
[
SM_AFSecurityPolicy
policyWithPinningMode
:
AFSSLPinningModeCertificate
];
securityPolicy
.
allowInvalidCertificates
=
NO
;
securityPolicy
.
validatesDomainName
=
YES
;
}
CDVPluginResult
*
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_OK
];
[
self
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
}
-
(
void
)
setClientAuthMode
:(
CDVInvokedUrlCommand
*
)
command
{
CDVPluginResult
*
pluginResult
;
NSString
*
mode
=
[
command
.
arguments
objectAtIndex
:
0
];
if
([
mode
isEqualToString
:
@"none"
])
{
x509Credential
=
nil
;
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_OK
];
}
if
([
mode
isEqualToString
:
@"systemstore"
])
{
NSString
*
alias
=
[
command
.
arguments
objectAtIndex
:
1
];
// TODO
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_ERROR
messageAsString
:
@"mode 'systemstore' is not supported on iOS"
];
}
if
([
mode
isEqualToString
:
@"buffer"
])
{
CFDataRef
container
=
(
__bridge
CFDataRef
)
[
command
.
arguments
objectAtIndex
:
2
];
CFStringRef
password
=
(
__bridge
CFStringRef
)
[
command
.
arguments
objectAtIndex
:
3
];
const
void
*
keys
[]
=
{
kSecImportExportPassphrase
};
const
void
*
values
[]
=
{
password
};
CFDictionaryRef
options
=
CFDictionaryCreate
(
NULL
,
keys
,
values
,
1
,
NULL
,
NULL
);
CFArrayRef
items
;
OSStatus
securityError
=
SecPKCS12Import
(
container
,
options
,
&
items
);
CFRelease
(
options
);
if
(
securityError
!=
noErr
)
{
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_ERROR
];
}
else
{
CFDictionaryRef
identityDict
=
CFArrayGetValueAtIndex
(
items
,
0
);
SecIdentityRef
identity
=
(
SecIdentityRef
)
CFDictionaryGetValue
(
identityDict
,
kSecImportItemIdentity
);
SecTrustRef
trust
=
(
SecTrustRef
)
CFDictionaryGetValue
(
identityDict
,
kSecImportItemTrust
);
int
count
=
(
int
)
SecTrustGetCertificateCount
(
trust
);
NSMutableArray
*
trustCertificates
=
nil
;
if
(
count
>
1
)
{
trustCertificates
=
[
NSMutableArray
arrayWithCapacity
:
SecTrustGetCertificateCount
(
trust
)];
for
(
int
i
=
1
;
i
<
count
;
++
i
)
{
[
trustCertificates
addObject
:(
id
)
SecTrustGetCertificateAtIndex
(
trust
,
i
)];
}
}
self
->
x509Credential
=
[
NSURLCredential
credentialWithIdentity
:
identity
certificates
:
trustCertificates
persistence
:
NSURLCredentialPersistenceForSession
];
CFRelease
(
items
);
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_OK
];
}
}
[
self
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
}
-
(
void
)
post
:(
CDVInvokedUrlCommand
*
)
command
{
[
self
executeRequestWithData
:
command
withMethod
:
@"POST"
];
}
-
(
void
)
put
:(
CDVInvokedUrlCommand
*
)
command
{
[
self
executeRequestWithData
:
command
withMethod
:
@"PUT"
];
}
-
(
void
)
patch
:(
CDVInvokedUrlCommand
*
)
command
{
[
self
executeRequestWithData
:
command
withMethod
:
@"PATCH"
];
}
-
(
void
)
get
:(
CDVInvokedUrlCommand
*
)
command
{
[
self
executeRequestWithoutData
:
command
withMethod
:
@"GET"
];
}
-
(
void
)
delete
:(
CDVInvokedUrlCommand
*
)
command
{
[
self
executeRequestWithoutData
:
command
withMethod
:
@"DELETE"
];
}
-
(
void
)
head
:(
CDVInvokedUrlCommand
*
)
command
{
[
self
executeRequestWithoutData
:
command
withMethod
:
@"HEAD"
];
}
-
(
void
)
options
:(
CDVInvokedUrlCommand
*
)
command
{
[
self
executeRequestWithoutData
:
command
withMethod
:
@"OPTIONS"
];
}
-
(
void
)
uploadFiles
:(
CDVInvokedUrlCommand
*
)
command
{
SM_AFHTTPSessionManager
*
manager
=
[
SM_AFHTTPSessionManager
manager
];
NSString
*
url
=
[
command
.
arguments
objectAtIndex
:
0
];
NSDictionary
*
headers
=
[
command
.
arguments
objectAtIndex
:
1
];
NSArray
*
filePaths
=
[
command
.
arguments
objectAtIndex
:
2
];
NSArray
*
names
=
[
command
.
arguments
objectAtIndex
:
3
];
NSTimeInterval
connectTimeout
=
[[
command
.
arguments
objectAtIndex
:
4
]
doubleValue
];
NSTimeInterval
readTimeout
=
[[
command
.
arguments
objectAtIndex
:
5
]
doubleValue
];
bool
followRedirect
=
[[
command
.
arguments
objectAtIndex
:
6
]
boolValue
];
NSString
*
responseType
=
[
command
.
arguments
objectAtIndex
:
7
];
NSNumber
*
reqId
=
[
command
.
arguments
objectAtIndex
:
8
];
[
self
setRequestHeaders
:
headers
forManager
:
manager
];
[
self
setTimeout
:
readTimeout
forManager
:
manager
];
[
self
setRedirect
:
followRedirect
forManager
:
manager
];
[
self
setResponseSerializer
:
responseType
forManager
:
manager
];
CordovaHttpPlugin
*
__weak
weakSelf
=
self
;
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
startActivity
];
@try
{
NSURLSessionDataTask
*
task
=
[
manager
POST
:
url
parameters
:
nil
constructingBodyWithBlock
:^
(
id
<
AFMultipartFormData
>
formData
)
{
NSError
*
error
;
for
(
int
i
=
0
;
i
<
[
filePaths
count
];
i
++
)
{
NSString
*
filePath
=
(
NSString
*
)
[
filePaths
objectAtIndex
:
i
];
NSString
*
uploadName
=
(
NSString
*
)
[
names
objectAtIndex
:
i
];
NSURL
*
fileURL
=
[
NSURL
URLWithString
:
filePath
];
[
formData
appendPartWithFileURL
:
fileURL
name
:
uploadName
error
:&
error
];
}
if
(
error
)
{
[
weakSelf
removeRequest
:
reqId
];
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
[
dictionary
setObject
:[
NSNumber
numberWithInt
:
500
]
forKey
:
@"status"
];
[
dictionary
setObject
:
@"Could not add file to post body."
forKey
:
@"error"
];
CDVPluginResult
*
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_ERROR
messageAsDictionary
:
dictionary
];
[
weakSelf
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
return
;
}
}
progress
:
nil
success
:^
(
NSURLSessionTask
*
task
,
id
responseObject
)
{
[
weakSelf
removeRequest
:
reqId
];
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
[
self
handleSuccess
:
dictionary
withResponse
:(
NSHTTPURLResponse
*
)
task
.
response
andData
:
responseObject
];
CDVPluginResult
*
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_OK
messageAsDictionary
:
dictionary
];
[
weakSelf
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
}
failure
:^
(
NSURLSessionTask
*
task
,
NSError
*
error
)
{
[
weakSelf
removeRequest
:
reqId
];
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
[
self
handleError
:
dictionary
withResponse
:(
NSHTTPURLResponse
*
)
task
.
response
error
:
error
];
CDVPluginResult
*
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_ERROR
messageAsDictionary
:
dictionary
];
[
weakSelf
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
}];
[
self
addRequest
:
reqId
forTask
:
task
];
}
@catch
(
NSException
*
exception
)
{
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
[
self
handleException
:
exception
withCommand
:
command
];
}
}
-
(
void
)
downloadFile
:(
CDVInvokedUrlCommand
*
)
command
{
SM_AFHTTPSessionManager
*
manager
=
[
SM_AFHTTPSessionManager
manager
];
manager
.
responseSerializer
=
[
SM_AFHTTPResponseSerializer
serializer
];
NSString
*
url
=
[
command
.
arguments
objectAtIndex
:
0
];
NSDictionary
*
headers
=
[
command
.
arguments
objectAtIndex
:
1
];
NSString
*
filePath
=
[
command
.
arguments
objectAtIndex
:
2
];
NSTimeInterval
connectTimeout
=
[[
command
.
arguments
objectAtIndex
:
3
]
doubleValue
];
NSTimeInterval
readTimeout
=
[[
command
.
arguments
objectAtIndex
:
4
]
doubleValue
];
bool
followRedirect
=
[[
command
.
arguments
objectAtIndex
:
5
]
boolValue
];
NSNumber
*
reqId
=
[
command
.
arguments
objectAtIndex
:
6
];
[
self
setRequestHeaders
:
headers
forManager
:
manager
];
[
self
setupAuthChallengeBlock
:
manager
];
[
self
setTimeout
:
readTimeout
forManager
:
manager
];
[
self
setRedirect
:
followRedirect
forManager
:
manager
];
if
([
filePath
hasPrefix
:
@"file://"
])
{
filePath
=
[
filePath
substringFromIndex
:
7
];
}
CordovaHttpPlugin
*
__weak
weakSelf
=
self
;
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
startActivity
];
@try
{
NSURLSessionDataTask
*
task
=
[
manager
GET
:
url
parameters
:
nil
progress
:
nil
success
:^
(
NSURLSessionTask
*
task
,
id
responseObject
)
{
[
weakSelf
removeRequest
:
reqId
];
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* Modified by Andrew Stephan for Sync OnSet
*
*/
// Download response is okay; begin streaming output to file
NSString
*
parentPath
=
[
filePath
stringByDeletingLastPathComponent
];
// create parent directories if needed
NSError
*
error
;
if
([[
NSFileManager
defaultManager
]
createDirectoryAtPath
:
parentPath
withIntermediateDirectories
:
YES
attributes
:
nil
error
:&
error
]
==
NO
)
{
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
[
dictionary
setObject
:[
NSNumber
numberWithInt
:
500
]
forKey
:
@"status"
];
if
(
error
)
{
[
dictionary
setObject
:[
NSString
stringWithFormat
:
@"Could not create path to save downloaded file: %@"
,
[
error
localizedDescription
]]
forKey
:
@"error"
];
}
else
{
[
dictionary
setObject
:
@"Could not create path to save downloaded file"
forKey
:
@"error"
];
}
CDVPluginResult
*
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_ERROR
messageAsDictionary
:
dictionary
];
[
weakSelf
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
return
;
}
NSData
*
data
=
(
NSData
*
)
responseObject
;
if
(
!
[
data
writeToFile
:
filePath
atomically
:
YES
])
{
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
[
dictionary
setObject
:[
NSNumber
numberWithInt
:
500
]
forKey
:
@"status"
];
[
dictionary
setObject
:
@"Could not write the data to the given filePath."
forKey
:
@"error"
];
CDVPluginResult
*
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_ERROR
messageAsDictionary
:
dictionary
];
[
weakSelf
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
return
;
}
id
filePlugin
=
[
self
.
commandDelegate
getCommandInstance
:
@"File"
];
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
[
self
handleSuccess
:
dictionary
withResponse
:(
NSHTTPURLResponse
*
)
task
.
response
andData
:
nil
];
[
dictionary
setObject
:[
filePlugin
getDirectoryEntry
:
filePath
isDirectory
:
NO
]
forKey
:
@"file"
];
CDVPluginResult
*
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_OK
messageAsDictionary
:
dictionary
];
[
weakSelf
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
}
failure
:^
(
NSURLSessionTask
*
task
,
NSError
*
error
)
{
[
weakSelf
removeRequest
:
reqId
];
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
[
self
handleError
:
dictionary
withResponse
:(
NSHTTPURLResponse
*
)
task
.
response
error
:
error
];
[
dictionary
setObject
:
@"There was an error downloading the file"
forKey
:
@"error"
];
CDVPluginResult
*
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_ERROR
messageAsDictionary
:
dictionary
];
[
weakSelf
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
}];
[
self
addRequest
:
reqId
forTask
:
task
];
}
@catch
(
NSException
*
exception
)
{
[[
SDNetworkActivityIndicator
sharedActivityIndicator
]
stopActivity
];
[
self
handleException
:
exception
withCommand
:
command
];
}
}
-
(
void
)
abort
:(
CDVInvokedUrlCommand
*
)
command
{
NSNumber
*
reqId
=
[
command
.
arguments
objectAtIndex
:
0
];
CDVPluginResult
*
pluginResult
;
bool
removed
=
false
;
NSURLSessionDataTask
*
task
=
[
reqDict
objectForKey
:
reqId
];
if
(
task
){
@try
{
[
task
cancel
];
removed
=
true
;
}
@catch
(
NSException
*
exception
)
{
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
[
dictionary
setValue
:
exception
.
userInfo
forKey
:
@"error"
];
[
dictionary
setObject
:[
NSNumber
numberWithInt
:
-
1
]
forKey
:
@"status"
];
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_ERROR
messageAsDictionary
:
dictionary
];
}
}
if
(
!
pluginResult
){
NSMutableDictionary
*
dictionary
=
[
NSMutableDictionary
dictionary
];
[
dictionary
setObject
:[
NSNumber
numberWithBool
:
removed
]
forKey
:
@"aborted"
];
pluginResult
=
[
CDVPluginResult
resultWithStatus
:
CDVCommandStatus_OK
messageAsDictionary
:
dictionary
];
}
[
self
.
commandDelegate
sendPluginResult
:
pluginResult
callbackId
:
command
.
callbackId
];
}
@end
src/ios/SDNetworkActivityIndicator/LICENSE
0 → 100644
View file @
09c88efe
Copyright (c) 2010 Olivier Poitrey <rs@dailymotion.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Prev
1
2
3
4
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment