Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
new-api
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
541b9f2e
authored
Apr 25, 2023
by
JustSong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: fully support stream mode now (close #3)
parent
2ed58bb0
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
133 additions
and
49 deletions
+133
-49
common/custom_event.go
+82
-0
controller/relay.go
+49
-47
main.go
+2
-2
No files found.
common/custom_event.go
0 → 100644
View file @
541b9f2e
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package
common
import
(
"fmt"
"io"
"net/http"
"strings"
)
type
stringWriter
interface
{
io
.
Writer
writeString
(
string
)
(
int
,
error
)
}
type
stringWrapper
struct
{
io
.
Writer
}
func
(
w
stringWrapper
)
writeString
(
str
string
)
(
int
,
error
)
{
return
w
.
Writer
.
Write
([]
byte
(
str
))
}
func
checkWriter
(
writer
io
.
Writer
)
stringWriter
{
if
w
,
ok
:=
writer
.
(
stringWriter
);
ok
{
return
w
}
else
{
return
stringWrapper
{
writer
}
}
}
// Server-Sent Events
// W3C Working Draft 29 October 2009
// http://www.w3.org/TR/2009/WD-eventsource-20091029/
var
contentType
=
[]
string
{
"text/event-stream"
}
var
noCache
=
[]
string
{
"no-cache"
}
var
fieldReplacer
=
strings
.
NewReplacer
(
"
\n
"
,
"
\\
n"
,
"
\r
"
,
"
\\
r"
)
var
dataReplacer
=
strings
.
NewReplacer
(
"
\n
"
,
"
\n
data:"
,
"
\r
"
,
"
\\
r"
)
type
CustomEvent
struct
{
Event
string
Id
string
Retry
uint
Data
interface
{}
}
func
encode
(
writer
io
.
Writer
,
event
CustomEvent
)
error
{
w
:=
checkWriter
(
writer
)
return
writeData
(
w
,
event
.
Data
)
}
func
writeData
(
w
stringWriter
,
data
interface
{})
error
{
dataReplacer
.
WriteString
(
w
,
fmt
.
Sprint
(
data
))
if
strings
.
HasPrefix
(
data
.
(
string
),
"data"
)
{
w
.
writeString
(
"
\n\n
"
)
}
return
nil
}
func
(
r
CustomEvent
)
Render
(
w
http
.
ResponseWriter
)
error
{
r
.
WriteContentType
(
w
)
return
encode
(
w
,
r
)
}
func
(
r
CustomEvent
)
WriteContentType
(
w
http
.
ResponseWriter
)
{
header
:=
w
.
Header
()
header
[
"Content-Type"
]
=
contentType
if
_
,
exist
:=
header
[
"Cache-Control"
];
!
exist
{
header
[
"Cache-Control"
]
=
noCache
}
}
controller/relay.go
View file @
541b9f2e
...
@@ -47,58 +47,60 @@ func Relay(c *gin.Context) {
...
@@ -47,58 +47,60 @@ func Relay(c *gin.Context) {
return
return
}
}
defer
resp
.
Body
.
Close
()
defer
resp
.
Body
.
Close
()
scanner
:=
bufio
.
NewScanner
(
resp
.
Body
)
isStream
:=
resp
.
Header
.
Get
(
"Content-Type"
)
==
"text/event-stream"
scanner
.
Split
(
func
(
data
[]
byte
,
atEOF
bool
)
(
advance
int
,
token
[]
byte
,
err
error
)
{
if
isStream
{
if
atEOF
&&
len
(
data
)
==
0
{
scanner
:=
bufio
.
NewScanner
(
resp
.
Body
)
return
0
,
nil
,
nil
scanner
.
Split
(
func
(
data
[]
byte
,
atEOF
bool
)
(
advance
int
,
token
[]
byte
,
err
error
)
{
}
if
atEOF
&&
len
(
data
)
==
0
{
return
0
,
nil
,
nil
}
if
i
:=
strings
.
Index
(
string
(
data
),
"
\n\n
"
);
i
>=
0
{
if
i
:=
strings
.
Index
(
string
(
data
),
"
\n\n
"
);
i
>=
0
{
return
i
+
2
,
data
[
0
:
i
],
nil
return
i
+
2
,
data
[
0
:
i
],
nil
}
}
if
atEOF
{
if
atEOF
{
return
len
(
data
),
data
,
nil
return
len
(
data
),
data
,
nil
}
}
return
0
,
nil
,
nil
return
0
,
nil
,
nil
})
})
dataChan
:=
make
(
chan
string
)
dataChan
:=
make
(
chan
string
)
stopChan
:=
make
(
chan
bool
)
stopChan
:=
make
(
chan
bool
)
go
func
()
{
go
func
()
{
for
scanner
.
Scan
()
{
for
scanner
.
Scan
()
{
data
:=
scanner
.
Text
()
data
:=
scanner
.
Text
()
dataChan
<-
data
dataChan
<-
data
}
stopChan
<-
true
}()
for
k
,
v
:=
range
resp
.
Header
{
c
.
Writer
.
Header
()
.
Set
(
k
,
v
[
0
])
}
c
.
Writer
.
Header
()
.
Set
(
"Content-Type"
,
"text/event-stream"
)
c
.
Writer
.
Header
()
.
Set
(
"Cache-Control"
,
"no-cache"
)
c
.
Writer
.
Header
()
.
Set
(
"Connection"
,
"keep-alive"
)
c
.
Writer
.
WriteHeaderNow
()
//w := c.Writer
//flusher, _ := w.(http.Flusher)
c
.
Stream
(
func
(
w
io
.
Writer
)
bool
{
select
{
case
data
:=
<-
dataChan
:
suffix
:=
""
if
strings
.
HasPrefix
(
data
,
"data: "
)
{
suffix
=
"
\n\n
"
}
}
_
,
err
:=
fmt
.
Fprintf
(
w
,
"%s%s"
,
data
,
suffix
)
stopChan
<-
true
if
err
!=
nil
{
}()
c
.
Writer
.
Header
()
.
Set
(
"Content-Type"
,
"text/event-stream"
)
c
.
Writer
.
Header
()
.
Set
(
"Cache-Control"
,
"no-cache"
)
c
.
Writer
.
Header
()
.
Set
(
"Connection"
,
"keep-alive"
)
c
.
Writer
.
Header
()
.
Set
(
"Transfer-Encoding"
,
"chunked"
)
c
.
Stream
(
func
(
w
io
.
Writer
)
bool
{
select
{
case
data
:=
<-
dataChan
:
c
.
Render
(
-
1
,
common
.
CustomEvent
{
Data
:
data
})
return
true
case
<-
stopChan
:
return
false
return
false
}
}
flusher
,
_
:=
w
.
(
http
.
Flusher
)
})
flusher
.
Flush
()
return
//fmt.Println(data)
}
else
{
return
true
for
k
,
v
:=
range
resp
.
Header
{
case
<-
stopChan
:
c
.
Writer
.
Header
()
.
Set
(
k
,
v
[
0
])
return
false
}
_
,
err
=
io
.
Copy
(
c
.
Writer
,
resp
.
Body
)
if
err
!=
nil
{
c
.
JSON
(
http
.
StatusOK
,
gin
.
H
{
"error"
:
gin
.
H
{
"message"
:
err
.
Error
(),
"type"
:
"one_api_error"
,
},
})
return
}
}
})
}
return
}
}
main.go
View file @
541b9f2e
...
@@ -2,7 +2,6 @@ package main
...
@@ -2,7 +2,6 @@ package main
import
(
import
(
"embed"
"embed"
"github.com/gin-contrib/gzip"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-contrib/sessions/redis"
"github.com/gin-contrib/sessions/redis"
...
@@ -51,7 +50,8 @@ func main() {
...
@@ -51,7 +50,8 @@ func main() {
// Initialize HTTP server
// Initialize HTTP server
server
:=
gin
.
Default
()
server
:=
gin
.
Default
()
server
.
Use
(
gzip
.
Gzip
(
gzip
.
DefaultCompression
))
// This will cause SSE not to work!!!
//server.Use(gzip.Gzip(gzip.DefaultCompression))
server
.
Use
(
middleware
.
CORS
())
server
.
Use
(
middleware
.
CORS
())
// Initialize session store
// Initialize session store
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment