I created an NTLM authenticating SOAP client based on KSOAP-Android and JCIFS. The implementation looks something like this:
public class NtlmServiceConnection implements ServiceConnection
{
public NtlmServiceConnection(final SoapConnectionInfo connectionInfo, String path)
{
httpclient = new DefaultHttpClient();
httpclient.getAuthSchemes().register(AuthPolicy.NTLM, new NTLMSchemeFactory());
//...
#Override
public InputStream openInputStream() throws IOException {
ByteArrayEntity re = new ByteArrayEntity(bufferStream.toByteArray());
post.removeHeaders("CONTENT-LENGTH");
post.setEntity(re);
HttpResponse rep = httpclient.execute(post);
InputStream stream = rep.getEntity().getContent();
return stream;
}
//....
}
From the looks of it KSOAP is generating the correct message because bufferStream is populated with the SOAP envelope as expected. JCIFS seems to be doing its job as well as I can see the NTLM challenge response taking place via Wireshark. The issue is that the message body is missing. It is simply null. Due to this the web service encounters a 501 and the InputStream returned is null.
Anyone have a clue why this would happen?
Note: I'm removing the CONTENT-LENGTH header below because setEntity apparently tries to set this but KSOAP has already set it. I simply remove it and allow setEntity to reset it.
I finally figured it out and blogged about it here: http://csharpening.net/blog/?p=271
Related
I want to use the Video Cognitive Service in Android.
The sample that Microsoft provided is used in C#.
The video function is sending an URL to the server,
So I think it is possible using HTTP POST to send an URL in Android.
http://ppt.cc/V1piA
The problem I met is that I don't know the URL format in "application/octet-stream", and I didn't see the example on the Microsoft website.
Is it possible using HTTP POST in Android to upload a downloaded video to the server, and I can get the analysis result from the server?
If possible, what is the format of the HTTP POST to send request to the server?
Thanks.
You may try something like this to send image files for cognitive-services face detect.
Using org.apache.httpcomponents::httpclient :
#Test
public void testSendBinary() throws MalformedURLException {
File picfile = new File("app/sampledata/my_file.jpeg");
if (!picfile.exists()) throw new AssertionError();
HttpClient httpclient = HttpClients.createDefault();
try {
URIBuilder builder = new URIBuilder("https://westcentralus.api.cognitive.microsoft.com/face/v1.0/detect");
builder.setParameter("returnFaceId", "true");
builder.setParameter("returnFaceLandmarks", "false");
URI uri = builder.build();
HttpPost request = new HttpPost(uri);
request.setHeader("Content-Type", "application/octet-stream");
request.setHeader("Ocp-Apim-Subscription-Key", "***");
// Request body
request.setEntity(new FileEntity(picfile));
HttpResponse response = httpclient.execute(request);
HttpEntity entity = response.getEntity();
if (entity != null) {
System.out.println(EntityUtils.toString(entity));
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
HTTP POST refers to the HTTP method 'POST', application/octet-stream refers to the media type - in this case a stream of application specific octets or bytes.
This is, unfortunately, very subjective as the mechanism for uploading content via HTTP action may be preferred one way or another. Suffice it to say, you will create an InputStream of your content, format a POST request using the mechanism of your choosing:
straight Java
HTTPClient
Making sure to set the content-type of the POST to application/octet-stream.
After performing the post, consult your API documentation for expected return types.
Okay, so I was trying to send Http Post Requests to this one site, and I sniffed the sent request with wireshark thus getting the text data from the post request of this site. I used this in a stock Java application, and it worked perfectly fine. I could use the post method regularly with no problem whatsoever, and it would return the appropriate website. Then I tried doing this with Android. Instead of returning the actual html data after executing the post request, it returns the regular page html data untouched. It DOES send a post request (sniff with wireshark again), it just doesn't seem to get the appropriate response. I took the exact same method used from another one of my projects, which worked perfectly fine in that project, and pasted it into my new project. I added the INTERNET user permission in Android, so there's nothing wrong with that. The only visible difference is that I used NameValuePairs in the other one (the one that worked) and in this one I'm directly putting the string into a StringEntity without encoding (using UTF-8 encoding screws up the String though). I used this exact same line of text in regular Java like I said, and it worked fine with no encoding. So what could be the problem? This is the code:
public static String sendNamePostRequest(String urlString) {
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(urlString);
StringBuffer sb = new StringBuffer();
try {
post.setEntity(new StringEntity(
"__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=%2FwEPDwULLTE3NDM5MzMwMzRkZA%3D%3D&__EVENTVALIDATION=%2FwEWBAL%2B%2B4CfBgK52%2BLYCQK1gpH7BAL0w%2FPHAQ%3D%3D&_nameTextBox=John&_zoekButton=Zoek&numberOfLettersField=3"));
HttpResponse response = client.execute(post);
HttpEntity entity = response.getEntity();
BufferedReader br = new BufferedReader(new InputStreamReader(
entity.getContent()));
String in = "";
while ((in = br.readLine()) != null) {
sb.append(in + "\n");
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
Can you see what's wrong here?
I've got a class:
public class WebReader implements IWebReader {
HttpClient client;
public WebReader() {
client = new DefaultHttpClient();
}
public WebReader(HttpClient httpClient) {
client = httpClient;
}
/**
* Reads the web resource at the specified path with the params given.
* #param path Path of the resource to be read.
* #param params Parameters needed to be transferred to the server using POST method.
* #param compression If it's needed to use compression. Default is <b>true</b>.
* #return <p>Returns the string got from the server. If there was an error downloading file,
* an empty string is returned, the information about the error is written to the log file.</p>
*/
public String readWebResource(String path, ArrayList<BasicNameValuePair> params, Boolean compression) {
HttpPost httpPost = new HttpPost(path);
String result = "";
if (compression)
httpPost.addHeader("Accept-Encoding", "gzip");
if (params.size() > 0){
try {
httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
}
try {
HttpResponse response = client.execute(httpPost);
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200) {
HttpEntity entity = response.getEntity();
InputStream content = entity.getContent();
if (entity.getContentEncoding() != null
&& "gzip".equalsIgnoreCase(entity.getContentEncoding()
.getValue()))
result = uncompressInputStream(content);
else
result = convertStreamToString(content);
} else {
Log.e(MyApp.class.toString(), "Failed to download file");
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
private String uncompressInputStream(InputStream inputStream)
throws IOException {...}
private String convertStreamToString(InputStream is) {...}
}
I cannot find a way to test it using a standard framework. Especially, I need to simulate total internet lost from inside the test.
There are suggestions to manually turn the Internet in the emulator off while performing the test. But it seems to me as not quite a good solution, because the automatic tests should be... automatic.
I added a "client" field to the class trying to mock it from inside the test class. But implementation of the HttpClient interface seems quite complex.
The Robolectric framework allows the developers to test Http connection as far as I know. But I guess there is some way to write such a test without using so big additional framework.
So are there any short and straightforward ways of unit testing classes that use HttpClient? How did you solve this in your projects?
I added a "client" field to the class trying to mock it from inside the test class. But implementation of the HttpClient interface seems quite complex.
I am a little bit confuse about this statement. From the question title, you are asking about unit-testing httpClint, by mocking a FakeHttpClient may help you unit-testing other part of app except httpClient, but doesn't help anything for unit-testing httpClient. What you need is a FakeHttpLayer for unit-testing httpClient (no remote server, network requires, hence unit-testing).
HttpClient Dummy Test:
If you only need examine app behavior in the situation that internet is lost, then a classic Android Instrument Test is sufficient, you can programmatically turn the Internet in the emulator off while performing the test:
public void testWhenInternetOK() {
... ...
webReader.readWebResource();
// expect HTTP 200 response.
... ...
}
public void testWhenInternetLost() {
... ...
wifiManager = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
wifiManager.setWifiEnabled(false);
webReader.readWebResource();
// expect no HTTP response.
... ...
}
This requires the remote http server is completely setup and in a working state, and whenever you run your test class, a real http communication is made over network and hit on http server.
HttpClient Advanced Test:
If you want to test app behavior more precisely, for instance, you want to test a http call in you app to see if it is handle different http response properly. the Robolectric is the best choice. You can use FakeHttpLayer and mock the http request and response to whatever you like.
public void setup() {
String url = "http://...";
// First http request fired in test, mock a HTTP 200 response (ContentType: application/json)
HttpResponse response1 = new DefaultHttpResponseFactory().newHttpResponse(HttpVersion.HTTP_1_1, 200, null);
BasicHttpEntity entity1 = new BasicHttpEntity();
entity1.setContentType("application/json");
response1.setEntity(entity1);
// Second http request fired in test, mock a HTTP 404 response (ContentType: text/html)
HttpResponse response2 = new DefaultHttpResponseFactory().newHttpResponse(HttpVersion.HTTP_1_1, 404, null);
BasicHttpEntity entity2 = new BasicHttpEntity();
entity2.setContentType("text/html");
response2.setEntity(entity2);
List<HttpResponse> responses = new ArrayList<HttpResponse>();
responses.add(response1);
responses.add(response2);
Robolectric.addHttpResponseRule(new FakeHttpLayer.UriRequestMatcher("POST", url), responses);
}
public void testFoo() {
... ...
webReader.readWebResource(); // <- a call that perform a http post request to url.
// expect HTTP 200 response.
... ...
}
public void testBar() {
... ...
webReader.readWebResource(); // <- a call that perform a http post request to url.
// expect HTTP 404 response.
... ...
}
Some pros of using Robolectric are:
Purely JUnit test, no instrument test so don't need start emulator (or real device) to run the test, increase development speed.
Latest Robolectric support single line of code to enable/disable FakeHttpLayer, where you can set http request to be interpreted by FakeHttpLayer (no real http call over network), or set the http request bypass the FakeHttpLayer(perform real http call over network). Check out this SO question for more details.
If you check out the source of Robolectric, you can see it is quite complex to implement a FakeHtppLayer properly by yourself. I would recommend to use the existing test framework instead of implementing your own API.
Hope this helps.
I'm trying to send some json data from Android to a clojure/compojure server
However I can't seem to able to properly send or receive the data, and I'm not quite sure if the problem lies with Android or compojure.
Here is the java code
String PATH = "http://localhost:8080/get_position";
DefaultHttpClient mClient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(PATH);
HttpResponse response;
httpget.getParams().setParameter("measurements", measurements.toString());
response = mClient.execute(httpget);
HttpEntity entity = response.getEntity();
Where mesurements is the JSON object.
And the main compojure code for handling the routing
(defroutes main-routes
(POST "/get_position" {params :params}
(emit-json (find-location (:results (read-json (:measurements params))))))
(route/not-found "Page not found"))
The request is properly received, but I get an error that params is nil
java.lang.IllegalArgumentException: No implementation of method: :read-json-from of protocol: #'clojure.data.json/Read-JSON-From found for class: nil
Does anyone see a problem with this code or knows the correct way to do this?
The params map has strings as keys, I believe, not keywords.
I recommend using ring-json-params.
I am sending a JSON object to a HTTP Server by using the following code.
The main thing is that I have to send Boolean values also.
public void getServerData() throws JSONException, ClientProtocolException, IOException {
ArrayList<String> stringData = new ArrayList<String>();
DefaultHttpClient httpClient = new DefaultHttpClient();
ResponseHandler <String> resonseHandler = new BasicResponseHandler();
HttpPost postMethod = new HttpPost("http://consulting.for-the.biz/TicketMasterDev/TicketService.svc/SaveCustomer");
JSONObject json = new JSONObject();
json.put("AlertEmail",true);
json.put("APIKey","abc123456789");
json.put("Id",0);
json.put("Phone",number.getText().toString());
json.put("Name",name.getText().toString());
json.put("Email",email.getText().toString());
json.put("AlertPhone",false);
postMethod.setEntity(new ByteArrayEntity(json.toString().getBytes("UTF8")));
String response = httpClient.execute(postMethod,resonseHandler);
Log.e("response :", response);
}
but its showing the exception in the line
String response = httpClient.execute(postMethod,resonseHandler);
as
org.apache.http.client.HttpResponseException: Bad Request
can any one help me.
The Bad Request is the server saying that it doesn't like something in your POST.
The only obvious problem that I can see is that you're not telling the server that you're sending it JSON, so you may need to set a Content-Type header to indicate that the body is application/json:
postMethod.setHeader( "Content-Type", "application/json" );
If that doesn't work, you may need to look at the server logs to see why it doesn't like your POST.
If you don't have direct access to the server logs, then you need to liaise with the owner of the server to try and debug things. It could be that the format of your JSON is slightly wrong, there's a required field missing, or some other such problem.
If you can't get access use to the owner of the server, the you could try using a packet sniffer, such as WireShark, to capture packets both from your app, and from a successful POST and compare the two to try and work out what is different. This can be a little bit like finding a needle in a haystack though, particularly for large bodies.
If you can't get an example of a successful POST, then you're pretty well stuffed, as you have no point of reference.
This may be non-technical, but
String response = httpClient.execute(postMethod,-->resonseHandler);
There is a spelling mistake in variable name here, use responseHandler(defined above)