Compare commits
	
		
			73 Commits
		
	
	
		
			2962a644da
			...
			master
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						74fcb06828
	
				 | 
					
					
						|||
| 
						
						
							
						
						8cf1abf135
	
				 | 
					
					
						|||
| 
						
						
							
						
						b87fbc93a6
	
				 | 
					
					
						|||
| 
						
						
							
						
						ac1a21eac8
	
				 | 
					
					
						|||
| 
						
						
							
						
						6934550068
	
				 | 
					
					
						|||
| 
						
						
							
						
						cda7e1b1f3
	
				 | 
					
					
						|||
| 
						
						
							
						
						14c6d285fc
	
				 | 
					
					
						|||
| 
						
						
							
						
						ab5941aaef
	
				 | 
					
					
						|||
| 
						
						
							
						
						84d6c658e8
	
				 | 
					
					
						|||
| 
						
						
							
						
						a89fadf860
	
				 | 
					
					
						|||
| 
						
						
							
						
						4023155b67
	
				 | 
					
					
						|||
| 
						
						
							
						
						4ed58dd89a
	
				 | 
					
					
						|||
| 
						
						
							
						
						022668fb23
	
				 | 
					
					
						|||
| 
						
						
							
						
						ba5a6374b6
	
				 | 
					
					
						|||
| 
						
						
							
						
						67008963cf
	
				 | 
					
					
						|||
| 
						
						
							
						
						c1b6918db3
	
				 | 
					
					
						|||
| 
						
						
							
						
						3cf3c0215e
	
				 | 
					
					
						|||
| 
						
						
							
						
						a2a72c8b92
	
				 | 
					
					
						|||
| 
						
						
							
						
						57f89eba07
	
				 | 
					
					
						|||
| 
						
						
							
						
						4795ee851d
	
				 | 
					
					
						|||
| 
						
						
							
						
						87a644cc59
	
				 | 
					
					
						|||
| 
						
						
							
						
						990cf5a1d4
	
				 | 
					
					
						|||
| 
						
						
							
						
						4a5c818ba1
	
				 | 
					
					
						|||
| 
						
						
							
						
						af5929a383
	
				 | 
					
					
						|||
| 
						
						
							
						
						9f5845fc51
	
				 | 
					
					
						|||
| 
						
						
							
						
						3daf254b41
	
				 | 
					
					
						|||
| 
						
						
							
						
						c533fa2fd7
	
				 | 
					
					
						|||
| 
						
						
							
						
						2fe2d6bc03
	
				 | 
					
					
						|||
| 
						
						
							
						
						df93937789
	
				 | 
					
					
						|||
| 
						
						
							
						
						ae447065f7
	
				 | 
					
					
						|||
| 
						
						
							
						
						f9e157293c
	
				 | 
					
					
						|||
| 
						
						
							
						
						42d1ff064f
	
				 | 
					
					
						|||
| 
						
						
							
						
						710f8e0b2d
	
				 | 
					
					
						|||
| 
						
						
							
						
						4938ee43bd
	
				 | 
					
					
						|||
| 
						
						
							
						
						6927d022fb
	
				 | 
					
					
						|||
| 
						
						
							
						
						75b2094cdd
	
				 | 
					
					
						|||
| 
						
						
							
						
						b3c377afdb
	
				 | 
					
					
						|||
| 
						
						
							
						
						4236a4943a
	
				 | 
					
					
						|||
| 
						
						
							
						
						23c728e535
	
				 | 
					
					
						|||
| 
						
						
							
						
						dfe814316f
	
				 | 
					
					
						|||
| 
						
						
							
						
						efc663a178
	
				 | 
					
					
						|||
| 
						
						
							
						
						2b8f52ac72
	
				 | 
					
					
						|||
| 
						
						
							
						
						bb7ffe1da2
	
				 | 
					
					
						|||
| 
						
						
							
						
						ad1aba9d22
	
				 | 
					
					
						|||
| 
						
						
							
						
						0107d09abc
	
				 | 
					
					
						|||
| 
						
						
							
						
						01767198f2
	
				 | 
					
					
						|||
| 
						
						
							
						
						5854ed1b32
	
				 | 
					
					
						|||
| 
						
						
							
						
						63c8a79479
	
				 | 
					
					
						|||
| 
						
						
							
						
						d489362a28
	
				 | 
					
					
						|||
| 
						
						
							
						
						c87869bef7
	
				 | 
					
					
						|||
| 
						
						
							
						
						fcf65f8377
	
				 | 
					
					
						|||
| 
						
						
							
						
						d820bc2f23
	
				 | 
					
					
						|||
| 
						
						
							
						
						b458fc1f99
	
				 | 
					
					
						|||
| 
						
						
							
						
						0771c142fe
	
				 | 
					
					
						|||
| 
						
						
							
						
						742632a931
	
				 | 
					
					
						|||
| 
						
						
							
						
						2221828763
	
				 | 
					
					
						|||
| 
						
						
							
						
						c2a00511c0
	
				 | 
					
					
						|||
| 
						
						
							
						
						2b18ebf314
	
				 | 
					
					
						|||
| 
						
						
							
						
						5d2cd01db0
	
				 | 
					
					
						|||
| 
						
						
							
						
						ee79249d23
	
				 | 
					
					
						|||
| 
						
						
							
						
						160d23018a
	
				 | 
					
					
						|||
| 
						
						
							
						
						fed2892ee1
	
				 | 
					
					
						|||
| 
						
						
							
						
						667b01cb73
	
				 | 
					
					
						|||
| 
						
						
							
						
						20c8578084
	
				 | 
					
					
						|||
| 
						
						
							
						
						57a3b4e990
	
				 | 
					
					
						|||
| 
						
						
							
						
						e4d1529b4d
	
				 | 
					
					
						|||
| 
						
						
							
						
						897a263ee7
	
				 | 
					
					
						|||
| 
						
						
							
						
						84702fa47d
	
				 | 
					
					
						|||
| 
						
						
							
						
						b315892249
	
				 | 
					
					
						|||
| 
						
						
							
						
						710f5f197f
	
				 | 
					
					
						|||
| 
						
						
							
						
						ba68585d14
	
				 | 
					
					
						|||
| 
						
						
							
						
						984e5b4e7f
	
				 | 
					
					
						|||
| 
						
						
							
						
						d57a8bd3c7
	
				 | 
					
					
						
							
								
								
									
										33
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					# clang-format is fairly limited, and these rules are approximate:
 | 
				
			||||||
 | 
					#  - array initializers can get terribly mangled with clang-format 12.0,
 | 
				
			||||||
 | 
					#  - sometimes it still aligns with space characters,
 | 
				
			||||||
 | 
					#  - EV_DEFAULT_ and EV_A_ are always taken as identifiers,
 | 
				
			||||||
 | 
					#  - struct name NL { NL ... NL } NL name; is unachievable.
 | 
				
			||||||
 | 
					BasedOnStyle: GNU
 | 
				
			||||||
 | 
					ColumnLimit: 80
 | 
				
			||||||
 | 
					IndentWidth: 4
 | 
				
			||||||
 | 
					TabWidth: 4
 | 
				
			||||||
 | 
					UseTab: ForContinuationAndIndentation
 | 
				
			||||||
 | 
					BreakBeforeBraces: Allman
 | 
				
			||||||
 | 
					SpaceAfterCStyleCast: true
 | 
				
			||||||
 | 
					AlignAfterOpenBracket: DontAlign
 | 
				
			||||||
 | 
					AlignOperands: DontAlign
 | 
				
			||||||
 | 
					AlignConsecutiveMacros: Consecutive
 | 
				
			||||||
 | 
					AllowAllArgumentsOnNextLine: false
 | 
				
			||||||
 | 
					AllowAllParametersOfDeclarationOnNextLine: false
 | 
				
			||||||
 | 
					IndentGotoLabels: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# IncludeCategories has some potential, but it may also break the build.
 | 
				
			||||||
 | 
					# Note that the documentation says the value should be "Never".
 | 
				
			||||||
 | 
					SortIncludes: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# This is a compromise, it generally works out aesthetically better.
 | 
				
			||||||
 | 
					BinPackArguments: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Unfortunately, this can't be told to align to column 40 or so.
 | 
				
			||||||
 | 
					SpacesBeforeTrailingComments: 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# liberty-specific macro body wrappers.
 | 
				
			||||||
 | 
					MacroBlockBegin: "BLOCK_START"
 | 
				
			||||||
 | 
					MacroBlockEnd: "BLOCK_END"
 | 
				
			||||||
 | 
					ForEachMacros: ["LIST_FOR_EACH"]
 | 
				
			||||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -7,3 +7,5 @@
 | 
				
			|||||||
/json-rpc-shell.files
 | 
					/json-rpc-shell.files
 | 
				
			||||||
/json-rpc-shell.creator*
 | 
					/json-rpc-shell.creator*
 | 
				
			||||||
/json-rpc-shell.includes
 | 
					/json-rpc-shell.includes
 | 
				
			||||||
 | 
					/json-rpc-shell.cflags
 | 
				
			||||||
 | 
					/json-rpc-shell.cxxflags
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										131
									
								
								CMakeLists.txt
									
									
									
									
									
								
							
							
						
						
									
										131
									
								
								CMakeLists.txt
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
				
			|||||||
project (json-rpc-shell C)
 | 
					cmake_minimum_required (VERSION 3.0...3.27)
 | 
				
			||||||
cmake_minimum_required (VERSION 2.8.5)
 | 
					project (json-rpc-shell VERSION 1.2.0 LANGUAGES C)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Options
 | 
					# Options
 | 
				
			||||||
option (WANT_READLINE "Use GNU Readline for the UI (better)" ON)
 | 
					option (WANT_READLINE "Use GNU Readline for the UI (better)" ON)
 | 
				
			||||||
@@ -10,54 +10,73 @@ if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
 | 
				
			|||||||
	# -Wunused-function is pretty annoying here, as everything is static
 | 
						# -Wunused-function is pretty annoying here, as everything is static
 | 
				
			||||||
	set (CMAKE_C_FLAGS
 | 
						set (CMAKE_C_FLAGS
 | 
				
			||||||
		"${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Wno-unused-function")
 | 
							"${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Wno-unused-function")
 | 
				
			||||||
endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
 | 
					endif ()
 | 
				
			||||||
 | 
					 | 
				
			||||||
# Version
 | 
					 | 
				
			||||||
set (project_VERSION_MAJOR "0")
 | 
					 | 
				
			||||||
set (project_VERSION_MINOR "1")
 | 
					 | 
				
			||||||
set (project_VERSION_PATCH "0")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
set (project_VERSION "${project_VERSION_MAJOR}")
 | 
					 | 
				
			||||||
set (project_VERSION "${project_VERSION}.${project_VERSION_MINOR}")
 | 
					 | 
				
			||||||
