简体   繁体   English

通过TLS将HTTP2帧发送到服务器

[英]Send HTTP2 frame to server over TLS

I am trying to learn how the HTTP2 protocol works. 我正在尝试学习HTTP2协议的工作方式。 I see that Apple uses it for their push notification server. 我看到苹果公司将其用于推送通知服务器。 I am using the specification for the frame from Discover HTTP . 我正在使用Discover HTTP中的框架规范。

As a test, I wrote code that is supposed to communicate with that server. 作为测试,我编写了应该与该服务器通信的代码。 However, I keep getting the error that I'm missing the "Settings" frame. 但是,我不断收到我丢失“设置”框架的错误。

My code is as follows: 我的代码如下:

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dlfcn.h>
#include <unistd.h>
#include <openssl/ssl.h>

#define SOCKET_ERROR -1

struct Frame  //Represents and HTTP2 Frame.
{
    char len[24];
    char type[8];
    char flags[8];
    char identifier[31];
    char payload[];
} __attribute__((packed));

void writeFrame(SSL* ssl)
{
    //First thing after connecting is to send the PREFACE.
    std::string preface = "PRI * HTTP/2.0\r\n\r\n";
    preface += "SM\r\n\r\n";

    SSL_write(ssl, preface.c_str(), preface.length());

    //Now to send the first frame. Aka the SETTINGS frame.
    Frame* frame = (Frame *)malloc(sizeof(Frame));
    memset(frame, 0, sizeof(Frame));

    int frameSize = 100;
    memcpy(frame->len, &frameSize, sizeof(frameSize));
    memcpy(frame->type, "SETTINGS", strlen("SETTINGS"));
    memcpy(frame->identifier, "SETTINGS", strlen("SETTINGS"));

    SSL_write(ssl, frame, sizeof(Frame));

    //Read server response.
    char buffer[10000];
    memset(buffer, 0, sizeof(buffer));
    int dataLen;
    while ((dataLen = SSL_read(ssl, buffer, sizeof(buffer)) > 0))
    {
        int i = 0;
        while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r') {
            std::cout << buffer[i];
            i += 1;
        }
    }

    //The above gives me the error: 
    //First received frame was not SETTINGS. 
    //Hex dump for first 5 bytes: 6400000000

    //Try to POST to the server now.
    std::string payload = "POST /3/device HTTP/2.0\r\n";
    payload += ":method:POST\r\n";
    payload += ":scheme: https\r\n";
    payload += "cache-control: no-cache\r\n";
    payload += "user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36\r\n";
    payload += "content-type: text/plain;charset=UTF-8\r\n\r\n";

    int sentBytes = SSL_write(ssl, payload.c_str(), payload.length());

    if (sentBytes < payload.length() || sentBytes == SOCKET_ERROR)
    {
        return;
    }        

    memset(buffer, 0, sizeof(buffer));
    while ((dataLen = SSL_read(ssl, buffer, sizeof(buffer)) > 0))
    {
        int i = 0;
        while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r') {
            std::cout << buffer[i];
            i += 1;
        }
    }
}

int main(int argc, const char * argv[]) {

    SSL_library_init();
    SSL_load_error_strings();
    OpenSSL_add_all_algorithms();

    std::string address = "api.development.push.apple.com";
    struct addrinfo hints = {0};
    struct addrinfo* result = nullptr;

    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    getaddrinfo(address.c_str(), nullptr, &hints, &result);

    int sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (sock == -1)
    {
        return -1;
    }

    struct sockaddr_in sockAddr;
    sockAddr.sin_addr = reinterpret_cast<struct sockaddr_in*>(result->ai_addr)->sin_addr;
    sockAddr.sin_family = result->ai_family;
    sockAddr.sin_port = htons(443);
    freeaddrinfo(result);

    if (connect(sock, reinterpret_cast<sockaddr *>(&sockAddr), sizeof(sockAddr)) == SOCKET_ERROR)
    {
        close(sock);
        return -1;
    }

    SSL_CTX* ctx = SSL_CTX_new(TLSv1_2_method());
    SSL* ssl = SSL_new(ctx);
    SSL_set_fd(ssl, sock);
    SSL_connect(ssl);

    writeFrame(ssl);

    SSL_CTX_free(ctx);
    SSL_shutdown(ssl);
    SSL_free(ssl);
    close(sock);
    return 0;
}

How can I send the SETTINGS frame and other frames to the server? 如何将SETTINGS框架和其他框架发送到服务器? What have I missed? 我错过了什么?

You have several errors. 您有几个错误。

First of all, the Frame data structure, where the lengths of the array fields are wrong. 首先是Frame数据结构,其中数组字段的长度是错误的。 You seem to have copied their length in bits, but reported them in the Frame data structure in bytes. 您似乎已经复制了它们的长度(以位为单位),但在Frame数据结构中以字节为单位报告了它们。 You want this instead: 您想要这个:

struct Frame {
    char len[3];
    char type;
    char flags;
    char identifier[4];
    char payload[];
}

Furthermore, the type of the frame is not a string, nor is the identifier. 此外,帧的类型不是字符串,也不是标识符。

Finally, the format of the request is completely wrong, resembling HTTP/1.1, while the HTTP/2 format is completely different and based on HPACK. 最后,请求的格式完全错误,类似于HTTP / 1.1,而HTTP / 2格式则完全不同且基于HPACK。

I suggest you have a careful read of the HTTP/2 specification before writing further code. 我建议您在编写进一步的代码之前,仔细阅读HTTP / 2规范

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM