Search in sources :

Example 1 with HttpHeadResult

use of org.odk.collect.android.openrosa.HttpHeadResult in project collect by opendatakit.

the class InstanceServerUploader method uploadOneSubmission.

/**
 * Uploads all files associated with an instance to the specified URL. Writes fail/success
 * status to database.
 * <p>
 * Returns a custom success message if one is provided by the server.
 */
@Override
public String uploadOneSubmission(Instance instance, String urlString) throws UploadException {
    Uri submissionUri = Uri.parse(urlString);
    long contentLength = 10000000L;
    // the proper scheme.
    if (uriRemap.containsKey(submissionUri)) {
        submissionUri = uriRemap.get(submissionUri);
        Timber.i("Using Uri remap for submission %s. Now: %s", instance.getDbId(), submissionUri.toString());
    } else {
        if (submissionUri.getHost() == null) {
            submissionComplete(instance, false);
            throw new UploadException(FAIL + "Host name may not be null");
        }
        URI uri;
        try {
            uri = URI.create(submissionUri.toString());
        } catch (IllegalArgumentException e) {
            submissionComplete(instance, false);
            Timber.d(e.getMessage() != null ? e.getMessage() : e.toString());
            throw new UploadException(getLocalizedString(Collect.getInstance(), R.string.url_error));
        }
        HttpHeadResult headResult;
        CaseInsensitiveHeaders responseHeaders;
        try {
            headResult = httpInterface.executeHeadRequest(uri, webCredentialsUtils.getCredentials(uri));
            responseHeaders = headResult.getHeaders();
            if (responseHeaders.containsHeader(OpenRosaConstants.ACCEPT_CONTENT_LENGTH_HEADER)) {
                String contentLengthString = responseHeaders.getAnyValue(OpenRosaConstants.ACCEPT_CONTENT_LENGTH_HEADER);
                try {
                    contentLength = Long.parseLong(contentLengthString);
                } catch (Exception e) {
                    Timber.e(e, "Exception thrown parsing contentLength %s", contentLengthString);
                }
            }
        } catch (Exception e) {
            submissionComplete(instance, false);
            throw new UploadException(FAIL + (e.getMessage() != null ? e.getMessage() : e.toString()));
        }
        if (headResult.getStatusCode() == HttpsURLConnection.HTTP_UNAUTHORIZED) {
            submissionComplete(instance, false);
            throw new UploadAuthRequestedException(getLocalizedString(Collect.getInstance(), R.string.server_auth_credentials, submissionUri.getHost()), submissionUri);
        } else if (headResult.getStatusCode() == HttpsURLConnection.HTTP_NO_CONTENT) {
            // Redirect header received
            if (responseHeaders.containsHeader("Location")) {
                try {
                    Uri newURI = Uri.parse(URLDecoder.decode(responseHeaders.getAnyValue("Location"), "utf-8"));
                    // Allow redirects within same host. This could be redirecting to HTTPS.
                    if (submissionUri.getHost().equalsIgnoreCase(newURI.getHost())) {
                        // Re-add params if server didn't respond with params
                        if (newURI.getQuery() == null) {
                            newURI = newURI.buildUpon().encodedQuery(submissionUri.getEncodedQuery()).build();
                        }
                        uriRemap.put(submissionUri, newURI);
                        submissionUri = newURI;
                    } else {
                        // Don't follow a redirection attempt to a different host.
                        // We can't tell if this is a spoof or not.
                        submissionComplete(instance, false);
                        throw new UploadException(FAIL + "Unexpected redirection attempt to a different host: " + newURI.toString());
                    }
                } catch (Exception e) {
                    submissionComplete(instance, false);
                    throw new UploadException(FAIL + urlString + " " + e.toString());
                }
            }
        } else {
            if (headResult.getStatusCode() >= HttpsURLConnection.HTTP_OK && headResult.getStatusCode() < HttpsURLConnection.HTTP_MULT_CHOICE) {
                submissionComplete(instance, false);
                throw new UploadException("Failed to send to " + uri + ". Is this an OpenRosa " + "submission endpoint? If you have a web proxy you may need to log in to " + "your network.\n\nHEAD request result status code: " + headResult.getStatusCode());
            }
        }
    }
    // When encrypting submissions, there is a failure window that may mark the submission as
    // complete but leave the file-to-be-uploaded with the name "submission.xml" and the plaintext
    // submission files on disk.  In this case, upload the submission.xml and all the files in
    // the directory. This means the plaintext files and the encrypted files will be sent to the
    // server and the server will have to figure out what to do with them.
    File instanceFile = new File(instance.getInstanceFilePath());
    File submissionFile = new File(instanceFile.getParentFile(), "submission.xml");
    if (submissionFile.exists()) {
        Timber.w("submission.xml will be uploaded instead of %s", instanceFile.getAbsolutePath());
    } else {
        submissionFile = instanceFile;
    }
    if (!instanceFile.exists() && !submissionFile.exists()) {
        submissionComplete(instance, false);
        throw new UploadException(FAIL + "instance XML file does not exist!");
    }
    List<File> files = getFilesInParentDirectory(instanceFile, submissionFile);
    // TODO: when can this happen? It used to cause the whole submission attempt to fail. Should it?
    if (files == null) {
        throw new UploadException("Error reading files to upload");
    }
    HttpPostResult postResult;
    ResponseMessageParser messageParser = new ResponseMessageParser();
    try {
        URI uri = URI.create(submissionUri.toString());
        postResult = httpInterface.uploadSubmissionAndFiles(submissionFile, files, uri, webCredentialsUtils.getCredentials(uri), contentLength);
        int responseCode = postResult.getResponseCode();
        messageParser.setMessageResponse(postResult.getHttpResponse());
        if (responseCode != HttpsURLConnection.HTTP_CREATED && responseCode != HttpsURLConnection.HTTP_ACCEPTED) {
            UploadException exception;
            if (responseCode == HttpsURLConnection.HTTP_OK) {
                exception = new UploadException(FAIL + "Network login failure? Again?");
            } else if (responseCode == HttpsURLConnection.HTTP_UNAUTHORIZED) {
                exception = new UploadException(FAIL + postResult.getReasonPhrase() + " (" + responseCode + ") at " + urlString);
            } else {
                if (messageParser.isValid()) {
                    exception = new UploadException(FAIL + messageParser.getMessageResponse());
                } else if (responseCode == HttpsURLConnection.HTTP_BAD_REQUEST) {
                    Timber.w(FAIL + postResult.getReasonPhrase() + " (" + responseCode + ") at " + urlString);
                    exception = new UploadException("Failed to upload. Please make sure the form is configured to accept submissions on the server");
                } else {
                    exception = new UploadException(FAIL + postResult.getReasonPhrase() + " (" + responseCode + ") at " + urlString);
                }
            }
            submissionComplete(instance, false);
            throw exception;
        }
    } catch (Exception e) {
        submissionComplete(instance, false);
        throw new UploadException(FAIL + "Generic Exception: " + (e.getMessage() != null ? e.getMessage() : e.toString()));
    }
    submissionComplete(instance, true);
    if (messageParser.isValid()) {
        return messageParser.getMessageResponse();
    }
    return null;
}
Also used : ResponseMessageParser(org.odk.collect.android.utilities.ResponseMessageParser) LocalizedApplicationKt.getLocalizedString(org.odk.collect.strings.localization.LocalizedApplicationKt.getLocalizedString) Uri(android.net.Uri) URI(java.net.URI) HttpHeadResult(org.odk.collect.android.openrosa.HttpHeadResult) UnsupportedEncodingException(java.io.UnsupportedEncodingException) CaseInsensitiveHeaders(org.odk.collect.android.openrosa.CaseInsensitiveHeaders) File(java.io.File) HttpPostResult(org.odk.collect.android.openrosa.HttpPostResult)