set (project_VERSION "${project_VERSION}.${project_VERSION_PATCH}")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# For custom modules
 | 
					# For custom modules
 | 
				
			||||||
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
 | 
					set (CMAKE_MODULE_PATH
 | 
				
			||||||
 | 
						"${PROJECT_SOURCE_DIR}/cmake;${PROJECT_SOURCE_DIR}/liberty/cmake")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Dependencies
 | 
					# Dependencies
 | 
				
			||||||
find_package (Curses)
 | 
					find_package (Curses)
 | 
				
			||||||
 | 
					find_package (Ncursesw)
 | 
				
			||||||
find_package (PkgConfig REQUIRED)
 | 
					find_package (PkgConfig REQUIRED)
 | 
				
			||||||
pkg_check_modules (dependencies REQUIRED libcurl jansson)
 | 
					 | 
				
			||||||
# Note that cURL can link to a different version of libssl than we do,
 | 
					# Note that cURL can link to a different version of libssl than we do,
 | 
				
			||||||
# in which case the results are undefined
 | 
					# in which case the results are undefined
 | 
				
			||||||
pkg_check_modules (libssl REQUIRED libssl libcrypto)
 | 
					pkg_check_modules (dependencies REQUIRED libcurl jansson libssl libcrypto)
 | 
				
			||||||
find_package (LibEV REQUIRED)
 | 
					find_package (LibEV REQUIRED)
 | 
				
			||||||
pkg_check_modules (ncursesw ncursesw)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
set (project_libraries ${dependencies_LIBRARIES}
 | 
					set (project_libraries ${dependencies_LIBRARIES} ${LibEV_LIBRARIES})
 | 
				
			||||||
	${libssl_LIBRARIES} ${LIBEV_LIBRARIES})
 | 
					include_directories (${dependencies_INCLUDE_DIRS} ${LibEV_INCLUDE_DIRS})
 | 
				
			||||||
