Привет. Сегодня мы напишем простой kotlin http server без использования сторонних библиотек. Наш сервер будет принимать get запросы и параметры из адресной строки. Для чего это нужно и почему бы не использовать другие средства? Потому что наша цель — просто just 4fun, ну и заодно разобраться в общих чертах как оно работает. Лучшим решением для того чтобы создать сервер на kotlin будут сторонние фреймворки и библиотеки, такие как — Ktor, Spring и т.д.
Kotlin Http Server — начало
В Java есть готовый класс HttpServer, который реализует простой HTTP-сервер, привязанный к IP и номеру порта и прослушивает входящие TCP-соединения от клиентов по этому адресу. Подкласс HttpsServer реализует сервер, который обрабатывает запросы HTTPS.

Приступим к делу. Функция simpleServer принимает на вход параметр port, где мы указываем порт, на котором будет работать наш сервер. В ней создаем контекст, то есть путь по которому мы будем получать данные от http server.
Затем добавляем заголовок «Content-type», «text/plain» и создаем OutputStream, в который передадим текст «Hello from server!». С помощью метода write передаем строку в поток. Не забываем вызвать encodeToByteArray, чтобы конвертировать string в массив байт. Методом flush отдаем данные. Запускаем при помощи HttpServer.start.
fun simpleServer(port: Int) {
HttpServer.create(InetSocketAddress(port), 0).apply {
createContext("/api") { http ->
http.responseHeaders.add("Content-type", "text/plain")
http.sendResponseHeaders(200, 0)
val os: OutputStream = http.responseBody
os.write("Hello from server!".encodeToByteArray())
os.flush()
http.close()
}
start()
}
}
Если мы откроем браузер и перейдем по адресу 127.0.0.1/api, то увидим приветственную надпись — «Hello from server!»
Дополнительные методы
Все хорошо, но кроме get запроса мы можем послать post и получим ошибку. Ввиду того, что пример этот учебный, давайте ограничимся только get. Так же, неплохо было бы добавить потоков, чтобы сервак выдерживал минимальную нагрузку.
Добавляем пул потоков в количестве 10 штук
val threadPool = Executors.newFixedThreadPool(10) as ThreadPoolExecutor
Затем устанавливаем его с помощью метода setExecutor. Для наглядности, допишем вывод в консоль состояние работы.
fun simpleServer(port: Int) {
HttpServer.create(InetSocketAddress(port), 0).apply {
setExecutor(threadPool);
println("Server runs at: 127.0.0.1:${address.port}")
createContext("/api") { http ->
when(http.requestMethod) {
"GET" -> {
http.responseHeaders.add("Content-type", "text/plain")
http.sendResponseHeaders(200, 0)
val os: OutputStream = http.responseBody
if (http.requestURI.toString().indexOf("?") > 0) {
os.write(getParams(http.requestURI.toString()).toString().encodeToByteArray())
os.flush()
} else {
os.write("Hello from server!".encodeToByteArray())
os.flush()
}
http.close()
}
"POST" -> { http.sendResponseHeaders(405, 0) }
}
}
start()
}
}
В блоке when проверим метод запроса, и если он «GET», то выполняем код, а если «POST» — отправляем заголовок с кодом ошибки 405, то есть о том, что данный метод не поддерживается. Уже неплохо, но чего-то все равно не хватает…
HttpServer параметры из url
Как и любой сервер, даже самый примитивный, наш должен уметь получать параметры из url. Сделать это можно разными способами. Но мы пойдем по простому пути и накидаем функцию, которая будет парсить ссылку вида «/api?limt=10&page=1» и т.д.
fun getParams(paramString: String): Map<String,String> {
val params = mutableMapOf<String,String>()
paramString
.substring(paramString.indexOf("?")+1, paramString.length)
.split(Regex("&"))
.forEach {
params.put(it.split("=")[0],it.split("=")[1])
}
return params.toMap()
}
Метод getParams на вход принимает строку, которую обрезает с конца и до символа «?». Затем разбивает ее на массив по «=». Левую часть мы кладем в Map в качестве названия параметра сервера, а правую часть в виде значения.
Чтобы увидеть get-параметры в ответе, можно добавить
os.write(getParams(http.requestURI.toString()).toString().encodeToByteArray())
Пора проверить как все работает. В главном методе main запускаем simpleServer, передав в качестве аргумента номер порта
fun main(args: Array<String>) {
simpleServer(8080)
}
В консоль выводится сообщение: Server runs at: 127.0.0.1:8080. Теперь запускаем любой браузер на ПК и переходим по адресу 127.0.0.1:8080/api где укажем два параметра: title равный hello и port со значением 8080.

В итоге, мы видим искомый ответ в браузере. Если мы передаем параметры в url, то видим их в теле страницы, если нет — то отображается наша приветственная надпись
Выводы
Целью статьи не было разобрать «по косточкам» работу HttpServer, а просто показать базовые принципы работы. К тому же, если начать писать свой «сервак», добавив:
- все типы запросов (post, put, update…)
- поддержку cookie
- сессии
- авторизацию O-Auth и многое другое
…то в конечном счете получится один из фреймворков-велосипедов. Необходимо ли это? Не уверен, ведь есть ktor, Springboot и еще много хороших инструментов. В следующих статьях мы возьмем один из них и набросаем REST API сервис с генерацией пользователей. Конечно, зальем его на heroku и протестируем.
Всем пока, и спасибо за внимание!