package main
import (
"log"
"net/http"
)
func main() {
server := &http.Server{
Handler: handler,
}
// 创建标准输入输出监听器
listener := NewStdioListener()
// 在标准输入输出连接上处理单个HTTP请求
log.Println("启动标准输入输出HTTP服务器...")
go func() {
listener.Wait()
log.Println("关闭 Server...")
log.Println("关闭标准输入输出HTTP服务器: ", server.Close())
}()
// 启动 HTTP 服务
if err := server.Serve(listener); err != nil {
if err != http.ErrServerClosed {
panic(err)
}
}
}
module glot
require (
github.com/gorilla/mux v1.8.1
github.com/duckdb/duckdb-go/v2 v2.5.1
)
package main
import (
"database/sql"
"errors"
"fmt"
"log"
"net/http"
"net/http/httputil"
"github.com/gorilla/mux"
_ "github.com/duckdb/duckdb-go/v2"
)
var (
handler = mux.NewRouter()
)
func init() {
handler.HandleFunc("/save/{table}", func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) // 获取路径参数
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.WriteHeader(http.StatusOK)
w.Write([]byte("Hello from stdin/stdout HTTP server!\n"+handle(vars["table"])))
})
handler.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
req, _ :=httputil.DumpRequest(r, false)
w.WriteHeader(http.StatusNotFound)
w.Write(req)
})
}
func handle(table string) string {
db, err := sql.Open("duckdb", "~/.cache/duckdb/duckdb.ddb")
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec("CREATE TABLE "+table+" (id INTEGER, name VARCHAR)")
if err != nil {
log.Fatal(err)
}
_, err = db.Exec("INSERT INTO people VALUES (42, 'John')")
if err != nil {
log.Fatal(err)
}
var (
id int
name string
)
row := db.QueryRow("SELECT id, name FROM "+table)
err = row.Scan(&id, &name)
if errors.Is(err, sql.ErrNoRows) {
log.Println("no rows")
} else if err != nil {
log.Fatal(err)
}
return fmt.Sprintf("id: %d, name: %s\n", id, name)
}
package main
import (
"bufio"
"errors"
"io"
"log"
"net"
"os"
"sync"
"time"
)
// ------------------------------
// 实现一个 net.Listener
// ------------------------------
type stdioListener struct {
conn net.Conn
done chan struct{}
close chan struct{}
}
func NewStdioListener() *stdioListener {
done := make(chan struct{})
return &stdioListener{
conn: NewStdioConn(done),
done: done,
close: make(chan struct{}),
}
}
func (l *stdioListener) Wait() {
<-l.done
}
func (l *stdioListener) Accept() (net.Conn, error) {
if l.conn != nil {
conn := l.conn
l.conn = nil
return conn, nil
}
log.Println("!!!!111...")
log.Println("!!!!222...")
<-l.close
log.Println("!!!!333...")
return nil, net.ErrClosed
}
func (l *stdioListener) Close() error {
log.Println("关闭 stdioListener...")
close(l.close)
return nil
}
func (l *stdioListener) Addr() net.Addr {
return &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}
}
// StdioConn 实现 net.Conn 接口,将 stdin/stdout 作为连接
type StdioConn struct {
reader *bufio.Reader
writer *bufio.Writer
closed bool
closeCh chan struct{}
mutex sync.Mutex
deadline time.Time
}
func NewStdioConn(closeCh chan struct{}) *StdioConn {
return &StdioConn{
reader: bufio.NewReader(os.Stdin),
writer: bufio.NewWriter(os.Stdout),
closeCh: closeCh,
}
}
func (c *StdioConn) Read(b []byte) (n int, err error) {
c.mutex.Lock()
defer c.mutex.Unlock()
if c.closed {
return 0, io.EOF
}
if !c.deadline.IsZero() && time.Now().After(c.deadline) {
return 0, errors.New("read timeout")
}
return c.reader.Read(b)
}
func (c *StdioConn) Write(b []byte) (n int, err error) {
c.mutex.Lock()
defer c.mutex.Unlock()
if c.closed {
return 0, io.EOF
}
n, err = c.writer.Write(b)
if err != nil {
return n, err
}
return n, c.writer.Flush()
}
func (c *StdioConn) Close() error {
c.mutex.Lock()
defer c.mutex.Unlock()
log.Println("关闭 StdioConn...")
close(c.closeCh)
c.closed = true
return nil
}
func (c *StdioConn) LocalAddr() net.Addr {
return &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}
}
func (c *StdioConn) RemoteAddr() net.Addr {
return &net.IPAddr{IP: net.IPv4(127, 0, 0, 1)}
}
func (c *StdioConn) SetDeadline(t time.Time) error {
c.mutex.Lock()
defer c.mutex.Unlock()
c.deadline = t
return nil
}
func (c *StdioConn) SetReadDeadline(t time.Time) error {
return c.SetDeadline(t)
}
func (c *StdioConn) SetWriteDeadline(t time.Time) error {
return c.SetDeadline(t)
}
{
"in":"http",
"out":"http",
"debug": true,
"command":"go mod tidy && go run ."
}