diff --git a/src/core/url_request_custom_job.cpp b/src/core/url_request_custom_job.cpp
index 0b8aaf9ba455395dbf130fddd27886f6a258a77c..cd71b6900afca6314d6b243ae9ec95326d145599 100644
--- a/src/core/url_request_custom_job.cpp
+++ b/src/core/url_request_custom_job.cpp
@@ -117,6 +117,18 @@ bool URLRequestCustomJob::GetCharset(std::string* charset)
     return false;
 }
 
+bool URLRequestCustomJob::IsRedirectResponse(GURL* location, int* http_status_code)
+{
+    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+    QMutexLocker lock(&m_mutex);
+    if (m_redirect.is_valid()) {
+        *location = m_redirect;
+        *http_status_code = 303;
+        return true;
+    }
+    return false;
+}
+
 void URLRequestCustomJob::setReplyMimeType(const std::string &mimeType)
 {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -158,6 +170,36 @@ bool URLRequestCustomJob::ReadRawData(IOBuffer *buf, int bufSize, int *bytesRead
     return false;
 }
 
+void URLRequestCustomJob::redirect(const GURL &url)
+{
+    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+    if (m_device || m_error)
+        return;
+
+    QMutexLocker lock(&m_mutex);
+    m_redirect = url;
+    content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, base::Bind(&URLRequestCustomJob::notifyStarted, m_weakFactory.GetWeakPtr()));
+}
+
+void URLRequestCustomJob::abort()
+{
+    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+    QMutexLocker lock(&m_mutex);
+    if (m_device && m_device->isOpen())
+        m_device->close();
+    m_device = 0;
+    content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, base::Bind(&URLRequestCustomJob::notifyCanceled, m_weakFactory.GetWeakPtr()));
+}
+
+void URLRequestCustomJob::notifyCanceled()
+{
+    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+    if (m_started)
+        NotifyDone(URLRequestStatus(URLRequestStatus::CANCELED, ERR_ABORTED));
+    else
+        NotifyStartError(URLRequestStatus(URLRequestStatus::CANCELED, ERR_ABORTED));
+}
+
 void URLRequestCustomJob::notifyStarted()
 {
     DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
diff --git a/src/core/url_request_custom_job.h b/src/core/url_request_custom_job.h
index ca20c719d90a4200aa203be76f78c5dafc9b378b..4975e71d2306566a7964beb337f2d648ea9ad05c 100644
--- a/src/core/url_request_custom_job.h
+++ b/src/core/url_request_custom_job.h
@@ -60,18 +60,22 @@ public:
     virtual bool ReadRawData(net::IOBuffer *buf, int bufSize, int *bytesRead) Q_DECL_OVERRIDE;
     virtual bool GetMimeType(std::string *mimeType) const Q_DECL_OVERRIDE;
     virtual bool GetCharset(std::string *charset) Q_DECL_OVERRIDE;
+    virtual bool IsRedirectResponse(GURL* location, int* http_status_code) Q_DECL_OVERRIDE;
 
     void setReplyMimeType(const std::string &);
     void setReplyCharset(const std::string &);
     void setReplyDevice(QIODevice *);
 
+    void redirect(const GURL &url);
     void fail(int);
+    void abort();
 
 protected:
     virtual ~URLRequestCustomJob();
     void startAsync();
     void notifyStarted();
     void notifyFailure();
+    void notifyCanceled();
 
 private:
     QMutex m_mutex;
@@ -81,6 +85,7 @@ private:
     std::string m_mimeType;
     std::string m_charset;
     int m_error;
+    GURL m_redirect;
     bool m_started;
     base::WeakPtrFactory<URLRequestCustomJob> m_weakFactory;
 
diff --git a/src/core/url_request_custom_job_delegate.cpp b/src/core/url_request_custom_job_delegate.cpp
index d324da34724922f395033ac166181628e3876046..caf7a0e3ef30f1db583a64abacc0c5962ff0a767 100644
--- a/src/core/url_request_custom_job_delegate.cpp
+++ b/src/core/url_request_custom_job_delegate.cpp
@@ -38,6 +38,7 @@
 #include "url_request_custom_job_delegate.h"
 
 #include "type_conversion.h"
+#include "net/base/net_errors.h"
 
 #include <QByteArray>
 
@@ -63,4 +64,40 @@ void URLRequestCustomJobDelegate::setReply(const QByteArray &contentType, QIODev
     m_job->setReplyDevice(device);
 }
 
+void URLRequestCustomJobDelegate::abort()
+{
+    m_job->abort();
+}
+
+void URLRequestCustomJobDelegate::redirect(const QUrl &url)
+{
+    m_job->redirect(toGurl(url));
+}
+
+void URLRequestCustomJobDelegate::fail(Error error)
+{
+    int net_error =  0;
+    switch (error) {
+    case NoError:
+        break;
+    case UrlInvalid:
+        net_error = net::ERR_INVALID_URL;
+        break;
+    case UrlNotFound:
+        net_error = net::ERR_FILE_NOT_FOUND;
+        break;
+    case RequestAborted:
+        net_error = net::ERR_ABORTED;
+        break;
+    case RequestDenied:
+        net_error = net::ERR_ACCESS_DENIED;
+        break;
+    case RequestFailed:
+        net_error = net::ERR_FAILED;
+        break;
+    }
+    if (net_error)
+        m_job->fail(net_error);
+}
+
 } // namespace
