Search in sources :

Example 1 with HttpPostResult

use of org.odk.collect.android.openrosa.HttpPostResult 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 HttpPostResult

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

the class OkHttpConnection method executePostRequest.

@NonNull
private HttpPostResult executePostRequest(@NonNull URI uri, @Nullable HttpCredentialsInterface credentials, MultipartBody multipartBody) throws Exception {
    OpenRosaServerClient httpClient = clientFactory.get(uri.getScheme(), userAgent, credentials);
    HttpPostResult postResult;
    Request request = new Request.Builder().url(uri.toURL()).post(multipartBody).build();
    Response response = httpClient.makeRequest(request, new Date());
    if (response.code() == 204) {
        throw new Exception();
    }
    postResult = new HttpPostResult(response.body().string(), response.code(), response.message());
    discardEntityBytes(response);
    return postResult;
}
Also used : Response(okhttp3.Response) OpenRosaServerClient(org.odk.collect.android.openrosa.OpenRosaServerClient) Request(okhttp3.Request) HttpPostResult(org.odk.collect.android.openrosa.HttpPostResult) Date(java.util.Date) NonNull(androidx.annotation.NonNull)

Example 3 with HttpPostResult

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

the class OkHttpConnection method uploadSubmissionAndFiles.

@NonNull
@Override
public HttpPostResult uploadSubmissionAndFiles(@NonNull File submissionFile, @NonNull List<File> fileList, @NonNull URI uri, @Nullable HttpCredentialsInterface credentials, @NonNull long contentLength) throws Exception {
    HttpPostResult postResult = null;
    boolean first = true;
    int fileIndex = 0;
    int lastFileIndex;
    while (fileIndex < fileList.size() || first) {
        lastFileIndex = fileIndex;
        first = false;
        long byteCount = 0L;
        RequestBody requestBody = RequestBody.create(MediaType.parse(HTTP_CONTENT_TYPE_TEXT_XML), submissionFile);
        MultipartBody.Builder multipartBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM).addPart(MultipartBody.Part.createFormData("xml_submission_file", submissionFile.getName(), requestBody));
        Timber.i("added xml_submission_file: %s", submissionFile.getName());
        byteCount += submissionFile.length();
        for (; fileIndex < fileList.size(); fileIndex++) {
            File file = fileList.get(fileIndex);
            String contentType = fileToContentTypeMapper.map(file.getName());
            RequestBody fileRequestBody = RequestBody.create(MediaType.parse(contentType), file);
            multipartBuilder.addPart(MultipartBody.Part.createFormData(file.getName(), file.getName(), fileRequestBody));
            byteCount += file.length();
            Timber.i("added file of type '%s' %s", contentType, file.getName());
            // we've added at least one attachment to the request...
            if (fileIndex + 1 < fileList.size()) {
                if ((fileIndex - lastFileIndex + 1 > 100) || (byteCount + fileList.get(fileIndex + 1).length() > contentLength)) {
                    // the next file would exceed the 10MB threshold...
                    Timber.i("Extremely long post is being split into multiple posts");
                    multipartBuilder.addPart(MultipartBody.Part.createFormData("*isIncomplete*", "yes"));
                    // advance over the last attachment added...
                    ++fileIndex;
                    break;
                }
            }
        }
        MultipartBody multipartBody = multipartBuilder.build();
        postResult = executePostRequest(uri, credentials, multipartBody);
        if (postResult.getResponseCode() != HttpURLConnection.HTTP_CREATED && postResult.getResponseCode() != HttpURLConnection.HTTP_ACCEPTED) {
            return postResult;
        }
    }
    return postResult;
}
Also used : MultipartBody(okhttp3.MultipartBody) HttpPostResult(org.odk.collect.android.openrosa.HttpPostResult) File(java.io.File) RequestBody(okhttp3.RequestBody) NonNull(androidx.annotation.NonNull)

Aggregations

HttpPostResult (org.odk.collect.android.openrosa.HttpPostResult)3 NonNull (androidx.annotation.NonNull)2 File (java.io.File)2 Uri (android.net.Uri)1 UnsupportedEncodingException (java.io.UnsupportedEncodingException)1 URI (java.net.URI)1 Date (java.util.Date)1 MultipartBody (okhttp3.MultipartBody)1 Request (okhttp3.Request)1 RequestBody (okhttp3.RequestBody)1 Response (okhttp3.Response)1 CaseInsensitiveHeaders (org.odk.collect.android.openrosa.CaseInsensitiveHeaders)1 HttpHeadResult (org.odk.collect.android.openrosa.HttpHeadResult)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