include_directories (${dependencies_INCLUDE_DIRS}
 | 
					link_directories (${dependencies_LIBRARY_DIRS})
 | 
				
			||||||
	${libssl_INCLUDE_DIRS} ${LIBEV_INCLUDE_DIRS})
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (ncursesw_FOUND)
 | 
					if ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")
 | 
				
			||||||
	list (APPEND project_libraries ${ncursesw_LIBRARIES})
 | 
						# Need this for SIGWINCH in FreeBSD and OpenBSD respectively;
 | 
				
			||||||
	include_directories (${ncursesw_INCLUDE_DIRS})
 | 
						# our POSIX version macros make it undefined
 | 
				
			||||||
 | 
						add_definitions (-D__BSD_VISIBLE=1 -D_BSD_SOURCE=1)
 | 
				
			||||||
 | 
					elseif (APPLE)
 | 
				
			||||||
 | 
						add_definitions (-D_DARWIN_C_SOURCE)
 | 
				
			||||||
 | 
					endif ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# -liconv may or may not be a part of libc
 | 
				
			||||||
 | 
					find_library (iconv_LIBRARIES iconv)
 | 
				
			||||||
 | 
					if (iconv_LIBRARIES)
 | 
				
			||||||
 | 
						list (APPEND project_libraries ${iconv_LIBRARIES})
 | 
				
			||||||
 | 
					endif ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					include (CheckCSourceRuns)
 | 
				
			||||||
 | 
					set (CMAKE_REQUIRED_LIBRARIES ${project_libraries})
 | 
				
			||||||
 | 
					get_property (CMAKE_REQUIRED_INCLUDES
 | 
				
			||||||
 | 
						DIRECTORY "${PROJECT_SOURCE_DIR}" PROPERTY INCLUDE_DIRECTORIES)
 | 
				
			||||||
 | 
					CHECK_C_SOURCE_RUNS ("#include <iconv.h>
 | 
				
			||||||
 | 
						int main () { return iconv_open (\"UTF-8//TRANSLIT\", \"ISO-8859-1\")
 | 
				
			||||||
 | 
							== (iconv_t) -1; }" ICONV_ACCEPTS_TRANSLIT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if (Ncursesw_FOUND)
 | 
				
			||||||
 | 
						list (APPEND project_libraries ${Ncursesw_LIBRARIES})
 | 
				
			||||||
 | 
						include_directories (${Ncursesw_INCLUDE_DIRS})
 | 
				
			||||||
 | 
						link_directories (${Ncursesw_LIBRARY_DIRS})
 | 
				
			||||||
elseif (CURSES_FOUND)
 | 
					elseif (CURSES_FOUND)
 | 
				
			||||||
	list (APPEND project_libraries ${CURSES_LIBRARY})
 | 
						list (APPEND project_libraries ${CURSES_LIBRARY})
 | 
				
			||||||
	include_directories (${CURSES_INCLUDE_DIR})
 | 
						include_directories (${CURSES_INCLUDE_DIR})
 | 
				
			||||||
else (CURSES_FOUND)
 | 
					else ()
 | 
				
			||||||
	message (SEND_ERROR "Curses not found")
 | 
						message (SEND_ERROR "Curses not found")
 | 
				
			||||||
endif (ncursesw_FOUND)
 | 
					endif ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if ((WANT_READLINE AND WANT_LIBEDIT) OR (NOT WANT_READLINE AND NOT WANT_LIBEDIT))
 | 
					if ((WANT_READLINE AND WANT_LIBEDIT) OR (NOT WANT_READLINE AND NOT WANT_LIBEDIT))
 | 
				
			||||||
	message (SEND_ERROR "You have to choose either GNU Readline or libedit")
 | 
						message (SEND_ERROR "You have to choose either GNU Readline or libedit")
 | 
				
			||||||
elseif (WANT_READLINE)
 | 
					elseif (WANT_READLINE)
 | 
				
			||||||
	list (APPEND project_libraries readline)
 | 
						# OpenBSD's default readline is too old
 | 
				
			||||||
 | 
						if ("${CMAKE_SYSTEM_NAME}" MATCHES "OpenBSD")
 | 
				
			||||||
 | 
							include_directories (${OPENBSD_LOCALBASE}/include/ereadline)
 | 
				
			||||||
 | 
							list (APPEND project_libraries ereadline)
 | 
				
			||||||
 | 
						else ()
 | 
				
			||||||
 | 
							list (APPEND project_libraries readline)
 | 
				
			||||||
 | 
						endif ()
 | 
				
			||||||
elseif (WANT_LIBEDIT)
 | 
					elseif (WANT_LIBEDIT)
 | 
				
			||||||
	pkg_check_modules (libedit REQUIRED libedit)
 | 
						pkg_check_modules (libedit REQUIRED libedit)
 | 
				
			||||||
	list (APPEND project_libraries ${libedit_LIBRARIES})
 | 
						list (APPEND project_libraries ${libedit_LIBRARIES})
 | 
				
			||||||
	include_directories (${libedit_INCLUDE_DIRS})
 | 
						include_directories (${libedit_INCLUDE_DIRS})
 | 
				
			||||||
endif ((WANT_READLINE AND WANT_LIBEDIT) OR (NOT WANT_READLINE AND NOT WANT_LIBEDIT))
 | 
					endif ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Generate a configuration file
 | 
					# Generate a configuration file
 | 
				
			||||||
set (HAVE_READLINE "${WANT_READLINE}")
 | 
					set (HAVE_READLINE "${WANT_READLINE}")
 | 
				
			||||||
@@ -86,23 +105,43 @@ install (TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
 | 
				
			|||||||
install (PROGRAMS json-format.pl DESTINATION ${CMAKE_INSTALL_BINDIR})
 | 
					install (PROGRAMS json-format.pl DESTINATION ${CMAKE_INSTALL_BINDIR})
 | 
				
			||||||
install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
 | 
					install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Generate documentation from program help
 | 
					# Generate documentation from text markup
 | 
				
			||||||
find_program (ASCIIDOCTOR_EXECUTABLE asciidoctor)
 | 
					find_program (ASCIIDOCTOR_EXECUTABLE asciidoctor)
 | 
				
			||||||
if (NOT ASCIIDOCTOR_EXECUTABLE)
 | 
					find_program (A2X_EXECUTABLE a2x)
 | 
				
			||||||
	message (FATAL_ERROR "asciidoctor not found")
 | 
					if (NOT ASCIIDOCTOR_EXECUTABLE AND NOT A2X_EXECUTABLE)
 | 
				
			||||||
endif (NOT ASCIIDOCTOR_EXECUTABLE)
 | 
						message (WARNING "Neither asciidoctor nor a2x were found, "
 | 
				
			||||||
 | 
							"falling back to a substandard manual page generator")
 | 
				
			||||||
 | 
					endif ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
foreach (page ${PROJECT_NAME})
 | 
					foreach (page ${PROJECT_NAME})
 | 
				
			||||||
	set (page_output "${PROJECT_BINARY_DIR}/${page}.1")
 | 
						set (page_output "${PROJECT_BINARY_DIR}/${page}.1")
 | 
				
			||||||
	list (APPEND project_MAN_PAGES "${page_output}")
 | 
						list (APPEND project_MAN_PAGES "${page_output}")
 | 
				
			||||||
	add_custom_command (OUTPUT ${page_output}
 | 
						if (ASCIIDOCTOR_EXECUTABLE)
 | 
				
			||||||
		COMMAND ${ASCIIDOCTOR_EXECUTABLE} -b manpage
 | 
							add_custom_command (OUTPUT ${page_output}
 | 
				
			||||||
			-a release-version=${project_VERSION}
 | 
								COMMAND ${ASCIIDOCTOR_EXECUTABLE} -b manpage
 | 
				
			||||||
			"${PROJECT_SOURCE_DIR}/${page}.adoc"
 | 
									-a release-version=${PROJECT_VERSION}
 | 
				
			||||||
			-o "${page_output}"
 | 
									-o "${page_output}"
 | 
				
			||||||
		DEPENDS ${page}.adoc
 | 
									"${PROJECT_SOURCE_DIR}/${page}.adoc"
 | 
				
			||||||
		COMMENT "Generating man page for ${page}" VERBATIM)
 | 
								DEPENDS ${page}.adoc
 | 
				
			||||||
endforeach (page)
 | 
								COMMENT "Generating man page for ${page}" VERBATIM)
 | 
				
			||||||
 | 
						elseif (A2X_EXECUTABLE)
 | 
				
			||||||
 | 
							add_custom_command (OUTPUT ${page_output}
 | 
				
			||||||
 | 
								COMMAND ${A2X_EXECUTABLE} --doctype manpage --format manpage
 | 
				
			||||||
 | 
									-a release-version=${PROJECT_VERSION}
 | 
				
			||||||
 | 
									-D "${PROJECT_BINARY_DIR}"
 | 
				
			||||||
 | 
									"${PROJECT_SOURCE_DIR}/${page}.adoc"
 | 
				
			||||||
 | 
								DEPENDS ${page}.adoc
 | 
				
			||||||
 | 
								COMMENT "Generating man page for ${page}" VERBATIM)
 | 
				
			||||||
 | 
						else ()
 | 
				
			||||||
 | 
							set (ASCIIMAN ${PROJECT_SOURCE_DIR}/liberty/tools/asciiman.awk)
 | 
				
			||||||
 | 
							add_custom_command (OUTPUT ${page_output}
 | 
				
			||||||
 | 
								COMMAND env LC_ALL=C asciidoc-release-version=${PROJECT_VERSION}
 | 
				
			||||||
 | 
									awk -f ${ASCIIMAN} "${PROJECT_SOURCE_DIR}/${page}.adoc"
 | 
				
			||||||
 | 
									> ${page_output}
 | 
				
			||||||
 | 
								DEPENDS ${page}.adoc ${ASCIIMAN}
 | 
				
			||||||
 | 
								COMMENT "Generating man page for ${page}" VERBATIM)
 | 
				
			||||||
 | 
						endif ()
 | 
				
			||||||
 | 
					endforeach ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_custom_target (docs ALL DEPENDS ${project_MAN_PAGES})
 | 
					add_custom_target (docs ALL DEPENDS ${project_MAN_PAGES})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -110,23 +149,19 @@ foreach (page ${project_MAN_PAGES})
 | 
				
			|||||||
	string (REGEX MATCH "\\.([0-9])$" manpage_suffix "${page}")
 | 
						string (REGEX MATCH "\\.([0-9])$" manpage_suffix "${page}")
 | 
				
			||||||
	install (FILES "${page}"
 | 
						install (FILES "${page}"
 | 
				
			||||||
		DESTINATION "${CMAKE_INSTALL_MANDIR}/man${CMAKE_MATCH_1}")
 | 
							DESTINATION "${CMAKE_INSTALL_MANDIR}/man${CMAKE_MATCH_1}")
 | 
				
			||||||
endforeach (page)
 | 
					endforeach ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# CPack
 | 
					# CPack
 | 
				
			||||||
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY
 | 
					set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "A shell for JSON-RPC 2.0")
 | 
				
			||||||
	"A shell for running JSON-RPC 2.0 queries")
 | 
					 | 
				
			||||||
set (CPACK_PACKAGE_VENDOR "Premysl Eric Janouch")
 | 
					set (CPACK_PACKAGE_VENDOR "Premysl Eric Janouch")
 | 
				
			||||||
set (CPACK_PACKAGE_CONTACT "Přemysl Eric Janouch <p@janouch.name>")
 | 
					set (CPACK_PACKAGE_CONTACT "Přemysl Eric Janouch <p@janouch.name>")
 | 
				
			||||||
set (CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
 | 
					set (CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
 | 
				
			||||||
set (CPACK_PACKAGE_VERSION_MAJOR ${project_VERSION_MAJOR})
 | 
					 | 
				
			||||||
set (CPACK_PACKAGE_VERSION_MINOR ${project_VERSION_MINOR})
 | 
					 | 
				
			||||||
set (CPACK_PACKAGE_VERSION_PATCH ${project_VERSION_PATCH})
 | 
					 | 
				
			||||||
set (CPACK_GENERATOR "TGZ;ZIP")
 | 
					set (CPACK_GENERATOR "TGZ;ZIP")
 | 
				
			||||||
set (CPACK_PACKAGE_FILE_NAME
 | 
					set (CPACK_PACKAGE_FILE_NAME
 | 
				
			||||||
	"${PROJECT_NAME}-${project_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
 | 
						"${PROJECT_NAME}-${PROJECT_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
 | 
				
			||||||
set (CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}-${project_VERSION}")
 | 
					set (CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}-${PROJECT_VERSION}")
 | 
				
			||||||
set (CPACK_SOURCE_GENERATOR "TGZ;ZIP")
 | 
					set (CPACK_SOURCE_GENERATOR "TGZ;ZIP")
 | 
				
			||||||
set (CPACK_SOURCE_IGNORE_FILES "/\\\\.git;/build;/CMakeLists.txt.user")
 | 
					set (CPACK_SOURCE_IGNORE_FILES "/\\\\.git;/build;/CMakeLists.txt.user")
 | 
				
			||||||
set (CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${project_VERSION}")
 | 
					set (CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
include (CPack)
 | 
					include (CPack)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
				
			|||||||
Copyright (c) 2014 - 2020, Přemysl Eric Janouch <p@janouch.name>
 | 
					Copyright (c) 2014 - 2022, Přemysl Eric Janouch <p@janouch.name>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Permission to use, copy, modify, and/or distribute this software for any
 | 
					Permission to use, copy, modify, and/or distribute this software for any
 | 
				
			||||||
purpose with or without fee is hereby granted.
 | 
					purpose with or without fee is hereby granted.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										34
									
								
								NEWS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								NEWS
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					1.2.0 (2024-12-24)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * Add a backend for co-processes, such as language servers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * Support reading OpenRPC documents from a file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * Respect the NO_COLOR environment variable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * Miscellaneous libedit (editline) fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * Miscellaneous portability improvements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * json-rpc-test-server: implement OpenRPC discovery
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * json-rpc-test-server: only serve regular files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * json-rpc-test-server: miscellaneous WebSocket fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.1.0 (2020-10-13)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * Add method name tab completion using OpenRPC information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * Bind M-Enter to insert a newline into the command line
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * json-rpc-test-server: fix a memory leak and request URI parsing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * Miscellaneous bug fixes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.0.0 (2020-09-05)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 * Initial release
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										40
									
								
								README.adoc
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								README.adoc
									
									
									
									
									
								
							@@ -2,22 +2,25 @@ json-rpc-shell
 | 
				
			|||||||
==============
 | 
					==============
 | 
				
			||||||
:compact-option:
 | 
					:compact-option:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
'json-rpc-shell' is a simple shell for running JSON-RPC 2.0 queries.
 | 
					'json-rpc-shell' is a shell for running JSON-RPC 2.0 queries.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This software has been created as a replacement for the following shell, which
 | 
					This software was originally created as a replacement for
 | 
				
			||||||
is written in Java: http://software.dzhuvinov.com/json-rpc-2.0-shell.html
 | 
					http://software.dzhuvinov.com/json-rpc-2.0-shell.html[a different shell] made by
 | 
				
			||||||
 | 
					Vladimir Dzhuvinov, in order to avoid Java, but has evolved since.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Features
 | 
					Features
 | 
				
			||||||
--------
 | 
					--------
 | 
				
			||||||
In addition to most of the features provided by Vladimir Dzhuvinov's shell
 | 
					In addition to most of the features provided by its predecessor, you will get
 | 
				
			||||||
you get the following niceties:
 | 
					the following niceties:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 - configurable JSON syntax highlight, which with prettyprinting turned on
 | 
					 - configurable JSON syntax highlight, which with prettyprinting turned on
 | 
				
			||||||
   helps you make sense of the results significantly
 | 
					   helps you make sense of the results significantly
 | 
				
			||||||
 - ability to pipe output through a shell command, so that you can view the
 | 
					 - ability to pipe output through a shell command, so that you can view the
 | 
				
			||||||
   results in your favourite editor or redirect them to a file
 | 
					   results in your favourite editor or redirect them to a file
 | 
				
			||||||
 - ability to edit the input line in your favourite editor as well with Alt+E
 | 
					 - ability to edit the input line in your favourite editor as well with Alt+E
 | 
				
			||||||
 - WebSockets (RFC 6455) can also be used as a transport rather than HTTP
 | 
					 - WebSocket (RFC 6455) can also be used as a transport rather than HTTP
 | 
				
			||||||
 | 
					 - even Language Server Protocol servers may be launched as a slave command
 | 
				
			||||||
 | 
					 - support for method name tab completion using OpenRPC discovery or file input
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Documentation
 | 
					Documentation
 | 
				
			||||||
-------------
 | 
					-------------
 | 
				
			||||||
@@ -26,19 +29,17 @@ The rest of this README will concern itself with externalities.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Packages
 | 
					Packages
 | 
				
			||||||
--------
 | 
					--------
 | 
				
			||||||
Regular releases are sporadic.  git master should be stable enough.  You can get
 | 
					Regular releases are sporadic.  git master should be stable enough.
 | 
				
			||||||
a package with the latest development version from Archlinux's AUR.
 | 
					You can get a package with the latest development version using Arch Linux's
 | 
				
			||||||
 | 
					https://aur.archlinux.org/packages/json-rpc-shell-git[AUR],
 | 
				
			||||||
 | 
					or as a https://git.janouch.name/p/nixexprs[Nix derivation].
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Building
 | 
					Building
 | 
				
			||||||
--------
 | 
					--------
 | 
				
			||||||
Build dependencies: CMake, pkg-config, asciidoctor,
 | 
					Build dependencies: CMake, pkg-config, liberty (included),
 | 
				
			||||||
                    liberty (included), http-parser (included) +
 | 
					 http-parser (included), asciidoctor or asciidoc (recommended but optional) +
 | 
				
			||||||
Runtime dependencies: libev, Jansson, cURL, openssl,
 | 
					Runtime dependencies:
 | 
				
			||||||
                      readline or libedit >= 2013-07-12,
 | 
					 libev, Jansson, cURL, openssl, readline or libedit >= 2013-07-12
 | 
				
			||||||
 | 
					 | 
				
			||||||
Avoid libedit if you can, in general it works but at the moment history is
 | 
					 | 
				
			||||||
acting up and I have no clue about fixing it.  Multiline editing is also
 | 
					 | 
				
			||||||
misbehaving there.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 $ git clone --recursive https://git.janouch.name/p/json-rpc-shell.git
 | 
					 $ git clone --recursive https://git.janouch.name/p/json-rpc-shell.git
 | 
				
			||||||
 $ mkdir json-rpc-shell/build
 | 
					 $ mkdir json-rpc-shell/build
 | 
				
			||||||
@@ -55,15 +56,12 @@ Or you can try telling CMake to make a package for you.  For Debian it is:
 | 
				
			|||||||
 $ cpack -G DEB
 | 
					 $ cpack -G DEB
 | 
				
			||||||
 # dpkg -i json-rpc-shell-*.deb
 | 
					 # dpkg -i json-rpc-shell-*.deb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Note that for versions of CMake before 2.8.9, you need to prefix `cpack` with
 | 
					 | 
				
			||||||
`fakeroot` or file ownership will end up wrong.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Test server
 | 
					Test server
 | 
				
			||||||
-----------
 | 
					-----------
 | 
				
			||||||
If you install development packages for libmagic, an included test server will
 | 
					If you install development packages for libmagic, an included test server will
 | 
				
			||||||
be built but not installed which provides a trivial JSON-RPC 2.0 service with
 | 
					be built but not installed which provides a trivial JSON-RPC 2.0 service with
 | 
				
			||||||
FastCGI, SCGI, and WebSocket interfaces.  It responds to `ping` and `date`
 | 
					FastCGI, SCGI, WebSocket and LSP-like co-process interfaces.  It responds to
 | 
				
			||||||
methods and it can serve static files.
 | 
					`ping` and `date`, supports OpenRPC discovery and it can serve static files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Contributing and Support
 | 
					Contributing and Support
 | 
				
			||||||
------------------------
 | 
					------------------------
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,14 +5,16 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Some distributions do add it, though
 | 
					# Some distributions do add it, though
 | 
				
			||||||
find_package (PkgConfig REQUIRED)
 | 
					find_package (PkgConfig REQUIRED)
 | 
				
			||||||
pkg_check_modules (LIBEV QUIET libev)
 | 
					pkg_check_modules (LibEV QUIET libev)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (NOT LIBEV_FOUND)
 | 
					set (required_vars LibEV_LIBRARIES)
 | 
				
			||||||
	find_path (LIBEV_INCLUDE_DIRS ev.h)
 | 
					if (NOT LibEV_FOUND)
 | 
				
			||||||
	find_library (LIBEV_LIBRARIES NAMES ev)
 | 
						find_path (LibEV_INCLUDE_DIRS ev.h)
 | 
				
			||||||
 | 
						find_library (LibEV_LIBRARIES NAMES ev)
 | 
				
			||||||
 | 
						list (APPEND required_vars LibEV_INCLUDE_DIRS)
 | 
				
			||||||
 | 
					endif ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (LIBEV_INCLUDE_DIRS AND LIBEV_LIBRARIES)
 | 
					include (FindPackageHandleStandardArgs)
 | 
				
			||||||
		set (LIBEV_FOUND TRUE)
 | 
					FIND_PACKAGE_HANDLE_STANDARD_ARGS (LibEV DEFAULT_MSG ${required_vars})
 | 
				
			||||||
	endif (LIBEV_INCLUDE_DIRS AND LIBEV_LIBRARIES)
 | 
					 | 
				
			||||||
endif (NOT LIBEV_FOUND)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mark_as_advanced (LibEV_LIBRARIES LibEV_INCLUDE_DIRS)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,10 +2,12 @@
 | 
				
			|||||||
#define CONFIG_H
 | 
					#define CONFIG_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define PROGRAM_NAME "${PROJECT_NAME}"
 | 
					#define PROGRAM_NAME "${PROJECT_NAME}"
 | 
				
			||||||
#define PROGRAM_VERSION "${project_VERSION}"
 | 
					#define PROGRAM_VERSION "${PROJECT_VERSION}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#cmakedefine HAVE_READLINE
 | 
					#cmakedefine HAVE_READLINE
 | 
				
			||||||
#cmakedefine HAVE_EDITLINE
 | 
					#cmakedefine HAVE_EDITLINE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#cmakedefine01 ICONV_ACCEPTS_TRANSLIT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif  // ! CONFIG_H
 | 
					#endif  // ! CONFIG_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
 Submodule http-parser updated: 5d414fcb4b...ec8b5ee63f
									
								
							@@ -1,21 +1,22 @@
 | 
				
			|||||||
json-rpc-shell(1)
 | 
					json-rpc-shell(1)
 | 
				
			||||||
=================
 | 
					=================
 | 
				
			||||||
:doctype: manpage
 | 
					:doctype: manpage
 | 
				
			||||||
:man manual: json-rpc-shell Manual
 | 
					:manmanual: json-rpc-shell Manual
 | 
				
			||||||
:man source: json-rpc-shell {release-version}
 | 
					:mansource: json-rpc-shell {release-version}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Name
 | 
					Name
 | 
				
			||||||
----
 | 
					----
 | 
				
			||||||
json-rpc-shell - a simple JSON-RPC 2.0 shell
 | 
					json-rpc-shell - a shell for JSON-RPC 2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Synopsis
 | 
					Synopsis
 | 
				
			||||||
--------
 | 
					--------
 | 
				
			||||||
*json-rpc-shell* [_OPTION_]... _ENDPOINT_
 | 
					*json-rpc-shell* [_OPTION_]... { _ENDPOINT_ | _COMMAND_ [_ARG_]... }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Description
 | 
					Description
 | 
				
			||||||
-----------
 | 
					-----------
 | 
				
			||||||
 | 
					:colon: :
 | 
				
			||||||
The _ENDPOINT_ must be either an HTTP or a WebSocket URL, with or without TLS
 | 
					The _ENDPOINT_ must be either an HTTP or a WebSocket URL, with or without TLS
 | 
				
			||||||
(i.e. one of the _http://_, _https://_, _ws://_, _wss://_ schemas).
 | 
					(i.e. one of the _http{colon}//_, _https{colon}//_, _ws://_, _wss://_ schemas).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*json-rpc-shell* will use it to send any JSON-RPC 2.0 requests you enter on its
 | 
					*json-rpc-shell* will use it to send any JSON-RPC 2.0 requests you enter on its
 | 
				
			||||||
command line. The server's response will be parsed and validated, stripping it
 | 
					command line. The server's response will be parsed and validated, stripping it
 | 
				
			||||||
@@ -27,11 +28,13 @@ processor.
 | 
				
			|||||||
Usage
 | 
					Usage
 | 
				
			||||||
~~~~~
 | 
					~~~~~
 | 
				
			||||||
Three things may appear on the internal command line, in a sequence.  The first
 | 
					Three things may appear on the internal command line, in a sequence.  The first
 | 
				
			||||||
one must always be the name of the JSON-RPC method to call, as a bare word,
 | 
					one is always the name of the JSON-RPC method to call, as a bare word, separated
 | 
				
			||||||
separated from the rest by white space.  Following that, you may enter two kinds
 | 
					from the rest by white space.  Following that, you may enter three kinds of JSON
 | 
				
			||||||
of JSON values.  If it is a string, a number, or a null value, it is taken as
 | 
					values.  If it is an object or an array, it constitutes the method parameters.
 | 
				
			||||||
the "id" to use for the request.  If it is an object or an array, it constitutes
 | 
					If it is a string or a number, it is taken as the "id" to use for the request,
 | 
				
			||||||
the method parameters.  Booleans may appear in neither.
 | 
					which would be chosen for you automatically if left unspecified.  Finally,
 | 
				
			||||||
 | 
					a null value indicates that the request should be sent as a notification,
 | 
				
			||||||
 | 
					lacking the ID completely.  Booleans cannot be used for anything.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The response to the method call may be piped through external commands, the same
 | 
					The response to the method call may be piped through external commands, the same
 | 
				
			||||||
way you would do it in a Unix shell.
 | 
					way you would do it in a Unix shell.
 | 
				
			||||||
@@ -41,13 +44,13 @@ this action as they might conflict with method names.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Options
 | 
					Options
 | 
				
			||||||
-------
 | 
					-------
 | 
				
			||||||
Controlling Output
 | 
					Controlling output
 | 
				
			||||||
~~~~~~~~~~~~~~~~~~
 | 
					~~~~~~~~~~~~~~~~~~
 | 
				
			||||||
*-p*, *--pretty*::
 | 
					*-c*, *--compact-output*::
 | 
				
			||||||
	Pretty-print responses, adding spaces and newlines where appropriate
 | 
						Do not pretty-print responses.  Normally, spaces and newlines are added
 | 
				
			||||||
	to improve readability.
 | 
						where appropriate to improve readability.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*--color* _WHEN_::
 | 
					*--color*=_WHEN_::
 | 
				
			||||||
	By default, when the output of the program is a terminal, JSON responses
 | 
						By default, when the output of the program is a terminal, JSON responses
 | 
				
			||||||
	are syntax-highlighted.  This corresponds to the _auto_ setting.  You may
 | 
						are syntax-highlighted.  This corresponds to the _auto_ setting.  You may
 | 
				
			||||||
	also set this to _always_ or _never_.  In either case, color is never
 | 
						also set this to _always_ or _never_.  In either case, color is never
 | 
				
			||||||
@@ -61,21 +64,29 @@ Controlling Output
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Protocol
 | 
					Protocol
 | 
				
			||||||
~~~~~~~~
 | 
					~~~~~~~~
 | 
				
			||||||
*-a*, *--auto-id*::
 | 
					*-n*, *--null-as-id*::
 | 
				
			||||||
	Choose message IDs automatically, in an increasing sequence.  Normally you
 | 
						Normally, entering a null JSON value on the command line causes
 | 
				
			||||||
	need to enter the ID on the command line manually, so as to distinguish
 | 
						a notification to be sent.  With this option, it is sent as the "id"
 | 
				
			||||||
	notifications from other requests.  Even with this option enabled, you can
 | 
						field of a normal request, which is discouraged by the specification.
 | 
				
			||||||
	still specify the ID, if you wish.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
*-t*, *--trust-all*::
 | 
					*-t*, *--trust-all*::
 | 
				
			||||||
	Trust all SSL/TLS certificates.  Useful in case that the certificate is
 | 
						Trust all SSL/TLS certificates.  Useful in case that the certificate is
 | 
				
			||||||
	self-signed, or when the CA isn't in your CA store.  Beware that this option
 | 
						self-signed, or when the CA isn't in your CA store.  Beware that this option
 | 
				
			||||||
	is about as good as using plain unencrypted HTTP.
 | 
						is about as good as using plain unencrypted HTTP.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*-o*, *--origin* _ORIGIN_::
 | 
					*-o* _ORIGIN_, *--origin*=_ORIGIN_::
 | 
				
			||||||
	Set the HTTP Origin header to _ORIGIN_.  Some servers may need this.
 | 
						Set the HTTP Origin header to _ORIGIN_.  Some servers may need this.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Program Information
 | 
					*-O*[__PATH__], *--openrpc*[**=**__PATH__]::
 | 
				
			||||||
 | 
						Call "rpc.discover" upon start-up in order to pull in OpenRPC data for
 | 
				
			||||||
 | 
						tab completion of method names.  If a path is given, it is read from a file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*-e*, *--execute*::
 | 
				
			||||||
 | 
						Rather than an _ENDPOINT_, accept a command line to execute and communicate
 | 
				
			||||||
 | 
						with using the JSON-RPC 2.0 protocol variation used in the Language Server
 | 
				
			||||||
 | 
						Protocol.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Program information
 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~
 | 
					~~~~~~~~~~~~~~~~~~~
 | 
				
			||||||
*-h*, *--help*::
 | 
					*-h*, *--help*::
 | 
				
			||||||
	Display a help message and exit.
 | 
						Display a help message and exit.
 | 
				
			||||||
@@ -83,11 +94,19 @@ Program Information
 | 
				
			|||||||
*-V*, *--version*::
 | 
					*-V*, *--version*::
 | 
				
			||||||
	Output version information and exit.
 | 
						Output version information and exit.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*--write-default-cfg*::
 | 
					*--write-default-cfg*[**=**__PATH__]::
 | 
				
			||||||
	Write a default configuration file, show its path and exit.
 | 
						Write a default configuration file, show its path and exit.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Environment
 | 
				
			||||||
 | 
					-----------
 | 
				
			||||||
 | 
					*VISUAL*, *EDITOR*::
 | 
				
			||||||
 | 
						The editor program to be launched by the M-e key binding.
 | 
				
			||||||
 | 
						If neither variable is set, it defaults to *vi*(1).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Files
 | 
					Files
 | 
				
			||||||
-----
 | 
					-----
 | 
				
			||||||
 | 
					*json-rpc-shell* follows the XDG Base Directory Specification.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_~/.config/json-rpc-shell/json-rpc-shell.conf_::
 | 
					_~/.config/json-rpc-shell/json-rpc-shell.conf_::
 | 
				
			||||||
	The configuration file, in which you can configure color output and
 | 
						The configuration file, in which you can configure color output and
 | 
				
			||||||
	CA certificate paths.  Use the *--write-default-cfg* option to create
 | 
						CA certificate paths.  Use the *--write-default-cfg* option to create
 | 
				
			||||||
@@ -106,15 +125,14 @@ requests, it is often convenient or even necessary to run a full text editor
 | 
				
			|||||||
in order to construct complex objects or arrays, and may even be used to import
 | 
					in order to construct complex objects or arrays, and may even be used to import
 | 
				
			||||||
data from elsewhere.  You can launch an editor for the current request using
 | 
					data from elsewhere.  You can launch an editor for the current request using
 | 
				
			||||||
the M-e key combination.  Both *readline*(3) and *editline*(7) also support
 | 
					the M-e key combination.  Both *readline*(3) and *editline*(7) also support
 | 
				
			||||||
multiline editing natively, though you need to press C-v C-j in order to insert
 | 
					multiline editing natively, press either M-Enter or C-v C-j in order to insert
 | 
				
			||||||
newlines.
 | 
					newlines.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WebSockets
 | 
					WebSocket
 | 
				
			||||||
~~~~~~~~~~
 | 
					~~~~~~~~~
 | 
				
			||||||
The JSON-RPC 2.0 specification doesn't say almost anything about underlying
 | 
					The JSON-RPC 2.0 specification doesn't say almost anything about underlying
 | 
				
			||||||
transports. As far as the author is aware, he is the only person combining it
 | 
					transports.  The way it's implemented here is that every request is sent as
 | 
				
			||||||
with WebSockets.  The way it's implemented here is that every request is sent as
 | 
					a single text message.  If it has an "id" field, i.e., it's not just
 | 
				
			||||||
a single text message.  If it has an "id" field, i.e. it's not just
 | 
					 | 
				
			||||||
a notification, the client waits for a message from the server in response.
 | 
					a notification, the client waits for a message from the server in response.
 | 
				
			||||||
Should any message arrive unexpectedly, you will receive a warning.
 | 
					Should any message arrive unexpectedly, you will receive a warning.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -123,57 +141,52 @@ the higher-level protocol (the "Sec-Ws-Protocol" HTTP field).
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Bugs
 | 
					Bugs
 | 
				
			||||||
----
 | 
					----
 | 
				
			||||||
The editline (libedit) frontend is more of a proof of concept that mostly seems
 | 
					The editline (libedit) frontend may exhibit some unexpected behaviour.
 | 
				
			||||||
to work but exhibits bugs that are not our fault.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Examples
 | 
					Examples
 | 
				
			||||||
--------
 | 
					--------
 | 
				
			||||||
Running some queries against json-rpc-test-server, included in the source
 | 
					Running some queries against json-rpc-test-server, included in the source
 | 
				
			||||||
distribution of this program (public services are hard to find):
 | 
					distribution of this program (public services are hard to find):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Pretty-printing and Manual IDs
 | 
					Methods without parameters
 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
					~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
				
			||||||
```
 | 
					 $ json-rpc-shell ws://localhost:1234
 | 
				
			||||||
$ json-rpc-shell -p ws://localhost:1234
 | 
					 json-rpc> ping
 | 
				
			||||||
json-rpc> date 1
 | 
					 "pong"
 | 
				
			||||||
{
 | 
					 json-rpc> date
 | 
				
			||||||
  "year": 2020,
 | 
					 {
 | 
				
			||||||
  "month": 9,
 | 
					   "year": 2020,
 | 
				
			||||||
  "day": 5,
 | 
					   "month": 9,
 | 
				
			||||||
  "hours": 2,
 | 
					   "day": 5,
 | 
				
			||||||
  "minutes": 23,
 | 
					   "hours": 2,
 | 
				
			||||||
  "seconds": 51
 | 
					   "minutes": 23,
 | 
				
			||||||
}
 | 
					   "seconds": 51
 | 
				
			||||||
```
 | 
					 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Notification With a Parameter
 | 
					Notification with a parameter
 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
					~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
				
			||||||
Notifications never produce a response, not even when the method is not known
 | 
					Notifications never produce a response, not even when the method is not known
 | 
				
			||||||
to the server:
 | 
					to the server:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					 $ json-rpc-shell ws://localhost:1234
 | 
				
			||||||
$ json-rpc-shell ws://localhost:1234
 | 
					 json-rpc> notify {"events": ["conquest", "war", "famine", "death"]} null
 | 
				
			||||||
json-rpc> notify {"events": ["conquest", "war", "famine", "death"]}
 | 
					 [Notification]
 | 
				
			||||||
[Notification]
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Piping In and Out
 | 
					Piping in and out
 | 
				
			||||||
~~~~~~~~~~~~~~~~~
 | 
					~~~~~~~~~~~~~~~~~
 | 
				
			||||||
GNU Readline always repeats the prompt, which makes this a bit less useful
 | 
					GNU Readline always repeats the prompt, which makes this a bit less useful
 | 
				
			||||||
for invoking from other programs:
 | 
					for invoking from other programs:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					 $ echo 'ping | jq ascii_upcase' | json-rpc-shell ws://localhost:1234
 | 
				
			||||||
$ echo 'ping | jq ascii_upcase' | json-rpc-shell -a ws://localhost:1234
 | 
					 json-rpc> ping | jq ascii_upcase
 | 
				
			||||||
json-rpc> ping | jq ascii_upcase
 | 
					 "PONG"
 | 
				
			||||||
"PONG"
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Reporting Bugs
 | 
					Reporting bugs
 | 
				
			||||||
--------------
 | 
					--------------
 | 
				
			||||||
Use https://git.janouch.name/p/json-rpc-shell to report bugs, request features,
 | 
					Use https://git.janouch.name/p/json-rpc-shell to report bugs, request features,
 | 
				
			||||||
or submit pull requests.
 | 
					or submit pull requests.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
See Also
 | 
					See also
 | 
				
			||||||
--------
 | 
					--------
 | 
				
			||||||
*jq*(1), *readline*(3) or *editline*(7)
 | 
					*jq*(1), *readline*(3) or *editline*(7)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1540
									
								
								json-rpc-shell.c
									
									
									
									
									
								
							
							
						
						
									
										1540
									
								
								json-rpc-shell.c
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,7 +1,7 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * json-rpc-test-server.c: JSON-RPC 2.0 demo server
 | 
					 * json-rpc-test-server.c: JSON-RPC 2.0 demo server
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (c) 2015 - 2020, Přemysl Eric Janouch <p@janouch.name>
 | 
					 * Copyright (c) 2015 - 2022, Přemysl Eric Janouch <p@janouch.name>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Permission to use, copy, modify, and/or distribute this software for any
 | 
					 * Permission to use, copy, modify, and/or distribute this software for any
 | 
				
			||||||
 * purpose with or without fee is hereby granted.
 | 
					 * purpose with or without fee is hereby granted.
 | 
				
			||||||
@@ -329,6 +329,7 @@ fcgi_muxer_on_get_values
 | 
				
			|||||||
	nv_parser.output = &values;
 | 
						nv_parser.output = &values;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fcgi_nv_parser_push (&nv_parser, parser->content.str, parser->content.len);
 | 
						fcgi_nv_parser_push (&nv_parser, parser->content.str, parser->content.len);
 | 
				
			||||||
 | 
						fcgi_nv_parser_free (&nv_parser);
 | 
				
			||||||
	const char *key = NULL;
 | 
						const char *key = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// No real-world servers seem to actually use multiplexing
 | 
						// No real-world servers seem to actually use multiplexing
 | 
				
			||||||
@@ -524,11 +525,11 @@ fcgi_muxer_push (struct fcgi_muxer *self, const void *data, size_t len)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// @}
 | 
					/// @}
 | 
				
			||||||
// --- WebSockets --------------------------------------------------------------
 | 
					// --- WebSocket ---------------------------------------------------------------
 | 
				
			||||||
/// @defgroup WebSockets
 | 
					/// @defgroup WebSocket
 | 
				
			||||||
/// @{
 | 
					/// @{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WebSockets aren't CGI-compatible, therefore we must handle the initial HTTP
 | 
					// WebSocket isn't CGI-compatible, therefore we must handle the initial HTTP
 | 
				
			||||||
// handshake ourselves.  Luckily it's not too much of a bother with http-parser.
 | 
					// handshake ourselves.  Luckily it's not too much of a bother with http-parser.
 | 
				
			||||||
// Typically there will be a normal HTTP server in front of us, proxying the
 | 
					// Typically there will be a normal HTTP server in front of us, proxying the
 | 
				
			||||||
// requests based on the URI.
 | 
					// requests based on the URI.
 | 
				
			||||||
@@ -536,7 +537,7 @@ fcgi_muxer_push (struct fcgi_muxer *self, const void *data, size_t len)
 | 
				
			|||||||
enum ws_handler_state
 | 
					enum ws_handler_state
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	WS_HANDLER_CONNECTING,              ///< Parsing HTTP
 | 
						WS_HANDLER_CONNECTING,              ///< Parsing HTTP
 | 
				
			||||||
	WS_HANDLER_OPEN,                    ///< Parsing WebSockets frames
 | 
						WS_HANDLER_OPEN,                    ///< Parsing WebSocket frames
 | 
				
			||||||
	WS_HANDLER_CLOSING,                 ///< Partial closure by us
 | 
						WS_HANDLER_CLOSING,                 ///< Partial closure by us
 | 
				
			||||||
	WS_HANDLER_FLUSHING,                ///< Just waiting for client EOF
 | 
						WS_HANDLER_FLUSHING,                ///< Just waiting for client EOF
 | 
				
			||||||
	WS_HANDLER_CLOSED                   ///< Dead, both sides closed
 | 
						WS_HANDLER_CLOSED                   ///< Dead, both sides closed
 | 
				
			||||||
@@ -850,6 +851,17 @@ ws_handler_on_close_timeout (EV_P_ ev_timer *watcher, int revents)
 | 
				
			|||||||
	self->close_cb (self, false /* half_close */);
 | 
						self->close_cb (self, false /* half_close */);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool ws_handler_fail_handshake (struct ws_handler *self,
 | 
				
			||||||
 | 
						const char *status, ...) ATTRIBUTE_SENTINEL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HTTP_101_SWITCHING_PROTOCOLS    "101 Switching Protocols"
 | 
				
			||||||
 | 
					#define HTTP_400_BAD_REQUEST            "400 Bad Request"
 | 
				
			||||||
 | 
					#define HTTP_405_METHOD_NOT_ALLOWED     "405 Method Not Allowed"
 | 
				
			||||||
 | 
					#define HTTP_408_REQUEST_TIMEOUT        "408 Request Timeout"
 | 
				
			||||||
 | 
					#define HTTP_417_EXPECTATION_FAILED     "407 Expectation Failed"
 | 
				
			||||||
 | 
					#define HTTP_426_UPGRADE_REQUIRED       "426 Upgrade Required"
 | 
				
			||||||
 | 
					#define HTTP_505_VERSION_NOT_SUPPORTED  "505 HTTP Version Not Supported"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
ws_handler_on_handshake_timeout (EV_P_ ev_timer *watcher, int revents)
 | 
					ws_handler_on_handshake_timeout (EV_P_ ev_timer *watcher, int revents)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -857,13 +869,7 @@ ws_handler_on_handshake_timeout (EV_P_ ev_timer *watcher, int revents)
 | 
				
			|||||||
	(void) revents;
 | 
						(void) revents;
 | 
				
			||||||
	struct ws_handler *self = watcher->data;
 | 
						struct ws_handler *self = watcher->data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// XXX: this is a no-op, since this currently doesn't even call shutdown
 | 
						ws_handler_fail_handshake (self, HTTP_408_REQUEST_TIMEOUT, NULL);
 | 
				
			||||||
	//   immediately but postpones it until later
 | 
					 | 
				
			||||||
	self->close_cb (self, true /* half_close */);
 | 
					 | 
				
			||||||
	self->state = WS_HANDLER_FLUSHING;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (self->on_close)
 | 
					 | 
				
			||||||
		self->on_close (self, WS_STATUS_ABNORMAL_CLOSURE, "handshake timeout");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	self->state = WS_HANDLER_CLOSED;
 | 
						self->state = WS_HANDLER_CLOSED;
 | 
				
			||||||
	self->close_cb (self, false /* half_close */);
 | 
						self->close_cb (self, false /* half_close */);
 | 
				
			||||||
@@ -1002,9 +1008,10 @@ ws_handler_on_headers_complete (http_parser *parser)
 | 
				
			|||||||
	if (self->have_header_value)
 | 
						if (self->have_header_value)
 | 
				
			||||||
		ws_handler_on_header_read (self);
 | 
							ws_handler_on_header_read (self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// We strictly require a protocol upgrade
 | 
						// We require a protocol upgrade.  1 is for "skip body", 2 is the same
 | 
				
			||||||
 | 
						// + "stop processing", return another number to indicate a problem here.
 | 
				
			||||||
	if (!parser->upgrade)
 | 
						if (!parser->upgrade)
 | 
				
			||||||
		return 2;
 | 
							return 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1013,17 +1020,10 @@ static int
 | 
				
			|||||||
ws_handler_on_url (http_parser *parser, const char *at, size_t len)
 | 
					ws_handler_on_url (http_parser *parser, const char *at, size_t len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ws_handler *self = parser->data;
 | 
						struct ws_handler *self = parser->data;
 | 
				
			||||||
	str_append_data (&self->value, at, len);
 | 
						str_append_data (&self->url, at, len);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define HTTP_101_SWITCHING_PROTOCOLS    "101 Switching Protocols"
 | 
					 | 
				
			||||||
#define HTTP_400_BAD_REQUEST            "400 Bad Request"
 | 
					 | 
				
			||||||
#define HTTP_405_METHOD_NOT_ALLOWED     "405 Method Not Allowed"
 | 
					 | 
				
			||||||
#define HTTP_417_EXPECTATION_FAILED     "407 Expectation Failed"
 | 
					 | 
				
			||||||
#define HTTP_426_UPGRADE_REQUIRED       "426 Upgrade Required"
 | 
					 | 
				
			||||||
#define HTTP_505_VERSION_NOT_SUPPORTED  "505 HTTP Version Not Supported"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
ws_handler_http_responsev (struct ws_handler *self,
 | 
					ws_handler_http_responsev (struct ws_handler *self,
 | 
				
			||||||
	const char *status, char *const *fields)
 | 
						const char *status, char *const *fields)
 | 
				
			||||||
@@ -1065,6 +1065,7 @@ ws_handler_fail_handshake (struct ws_handler *self, const char *status, ...)
 | 
				
			|||||||
	struct strv v = strv_make ();
 | 
						struct strv v = strv_make ();
 | 
				
			||||||
	while ((s = va_arg (ap, const char *)))
 | 
						while ((s = va_arg (ap, const char *)))
 | 
				
			||||||
		strv_append (&v, s);
 | 
							strv_append (&v, s);
 | 
				
			||||||
 | 
						strv_append (&v, "Connection: close");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	va_end (ap);
 | 
						va_end (ap);
 | 
				
			||||||
	ws_handler_http_responsev (self, status, v.vector);
 | 
						ws_handler_http_responsev (self, status, v.vector);
 | 
				
			||||||
@@ -1109,7 +1110,7 @@ ws_handler_finish_handshake (struct ws_handler *self)
 | 
				
			|||||||
	if (!connection || strcasecmp_ascii (connection, "Upgrade"))
 | 
						if (!connection || strcasecmp_ascii (connection, "Upgrade"))
 | 
				
			||||||
		FAIL_HANDSHAKE (HTTP_400_BAD_REQUEST);
 | 
							FAIL_HANDSHAKE (HTTP_400_BAD_REQUEST);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Check if we can actually upgrade the protocol to WebSockets
 | 
						// Check if we can actually upgrade the protocol to WebSocket
 | 
				
			||||||
	const char *upgrade = str_map_find (&self->headers, "Upgrade");
 | 
						const char *upgrade = str_map_find (&self->headers, "Upgrade");
 | 
				
			||||||
	struct http_protocol *offered_upgrades = NULL;
 | 
						struct http_protocol *offered_upgrades = NULL;
 | 
				
			||||||
	bool can_upgrade = false;
 | 
						bool can_upgrade = false;
 | 
				
			||||||
@@ -1267,11 +1268,13 @@ ws_handler_push (struct ws_handler *self, const void *data, size_t len)
 | 
				
			|||||||
		ev_timer_stop (EV_DEFAULT_ &self->handshake_timeout_watcher);
 | 
							ev_timer_stop (EV_DEFAULT_ &self->handshake_timeout_watcher);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (err == HPE_CB_headers_complete)
 | 
							if (err == HPE_CB_headers_complete)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
			print_debug ("WS handshake failed: %s", "missing `Upgrade' field");
 | 
								print_debug ("WS handshake failed: %s", "missing `Upgrade' field");
 | 
				
			||||||
		else
 | 
								FAIL_HANDSHAKE (HTTP_426_UPGRADE_REQUIRED,
 | 
				
			||||||
			print_debug ("WS handshake failed: %s",
 | 
									"Upgrade: websocket", SEC_WS_VERSION ": 13");
 | 
				
			||||||
				http_errno_description (err));
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							print_debug ("WS handshake failed: %s", http_errno_description (err));
 | 
				
			||||||
		FAIL_HANDSHAKE (HTTP_400_BAD_REQUEST);
 | 
							FAIL_HANDSHAKE (HTTP_400_BAD_REQUEST);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
@@ -1285,7 +1288,7 @@ static struct simple_config_item g_config_table[] =
 | 
				
			|||||||
	{ "bind_host",       NULL,              "Address of the server"          },
 | 
						{ "bind_host",       NULL,              "Address of the server"          },
 | 
				
			||||||
	{ "port_fastcgi",    "9000",            "Port to bind for FastCGI"       },
 | 
						{ "port_fastcgi",    "9000",            "Port to bind for FastCGI"       },
 | 
				
			||||||
	{ "port_scgi",       NULL,              "Port to bind for SCGI"          },
 | 
						{ "port_scgi",       NULL,              "Port to bind for SCGI"          },
 | 
				
			||||||
	{ "port_ws",         NULL,              "Port to bind for WebSockets"    },
 | 
						{ "port_ws",         NULL,              "Port to bind for WebSocket"     },
 | 
				
			||||||
	{ "pid_file",        NULL,              "Full path for the PID file"     },
 | 
						{ "pid_file",        NULL,              "Full path for the PID file"     },
 | 
				
			||||||
	// XXX: here belongs something like a web SPA that interfaces with us
 | 
						// XXX: here belongs something like a web SPA that interfaces with us
 | 
				
			||||||
	{ "static_root",     NULL,              "The root for static content"    },
 | 
						{ "static_root",     NULL,              "The root for static content"    },
 | 
				
			||||||
@@ -1445,6 +1448,39 @@ json_rpc_handler_info_cmp (const void *first, const void *second)
 | 
				
			|||||||
		((struct json_rpc_handler_info *) second)->method_name);
 | 
							((struct json_rpc_handler_info *) second)->method_name);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static json_t *
 | 
				
			||||||
 | 
					open_rpc_describe (const char *method, json_t *result)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return json_pack ("{sssoso}", "name", method, "params", json_pack ("[]"),
 | 
				
			||||||
 | 
							"result", json_pack ("{ssso}", "name", method, "schema", result));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This server rarely sees changes and we can afford to hardcode the schema
 | 
				
			||||||
 | 
					static json_t *
 | 
				
			||||||
 | 
					json_rpc_discover (struct server_context *ctx, json_t *params)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						(void) ctx;
 | 
				
			||||||
 | 
						(void) params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						json_t *info = json_pack ("{ssss}",
 | 
				
			||||||
 | 
							"title", PROGRAM_NAME, "version", PROGRAM_VERSION);
 | 
				
			||||||
 | 
						json_t *methods = json_pack ("[oooo]",
 | 
				
			||||||
 | 
							open_rpc_describe ("date", json_pack ("{ssso}", "type", "object",
 | 
				
			||||||
 | 
								"properties", json_pack ("{s{ss}s{ss}s{ss}s{ss}s{ss}s{ss}}",
 | 
				
			||||||
 | 
									"year",    "type", "number",
 | 
				
			||||||
 | 
									"month",   "type", "number",
 | 
				
			||||||
 | 
									"day",     "type", "number",
 | 
				
			||||||
 | 
									"hours",   "type", "number",
 | 
				
			||||||
 | 
									"minutes", "type", "number",
 | 
				
			||||||
 | 
									"seconds", "type", "number"))),
 | 
				
			||||||
 | 
							open_rpc_describe ("ping", json_pack ("{ss}", "type", "string")),
 | 
				
			||||||
 | 
							open_rpc_describe ("rpc.discover", json_pack ("{ss}", "$ref",
 | 
				
			||||||
 | 
								"https://github.com/open-rpc/meta-schema/raw/master/schema.json")),
 | 
				
			||||||
 | 
							open_rpc_describe ("wait", json_pack ("{ss}", "type", "null")));
 | 
				
			||||||
 | 
						return json_rpc_response (NULL, json_pack ("{sssoso}",
 | 
				
			||||||
 | 
							"openrpc", "1.2.6", "info", info, "methods", methods), NULL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static json_t *
 | 
					static json_t *
 | 
				
			||||||
json_rpc_ping (struct server_context *ctx, json_t *params)
 | 
					json_rpc_ping (struct server_context *ctx, json_t *params)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -1457,6 +1493,16 @@ json_rpc_ping (struct server_context *ctx, json_t *params)
 | 
				
			|||||||
	return json_rpc_response (NULL, json_string ("pong"), NULL);
 | 
						return json_rpc_response (NULL, json_string ("pong"), NULL);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static json_t *
 | 
				
			||||||
 | 
					json_rpc_wait (struct server_context *ctx, json_t *params)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						(void) ctx;
 | 
				
			||||||
 | 
						(void) params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sleep (1);
 | 
				
			||||||
 | 
						return json_rpc_response (NULL, json_null (), NULL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static json_t *
 | 
					static json_t *
 | 
				
			||||||
json_rpc_date (struct server_context *ctx, json_t *params)
 | 
					json_rpc_date (struct server_context *ctx, json_t *params)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@@ -1486,8 +1532,10 @@ process_json_rpc_request (struct server_context *ctx, json_t *request)
 | 
				
			|||||||
	// Eventually it might be better to move this into a map in the context.
 | 
						// Eventually it might be better to move this into a map in the context.
 | 
				
			||||||
	static struct json_rpc_handler_info handlers[] =
 | 
						static struct json_rpc_handler_info handlers[] =
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		{ "date", json_rpc_date },
 | 
							{ "date",         json_rpc_date     },
 | 
				
			||||||
		{ "ping", json_rpc_ping },
 | 
							{ "ping",         json_rpc_ping     },
 | 
				
			||||||
 | 
							{ "rpc.discover", json_rpc_discover },
 | 
				
			||||||
 | 
							{ "wait",         json_rpc_wait     },
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!json_is_object (request))
 | 
						if (!json_is_object (request))
 | 
				
			||||||
@@ -1544,7 +1592,6 @@ static void
 | 
				
			|||||||
process_json_rpc (struct server_context *ctx,
 | 
					process_json_rpc (struct server_context *ctx,
 | 
				
			||||||
	const void *data, size_t len, struct str *output)
 | 
						const void *data, size_t len, struct str *output)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					 | 
				
			||||||
	json_error_t e;
 | 
						json_error_t e;
 | 
				
			||||||
	json_t *request;
 | 
						json_t *request;
 | 
				
			||||||
	if (!(request = json_loadb (data, len, JSON_DECODE_ANY, &e)))
 | 
						if (!(request = json_loadb (data, len, JSON_DECODE_ANY, &e)))
 | 
				
			||||||
@@ -1619,15 +1666,37 @@ struct request_handler
 | 
				
			|||||||
	LIST_HEADER (struct request_handler)
 | 
						LIST_HEADER (struct request_handler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Install ourselves as the handler for the request, if applicable.
 | 
						/// Install ourselves as the handler for the request, if applicable.
 | 
				
			||||||
 | 
						/// If the request contains data, check it against CONTENT_LENGTH.
 | 
				
			||||||
 | 
						/// ("Transfer-Encoding: chunked" should be dechunked by the HTTP server,
 | 
				
			||||||
 | 
						/// however it is possible that it mishandles this situation.)
 | 
				
			||||||
	/// Sets @a continue_ to false if further processing should be stopped,
 | 
						/// Sets @a continue_ to false if further processing should be stopped,
 | 
				
			||||||
	/// meaning the request has already been handled.
 | 
						/// meaning the request has already been handled.
 | 
				
			||||||
 | 
						/// Note that starting the response before receiving all data denies you
 | 
				
			||||||
 | 
						/// the option of returning error status codes based on the data.
 | 
				
			||||||
	bool (*try_handle) (struct request *request,
 | 
						bool (*try_handle) (struct request *request,
 | 
				
			||||||
		struct str_map *headers, bool *continue_);
 | 
							struct str_map *headers, bool *continue_);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Handle incoming data.  "len == 0" means EOF.
 | 
						/// Handle incoming data.  "len == 0" means EOF.
 | 
				
			||||||
	/// Returns false if there is no more processing to be done.
 | 
						/// Returns false if there is no more processing to be done.
 | 
				
			||||||
	// FIXME: the EOF may or may not be delivered when request is cut short,
 | 
						/// EOF is never delivered on a network error (see client_read_loop()).
 | 
				
			||||||
	//   we should fix FastCGI not to deliver it on CONTENT_LENGTH mismatch
 | 
						// XXX: the EOF may or may not be delivered when the request is cut short:
 | 
				
			||||||
 | 
						//  - client_scgi delivers an EOF when it itself receives an EOF without
 | 
				
			||||||
 | 
						//    considering any mismatch, and it can deliver another one earlier
 | 
				
			||||||
 | 
						//    when the counter just goes down to 0... depends on what we return
 | 
				
			||||||
 | 
						//    from here upon the first occasion (whether we want to close).
 | 
				
			||||||
 | 
						//  - FCGI_ABORT_REQUEST /might/ not close the stdin and it /might/ cover
 | 
				
			||||||
 | 
						//    a CONTENT_LENGTH mismatch, since this callback wouldn't get invoked.
 | 
				
			||||||
 | 
						//    The FastCGI specification explicitly says to compare CONTENT_LENGTH
 | 
				
			||||||
 | 
						//    against the number of received bytes, which may only be smaller.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// We might want to adjust client_scgi and client_fcgi to not invoke
 | 
				
			||||||
 | 
						// request_push(EOF) when CONTENT_LENGTH hasn't been reached and remove
 | 
				
			||||||
 | 
						// the extra EOF generation from client_scgi (why is it there, does the
 | 
				
			||||||
 | 
						// server keep the connection open, or is it just a precaution?)
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// The finalization callback takes care of any needs to destruct data.
 | 
				
			||||||
 | 
						// If we handle this reliably in all clients, try_handle won't have to,
 | 
				
			||||||
 | 
						// as it will run in a stricter-than-CGI scenario.
 | 
				
			||||||
	bool (*push_cb) (struct request *request, const void *data, size_t len);
 | 
						bool (*push_cb) (struct request *request, const void *data, size_t len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Destroy the handler's data stored in the request object
 | 
						/// Destroy the handler's data stored in the request object
 | 
				
			||||||
@@ -1749,7 +1818,9 @@ request_handler_json_rpc_push
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// TODO: check buf.len against CONTENT_LENGTH; if it's less, then the
 | 
						// TODO: check buf.len against CONTENT_LENGTH; if it's less, then the
 | 
				
			||||||
	//   client hasn't been successful in transferring all of its data.
 | 
						//   client hasn't been successful in transferring all of its data.
 | 
				
			||||||
	//   See also comment on request_handler::push_cb.
 | 
						//   See also comment on request_handler::push_cb.  For JSON-RPC, though,
 | 
				
			||||||
 | 
						//   it shouldn't matter as an incomplete request will be invalid and
 | 
				
			||||||
 | 
						//   clients have no reason to append unnecessary trailing bytes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct str response = str_make ();
 | 
						struct str response = str_make ();
 | 
				
			||||||
	str_append (&response, "Status: 200 OK\n");
 | 
						str_append (&response, "Status: 200 OK\n");
 | 
				
			||||||
@@ -1866,8 +1937,13 @@ request_handler_static_try_handle
 | 
				
			|||||||
	char *path = xstrdup_printf ("%s%s", root, suffix);
 | 
						char *path = xstrdup_printf ("%s%s", root, suffix);
 | 
				
			||||||
	print_debug ("trying to statically serve %s", path);
 | 
						print_debug ("trying to statically serve %s", path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: check that this is a regular file
 | 
					 | 
				
			||||||
	FILE *fp = fopen (path, "rb");
 | 
						FILE *fp = fopen (path, "rb");
 | 
				
			||||||
 | 
						struct stat st = {};
 | 
				
			||||||
 | 
						if (fp && !fstat (fileno (fp), &st) && !S_ISREG (st.st_mode))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fclose (fp);
 | 
				
			||||||
 | 
							fp = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if (!fp)
 | 
						if (!fp)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		struct str response = str_make ();
 | 
							struct str response = str_make ();
 | 
				
			||||||
@@ -1912,8 +1988,8 @@ request_handler_static_try_handle
 | 
				
			|||||||
		request_write (request, buf, len);
 | 
							request_write (request, buf, len);
 | 
				
			||||||
	fclose (fp);
 | 
						fclose (fp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: this should rather not be returned all at once but in chunks;
 | 
						// TODO: this should rather not be returned all at once but in chunks
 | 
				
			||||||
	//   file read requests never return EAGAIN
 | 
						//   (consider Transfer-Encoding); file read requests never return EAGAIN
 | 
				
			||||||
	// TODO: actual file data should really be returned by a callback when
 | 
						// TODO: actual file data should really be returned by a callback when
 | 
				
			||||||
	//   the socket is writable with nothing to be sent (pumping the entire
 | 
						//   the socket is writable with nothing to be sent (pumping the entire
 | 
				
			||||||
	//   file all at once won't really work if it's huge).
 | 
						//   file all at once won't really work if it's huge).
 | 
				
			||||||
@@ -2047,6 +2123,8 @@ static void
 | 
				
			|||||||
client_shutdown (struct client *self)
 | 
					client_shutdown (struct client *self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	self->flushing = true;
 | 
						self->flushing = true;
 | 
				
			||||||
 | 
						// In case this shutdown is immediately followed by a close, try our best
 | 
				
			||||||
 | 
						(void) flush_queue (&self->write_queue, self->socket_fd);
 | 
				
			||||||
	ev_feed_event (EV_DEFAULT_ &self->write_watcher, EV_WRITE);
 | 
						ev_feed_event (EV_DEFAULT_ &self->write_watcher, EV_WRITE);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2358,14 +2436,15 @@ client_scgi_on_content (void *user_data, const void *data, size_t len)
 | 
				
			|||||||
		print_debug ("SCGI request got more data than CONTENT_LENGTH");
 | 
							print_debug ("SCGI request got more data than CONTENT_LENGTH");
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// We're in a slight disagreement with the specification since
 | 
						// We're in a slight disagreement with the SCGI specification since
 | 
				
			||||||
	// this tries to write output before it has read all the input
 | 
						// this tries to write output before it has read all the input
 | 
				
			||||||
	if (!request_push (&self->request, data, len))
 | 
						if (!request_push (&self->request, data, len))
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
 | 
						if ((self->remaining_content -= len))
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Signalise end of input to the request handler
 | 
						// Signalise end of input to the request handler
 | 
				
			||||||
	return (self->remaining_content -= len) != 0
 | 
						return request_push (&self->request, NULL, 0);
 | 
				
			||||||
		|| request_push (&self->request, NULL, 0);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
					// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
				
			||||||
@@ -2418,12 +2497,12 @@ client_scgi_create (EV_P_ int sock_fd)
 | 
				
			|||||||
	return &self->client;
 | 
						return &self->client;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// --- WebSockets client handler -----------------------------------------------
 | 
					// --- WebSocket client handler ------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct client_ws
 | 
					struct client_ws
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct client client;               ///< Parent class
 | 
						struct client client;               ///< Parent class
 | 
				
			||||||
	struct ws_handler handler;          ///< WebSockets connection handler
 | 
						struct ws_handler handler;          ///< WebSocket connection handler
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool
 | 
					static bool
 | 
				
			||||||
@@ -2514,6 +2593,165 @@ client_ws_create (EV_P_ int sock_fd)
 | 
				
			|||||||
	return &self->client;
 | 
						return &self->client;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// --- Co-process client -------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is mostly copied over from json-rpc-shell.c, only a bit simplified.
 | 
				
			||||||
 | 
					// We're giving up on header parsing in order to keep this small.
 | 
				
			||||||
 | 
					struct co_context
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct server_context *ctx;         ///< Server context
 | 
				
			||||||
 | 
						struct str message;                 ///< Message data
 | 
				
			||||||
 | 
						struct http_parser parser;          ///< HTTP parser
 | 
				
			||||||
 | 
						bool pending_fake_starter;          ///< Start of message?
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					client_co_on_message_begin (http_parser *parser)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct co_context *self = parser->data;
 | 
				
			||||||
 | 
						str_reset (&self->message);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					client_co_on_body (http_parser *parser, const char *at, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct co_context *self = parser->data;
 | 
				
			||||||
 | 
						str_append_data (&self->message, at, len);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					client_co_on_message_complete (http_parser *parser)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct co_context *self = parser->data;
 | 
				
			||||||
 | 
						http_parser_pause (&self->parser, true);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// The LSP incorporates a very thin subset of RFC 822, and it so happens
 | 
				
			||||||
 | 
					// that we may simply reuse the full HTTP parser here, with a small hack.
 | 
				
			||||||
 | 
					static const http_parser_settings client_co_http_settings =
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						.on_message_begin    = client_co_on_message_begin,
 | 
				
			||||||
 | 
						.on_body             = client_co_on_body,
 | 
				
			||||||
 | 
						.on_message_complete = client_co_on_message_complete,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					client_co_respond (const struct str *buf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct str wrapped = str_make();
 | 
				
			||||||
 | 
						str_append_printf (&wrapped,
 | 
				
			||||||
 | 
							"Content-Length: %zu\r\n"
 | 
				
			||||||
 | 
							"Content-Type: application/json; charset=utf-8\r\n"
 | 
				
			||||||
 | 
							"\r\n", buf->len);
 | 
				
			||||||
 | 
						str_append_data (&wrapped, buf->str, buf->len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (write (STDOUT_FILENO, wrapped.str, wrapped.len)
 | 
				
			||||||
 | 
							!= (ssize_t) wrapped.len)
 | 
				
			||||||
 | 
							exit_fatal ("write: %s", strerror (errno));
 | 
				
			||||||
 | 
						str_free (&wrapped);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					client_co_inject_starter (struct co_context *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// The default "Connection: keep-alive" maps well here.
 | 
				
			||||||
 | 
						// We cannot feed this line into the parser from within callbacks.
 | 
				
			||||||
 | 
						static const char starter[] = "POST / HTTP/1.1\r\n";
 | 
				
			||||||
 | 
						http_parser_pause (&self->parser, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t n_parsed = http_parser_execute (&self->parser,
 | 
				
			||||||
 | 
							&client_co_http_settings, starter, sizeof starter - 1);
 | 
				
			||||||
 | 
						enum http_errno err = HTTP_PARSER_ERRNO (&self->parser);
 | 
				
			||||||
 | 
						if (n_parsed != sizeof starter - 1 || err != HPE_OK)
 | 
				
			||||||
 | 
							exit_fatal ("protocol failure: %s", http_errno_description (err));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					client_co_process (struct co_context *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct str *message = &self->message;
 | 
				
			||||||
 | 
						struct str response = str_make ();
 | 
				
			||||||
 | 
						process_json_rpc (self->ctx, message->str, message->len, &response);
 | 
				
			||||||
 | 
						if (response.len)
 | 
				
			||||||
 | 
							client_co_respond (&response);
 | 
				
			||||||
 | 
						str_free (&response);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					client_co_parse (struct co_context *self, const char *data, size_t len,
 | 
				
			||||||
 | 
						size_t *n_parsed)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (self->pending_fake_starter)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							self->pending_fake_starter = false;
 | 
				
			||||||
 | 
							client_co_inject_starter (self);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*n_parsed = http_parser_execute
 | 
				
			||||||
 | 
							(&self->parser, &client_co_http_settings, data, len);
 | 
				
			||||||
 | 
						if (self->parser.upgrade)
 | 
				
			||||||
 | 
							exit_fatal ("protocol failure: %s", "unsupported upgrade attempt");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enum http_errno err = HTTP_PARSER_ERRNO (&self->parser);
 | 
				
			||||||
 | 
						if (err == HPE_PAUSED)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							self->pending_fake_starter = true;
 | 
				
			||||||
 | 
							client_co_process (self);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else if (err != HPE_OK)
 | 
				
			||||||
 | 
							exit_fatal ("protocol failure: %s", http_errno_description (err));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					client_co_on_data (struct co_context *self, const char *data, size_t len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t n_parsed = 0;
 | 
				
			||||||
 | 
						do
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							client_co_parse (self, data, len, &n_parsed);
 | 
				
			||||||
 | 
							data += n_parsed;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						while ((len -= n_parsed));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					client_co_run (struct server_context *ctx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct co_context self = {};
 | 
				
			||||||
 | 
						self.ctx = ctx;
 | 
				
			||||||
 | 
						self.message = str_make ();
 | 
				
			||||||
 | 
						http_parser_init (&self.parser, HTTP_REQUEST);
 | 
				
			||||||
 | 
						self.parser.data = &self;
 | 
				
			||||||
 | 
						self.pending_fake_starter = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						hard_assert (set_blocking (STDIN_FILENO, false));
 | 
				
			||||||
 | 
						struct str buf = str_make ();
 | 
				
			||||||
 | 
						struct pollfd pfd = { .fd = STDIN_FILENO, .events = POLLIN };
 | 
				
			||||||
 | 
						while (true)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (poll (&pfd, 1, -1) <= 0)
 | 
				
			||||||
 | 
								exit_fatal ("poll: %s", strerror (errno));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							str_remove_slice (&buf, 0, buf.len);
 | 
				
			||||||
 | 
							enum socket_io_result result = socket_io_try_read (pfd.fd, &buf);
 | 
				
			||||||
 | 
							int errno_saved = errno;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (buf.len)
 | 
				
			||||||
 | 
								client_co_on_data (&self, buf.str, buf.len);
 | 
				
			||||||
 | 
							if (result == SOCKET_IO_ERROR)
 | 
				
			||||||
 | 
								exit_fatal ("read: %s", strerror (errno_saved));
 | 
				
			||||||
 | 
							if (result == SOCKET_IO_EOF)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						str_free (&buf);
 | 
				
			||||||
 | 
						str_free (&self.message);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// --- Basic server stuff ------------------------------------------------------
 | 
					// --- Basic server stuff ------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct client *(*client_create_fn) (EV_P_ int sock_fd);
 | 
					typedef struct client *(*client_create_fn) (EV_P_ int sock_fd);
 | 
				
			||||||
@@ -2877,11 +3115,12 @@ daemonize (struct server_context *ctx)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
parse_program_arguments (int argc, char **argv)
 | 
					parse_program_arguments (int argc, char **argv, bool *running_as_slave)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	static const struct opt opts[] =
 | 
						static const struct opt opts[] =
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		{ 't', "test", NULL, 0, "self-test" },
 | 
							{ 't', "test", NULL, 0, "self-test" },
 | 
				
			||||||
 | 
							{ 's', "slave", NULL, 0, "co-process mode" },
 | 
				
			||||||
		{ 'd', "debug", NULL, 0, "run in debug mode" },
 | 
							{ 'd', "debug", NULL, 0, "run in debug mode" },
 | 
				
			||||||
		{ 'h', "help", NULL, 0, "display this help and exit" },
 | 
							{ 'h', "help", NULL, 0, "display this help and exit" },
 | 
				
			||||||
		{ 'V', "version", NULL, 0, "output version information and exit" },
 | 
							{ 'V', "version", NULL, 0, "output version information and exit" },
 | 
				
			||||||
@@ -2901,6 +3140,9 @@ parse_program_arguments (int argc, char **argv)
 | 
				
			|||||||
	case 't':
 | 
						case 't':
 | 
				
			||||||
		test_main (argc, argv);
 | 
							test_main (argc, argv);
 | 
				
			||||||
		exit (EXIT_SUCCESS);
 | 
							exit (EXIT_SUCCESS);
 | 
				
			||||||
 | 
						case 's':
 | 
				
			||||||
 | 
							*running_as_slave = true;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
	case 'd':
 | 
						case 'd':
 | 
				
			||||||
		g_debug_mode = true;
 | 
							g_debug_mode = true;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
@@ -2933,7 +3175,8 @@ parse_program_arguments (int argc, char **argv)
 | 
				
			|||||||
int
 | 
					int
 | 
				
			||||||
main (int argc, char *argv[])
 | 
					main (int argc, char *argv[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	parse_program_arguments (argc, argv);
 | 
						bool running_as_a_slave = false;
 | 
				
			||||||
 | 
						parse_program_arguments (argc, argv, &running_as_a_slave);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	print_status (PROGRAM_NAME " " PROGRAM_VERSION " starting");
 | 
						print_status (PROGRAM_NAME " " PROGRAM_VERSION " starting");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2948,6 +3191,15 @@ main (int argc, char *argv[])
 | 
				
			|||||||
		exit (EXIT_FAILURE);
 | 
							exit (EXIT_FAILURE);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// There's a lot of unnecessary left-over scaffolding in this program,
 | 
				
			||||||
 | 
						// for testing purposes assume that everything is synchronous
 | 
				
			||||||
 | 
						if (running_as_a_slave)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							client_co_run (&ctx);
 | 
				
			||||||
 | 
							server_context_free (&ctx);
 | 
				
			||||||
 | 
							return EXIT_SUCCESS;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct ev_loop *loop;
 | 
						struct ev_loop *loop;
 | 
				
			||||||
	if (!(loop = EV_DEFAULT))
 | 
						if (!(loop = EV_DEFAULT))
 | 
				
			||||||
		exit_fatal ("libev initialization failed");
 | 
							exit_fatal ("libev initialization failed");
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								liberty
									
									
									
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								liberty
									
									
									
									
									
								
							 Submodule liberty updated: 1a76b2032e...1930f138d4
									
								
							
		Reference in New Issue
	
	Block a user