static int new_writer(lua_State* L) { void* handle; writer_api api; int type = lua_type(L, 1); switch(type) { case LUA_TUSERDATA: handle = luaL_testudata(L, 1, OUT_MEMORY_STREAM); if(handle) { api.write = (stream_write_t)&outmemory_stream_write; api.flush = (stream_flush_t)&outmemory_stream_flush; api.close = (stream_close_t)&outmemory_stream_close; break; } handle = luaL_testudata(L, 1, LUA_FILEHANDLE); if(handle) { luaL_Stream* stream = handle; handle = stream->f; api.write = (stream_write_t)&fwrite; api.flush = (stream_flush_t)&fflush; api.close = (stream_close_t)&fclose; break; } luaL_error(L, "Inputstream expected"); case LUA_TTABLE: luaL_error(L, "Cannot yet use lua table writers"); break; default: luaL_error(L, "Inputstream expected"); break; } //First get the stream data. char* udata = create_userdata(L, sizeof(writer_t) + 4096, WRITER_META_TABLE); writer_t* writer = (writer_t*)udata; uint8_t* buffer = udata + sizeof(writer_t); *writer = writer_create(handle, buffer, 4096, api); return 1; }
int client_create(Client *client, const char *name, IO *io, uint32_t authentication_nonce, ClientDestroyDoneFunction destroy_done) { log_debug("Creating client from %s (handle: %d)", io->type, io->handle); string_copy(client->name, sizeof(client->name), name); client->io = io; client->disconnected = false; client->request_used = 0; client->request_header_checked = false; client->pending_request_count = 0; client->authentication_state = CLIENT_AUTHENTICATION_STATE_DISABLED; client->authentication_nonce = authentication_nonce; client->destroy_done = destroy_done; if (config_get_option_value("authentication.secret")->string != NULL) { client->authentication_state = CLIENT_AUTHENTICATION_STATE_ENABLED; } node_reset(&client->pending_request_sentinel); // create response writer if (writer_create(&client->response_writer, client->io, "response", packet_get_response_signature, "client", client_get_recipient_signature, client_recipient_disconnect, client) < 0) { log_error("Could not create response writer: %s (%d)", get_errno_name(errno), errno); return -1; } // add I/O object as event source return event_add_source(client->io->handle, EVENT_SOURCE_TYPE_GENERIC, EVENT_READ, client_handle_read, client); }
/*(*** http_connection_handler */ void http_connection_handler(void *svd, connection *cn, short events) { server *sv; http *ht; sv = svd; ht = cn->cn_data; log_msg(sv->sv_log, LOG_DEBUG, "Event mask on FD %d is 0x%04x", cn->cn_fd, events); if(events & POLLERR) { demux_delete_connection(sv->sv_demux, cn); log_msg(sv->sv_log, LOG_WARN, "Deleting connection on FD %d", cn->cn_fd); return; } if(events & POLLIN) { int fd; int r; char buf[PARAM_READ_BUFFER_SIZE]; fd = cn->cn_fd; r = read(fd, buf, sizeof(buf)); if(r < 0) { log_msg(sv->sv_log, LOG_DEBUG, "Read error on FD %d (%s)", cn->cn_fd, strerror(errno)); } else { if(r > 0) { log_msg(sv->sv_log, LOG_DEBUG, "Read %d bytes on FD %d", r, cn->cn_fd); http_add_bytes(ht, buf, r); } else { log_msg(sv->sv_log, LOG_INFO, "Connection closed on FD %d", cn->cn_fd); if(close(fd) < 0) { log_msg(sv->sv_log, LOG_ERROR, "Error closing FD %d (%s)", cn->cn_fd, strerror(errno)); } demux_delete_connection(sv->sv_demux, cn); } } } if(events & POLLOUT) { switch(ht->ht_state) { case HTTP_SERVING: case HTTP_RESPONDING: case HTTP_RESPONSE_ONLY: switch(writer_work(ht->ht_writer, cn->cn_fd)) { case WRITER_WORKING: break; case WRITER_DONE: switch(ht->ht_state) { case HTTP_RESPONDING: ht->ht_state = HTTP_SERVING; writer_delete(ht->ht_writer); ht->ht_writer = writer_create(ht->ht_resource->rs_start, ht->ht_resource->rs_length, 0); break; case HTTP_RESPONSE_ONLY: default: demux_delete_connection(sv->sv_demux, cn); break; } break; case WRITER_ERROR: log_msg(sv->sv_log, LOG_ERROR, "Error writing to FD %d (%s)", cn->cn_fd, strerror(errno)); demux_delete_connection(sv->sv_demux, cn); break; } break; default: break; } } }
/*(*** http_eater */ void http_eater(void *htd, char *p, int m) { http *ht; server *sv; ht = htd; sv = ht->ht_server; #if DEBUG { char buf[75]; int i; for(i = 0; i < m && i < sizeof(buf) - 1; i ++) { buf[i] = isprint(p[i]) ? p[i] : '?'; } buf[i] = 0; log_msg(sv->sv_log, LOG_DEBUG, "Got line [%s]", buf); } #endif reswitch: switch(ht->ht_state) { case HTTP_REQUEST: /* XXX: parse request */ { if(!http_parse_request_line(&ht->ht_hrl, p, m)) goto violation; /* MEMORY LEAK */ log_msg(sv->sv_log, LOG_DEBUG, "Method is [%s] URI is [%s] major is [%s] minor is [%s]", ht->ht_hrl.hrl_method, ht->ht_hrl.hrl_uri, ht->ht_hrl.hrl_major, ht->ht_hrl.hrl_minor); ht->ht_state = HTTP_HEADER; } break; case HTTP_HEADER: if(m) { /* Non-empty header lines */ http_release_header_line(&ht->ht_hhl); if(http_parse_header_line(&ht->ht_hhl, p, m)) { /* This is a nice header */ ht->ht_state = HTTP_MORE_HEADERS; } else goto violation; } else { /* Empty line : headers finished */ if(!http_process_header(ht)) goto violation; ht->ht_state = HTTP_BUILD_RESPONSE; goto reswitch; } break; case HTTP_MORE_HEADERS: if(m) { if(http_parse_header_continuation_line(&ht->ht_hhl, p, m)) break; /* Stay in this state. */ else { /* Not a continuation header. */ /* Post current header. */ if(!http_process_header(ht)) goto violation; ht->ht_state = HTTP_HEADER; goto reswitch; } } else { /* Empty line : headers finished */ if(!http_process_header(ht)) goto violation; ht->ht_state = HTTP_BUILD_RESPONSE; goto reswitch; } break; case HTTP_BUILD_RESPONSE: /* Build response */ { char *response; int response_length; char *content_type; size_t content_length; resource *rs; char *fn; if(!strcmp(ht->ht_hrl.hrl_method, "GET")) { fn = http_uri_to_filename(ht, ht->ht_hrl.hrl_uri); rs = resource_obtain(fn); if(!rs) { log_msg(sv->sv_log, LOG_ERROR, "Can't obtain %s (%s)", fn, strerror(errno)); response_length = xasprintf( &response, "HTTP/1.1 404 Not Found\r\n" "Content-Type: text/html\r\n" "Connection: close\r\n" "\r\n" "<html>" "<head><title>404 Not Found</title></head><body>" "Unable to find resource %s (%s)." "</body></html>", fn, strerror(errno)); ht->ht_state = HTTP_RESPONSE_ONLY; } else { content_type = rs->rs_mime_type; content_length = rs->rs_length; if(ht->ht_writer) { writer_delete(ht->ht_writer); ht->ht_writer = 0; } ht->ht_resource = rs; response_length = xasprintf( &response, "HTTP/1.1 200 OK\r\n" "Content-Type: %s\r\n" "Connection: close\r\n" "Content-Length: %d\r\n" "\r\n", content_type, content_length); ht->ht_state = HTTP_RESPONDING; } xfree(fn); } else { response_length = xasprintf( &response, "HTTP/1.1 501 Not implemented\r\n" "Content-Type: text/html\r\n" "Connection: close\r\n" "\r\n" "<html>" "<head><title>501 Not implemented</title></head><body>" "Method %s is not supported by this server." "</body></html>", ht->ht_hrl.hrl_method); ht->ht_state = HTTP_RESPONSE_ONLY; } ht->ht_response = response; ht->ht_writer = writer_create(response, response_length, response); demux_set_event_mask(sv->sv_demux, ht->ht_connection, POLLOUT); /* Stop reading */ } break; case HTTP_RESPONSE_ONLY: case HTTP_RESPONDING: break; case HTTP_SERVING: break; } return; violation: log_msg(sv->sv_log, LOG_ERROR, "HTTP protocol violation"); demux_delete_connection(sv->sv_demux, ht->ht_connection); return; }