diff --git a/src/core/url_request_custom_job_delegate.h b/src/core/url_request_custom_job_delegate.h
index e0b8028978c0c2d0c5c1721103f6249add7ff6f9..580149ecf90409f06a4b5d3761f91e596850d2a6 100644
--- a/src/core/url_request_custom_job_delegate.h
+++ b/src/core/url_request_custom_job_delegate.h
@@ -53,9 +53,22 @@ class QWEBENGINE_EXPORT URLRequestCustomJobDelegate : public QObject {
 public:
     ~URLRequestCustomJobDelegate();
 
+    enum Error {
+        NoError = 0,
+        UrlNotFound,
+        UrlInvalid,
+        RequestAborted,
+        RequestDenied,
+        RequestFailed
+    };
+
     QUrl url() const;
 
     void setReply(const QByteArray &contentType, QIODevice *device);
+    void redirect(const QUrl& url);
+    void abort();
+
+    void fail(Error);
 
 private:
     URLRequestCustomJobDelegate(URLRequestCustomJob *job);
diff --git a/src/webenginewidgets/api/qwebengineurlrequestjob.cpp b/src/webenginewidgets/api/qwebengineurlrequestjob.cpp
index 323cdcc7259b6576df55eb2c9889e3b777c02b7b..4a813236cc9fdebf96fcf5422b098f691094e6cd 100644
--- a/src/webenginewidgets/api/qwebengineurlrequestjob.cpp
+++ b/src/webenginewidgets/api/qwebengineurlrequestjob.cpp
@@ -40,6 +40,8 @@
 
 #include "url_request_custom_job_delegate.h"
 
+using QtWebEngineCore::URLRequestCustomJobDelegate;
+
 QT_BEGIN_NAMESPACE
 
 /*!
@@ -51,7 +53,7 @@ QT_BEGIN_NAMESPACE
     A QWebEngineUrlRequestJob is given to QWebEngineUrlSchemeHandler::requestStarted() and must
     be handled by the derived implementations of class.
 
-    A job can be handled by calling setReply().
+    A job can be handled by calling either setReply(), redirect() or setError().
 
     The class is owned by QtWebEngine and does not need to be deleted. Note QtWebEngine may delete
     the job when it is no longer needed, so the signal QObject::destroyed() must be monitored if
@@ -63,7 +65,7 @@ QT_BEGIN_NAMESPACE
 /*!
     \internal
  */
-QWebEngineUrlRequestJob::QWebEngineUrlRequestJob(QtWebEngineCore::URLRequestCustomJobDelegate * p)
+QWebEngineUrlRequestJob::QWebEngineUrlRequestJob(URLRequestCustomJobDelegate * p)
     : QObject(p) // owned by the jobdelegate and deleted when the job is done
     , d_ptr(p)
 {
@@ -92,6 +94,20 @@ void QWebEngineUrlRequestJob::setReply(const QByteArray &contentType, QIODevice
     d_ptr->setReply(contentType, device);
 }
 
+/*!
+    Fails the request with error \a error.
+ */
+void QWebEngineUrlRequestJob::setError(Error r)
+{
+    d_ptr->fail((URLRequestCustomJobDelegate::Error)r);
+}
 
+/*!
+    Tell the request is redirected to \a url.
+ */
+void QWebEngineUrlRequestJob::setRedirect(const QUrl &url)
+{
+    d_ptr->redirect(url);
+}
 
 QT_END_NAMESPACE
diff --git a/src/webenginewidgets/api/qwebengineurlrequestjob_p.h b/src/webenginewidgets/api/qwebengineurlrequestjob_p.h
index 72c9dc836c5feb51ec7c27c39d59107fdfaad2b6..e496e4051273cd5a76aff02fe26499e8169e4258 100644
--- a/src/webenginewidgets/api/qwebengineurlrequestjob_p.h
+++ b/src/webenginewidgets/api/qwebengineurlrequestjob_p.h
@@ -56,8 +56,19 @@ class QWEBENGINEWIDGETS_EXPORT QWebEngineUrlRequestJob : public QObject {
 public:
     ~QWebEngineUrlRequestJob();
 
+    enum Error {
+        NoError = 0,
+        UrlNotFound,
+        UrlInvalid,
+        RequestAborted,
+        RequestDenied,
+        RequestFailed
+    };
+
     QUrl requestUrl() const;
     void setReply(const QByteArray &contentType, QIODevice *device);
+    void setError(Error error);
+    void setRedirect(const QUrl &url);
 
 private:
     QWebEngineUrlRequestJob(QtWebEngineCore::URLRequestCustomJobDelegate *);