Example 2 with HttpHeadResult

use of org.odk.collect.android.openrosa.HttpHeadResult in project collect by opendatakit.

the class StubOpenRosaServer method executeHeadRequest.

@NonNull
@Override
public HttpHeadResult executeHeadRequest(@NonNull URI uri, @Nullable HttpCredentialsInterface credentials) throws Exception {
    if (alwaysReturnError) {
        return new HttpHeadResult(500, new CaseInsensitiveEmptyHeaders());
    }
    if (!uri.getHost().equals(HOST)) {
        return new HttpHeadResult(410, new CaseInsensitiveEmptyHeaders());
    } else if (credentialsIncorrect(credentials)) {
        return new HttpHeadResult(401, new CaseInsensitiveEmptyHeaders());
    } else if (uri.getPath().equals(OpenRosaConstants.SUBMISSION)) {
        HashMap<String, String> headers = getStandardHeaders();
        headers.put("x-openrosa-accept-content-length", "10485760");
        return new HttpHeadResult(204, new MapHeaders(headers));
    } else {
        return new HttpHeadResult(404, new CaseInsensitiveEmptyHeaders());
    }
}
Also used : CaseInsensitiveEmptyHeaders(org.odk.collect.android.openrosa.CaseInsensitiveEmptyHeaders) HttpHeadResult(org.odk.collect.android.openrosa.HttpHeadResult) NonNull(androidx.annotation.NonNull)

