Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting LWS_CALLBACK_CLOSED_CLIENT_HTTP all of a sudden #3107

Open
vahagnIV opened this issue Apr 8, 2024 · 3 comments
Open

Getting LWS_CALLBACK_CLOSED_CLIENT_HTTP all of a sudden #3107

vahagnIV opened this issue Apr 8, 2024 · 3 comments

Comments

@vahagnIV
Copy link

vahagnIV commented Apr 8, 2024

Hey guys,
I've been trying to connect to a websocket as a client for a day now. I know this sounds desperate but cold you please help me figure out what's wrong and explain what am I doing at all.

So, my goal is to connect to a certain websocket and send custom HTTP headers. Based on the minimal examples I arrived to the following code

static int
callback_http(struct lws *wsi, enum lws_callback_reasons reason,
              void *user, void *in, size_t len) {

  auto data = reinterpret_cast<MyStruct *>(user);

  switch(reason){
    case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: {
        unsigned char **p = (unsigned char **) in, *end = (*p) + len;
        if (0 != lws_add_http_header_by_token(wsi,
                                            WSI_TOKEN_HTTP_COOKIE,
                                            (const unsigned char *) data->cookie.c_str(), data->cookie.length(), p, end)
          || 0 != lws_add_http_header_by_token(wsi,
                                               WSI_TOKEN_VERSION,
                                               (const unsigned char *) "13", 2, p, end)
          || 0 != lws_add_http_header_by_token(wsi,
                                               WSI_TOKEN_HTTP_USER_AGENT,
                                               (const unsigned char *) data->user_agent.c_str(),
                                               data->user_agent.length(),
                                               p,
                                               end)
          || 0 != lws_add_http_header_by_name(wsi,
                                              (const unsigned char *) "Connection:",
                                              (const unsigned char *) "Upgrade",
                                              7,
                                              p,
                                              end)
          || 0 != lws_add_http_header_by_name(wsi,
                                              (const unsigned char *) "Upgrade:",
                                              (const unsigned char *) "websocket",
                                              7,
                                              p,
                                              end)
         )
          return -1;
        }
    break;
  case LWS_CALLBACK_CLIENT_WRITEABLE: {
    // SEND MESSAGE
    }
      break;

  }
  return 0;
}

static const struct lws_protocols protocols[] = {
    {
        "wss",
        callback_http,
        0, 0, 0, NULL, 0
    },
    LWS_PROTOCOL_LIST_TERM
};

static int
system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
                 int current, int target) {
  auto *context = reinterpret_cast<lws_context *>(mgr->parent);
  lws_client_connect_info i;

  if (current != LWS_SYSTATE_OPERATIONAL || target != LWS_SYSTATE_OPERATIONAL)
    return 0;

  lwsl_info("%s: operational\n", __func__);

  memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
  i.context = context;
  i.ssl_connection = LCCSCF_USE_SSL;
  i.ssl_connection |=
      LCCSCF_ACCEPT_TLS_DOWNGRADE_REDIRECTS |
          LCCSCF_ALLOW_INSECURE | LCCSCF_ALLOW_SELFSIGNED | LCCSCF_ALLOW_EXPIRED;

  i.alpn = "h2,http/1.1";
  i.port = 443;
  i.address = "myaddress.com";
  i.host = i.address;
  i.origin = "https://myaddress.com";
  i.userdata = lws_context_user(context);
  i.method = "GET";
  i.protocol = protocols[0].name;
  i.path = "/path/to/websocket";
  if (!lws_client_connect_via_info(&i)) {
    lwsl_err("Client creation failed\n");
    lws_cancel_service(context);
    return 1;
  }
  return 0;
}

int main(int argc, char **argv){
    MyStruct data;
    // Initialize data here
    lws_state_notify_link_t notifier = {{NULL, NULL, NULL},
                                        system_notify_cb, "http"};
    lws_state_notify_link_t *na[] = {&notifier, NULL};

    struct lws_context_creation_info info{};
    memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
    info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
        LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW;
    info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
    info.protocols = protocols;
    info.user = &data;
    info.register_notifier_list = na;
    info.connect_timeout_secs = 30;
//    info.fd_limit_per_thread = 1 + 1 + 1;
    cx_ = lws_create_context(&info);

    int n = 1;
    while (n >= 0) {
      n = lws_service(cx_, 20);
      std::this_thread::sleep_for(std::chrono::milliseconds(20));
    }
}

First of all, how do I make sure it connects to wss://myaddress.com and not https://myaddress.com. Because Chrome shows that it connects to wss.

Since I couldn't find documentation except the example applications, I do not understand some parts oof this code:

  1. What is "h2" and what is "alpn"?
  2. When I run this application depending on the position of stars and other random events I get the callback_http with various reasons. However, it always ends with LWS_CALLBACK_CLOSED_CLIENT_HTTP
    A typical order would be
  3. LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS
  4. LWS_CALLBACK_GET_THREAD_ID
  5. LWS_CALLBACK_VHOST_CERT_AGING
  6. LWS_CALLBACK_PROTOCOL_INIT
  7. LWS_CALLBACK_EVENT_WAIT_CANCELLED (this is randomly called)
  8. LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL
  9. LWS_CALLBACK_CONNECTING
  10. LWS_CALLBACK_GET_THREAD_ID
  11. LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED (suddenly!!! Where's the server coming from. I created a client! )
  12. LWS_CALLBACK_GET_THREAD_ID
  13. LWS_CALLBACK_WSI_CREATE
  14. LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION (multiple times)
  15. LWS_CALLBACK_GET_THREAD_ID (again. Why?)
  16. LWS_CALLBACK_WSI_CREATE (again. Why?)
  17. LWS_CALLBACK_GET_THREAD_ID
  18. LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER
  19. LWS_CALLBACK_GET_THREAD_ID
  20. LWS_CALLBACK_CLOSED_CLIENT_HTTP

Depending on how long I stay in the debugger (or maybe something else) I might receive LWS_CALLBACK_CLIENT_HTTP_WRITEABLE. No matter what I write there (or skip) the next step is p.18.

I am most certainly doing something wrong but I cannot figure out what exactly.

I stripped the original code keeping only the relevant parts (the rest is logging, logging, logging...) so it might not compile but you'll still get the point.

@lws-team
Copy link
Member

lws-team commented Apr 8, 2024

There are nice verbose logs in lws that tell you all about what it's doing... just enable them as the examples do.

@vahagnIV
Copy link
Author

vahagnIV commented Apr 9, 2024

Thank you, @lws-team for a quick response. Could you explain what does
lws_h2_parse_end_of_frame: WINDOW_UPDATE 0x10000 + 0x7fff0000 = 0x80000000, too high
mean?

@lws-team
Copy link
Member

lws-team commented Apr 9, 2024

It means lws understands the server you're talking to overflowed the max window size possible. Some servers don't keep track well. There's a context creation info.option flag LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW you can use to silently fix it up instead.

h2 means http/2.

alpn is an early indication of what protocol you want to talk on tls.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants