LWS_VISIBLE int libwebsockets_serve_http_file( struct libwebsocket_context *context, struct libwebsocket *wsi, const char *file, const char *content_type, const char *other_headers, int other_headers_len) { unsigned char *response = context->service_buffer + LWS_SEND_BUFFER_PRE_PADDING; unsigned char *p = response; unsigned char *end = p + sizeof(context->service_buffer) - LWS_SEND_BUFFER_PRE_PADDING; int ret = 0; wsi->u.http.fd = lws_plat_open_file(file, &wsi->u.http.filelen); if (wsi->u.http.fd == LWS_INVALID_FILE) { lwsl_err("Unable to open '%s'\n", file); libwebsockets_return_http_status(context, wsi, HTTP_STATUS_NOT_FOUND, NULL); return -1; } if (lws_add_http_header_status(context, wsi, 200, &p, end)) return -1; if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_SERVER, (unsigned char *)"libwebsockets", 13, &p, end)) return -1; if (lws_add_http_header_by_token(context, wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char *)content_type, strlen(content_type), &p, end)) return -1; if (lws_add_http_header_content_length(context, wsi, wsi->u.http.filelen, &p, end)) return -1; if (other_headers) { if ((end - p) < other_headers_len) return -1; memcpy(p, other_headers, other_headers_len); p += other_headers_len; } if (lws_finalize_http_header(context, wsi, &p, end)) return -1; ret = libwebsocket_write(wsi, response, p - response, LWS_WRITE_HTTP_HEADERS); if (ret != (p - response)) { lwsl_err("_write returned %d from %d\n", ret, (p - response)); return -1; } wsi->u.http.filepos = 0; wsi->state = WSI_STATE_HTTP_ISSUING_FILE; return libwebsockets_serve_http_file_fragment(context, wsi); }
LWS_VISIBLE int libwebsockets_serve_http_file( struct libwebsocket_context *context, struct libwebsocket *wsi, const char *file, const char *content_type, const char *other_headers) { struct stat stat_buf; unsigned char *p = context->service_buffer; int ret = 0; int n; wsi->u.http.fd = open(file, O_RDONLY #ifdef WIN32 | _O_BINARY #endif ); if (wsi->u.http.fd < 1) { lwsl_err("Unable to open '%s'\n", file); libwebsockets_return_http_status(context, wsi, HTTP_STATUS_NOT_FOUND, NULL); wsi->u.http.fd = -1; return -1; } fstat(wsi->u.http.fd, &stat_buf); wsi->u.http.filelen = stat_buf.st_size; p += sprintf((char *)p, "HTTP/1.0 200 OK\x0d\x0aServer: pilight\x0d\x0a""Content-Type: %s\x0d\x0a", content_type); if (other_headers) { n = strlen(other_headers); memcpy(p, other_headers, n); p += n; } p += sprintf((char *)p, "Content-Length: %u\x0d\x0a\x0d\x0a", (unsigned int)stat_buf.st_size); ret = libwebsocket_write(wsi, context->service_buffer, p - context->service_buffer, LWS_WRITE_HTTP); if (ret != (p - context->service_buffer)) { lwsl_err("_write returned %d from %d\n", ret, (p - context->service_buffer)); return -1; } wsi->u.http.filepos = 0; wsi->state = WSI_STATE_HTTP_ISSUING_FILE; return libwebsockets_serve_http_file_fragment(context, wsi); }
LWS_VISIBLE int libwebsockets_serve_http_file( struct libwebsocket_context *context, struct libwebsocket *wsi, const char *file, const char *content_type, const char *other_headers) { unsigned char *p = context->service_buffer; int ret = 0; int n; wsi->u.http.fd = lws_plat_open_file(file, &wsi->u.http.filelen); if (wsi->u.http.fd == LWS_INVALID_FILE) { lwsl_err("Unable to open '%s'\n", file); libwebsockets_return_http_status(context, wsi, HTTP_STATUS_NOT_FOUND, NULL); return -1; } p += sprintf((char *)p, "HTTP/1.0 200 OK\x0d\x0aServer: libwebsockets\x0d\x0a""Content-Type: %s\x0d\x0a", content_type); if (other_headers) { n = strlen(other_headers); memcpy(p, other_headers, n); p += n; } p += sprintf((char *)p, "Content-Length: %lu\x0d\x0a\x0d\x0a", wsi->u.http.filelen); ret = libwebsocket_write(wsi, context->service_buffer, p - context->service_buffer, LWS_WRITE_HTTP); if (ret != (p - context->service_buffer)) { lwsl_err("_write returned %d from %d\n", ret, (p - context->service_buffer)); return -1; } wsi->u.http.filepos = 0; wsi->state = WSI_STATE_HTTP_ISSUING_FILE; return libwebsockets_serve_http_file_fragment(context, wsi); }
int lws_server_socket_service(struct libwebsocket_context *context, struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd) { struct libwebsocket *new_wsi = NULL; int accept_fd = 0; socklen_t clilen; struct sockaddr_in cli_addr; int n; int len; switch (wsi->mode) { case LWS_CONNMODE_HTTP_SERVING: case LWS_CONNMODE_HTTP_SERVING_ACCEPTED: /* handle http headers coming in */ /* pending truncated sends have uber priority */ if (wsi->truncated_send_malloc) { if (pollfd->revents & LWS_POLLOUT) if (lws_issue_raw(wsi, wsi->truncated_send_malloc + wsi->truncated_send_offset, wsi->truncated_send_len) < 0) { lwsl_info("closing from socket service\n"); return -1; } /* * we can't afford to allow input processing send * something new, so spin around he event loop until * he doesn't have any partials */ break; } /* any incoming data ready? */ if (pollfd->revents & LWS_POLLIN) { len = lws_ssl_capable_read(wsi, context->service_buffer, sizeof(context->service_buffer)); switch (len) { case 0: lwsl_info("lws_server_skt_srv: read 0 len\n"); /* lwsl_info(" state=%d\n", wsi->state); */ if (!wsi->hdr_parsing_completed) free(wsi->u.hdr.ah); /* fallthru */ case LWS_SSL_CAPABLE_ERROR: libwebsocket_close_and_free_session( context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; case LWS_SSL_CAPABLE_MORE_SERVICE: break; } /* just ignore incoming if waiting for close */ if (wsi->state != WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) { /* hm this may want to send (via HTTP callback for example) */ n = libwebsocket_read(context, wsi, context->service_buffer, len); if (n < 0) /* we closed wsi */ return 0; /* hum he may have used up the writability above */ break; } } /* this handles POLLOUT for http serving fragments */ if (!(pollfd->revents & LWS_POLLOUT)) break; /* one shot */ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) goto fail; lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE); if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE) { n = user_callback_handle_rxflow( wsi->protocol->callback, wsi->protocol->owning_server, wsi, LWS_CALLBACK_HTTP_WRITEABLE, wsi->user_space, NULL, 0); if (n < 0) libwebsocket_close_and_free_session( context, wsi, LWS_CLOSE_STATUS_NOSTATUS); break; } /* nonzero for completion or error */ if (libwebsockets_serve_http_file_fragment(context, wsi)) libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); break; case LWS_CONNMODE_SERVER_LISTENER: /* pollin means a client has connected to us then */ if (!(pollfd->revents & LWS_POLLIN)) break; /* listen socket got an unencrypted connection... */ clilen = sizeof(cli_addr); lws_latency_pre(context, wsi); accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr, &clilen); lws_latency(context, wsi, "unencrypted accept LWS_CONNMODE_SERVER_LISTENER", accept_fd, accept_fd >= 0); if (accept_fd < 0) { if (LWS_ERRNO == LWS_EAGAIN || LWS_ERRNO == LWS_EWOULDBLOCK) { lwsl_debug("accept asks to try again\n"); break; } lwsl_warn("ERROR on accept: %s\n", strerror(LWS_ERRNO)); break; } lws_plat_set_socket_options(context, accept_fd); /* * look at who we connected to and give user code a chance * to reject based on client IP. There's no protocol selected * yet so we issue this to protocols[0] */ if ((context->protocols[0].callback)(context, wsi, LWS_CALLBACK_FILTER_NETWORK_CONNECTION, NULL, (void *)(long)accept_fd, 0)) { lwsl_debug("Callback denied network connection\n"); compatible_close(accept_fd); break; } new_wsi = libwebsocket_create_new_server_wsi(context); if (new_wsi == NULL) { compatible_close(accept_fd); break; } new_wsi->sock = accept_fd; /* the transport is accepted... give him time to negotiate */ libwebsocket_set_timeout(new_wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, AWAITING_TIMEOUT); /* * A new connection was accepted. Give the user a chance to * set properties of the newly created wsi. There's no protocol * selected yet so we issue this to protocols[0] */ (context->protocols[0].callback)(context, new_wsi, LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, NULL, NULL, 0); lws_libev_accept(context, new_wsi, accept_fd); if (!LWS_SSL_ENABLED(context)) { lwsl_debug("accepted new conn port %u on fd=%d\n", ntohs(cli_addr.sin_port), accept_fd); insert_wsi_socket_into_fds(context, new_wsi); } break; default: break; } if (lws_server_socket_service_ssl(context, &wsi, new_wsi, accept_fd, pollfd)) goto fail; return 0; fail: libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 1; }
int lws_server_socket_service(struct libwebsocket_context *context, struct libwebsocket *wsi, struct pollfd *pollfd) { struct libwebsocket *new_wsi; int accept_fd; socklen_t clilen; struct sockaddr_in cli_addr; int n; ssize_t len; #ifdef LWS_OPENSSL_SUPPORT int m; #ifndef USE_CYASSL BIO *bio; #endif #endif switch (wsi->mode) { case LWS_CONNMODE_HTTP_SERVING: case LWS_CONNMODE_HTTP_SERVING_ACCEPTED: /* handle http headers coming in */ /* pending truncated sends have uber priority */ if (wsi->truncated_send_malloc) { if (pollfd->revents & POLLOUT) lws_issue_raw(wsi, wsi->truncated_send_malloc + wsi->truncated_send_offset, wsi->truncated_send_len); /* * we can't afford to allow input processing send * something new, so spin around he event loop until * he doesn't have any partials */ break; } /* any incoming data ready? */ if (pollfd->revents & POLLIN) { #ifdef LWS_OPENSSL_SUPPORT if (wsi->ssl) len = SSL_read(wsi->ssl, context->service_buffer, sizeof(context->service_buffer)); else #endif len = recv(pollfd->fd, context->service_buffer, sizeof(context->service_buffer), 0); if (len < 0) { lwsl_debug("Socket read returned %d\n", len); if (errno != EINTR && errno != EAGAIN) libwebsocket_close_and_free_session( context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } if (!len) { lwsl_info("lws_server_skt_srv: read 0 len\n"); /* lwsl_info(" state=%d\n", wsi->state); */ if (!wsi->hdr_parsing_completed) free(wsi->u.hdr.ah); libwebsocket_close_and_free_session( context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; } /* hm this may want to send (via HTTP callback for example) */ n = libwebsocket_read(context, wsi, context->service_buffer, len); if (n < 0) /* we closed wsi */ return 0; /* hum he may have used up the writability above */ break; } /* this handles POLLOUT for http serving fragments */ if (!(pollfd->revents & POLLOUT)) break; /* one shot */ lws_change_pollfd(wsi, POLLOUT, 0); if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE) { n = user_callback_handle_rxflow( wsi->protocol->callback, wsi->protocol->owning_server, wsi, LWS_CALLBACK_HTTP_WRITEABLE, wsi->user_space, NULL, 0); if (n < 0) libwebsocket_close_and_free_session( context, wsi, LWS_CLOSE_STATUS_NOSTATUS); break; } /* nonzero for completion or error */ if (libwebsockets_serve_http_file_fragment(context, wsi)) libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); break; case LWS_CONNMODE_SERVER_LISTENER: /* pollin means a client has connected to us then */ if (!(pollfd->revents & POLLIN)) break; /* listen socket got an unencrypted connection... */ clilen = sizeof(cli_addr); lws_latency_pre(context, wsi); accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr, &clilen); lws_latency(context, wsi, "unencrypted accept LWS_CONNMODE_SERVER_LISTENER", accept_fd, accept_fd >= 0); if (accept_fd < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { lwsl_debug("accept asks to try again\n"); break; } lwsl_warn("ERROR on accept: %s\n", strerror(errno)); break; } lws_set_socket_options(context, accept_fd); /* * look at who we connected to and give user code a chance * to reject based on client IP. There's no protocol selected * yet so we issue this to protocols[0] */ if ((context->protocols[0].callback)(context, wsi, LWS_CALLBACK_FILTER_NETWORK_CONNECTION, NULL, (void *)(long)accept_fd, 0)) { lwsl_debug("Callback denied network connection\n"); compatible_close(accept_fd); break; } new_wsi = libwebsocket_create_new_server_wsi(context); if (new_wsi == NULL) { compatible_close(accept_fd); break; } new_wsi->sock = accept_fd; #ifdef LWS_OPENSSL_SUPPORT new_wsi->ssl = NULL; if (!context->use_ssl) { #endif lwsl_debug("accepted new conn port %u on fd=%d\n", ntohs(cli_addr.sin_port), accept_fd); insert_wsi_socket_into_fds(context, new_wsi); break; #ifdef LWS_OPENSSL_SUPPORT } new_wsi->ssl = SSL_new(context->ssl_ctx); if (new_wsi->ssl == NULL) { lwsl_err("SSL_new failed: %s\n", ERR_error_string(SSL_get_error( new_wsi->ssl, 0), NULL)); libwebsockets_decode_ssl_error(); free(new_wsi); compatible_close(accept_fd); break; } SSL_set_ex_data(new_wsi->ssl, openssl_websocket_private_data_index, context); SSL_set_fd(new_wsi->ssl, accept_fd); #ifdef USE_CYASSL CyaSSL_set_using_nonblock(new_wsi->ssl, 1); #else bio = SSL_get_rbio(new_wsi->ssl); if (bio) BIO_set_nbio(bio, 1); /* nonblocking */ else lwsl_notice("NULL rbio\n"); bio = SSL_get_wbio(new_wsi->ssl); if (bio) BIO_set_nbio(bio, 1); /* nonblocking */ else lwsl_notice("NULL rbio\n"); #endif /* * we are not accepted yet, but we need to enter ourselves * as a live connection. That way we can retry when more * pieces come if we're not sorted yet */ wsi = new_wsi; wsi->mode = LWS_CONNMODE_SSL_ACK_PENDING; insert_wsi_socket_into_fds(context, wsi); libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT, AWAITING_TIMEOUT); lwsl_info("inserted SSL accept into fds, trying SSL_accept\n"); /* fallthru */ case LWS_CONNMODE_SSL_ACK_PENDING: lws_change_pollfd(wsi, POLLOUT, 0); lws_latency_pre(context, wsi); n = recv(wsi->sock, context->service_buffer, sizeof(context->service_buffer), MSG_PEEK); /* * optionally allow non-SSL connect on SSL listening socket * This is disabled by default, if enabled it goes around any * SSL-level access control (eg, client-side certs) so leave * it disabled unless you know it's not a problem for you */ if (context->allow_non_ssl_on_ssl_port && n >= 1 && context->service_buffer[0] >= ' ') { /* * TLS content-type for Handshake is 0x16 * TLS content-type for ChangeCipherSpec Record is 0x14 * * A non-ssl session will start with the HTTP method in * ASCII. If we see it's not a legit SSL handshake * kill the SSL for this connection and try to handle * as a HTTP connection upgrade directly. */ wsi->use_ssl = 0; SSL_shutdown(wsi->ssl); SSL_free(wsi->ssl); wsi->ssl = NULL; goto accepted; } /* normal SSL connection processing path */ n = SSL_accept(wsi->ssl); lws_latency(context, wsi, "SSL_accept LWS_CONNMODE_SSL_ACK_PENDING\n", n, n == 1); if (n != 1) { m = SSL_get_error(wsi->ssl, n); lwsl_debug("SSL_accept failed %d / %s\n", m, ERR_error_string(m, NULL)); if (m == SSL_ERROR_WANT_READ) { lws_change_pollfd(wsi, 0, POLLIN); lwsl_info("SSL_ERROR_WANT_READ\n"); break; } if (m == SSL_ERROR_WANT_WRITE) { lws_change_pollfd(wsi, 0, POLLOUT); break; } lwsl_debug("SSL_accept failed skt %u: %s\n", pollfd->fd, ERR_error_string(m, NULL)); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); break; } accepted: /* OK, we are accepted */ libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); wsi->mode = LWS_CONNMODE_HTTP_SERVING; lwsl_debug("accepted new SSL conn\n"); break; #endif default: break; } return 0; }