Example 3 with HttpHeadResult

use of org.odk.collect.android.openrosa.HttpHeadResult in project collect by opendatakit.

the class OkHttpConnection method executeHeadRequest.

@NonNull
@Override
public HttpHeadResult executeHeadRequest(@NonNull URI uri, @Nullable HttpCredentialsInterface credentials) throws Exception {
    OpenRosaServerClient httpClient = clientFactory.get(uri.getScheme(), userAgent, credentials);
    Request request = new Request.Builder().url(uri.toURL()).head().build();
    Timber.i("Issuing HEAD request to: %s", uri.toString());
    Response response = httpClient.makeRequest(request, new Date());
    int statusCode = response.code();
    CaseInsensitiveHeaders responseHeaders = new CaseInsensitiveEmptyHeaders();
    if (statusCode == HttpURLConnection.HTTP_NO_CONTENT) {
        responseHeaders = new OkHttpCaseInsensitiveHeaders(response.headers());
    }
    discardEntityBytes(response);
    return new HttpHeadResult(statusCode, responseHeaders);
}
Also used : Response(okhttp3.Response) OpenRosaServerClient(org.odk.collect.android.openrosa.OpenRosaServerClient) CaseInsensitiveHeaders(org.odk.collect.android.openrosa.CaseInsensitiveHeaders) CaseInsensitiveEmptyHeaders(org.odk.collect.android.openrosa.CaseInsensitiveEmptyHeaders) Request(okhttp3.Request) HttpHeadResult(org.odk.collect.android.openrosa.HttpHeadResult) Date(java.util.Date) NonNull(androidx.annotation.NonNull)

Aggregations

HttpHeadResult (org.odk.collect.android.openrosa.HttpHeadResult)3 NonNull (androidx.annotation.NonNull)2 CaseInsensitiveEmptyHeaders (org.odk.collect.android.openrosa.CaseInsensitiveEmptyHeaders)2 CaseInsensitiveHeaders (org.odk.collect.android.openrosa.CaseInsensitiveHeaders)2 Uri (android.net.Uri)1 File (java.io.File)1 UnsupportedEncodingException (java.io.UnsupportedEncodingException)1 URI (java.net.URI)1 Date (java.util.Date)1 Request (okhttp3.Request)1 Response (okhttp3.Response)1 HttpPostResult (org.odk.collect.android.openrosa.HttpPostResult)1 OpenRosaServerClient (org.odk.collect.android.openrosa.OpenRosaServerClient)1 ResponseMessageParser (org.odk.collect.android.utilities.ResponseMessageParser)1 LocalizedApplicationKt.getLocalizedString (org.odk.collect.strings.localization.LocalizedApplicationKt.getLocalizedString)1