Подключение Qt с помощью SSL к серверу Jetty

У меня есть некоторые проблемы с подключением клиента qt к встроенному серверу Jetty.

Сначала я использую следующие компоненты:

Qt 4.4.3 (скомпилировано с активной поддержкой openssl) jetty 8.8.1 java 6

Я знаю, что версии не самые последние, но из-за проблем с лицензированием и пожеланиями клиентов я не могу использовать более новую.

Таким образом, сценарий заключается в том, что клиент qt должен отправлять запросы HTTP GET и POST на сервер Jetty. Пока я использую простой http с объектом QHttp, он работает нормально, проблемы начинаются, когда я переключаюсь на SSL.

Моей первой попыткой было использование объекта QSslSocket для запроса GET:

// Load certs + private key to socket   
_pSocket = new QSslSocket(this);
_pSocket->setLocalCertificate(_certificate);
_pSocket->setPrivateKey(_privatekey);
_pSocket->addDefaultCaCertificate(_cacertificate);

connect (_pSocket, SIGNAL(encrypted()), this, SLOT(_encrypted()));
_pSocket->connectToHostEncrypted("localhost", 8000);

со следующей функцией слота для зашифрованного состояния:

void TestClient::_encrypted() {
    QString _path("/testpath/list");
    QByteArray buffer("GET ");
    buffer.append(_path).append(" HTTP/1.1\r\n");
    _pSocket->write(buffer);
}

Здесь у меня есть первая проблема:

Это приводит к следующей строке, которая, насколько я вижу, соответствует RFC 2616:

"GET /testpath/list HTTP/1.1\r\n"

По какой-то причине у сервера Jetty есть проблема, связанная с тем, что клиент остается в цикле, пока клиент не закроет соединение из-за истечения времени ожидания.

Но если я использую следующую строку, она прекрасно работает:

"GET /testpath/list\r\n"

Вот мой первый вопрос: есть ли у вас объяснение этому поведению? Я могу жить с этим, но я хочу знать причину

Моя вторая проблема - запрос POST, он всегда терпит неудачу.

Эти примеры я уже пробовал:

"POST /testpath/receive/\r\n{"data":"hello world ?!"}\r\n"
"POST /testpath/receive/ HTTP/1.1\r\n{"data":"hello world ?!"}\r\n"
"POST /testpath/receive/\r\n\r\n{"data":"hello world ?!"}\r\n"
"POST /testpath/receive/ HTTP/1.1\r\n\r\n{"data":"hello world ?!"}\r\n"

У меня такое ощущение, что тело каждый раз пусто, поэтому мой сервер падает, потому что он пытается разобрать пустую строку как json. По крайней мере, следующий журнал показывает, что:

2013-11-19 17:11:51.671, INFO, foo.bar.RepositoryHandler, qtp11155366-16 - /testpath/receive request type : /receive
2013-11-19 17:11:51.811, ERROR, foo.bar.RepositoryHandler, qtp11155366-16 - /testpath/receive missing or unknown elements in JSON request. Check JSON against documentation
2013-11-19 17:11:51.874, WARN, org.eclipse.jetty.server.AbstractHttpConnection, qtp11155366-16 - /testpath/receive /testpath/receive
java.lang.NullPointerException: null
    at foo.bar.RepositoryHandler.decodeViewingRequest(RepositoryHandler.java:366) ~[MyServer.jar:na]
    at foo.bar.RepositoryHandler.handle(RepositoryHandler.java:182) ~[MyServer.jar:na]

Итак, все вместе, я думаю, у меня есть несколько серьезных ошибок в моих запросах. А какой?

Моя вторая попытка состояла в том, чтобы использовать объект QHttp и изменить QSocket, который он использует, с QSslSocket, который я уже инициировал.

Вот код основной функции:

QSslSocket* _pSocket;
QHttp* _pHttp;
int _id;
QBuffer* _pBuffer;
QByteArray _data;   

_pSocket = new QSslSocket(this);
_pSocket->setLocalCertificate(_certificate);
_pSocket->setPrivateKey(_privatekey);
_pSocket->addDefaultCaCertificate(_cacertificate);

QUrl url;
url.setScheme("https");
url.setHost("localhost");
url.setPort(8001);
url.setPath("/testpath/receive");

connect (_pSocket, SIGNAL(encrypted()), this, SLOT(_encrypted()));
connect(_pHttp,SIGNAL(requestFinished(int,bool)),this,SLOT(_requestFinished(int,bool))); 
connect(_pHttp,SIGNAL(done(bool)),this,SLOT(_done(bool)));

_pBuffer = new QBuffer(&_data);
_pHttp->setSocket(_pSocket);
_pSocket->connectToHostEncrypted(strHost, strPort.toInt());
_id = _pHttp->get(url.toString(),_pBuffer);

И обратные вызовы:

void _requestFinished(int id, bool error) {
    if(id = _id)
        qDebug() << "data=" << _data;
}

void _encrypted() {
    qDebug() << "encrypted";
}

void _done(bool error) {
    logInfo() << "_done";

    if(_pHttp) {
        _pHttp->abort();
        delete _pHttp;
        _pHttp = 0;
    }
    if(_pBuffer) {
        delete _pBuffer;
        _pBuffer = 0;
    }    

    if(_pSocket) {
        _pSocket->disconnectFromHost();
        delete _pSocket;
        _pSocket = 0;
    }
}

Я думаю, мне нужно только изменить позицию вызова _pHttp->get, возможно, в обратном вызове _encrypted, но я не уверен.

Хороший совет?

Спасибо роберт

1 ответ

Решение

Ваш HTTP-запрос является неполным, согласно RFC2616.

"GET /testpath/list HTTP/1.1\r\n"

Это неверно.

Попробуйте это вместо этого.

"GET /testpath/list HTTP/1.1\r\n" + /* request line (required) */
"Host: localhost\r\n" + /* host header (required minimum) */
"\r\n" /* terminating CR + LF (required) */

Как указано в разделе 5.1.2

   The most common form of Request-URI is that used to identify a
   resource on an origin server or gateway. In this case the absolute
   path of the URI MUST be transmitted (see section 3.2.1, abs_path) as
   the Request-URI, and the network location of the URI (authority) MUST
   be transmitted in a Host header field. For example, a client wishing
   to retrieve the resource above directly from the origin server would
   create a TCP connection to port 80 of the host "www.w3.org" and send
   the lines:

       GET /pub/WWW/TheProject.html HTTP/1.1
       Host: www.w3.org

Строка Request-URI и заголовок заголовка узла являются обязательными.

Другие вопросы по тегам