2023-10-17 12:31:13 +08:00
// Copyright 2023, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
2022-06-12 13:39:48 -07:00
package main
import (
2022-07-01 12:17:19 -07:00
"context"
2023-08-31 22:04:31 -07:00
"encoding/base64"
2022-06-12 13:39:48 -07:00
"encoding/json"
2022-06-16 22:22:47 -07:00
"errors"
2022-06-12 13:39:48 -07:00
"fmt"
2023-12-22 17:55:20 -08:00
"html"
2023-08-31 22:04:31 -07:00
"io"
2022-06-16 22:22:47 -07:00
"io/fs"
2022-10-31 12:40:45 -07:00
"log"
2023-08-31 22:04:31 -07:00
"mime/multipart"
2022-06-12 13:39:48 -07:00
"net/http"
"os"
2023-03-12 15:16:49 -07:00
"os/signal"
2024-03-11 15:07:29 -07:00
"path"
2023-08-31 22:04:31 -07:00
"path/filepath"
"regexp"
2023-11-09 16:26:44 -08:00
"runtime"
2022-07-15 01:57:45 -07:00
"runtime/debug"
2022-09-03 23:57:05 -07:00
"strconv"
2022-06-13 11:11:56 -07:00
"strings"
2022-06-14 22:39:09 -07:00
"sync"
2022-10-30 13:05:53 -07:00
"syscall"
2022-06-12 13:39:48 -07:00
"time"
2022-06-13 11:11:56 -07:00
"github.com/google/uuid"
2022-06-12 13:39:48 -07:00
"github.com/gorilla/mux"
2024-01-16 16:11:04 -08:00
"github.com/wavetermdev/waveterm/waveshell/pkg/base"
2023-10-16 13:30:10 -07:00
"github.com/wavetermdev/waveterm/waveshell/pkg/packet"
"github.com/wavetermdev/waveterm/waveshell/pkg/server"
2024-02-15 17:42:43 -08:00
"github.com/wavetermdev/waveterm/waveshell/pkg/wlog"
2023-10-16 13:30:10 -07:00
"github.com/wavetermdev/waveterm/wavesrv/pkg/cmdrunner"
"github.com/wavetermdev/waveterm/wavesrv/pkg/pcloud"
2024-03-13 23:59:20 -07:00
"github.com/wavetermdev/waveterm/wavesrv/pkg/promptenc"
2023-12-15 17:43:54 -08:00
"github.com/wavetermdev/waveterm/wavesrv/pkg/releasechecker"
2023-10-16 13:30:10 -07:00
"github.com/wavetermdev/waveterm/wavesrv/pkg/remote"
"github.com/wavetermdev/waveterm/wavesrv/pkg/rtnstate"
"github.com/wavetermdev/waveterm/wavesrv/pkg/scbase"
"github.com/wavetermdev/waveterm/wavesrv/pkg/scpacket"
"github.com/wavetermdev/waveterm/wavesrv/pkg/scws"
"github.com/wavetermdev/waveterm/wavesrv/pkg/sstore"
2024-03-25 20:20:52 -07:00
"github.com/wavetermdev/waveterm/wavesrv/pkg/telemetry"
2023-10-16 13:30:10 -07:00
"github.com/wavetermdev/waveterm/wavesrv/pkg/wsshell"
2022-06-12 13:39:48 -07:00
)
2022-12-20 16:16:46 -08:00
type WebFnType = func ( http . ResponseWriter , * http . Request )
2022-06-12 13:39:48 -07:00
const HttpReadTimeout = 5 * time . Second
const HttpWriteTimeout = 21 * time . Second
const HttpMaxHeaderBytes = 60000
const HttpTimeoutDuration = 21 * time . Second
2023-11-01 01:26:19 -07:00
const MainServerAddr = "127.0.0.1:1619" // wavesrv, P=16, S=19, PS=1619
const WebSocketServerAddr = "127.0.0.1:1623" // wavesrv:websocket, P=16, W=23, PW=1623
2023-08-14 17:04:16 -07:00
const MainServerDevAddr = "127.0.0.1:8090"
const WebSocketServerDevAddr = "127.0.0.1:8091"
2022-06-16 22:22:47 -07:00
const WSStateReconnectTime = 30 * time . Second
const WSStatePacketChSize = 20
2022-06-15 18:13:16 -07:00
2023-01-25 22:49:50 -08:00
const InitialTelemetryWait = 30 * time . Second
const TelemetryTick = 30 * time . Minute
const TelemetryInterval = 8 * time . Hour
2023-08-31 22:04:31 -07:00
const MaxWriteFileMemSize = 20 * ( 1024 * 1024 ) // 20M
2024-02-23 17:44:00 -08:00
// these are set at build time
var WaveVersion = "v0.0.0"
var BuildTime = "0"
2022-06-16 22:22:47 -07:00
var GlobalLock = & sync . Mutex {}
2022-07-13 14:16:08 -07:00
var WSStateMap = make ( map [ string ] * scws . WSState ) // clientid -> WsState
2023-03-12 15:24:53 -07:00
var shutdownOnce sync . Once
2023-08-31 22:04:31 -07:00
var ContentTypeHeaderValidRe = regexp . MustCompile ( `^\w+/[\w.+-]+$` )
2022-06-12 13:39:48 -07:00
2023-01-16 23:36:52 -08:00
type ClientActiveState struct {
Fg bool `json:"fg"`
Active bool `json:"active"`
Open bool `json:"open"`
}
2023-12-22 17:55:20 -08:00
// Error constants
const (
ErrorDecodingJson = "error decoding json: %w"
ErrorPanic = "panic: %v"
ErrorInvalidScreenId = "invalid screenid: %v"
ErrorInvalidLineId = "invalid lineid: %v"
)
// Header constants
const (
CacheControlHeaderKey = "Cache-Control"
CacheControlHeaderNoCache = "no-cache"
ContentTypeHeaderKey = "Content-Type"
ContentTypeJson = "application/json"
)
2022-07-13 14:16:08 -07:00
func setWSState ( state * scws . WSState ) {
2022-06-16 22:22:47 -07:00
GlobalLock . Lock ()
defer GlobalLock . Unlock ()
WSStateMap [ state . ClientId ] = state
}
2022-07-13 14:16:08 -07:00
func getWSState ( clientId string ) * scws . WSState {
2022-06-16 22:22:47 -07:00
GlobalLock . Lock ()
defer GlobalLock . Unlock ()
return WSStateMap [ clientId ]
}
func removeWSStateAfterTimeout ( clientId string , connectTime time . Time , waitDuration time . Duration ) {
go func () {
time . Sleep ( waitDuration )
GlobalLock . Lock ()
defer GlobalLock . Unlock ()
state := WSStateMap [ clientId ]
if state == nil || state . ConnectTime != connectTime {
return
}
delete ( WSStateMap , clientId )
2022-07-13 14:16:08 -07:00
state . UnWatchScreen ()
2022-06-16 22:22:47 -07:00
}()
}
2022-06-12 13:39:48 -07:00
func HandleWs ( w http . ResponseWriter , r * http . Request ) {
shell , err := wsshell . StartWS ( w , r )
if err != nil {
2022-10-31 12:40:45 -07:00
log . Printf ( "WebSocket Upgrade Failed %T: %v\n" , w , err )
2022-06-12 13:39:48 -07:00
return
}
2022-06-16 22:22:47 -07:00
defer shell . Conn . Close ()
clientId := r . URL . Query (). Get ( "clientid" )
if clientId == "" {
close ( shell . WriteChan )
return
}
state := getWSState ( clientId )
if state == nil {
2024-03-14 00:22:59 -07:00
state = scws . MakeWSState ( clientId , scbase . WaveAuthKey )
2022-07-13 14:16:08 -07:00
state . ReplaceShell ( shell )
2022-06-16 22:22:47 -07:00
setWSState ( state )
} else {
2022-07-13 14:16:08 -07:00
state . UpdateConnectTime ()
state . ReplaceShell ( shell )
2022-06-16 22:22:47 -07:00
}
2022-07-13 14:16:08 -07:00
stateConnectTime := state . GetConnectTime ()
2022-06-14 22:39:09 -07:00
defer func () {
2022-06-16 22:22:47 -07:00
removeWSStateAfterTimeout ( clientId , stateConnectTime , WSStateReconnectTime )
2022-06-12 13:39:48 -07:00
}()
2022-10-31 12:40:45 -07:00
log . Printf ( "WebSocket opened %s %s\n" , state . ClientId , shell . RemoteAddr )
2022-07-13 14:16:08 -07:00
state . RunWSRead ()
2022-06-12 13:39:48 -07:00
}
2022-06-17 15:30:42 -07:00
// todo: sync multiple writes to the same fifoName into a single go-routine and do liveness checking on fifo
// if this returns an error, likely the fifo is dead and the cmd should be marked as 'done'
func writeToFifo ( fifoName string , data [] byte ) error {
rwfd , err := os . OpenFile ( fifoName , os . O_RDWR , 0600 )
if err != nil {
return err
}
defer rwfd . Close ()
fifoWriter , err := os . OpenFile ( fifoName , os . O_WRONLY , 0600 ) // blocking open (open won't block because of rwfd)
if err != nil {
return err
}
defer fifoWriter . Close ()
// this *could* block if the fifo buffer is full
// unlikely because if the reader is dead, and len(data) < pipe size, then the buffer will be empty and will clear after rwfd is closed
_ , err = fifoWriter . Write ( data )
if err != nil {
return err
}
return nil
}
2022-09-25 00:26:33 -07:00
func HandleGetClientData ( w http . ResponseWriter , r * http . Request ) {
cdata , err := sstore . EnsureClientData ( r . Context ())
if err != nil {
WriteJsonError ( w , err )
return
}
2023-05-08 16:06:51 -07:00
cdata = cdata . Clean ()
2022-09-25 00:26:33 -07:00
WriteJsonSuccess ( w , cdata )
}
func HandleSetWinSize ( w http . ResponseWriter , r * http . Request ) {
decoder := json . NewDecoder ( r . Body )
var winSize sstore . ClientWinSizeType
err := decoder . Decode ( & winSize )
if err != nil {
2023-12-22 17:55:20 -08:00
WriteJsonError ( w , fmt . Errorf ( ErrorDecodingJson , err ))
2022-09-25 00:26:33 -07:00
return
}
err = sstore . SetWinSize ( r . Context (), winSize )
if err != nil {
WriteJsonError ( w , fmt . Errorf ( "error setting winsize: %w" , err ))
return
}
WriteJsonSuccess ( w , true )
}
2023-01-16 23:36:52 -08:00
// params: fg, active, open
func HandleLogActiveState ( w http . ResponseWriter , r * http . Request ) {
decoder := json . NewDecoder ( r . Body )
var activeState ClientActiveState
err := decoder . Decode ( & activeState )
if err != nil {
2023-12-22 17:55:20 -08:00
WriteJsonError ( w , fmt . Errorf ( ErrorDecodingJson , err ))
2023-01-16 23:36:52 -08:00
return
}
2024-03-25 20:20:52 -07:00
activity := telemetry . ActivityUpdate {}
2023-01-16 23:36:52 -08:00
if activeState . Fg {
activity . FgMinutes = 1
}
if activeState . Active {
activity . ActiveMinutes = 1
}
if activeState . Open {
activity . OpenMinutes = 1
}
2023-02-21 22:41:56 -08:00
activity . NumConns = remote . NumRemotes ()
2024-03-25 20:20:52 -07:00
err = telemetry . UpdateCurrentActivity ( r . Context (), activity )
2023-01-16 23:36:52 -08:00
if err != nil {
WriteJsonError ( w , fmt . Errorf ( "error updating activity: %w" , err ))
return
}
WriteJsonSuccess ( w , true )
}
2023-03-13 01:52:30 -07:00
// params: screenid
2023-03-13 10:50:29 -07:00
func HandleGetScreenLines ( w http . ResponseWriter , r * http . Request ) {
2022-07-02 13:31:56 -07:00
qvals := r . URL . Query ()
2023-03-13 01:52:30 -07:00
screenId := qvals . Get ( "screenid" )
if _ , err := uuid . Parse ( screenId ); err != nil {
2023-12-22 17:55:20 -08:00
WriteJsonError ( w , fmt . Errorf ( "invalid screenid, err: %w" , err ))
2022-07-02 13:31:56 -07:00
return
}
2023-03-13 10:50:29 -07:00
screenLines , err := sstore . GetScreenLinesById ( r . Context (), screenId )
2022-07-02 13:31:56 -07:00
if err != nil {
WriteJsonError ( w , err )
return
}
2023-03-13 12:10:23 -07:00
WriteJsonSuccess ( w , screenLines )
2022-07-02 13:31:56 -07:00
}
2022-10-27 00:33:50 -07:00
func HandleRtnState ( w http . ResponseWriter , r * http . Request ) {
2022-11-28 18:03:02 -08:00
defer func () {
r := recover ()
if r == nil {
return
}
log . Printf ( "[error] in handlertnstate: %v\n" , r )
debug . PrintStack ()
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( fmt . Sprintf ( ErrorPanic , r )))
2022-11-28 18:03:02 -08:00
}()
2022-10-27 00:33:50 -07:00
qvals := r . URL . Query ()
2023-03-20 19:20:57 -07:00
screenId := qvals . Get ( "screenid" )
2023-07-30 18:32:56 -07:00
lineId := qvals . Get ( "lineid" )
if screenId == "" || lineId == "" {
2022-10-27 00:33:50 -07:00
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( "must specify screenid and lineid" ))
2022-10-27 00:33:50 -07:00
return
}
2023-03-20 19:20:57 -07:00
if _ , err := uuid . Parse ( screenId ); err != nil {
2022-10-27 00:33:50 -07:00
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( fmt . Sprintf ( ErrorInvalidScreenId , err )))
2022-10-27 00:33:50 -07:00
return
}
2023-07-30 18:32:56 -07:00
if _ , err := uuid . Parse ( lineId ); err != nil {
2022-10-27 00:33:50 -07:00
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( fmt . Sprintf ( ErrorInvalidLineId , err )))
2022-10-27 00:33:50 -07:00
return
}
2023-07-30 18:32:56 -07:00
data , err := rtnstate . GetRtnStateDiff ( r . Context (), screenId , lineId )
2022-10-27 00:33:50 -07:00
if err != nil {
w . WriteHeader ( 500 )
w . Write ([] byte ( fmt . Sprintf ( "cannot get rtnstate diff: %v" , err )))
return
}
w . WriteHeader ( http . StatusOK )
w . Write ( data )
}
2022-09-14 23:10:35 -07:00
func HandleRemotePty ( w http . ResponseWriter , r * http . Request ) {
qvals := r . URL . Query ()
remoteId := qvals . Get ( "remoteid" )
if remoteId == "" {
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( "must specify remoteid" ))
2022-09-14 23:10:35 -07:00
return
}
if _ , err := uuid . Parse ( remoteId ); err != nil {
w . WriteHeader ( 500 )
w . Write ([] byte ( fmt . Sprintf ( "invalid remoteid: %v" , err )))
return
}
realOffset , data , err := remote . ReadRemotePty ( r . Context (), remoteId )
if err != nil {
w . WriteHeader ( 500 )
w . Write ([] byte ( fmt . Sprintf ( "error reading ptyout file: %v" , err )))
return
}
w . Header (). Set ( "X-PtyDataOffset" , strconv . FormatInt ( realOffset , 10 ))
w . WriteHeader ( http . StatusOK )
w . Write ( data )
}
2022-07-03 12:01:56 -07:00
func HandleGetPtyOut ( w http . ResponseWriter , r * http . Request ) {
2022-06-12 13:39:48 -07:00
qvals := r . URL . Query ()
2023-03-20 19:20:57 -07:00
screenId := qvals . Get ( "screenid" )
2023-07-30 18:32:56 -07:00
lineId := qvals . Get ( "lineid" )
if screenId == "" || lineId == "" {
2022-06-12 13:39:48 -07:00
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( "must specify screenid and lineid" ))
2022-06-12 13:39:48 -07:00
return
}
2023-03-20 19:20:57 -07:00
if _ , err := uuid . Parse ( screenId ); err != nil {
2022-07-06 22:46:22 -07:00
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( fmt . Sprintf ( ErrorInvalidScreenId , err )))
2022-07-06 22:46:22 -07:00
return
}
2023-07-30 18:32:56 -07:00
if _ , err := uuid . Parse ( lineId ); err != nil {
2022-07-06 22:46:22 -07:00
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( fmt . Sprintf ( ErrorInvalidLineId , err )))
2022-07-06 22:46:22 -07:00
return
}
2023-07-30 18:32:56 -07:00
realOffset , data , err := sstore . ReadFullPtyOutFile ( r . Context (), screenId , lineId )
2022-06-12 13:39:48 -07:00
if err != nil {
2022-06-16 22:22:47 -07:00
if errors . Is ( err , fs . ErrNotExist ) {
w . WriteHeader ( http . StatusOK )
return
}
2022-06-12 13:39:48 -07:00
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( html . EscapeString ( fmt . Sprintf ( "error reading ptyout file: %v" , err ))))
2022-06-12 13:39:48 -07:00
return
}
2022-09-03 23:57:05 -07:00
w . Header (). Set ( "X-PtyDataOffset" , strconv . FormatInt ( realOffset , 10 ))
2022-06-12 13:39:48 -07:00
w . WriteHeader ( http . StatusOK )
2022-08-19 13:23:00 -07:00
w . Write ( data )
2022-06-12 13:39:48 -07:00
}
2023-08-31 22:04:31 -07:00
type writeFileParamsType struct {
ScreenId string `json:"screenid"`
LineId string `json:"lineid"`
Path string `json:"path"`
UseTemp bool `json:"usetemp,omitempty"`
}
func parseWriteFileParams ( r * http . Request ) ( * writeFileParamsType , multipart . File , error ) {
err := r . ParseMultipartForm ( MaxWriteFileMemSize )
if err != nil {
return nil , nil , fmt . Errorf ( "cannot parse multipart form data: %v" , err )
}
form := r . MultipartForm
if len ( form . Value [ "params" ]) == 0 {
return nil , nil , fmt . Errorf ( "no params found" )
}
paramsStr := form . Value [ "params" ][ 0 ]
var params writeFileParamsType
err = json . Unmarshal ([] byte ( paramsStr ), & params )
if err != nil {
return nil , nil , fmt . Errorf ( "bad params json: %v" , err )
}
if len ( form . File [ "data" ]) == 0 {
return nil , nil , fmt . Errorf ( "no data found" )
}
fileHeader := form . File [ "data" ][ 0 ]
file , err := fileHeader . Open ()
if err != nil {
return nil , nil , fmt . Errorf ( "error opening multipart data file: %v" , err )
}
return & params , file , nil
}
func HandleWriteFile ( w http . ResponseWriter , r * http . Request ) {
defer func () {
r := recover ()
if r == nil {
return
}
log . Printf ( "[error] in write-file: %v\n" , r )
debug . PrintStack ()
2023-12-22 17:55:20 -08:00
WriteJsonError ( w , fmt . Errorf ( ErrorPanic , r ))
2023-08-31 22:04:31 -07:00
}()
2023-12-22 17:55:20 -08:00
w . Header (). Set ( CacheControlHeaderKey , CacheControlHeaderNoCache )
2023-08-31 22:04:31 -07:00
params , mpFile , err := parseWriteFileParams ( r )
if err != nil {
WriteJsonError ( w , fmt . Errorf ( "error parsing multipart form params: %w" , err ))
return
}
if params . ScreenId == "" || params . LineId == "" || params . Path == "" {
WriteJsonError ( w , fmt . Errorf ( "invalid params, must set screenid, lineid, and path" ))
return
}
if _ , err := uuid . Parse ( params . ScreenId ); err != nil {
2023-12-22 17:55:20 -08:00
WriteJsonError ( w , fmt . Errorf ( ErrorInvalidScreenId , err ))
2023-08-31 22:04:31 -07:00
return
}
if _ , err := uuid . Parse ( params . LineId ); err != nil {
2023-12-22 17:55:20 -08:00
WriteJsonError ( w , fmt . Errorf ( ErrorInvalidLineId , err ))
2023-08-31 22:04:31 -07:00
return
}
_ , cmd , err := sstore . GetLineCmdByLineId ( r . Context (), params . ScreenId , params . LineId )
if err != nil {
WriteJsonError ( w , fmt . Errorf ( "cannot retrieve line/cmd: %v" , err ))
return
}
if cmd == nil {
WriteJsonError ( w , fmt . Errorf ( "line not found" ))
return
}
if cmd . Remote . RemoteId == "" {
WriteJsonError ( w , fmt . Errorf ( "invalid line, no remote" ))
return
}
msh := remote . GetRemoteById ( cmd . Remote . RemoteId )
if msh == nil {
WriteJsonError ( w , fmt . Errorf ( "invalid line, cannot resolve remote" ))
return
}
2023-09-01 20:40:28 -07:00
rrState := msh . GetRemoteRuntimeState ()
fullPath , err := rrState . ExpandHomeDir ( params . Path )
if err != nil {
WriteJsonError ( w , fmt . Errorf ( "error expanding homedir: %v" , err ))
return
}
2023-08-31 22:04:31 -07:00
cwd := cmd . FeState [ "cwd" ]
writePk := packet . MakeWriteFilePacket ()
writePk . ReqId = uuid . New (). String ()
writePk . UseTemp = params . UseTemp
2023-09-01 20:40:28 -07:00
if filepath . IsAbs ( fullPath ) {
writePk . Path = fullPath
2023-08-31 22:04:31 -07:00
} else {
2023-09-01 20:40:28 -07:00
writePk . Path = filepath . Join ( cwd , fullPath )
2023-08-31 22:04:31 -07:00
}
iter , err := msh . PacketRpcIter ( r . Context (), writePk )
if err != nil {
WriteJsonError ( w , fmt . Errorf ( "error: %v" , err ))
return
}
// first packet should be WriteFileReady
readyIf , err := iter . Next ( r . Context ())
if err != nil {
WriteJsonError ( w , fmt . Errorf ( "error while getting ready response: %w" , err ))
return
}
readyPk , ok := readyIf .( * packet . WriteFileReadyPacketType )
if ! ok {
WriteJsonError ( w , fmt . Errorf ( "bad ready packet received: %T" , readyIf ))
return
}
if readyPk . Error != "" {
WriteJsonError ( w , fmt . Errorf ( "ready error: %s" , readyPk . Error ))
return
}
var buffer [ server . MaxFileDataPacketSize ] byte
bufSlice := buffer [:]
for {
dataPk := packet . MakeFileDataPacket ( writePk . ReqId )
nr , err := io . ReadFull ( mpFile , bufSlice )
if err == io . ErrUnexpectedEOF || err == io . EOF {
dataPk . Eof = true
} else if err != nil {
dataErr := fmt . Errorf ( "error reading file data: %v" , err )
dataPk . Error = dataErr . Error ()
msh . SendFileData ( dataPk )
WriteJsonError ( w , dataErr )
return
}
if nr > 0 {
dataPk . Data = make ([] byte , nr )
copy ( dataPk . Data , bufSlice [ 0 : nr ])
}
msh . SendFileData ( dataPk )
if dataPk . Eof {
break
}
// slight throttle for sending packets
time . Sleep ( 10 * time . Millisecond )
}
doneIf , err := iter . Next ( r . Context ())
if err != nil {
WriteJsonError ( w , fmt . Errorf ( "error while getting done response: %w" , err ))
return
}
donePk , ok := doneIf .( * packet . WriteFileDonePacketType )
if ! ok {
WriteJsonError ( w , fmt . Errorf ( "bad done packet received: %T" , doneIf ))
return
}
if donePk . Error != "" {
WriteJsonError ( w , fmt . Errorf ( "dne error: %s" , donePk . Error ))
return
}
WriteJsonSuccess ( w , nil )
}
func HandleReadFile ( w http . ResponseWriter , r * http . Request ) {
qvals := r . URL . Query ()
screenId := qvals . Get ( "screenid" )
lineId := qvals . Get ( "lineid" )
path := qvals . Get ( "path" ) // validate path?
contentType := qvals . Get ( "mimetype" )
if contentType == "" {
contentType = "application/octet-stream"
}
if screenId == "" || lineId == "" {
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( "must specify sessionid, screenid, and lineid" ))
2023-08-31 22:04:31 -07:00
return
}
if path == "" {
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( "must specify path" ))
2023-08-31 22:04:31 -07:00
return
}
if _ , err := uuid . Parse ( screenId ); err != nil {
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( fmt . Sprintf ( ErrorInvalidScreenId , err )))
2023-08-31 22:04:31 -07:00
return
}
if _ , err := uuid . Parse ( lineId ); err != nil {
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( fmt . Sprintf ( ErrorInvalidLineId , err )))
2023-08-31 22:04:31 -07:00
return
}
if ! ContentTypeHeaderValidRe . MatchString ( contentType ) {
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( "invalid mimetype specified" ))
2023-08-31 22:04:31 -07:00
return
}
_ , cmd , err := sstore . GetLineCmdByLineId ( r . Context (), screenId , lineId )
if err != nil {
w . WriteHeader ( 500 )
w . Write ([] byte ( fmt . Sprintf ( "invalid lineid: %v" , err )))
return
}
if cmd == nil {
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( "invalid line, no cmd" ))
2023-08-31 22:04:31 -07:00
return
}
if cmd . Remote . RemoteId == "" {
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( "invalid line, no remote" ))
2023-08-31 22:04:31 -07:00
return
}
msh := remote . GetRemoteById ( cmd . Remote . RemoteId )
if msh == nil {
w . WriteHeader ( 500 )
2023-12-22 17:55:20 -08:00
w . Write ([] byte ( "invalid line, cannot resolve remote" ))
2023-08-31 22:04:31 -07:00
return
}
2023-09-01 20:40:28 -07:00
rrState := msh . GetRemoteRuntimeState ()
fullPath , err := rrState . ExpandHomeDir ( path )
if err != nil {
WriteJsonError ( w , fmt . Errorf ( "error expanding homedir: %v" , err ))
return
}
streamPk := packet . MakeStreamFilePacket ()
streamPk . ReqId = uuid . New (). String ()
cwd := cmd . FeState [ "cwd" ]
if filepath . IsAbs ( fullPath ) {
streamPk . Path = fullPath
} else {
streamPk . Path = filepath . Join ( cwd , fullPath )
}
2023-08-31 22:04:31 -07:00
iter , err := msh . StreamFile ( r . Context (), streamPk )
if err != nil {
w . WriteHeader ( 500 )
w . Write ([] byte ( fmt . Sprintf ( "error trying to stream file: %v" , err )))
return
}
defer iter . Close ()
respIf , err := iter . Next ( r . Context ())
if err != nil {
w . WriteHeader ( 500 )
w . Write ([] byte ( fmt . Sprintf ( "error getting streamfile response: %v" , err )))
return
}
resp , ok := respIf .( * packet . StreamFileResponseType )
if ! ok {
w . WriteHeader ( 500 )
w . Write ([] byte ( fmt . Sprintf ( "bad response packet type: %T" , respIf )))
return
}
if resp . Error != "" {
w . WriteHeader ( 500 )
w . Write ([] byte ( fmt . Sprintf ( "error response: %s" , resp . Error )))
return
}
infoJson , _ := json . Marshal ( resp . Info )
w . Header (). Set ( "X-FileInfo" , base64 . StdEncoding . EncodeToString ( infoJson ))
2023-12-22 17:55:20 -08:00
w . Header (). Set ( ContentTypeHeaderKey , contentType )
2023-08-31 22:04:31 -07:00
w . WriteHeader ( http . StatusOK )
for {
dataPkIf , err := iter . Next ( r . Context ())
if err != nil {
log . Printf ( "error in read-file while getting data: %v\n" , err )
break
}
if dataPkIf == nil {
break
}
dataPk , ok := dataPkIf .( * packet . FileDataPacketType )
if ! ok {
log . Printf ( "error in read-file, invalid data packet type: %T" , dataPkIf )
break
}
if dataPk . Error != "" {
log . Printf ( "in read-file, data packet error: %s" , dataPk . Error )
break
}
w . Write ( dataPk . Data )
}
}
2022-06-12 13:39:48 -07:00
func WriteJsonError ( w http . ResponseWriter , errVal error ) {
2023-12-22 17:55:20 -08:00
w . Header (). Set ( ContentTypeHeaderKey , ContentTypeJson )
2022-07-15 17:37:32 -07:00
w . WriteHeader ( 200 )
2022-06-12 13:39:48 -07:00
errMap := make ( map [ string ] interface {})
errMap [ "error" ] = errVal . Error ()
2024-03-06 16:37:54 -08:00
errorCode := base . GetErrorCode ( errVal )
if errorCode != "" {
errMap [ "errorcode" ] = errorCode
}
2022-06-12 13:39:48 -07:00
barr , _ := json . Marshal ( errMap )
w . Write ( barr )
}
func WriteJsonSuccess ( w http . ResponseWriter , data interface {}) {
2023-12-22 17:55:20 -08:00
w . Header (). Set ( ContentTypeHeaderKey , ContentTypeJson )
2022-06-12 13:39:48 -07:00
rtnMap := make ( map [ string ] interface {})
rtnMap [ "success" ] = true
if data != nil {
rtnMap [ "data" ] = data
}
barr , err := json . Marshal ( rtnMap )
if err != nil {
WriteJsonError ( w , err )
return
}
w . WriteHeader ( 200 )
w . Write ( barr )
}
func HandleRunCommand ( w http . ResponseWriter , r * http . Request ) {
2022-07-15 01:57:45 -07:00
defer func () {
r := recover ()
if r == nil {
return
}
2022-10-31 12:40:45 -07:00
log . Printf ( "[error] in run-command: %v\n" , r )
2022-07-15 01:57:45 -07:00
debug . PrintStack ()
2023-12-22 17:55:20 -08:00
WriteJsonError ( w , fmt . Errorf ( ErrorPanic , r ))
2022-07-15 01:57:45 -07:00
}()
2023-12-22 17:55:20 -08:00
w . Header (). Set ( CacheControlHeaderKey , CacheControlHeaderNoCache )
2022-06-12 13:39:48 -07:00
decoder := json . NewDecoder ( r . Body )
2022-07-02 13:31:56 -07:00
var commandPk scpacket . FeCommandPacketType
err := decoder . Decode ( & commandPk )
2022-06-12 13:39:48 -07:00
if err != nil {
WriteJsonError ( w , fmt . Errorf ( "error decoding json: %w" , err ))
return
}
2022-07-15 17:37:32 -07:00
update , err := cmdrunner . HandleCommand ( r . Context (), & commandPk )
2022-07-03 12:01:56 -07:00
if err != nil {
WriteJsonError ( w , err )
2022-06-13 11:11:56 -07:00
return
}
2023-05-08 16:06:51 -07:00
if update != nil {
update . Clean ()
}
2022-07-15 17:37:32 -07:00
WriteJsonSuccess ( w , update )
2022-07-03 12:01:56 -07:00
}
2024-03-18 17:11:13 -07:00
func CheckIsDir ( dirHandler http . Handler , fileHandler http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
configPath := r . URL . Path
configAbsPath , err := filepath . Abs ( configPath )
if err != nil {
w . WriteHeader ( 500 )
2024-03-20 16:29:46 -07:00
w . Write ([] byte ( fmt . Sprintf ( "error getting absolute path: %v" , err )))
2024-03-18 17:11:13 -07:00
return
}
configBaseDir := path . Join ( scbase . GetWaveHomeDir (), "config" )
configFullPath := path . Join ( scbase . GetWaveHomeDir (), configAbsPath )
if ! strings . HasPrefix ( configFullPath , configBaseDir ) {
w . WriteHeader ( 500 )
w . Write ([] byte ( fmt . Sprintf ( "error: path is not in config folder" )))
return
}
fstat , err := os . Stat ( configFullPath )
if errors . Is ( err , fs . ErrNotExist ) {
w . WriteHeader ( 404 )
2024-03-20 16:29:46 -07:00
w . Write ([] byte ( fmt . Sprintf ( "file not found: %v" , configAbsPath )))
2024-03-18 17:11:13 -07:00
return
} else if err != nil {
w . WriteHeader ( 500 )
2024-03-20 16:29:46 -07:00
w . Write ([] byte ( fmt . Sprintf ( "file stat err: %v" , err )))
2024-03-18 17:11:13 -07:00
return
}
if fstat . IsDir () {
AuthKeyMiddleWare ( dirHandler ). ServeHTTP ( w , r )
} else {
AuthKeyMiddleWare ( fileHandler ). ServeHTTP ( w , r )
}
})
}
2024-03-11 15:07:29 -07:00
func AuthKeyMiddleWare ( next http . Handler ) http . Handler {
return http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
reqAuthKey := r . Header . Get ( "X-AuthKey" )
w . Header (). Set ( CacheControlHeaderKey , CacheControlHeaderNoCache )
if reqAuthKey == "" {
w . WriteHeader ( 500 )
w . Write ([] byte ( "no x-authkey header" ))
return
}
2024-03-14 00:22:59 -07:00
if reqAuthKey != scbase . WaveAuthKey {
2024-03-11 15:07:29 -07:00
w . WriteHeader ( 500 )
w . Write ([] byte ( "x-authkey header is invalid" ))
return
}
next . ServeHTTP ( w , r )
})
}
2024-03-13 23:59:20 -07:00
func AuthKeyWrapAllowHmac ( fn WebFnType ) WebFnType {
return func ( w http . ResponseWriter , r * http . Request ) {
reqAuthKey := r . Header . Get ( "X-AuthKey" )
if reqAuthKey == "" {
// try hmac
qvals := r . URL . Query ()
if ! qvals . Has ( "hmac" ) {
w . WriteHeader ( 500 )
w . Write ([] byte ( "no x-authkey header" ))
return
}
2024-03-14 00:22:59 -07:00
hmacOk , err := promptenc . ValidateUrlHmac ([] byte ( scbase . WaveAuthKey ), r . URL . Path , qvals )
2024-03-13 23:59:20 -07:00
if err != nil || ! hmacOk {
w . WriteHeader ( 500 )
w . Write ([] byte ( fmt . Sprintf ( "error validating hmac" )))
return
}
// fallthrough (hmac is valid)
2024-03-14 00:22:59 -07:00
} else if reqAuthKey != scbase . WaveAuthKey {
2024-03-13 23:59:20 -07:00
w . WriteHeader ( 500 )
w . Write ([] byte ( "x-authkey header is invalid" ))
return
}
w . Header (). Set ( CacheControlHeaderKey , CacheControlHeaderNoCache )
fn ( w , r )
}
}
2022-12-20 16:16:46 -08:00
func AuthKeyWrap ( fn WebFnType ) WebFnType {
return func ( w http . ResponseWriter , r * http . Request ) {
reqAuthKey := r . Header . Get ( "X-AuthKey" )
if reqAuthKey == "" {
w . WriteHeader ( 500 )
w . Write ([] byte ( "no x-authkey header" ))
return
}
2024-03-14 00:22:59 -07:00
if reqAuthKey != scbase . WaveAuthKey {
2022-12-20 16:16:46 -08:00
w . WriteHeader ( 500 )
w . Write ([] byte ( "x-authkey header is invalid" ))
return
}
2023-12-22 17:55:20 -08:00
w . Header (). Set ( CacheControlHeaderKey , CacheControlHeaderNoCache )
2022-12-20 16:16:46 -08:00
fn ( w , r )
}
}
2022-06-15 18:13:16 -07:00
func runWebSocketServer () {
gr := mux . NewRouter ()
gr . HandleFunc ( "/ws" , HandleWs )
2022-12-28 17:47:12 -08:00
serverAddr := WebSocketServerAddr
if scbase . IsDevMode () {
serverAddr = WebSocketServerDevAddr
}
2022-06-15 18:13:16 -07:00
server := & http . Server {
2022-12-28 17:47:12 -08:00
Addr : serverAddr ,
2022-06-15 18:13:16 -07:00
ReadTimeout : HttpReadTimeout ,
WriteTimeout : HttpWriteTimeout ,
MaxHeaderBytes : HttpMaxHeaderBytes ,
Handler : gr ,
}
server . SetKeepAlivesEnabled ( false )
2022-12-28 17:47:12 -08:00
log . Printf ( "Running websocket server on %s\n" , serverAddr )
2022-06-15 18:13:16 -07:00
err := server . ListenAndServe ()
if err != nil {
2022-10-31 12:40:45 -07:00
log . Printf ( "[error] trying to run websocket server: %v\n" , err )
2022-06-15 18:13:16 -07:00
}
}
2022-09-05 16:31:22 -07:00
func test () error {
return nil
}
2023-01-22 23:10:18 -08:00
func sendTelemetryWrapper () {
2023-01-23 12:54:32 -08:00
defer func () {
r := recover ()
if r == nil {
return
}
log . Printf ( "[error] in sendTelemetryWrapper: %v\n" , r )
debug . PrintStack ()
}()
2023-01-22 23:10:18 -08:00
ctx , cancelFn := context . WithTimeout ( context . Background (), 5 * time . Second )
defer cancelFn ()
2023-01-23 12:54:32 -08:00
err := pcloud . SendTelemetry ( ctx , false )
2023-01-22 23:10:18 -08:00
if err != nil {
log . Printf ( "[error] sending telemetry: %v\n" , err )
}
2023-01-17 16:02:44 -08:00
}
2023-12-15 17:43:54 -08:00
func checkNewReleaseWrapper () {
defer func () {
r := recover ()
if r == nil {
return
}
log . Printf ( "[error] in checkNewReleaseWrapper: %v\n" , r )
debug . PrintStack ()
}()
ctx , cancelFn := context . WithTimeout ( context . Background (), 5 * time . Second )
defer cancelFn ()
_ , err := releasechecker . CheckNewRelease ( ctx , false )
if err != nil {
log . Printf ( "[error] checking for new release: %v\n" , err )
return
}
}
2023-01-25 22:49:50 -08:00
func telemetryLoop () {
var lastSent time . Time
time . Sleep ( InitialTelemetryWait )
for {
2023-12-15 17:43:54 -08:00
dur := time . Since ( lastSent )
2023-01-25 22:49:50 -08:00
if lastSent . IsZero () || dur >= TelemetryInterval {
lastSent = time . Now ()
sendTelemetryWrapper ()
2023-12-15 17:43:54 -08:00
checkNewReleaseWrapper ()
2023-01-25 22:49:50 -08:00
}
time . Sleep ( TelemetryTick )
}
}
2022-10-30 13:05:53 -07:00
// watch stdin, kill server if stdin is closed
func stdinReadWatch () {
buf := make ([] byte , 1024 )
for {
_ , err := os . Stdin . Read ( buf )
if err != nil {
2023-03-12 15:24:53 -07:00
doShutdown ( fmt . Sprintf ( "stdin closed/error (%v)" , err ))
2023-01-23 15:48:54 -08:00
break
2022-10-30 13:05:53 -07:00
}
}
}
2023-03-12 15:16:49 -07:00
// ignore SIGHUP
func installSignalHandlers () {
sigCh := make ( chan os . Signal , 1 )
signal . Notify ( sigCh , syscall . SIGHUP )
go func () {
for sig := range sigCh {
2023-03-12 15:24:53 -07:00
doShutdown ( fmt . Sprintf ( "got signal %v" , sig ))
break
2023-03-12 15:16:49 -07:00
}
}()
}
2023-03-12 15:24:53 -07:00
func doShutdown ( reason string ) {
shutdownOnce . Do ( func () {
2023-11-01 01:26:19 -07:00
log . Printf ( "[wave] local server %v, start shutdown\n" , reason )
2023-03-12 15:24:53 -07:00
sendTelemetryWrapper ()
2023-11-01 01:26:19 -07:00
log . Printf ( "[wave] closing db connection\n" )
2023-03-12 15:24:53 -07:00
sstore . CloseDB ()
2023-11-01 01:26:19 -07:00
log . Printf ( "[wave] *** shutting down local server\n" )
2023-03-12 15:24:53 -07:00
time . Sleep ( 1 * time . Second )
syscall . Kill ( syscall . Getpid (), syscall . SIGINT )
time . Sleep ( 5 * time . Second )
syscall . Kill ( syscall . Getpid (), syscall . SIGKILL )
})
}
2024-03-18 17:11:13 -07:00
func configDirHandler ( w http . ResponseWriter , r * http . Request ) {
log . Printf ( "running?" )
configPath := r . URL . Path
configFullPath := path . Join ( scbase . GetWaveHomeDir (), configPath )
dirFile , err := os . Open ( configFullPath )
if err != nil {
w . WriteHeader ( 500 )
2024-03-20 16:29:46 -07:00
w . Write ([] byte ( fmt . Sprintf ( "error opening specified dir: %v" , err )))
2024-03-18 17:11:13 -07:00
return
}
entries , err := dirFile . Readdir ( 0 )
if err != nil {
w . WriteHeader ( 500 )
2024-03-20 16:29:46 -07:00
w . Write ([] byte ( fmt . Sprintf ( "error getting files: %v" , err )))
2024-03-18 17:11:13 -07:00
return
}
var files [] * packet . FileStatPacketType
for index := 0 ; index < len ( entries ); index ++ {
curEntry := entries [ index ]
curFile := packet . MakeFileStatPacketFromFileInfo ( curEntry , "" , false )
files = append ( files , curFile )
}
dirListJson , err := json . Marshal ( files )
if err != nil {
w . WriteHeader ( 500 )
2024-03-20 16:29:46 -07:00
w . Write ([] byte ( fmt . Sprintf ( "json err: %v" , err )))
2024-03-18 17:11:13 -07:00
return
}
w . WriteHeader ( 200 )
w . Header (). Set ( "Content-Type" , "application/json" )
w . Write ( dirListJson )
}
2022-06-12 13:39:48 -07:00
func main () {
2023-02-23 15:17:47 -08:00
scbase . BuildTime = BuildTime
2024-02-23 17:44:00 -08:00
scbase . WaveVersion = WaveVersion
2024-01-16 16:11:04 -08:00
base . ProcessType = base . ProcessType_WaveSrv
2024-02-15 17:42:43 -08:00
wlog . GlobalSubsystem = base . ProcessType_WaveSrv
wlog . LogConsumer = wlog . LogWithLogger
2023-02-23 15:17:47 -08:00
2022-09-05 16:31:22 -07:00
if len ( os . Args ) >= 2 && os . Args [ 1 ] == "--test" {
2022-10-31 12:40:45 -07:00
log . Printf ( "running test fn\n" )
2022-09-05 16:31:22 -07:00
err := test ()
if err != nil {
2022-10-31 12:40:45 -07:00
log . Printf ( "[error] %v\n" , err )
2022-09-05 16:31:22 -07:00
}
return
}
2023-11-01 01:26:19 -07:00
scHomeDir := scbase . GetWaveHomeDir ()
log . Printf ( "[wave] *** starting wavesrv version %s+%s\n" , scbase . WaveVersion , scbase . BuildTime )
log . Printf ( "[wave] homedir = %q\n" , scHomeDir )
2022-10-31 12:40:45 -07:00
2023-11-01 01:26:19 -07:00
scLock , err := scbase . AcquireWaveLock ()
2022-07-07 16:29:14 -07:00
if err != nil || scLock == nil {
2023-11-09 16:26:44 -08:00
log . Printf ( "[error] cannot acquire wave lock (another instance of wavesrv is likely running): %v\n" , err )
2022-07-07 16:29:14 -07:00
return
}
2022-07-01 10:48:14 -07:00
if len ( os . Args ) >= 2 && strings . HasPrefix ( os . Args [ 1 ], "--migrate" ) {
err := sstore . MigrateCommandOpts ( os . Args [ 1 :])
if err != nil {
2022-10-31 12:40:45 -07:00
log . Printf ( "[error] migrate cmd: %v\n" , err )
2022-07-01 10:48:14 -07:00
}
return
}
2024-03-14 00:22:59 -07:00
err = scbase . InitializeWaveAuthKey ()
2022-12-20 16:16:46 -08:00
if err != nil {
log . Printf ( "[error] %v\n" , err )
return
}
2022-07-07 16:29:14 -07:00
err = sstore . TryMigrateUp ()
2022-07-01 10:48:14 -07:00
if err != nil {
2022-10-31 12:40:45 -07:00
log . Printf ( "[error] migrate up: %v\n" , err )
2022-07-01 10:48:14 -07:00
return
}
2022-09-25 00:26:33 -07:00
clientData , err := sstore . EnsureClientData ( context . Background ())
2022-08-26 16:21:19 -07:00
if err != nil {
2022-10-31 12:40:45 -07:00
log . Printf ( "[error] ensuring client data: %v\n" , err )
2022-08-26 16:21:19 -07:00
return
}
2022-10-31 12:40:45 -07:00
log . Printf ( "userid = %s\n" , clientData . UserId )
2022-07-01 12:17:19 -07:00
err = sstore . EnsureLocalRemote ( context . Background ())
if err != nil {
2022-10-31 12:40:45 -07:00
log . Printf ( "[error] ensuring local remote: %v\n" , err )
2022-07-01 12:17:19 -07:00
return
}
2023-11-01 23:41:04 -07:00
err = sstore . EnsureOneSession ( context . Background ())
2022-07-01 14:45:33 -07:00
if err != nil {
2022-10-31 12:40:45 -07:00
log . Printf ( "[error] ensuring default session: %v\n" , err )
2022-07-01 14:45:33 -07:00
return
2022-07-01 12:17:19 -07:00
}
2022-07-01 17:38:36 -07:00
err = remote . LoadRemotes ( context . Background ())
2022-06-12 13:39:48 -07:00
if err != nil {
2022-10-31 12:40:45 -07:00
log . Printf ( "[error] loading remotes: %v\n" , err )
2022-06-12 13:39:48 -07:00
return
}
2022-07-06 19:01:00 -07:00
2022-07-07 16:29:14 -07:00
err = sstore . HangupAllRunningCmds ( context . Background ())
if err != nil {
2023-01-11 20:53:46 -08:00
log . Printf ( "[error] calling HUP on all running commands: %v\n" , err )
}
err = sstore . ReInitFocus ( context . Background ())
if err != nil {
2023-03-14 16:37:22 -07:00
log . Printf ( "[error] resetting screen focus: %v\n" , err )
2022-07-07 16:29:14 -07:00
}
2022-07-06 19:01:00 -07:00
2023-01-25 22:49:50 -08:00
log . Printf ( "PCLOUD_ENDPOINT=%s\n" , pcloud . GetEndpoint ())
2024-03-25 20:20:52 -07:00
telemetry . UpdateActivityWrap ( context . Background (), telemetry . ActivityUpdate { NumConns : remote . NumRemotes ()}, "numconns" ) // set at least one record into activity
2023-03-12 15:16:49 -07:00
installSignalHandlers ()
2023-01-25 22:49:50 -08:00
go telemetryLoop ()
2022-10-30 13:05:53 -07:00
go stdinReadWatch ()
2022-06-15 18:13:16 -07:00
go runWebSocketServer ()
2023-03-26 18:48:43 -07:00
go func () {
time . Sleep ( 10 * time . Second )
pcloud . StartUpdateWriter ()
}()
2022-06-12 13:39:48 -07:00
gr := mux . NewRouter ()
2022-12-20 16:16:46 -08:00
gr . HandleFunc ( "/api/ptyout" , AuthKeyWrap ( HandleGetPtyOut ))
gr . HandleFunc ( "/api/remote-pty" , AuthKeyWrap ( HandleRemotePty ))
gr . HandleFunc ( "/api/rtnstate" , AuthKeyWrap ( HandleRtnState ))
2023-03-13 10:50:29 -07:00
gr . HandleFunc ( "/api/get-screen-lines" , AuthKeyWrap ( HandleGetScreenLines ))
2022-12-20 16:16:46 -08:00
gr . HandleFunc ( "/api/run-command" , AuthKeyWrap ( HandleRunCommand )). Methods ( "POST" )
gr . HandleFunc ( "/api/get-client-data" , AuthKeyWrap ( HandleGetClientData ))
gr . HandleFunc ( "/api/set-winsize" , AuthKeyWrap ( HandleSetWinSize ))
2023-01-16 23:36:52 -08:00
gr . HandleFunc ( "/api/log-active-state" , AuthKeyWrap ( HandleLogActiveState ))
2024-03-13 23:59:20 -07:00
gr . HandleFunc ( "/api/read-file" , AuthKeyWrapAllowHmac ( HandleReadFile ))
2023-08-31 22:04:31 -07:00
gr . HandleFunc ( "/api/write-file" , AuthKeyWrap ( HandleWriteFile )). Methods ( "POST" )
2024-03-11 15:07:29 -07:00
configPath := path . Join ( scbase . GetWaveHomeDir (), "config" ) + "/"
log . Printf ( "[wave] config path: %q\n" , configPath )
2024-03-18 17:11:13 -07:00
isFileHandler := http . StripPrefix ( "/config/" , http . FileServer ( http . Dir ( configPath )))
isDirHandler := http . HandlerFunc ( configDirHandler )
gr . PathPrefix ( "/config/" ). Handler ( CheckIsDir ( isDirHandler , isFileHandler ))
2024-03-11 15:07:29 -07:00
2022-12-28 17:47:12 -08:00
serverAddr := MainServerAddr
if scbase . IsDevMode () {
serverAddr = MainServerDevAddr
}
2022-06-12 13:39:48 -07:00
server := & http . Server {
2022-12-28 17:47:12 -08:00
Addr : serverAddr ,
2022-06-12 13:39:48 -07:00
ReadTimeout : HttpReadTimeout ,
WriteTimeout : HttpWriteTimeout ,
MaxHeaderBytes : HttpMaxHeaderBytes ,
Handler : http . TimeoutHandler ( gr , HttpTimeoutDuration , "Timeout" ),
}
server . SetKeepAlivesEnabled ( false )
2022-12-28 17:47:12 -08:00
log . Printf ( "Running main server on %s\n" , serverAddr )
2022-06-12 13:39:48 -07:00
err = server . ListenAndServe ()
if err != nil {
2022-10-31 12:40:45 -07:00
log . Printf ( "ERROR: %v\n" , err )
2022-06-12 13:39:48 -07:00
}
2023-11-09 16:26:44 -08:00
runtime . KeepAlive ( scLock )
2022-06-12 13:39:48 -07:00
}